[WIN32] Fix a delay in handling large POSTs to named pipe based
[mod_fastcgi.git] / fcgi_pm.c
blobfbed7f5750fc571f69a15dd9d49ec362b17a187a
1 /*
2 * $Id: fcgi_pm.c,v 1.83 2002/10/24 00:58:57 robs Exp $
3 */
6 #include "fcgi.h"
8 #if defined(APACHE2) && !defined(WIN32)
9 #include <pwd.h>
10 #include <unistd.h>
11 #include "unixd.h"
12 #include "apr_signal.h"
13 #endif
15 #ifndef WIN32
16 #include <utime.h>
17 #endif
19 #ifdef _HPUX_SOURCE
20 #include <unistd.h>
21 #define seteuid(arg) setresuid(-1, (arg), -1)
22 #endif
24 int fcgi_dynamic_total_proc_count = 0; /* number of running apps */
25 time_t fcgi_dynamic_epoch = 0; /* last time kill_procs was
26 * invoked by process mgr */
27 time_t fcgi_dynamic_last_analyzed = 0; /* last time calculation was
28 * made for the dynamic procs */
30 static time_t now = 0;
32 #ifdef WIN32
33 #pragma warning ( disable : 4100 4102 )
34 static BOOL bTimeToDie = FALSE; /* process termination flag */
35 HANDLE fcgi_event_handles[3];
36 #ifndef SIGKILL
37 #define SIGKILL 9
38 #endif
39 #endif
42 #ifndef WIN32
43 static int seteuid_root(void)
45 int rc = seteuid(getuid());
46 if (rc) {
47 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
48 "FastCGI: seteuid(0) failed");
50 return rc;
53 static int seteuid_user(void)
55 int rc = seteuid(ap_user_id);
56 if (rc) {
57 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
58 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id);
60 return rc;
62 #endif
65 * Signal the process to exit. How (or if) the process responds
66 * depends on the FastCGI application library (esp. on Win32) and
67 * possibly application code (signal handlers and whether or not
68 * SA_RESTART is on). At any rate, we send the signal with the
69 * hopes that the process will exit on its own. Later, as we
70 * review the state of application processes, if we see one marked
71 * for death, but that hasn't died within a specified period of
72 * time, fcgi_kill() is called again with a KILL)
74 static void fcgi_kill(ServerProcess *process, int sig)
76 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process->pid, sig);
78 process->state = FCGI_VICTIM_STATE;
80 #ifdef WIN32
82 if (sig == SIGTERM)
84 SetEvent(process->terminationEvent);
86 else if (sig == SIGKILL)
88 TerminateProcess(process->handle, 1);
90 else
92 ap_assert(0);
95 #else /* !WIN32 */
97 if (fcgi_wrapper)
99 seteuid_root();
102 kill(process->pid, sig);
104 if (fcgi_wrapper)
106 seteuid_user();
109 #endif /* !WIN32 */
112 /*******************************************************************************
113 * Send SIGTERM to each process in the server class, remove socket
114 * file if appropriate. Currently this is only called when the PM is shutting
115 * down and thus memory isn't freed and sockets and files aren't closed.
117 static void shutdown_all()
119 fcgi_server *s = fcgi_servers;
121 while (s)
123 ServerProcess *proc = s->procs;
124 int i;
125 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
126 ? dynamicMaxClassProcs
127 : s->numProcesses;
129 #ifndef WIN32
130 if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL)
132 /* Remove the socket file */
133 if (unlink(s->socket_path) != 0 && errno != ENOENT) {
134 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
135 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
136 s->socket_path,
137 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path);
140 #endif
142 /* Send TERM to all processes */
143 for (i = 0; i < numChildren; i++, proc++)
145 if (proc->state == FCGI_RUNNING_STATE)
147 fcgi_kill(proc, SIGTERM);
151 s = s->next;
154 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
157 * WIN32 applications may not have support for the shutdown event
158 * depending on their application library version
161 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT);
162 s = fcgi_servers;
164 while (s)
166 ServerProcess *proc = s->procs;
167 int i;
168 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
169 ? dynamicMaxClassProcs
170 : s->numProcesses;
172 /* Send KILL to all processes */
173 for (i = 0; i < numChildren; i++, proc++)
175 if (proc->state == FCGI_RUNNING_STATE)
177 fcgi_kill(proc, SIGKILL);
181 s = s->next;
184 #endif /* WIN32 */
187 static int init_listen_sock(fcgi_server * fs)
189 ap_assert(fs->directive != APP_CLASS_EXTERNAL);
191 /* Create the socket */
192 if ((fs->listenFd = socket(fs->socket_addr->sa_family, SOCK_STREAM, 0)) < 0)
194 #ifdef WIN32
195 errno = WSAGetLastError(); // Not sure if this will work as expected
196 #endif
197 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
198 "FastCGI: can't create %sserver \"%s\": socket() failed",
199 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
200 fs->fs_path);
201 return -1;
204 #ifndef WIN32
205 if (fs->socket_addr->sa_family == AF_UNIX)
207 /* Remove any existing socket file.. just in case */
208 unlink(((struct sockaddr_un *)fs->socket_addr)->sun_path);
210 else
211 #endif
213 int flag = 1;
214 setsockopt(fs->listenFd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
217 /* Bind it to the socket_addr */
218 if (bind(fs->listenFd, fs->socket_addr, fs->socket_addr_len))
220 char port[11];
222 #ifdef WIN32
223 errno = WSAGetLastError();
224 #endif
225 ap_snprintf(port, sizeof(port), "port=%d",
226 ((struct sockaddr_in *)fs->socket_addr)->sin_port);
228 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
229 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
230 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
231 fs->fs_path,
232 #ifndef WIN32
233 (fs->socket_addr->sa_family == AF_UNIX) ?
234 ((struct sockaddr_un *)fs->socket_addr)->sun_path :
235 #endif
236 port);
239 #ifndef WIN32
240 /* Twiddle Unix socket permissions */
241 else if (fs->socket_addr->sa_family == AF_UNIX
242 && chmod(((struct sockaddr_un *)fs->socket_addr)->sun_path, S_IRUSR | S_IWUSR))
244 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
245 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
246 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
247 fs->fs_path);
249 #endif
251 /* Set to listen */
252 else if (listen(fs->listenFd, fs->listenQueueDepth))
254 #ifdef WIN32
255 errno = WSAGetLastError();
256 #endif
257 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
258 "FastCGI: can't create %sserver \"%s\": listen() failed",
259 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
260 fs->fs_path);
262 else
264 return 0;
267 #ifdef WIN32
268 closesocket(fs->listenFd);
269 #else
270 close(fs->listenFd);
271 #endif
273 fs->listenFd = -1;
275 return -2;
279 *----------------------------------------------------------------------
281 * pm_main
283 * The FastCGI process manager, which runs as a separate
284 * process responsible for:
285 * - Starting all the FastCGI proceses.
286 * - Restarting any of these processes that die (indicated
287 * by SIGCHLD).
288 * - Catching SIGTERM and relaying it to all the FastCGI
289 * processes before exiting.
291 * Inputs:
292 * Uses global variable fcgi_servers.
294 * Results:
295 * Does not return.
297 * Side effects:
298 * Described above.
300 *----------------------------------------------------------------------
302 #ifndef WIN32
303 static int caughtSigTerm = FALSE;
304 static int caughtSigChld = FALSE;
305 static int caughtSigAlarm = FALSE;
307 static void signal_handler(int signo)
309 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
310 /* SIGUSR1 & SIGHUP are sent by apache to its process group
311 * when apache get 'em. Apache follows up (1.2.x) with attacks
312 * on each of its child processes, but we've got the KillMgr
313 * sitting between us so we never see the KILL. The main loop
314 * in ProcMgr also checks to see if the KillMgr has terminated,
315 * and if it has, we handl it as if we should shutdown too. */
316 caughtSigTerm = TRUE;
317 } else if(signo == SIGCHLD) {
318 caughtSigChld = TRUE;
319 } else if(signo == SIGALRM) {
320 caughtSigAlarm = TRUE;
323 #endif
326 *----------------------------------------------------------------------
328 * spawn_fs_process --
330 * Fork and exec the specified fcgi process.
332 * Results:
333 * 0 for successful fork, -1 for failed fork.
335 * In case the child fails before or in the exec, the child
336 * obtains the error log by calling getErrLog, logs
337 * the error, and exits with exit status = errno of
338 * the failed system call.
340 * Side effects:
341 * Child process created.
343 *----------------------------------------------------------------------
345 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
347 #ifndef WIN32
349 pid_t child_pid;
350 int i;
351 char *dirName;
352 char *dnEnd, *failedSysCall;
354 child_pid = fork();
355 if (child_pid) {
356 return child_pid;
359 /* We're the child. We're gonna exec() so pools don't matter. */
361 dnEnd = strrchr(fs->fs_path, '/');
362 if (dnEnd == NULL) {
363 dirName = "./";
364 } else {
365 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
366 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
368 if (chdir(dirName) < 0) {
369 failedSysCall = "chdir()";
370 goto FailedSystemCallExit;
373 #ifndef __EMX__
374 /* OS/2 dosen't support nice() */
375 if (fs->processPriority != 0) {
376 if (nice(fs->processPriority) == -1) {
377 failedSysCall = "nice()";
378 goto FailedSystemCallExit;
381 #endif
383 /* Open the listenFd on spec'd fd */
384 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
385 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
387 /* Close all other open fds, except stdout/stderr. Leave these two open so
388 * FastCGI applications don't have to find and fix ALL 3rd party libs that
389 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
390 * main server error_log - @@@ provide a directive control where this goes.
392 ap_error_log2stderr(fcgi_apache_main_server);
393 dup2(2, 1);
394 for (i = 0; i < FCGI_MAX_FD; i++) {
395 if (i != FCGI_LISTENSOCK_FILENO && i != 2 && i != 1) {
396 close(i);
400 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
401 * install its own handler. */
402 signal(SIGPIPE, SIG_IGN);
404 if (fcgi_wrapper && (fcgi_user_id != fs->uid || fcgi_group_id != fs->gid)) {
405 char *shortName = strrchr(fs->fs_path, '/') + 1;
407 /* Relinquish our root real uid powers */
408 seteuid_root();
409 setuid(ap_user_id);
411 do {
412 execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group, shortName, NULL, fs->envp);
413 } while (errno == EINTR);
415 else {
416 do {
417 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
418 } while (errno == EINTR);
421 failedSysCall = "execle()";
423 FailedSystemCallExit:
424 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
425 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
426 exit(-1);
428 /* avoid an irrelevant compiler warning */
429 return(0);
431 #else /* WIN32 */
433 #ifdef APACHE2
435 /* based on mod_cgi.c:run_cgi_child() */
437 apr_pool_t * tp;
438 char * termination_env_string;
439 HANDLE listen_handle = INVALID_HANDLE_VALUE;
440 apr_procattr_t * procattr;
441 apr_proc_t proc = { 0 };
442 apr_file_t * file;
443 int i = 0;
445 if (apr_pool_create(&tp, fcgi_config_pool))
446 return 0;
448 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
449 if (process->terminationEvent == NULL)
450 goto CLEANUP;
452 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
454 termination_env_string = ap_psprintf(tp,
455 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
457 while (fs->envp[i]) i++;
458 fs->envp[i++] = termination_env_string;
459 fs->envp[i] = (char *) fs->mutex_env_string;
461 ap_assert(fs->envp[i + 1] == NULL);
463 if (fs->socket_path)
465 SECURITY_ATTRIBUTES sa = { 0 };
467 sa.bInheritHandle = TRUE;
468 sa.nLength = sizeof(sa);
470 listen_handle = CreateNamedPipe(fs->socket_path,
471 PIPE_ACCESS_DUPLEX,
472 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
473 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
475 if (listen_handle == INVALID_HANDLE_VALUE)
477 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
478 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
479 fs->fs_path);
480 goto CLEANUP;
483 else
485 listen_handle = (HANDLE) fs->listenFd;
488 if (apr_procattr_create(&procattr, tp))
489 goto CLEANUP;
491 if (apr_procattr_dir_set(procattr, ap_make_dirstr_parent(tp, fs->fs_path)))
492 goto CLEANUP;
494 if (apr_procattr_detach_set(procattr, 1))
495 goto CLEANUP;
497 if (apr_os_file_put(&file, &listen_handle, 0, tp))
498 goto CLEANUP;
500 /* procattr is opaque so we have to use this - unfortuantely it dups */
501 if (apr_procattr_child_in_set(procattr, file, NULL))
502 goto CLEANUP;
504 if (apr_proc_create(&proc, fs->fs_path, NULL, fs->envp, procattr, tp))
505 goto CLEANUP;
507 process->handle = proc.hproc;
510 CLEANUP:
512 if (i)
514 fs->envp[i - 1] = NULL;
517 ap_destroy_pool(tp);
519 return proc.pid;
521 #else /* WIN32 && !APACHE2 */
523 /* Adapted from Apache's util_script.c ap_call_exec() */
524 char *interpreter = NULL;
525 char *quoted_filename;
526 char *pCommand;
527 char *pEnvBlock, *pNext;
529 int i = 0;
530 int iEnvBlockLen = 1;
532 file_type_e fileType;
534 STARTUPINFO si;
535 PROCESS_INFORMATION pi;
537 request_rec r;
538 pid_t pid = -1;
540 pool * tp = ap_make_sub_pool(fcgi_config_pool);
542 HANDLE listen_handle;
543 char * termination_env_string = NULL;
545 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
546 if (process->terminationEvent == NULL)
548 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
549 "FastCGI: can't create termination event for server \"%s\", "
550 "CreateEvent() failed", fs->fs_path);
551 goto CLEANUP;
553 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
555 termination_env_string = ap_psprintf(tp,
556 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
558 if (fs->socket_path)
560 SECURITY_ATTRIBUTES sa;
562 sa.lpSecurityDescriptor = NULL;
563 sa.bInheritHandle = TRUE;
564 sa.nLength = sizeof(sa);
566 listen_handle = CreateNamedPipe(fs->socket_path,
567 PIPE_ACCESS_DUPLEX,
568 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
569 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
571 if (listen_handle == INVALID_HANDLE_VALUE)
573 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
574 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
575 goto CLEANUP;
578 else
580 listen_handle = (HANDLE) fs->listenFd;
583 memset(&si, 0, sizeof(si));
584 memset(&pi, 0, sizeof(pi));
585 memset(&r, 0, sizeof(r));
587 // Can up a fake request to pass to ap_get_win32_interpreter()
588 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
589 r.server = fcgi_apache_main_server;
590 r.filename = (char *) fs->fs_path;
591 r.pool = tp;
593 fileType = ap_get_win32_interpreter(&r, &interpreter);
595 if (fileType == eFileTypeUNKNOWN) {
596 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
597 "FastCGI: %s is not executable; ensure interpreted scripts have "
598 "\"#!\" as their first line",
599 fs->fs_path);
600 ap_destroy_pool(tp);
601 goto CLEANUP;
605 * We have the interpreter (if there is one) and we have
606 * the arguments (if there are any).
607 * Build the command string to pass to CreateProcess.
609 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
610 if (interpreter && *interpreter) {
611 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
613 else {
614 pCommand = quoted_filename;
618 * Make child process use hPipeOutputWrite as standard out,
619 * and make sure it does not show on screen.
621 si.cb = sizeof(si);
622 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
623 si.wShowWindow = SW_HIDE;
624 si.hStdInput = listen_handle;
626 // XXX These should be open to the error_log
627 si.hStdOutput = INVALID_HANDLE_VALUE;
628 si.hStdError = INVALID_HANDLE_VALUE;
631 * Win32's CreateProcess call requires that the environment
632 * be passed in an environment block, a null terminated block of
633 * null terminated strings.
634 * @todo we should store the env in this format for win32.
636 while (fs->envp[i])
638 iEnvBlockLen += strlen(fs->envp[i]) + 1;
639 i++;
642 iEnvBlockLen += strlen(termination_env_string) + 1;
643 iEnvBlockLen += strlen(fs->mutex_env_string) + 1;
645 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
647 i = 0;
648 pNext = pEnvBlock;
649 while (fs->envp[i])
651 strcpy(pNext, fs->envp[i]);
652 pNext += strlen(pNext) + 1;
653 i++;
656 strcpy(pNext, termination_env_string);
657 pNext += strlen(pNext) + 1;
658 strcpy(pNext, fs->mutex_env_string);
660 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
662 pEnvBlock,
663 ap_make_dirstr_parent(tp, fs->fs_path),
664 &si, &pi))
666 /* Hack to get 16-bit CGI's working. It works for all the
667 * standard modules shipped with Apache. pi.dwProcessId is 0
668 * for 16-bit CGIs and all the Unix specific code that calls
669 * ap_call_exec interprets this as a failure case. And we can't
670 * use -1 either because it is mapped to 0 by the caller.
672 pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId;
674 process->handle = pi.hProcess;
675 CloseHandle(pi.hThread);
678 if (fs->socket_path)
680 CloseHandle(listen_handle);
683 CLEANUP:
685 ap_destroy_pool(tp);
687 return pid;
689 #endif /* !APACHE2 */
690 #endif /* WIN32 */
693 #ifndef WIN32
694 static void reduce_privileges(void)
696 const char *name;
698 if (geteuid() != 0)
699 return;
701 #ifndef __EMX__
702 /* Get username if passed as a uid */
703 if (ap_user_name[0] == '#') {
704 uid_t uid = atoi(&ap_user_name[1]);
705 struct passwd *ent = getpwuid(uid);
707 if (ent == NULL) {
708 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
709 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
710 "you probably need to modify the User directive", (unsigned)uid);
711 exit(1);
713 name = ent->pw_name;
715 else
716 name = ap_user_name;
718 /* Change Group */
719 if (setgid(ap_group_id) == -1) {
720 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
721 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
722 exit(1);
725 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
727 /* Initialize supplementary groups */
728 if (initgroups(name, ap_group_id) == -1) {
729 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
730 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
731 name, (unsigned)ap_group_id);
732 exit(1);
734 #endif /* __EMX__ */
736 /* Change User */
737 if (fcgi_wrapper) {
738 if (seteuid_user() == -1) {
739 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
740 "FastCGI: process manager exiting, failed to reduce privileges");
741 exit(1);
744 else {
745 if (setuid(ap_user_id) == -1) {
746 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
747 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
748 exit(1);
753 /*************
754 * Change the name of this process - best we can easily.
756 static void change_process_name(const char * const name)
758 /* under Apache2, ap_server_argv0 is const */
759 strncpy((char *) ap_server_argv0, name, strlen(ap_server_argv0));
761 #endif /* !WIN32 */
763 static void schedule_start(fcgi_server *s, int proc)
765 /* If we've started one recently, don't register another */
766 time_t time_passed = now - s->restartTime;
768 if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay))
769 || ((s->procs[proc].pid == 0) && (time_passed < s->initStartDelay)))
771 FCGIDBG6("ignore_job: slot=%d, pid=%ld, time_passed=%ld, initStartDelay=%ld, restartDelay=%ld", proc, (long) s->procs[proc].pid, time_passed, s->initStartDelay, s->restartDelay);
772 return;
775 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
776 s->procs[proc].state = FCGI_START_STATE;
777 if (proc == dynamicMaxClassProcs - 1) {
778 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
779 "FastCGI: scheduled the %sstart of the last (dynamic) server "
780 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
781 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
786 *----------------------------------------------------------------------
788 * dynamic_read_msgs
790 * Removes the records written by request handlers and decodes them.
791 * We also update the data structures to reflect the changes.
793 *----------------------------------------------------------------------
796 static void dynamic_read_msgs(int read_ready)
798 fcgi_server *s;
799 int rc;
801 #ifndef WIN32
802 static int buflen = 0;
803 static char buf[FCGI_MSGS_BUFSIZE + 1];
804 char *ptr1, *ptr2, opcode;
805 char execName[FCGI_MAXPATH + 1];
806 char user[MAX_USER_NAME_LEN + 2];
807 char group[MAX_GID_CHAR_LEN + 1];
808 unsigned long q_usec = 0UL, req_usec = 0UL;
809 #else
810 fcgi_pm_job *joblist = NULL;
811 fcgi_pm_job *cjob = NULL;
812 #endif
814 pool *sp = NULL, *tp;
816 #ifndef WIN32
817 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
818 #endif
821 * To prevent the idle application from running indefinitely, we
822 * check the timer and if it is expired, we recompute the values
823 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
824 * message is received, only updates are made to the data structures.
826 if (fcgi_dynamic_last_analyzed == 0) {
827 fcgi_dynamic_last_analyzed = now;
829 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
830 for (s = fcgi_servers; s != NULL; s = s->next) {
831 if (s->directive != APP_CLASS_DYNAMIC)
832 break;
834 /* Advance the last analyzed timestamp by the elapsed time since
835 * it was last set. Round the increase down to the nearest
836 * multiple of dynamicUpdateInterval */
838 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
839 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
840 s->totalConnTime = 0UL;
841 s->totalQueueTime = 0UL;
845 if (read_ready <= 0) {
846 return;
849 #ifndef WIN32
850 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
851 if (rc <= 0) {
852 if (!caughtSigTerm) {
853 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
854 "FastCGI: read() from pipe failed (%d)", rc);
855 if (rc == 0) {
856 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
857 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
858 caughtSigTerm = TRUE;
861 return;
863 buflen += rc;
864 buf[buflen] = '\0';
866 #else
868 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
869 * request to do something) and/or when a timeout expires.
870 * There really should be no reason why this wait would get stuck
871 * but there's no point in waiting forever. */
873 rc = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
875 if (rc != WAIT_OBJECT_0 && rc != WAIT_ABANDONED)
877 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
878 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
879 return;
882 joblist = fcgi_dynamic_mbox;
883 fcgi_dynamic_mbox = NULL;
885 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
887 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
888 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
891 cjob = joblist;
892 #endif
894 #ifdef APACHE2
895 apr_pool_create(&tp, fcgi_config_pool);
896 #else
897 tp = ap_make_sub_pool(fcgi_config_pool);
898 #endif
900 #ifndef WIN32
901 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
902 int scan_failed = 0;
904 ptr2 = strchr(ptr1, '*');
905 if (ptr2) {
906 *ptr2++ = '\0';
908 else {
909 break;
912 opcode = *ptr1;
914 switch (opcode)
916 case FCGI_SERVER_START_JOB:
917 case FCGI_SERVER_RESTART_JOB:
919 if (sscanf(ptr1, "%c %s %16s %15s",
920 &opcode, execName, user, group) != 4)
922 scan_failed = 1;
924 break;
926 case FCGI_REQUEST_TIMEOUT_JOB:
928 if (sscanf(ptr1, "%c %s %16s %15s",
929 &opcode, execName, user, group) != 4)
931 scan_failed = 1;
933 break;
935 case FCGI_REQUEST_COMPLETE_JOB:
937 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
938 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
940 scan_failed = 1;
942 break;
944 default:
946 scan_failed = 1;
947 break;
950 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode, execName, user, group, q_usec, req_usec);
952 if (scan_failed) {
953 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
954 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
955 goto NextJob;
957 #else
958 /* Update data structures for processing */
959 while (cjob != NULL) {
960 joblist = cjob->next;
961 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
962 #endif
964 #ifndef WIN32
965 s = fcgi_util_fs_get(execName, user, group);
966 #else
967 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
968 #endif
970 #ifndef WIN32
971 if (s==NULL && opcode != FCGI_REQUEST_COMPLETE_JOB)
972 #else
973 if (s==NULL && cjob->id != FCGI_REQUEST_COMPLETE_JOB)
974 #endif
976 #ifdef WIN32
978 HANDLE mutex = CreateMutex(NULL, FALSE, cjob->fs_path);
980 if (mutex == NULL)
982 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
983 "FastCGI: can't create accept mutex "
984 "for (dynamic) server \"%s\"", cjob->fs_path);
985 goto BagNewServer;
988 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
989 #else
990 const char *err;
991 #endif
993 /* Create a perm subpool to hold the new server data,
994 * we can destroy it if something doesn't pan out */
995 #ifdef APACHE2
996 apr_pool_create(&sp, fcgi_config_pool);
997 #else
998 sp = ap_make_sub_pool(fcgi_config_pool);
999 #endif
1001 /* Create a new "dynamic" server */
1002 s = fcgi_util_fs_new(sp);
1004 s->directive = APP_CLASS_DYNAMIC;
1005 s->restartDelay = dynamicRestartDelay;
1006 s->listenQueueDepth = dynamicListenQueueDepth;
1007 s->initStartDelay = dynamicInitStartDelay;
1008 s->envp = dynamicEnvp;
1009 s->flush = dynamicFlush;
1011 #ifdef WIN32
1012 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
1013 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
1014 #else
1015 s->fs_path = ap_pstrdup(sp, execName);
1016 #endif
1017 ap_getparents(s->fs_path);
1018 ap_no2slash(s->fs_path);
1019 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
1021 /* XXX the socket_path (both Unix and Win) *is* deducible and
1022 * thus can and will be used by other apache instances without
1023 * the use of shared data regarding the processes serving the
1024 * requests. This can result in slightly unintuitive process
1025 * counts and security implications. This is prevented
1026 * if suexec (Unix) is in use. This is both a feature and a flaw.
1027 * Changing it now would break existing installations. */
1029 #ifndef WIN32
1030 /* Create socket file's path */
1031 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
1032 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1034 /* Create sockaddr, prealloc it so it won't get created in tp */
1035 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
1036 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
1037 &s->socket_addr_len, s->socket_path);
1038 if (err) {
1039 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1040 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
1041 goto BagNewServer;
1044 if (init_listen_sock(s)) {
1045 goto BagNewServer;
1048 /* If a wrapper is being used, config user/group info */
1049 if (fcgi_wrapper) {
1050 if (user[0] == '~') {
1051 /* its a user dir uri, the rest is a username, not a uid */
1052 struct passwd *pw = getpwnam(&user[1]);
1054 if (!pw) {
1055 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1056 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1057 execName, &user[1]);
1058 goto BagNewServer;
1060 s->uid = pw->pw_uid;
1061 s->user = ap_pstrdup(sp, user);
1062 s->username = s->user;
1064 s->gid = pw->pw_gid;
1065 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
1067 else {
1068 struct passwd *pw;
1070 s->uid = (uid_t)atol(user);
1071 pw = getpwuid(s->uid);
1072 if (!pw) {
1073 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1074 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1075 execName, (long)s->uid);
1076 goto BagNewServer;
1078 s->user = ap_pstrdup(sp, user);
1079 s->username = ap_pstrdup(sp, pw->pw_name);
1081 s->gid = (gid_t)atol(group);
1082 s->group = ap_pstrdup(sp, group);
1085 #else
1086 /* Create socket file's path */
1087 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
1088 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1089 s->listenFd = 0;
1090 #endif
1092 fcgi_util_fs_add(s);
1094 else {
1095 #ifndef WIN32
1096 if (opcode == FCGI_SERVER_RESTART_JOB) {
1097 #else
1098 if (cjob->id==FCGI_SERVER_RESTART_JOB) {
1099 #endif
1100 /* Check to see if the binary has changed. If so,
1101 * kill the FCGI application processes, and
1102 * restart them.
1104 struct stat stbuf;
1105 int i;
1106 #ifdef WIN32
1107 char * app_path = cjob->fs_path;
1108 #else
1109 char * app_path = execName;
1110 #endif
1112 if (stat(app_path, &stbuf) == 0 && stbuf.st_mtime > s->startTime)
1114 int do_restart = 0;
1116 /* prevent addition restart requests */
1117 s->startTime = now;
1118 #ifndef WIN32
1119 utime(s->socket_path, NULL);
1120 #endif
1122 /* kill old server(s) */
1123 for (i = 0; i < dynamicMaxClassProcs; i++)
1125 if (s->procs[i].pid > 0
1126 && stbuf.st_mtime > s->procs[i].start_time)
1128 fcgi_kill(&s->procs[i], SIGTERM);
1129 do_restart++;
1133 if (do_restart)
1135 ap_log_error(FCGI_LOG_WARN_NOERRNO,
1136 fcgi_apache_main_server, "FastCGI: restarting "
1137 "old server \"%s\" processes, newer version "
1138 "found", app_path);
1142 /* If dynamicAutoRestart, don't mark any new processes
1143 * for starting because we probably got the
1144 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1145 * will be restarting all of those we just killed.
1147 if (dynamicAutoRestart)
1148 goto NextJob;
1150 #ifndef WIN32
1151 else if (opcode == FCGI_SERVER_START_JOB) {
1152 #else
1153 else if (cjob->id==FCGI_SERVER_START_JOB) {
1154 #endif
1155 /* we've been asked to start a process--only start
1156 * it if we're not already running at least one
1157 * instance.
1159 int i;
1161 for (i = 0; i < dynamicMaxClassProcs; i++) {
1162 if (s->procs[i].state == FCGI_RUNNING_STATE)
1163 break;
1165 /* if already running, don't start another one */
1166 if (i < dynamicMaxClassProcs) {
1167 goto NextJob;
1172 #ifndef WIN32
1173 switch (opcode)
1174 #else
1175 switch (cjob->id)
1176 #endif
1178 int i, start;
1180 case FCGI_SERVER_RESTART_JOB:
1182 start = FALSE;
1184 /* We just waxed 'em all. Try to find an idle slot. */
1186 for (i = 0; i < dynamicMaxClassProcs; ++i)
1188 if (s->procs[i].state == FCGI_START_STATE
1189 || s->procs[i].state == FCGI_RUNNING_STATE)
1191 break;
1193 else if (s->procs[i].state == FCGI_KILLED_STATE
1194 || s->procs[i].state == FCGI_READY_STATE)
1196 start = TRUE;
1197 break;
1201 /* Nope, just use the first slot */
1202 if (i == dynamicMaxClassProcs)
1204 start = TRUE;
1205 i = 0;
1208 if (start)
1210 schedule_start(s, i);
1213 break;
1215 case FCGI_SERVER_START_JOB:
1216 case FCGI_REQUEST_TIMEOUT_JOB:
1218 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1220 * Extra instances should have been
1221 * terminated beforehand, probably need
1222 * to increase ProcessSlack parameter
1224 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1225 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1226 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1227 goto NextJob;
1230 /* find next free slot */
1231 for (i = 0; i < dynamicMaxClassProcs; i++)
1233 if (s->procs[i].state == FCGI_START_STATE)
1235 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1236 break;
1238 else if (s->procs[i].state == FCGI_RUNNING_STATE)
1240 continue;
1243 schedule_start(s, i);
1244 break;
1247 #ifdef FCGI_DEBUG
1248 if (i >= dynamicMaxClassProcs) {
1249 FCGIDBG1("ignore_job: slots are max'd");
1251 #endif
1252 break;
1253 case FCGI_REQUEST_COMPLETE_JOB:
1254 /* only record stats if we have a structure */
1255 if (s) {
1256 #ifndef WIN32
1257 s->totalConnTime += req_usec;
1258 s->totalQueueTime += q_usec;
1259 #else
1260 s->totalConnTime += cjob->start_time;
1261 s->totalQueueTime += cjob->qsec;
1262 #endif
1264 break;
1267 NextJob:
1269 #ifdef WIN32
1270 /* Cleanup job data */
1271 free(cjob->fs_path);
1272 free(cjob->user);
1273 free(cjob->group);
1274 free(cjob);
1275 cjob = joblist;
1276 #endif
1278 continue;
1280 BagNewServer:
1281 if (sp) ap_destroy_pool(sp);
1283 #ifdef WIN32
1284 free(cjob->fs_path);
1285 free(cjob);
1286 cjob = joblist;
1287 #endif
1290 #ifndef WIN32
1291 if (ptr1 == buf) {
1292 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1293 "FastCGI: really bogus message: \"%s\"", ptr1);
1294 ptr1 += strlen(buf);
1297 buflen -= ptr1 - buf;
1298 if (buflen) {
1299 memmove(buf, ptr1, buflen);
1301 #endif
1303 ap_destroy_pool(tp);
1307 *----------------------------------------------------------------------
1309 * dynamic_kill_idle_fs_procs
1311 * Implement a kill policy for the dynamic FastCGI applications.
1312 * We also update the data structures to reflect the changes.
1314 * Side effects:
1315 * Processes are marked for deletion possibly killed.
1317 *----------------------------------------------------------------------
1319 static void dynamic_kill_idle_fs_procs(void)
1321 fcgi_server *s;
1322 int victims = 0;
1324 for (s = fcgi_servers; s != NULL; s = s->next)
1327 * server's smoothed running time, or if that's 0, the current total
1329 unsigned long connTime;
1332 * maximum number of microseconds that all of a server's running
1333 * processes together could have spent running since the last check
1335 unsigned long totalTime;
1338 * percentage, 0-100, of totalTime that the processes actually used
1340 int loadFactor;
1342 int i;
1343 int really_running = 0;
1345 if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1347 continue;
1350 /* s->numProcesses includes pending kills so get the "active" count */
1351 for (i = 0; i < dynamicMaxClassProcs; ++i)
1353 if (s->procs[i].state == FCGI_RUNNING_STATE) ++really_running;
1356 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1357 totalTime = really_running * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1359 loadFactor = 100 * connTime / totalTime;
1361 if (really_running == 1)
1363 if (loadFactor >= dynamicThreshold1)
1365 continue;
1368 else
1370 int load = really_running / ( really_running - 1) * loadFactor;
1372 if (load >= dynamicThresholdN)
1374 continue;
1379 * Run through the procs to see if we can get away w/o waxing one.
1381 for (i = 0; i < dynamicMaxClassProcs; ++i)
1383 if (s->procs[i].state == FCGI_START_STATE)
1385 s->procs[i].state = FCGI_READY_STATE;
1386 break;
1388 else if (s->procs[i].state == FCGI_VICTIM_STATE)
1390 break;
1394 if (i >= dynamicMaxClassProcs)
1396 ServerProcess * procs = s->procs;
1397 int youngest = -1;
1399 for (i = 0; i < dynamicMaxClassProcs; ++i)
1401 if (procs[i].state == FCGI_RUNNING_STATE)
1403 if (youngest == -1 || procs[i].start_time >= procs[youngest].start_time)
1405 youngest = i;
1410 if (youngest != -1)
1412 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1413 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1414 s->fs_path, (long) s->procs[youngest].pid);
1416 fcgi_kill(&s->procs[youngest], SIGTERM);
1418 victims++;
1422 * If the number of non-victims is less than or equal to
1423 * the minimum that may be running without being killed off,
1424 * don't select any more victims.
1426 if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1428 break;
1434 #ifdef WIN32
1436 // This is a little bogus, there's gotta be a better way to do this
1437 // Can we use WaitForMultipleObjects()
1438 #define FCGI_PROC_WAIT_TIME 100
1440 void child_wait_thread_main(void *dummy) {
1441 fcgi_server *s;
1442 DWORD dwRet = WAIT_TIMEOUT;
1443 int numChildren;
1444 int i;
1445 int waited;
1447 while (!bTimeToDie) {
1448 waited = 0;
1450 for (s = fcgi_servers; s != NULL; s = s->next) {
1451 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1452 continue;
1454 if (s->directive == APP_CLASS_DYNAMIC) {
1455 numChildren = dynamicMaxClassProcs;
1457 else {
1458 numChildren = s->numProcesses;
1461 for (i=0; i < numChildren; i++) {
1462 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1464 DWORD exitStatus = 0;
1466 /* timeout is currently set for 100 miliecond */
1467 /* it may need to be longer or user customizable */
1468 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1470 waited = 1;
1472 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1473 /* a child fs has died */
1474 /* mark the child as dead */
1476 if (s->directive == APP_CLASS_STANDARD) {
1477 /* restart static app */
1478 s->procs[i].state = FCGI_START_STATE;
1479 s->numFailures++;
1481 else {
1482 s->numProcesses--;
1483 fcgi_dynamic_total_proc_count--;
1484 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1486 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1487 s->procs[i].state = FCGI_KILLED_STATE;
1489 else {
1490 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1491 s->numFailures++;
1493 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1494 s->procs[i].state = FCGI_START_STATE;
1496 else {
1497 s->procs[i].state = FCGI_READY_STATE;
1502 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1504 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1505 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1506 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1507 s->fs_path, (long) s->procs[i].pid, exitStatus);
1509 CloseHandle(s->procs[i].handle);
1510 s->procs[i].handle = INVALID_HANDLE_VALUE;
1511 s->procs[i].pid = -1;
1513 /* wake up the main thread */
1514 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1519 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1522 #endif
1524 #ifndef WIN32
1525 static void setup_signals(void)
1527 struct sigaction sa;
1529 /* Setup handlers */
1531 sa.sa_handler = signal_handler;
1532 sigemptyset(&sa.sa_mask);
1533 sa.sa_flags = 0;
1535 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1536 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1537 "sigaction(SIGTERM) failed");
1539 /* httpd restart */
1540 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1541 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1542 "sigaction(SIGHUP) failed");
1544 /* httpd graceful restart */
1545 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1546 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1547 "sigaction(SIGUSR1) failed");
1549 /* read messages from request handlers - kill interval expired */
1550 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1551 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1552 "sigaction(SIGALRM) failed");
1554 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1555 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1556 "sigaction(SIGCHLD) failed");
1559 #endif
1561 #if !defined(WIN32) && !defined(APACHE2)
1562 int fcgi_pm_main(void *dummy, child_info *info)
1563 #else
1564 void fcgi_pm_main(void *dummy)
1565 #endif
1567 fcgi_server *s;
1568 unsigned int i;
1569 int read_ready = 0;
1570 int alarmLeft = 0;
1572 #ifdef WIN32
1573 DWORD dwRet;
1574 HANDLE child_wait_thread = INVALID_HANDLE_VALUE;
1575 #else
1576 int callWaitPid, callDynamicProcs;
1577 #endif
1579 #ifdef WIN32
1580 // Add SystemRoot to the dynamic environment
1581 char ** envp = dynamicEnvp;
1582 for (i = 0; *envp; ++i) {
1583 ++envp;
1585 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1587 #else
1589 reduce_privileges();
1590 change_process_name("fcgi-pm");
1592 close(fcgi_pm_pipe[1]);
1593 setup_signals();
1595 if (fcgi_wrapper) {
1596 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1597 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1599 #endif
1601 /* Initialize AppClass */
1602 for (s = fcgi_servers; s != NULL; s = s->next)
1604 if (s->directive != APP_CLASS_STANDARD)
1605 continue;
1607 #ifdef WIN32
1608 if (s->socket_path)
1609 s->listenFd = 0;
1610 #endif
1612 for (i = 0; i < s->numProcesses; ++i)
1613 s->procs[i].state = FCGI_START_STATE;
1616 #ifdef WIN32
1617 child_wait_thread = (HANDLE) _beginthread(child_wait_thread_main, 0, NULL);
1619 if (child_wait_thread == (HANDLE) -1)
1621 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1622 "FastCGI: failed to create process manager's wait thread!");
1625 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1626 "FastCGI: process manager initialized");
1627 #else
1628 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1629 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1630 #endif
1632 now = time(NULL);
1635 * Loop until SIGTERM
1637 for (;;) {
1638 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1639 #ifdef WIN32
1640 time_t expire;
1641 #else
1642 pid_t childPid;
1643 int waitStatus;
1644 #endif
1645 unsigned int numChildren;
1648 * If we came out of sigsuspend() for any reason other than
1649 * SIGALRM, pick up where we left off.
1651 if (alarmLeft)
1652 sleepSeconds = alarmLeft;
1655 * Examine each configured AppClass for a process that needs
1656 * starting. Compute the earliest time when the start should
1657 * be attempted, starting it now if the time has passed. Also,
1658 * remember that we do NOT need to restart externally managed
1659 * FastCGI applications.
1661 for (s = fcgi_servers; s != NULL; s = s->next)
1663 if (s->directive == APP_CLASS_EXTERNAL)
1664 continue;
1666 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1667 ? dynamicMaxClassProcs
1668 : s->numProcesses;
1670 for (i = 0; i < numChildren; ++i)
1672 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1674 int restart = (s->procs[i].pid < 0);
1675 time_t restartTime = s->restartTime;
1677 if (s->bad)
1679 /* we've gone to using the badDelay, the only thing that
1680 resets bad is when badDelay has expired. but numFailures
1681 is only just set below its threshold. the proc's
1682 start_times are all reset when the bad is. the numFailures
1683 is reset when we see an app run for a period */
1685 s->procs[i].start_time = 0;
1688 if (s->numFailures > MAX_FAILED_STARTS)
1690 time_t last_start_time = s->procs[i].start_time;
1692 if (last_start_time && now - last_start_time > RUNTIME_SUCCESS_INTERVAL)
1694 s->bad = 0;
1695 s->numFailures = 0;
1696 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1697 "FastCGI:%s server \"%s\" has remained"
1698 " running for more than %d seconds, its restart"
1699 " interval has been restored to %d seconds",
1700 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1701 s->fs_path, RUNTIME_SUCCESS_INTERVAL, s->restartDelay);
1703 else
1705 unsigned int j;
1707 for (j = 0; j < numChildren; ++j)
1709 if (s->procs[j].pid <= 0) continue;
1710 if (s->procs[j].state != FCGI_RUNNING_STATE) continue;
1711 if (s->procs[j].start_time == 0) continue;
1712 if (now - s->procs[j].start_time > RUNTIME_SUCCESS_INTERVAL) break;
1715 if (j >= numChildren)
1717 s->bad = 1;
1718 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1719 "FastCGI:%s server \"%s\" has failed to remain"
1720 " running for %d seconds given %d attempts, its restart"
1721 " interval has been backed off to %d seconds",
1722 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1723 s->fs_path, RUNTIME_SUCCESS_INTERVAL, MAX_FAILED_STARTS,
1724 FAILED_STARTS_DELAY);
1726 else
1728 s->bad = 0;
1729 s->numFailures = 0;
1730 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1731 "FastCGI:%s server \"%s\" has remained"
1732 " running for more than %d seconds, its restart"
1733 " interval has been restored to %d seconds",
1734 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1735 s->fs_path, RUNTIME_SUCCESS_INTERVAL, s->restartDelay);
1740 if (s->bad)
1742 restartTime += FAILED_STARTS_DELAY;
1744 else
1746 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1749 if (restartTime <= now)
1751 if (s->bad)
1753 s->bad = 0;
1754 s->numFailures = MAX_FAILED_STARTS;
1757 if (s->listenFd < 0 && init_listen_sock(s))
1759 if (sleepSeconds > s->initStartDelay)
1760 sleepSeconds = s->initStartDelay;
1761 break;
1763 #ifndef WIN32
1764 if (caughtSigTerm) {
1765 goto ProcessSigTerm;
1767 #endif
1768 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1769 if (s->procs[i].pid <= 0) {
1770 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1771 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1772 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1773 s->fs_path);
1775 sleepSeconds = min(sleepSeconds,
1776 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1778 ap_assert(s->procs[i].pid < 0);
1779 break;
1782 s->procs[i].start_time = now;
1783 s->restartTime = now;
1785 if (s->startTime == 0) {
1786 s->startTime = now;
1789 if (s->directive == APP_CLASS_DYNAMIC) {
1790 s->numProcesses++;
1791 fcgi_dynamic_total_proc_count++;
1792 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1795 s->procs[i].state = FCGI_RUNNING_STATE;
1797 if (fcgi_wrapper) {
1798 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1799 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1800 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1801 s->fs_path, (long) s->uid, (long) s->gid,
1802 restart ? "re" : "", (long) s->procs[i].pid);
1804 else {
1805 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1806 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1807 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1808 s->fs_path, restart ? "re" : "", (long) s->procs[i].pid);
1810 ap_assert(s->procs[i].pid > 0);
1811 } else {
1812 sleepSeconds = min(sleepSeconds, restartTime - now);
1818 #ifndef WIN32
1820 if(caughtSigTerm) {
1821 goto ProcessSigTerm;
1823 if((!caughtSigChld) && (!caughtSigAlarm)) {
1824 fd_set rfds;
1826 alarm(sleepSeconds);
1828 FD_ZERO(&rfds);
1829 FD_SET(fcgi_pm_pipe[0], &rfds);
1830 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1832 alarmLeft = alarm(0);
1834 callWaitPid = caughtSigChld;
1835 caughtSigChld = FALSE;
1836 callDynamicProcs = caughtSigAlarm;
1837 caughtSigAlarm = FALSE;
1839 now = time(NULL);
1842 * Dynamic fcgi process management
1844 if((callDynamicProcs) || (!callWaitPid)) {
1845 dynamic_read_msgs(read_ready);
1846 if(fcgi_dynamic_epoch == 0) {
1847 fcgi_dynamic_epoch = now;
1849 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1850 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1851 dynamic_kill_idle_fs_procs();
1852 fcgi_dynamic_epoch = now;
1856 if(!callWaitPid) {
1857 continue;
1860 /* We've caught SIGCHLD, so find out who it was using waitpid,
1861 * write a log message and update its data structure. */
1863 for (;;) {
1864 if (caughtSigTerm)
1865 goto ProcessSigTerm;
1867 childPid = waitpid(-1, &waitStatus, WNOHANG);
1869 if (childPid == -1 || childPid == 0)
1870 break;
1872 for (s = fcgi_servers; s != NULL; s = s->next) {
1873 if (s->directive == APP_CLASS_EXTERNAL)
1874 continue;
1876 if (s->directive == APP_CLASS_DYNAMIC)
1877 numChildren = dynamicMaxClassProcs;
1878 else
1879 numChildren = s->numProcesses;
1881 for (i = 0; i < numChildren; i++) {
1882 if (s->procs[i].pid == childPid)
1883 goto ChildFound;
1887 /* TODO: print something about this unknown child */
1888 continue;
1890 ChildFound:
1891 s->procs[i].pid = -1;
1893 if (s->directive == APP_CLASS_STANDARD) {
1894 /* Always restart static apps */
1895 s->procs[i].state = FCGI_START_STATE;
1896 s->numFailures++;
1898 else {
1899 s->numProcesses--;
1900 fcgi_dynamic_total_proc_count--;
1902 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1903 s->procs[i].state = FCGI_KILLED_STATE;
1905 else {
1906 /* A dynamic app died or exited without provocation from the PM */
1907 s->numFailures++;
1909 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1910 s->procs[i].state = FCGI_START_STATE;
1911 else
1912 s->procs[i].state = FCGI_READY_STATE;
1916 if (WIFEXITED(waitStatus)) {
1917 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1918 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1919 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1920 s->fs_path, (long) childPid, WEXITSTATUS(waitStatus));
1922 else if (WIFSIGNALED(waitStatus)) {
1923 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1924 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1925 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1926 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus),
1927 #ifdef WCOREDUMP
1928 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1929 #else
1930 "");
1931 #endif
1933 else if (WIFSTOPPED(waitStatus)) {
1934 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1935 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1936 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1937 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus));
1939 } /* for (;;), waitpid() */
1941 #else /* WIN32 */
1943 /* wait for an event to occur or timer expires */
1944 expire = time(NULL) + sleepSeconds;
1945 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1947 if (dwRet == WAIT_FAILED) {
1948 /* There is something seriously wrong here */
1949 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1950 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1951 bTimeToDie = TRUE;
1954 if (dwRet != WAIT_TIMEOUT) {
1955 now = time(NULL);
1957 if (now < expire)
1958 alarmLeft = expire - now;
1962 * Dynamic fcgi process management
1964 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1965 if (dwRet == MBOX_EVENT) {
1966 read_ready = 1;
1969 now = time(NULL);
1971 dynamic_read_msgs(read_ready);
1973 if(fcgi_dynamic_epoch == 0) {
1974 fcgi_dynamic_epoch = now;
1977 if ((now-fcgi_dynamic_epoch >= (int) dynamicKillInterval) ||
1978 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1979 dynamic_kill_idle_fs_procs();
1980 fcgi_dynamic_epoch = now;
1982 read_ready = 0;
1984 else if (dwRet == WAKE_EVENT) {
1985 continue;
1987 else if (dwRet == TERM_EVENT) {
1988 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1989 "FastCGI: Termination event received process manager shutting down");
1991 bTimeToDie = TRUE;
1992 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
1994 goto ProcessSigTerm;
1996 else {
1997 // Have an received an unknown event - should not happen
1998 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1999 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
2001 bTimeToDie = TRUE;
2002 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2004 goto ProcessSigTerm;
2007 #endif /* WIN32 */
2009 } /* for (;;), the whole shoot'n match */
2011 ProcessSigTerm:
2013 * Kill off the children, then exit.
2015 shutdown_all();
2017 #ifdef WIN32
2018 return;
2019 #else
2020 exit(0);
2021 #endif
2024 #ifdef WIN32
2025 int fcgi_pm_add_job(fcgi_pm_job *new_job)
2027 int rv = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
2029 if (rv != WAIT_OBJECT_0 && rv != WAIT_ABANDONED)
2031 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2032 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2033 return -1;
2036 new_job->next = fcgi_dynamic_mbox;
2037 fcgi_dynamic_mbox = new_job;
2039 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
2041 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2042 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
2045 return 0;
2047 #endif