Fastcgi module for Apache 2.2
[mod_fastcgi.git] / fcgi_pm.c
blob7d0d8c11b7431208928ee27537a941081f09f922
1 /*
2 * $Id: fcgi_pm.c,v 1.93 2004/04/15 02:01:26 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 if (fcgi_wrapper)
409 char *shortName;
411 /* Relinquish our root real uid powers */
412 seteuid_root();
413 setuid(ap_user_id);
415 /* Apache (2 anyway) doesn't use suexec if there is no user/group in
416 * effect - this translates to a uid/gid of 0/0 (which should never
417 * be a valid uid/gid for an suexec invocation so it should be safe */
418 if (fs->uid == 0 && fs->gid == 0) {
419 goto NO_SUEXEC;
422 #ifdef NO_SUEXEC_FOR_AP_USER_N_GROUP
424 /* AP13 does not use suexec if the target uid/gid is the same as the
425 * server's - AP20 does. I (now) consider the AP2 approach better
426 * (fcgi_pm.c v1.42 incorporated the 1.3 behaviour, v1.84 reverted it,
427 * v1.85 added the compile time option to use the old behaviour). */
428 if (fcgi_user_id == fs->uid && fcgi_group_id == fs->gid) {
429 goto NO_SUEXEC;
432 #endif
433 shortName = strrchr(fs->fs_path, '/') + 1;
435 do {
436 execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group,
437 shortName, NULL, fs->envp);
438 } while (errno == EINTR);
440 else
442 NO_SUEXEC:
443 do {
444 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
445 } while (errno == EINTR);
448 failedSysCall = "execle()";
450 FailedSystemCallExit:
451 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
452 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
453 exit(-1);
455 /* avoid an irrelevant compiler warning */
456 return(0);
458 #else /* WIN32 */
460 #ifdef APACHE2
462 /* based on mod_cgi.c:run_cgi_child() */
464 apr_pool_t * tp;
465 char * termination_env_string;
466 HANDLE listen_handle = INVALID_HANDLE_VALUE;
467 apr_procattr_t * procattr;
468 apr_proc_t proc = { 0 };
469 apr_file_t * file;
470 int i = 0;
471 cgi_exec_info_t e_info = { 0 };
472 request_rec r = { 0 };
473 const char *command;
474 const char **argv;
475 int rv;
476 APR_OPTIONAL_FN_TYPE(ap_cgi_build_command) *cgi_build_command;
478 cgi_build_command = APR_RETRIEVE_OPTIONAL_FN(ap_cgi_build_command);
479 if (cgi_build_command == NULL)
481 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
482 "FastCGI: can't exec server \"%s\", mod_cgi isn't loaded",
483 fs->fs_path);
484 return 0;
487 if (apr_pool_create(&tp, fcgi_config_pool))
488 return 0;
490 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
491 if (process->terminationEvent == NULL)
492 goto CLEANUP;
494 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
496 termination_env_string = ap_psprintf(tp,
497 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
499 while (fs->envp[i]) i++;
500 fs->envp[i++] = termination_env_string;
501 fs->envp[i] = (char *) fs->mutex_env_string;
503 ap_assert(fs->envp[i + 1] == NULL);
505 if (fs->socket_path)
507 SECURITY_ATTRIBUTES sa = { 0 };
509 sa.bInheritHandle = TRUE;
510 sa.nLength = sizeof(sa);
512 listen_handle = CreateNamedPipe(fs->socket_path,
513 PIPE_ACCESS_DUPLEX,
514 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
515 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
517 if (listen_handle == INVALID_HANDLE_VALUE)
519 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
520 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
521 fs->fs_path);
522 goto CLEANUP;
525 else
527 listen_handle = (HANDLE) fs->listenFd;
530 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
531 r.server = fcgi_apache_main_server;
532 r.filename = (char *) fs->fs_path;
533 r.pool = tp;
534 r.subprocess_env = apr_table_make(tp, 0);
536 e_info.cmd_type = APR_PROGRAM;
538 rv = cgi_build_command(&command, &argv, &r, tp, &e_info);
539 if (rv != APR_SUCCESS)
541 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
542 "FastCGI: don't know how to spawn cmd child process: %s",
543 fs->fs_path);
544 goto CLEANUP;
547 if (apr_procattr_create(&procattr, tp))
548 goto CLEANUP;
550 if (apr_procattr_dir_set(procattr, ap_make_dirstr_parent(tp, fs->fs_path)))
551 goto CLEANUP;
553 if (apr_procattr_cmdtype_set(procattr, e_info.cmd_type))
554 goto CLEANUP;
556 if (apr_procattr_detach_set(procattr, 1))
557 goto CLEANUP;
559 if (apr_os_file_put(&file, &listen_handle, 0, tp))
560 goto CLEANUP;
562 /* procattr is opaque so we have to use this - unfortuantely it dups */
563 if (apr_procattr_child_in_set(procattr, file, NULL))
564 goto CLEANUP;
566 if (apr_proc_create(&proc, command, argv, fs->envp, procattr, tp))
567 goto CLEANUP;
569 process->handle = proc.hproc;
571 CLEANUP:
573 if (fs->socket_path && listen_handle != INVALID_HANDLE_VALUE)
575 CloseHandle(listen_handle);
578 if (i)
580 fs->envp[i - 1] = NULL;
583 ap_destroy_pool(tp);
585 return proc.pid;
587 #else /* WIN32 && !APACHE2 */
589 /* Adapted from Apache's util_script.c ap_call_exec() */
590 char *interpreter = NULL;
591 char *quoted_filename;
592 char *pCommand;
593 char *pEnvBlock, *pNext;
595 int i = 0;
596 int iEnvBlockLen = 1;
598 file_type_e fileType;
600 STARTUPINFO si;
601 PROCESS_INFORMATION pi;
603 request_rec r;
604 pid_t pid = -1;
606 pool * tp = ap_make_sub_pool(fcgi_config_pool);
608 HANDLE listen_handle = INVALID_HANDLE_VALUE;
609 char * termination_env_string = NULL;
611 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
612 if (process->terminationEvent == NULL)
614 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
615 "FastCGI: can't create termination event for server \"%s\", "
616 "CreateEvent() failed", fs->fs_path);
617 goto CLEANUP;
619 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
621 termination_env_string = ap_psprintf(tp,
622 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
624 if (fs->socket_path)
626 SECURITY_ATTRIBUTES sa;
628 sa.lpSecurityDescriptor = NULL;
629 sa.bInheritHandle = TRUE;
630 sa.nLength = sizeof(sa);
632 listen_handle = CreateNamedPipe(fs->socket_path,
633 PIPE_ACCESS_DUPLEX,
634 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
635 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
637 if (listen_handle == INVALID_HANDLE_VALUE)
639 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
640 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
641 goto CLEANUP;
644 else
646 listen_handle = (HANDLE) fs->listenFd;
649 memset(&si, 0, sizeof(si));
650 memset(&pi, 0, sizeof(pi));
651 memset(&r, 0, sizeof(r));
653 /* Can up a fake request to pass to ap_get_win32_interpreter() */
654 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
655 r.server = fcgi_apache_main_server;
656 r.filename = (char *) fs->fs_path;
657 r.pool = tp;
659 fileType = ap_get_win32_interpreter(&r, &interpreter);
661 if (fileType == eFileTypeUNKNOWN) {
662 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
663 "FastCGI: %s is not executable; ensure interpreted scripts have "
664 "\"#!\" as their first line",
665 fs->fs_path);
666 ap_destroy_pool(tp);
667 goto CLEANUP;
671 * We have the interpreter (if there is one) and we have
672 * the arguments (if there are any).
673 * Build the command string to pass to CreateProcess.
675 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
676 if (interpreter && *interpreter) {
677 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
679 else {
680 pCommand = quoted_filename;
684 * Make child process use hPipeOutputWrite as standard out,
685 * and make sure it does not show on screen.
687 si.cb = sizeof(si);
688 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
689 si.wShowWindow = SW_HIDE;
690 si.hStdInput = listen_handle;
692 /* XXX These should be open to the error_log */
693 si.hStdOutput = INVALID_HANDLE_VALUE;
694 si.hStdError = INVALID_HANDLE_VALUE;
697 * Win32's CreateProcess call requires that the environment
698 * be passed in an environment block, a null terminated block of
699 * null terminated strings.
700 * @todo we should store the env in this format for win32.
702 while (fs->envp[i])
704 iEnvBlockLen += strlen(fs->envp[i]) + 1;
705 i++;
708 iEnvBlockLen += strlen(termination_env_string) + 1;
709 iEnvBlockLen += strlen(fs->mutex_env_string) + 1;
711 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
713 i = 0;
714 pNext = pEnvBlock;
715 while (fs->envp[i])
717 strcpy(pNext, fs->envp[i]);
718 pNext += strlen(pNext) + 1;
719 i++;
722 strcpy(pNext, termination_env_string);
723 pNext += strlen(pNext) + 1;
724 strcpy(pNext, fs->mutex_env_string);
726 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
728 pEnvBlock,
729 ap_make_dirstr_parent(tp, fs->fs_path),
730 &si, &pi))
732 /* Hack to get 16-bit CGI's working. It works for all the
733 * standard modules shipped with Apache. pi.dwProcessId is 0
734 * for 16-bit CGIs and all the Unix specific code that calls
735 * ap_call_exec interprets this as a failure case. And we can't
736 * use -1 either because it is mapped to 0 by the caller.
738 pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId;
740 process->handle = pi.hProcess;
741 CloseHandle(pi.hThread);
744 CLEANUP:
746 if (fs->socket_path && listen_handle != INVALID_HANDLE_VALUE)
748 CloseHandle(listen_handle);
751 ap_destroy_pool(tp);
753 return pid;
755 #endif /* !APACHE2 */
756 #endif /* WIN32 */
759 #ifndef WIN32
760 static void reduce_privileges(void)
762 const char *name;
764 if (geteuid() != 0)
765 return;
767 #ifndef __EMX__
768 /* Get username if passed as a uid */
769 if (ap_user_name[0] == '#') {
770 uid_t uid = atoi(&ap_user_name[1]);
771 struct passwd *ent = getpwuid(uid);
773 if (ent == NULL) {
774 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
775 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
776 "you probably need to modify the User directive", (unsigned)uid);
777 exit(1);
779 name = ent->pw_name;
781 else
782 name = ap_user_name;
784 /* Change Group */
785 if (setgid(ap_group_id) == -1) {
786 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
787 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
788 exit(1);
791 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
793 /* Initialize supplementary groups */
794 if (initgroups(name, ap_group_id) == -1) {
795 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
796 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
797 name, (unsigned)ap_group_id);
798 exit(1);
800 #endif /* __EMX__ */
802 /* Change User */
803 if (fcgi_wrapper) {
804 if (seteuid_user() == -1) {
805 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
806 "FastCGI: process manager exiting, failed to reduce privileges");
807 exit(1);
810 else {
811 if (setuid(ap_user_id) == -1) {
812 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
813 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
814 exit(1);
819 /*************
820 * Change the name of this process - best we can easily.
822 static void change_process_name(const char * const name)
824 /* under Apache2, ap_server_argv0 is const */
825 strncpy((char *) ap_server_argv0, name, strlen(ap_server_argv0));
827 #endif /* !WIN32 */
829 static void schedule_start(fcgi_server *s, int proc)
831 /* If we've started one recently, don't register another */
832 time_t time_passed = now - s->restartTime;
834 if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay))
835 || ((s->procs[proc].pid == 0) && (time_passed < s->initStartDelay)))
837 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);
838 return;
841 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
842 s->procs[proc].state = FCGI_START_STATE;
843 if (proc == dynamicMaxClassProcs - 1) {
844 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
845 "FastCGI: scheduled the %sstart of the last (dynamic) server "
846 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
847 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
852 *----------------------------------------------------------------------
854 * dynamic_read_msgs
856 * Removes the records written by request handlers and decodes them.
857 * We also update the data structures to reflect the changes.
859 *----------------------------------------------------------------------
862 static void dynamic_read_msgs(int read_ready)
864 fcgi_server *s;
865 int rc;
867 #ifndef WIN32
868 static int buflen = 0;
869 static char buf[FCGI_MSGS_BUFSIZE + 1];
870 char *ptr1, *ptr2, opcode;
871 char execName[FCGI_MAXPATH + 1];
872 char user[MAX_USER_NAME_LEN + 2];
873 char group[MAX_GID_CHAR_LEN + 1];
874 unsigned long q_usec = 0UL, req_usec = 0UL;
875 #else
876 fcgi_pm_job *joblist = NULL;
877 fcgi_pm_job *cjob = NULL;
878 #endif
880 pool *sp = NULL, *tp;
882 #ifndef WIN32
883 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
884 #endif
887 * To prevent the idle application from running indefinitely, we
888 * check the timer and if it is expired, we recompute the values
889 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
890 * message is received, only updates are made to the data structures.
892 if (fcgi_dynamic_last_analyzed == 0) {
893 fcgi_dynamic_last_analyzed = now;
895 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
896 for (s = fcgi_servers; s != NULL; s = s->next) {
897 if (s->directive != APP_CLASS_DYNAMIC)
898 break;
900 /* Advance the last analyzed timestamp by the elapsed time since
901 * it was last set. Round the increase down to the nearest
902 * multiple of dynamicUpdateInterval */
904 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
905 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
906 s->totalConnTime = 0UL;
907 s->totalQueueTime = 0UL;
911 if (read_ready <= 0) {
912 return;
915 #ifndef WIN32
916 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
917 if (rc <= 0) {
918 if (!caughtSigTerm) {
919 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
920 "FastCGI: read() from pipe failed (%d)", rc);
921 if (rc == 0) {
922 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
923 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
924 caughtSigTerm = TRUE;
927 return;
929 buflen += rc;
930 buf[buflen] = '\0';
932 #else
934 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
935 * request to do something) and/or when a timeout expires.
936 * There really should be no reason why this wait would get stuck
937 * but there's no point in waiting forever. */
939 rc = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
941 if (rc != WAIT_OBJECT_0 && rc != WAIT_ABANDONED)
943 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
944 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
945 return;
948 joblist = fcgi_dynamic_mbox;
949 fcgi_dynamic_mbox = NULL;
951 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
953 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
954 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
957 cjob = joblist;
958 #endif
960 #ifdef APACHE2
961 apr_pool_create(&tp, fcgi_config_pool);
962 #else
963 tp = ap_make_sub_pool(fcgi_config_pool);
964 #endif
966 #ifndef WIN32
967 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
968 int scan_failed = 0;
970 ptr2 = strchr(ptr1, '*');
971 if (ptr2) {
972 *ptr2++ = '\0';
974 else {
975 break;
978 opcode = *ptr1;
980 switch (opcode)
982 case FCGI_SERVER_START_JOB:
983 case FCGI_SERVER_RESTART_JOB:
985 if (sscanf(ptr1, "%c %s %16s %15s",
986 &opcode, execName, user, group) != 4)
988 scan_failed = 1;
990 break;
992 case FCGI_REQUEST_TIMEOUT_JOB:
994 if (sscanf(ptr1, "%c %s %16s %15s",
995 &opcode, execName, user, group) != 4)
997 scan_failed = 1;
999 break;
1001 case FCGI_REQUEST_COMPLETE_JOB:
1003 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
1004 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
1006 scan_failed = 1;
1008 break;
1010 default:
1012 scan_failed = 1;
1013 break;
1016 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode, execName, user, group, q_usec, req_usec);
1018 if (scan_failed) {
1019 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1020 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
1021 goto NextJob;
1023 #else
1024 /* Update data structures for processing */
1025 while (cjob != NULL) {
1026 joblist = cjob->next;
1027 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
1028 #endif
1030 #ifndef WIN32
1031 s = fcgi_util_fs_get(execName, user, group);
1032 #else
1033 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
1034 #endif
1036 #ifndef WIN32
1037 if (s==NULL && opcode != FCGI_REQUEST_COMPLETE_JOB)
1038 #else
1039 if (s==NULL && cjob->id != FCGI_REQUEST_COMPLETE_JOB)
1040 #endif
1042 #ifdef WIN32
1044 HANDLE mutex = CreateMutex(NULL, FALSE, cjob->fs_path);
1046 if (mutex == NULL)
1048 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1049 "FastCGI: can't create accept mutex "
1050 "for (dynamic) server \"%s\"", cjob->fs_path);
1051 goto BagNewServer;
1054 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
1055 #else
1056 const char *err;
1057 #endif
1059 /* Create a perm subpool to hold the new server data,
1060 * we can destroy it if something doesn't pan out */
1061 #ifdef APACHE2
1062 apr_pool_create(&sp, fcgi_config_pool);
1063 #else
1064 sp = ap_make_sub_pool(fcgi_config_pool);
1065 #endif
1067 /* Create a new "dynamic" server */
1068 s = fcgi_util_fs_new(sp);
1070 s->directive = APP_CLASS_DYNAMIC;
1071 s->restartDelay = dynamicRestartDelay;
1072 s->listenQueueDepth = dynamicListenQueueDepth;
1073 s->initStartDelay = dynamicInitStartDelay;
1074 s->envp = dynamicEnvp;
1075 s->flush = dynamicFlush;
1077 #ifdef WIN32
1078 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
1079 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
1080 #else
1081 s->fs_path = ap_pstrdup(sp, execName);
1082 #endif
1083 ap_getparents(s->fs_path);
1084 ap_no2slash(s->fs_path);
1085 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
1087 /* XXX the socket_path (both Unix and Win) *is* deducible and
1088 * thus can and will be used by other apache instances without
1089 * the use of shared data regarding the processes serving the
1090 * requests. This can result in slightly unintuitive process
1091 * counts and security implications. This is prevented
1092 * if suexec (Unix) is in use. This is both a feature and a flaw.
1093 * Changing it now would break existing installations. */
1095 #ifndef WIN32
1096 /* Create socket file's path */
1097 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
1098 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1100 /* Create sockaddr, prealloc it so it won't get created in tp */
1101 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
1102 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
1103 &s->socket_addr_len, s->socket_path);
1104 if (err) {
1105 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1106 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
1107 goto BagNewServer;
1110 if (init_listen_sock(s)) {
1111 goto BagNewServer;
1114 /* If a wrapper is being used, config user/group info */
1115 if (fcgi_wrapper) {
1116 if (user[0] == '~') {
1117 /* its a user dir uri, the rest is a username, not a uid */
1118 struct passwd *pw = getpwnam(&user[1]);
1120 if (!pw) {
1121 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1122 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1123 execName, &user[1]);
1124 goto BagNewServer;
1126 s->uid = pw->pw_uid;
1127 s->user = ap_pstrdup(sp, user);
1128 s->username = s->user;
1130 s->gid = pw->pw_gid;
1131 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
1133 else {
1134 struct passwd *pw;
1136 s->uid = (uid_t)atol(user);
1137 pw = getpwuid(s->uid);
1138 if (!pw) {
1139 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1140 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1141 execName, (long)s->uid);
1142 goto BagNewServer;
1144 s->user = ap_pstrdup(sp, user);
1145 s->username = ap_pstrdup(sp, pw->pw_name);
1147 s->gid = (gid_t)atol(group);
1148 s->group = ap_pstrdup(sp, group);
1151 #else
1152 /* Create socket file's path */
1153 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
1154 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1155 s->listenFd = 0;
1156 #endif
1158 fcgi_util_fs_add(s);
1160 else {
1161 #ifndef WIN32
1162 if (opcode == FCGI_SERVER_RESTART_JOB) {
1163 #else
1164 if (cjob->id==FCGI_SERVER_RESTART_JOB) {
1165 #endif
1166 /* Check to see if the binary has changed. If so,
1167 * kill the FCGI application processes, and
1168 * restart them.
1170 struct stat stbuf;
1171 int i;
1172 #ifdef WIN32
1173 char * app_path = cjob->fs_path;
1174 #else
1175 char * app_path = execName;
1176 #endif
1178 if (stat(app_path, &stbuf) == 0 && stbuf.st_mtime > s->startTime)
1180 int do_restart = 0;
1182 /* prevent addition restart requests */
1183 s->startTime = now;
1184 #ifndef WIN32
1185 utime(s->socket_path, NULL);
1186 #endif
1188 /* kill old server(s) */
1189 for (i = 0; i < dynamicMaxClassProcs; i++)
1191 if (s->procs[i].pid > 0
1192 && stbuf.st_mtime > s->procs[i].start_time)
1194 fcgi_kill(&s->procs[i], SIGTERM);
1195 do_restart++;
1199 if (do_restart)
1201 ap_log_error(FCGI_LOG_WARN_NOERRNO,
1202 fcgi_apache_main_server, "FastCGI: restarting "
1203 "old server \"%s\" processes, newer version "
1204 "found", app_path);
1208 /* If dynamicAutoRestart, don't mark any new processes
1209 * for starting because we probably got the
1210 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1211 * will be restarting all of those we just killed.
1213 if (dynamicAutoRestart)
1214 goto NextJob;
1216 #ifndef WIN32
1217 else if (opcode == FCGI_SERVER_START_JOB) {
1218 #else
1219 else if (cjob->id==FCGI_SERVER_START_JOB) {
1220 #endif
1221 /* we've been asked to start a process--only start
1222 * it if we're not already running at least one
1223 * instance.
1225 int i;
1227 for (i = 0; i < dynamicMaxClassProcs; i++) {
1228 if (s->procs[i].state == FCGI_RUNNING_STATE)
1229 break;
1231 /* if already running, don't start another one */
1232 if (i < dynamicMaxClassProcs) {
1233 goto NextJob;
1238 #ifndef WIN32
1239 switch (opcode)
1240 #else
1241 switch (cjob->id)
1242 #endif
1244 int i, start;
1246 case FCGI_SERVER_RESTART_JOB:
1248 start = FALSE;
1250 /* We just waxed 'em all. Try to find an idle slot. */
1252 for (i = 0; i < dynamicMaxClassProcs; ++i)
1254 if (s->procs[i].state == FCGI_START_STATE
1255 || s->procs[i].state == FCGI_RUNNING_STATE)
1257 break;
1259 else if (s->procs[i].state == FCGI_KILLED_STATE
1260 || s->procs[i].state == FCGI_READY_STATE)
1262 start = TRUE;
1263 break;
1267 /* Nope, just use the first slot */
1268 if (i == dynamicMaxClassProcs)
1270 start = TRUE;
1271 i = 0;
1274 if (start)
1276 schedule_start(s, i);
1279 break;
1281 case FCGI_SERVER_START_JOB:
1282 case FCGI_REQUEST_TIMEOUT_JOB:
1284 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1286 * Extra instances should have been
1287 * terminated beforehand, probably need
1288 * to increase ProcessSlack parameter
1290 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1291 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1292 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1293 goto NextJob;
1296 /* find next free slot */
1297 for (i = 0; i < dynamicMaxClassProcs; i++)
1299 if (s->procs[i].state == FCGI_START_STATE)
1301 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1302 break;
1304 else if (s->procs[i].state == FCGI_RUNNING_STATE)
1306 continue;
1309 schedule_start(s, i);
1310 break;
1313 #ifdef FCGI_DEBUG
1314 if (i >= dynamicMaxClassProcs) {
1315 FCGIDBG1("ignore_job: slots are max'd");
1317 #endif
1318 break;
1319 case FCGI_REQUEST_COMPLETE_JOB:
1320 /* only record stats if we have a structure */
1321 if (s) {
1322 #ifndef WIN32
1323 s->totalConnTime += req_usec;
1324 s->totalQueueTime += q_usec;
1325 #else
1326 s->totalConnTime += cjob->start_time;
1327 s->totalQueueTime += cjob->qsec;
1328 #endif
1330 break;
1333 NextJob:
1335 #ifdef WIN32
1336 /* Cleanup job data */
1337 free(cjob->fs_path);
1338 free(cjob->user);
1339 free(cjob->group);
1340 free(cjob);
1341 cjob = joblist;
1342 #endif
1344 continue;
1346 BagNewServer:
1347 if (sp) ap_destroy_pool(sp);
1349 #ifdef WIN32
1350 free(cjob->fs_path);
1351 free(cjob);
1352 cjob = joblist;
1353 #endif
1356 #ifndef WIN32
1357 if (ptr1 == buf) {
1358 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1359 "FastCGI: really bogus message: \"%s\"", ptr1);
1360 ptr1 += strlen(buf);
1363 buflen -= ptr1 - buf;
1364 if (buflen) {
1365 memmove(buf, ptr1, buflen);
1367 #endif
1369 ap_destroy_pool(tp);
1373 *----------------------------------------------------------------------
1375 * dynamic_kill_idle_fs_procs
1377 * Implement a kill policy for the dynamic FastCGI applications.
1378 * We also update the data structures to reflect the changes.
1380 * Side effects:
1381 * Processes are marked for deletion possibly killed.
1383 *----------------------------------------------------------------------
1385 static void dynamic_kill_idle_fs_procs(void)
1387 fcgi_server *s;
1388 int victims = 0;
1390 for (s = fcgi_servers; s != NULL; s = s->next)
1393 * server's smoothed running time, or if that's 0, the current total
1395 unsigned long connTime;
1398 * maximum number of microseconds that all of a server's running
1399 * processes together could have spent running since the last check
1401 unsigned long totalTime;
1404 * percentage, 0-100, of totalTime that the processes actually used
1406 int loadFactor;
1408 int i;
1409 int really_running = 0;
1411 if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1413 continue;
1416 /* s->numProcesses includes pending kills so get the "active" count */
1417 for (i = 0; i < dynamicMaxClassProcs; ++i)
1419 if (s->procs[i].state == FCGI_RUNNING_STATE) ++really_running;
1422 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1423 totalTime = really_running * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1425 loadFactor = 100 * connTime / totalTime;
1427 if (really_running == 1)
1429 if (loadFactor >= dynamicThreshold1)
1431 continue;
1434 else
1436 int load = really_running / ( really_running - 1) * loadFactor;
1438 if (load >= dynamicThresholdN)
1440 continue;
1445 * Run through the procs to see if we can get away w/o waxing one.
1447 for (i = 0; i < dynamicMaxClassProcs; ++i)
1449 if (s->procs[i].state == FCGI_START_STATE)
1451 s->procs[i].state = FCGI_READY_STATE;
1452 break;
1454 else if (s->procs[i].state == FCGI_VICTIM_STATE)
1456 break;
1460 if (i >= dynamicMaxClassProcs)
1462 ServerProcess * procs = s->procs;
1463 int youngest = -1;
1465 for (i = 0; i < dynamicMaxClassProcs; ++i)
1467 if (procs[i].state == FCGI_RUNNING_STATE)
1469 if (youngest == -1 || procs[i].start_time >= procs[youngest].start_time)
1471 youngest = i;
1476 if (youngest != -1)
1478 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1479 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1480 s->fs_path, (long) s->procs[youngest].pid);
1482 fcgi_kill(&s->procs[youngest], SIGTERM);
1484 victims++;
1488 * If the number of non-victims is less than or equal to
1489 * the minimum that may be running without being killed off,
1490 * don't select any more victims.
1492 if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1494 break;
1500 #ifdef WIN32
1502 /* This is a little bogus, there's gotta be a better way to do this
1503 * Can we use WaitForMultipleObjects() */
1504 #define FCGI_PROC_WAIT_TIME 100
1506 void child_wait_thread_main(void *dummy) {
1507 fcgi_server *s;
1508 DWORD dwRet = WAIT_TIMEOUT;
1509 int numChildren;
1510 int i;
1511 int waited;
1513 while (!bTimeToDie) {
1514 waited = 0;
1516 for (s = fcgi_servers; s != NULL; s = s->next) {
1517 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1518 continue;
1520 if (s->directive == APP_CLASS_DYNAMIC) {
1521 numChildren = dynamicMaxClassProcs;
1523 else {
1524 numChildren = s->numProcesses;
1527 for (i=0; i < numChildren; i++) {
1528 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1530 DWORD exitStatus = 0;
1532 /* timeout is currently set for 100 miliecond */
1533 /* it may need to be longer or user customizable */
1534 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1536 waited = 1;
1538 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1539 /* a child fs has died */
1540 /* mark the child as dead */
1542 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1544 if (s->directive == APP_CLASS_STANDARD) {
1545 /* restart static app */
1546 s->procs[i].state = FCGI_START_STATE;
1547 if (exitStatus != 0) {
1548 /* don't bump failure count on exit 0 */
1549 s->numFailures++;
1552 else {
1553 s->numProcesses--;
1554 fcgi_dynamic_total_proc_count--;
1555 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1557 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1558 s->procs[i].state = FCGI_KILLED_STATE;
1560 else {
1561 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1562 if (exitStatus != 0) {
1563 /* don't bump failure count on exit 0 */
1564 s->numFailures++;
1567 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1568 s->procs[i].state = FCGI_START_STATE;
1570 else {
1571 s->procs[i].state = FCGI_READY_STATE;
1576 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1577 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1578 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1579 s->fs_path, (long) s->procs[i].pid, exitStatus);
1581 CloseHandle(s->procs[i].handle);
1582 CloseHandle(s->procs[i].terminationEvent);
1583 s->procs[i].handle = INVALID_HANDLE_VALUE;
1584 s->procs[i].terminationEvent = INVALID_HANDLE_VALUE;
1585 s->procs[i].pid = -1;
1587 /* wake up the main thread */
1588 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1593 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1596 #endif
1598 #ifndef WIN32
1599 static void setup_signals(void)
1601 struct sigaction sa;
1603 /* Setup handlers */
1605 sa.sa_handler = signal_handler;
1606 sigemptyset(&sa.sa_mask);
1607 sa.sa_flags = 0;
1609 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1610 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1611 "sigaction(SIGTERM) failed");
1613 /* httpd restart */
1614 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1615 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1616 "sigaction(SIGHUP) failed");
1618 /* httpd graceful restart */
1619 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1620 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1621 "sigaction(SIGUSR1) failed");
1623 /* read messages from request handlers - kill interval expired */
1624 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1625 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1626 "sigaction(SIGALRM) failed");
1628 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1629 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1630 "sigaction(SIGCHLD) failed");
1633 #endif
1635 #if !defined(WIN32) && !defined(APACHE2)
1636 int fcgi_pm_main(void *dummy, child_info *info)
1637 #else
1638 void fcgi_pm_main(void *dummy)
1639 #endif
1641 fcgi_server *s;
1642 unsigned int i;
1643 int read_ready = 0;
1644 int alarmLeft = 0;
1646 #ifdef WIN32
1647 DWORD dwRet;
1648 HANDLE child_wait_thread = INVALID_HANDLE_VALUE;
1649 #else
1650 int callWaitPid, callDynamicProcs;
1651 #endif
1653 #ifdef WIN32
1654 /* Add SystemRoot to the dynamic environment */
1655 char ** envp = dynamicEnvp;
1656 for (i = 0; *envp; ++i) {
1657 ++envp;
1659 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1661 #else
1663 reduce_privileges();
1664 change_process_name("fcgi-pm");
1666 close(fcgi_pm_pipe[1]);
1667 setup_signals();
1669 if (fcgi_wrapper) {
1670 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1671 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1673 #endif
1675 /* Initialize AppClass */
1676 for (s = fcgi_servers; s != NULL; s = s->next)
1678 if (s->directive != APP_CLASS_STANDARD)
1679 continue;
1681 #ifdef WIN32
1682 if (s->socket_path)
1683 s->listenFd = 0;
1684 #endif
1686 for (i = 0; i < s->numProcesses; ++i)
1687 s->procs[i].state = FCGI_START_STATE;
1690 #ifdef WIN32
1691 child_wait_thread = (HANDLE) _beginthread(child_wait_thread_main, 0, NULL);
1693 if (child_wait_thread == (HANDLE) -1)
1695 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1696 "FastCGI: failed to create process manager's wait thread!");
1699 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1700 "FastCGI: process manager initialized");
1701 #else
1702 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1703 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1704 #endif
1706 now = time(NULL);
1709 * Loop until SIGTERM
1711 for (;;) {
1712 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1713 #ifdef WIN32
1714 time_t expire;
1715 #else
1716 pid_t childPid;
1717 int waitStatus;
1718 #endif
1719 unsigned int numChildren;
1720 unsigned int minServerLife;
1723 * If we came out of sigsuspend() for any reason other than
1724 * SIGALRM, pick up where we left off.
1726 if (alarmLeft)
1727 sleepSeconds = alarmLeft;
1730 * Examine each configured AppClass for a process that needs
1731 * starting. Compute the earliest time when the start should
1732 * be attempted, starting it now if the time has passed. Also,
1733 * remember that we do NOT need to restart externally managed
1734 * FastCGI applications.
1736 for (s = fcgi_servers; s != NULL; s = s->next)
1738 if (s->directive == APP_CLASS_EXTERNAL)
1739 continue;
1741 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1742 ? dynamicMaxClassProcs
1743 : s->numProcesses;
1745 minServerLife = (s->directive == APP_CLASS_DYNAMIC)
1746 ? dynamicMinServerLife
1747 : s->minServerLife;
1749 for (i = 0; i < numChildren; ++i)
1751 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1753 int restart = (s->procs[i].pid < 0);
1754 time_t restartTime = s->restartTime;
1756 if (s->bad)
1758 /* we've gone to using the badDelay, the only thing that
1759 resets bad is when badDelay has expired. but numFailures
1760 is only just set below its threshold. the proc's
1761 start_times are all reset when the bad is. the numFailures
1762 is reset when we see an app run for a period */
1764 s->procs[i].start_time = 0;
1767 if (s->numFailures > MAX_FAILED_STARTS)
1769 time_t last_start_time = s->procs[i].start_time;
1771 if (last_start_time && now - last_start_time > minServerLife)
1773 s->bad = 0;
1774 s->numFailures = 0;
1775 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1776 "FastCGI:%s server \"%s\" has remained"
1777 " running for more than %d seconds, its restart"
1778 " interval has been restored to %d seconds",
1779 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1780 s->fs_path, minServerLife, s->restartDelay);
1782 else
1784 unsigned int j;
1786 for (j = 0; j < numChildren; ++j)
1788 if (s->procs[j].pid <= 0) continue;
1789 if (s->procs[j].state != FCGI_RUNNING_STATE) continue;
1790 if (s->procs[j].start_time == 0) continue;
1791 if (now - s->procs[j].start_time > minServerLife) break;
1794 if (j >= numChildren)
1796 s->bad = 1;
1797 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1798 "FastCGI:%s server \"%s\" has failed to remain"
1799 " running for %d seconds given %d attempts, its restart"
1800 " interval has been backed off to %d seconds",
1801 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1802 s->fs_path, minServerLife, MAX_FAILED_STARTS,
1803 FAILED_STARTS_DELAY);
1805 else
1807 s->bad = 0;
1808 s->numFailures = 0;
1809 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1810 "FastCGI:%s server \"%s\" has remained"
1811 " running for more than %d seconds, its restart"
1812 " interval has been restored to %d seconds",
1813 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1814 s->fs_path, minServerLife, s->restartDelay);
1819 if (s->bad)
1821 restartTime += FAILED_STARTS_DELAY;
1823 else
1825 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1828 if (restartTime <= now)
1830 if (s->bad)
1832 s->bad = 0;
1833 s->numFailures = MAX_FAILED_STARTS;
1836 if (s->listenFd < 0 && init_listen_sock(s))
1838 if (sleepSeconds > s->initStartDelay)
1839 sleepSeconds = s->initStartDelay;
1840 break;
1842 #ifndef WIN32
1843 if (caughtSigTerm) {
1844 goto ProcessSigTerm;
1846 #endif
1847 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1848 if (s->procs[i].pid <= 0) {
1849 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1850 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1851 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1852 s->fs_path);
1854 sleepSeconds = min(sleepSeconds,
1855 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1857 s->procs[i].pid = -1;
1858 break;
1861 s->procs[i].start_time = now;
1862 s->restartTime = now;
1864 if (s->startTime == 0) {
1865 s->startTime = now;
1868 if (s->directive == APP_CLASS_DYNAMIC) {
1869 s->numProcesses++;
1870 fcgi_dynamic_total_proc_count++;
1871 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1874 s->procs[i].state = FCGI_RUNNING_STATE;
1876 if (fcgi_wrapper) {
1877 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1878 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1879 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1880 s->fs_path, (long) s->uid, (long) s->gid,
1881 restart ? "re" : "", (long) s->procs[i].pid);
1883 else {
1884 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1885 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1886 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1887 s->fs_path, restart ? "re" : "", (long) s->procs[i].pid);
1889 ap_assert(s->procs[i].pid > 0);
1890 } else {
1891 sleepSeconds = min(sleepSeconds, restartTime - now);
1897 #ifndef WIN32
1899 if(caughtSigTerm) {
1900 goto ProcessSigTerm;
1902 if((!caughtSigChld) && (!caughtSigAlarm)) {
1903 fd_set rfds;
1905 alarm(sleepSeconds);
1907 FD_ZERO(&rfds);
1908 FD_SET(fcgi_pm_pipe[0], &rfds);
1909 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1911 alarmLeft = alarm(0);
1913 callWaitPid = caughtSigChld;
1914 caughtSigChld = FALSE;
1915 callDynamicProcs = caughtSigAlarm;
1916 caughtSigAlarm = FALSE;
1918 now = time(NULL);
1921 * Dynamic fcgi process management
1923 if((callDynamicProcs) || (!callWaitPid)) {
1924 dynamic_read_msgs(read_ready);
1925 if(fcgi_dynamic_epoch == 0) {
1926 fcgi_dynamic_epoch = now;
1928 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1929 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1930 dynamic_kill_idle_fs_procs();
1931 fcgi_dynamic_epoch = now;
1935 if(!callWaitPid) {
1936 continue;
1939 /* We've caught SIGCHLD, so find out who it was using waitpid,
1940 * write a log message and update its data structure. */
1942 for (;;) {
1943 if (caughtSigTerm)
1944 goto ProcessSigTerm;
1946 childPid = waitpid(-1, &waitStatus, WNOHANG);
1948 if (childPid == -1 || childPid == 0)
1949 break;
1951 for (s = fcgi_servers; s != NULL; s = s->next) {
1952 if (s->directive == APP_CLASS_EXTERNAL)
1953 continue;
1955 if (s->directive == APP_CLASS_DYNAMIC)
1956 numChildren = dynamicMaxClassProcs;
1957 else
1958 numChildren = s->numProcesses;
1960 for (i = 0; i < numChildren; i++) {
1961 if (s->procs[i].pid == childPid)
1962 goto ChildFound;
1966 /* TODO: print something about this unknown child */
1967 continue;
1969 ChildFound:
1970 s->procs[i].pid = -1;
1972 if (s->directive == APP_CLASS_STANDARD) {
1973 /* Always restart static apps */
1974 s->procs[i].state = FCGI_START_STATE;
1975 if (! (WIFEXITED(waitStatus) && (WEXITSTATUS(waitStatus) == 0))) {
1976 /* don't bump the failure count if the app exited with 0 */
1977 s->numFailures++;
1980 else {
1981 s->numProcesses--;
1982 fcgi_dynamic_total_proc_count--;
1984 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1985 s->procs[i].state = FCGI_KILLED_STATE;
1987 else {
1988 /* A dynamic app died or exited without provocation from the PM */
1990 if (! (WIFEXITED(waitStatus) && (WEXITSTATUS(waitStatus) == 0))) {
1991 /* don't bump the failure count if the app exited with 0 */
1992 s->numFailures++;
1995 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1996 s->procs[i].state = FCGI_START_STATE;
1997 else
1998 s->procs[i].state = FCGI_READY_STATE;
2002 if (WIFEXITED(waitStatus)) {
2003 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
2004 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
2005 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
2006 s->fs_path, (long) childPid, WEXITSTATUS(waitStatus));
2008 else if (WIFSIGNALED(waitStatus)) {
2009 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
2010 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
2011 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
2012 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus),
2013 #ifdef WCOREDUMP
2014 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
2015 #else
2016 "");
2017 #endif
2019 else if (WIFSTOPPED(waitStatus)) {
2020 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
2021 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
2022 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
2023 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus));
2025 } /* for (;;), waitpid() */
2027 #else /* WIN32 */
2029 /* wait for an event to occur or timer expires */
2030 expire = time(NULL) + sleepSeconds;
2031 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
2033 if (dwRet == WAIT_FAILED) {
2034 /* There is something seriously wrong here */
2035 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
2036 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
2037 bTimeToDie = TRUE;
2040 if (dwRet != WAIT_TIMEOUT) {
2041 now = time(NULL);
2043 if (now < expire)
2044 alarmLeft = expire - now;
2048 * Dynamic fcgi process management
2050 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
2051 if (dwRet == MBOX_EVENT) {
2052 read_ready = 1;
2055 now = time(NULL);
2057 dynamic_read_msgs(read_ready);
2059 if(fcgi_dynamic_epoch == 0) {
2060 fcgi_dynamic_epoch = now;
2063 if ((now-fcgi_dynamic_epoch >= (int) dynamicKillInterval) ||
2064 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
2065 dynamic_kill_idle_fs_procs();
2066 fcgi_dynamic_epoch = now;
2068 read_ready = 0;
2070 else if (dwRet == WAKE_EVENT) {
2071 continue;
2073 else if (dwRet == TERM_EVENT) {
2074 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
2075 "FastCGI: Termination event received process manager shutting down");
2077 bTimeToDie = TRUE;
2078 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2080 goto ProcessSigTerm;
2082 else {
2083 /* Have an received an unknown event - should not happen */
2084 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
2085 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
2087 bTimeToDie = TRUE;
2088 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2090 goto ProcessSigTerm;
2093 #endif /* WIN32 */
2095 } /* for (;;), the whole shoot'n match */
2097 ProcessSigTerm:
2099 * Kill off the children, then exit.
2101 shutdown_all();
2103 #ifdef WIN32
2104 return;
2105 #else
2106 exit(0);
2107 #endif
2110 #ifdef WIN32
2111 int fcgi_pm_add_job(fcgi_pm_job *new_job)
2113 int rv = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
2115 if (rv != WAIT_OBJECT_0 && rv != WAIT_ABANDONED)
2117 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2118 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2119 return -1;
2122 new_job->next = fcgi_dynamic_mbox;
2123 fcgi_dynamic_mbox = new_job;
2125 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
2127 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2128 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
2131 return 0;
2133 #endif