Don't count an application exit towards the number of
[mod_fastcgi.git] / fcgi_pm.c
blobc6148040572849e26458da3d0450d70f3f100e43
1 /*
2 * $Id: fcgi_pm.c,v 1.92 2004/04/15 01:23:05 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 #ifdef APACHE2
34 #include "mod_cgi.h"
35 #endif
36 #pragma warning ( disable : 4100 4102 )
37 static BOOL bTimeToDie = FALSE; /* process termination flag */
38 HANDLE fcgi_event_handles[3];
39 #ifndef SIGKILL
40 #define SIGKILL 9
41 #endif
42 #endif
45 #ifndef WIN32
46 static int seteuid_root(void)
48 int rc = seteuid(getuid());
49 if (rc) {
50 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
51 "FastCGI: seteuid(0) failed");
53 return rc;
56 static int seteuid_user(void)
58 int rc = seteuid(ap_user_id);
59 if (rc) {
60 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
61 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id);
63 return rc;
65 #endif
68 * Signal the process to exit. How (or if) the process responds
69 * depends on the FastCGI application library (esp. on Win32) and
70 * possibly application code (signal handlers and whether or not
71 * SA_RESTART is on). At any rate, we send the signal with the
72 * hopes that the process will exit on its own. Later, as we
73 * review the state of application processes, if we see one marked
74 * for death, but that hasn't died within a specified period of
75 * time, fcgi_kill() is called again with a KILL)
77 static void fcgi_kill(ServerProcess *process, int sig)
79 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process->pid, sig);
81 process->state = FCGI_VICTIM_STATE;
83 #ifdef WIN32
85 if (sig == SIGTERM)
87 SetEvent(process->terminationEvent);
89 else if (sig == SIGKILL)
91 TerminateProcess(process->handle, 1);
93 else
95 ap_assert(0);
98 #else /* !WIN32 */
100 if (fcgi_wrapper)
102 seteuid_root();
105 kill(process->pid, sig);
107 if (fcgi_wrapper)
109 seteuid_user();
112 #endif /* !WIN32 */
115 /*******************************************************************************
116 * Send SIGTERM to each process in the server class, remove socket
117 * file if appropriate. Currently this is only called when the PM is shutting
118 * down and thus memory isn't freed and sockets and files aren't closed.
120 static void shutdown_all()
122 fcgi_server *s = fcgi_servers;
124 while (s)
126 ServerProcess *proc = s->procs;
127 int i;
128 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
129 ? dynamicMaxClassProcs
130 : s->numProcesses;
132 #ifndef WIN32
133 if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL)
135 /* Remove the socket file */
136 if (unlink(s->socket_path) != 0 && errno != ENOENT) {
137 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
138 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
139 s->socket_path,
140 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path);
143 #endif
145 /* Send TERM to all processes */
146 for (i = 0; i < numChildren; i++, proc++)
148 if (proc->state == FCGI_RUNNING_STATE)
150 fcgi_kill(proc, SIGTERM);
154 s = s->next;
157 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
160 * WIN32 applications may not have support for the shutdown event
161 * depending on their application library version
164 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT);
165 s = fcgi_servers;
167 while (s)
169 ServerProcess *proc = s->procs;
170 int i;
171 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
172 ? dynamicMaxClassProcs
173 : s->numProcesses;
175 /* Send KILL to all processes */
176 for (i = 0; i < numChildren; i++, proc++)
178 if (proc->state == FCGI_RUNNING_STATE)
180 fcgi_kill(proc, SIGKILL);
184 s = s->next;
187 #endif /* WIN32 */
190 static int init_listen_sock(fcgi_server * fs)
192 ap_assert(fs->directive != APP_CLASS_EXTERNAL);
194 /* Create the socket */
195 if ((fs->listenFd = socket(fs->socket_addr->sa_family, SOCK_STREAM, 0)) < 0)
197 #ifdef WIN32
198 errno = WSAGetLastError(); /* Not sure if this will work as expected */
199 #endif
200 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
201 "FastCGI: can't create %sserver \"%s\": socket() failed",
202 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
203 fs->fs_path);
204 return -1;
207 #ifndef WIN32
208 if (fs->socket_addr->sa_family == AF_UNIX)
210 /* Remove any existing socket file.. just in case */
211 unlink(((struct sockaddr_un *)fs->socket_addr)->sun_path);
213 else
214 #endif
216 int flag = 1;
217 setsockopt(fs->listenFd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
220 /* Bind it to the socket_addr */
221 if (bind(fs->listenFd, fs->socket_addr, fs->socket_addr_len))
223 char port[11];
225 #ifdef WIN32
226 errno = WSAGetLastError();
227 #endif
228 ap_snprintf(port, sizeof(port), "port=%d",
229 ((struct sockaddr_in *)fs->socket_addr)->sin_port);
231 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
232 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
233 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
234 fs->fs_path,
235 #ifndef WIN32
236 (fs->socket_addr->sa_family == AF_UNIX) ?
237 ((struct sockaddr_un *)fs->socket_addr)->sun_path :
238 #endif
239 port);
242 #ifndef WIN32
243 /* Twiddle Unix socket permissions */
244 else if (fs->socket_addr->sa_family == AF_UNIX
245 && chmod(((struct sockaddr_un *)fs->socket_addr)->sun_path, S_IRUSR | S_IWUSR))
247 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
248 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
249 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
250 fs->fs_path);
252 #endif
254 /* Set to listen */
255 else if (listen(fs->listenFd, fs->listenQueueDepth))
257 #ifdef WIN32
258 errno = WSAGetLastError();
259 #endif
260 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
261 "FastCGI: can't create %sserver \"%s\": listen() failed",
262 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
263 fs->fs_path);
265 else
267 return 0;
270 #ifdef WIN32
271 closesocket(fs->listenFd);
272 #else
273 close(fs->listenFd);
274 #endif
276 fs->listenFd = -1;
278 return -2;
282 *----------------------------------------------------------------------
284 * pm_main
286 * The FastCGI process manager, which runs as a separate
287 * process responsible for:
288 * - Starting all the FastCGI proceses.
289 * - Restarting any of these processes that die (indicated
290 * by SIGCHLD).
291 * - Catching SIGTERM and relaying it to all the FastCGI
292 * processes before exiting.
294 * Inputs:
295 * Uses global variable fcgi_servers.
297 * Results:
298 * Does not return.
300 * Side effects:
301 * Described above.
303 *----------------------------------------------------------------------
305 #ifndef WIN32
306 static int caughtSigTerm = FALSE;
307 static int caughtSigChld = FALSE;
308 static int caughtSigAlarm = FALSE;
310 static void signal_handler(int signo)
312 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
313 /* SIGUSR1 & SIGHUP are sent by apache to its process group
314 * when apache get 'em. Apache follows up (1.2.x) with attacks
315 * on each of its child processes, but we've got the KillMgr
316 * sitting between us so we never see the KILL. The main loop
317 * in ProcMgr also checks to see if the KillMgr has terminated,
318 * and if it has, we handl it as if we should shutdown too. */
319 caughtSigTerm = TRUE;
320 } else if(signo == SIGCHLD) {
321 caughtSigChld = TRUE;
322 } else if(signo == SIGALRM) {
323 caughtSigAlarm = TRUE;
326 #endif
329 *----------------------------------------------------------------------
331 * spawn_fs_process --
333 * Fork and exec the specified fcgi process.
335 * Results:
336 * 0 for successful fork, -1 for failed fork.
338 * In case the child fails before or in the exec, the child
339 * obtains the error log by calling getErrLog, logs
340 * the error, and exits with exit status = errno of
341 * the failed system call.
343 * Side effects:
344 * Child process created.
346 *----------------------------------------------------------------------
348 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
350 #ifndef WIN32
352 pid_t child_pid;
353 int i;
354 char *dirName;
355 char *dnEnd, *failedSysCall;
357 child_pid = fork();
358 if (child_pid) {
359 return child_pid;
362 /* We're the child. We're gonna exec() so pools don't matter. */
364 dnEnd = strrchr(fs->fs_path, '/');
365 if (dnEnd == NULL) {
366 dirName = "./";
367 } else {
368 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
369 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
371 if (chdir(dirName) < 0) {
372 failedSysCall = "chdir()";
373 goto FailedSystemCallExit;
376 #ifndef __EMX__
377 /* OS/2 dosen't support nice() */
378 if (fs->processPriority != 0) {
379 if (nice(fs->processPriority) == -1) {
380 failedSysCall = "nice()";
381 goto FailedSystemCallExit;
384 #endif
386 /* Open the listenFd on spec'd fd */
387 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
388 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
390 /* Close all other open fds, except stdout/stderr. Leave these two open so
391 * FastCGI applications don't have to find and fix ALL 3rd party libs that
392 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
393 * main server error_log - @@@ provide a directive control where this goes.
395 ap_error_log2stderr(fcgi_apache_main_server);
396 dup2(2, 1);
397 for (i = 0; i < FCGI_MAX_FD; i++) {
398 if (i != FCGI_LISTENSOCK_FILENO && i != 2 && i != 1) {
399 close(i);
403 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
404 * install its own handler. */
405 signal(SIGPIPE, SIG_IGN);
407 /* Apache (2 anyway) doesn't use suexec if there is no user/group in
408 * effect - this translates to a uid/gid of 0/0 (which should never
409 * be a valid uid/gid for a suexec invocation so it should be safe */
410 if (fcgi_wrapper && fs->uid && fs->gid)
412 char *shortName;
414 /* Relinquish our root real uid powers */
415 seteuid_root();
416 setuid(ap_user_id);
418 /* AP13 does not use suexec if the target uid/gid is the same as the
419 * server's - AP20 does. I (now) consider the AP2 approach better
420 * (fcgi_pm.c v1.42 incorporated the 1.3 behaviour, v1.84 reverted it,
421 * v1.85 added the compile time option to use the old behaviour). */
423 #ifdef NO_SUEXEC_FOR_AP_USER_N_GROUP
425 if (fcgi_user_id == fs->uid && fcgi_group_id == fs->gid)
427 goto NO_SUEXEC;
430 #endif
431 shortName = strrchr(fs->fs_path, '/') + 1;
433 do {
434 execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group,
435 shortName, NULL, fs->envp);
436 } while (errno == EINTR);
438 else
441 NO_SUEXEC:
442 do {
443 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
444 } while (errno == EINTR);
447 failedSysCall = "execle()";
449 FailedSystemCallExit:
450 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
451 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
452 exit(-1);
454 /* avoid an irrelevant compiler warning */
455 return(0);
457 #else /* WIN32 */
459 #ifdef APACHE2
461 /* based on mod_cgi.c:run_cgi_child() */
463 apr_pool_t * tp;
464 char * termination_env_string;
465 HANDLE listen_handle = INVALID_HANDLE_VALUE;
466 apr_procattr_t * procattr;
467 apr_proc_t proc = { 0 };
468 apr_file_t * file;
469 int i = 0;
470 cgi_exec_info_t e_info = { 0 };
471 request_rec r = { 0 };
472 const char *command;
473 const char **argv;
474 int rv;
475 APR_OPTIONAL_FN_TYPE(ap_cgi_build_command) *cgi_build_command;
477 cgi_build_command = APR_RETRIEVE_OPTIONAL_FN(ap_cgi_build_command);
478 if (cgi_build_command == NULL)
480 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
481 "FastCGI: can't exec server \"%s\", mod_cgi isn't loaded",
482 fs->fs_path);
483 return 0;
486 if (apr_pool_create(&tp, fcgi_config_pool))
487 return 0;
489 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
490 if (process->terminationEvent == NULL)
491 goto CLEANUP;
493 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
495 termination_env_string = ap_psprintf(tp,
496 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
498 while (fs->envp[i]) i++;
499 fs->envp[i++] = termination_env_string;
500 fs->envp[i] = (char *) fs->mutex_env_string;
502 ap_assert(fs->envp[i + 1] == NULL);
504 if (fs->socket_path)
506 SECURITY_ATTRIBUTES sa = { 0 };
508 sa.bInheritHandle = TRUE;
509 sa.nLength = sizeof(sa);
511 listen_handle = CreateNamedPipe(fs->socket_path,
512 PIPE_ACCESS_DUPLEX,
513 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
514 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
516 if (listen_handle == INVALID_HANDLE_VALUE)
518 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
519 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
520 fs->fs_path);
521 goto CLEANUP;
524 else
526 listen_handle = (HANDLE) fs->listenFd;
529 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
530 r.server = fcgi_apache_main_server;
531 r.filename = (char *) fs->fs_path;
532 r.pool = tp;
533 r.subprocess_env = apr_table_make(tp, 0);
535 e_info.cmd_type = APR_PROGRAM;
537 rv = cgi_build_command(&command, &argv, &r, tp, &e_info);
538 if (rv != APR_SUCCESS)
540 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
541 "FastCGI: don't know how to spawn cmd child process: %s",
542 fs->fs_path);
543 goto CLEANUP;
546 if (apr_procattr_create(&procattr, tp))
547 goto CLEANUP;
549 if (apr_procattr_dir_set(procattr, ap_make_dirstr_parent(tp, fs->fs_path)))
550 goto CLEANUP;
552 if (apr_procattr_cmdtype_set(procattr, e_info.cmd_type))
553 goto CLEANUP;
555 if (apr_procattr_detach_set(procattr, 1))
556 goto CLEANUP;
558 if (apr_os_file_put(&file, &listen_handle, 0, tp))
559 goto CLEANUP;
561 /* procattr is opaque so we have to use this - unfortuantely it dups */
562 if (apr_procattr_child_in_set(procattr, file, NULL))
563 goto CLEANUP;
565 if (apr_proc_create(&proc, command, argv, fs->envp, procattr, tp))
566 goto CLEANUP;
568 process->handle = proc.hproc;
570 CLEANUP:
572 if (fs->socket_path && listen_handle != INVALID_HANDLE_VALUE)
574 CloseHandle(listen_handle);
577 if (i)
579 fs->envp[i - 1] = NULL;
582 ap_destroy_pool(tp);
584 return proc.pid;
586 #else /* WIN32 && !APACHE2 */
588 /* Adapted from Apache's util_script.c ap_call_exec() */
589 char *interpreter = NULL;
590 char *quoted_filename;
591 char *pCommand;
592 char *pEnvBlock, *pNext;
594 int i = 0;
595 int iEnvBlockLen = 1;
597 file_type_e fileType;
599 STARTUPINFO si;
600 PROCESS_INFORMATION pi;
602 request_rec r;
603 pid_t pid = -1;
605 pool * tp = ap_make_sub_pool(fcgi_config_pool);
607 HANDLE listen_handle = INVALID_HANDLE_VALUE;
608 char * termination_env_string = NULL;
610 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
611 if (process->terminationEvent == NULL)
613 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
614 "FastCGI: can't create termination event for server \"%s\", "
615 "CreateEvent() failed", fs->fs_path);
616 goto CLEANUP;
618 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
620 termination_env_string = ap_psprintf(tp,
621 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
623 if (fs->socket_path)
625 SECURITY_ATTRIBUTES sa;
627 sa.lpSecurityDescriptor = NULL;
628 sa.bInheritHandle = TRUE;
629 sa.nLength = sizeof(sa);
631 listen_handle = CreateNamedPipe(fs->socket_path,
632 PIPE_ACCESS_DUPLEX,
633 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
634 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
636 if (listen_handle == INVALID_HANDLE_VALUE)
638 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
639 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
640 goto CLEANUP;
643 else
645 listen_handle = (HANDLE) fs->listenFd;
648 memset(&si, 0, sizeof(si));
649 memset(&pi, 0, sizeof(pi));
650 memset(&r, 0, sizeof(r));
652 /* Can up a fake request to pass to ap_get_win32_interpreter() */
653 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
654 r.server = fcgi_apache_main_server;
655 r.filename = (char *) fs->fs_path;
656 r.pool = tp;
658 fileType = ap_get_win32_interpreter(&r, &interpreter);
660 if (fileType == eFileTypeUNKNOWN) {
661 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
662 "FastCGI: %s is not executable; ensure interpreted scripts have "
663 "\"#!\" as their first line",
664 fs->fs_path);
665 ap_destroy_pool(tp);
666 goto CLEANUP;
670 * We have the interpreter (if there is one) and we have
671 * the arguments (if there are any).
672 * Build the command string to pass to CreateProcess.
674 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
675 if (interpreter && *interpreter) {
676 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
678 else {
679 pCommand = quoted_filename;
683 * Make child process use hPipeOutputWrite as standard out,
684 * and make sure it does not show on screen.
686 si.cb = sizeof(si);
687 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
688 si.wShowWindow = SW_HIDE;
689 si.hStdInput = listen_handle;
691 /* XXX These should be open to the error_log */
692 si.hStdOutput = INVALID_HANDLE_VALUE;
693 si.hStdError = INVALID_HANDLE_VALUE;
696 * Win32's CreateProcess call requires that the environment
697 * be passed in an environment block, a null terminated block of
698 * null terminated strings.
699 * @todo we should store the env in this format for win32.
701 while (fs->envp[i])
703 iEnvBlockLen += strlen(fs->envp[i]) + 1;
704 i++;
707 iEnvBlockLen += strlen(termination_env_string) + 1;
708 iEnvBlockLen += strlen(fs->mutex_env_string) + 1;
710 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
712 i = 0;
713 pNext = pEnvBlock;
714 while (fs->envp[i])
716 strcpy(pNext, fs->envp[i]);
717 pNext += strlen(pNext) + 1;
718 i++;
721 strcpy(pNext, termination_env_string);
722 pNext += strlen(pNext) + 1;
723 strcpy(pNext, fs->mutex_env_string);
725 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
727 pEnvBlock,
728 ap_make_dirstr_parent(tp, fs->fs_path),
729 &si, &pi))
731 /* Hack to get 16-bit CGI's working. It works for all the
732 * standard modules shipped with Apache. pi.dwProcessId is 0
733 * for 16-bit CGIs and all the Unix specific code that calls
734 * ap_call_exec interprets this as a failure case. And we can't
735 * use -1 either because it is mapped to 0 by the caller.
737 pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId;
739 process->handle = pi.hProcess;
740 CloseHandle(pi.hThread);
743 CLEANUP:
745 if (fs->socket_path && listen_handle != INVALID_HANDLE_VALUE)
747 CloseHandle(listen_handle);
750 ap_destroy_pool(tp);
752 return pid;
754 #endif /* !APACHE2 */
755 #endif /* WIN32 */
758 #ifndef WIN32
759 static void reduce_privileges(void)
761 const char *name;
763 if (geteuid() != 0)
764 return;
766 #ifndef __EMX__
767 /* Get username if passed as a uid */
768 if (ap_user_name[0] == '#') {
769 uid_t uid = atoi(&ap_user_name[1]);
770 struct passwd *ent = getpwuid(uid);
772 if (ent == NULL) {
773 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
774 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
775 "you probably need to modify the User directive", (unsigned)uid);
776 exit(1);
778 name = ent->pw_name;
780 else
781 name = ap_user_name;
783 /* Change Group */
784 if (setgid(ap_group_id) == -1) {
785 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
786 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
787 exit(1);
790 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
792 /* Initialize supplementary groups */
793 if (initgroups(name, ap_group_id) == -1) {
794 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
795 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
796 name, (unsigned)ap_group_id);
797 exit(1);
799 #endif /* __EMX__ */
801 /* Change User */
802 if (fcgi_wrapper) {
803 if (seteuid_user() == -1) {
804 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
805 "FastCGI: process manager exiting, failed to reduce privileges");
806 exit(1);
809 else {
810 if (setuid(ap_user_id) == -1) {
811 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
812 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
813 exit(1);
818 /*************
819 * Change the name of this process - best we can easily.
821 static void change_process_name(const char * const name)
823 /* under Apache2, ap_server_argv0 is const */
824 strncpy((char *) ap_server_argv0, name, strlen(ap_server_argv0));
826 #endif /* !WIN32 */
828 static void schedule_start(fcgi_server *s, int proc)
830 /* If we've started one recently, don't register another */
831 time_t time_passed = now - s->restartTime;
833 if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay))
834 || ((s->procs[proc].pid == 0) && (time_passed < s->initStartDelay)))
836 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);
837 return;
840 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
841 s->procs[proc].state = FCGI_START_STATE;
842 if (proc == dynamicMaxClassProcs - 1) {
843 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
844 "FastCGI: scheduled the %sstart of the last (dynamic) server "
845 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
846 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
851 *----------------------------------------------------------------------
853 * dynamic_read_msgs
855 * Removes the records written by request handlers and decodes them.
856 * We also update the data structures to reflect the changes.
858 *----------------------------------------------------------------------
861 static void dynamic_read_msgs(int read_ready)
863 fcgi_server *s;
864 int rc;
866 #ifndef WIN32
867 static int buflen = 0;
868 static char buf[FCGI_MSGS_BUFSIZE + 1];
869 char *ptr1, *ptr2, opcode;
870 char execName[FCGI_MAXPATH + 1];
871 char user[MAX_USER_NAME_LEN + 2];
872 char group[MAX_GID_CHAR_LEN + 1];
873 unsigned long q_usec = 0UL, req_usec = 0UL;
874 #else
875 fcgi_pm_job *joblist = NULL;
876 fcgi_pm_job *cjob = NULL;
877 #endif
879 pool *sp = NULL, *tp;
881 #ifndef WIN32
882 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
883 #endif
886 * To prevent the idle application from running indefinitely, we
887 * check the timer and if it is expired, we recompute the values
888 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
889 * message is received, only updates are made to the data structures.
891 if (fcgi_dynamic_last_analyzed == 0) {
892 fcgi_dynamic_last_analyzed = now;
894 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
895 for (s = fcgi_servers; s != NULL; s = s->next) {
896 if (s->directive != APP_CLASS_DYNAMIC)
897 break;
899 /* Advance the last analyzed timestamp by the elapsed time since
900 * it was last set. Round the increase down to the nearest
901 * multiple of dynamicUpdateInterval */
903 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
904 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
905 s->totalConnTime = 0UL;
906 s->totalQueueTime = 0UL;
910 if (read_ready <= 0) {
911 return;
914 #ifndef WIN32
915 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
916 if (rc <= 0) {
917 if (!caughtSigTerm) {
918 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
919 "FastCGI: read() from pipe failed (%d)", rc);
920 if (rc == 0) {
921 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
922 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
923 caughtSigTerm = TRUE;
926 return;
928 buflen += rc;
929 buf[buflen] = '\0';
931 #else
933 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
934 * request to do something) and/or when a timeout expires.
935 * There really should be no reason why this wait would get stuck
936 * but there's no point in waiting forever. */
938 rc = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
940 if (rc != WAIT_OBJECT_0 && rc != WAIT_ABANDONED)
942 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
943 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
944 return;
947 joblist = fcgi_dynamic_mbox;
948 fcgi_dynamic_mbox = NULL;
950 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
952 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
953 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
956 cjob = joblist;
957 #endif
959 #ifdef APACHE2
960 apr_pool_create(&tp, fcgi_config_pool);
961 #else
962 tp = ap_make_sub_pool(fcgi_config_pool);
963 #endif
965 #ifndef WIN32
966 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
967 int scan_failed = 0;
969 ptr2 = strchr(ptr1, '*');
970 if (ptr2) {
971 *ptr2++ = '\0';
973 else {
974 break;
977 opcode = *ptr1;
979 switch (opcode)
981 case FCGI_SERVER_START_JOB:
982 case FCGI_SERVER_RESTART_JOB:
984 if (sscanf(ptr1, "%c %s %16s %15s",
985 &opcode, execName, user, group) != 4)
987 scan_failed = 1;
989 break;
991 case FCGI_REQUEST_TIMEOUT_JOB:
993 if (sscanf(ptr1, "%c %s %16s %15s",
994 &opcode, execName, user, group) != 4)
996 scan_failed = 1;
998 break;
1000 case FCGI_REQUEST_COMPLETE_JOB:
1002 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
1003 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
1005 scan_failed = 1;
1007 break;
1009 default:
1011 scan_failed = 1;
1012 break;
1015 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode, execName, user, group, q_usec, req_usec);
1017 if (scan_failed) {
1018 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1019 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
1020 goto NextJob;
1022 #else
1023 /* Update data structures for processing */
1024 while (cjob != NULL) {
1025 joblist = cjob->next;
1026 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
1027 #endif
1029 #ifndef WIN32
1030 s = fcgi_util_fs_get(execName, user, group);
1031 #else
1032 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
1033 #endif
1035 #ifndef WIN32
1036 if (s==NULL && opcode != FCGI_REQUEST_COMPLETE_JOB)
1037 #else
1038 if (s==NULL && cjob->id != FCGI_REQUEST_COMPLETE_JOB)
1039 #endif
1041 #ifdef WIN32
1043 HANDLE mutex = CreateMutex(NULL, FALSE, cjob->fs_path);
1045 if (mutex == NULL)
1047 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1048 "FastCGI: can't create accept mutex "
1049 "for (dynamic) server \"%s\"", cjob->fs_path);
1050 goto BagNewServer;
1053 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
1054 #else
1055 const char *err;
1056 #endif
1058 /* Create a perm subpool to hold the new server data,
1059 * we can destroy it if something doesn't pan out */
1060 #ifdef APACHE2
1061 apr_pool_create(&sp, fcgi_config_pool);
1062 #else
1063 sp = ap_make_sub_pool(fcgi_config_pool);
1064 #endif
1066 /* Create a new "dynamic" server */
1067 s = fcgi_util_fs_new(sp);
1069 s->directive = APP_CLASS_DYNAMIC;
1070 s->restartDelay = dynamicRestartDelay;
1071 s->listenQueueDepth = dynamicListenQueueDepth;
1072 s->initStartDelay = dynamicInitStartDelay;
1073 s->envp = dynamicEnvp;
1074 s->flush = dynamicFlush;
1076 #ifdef WIN32
1077 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
1078 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
1079 #else
1080 s->fs_path = ap_pstrdup(sp, execName);
1081 #endif
1082 ap_getparents(s->fs_path);
1083 ap_no2slash(s->fs_path);
1084 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
1086 /* XXX the socket_path (both Unix and Win) *is* deducible and
1087 * thus can and will be used by other apache instances without
1088 * the use of shared data regarding the processes serving the
1089 * requests. This can result in slightly unintuitive process
1090 * counts and security implications. This is prevented
1091 * if suexec (Unix) is in use. This is both a feature and a flaw.
1092 * Changing it now would break existing installations. */
1094 #ifndef WIN32
1095 /* Create socket file's path */
1096 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
1097 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1099 /* Create sockaddr, prealloc it so it won't get created in tp */
1100 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
1101 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
1102 &s->socket_addr_len, s->socket_path);
1103 if (err) {
1104 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1105 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
1106 goto BagNewServer;
1109 if (init_listen_sock(s)) {
1110 goto BagNewServer;
1113 /* If a wrapper is being used, config user/group info */
1114 if (fcgi_wrapper) {
1115 if (user[0] == '~') {
1116 /* its a user dir uri, the rest is a username, not a uid */
1117 struct passwd *pw = getpwnam(&user[1]);
1119 if (!pw) {
1120 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1121 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1122 execName, &user[1]);
1123 goto BagNewServer;
1125 s->uid = pw->pw_uid;
1126 s->user = ap_pstrdup(sp, user);
1127 s->username = s->user;
1129 s->gid = pw->pw_gid;
1130 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
1132 else {
1133 struct passwd *pw;
1135 s->uid = (uid_t)atol(user);
1136 pw = getpwuid(s->uid);
1137 if (!pw) {
1138 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1139 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1140 execName, (long)s->uid);
1141 goto BagNewServer;
1143 s->user = ap_pstrdup(sp, user);
1144 s->username = ap_pstrdup(sp, pw->pw_name);
1146 s->gid = (gid_t)atol(group);
1147 s->group = ap_pstrdup(sp, group);
1150 #else
1151 /* Create socket file's path */
1152 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
1153 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1154 s->listenFd = 0;
1155 #endif
1157 fcgi_util_fs_add(s);
1159 else {
1160 #ifndef WIN32
1161 if (opcode == FCGI_SERVER_RESTART_JOB) {
1162 #else
1163 if (cjob->id==FCGI_SERVER_RESTART_JOB) {
1164 #endif
1165 /* Check to see if the binary has changed. If so,
1166 * kill the FCGI application processes, and
1167 * restart them.
1169 struct stat stbuf;
1170 int i;
1171 #ifdef WIN32
1172 char * app_path = cjob->fs_path;
1173 #else
1174 char * app_path = execName;
1175 #endif
1177 if (stat(app_path, &stbuf) == 0 && stbuf.st_mtime > s->startTime)
1179 int do_restart = 0;
1181 /* prevent addition restart requests */
1182 s->startTime = now;
1183 #ifndef WIN32
1184 utime(s->socket_path, NULL);
1185 #endif
1187 /* kill old server(s) */
1188 for (i = 0; i < dynamicMaxClassProcs; i++)
1190 if (s->procs[i].pid > 0
1191 && stbuf.st_mtime > s->procs[i].start_time)
1193 fcgi_kill(&s->procs[i], SIGTERM);
1194 do_restart++;
1198 if (do_restart)
1200 ap_log_error(FCGI_LOG_WARN_NOERRNO,
1201 fcgi_apache_main_server, "FastCGI: restarting "
1202 "old server \"%s\" processes, newer version "
1203 "found", app_path);
1207 /* If dynamicAutoRestart, don't mark any new processes
1208 * for starting because we probably got the
1209 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1210 * will be restarting all of those we just killed.
1212 if (dynamicAutoRestart)
1213 goto NextJob;
1215 #ifndef WIN32
1216 else if (opcode == FCGI_SERVER_START_JOB) {
1217 #else
1218 else if (cjob->id==FCGI_SERVER_START_JOB) {
1219 #endif
1220 /* we've been asked to start a process--only start
1221 * it if we're not already running at least one
1222 * instance.
1224 int i;
1226 for (i = 0; i < dynamicMaxClassProcs; i++) {
1227 if (s->procs[i].state == FCGI_RUNNING_STATE)
1228 break;
1230 /* if already running, don't start another one */
1231 if (i < dynamicMaxClassProcs) {
1232 goto NextJob;
1237 #ifndef WIN32
1238 switch (opcode)
1239 #else
1240 switch (cjob->id)
1241 #endif
1243 int i, start;
1245 case FCGI_SERVER_RESTART_JOB:
1247 start = FALSE;
1249 /* We just waxed 'em all. Try to find an idle slot. */
1251 for (i = 0; i < dynamicMaxClassProcs; ++i)
1253 if (s->procs[i].state == FCGI_START_STATE
1254 || s->procs[i].state == FCGI_RUNNING_STATE)
1256 break;
1258 else if (s->procs[i].state == FCGI_KILLED_STATE
1259 || s->procs[i].state == FCGI_READY_STATE)
1261 start = TRUE;
1262 break;
1266 /* Nope, just use the first slot */
1267 if (i == dynamicMaxClassProcs)
1269 start = TRUE;
1270 i = 0;
1273 if (start)
1275 schedule_start(s, i);
1278 break;
1280 case FCGI_SERVER_START_JOB:
1281 case FCGI_REQUEST_TIMEOUT_JOB:
1283 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1285 * Extra instances should have been
1286 * terminated beforehand, probably need
1287 * to increase ProcessSlack parameter
1289 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1290 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1291 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1292 goto NextJob;
1295 /* find next free slot */
1296 for (i = 0; i < dynamicMaxClassProcs; i++)
1298 if (s->procs[i].state == FCGI_START_STATE)
1300 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1301 break;
1303 else if (s->procs[i].state == FCGI_RUNNING_STATE)
1305 continue;
1308 schedule_start(s, i);
1309 break;
1312 #ifdef FCGI_DEBUG
1313 if (i >= dynamicMaxClassProcs) {
1314 FCGIDBG1("ignore_job: slots are max'd");
1316 #endif
1317 break;
1318 case FCGI_REQUEST_COMPLETE_JOB:
1319 /* only record stats if we have a structure */
1320 if (s) {
1321 #ifndef WIN32
1322 s->totalConnTime += req_usec;
1323 s->totalQueueTime += q_usec;
1324 #else
1325 s->totalConnTime += cjob->start_time;
1326 s->totalQueueTime += cjob->qsec;
1327 #endif
1329 break;
1332 NextJob:
1334 #ifdef WIN32
1335 /* Cleanup job data */
1336 free(cjob->fs_path);
1337 free(cjob->user);
1338 free(cjob->group);
1339 free(cjob);
1340 cjob = joblist;
1341 #endif
1343 continue;
1345 BagNewServer:
1346 if (sp) ap_destroy_pool(sp);
1348 #ifdef WIN32
1349 free(cjob->fs_path);
1350 free(cjob);
1351 cjob = joblist;
1352 #endif
1355 #ifndef WIN32
1356 if (ptr1 == buf) {
1357 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1358 "FastCGI: really bogus message: \"%s\"", ptr1);
1359 ptr1 += strlen(buf);
1362 buflen -= ptr1 - buf;
1363 if (buflen) {
1364 memmove(buf, ptr1, buflen);
1366 #endif
1368 ap_destroy_pool(tp);
1372 *----------------------------------------------------------------------
1374 * dynamic_kill_idle_fs_procs
1376 * Implement a kill policy for the dynamic FastCGI applications.
1377 * We also update the data structures to reflect the changes.
1379 * Side effects:
1380 * Processes are marked for deletion possibly killed.
1382 *----------------------------------------------------------------------
1384 static void dynamic_kill_idle_fs_procs(void)
1386 fcgi_server *s;
1387 int victims = 0;
1389 for (s = fcgi_servers; s != NULL; s = s->next)
1392 * server's smoothed running time, or if that's 0, the current total
1394 unsigned long connTime;
1397 * maximum number of microseconds that all of a server's running
1398 * processes together could have spent running since the last check
1400 unsigned long totalTime;
1403 * percentage, 0-100, of totalTime that the processes actually used
1405 int loadFactor;
1407 int i;
1408 int really_running = 0;
1410 if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1412 continue;
1415 /* s->numProcesses includes pending kills so get the "active" count */
1416 for (i = 0; i < dynamicMaxClassProcs; ++i)
1418 if (s->procs[i].state == FCGI_RUNNING_STATE) ++really_running;
1421 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1422 totalTime = really_running * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1424 loadFactor = 100 * connTime / totalTime;
1426 if (really_running == 1)
1428 if (loadFactor >= dynamicThreshold1)
1430 continue;
1433 else
1435 int load = really_running / ( really_running - 1) * loadFactor;
1437 if (load >= dynamicThresholdN)
1439 continue;
1444 * Run through the procs to see if we can get away w/o waxing one.
1446 for (i = 0; i < dynamicMaxClassProcs; ++i)
1448 if (s->procs[i].state == FCGI_START_STATE)
1450 s->procs[i].state = FCGI_READY_STATE;
1451 break;
1453 else if (s->procs[i].state == FCGI_VICTIM_STATE)
1455 break;
1459 if (i >= dynamicMaxClassProcs)
1461 ServerProcess * procs = s->procs;
1462 int youngest = -1;
1464 for (i = 0; i < dynamicMaxClassProcs; ++i)
1466 if (procs[i].state == FCGI_RUNNING_STATE)
1468 if (youngest == -1 || procs[i].start_time >= procs[youngest].start_time)
1470 youngest = i;
1475 if (youngest != -1)
1477 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1478 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1479 s->fs_path, (long) s->procs[youngest].pid);
1481 fcgi_kill(&s->procs[youngest], SIGTERM);
1483 victims++;
1487 * If the number of non-victims is less than or equal to
1488 * the minimum that may be running without being killed off,
1489 * don't select any more victims.
1491 if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1493 break;
1499 #ifdef WIN32
1501 /* This is a little bogus, there's gotta be a better way to do this
1502 * Can we use WaitForMultipleObjects() */
1503 #define FCGI_PROC_WAIT_TIME 100
1505 void child_wait_thread_main(void *dummy) {
1506 fcgi_server *s;
1507 DWORD dwRet = WAIT_TIMEOUT;
1508 int numChildren;
1509 int i;
1510 int waited;
1512 while (!bTimeToDie) {
1513 waited = 0;
1515 for (s = fcgi_servers; s != NULL; s = s->next) {
1516 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1517 continue;
1519 if (s->directive == APP_CLASS_DYNAMIC) {
1520 numChildren = dynamicMaxClassProcs;
1522 else {
1523 numChildren = s->numProcesses;
1526 for (i=0; i < numChildren; i++) {
1527 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1529 DWORD exitStatus = 0;
1531 /* timeout is currently set for 100 miliecond */
1532 /* it may need to be longer or user customizable */
1533 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1535 waited = 1;
1537 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1538 /* a child fs has died */
1539 /* mark the child as dead */
1541 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1543 if (s->directive == APP_CLASS_STANDARD) {
1544 /* restart static app */
1545 s->procs[i].state = FCGI_START_STATE;
1546 if (exitStatus != 0) {
1547 /* don't bump failure count on exit 0 */
1548 s->numFailures++;
1551 else {
1552 s->numProcesses--;
1553 fcgi_dynamic_total_proc_count--;
1554 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1556 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1557 s->procs[i].state = FCGI_KILLED_STATE;
1559 else {
1560 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1561 if (exitStatus != 0) {
1562 /* don't bump failure count on exit 0 */
1563 s->numFailures++;
1566 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1567 s->procs[i].state = FCGI_START_STATE;
1569 else {
1570 s->procs[i].state = FCGI_READY_STATE;
1575 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1576 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1577 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1578 s->fs_path, (long) s->procs[i].pid, exitStatus);
1580 CloseHandle(s->procs[i].handle);
1581 CloseHandle(s->procs[i].terminationEvent);
1582 s->procs[i].handle = INVALID_HANDLE_VALUE;
1583 s->procs[i].terminationEvent = INVALID_HANDLE_VALUE;
1584 s->procs[i].pid = -1;
1586 /* wake up the main thread */
1587 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1592 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1595 #endif
1597 #ifndef WIN32
1598 static void setup_signals(void)
1600 struct sigaction sa;
1602 /* Setup handlers */
1604 sa.sa_handler = signal_handler;
1605 sigemptyset(&sa.sa_mask);
1606 sa.sa_flags = 0;
1608 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1609 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1610 "sigaction(SIGTERM) failed");
1612 /* httpd restart */
1613 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1614 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1615 "sigaction(SIGHUP) failed");
1617 /* httpd graceful restart */
1618 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1619 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1620 "sigaction(SIGUSR1) failed");
1622 /* read messages from request handlers - kill interval expired */
1623 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1624 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1625 "sigaction(SIGALRM) failed");
1627 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1628 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1629 "sigaction(SIGCHLD) failed");
1632 #endif
1634 #if !defined(WIN32) && !defined(APACHE2)
1635 int fcgi_pm_main(void *dummy, child_info *info)
1636 #else
1637 void fcgi_pm_main(void *dummy)
1638 #endif
1640 fcgi_server *s;
1641 unsigned int i;
1642 int read_ready = 0;
1643 int alarmLeft = 0;
1645 #ifdef WIN32
1646 DWORD dwRet;
1647 HANDLE child_wait_thread = INVALID_HANDLE_VALUE;
1648 #else
1649 int callWaitPid, callDynamicProcs;
1650 #endif
1652 #ifdef WIN32
1653 /* Add SystemRoot to the dynamic environment */
1654 char ** envp = dynamicEnvp;
1655 for (i = 0; *envp; ++i) {
1656 ++envp;
1658 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1660 #else
1662 reduce_privileges();
1663 change_process_name("fcgi-pm");
1665 close(fcgi_pm_pipe[1]);
1666 setup_signals();
1668 if (fcgi_wrapper) {
1669 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1670 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1672 #endif
1674 /* Initialize AppClass */
1675 for (s = fcgi_servers; s != NULL; s = s->next)
1677 if (s->directive != APP_CLASS_STANDARD)
1678 continue;
1680 #ifdef WIN32
1681 if (s->socket_path)
1682 s->listenFd = 0;
1683 #endif
1685 for (i = 0; i < s->numProcesses; ++i)
1686 s->procs[i].state = FCGI_START_STATE;
1689 #ifdef WIN32
1690 child_wait_thread = (HANDLE) _beginthread(child_wait_thread_main, 0, NULL);
1692 if (child_wait_thread == (HANDLE) -1)
1694 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1695 "FastCGI: failed to create process manager's wait thread!");
1698 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1699 "FastCGI: process manager initialized");
1700 #else
1701 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1702 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1703 #endif
1705 now = time(NULL);
1708 * Loop until SIGTERM
1710 for (;;) {
1711 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1712 #ifdef WIN32
1713 time_t expire;
1714 #else
1715 pid_t childPid;
1716 int waitStatus;
1717 #endif
1718 unsigned int numChildren;
1719 unsigned int minServerLife;
1722 * If we came out of sigsuspend() for any reason other than
1723 * SIGALRM, pick up where we left off.
1725 if (alarmLeft)
1726 sleepSeconds = alarmLeft;
1729 * Examine each configured AppClass for a process that needs
1730 * starting. Compute the earliest time when the start should
1731 * be attempted, starting it now if the time has passed. Also,
1732 * remember that we do NOT need to restart externally managed
1733 * FastCGI applications.
1735 for (s = fcgi_servers; s != NULL; s = s->next)
1737 if (s->directive == APP_CLASS_EXTERNAL)
1738 continue;
1740 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1741 ? dynamicMaxClassProcs
1742 : s->numProcesses;
1744 minServerLife = (s->directive == APP_CLASS_DYNAMIC)
1745 ? dynamicMinServerLife
1746 : s->minServerLife;
1748 for (i = 0; i < numChildren; ++i)
1750 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1752 int restart = (s->procs[i].pid < 0);
1753 time_t restartTime = s->restartTime;
1755 if (s->bad)
1757 /* we've gone to using the badDelay, the only thing that
1758 resets bad is when badDelay has expired. but numFailures
1759 is only just set below its threshold. the proc's
1760 start_times are all reset when the bad is. the numFailures
1761 is reset when we see an app run for a period */
1763 s->procs[i].start_time = 0;
1766 if (s->numFailures > MAX_FAILED_STARTS)
1768 time_t last_start_time = s->procs[i].start_time;
1770 if (last_start_time && now - last_start_time > minServerLife)
1772 s->bad = 0;
1773 s->numFailures = 0;
1774 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1775 "FastCGI:%s server \"%s\" has remained"
1776 " running for more than %d seconds, its restart"
1777 " interval has been restored to %d seconds",
1778 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1779 s->fs_path, minServerLife, s->restartDelay);
1781 else
1783 unsigned int j;
1785 for (j = 0; j < numChildren; ++j)
1787 if (s->procs[j].pid <= 0) continue;
1788 if (s->procs[j].state != FCGI_RUNNING_STATE) continue;
1789 if (s->procs[j].start_time == 0) continue;
1790 if (now - s->procs[j].start_time > minServerLife) break;
1793 if (j >= numChildren)
1795 s->bad = 1;
1796 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1797 "FastCGI:%s server \"%s\" has failed to remain"
1798 " running for %d seconds given %d attempts, its restart"
1799 " interval has been backed off to %d seconds",
1800 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1801 s->fs_path, minServerLife, MAX_FAILED_STARTS,
1802 FAILED_STARTS_DELAY);
1804 else
1806 s->bad = 0;
1807 s->numFailures = 0;
1808 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1809 "FastCGI:%s server \"%s\" has remained"
1810 " running for more than %d seconds, its restart"
1811 " interval has been restored to %d seconds",
1812 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1813 s->fs_path, minServerLife, s->restartDelay);
1818 if (s->bad)
1820 restartTime += FAILED_STARTS_DELAY;
1822 else
1824 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1827 if (restartTime <= now)
1829 if (s->bad)
1831 s->bad = 0;
1832 s->numFailures = MAX_FAILED_STARTS;
1835 if (s->listenFd < 0 && init_listen_sock(s))
1837 if (sleepSeconds > s->initStartDelay)
1838 sleepSeconds = s->initStartDelay;
1839 break;
1841 #ifndef WIN32
1842 if (caughtSigTerm) {
1843 goto ProcessSigTerm;
1845 #endif
1846 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1847 if (s->procs[i].pid <= 0) {
1848 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1849 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1850 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1851 s->fs_path);
1853 sleepSeconds = min(sleepSeconds,
1854 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1856 s->procs[i].pid = -1;
1857 break;
1860 s->procs[i].start_time = now;
1861 s->restartTime = now;
1863 if (s->startTime == 0) {
1864 s->startTime = now;
1867 if (s->directive == APP_CLASS_DYNAMIC) {
1868 s->numProcesses++;
1869 fcgi_dynamic_total_proc_count++;
1870 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1873 s->procs[i].state = FCGI_RUNNING_STATE;
1875 if (fcgi_wrapper) {
1876 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1877 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1878 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1879 s->fs_path, (long) s->uid, (long) s->gid,
1880 restart ? "re" : "", (long) s->procs[i].pid);
1882 else {
1883 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1884 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1885 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1886 s->fs_path, restart ? "re" : "", (long) s->procs[i].pid);
1888 ap_assert(s->procs[i].pid > 0);
1889 } else {
1890 sleepSeconds = min(sleepSeconds, restartTime - now);
1896 #ifndef WIN32
1898 if(caughtSigTerm) {
1899 goto ProcessSigTerm;
1901 if((!caughtSigChld) && (!caughtSigAlarm)) {
1902 fd_set rfds;
1904 alarm(sleepSeconds);
1906 FD_ZERO(&rfds);
1907 FD_SET(fcgi_pm_pipe[0], &rfds);
1908 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1910 alarmLeft = alarm(0);
1912 callWaitPid = caughtSigChld;
1913 caughtSigChld = FALSE;
1914 callDynamicProcs = caughtSigAlarm;
1915 caughtSigAlarm = FALSE;
1917 now = time(NULL);
1920 * Dynamic fcgi process management
1922 if((callDynamicProcs) || (!callWaitPid)) {
1923 dynamic_read_msgs(read_ready);
1924 if(fcgi_dynamic_epoch == 0) {
1925 fcgi_dynamic_epoch = now;
1927 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1928 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1929 dynamic_kill_idle_fs_procs();
1930 fcgi_dynamic_epoch = now;
1934 if(!callWaitPid) {
1935 continue;
1938 /* We've caught SIGCHLD, so find out who it was using waitpid,
1939 * write a log message and update its data structure. */
1941 for (;;) {
1942 if (caughtSigTerm)
1943 goto ProcessSigTerm;
1945 childPid = waitpid(-1, &waitStatus, WNOHANG);
1947 if (childPid == -1 || childPid == 0)
1948 break;
1950 for (s = fcgi_servers; s != NULL; s = s->next) {
1951 if (s->directive == APP_CLASS_EXTERNAL)
1952 continue;
1954 if (s->directive == APP_CLASS_DYNAMIC)
1955 numChildren = dynamicMaxClassProcs;
1956 else
1957 numChildren = s->numProcesses;
1959 for (i = 0; i < numChildren; i++) {
1960 if (s->procs[i].pid == childPid)
1961 goto ChildFound;
1965 /* TODO: print something about this unknown child */
1966 continue;
1968 ChildFound:
1969 s->procs[i].pid = -1;
1971 if (s->directive == APP_CLASS_STANDARD) {
1972 /* Always restart static apps */
1973 s->procs[i].state = FCGI_START_STATE;
1974 if (! (WIFEXITED(waitStatus) && (WEXITSTATUS(waitStatus) == 0))) {
1975 /* don't bump the failure count if the app exited with 0 */
1976 s->numFailures++;
1979 else {
1980 s->numProcesses--;
1981 fcgi_dynamic_total_proc_count--;
1983 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1984 s->procs[i].state = FCGI_KILLED_STATE;
1986 else {
1987 /* A dynamic app died or exited without provocation from the PM */
1989 if (! (WIFEXITED(waitStatus) && (WEXITSTATUS(waitStatus) == 0))) {
1990 /* don't bump the failure count if the app exited with 0 */
1991 s->numFailures++;
1994 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1995 s->procs[i].state = FCGI_START_STATE;
1996 else
1997 s->procs[i].state = FCGI_READY_STATE;
2001 if (WIFEXITED(waitStatus)) {
2002 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
2003 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
2004 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
2005 s->fs_path, (long) childPid, WEXITSTATUS(waitStatus));
2007 else if (WIFSIGNALED(waitStatus)) {
2008 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
2009 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
2010 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
2011 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus),
2012 #ifdef WCOREDUMP
2013 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
2014 #else
2015 "");
2016 #endif
2018 else if (WIFSTOPPED(waitStatus)) {
2019 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
2020 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
2021 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
2022 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus));
2024 } /* for (;;), waitpid() */
2026 #else /* WIN32 */
2028 /* wait for an event to occur or timer expires */
2029 expire = time(NULL) + sleepSeconds;
2030 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
2032 if (dwRet == WAIT_FAILED) {
2033 /* There is something seriously wrong here */
2034 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
2035 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
2036 bTimeToDie = TRUE;
2039 if (dwRet != WAIT_TIMEOUT) {
2040 now = time(NULL);
2042 if (now < expire)
2043 alarmLeft = expire - now;
2047 * Dynamic fcgi process management
2049 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
2050 if (dwRet == MBOX_EVENT) {
2051 read_ready = 1;
2054 now = time(NULL);
2056 dynamic_read_msgs(read_ready);
2058 if(fcgi_dynamic_epoch == 0) {
2059 fcgi_dynamic_epoch = now;
2062 if ((now-fcgi_dynamic_epoch >= (int) dynamicKillInterval) ||
2063 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
2064 dynamic_kill_idle_fs_procs();
2065 fcgi_dynamic_epoch = now;
2067 read_ready = 0;
2069 else if (dwRet == WAKE_EVENT) {
2070 continue;
2072 else if (dwRet == TERM_EVENT) {
2073 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
2074 "FastCGI: Termination event received process manager shutting down");
2076 bTimeToDie = TRUE;
2077 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2079 goto ProcessSigTerm;
2081 else {
2082 /* Have an received an unknown event - should not happen */
2083 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
2084 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
2086 bTimeToDie = TRUE;
2087 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2089 goto ProcessSigTerm;
2092 #endif /* WIN32 */
2094 } /* for (;;), the whole shoot'n match */
2096 ProcessSigTerm:
2098 * Kill off the children, then exit.
2100 shutdown_all();
2102 #ifdef WIN32
2103 return;
2104 #else
2105 exit(0);
2106 #endif
2109 #ifdef WIN32
2110 int fcgi_pm_add_job(fcgi_pm_job *new_job)
2112 int rv = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
2114 if (rv != WAIT_OBJECT_0 && rv != WAIT_ABANDONED)
2116 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2117 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2118 return -1;
2121 new_job->next = fcgi_dynamic_mbox;
2122 fcgi_dynamic_mbox = new_job;
2124 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
2126 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2127 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
2130 return 0;
2132 #endif