[*nix] Don't use suexec when there is no user/group in effect.
[mod_fastcgi.git] / fcgi_pm.c
blob31638db3c70833d7addb6cdc0efec5da70a2d734
1 /*
2 * $Id: fcgi_pm.c,v 1.91 2004/04/15 00:32:56 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 if (s->directive == APP_CLASS_STANDARD) {
1542 /* restart static app */
1543 s->procs[i].state = FCGI_START_STATE;
1544 s->numFailures++;
1546 else {
1547 s->numProcesses--;
1548 fcgi_dynamic_total_proc_count--;
1549 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1551 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1552 s->procs[i].state = FCGI_KILLED_STATE;
1554 else {
1555 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1556 s->numFailures++;
1558 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1559 s->procs[i].state = FCGI_START_STATE;
1561 else {
1562 s->procs[i].state = FCGI_READY_STATE;
1567 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1569 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1570 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1571 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1572 s->fs_path, (long) s->procs[i].pid, exitStatus);
1574 CloseHandle(s->procs[i].handle);
1575 CloseHandle(s->procs[i].terminationEvent);
1576 s->procs[i].handle = INVALID_HANDLE_VALUE;
1577 s->procs[i].terminationEvent = INVALID_HANDLE_VALUE;
1578 s->procs[i].pid = -1;
1580 /* wake up the main thread */
1581 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1586 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1589 #endif
1591 #ifndef WIN32
1592 static void setup_signals(void)
1594 struct sigaction sa;
1596 /* Setup handlers */
1598 sa.sa_handler = signal_handler;
1599 sigemptyset(&sa.sa_mask);
1600 sa.sa_flags = 0;
1602 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1603 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1604 "sigaction(SIGTERM) failed");
1606 /* httpd restart */
1607 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1608 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1609 "sigaction(SIGHUP) failed");
1611 /* httpd graceful restart */
1612 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1613 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1614 "sigaction(SIGUSR1) failed");
1616 /* read messages from request handlers - kill interval expired */
1617 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1618 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1619 "sigaction(SIGALRM) failed");
1621 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1622 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1623 "sigaction(SIGCHLD) failed");
1626 #endif
1628 #if !defined(WIN32) && !defined(APACHE2)
1629 int fcgi_pm_main(void *dummy, child_info *info)
1630 #else
1631 void fcgi_pm_main(void *dummy)
1632 #endif
1634 fcgi_server *s;
1635 unsigned int i;
1636 int read_ready = 0;
1637 int alarmLeft = 0;
1639 #ifdef WIN32
1640 DWORD dwRet;
1641 HANDLE child_wait_thread = INVALID_HANDLE_VALUE;
1642 #else
1643 int callWaitPid, callDynamicProcs;
1644 #endif
1646 #ifdef WIN32
1647 /* Add SystemRoot to the dynamic environment */
1648 char ** envp = dynamicEnvp;
1649 for (i = 0; *envp; ++i) {
1650 ++envp;
1652 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1654 #else
1656 reduce_privileges();
1657 change_process_name("fcgi-pm");
1659 close(fcgi_pm_pipe[1]);
1660 setup_signals();
1662 if (fcgi_wrapper) {
1663 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1664 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1666 #endif
1668 /* Initialize AppClass */
1669 for (s = fcgi_servers; s != NULL; s = s->next)
1671 if (s->directive != APP_CLASS_STANDARD)
1672 continue;
1674 #ifdef WIN32
1675 if (s->socket_path)
1676 s->listenFd = 0;
1677 #endif
1679 for (i = 0; i < s->numProcesses; ++i)
1680 s->procs[i].state = FCGI_START_STATE;
1683 #ifdef WIN32
1684 child_wait_thread = (HANDLE) _beginthread(child_wait_thread_main, 0, NULL);
1686 if (child_wait_thread == (HANDLE) -1)
1688 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1689 "FastCGI: failed to create process manager's wait thread!");
1692 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1693 "FastCGI: process manager initialized");
1694 #else
1695 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1696 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1697 #endif
1699 now = time(NULL);
1702 * Loop until SIGTERM
1704 for (;;) {
1705 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1706 #ifdef WIN32
1707 time_t expire;
1708 #else
1709 pid_t childPid;
1710 int waitStatus;
1711 #endif
1712 unsigned int numChildren;
1713 unsigned int minServerLife;
1716 * If we came out of sigsuspend() for any reason other than
1717 * SIGALRM, pick up where we left off.
1719 if (alarmLeft)
1720 sleepSeconds = alarmLeft;
1723 * Examine each configured AppClass for a process that needs
1724 * starting. Compute the earliest time when the start should
1725 * be attempted, starting it now if the time has passed. Also,
1726 * remember that we do NOT need to restart externally managed
1727 * FastCGI applications.
1729 for (s = fcgi_servers; s != NULL; s = s->next)
1731 if (s->directive == APP_CLASS_EXTERNAL)
1732 continue;
1734 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1735 ? dynamicMaxClassProcs
1736 : s->numProcesses;
1738 minServerLife = (s->directive == APP_CLASS_DYNAMIC)
1739 ? dynamicMinServerLife
1740 : s->minServerLife;
1742 for (i = 0; i < numChildren; ++i)
1744 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1746 int restart = (s->procs[i].pid < 0);
1747 time_t restartTime = s->restartTime;
1749 if (s->bad)
1751 /* we've gone to using the badDelay, the only thing that
1752 resets bad is when badDelay has expired. but numFailures
1753 is only just set below its threshold. the proc's
1754 start_times are all reset when the bad is. the numFailures
1755 is reset when we see an app run for a period */
1757 s->procs[i].start_time = 0;
1760 if (s->numFailures > MAX_FAILED_STARTS)
1762 time_t last_start_time = s->procs[i].start_time;
1764 if (last_start_time && now - last_start_time > minServerLife)
1766 s->bad = 0;
1767 s->numFailures = 0;
1768 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1769 "FastCGI:%s server \"%s\" has remained"
1770 " running for more than %d seconds, its restart"
1771 " interval has been restored to %d seconds",
1772 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1773 s->fs_path, minServerLife, s->restartDelay);
1775 else
1777 unsigned int j;
1779 for (j = 0; j < numChildren; ++j)
1781 if (s->procs[j].pid <= 0) continue;
1782 if (s->procs[j].state != FCGI_RUNNING_STATE) continue;
1783 if (s->procs[j].start_time == 0) continue;
1784 if (now - s->procs[j].start_time > minServerLife) break;
1787 if (j >= numChildren)
1789 s->bad = 1;
1790 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1791 "FastCGI:%s server \"%s\" has failed to remain"
1792 " running for %d seconds given %d attempts, its restart"
1793 " interval has been backed off to %d seconds",
1794 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1795 s->fs_path, minServerLife, MAX_FAILED_STARTS,
1796 FAILED_STARTS_DELAY);
1798 else
1800 s->bad = 0;
1801 s->numFailures = 0;
1802 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1803 "FastCGI:%s server \"%s\" has remained"
1804 " running for more than %d seconds, its restart"
1805 " interval has been restored to %d seconds",
1806 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1807 s->fs_path, minServerLife, s->restartDelay);
1812 if (s->bad)
1814 restartTime += FAILED_STARTS_DELAY;
1816 else
1818 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1821 if (restartTime <= now)
1823 if (s->bad)
1825 s->bad = 0;
1826 s->numFailures = MAX_FAILED_STARTS;
1829 if (s->listenFd < 0 && init_listen_sock(s))
1831 if (sleepSeconds > s->initStartDelay)
1832 sleepSeconds = s->initStartDelay;
1833 break;
1835 #ifndef WIN32
1836 if (caughtSigTerm) {
1837 goto ProcessSigTerm;
1839 #endif
1840 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1841 if (s->procs[i].pid <= 0) {
1842 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1843 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1844 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1845 s->fs_path);
1847 sleepSeconds = min(sleepSeconds,
1848 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1850 s->procs[i].pid = -1;
1851 break;
1854 s->procs[i].start_time = now;
1855 s->restartTime = now;
1857 if (s->startTime == 0) {
1858 s->startTime = now;
1861 if (s->directive == APP_CLASS_DYNAMIC) {
1862 s->numProcesses++;
1863 fcgi_dynamic_total_proc_count++;
1864 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1867 s->procs[i].state = FCGI_RUNNING_STATE;
1869 if (fcgi_wrapper) {
1870 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1871 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1872 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1873 s->fs_path, (long) s->uid, (long) s->gid,
1874 restart ? "re" : "", (long) s->procs[i].pid);
1876 else {
1877 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1878 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1879 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1880 s->fs_path, restart ? "re" : "", (long) s->procs[i].pid);
1882 ap_assert(s->procs[i].pid > 0);
1883 } else {
1884 sleepSeconds = min(sleepSeconds, restartTime - now);
1890 #ifndef WIN32
1892 if(caughtSigTerm) {
1893 goto ProcessSigTerm;
1895 if((!caughtSigChld) && (!caughtSigAlarm)) {
1896 fd_set rfds;
1898 alarm(sleepSeconds);
1900 FD_ZERO(&rfds);
1901 FD_SET(fcgi_pm_pipe[0], &rfds);
1902 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1904 alarmLeft = alarm(0);
1906 callWaitPid = caughtSigChld;
1907 caughtSigChld = FALSE;
1908 callDynamicProcs = caughtSigAlarm;
1909 caughtSigAlarm = FALSE;
1911 now = time(NULL);
1914 * Dynamic fcgi process management
1916 if((callDynamicProcs) || (!callWaitPid)) {
1917 dynamic_read_msgs(read_ready);
1918 if(fcgi_dynamic_epoch == 0) {
1919 fcgi_dynamic_epoch = now;
1921 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1922 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1923 dynamic_kill_idle_fs_procs();
1924 fcgi_dynamic_epoch = now;
1928 if(!callWaitPid) {
1929 continue;
1932 /* We've caught SIGCHLD, so find out who it was using waitpid,
1933 * write a log message and update its data structure. */
1935 for (;;) {
1936 if (caughtSigTerm)
1937 goto ProcessSigTerm;
1939 childPid = waitpid(-1, &waitStatus, WNOHANG);
1941 if (childPid == -1 || childPid == 0)
1942 break;
1944 for (s = fcgi_servers; s != NULL; s = s->next) {
1945 if (s->directive == APP_CLASS_EXTERNAL)
1946 continue;
1948 if (s->directive == APP_CLASS_DYNAMIC)
1949 numChildren = dynamicMaxClassProcs;
1950 else
1951 numChildren = s->numProcesses;
1953 for (i = 0; i < numChildren; i++) {
1954 if (s->procs[i].pid == childPid)
1955 goto ChildFound;
1959 /* TODO: print something about this unknown child */
1960 continue;
1962 ChildFound:
1963 s->procs[i].pid = -1;
1965 if (s->directive == APP_CLASS_STANDARD) {
1966 /* Always restart static apps */
1967 s->procs[i].state = FCGI_START_STATE;
1968 s->numFailures++;
1970 else {
1971 s->numProcesses--;
1972 fcgi_dynamic_total_proc_count--;
1974 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1975 s->procs[i].state = FCGI_KILLED_STATE;
1977 else {
1978 /* A dynamic app died or exited without provocation from the PM */
1979 s->numFailures++;
1981 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1982 s->procs[i].state = FCGI_START_STATE;
1983 else
1984 s->procs[i].state = FCGI_READY_STATE;
1988 if (WIFEXITED(waitStatus)) {
1989 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1990 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1991 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1992 s->fs_path, (long) childPid, WEXITSTATUS(waitStatus));
1994 else if (WIFSIGNALED(waitStatus)) {
1995 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1996 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1997 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1998 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus),
1999 #ifdef WCOREDUMP
2000 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
2001 #else
2002 "");
2003 #endif
2005 else if (WIFSTOPPED(waitStatus)) {
2006 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
2007 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
2008 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
2009 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus));
2011 } /* for (;;), waitpid() */
2013 #else /* WIN32 */
2015 /* wait for an event to occur or timer expires */
2016 expire = time(NULL) + sleepSeconds;
2017 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
2019 if (dwRet == WAIT_FAILED) {
2020 /* There is something seriously wrong here */
2021 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
2022 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
2023 bTimeToDie = TRUE;
2026 if (dwRet != WAIT_TIMEOUT) {
2027 now = time(NULL);
2029 if (now < expire)
2030 alarmLeft = expire - now;
2034 * Dynamic fcgi process management
2036 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
2037 if (dwRet == MBOX_EVENT) {
2038 read_ready = 1;
2041 now = time(NULL);
2043 dynamic_read_msgs(read_ready);
2045 if(fcgi_dynamic_epoch == 0) {
2046 fcgi_dynamic_epoch = now;
2049 if ((now-fcgi_dynamic_epoch >= (int) dynamicKillInterval) ||
2050 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
2051 dynamic_kill_idle_fs_procs();
2052 fcgi_dynamic_epoch = now;
2054 read_ready = 0;
2056 else if (dwRet == WAKE_EVENT) {
2057 continue;
2059 else if (dwRet == TERM_EVENT) {
2060 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
2061 "FastCGI: Termination event received process manager shutting down");
2063 bTimeToDie = TRUE;
2064 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2066 goto ProcessSigTerm;
2068 else {
2069 /* Have an received an unknown event - should not happen */
2070 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
2071 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
2073 bTimeToDie = TRUE;
2074 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2076 goto ProcessSigTerm;
2079 #endif /* WIN32 */
2081 } /* for (;;), the whole shoot'n match */
2083 ProcessSigTerm:
2085 * Kill off the children, then exit.
2087 shutdown_all();
2089 #ifdef WIN32
2090 return;
2091 #else
2092 exit(0);
2093 #endif
2096 #ifdef WIN32
2097 int fcgi_pm_add_job(fcgi_pm_job *new_job)
2099 int rv = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
2101 if (rv != WAIT_OBJECT_0 && rv != WAIT_ABANDONED)
2103 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2104 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2105 return -1;
2108 new_job->next = fcgi_dynamic_mbox;
2109 fcgi_dynamic_mbox = new_job;
2111 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
2113 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2114 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
2117 return 0;
2119 #endif