Remove all C++ style comments (even in the WIN code)
[mod_fastcgi.git] / fcgi_pm.c
blobf3890786701f2b263751ad0df5ff9f744ded6b3f
1 /*
2 * $Id: fcgi_pm.c,v 1.89 2003/10/30 01:08:34 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 /* AP13 does not use suexec if the target uid/gid is the same as the
416 * server's - AP20 does. I (now) consider the AP2 approach better
417 * (fcgi_pm.c v1.42 incorporated the 1.3 behaviour, v1.84 reverted it,
418 * v1.85 added the compile time option to use the old behaviour). */
420 #ifdef NO_SUEXEC_FOR_AP_USER_N_GROUP
422 if (fcgi_user_id == fs->uid && fcgi_group_id == fs->gid)
424 goto NO_SUEXEC;
427 #endif
428 shortName = strrchr(fs->fs_path, '/') + 1;
430 do {
431 execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group,
432 shortName, NULL, fs->envp);
433 } while (errno == EINTR);
435 else
438 NO_SUEXEC:
439 do {
440 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
441 } while (errno == EINTR);
444 failedSysCall = "execle()";
446 FailedSystemCallExit:
447 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
448 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
449 exit(-1);
451 /* avoid an irrelevant compiler warning */
452 return(0);
454 #else /* WIN32 */
456 #ifdef APACHE2
458 /* based on mod_cgi.c:run_cgi_child() */
460 apr_pool_t * tp;
461 char * termination_env_string;
462 HANDLE listen_handle = INVALID_HANDLE_VALUE;
463 apr_procattr_t * procattr;
464 apr_proc_t proc = { 0 };
465 apr_file_t * file;
466 int i = 0;
467 cgi_exec_info_t e_info = { 0 };
468 request_rec r = { 0 };
469 const char *command;
470 const char **argv;
471 int rv;
472 APR_OPTIONAL_FN_TYPE(ap_cgi_build_command) *cgi_build_command;
474 cgi_build_command = APR_RETRIEVE_OPTIONAL_FN(ap_cgi_build_command);
475 if (cgi_build_command == NULL)
477 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
478 "FastCGI: can't exec server \"%s\", mod_cgi isn't loaded",
479 fs->fs_path);
480 return 0;
483 if (apr_pool_create(&tp, fcgi_config_pool))
484 return 0;
486 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
487 if (process->terminationEvent == NULL)
488 goto CLEANUP;
490 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
492 termination_env_string = ap_psprintf(tp,
493 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
495 while (fs->envp[i]) i++;
496 fs->envp[i++] = termination_env_string;
497 fs->envp[i] = (char *) fs->mutex_env_string;
499 ap_assert(fs->envp[i + 1] == NULL);
501 if (fs->socket_path)
503 SECURITY_ATTRIBUTES sa = { 0 };
505 sa.bInheritHandle = TRUE;
506 sa.nLength = sizeof(sa);
508 listen_handle = CreateNamedPipe(fs->socket_path,
509 PIPE_ACCESS_DUPLEX,
510 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
511 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
513 if (listen_handle == INVALID_HANDLE_VALUE)
515 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
516 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
517 fs->fs_path);
518 goto CLEANUP;
521 else
523 listen_handle = (HANDLE) fs->listenFd;
526 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
527 r.server = fcgi_apache_main_server;
528 r.filename = (char *) fs->fs_path;
529 r.pool = tp;
530 r.subprocess_env = apr_table_make(tp, 0);
532 e_info.cmd_type = APR_PROGRAM;
534 rv = cgi_build_command(&command, &argv, &r, tp, &e_info);
535 if (rv != APR_SUCCESS)
537 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
538 "FastCGI: don't know how to spawn cmd child process: %s",
539 fs->fs_path);
540 goto CLEANUP;
543 if (apr_procattr_create(&procattr, tp))
544 goto CLEANUP;
546 if (apr_procattr_dir_set(procattr, ap_make_dirstr_parent(tp, fs->fs_path)))
547 goto CLEANUP;
549 if (apr_procattr_cmdtype_set(procattr, e_info.cmd_type))
550 goto CLEANUP;
552 if (apr_procattr_detach_set(procattr, 1))
553 goto CLEANUP;
555 if (apr_os_file_put(&file, &listen_handle, 0, tp))
556 goto CLEANUP;
558 /* procattr is opaque so we have to use this - unfortuantely it dups */
559 if (apr_procattr_child_in_set(procattr, file, NULL))
560 goto CLEANUP;
562 if (apr_proc_create(&proc, command, argv, fs->envp, procattr, tp))
563 goto CLEANUP;
565 process->handle = proc.hproc;
567 CLEANUP:
569 if (fs->socket_path && listen_handle != INVALID_HANDLE_VALUE)
571 CloseHandle(listen_handle);
574 if (i)
576 fs->envp[i - 1] = NULL;
579 ap_destroy_pool(tp);
581 return proc.pid;
583 #else /* WIN32 && !APACHE2 */
585 /* Adapted from Apache's util_script.c ap_call_exec() */
586 char *interpreter = NULL;
587 char *quoted_filename;
588 char *pCommand;
589 char *pEnvBlock, *pNext;
591 int i = 0;
592 int iEnvBlockLen = 1;
594 file_type_e fileType;
596 STARTUPINFO si;
597 PROCESS_INFORMATION pi;
599 request_rec r;
600 pid_t pid = -1;
602 pool * tp = ap_make_sub_pool(fcgi_config_pool);
604 HANDLE listen_handle = INVALID_HANDLE_VALUE;
605 char * termination_env_string = NULL;
607 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
608 if (process->terminationEvent == NULL)
610 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
611 "FastCGI: can't create termination event for server \"%s\", "
612 "CreateEvent() failed", fs->fs_path);
613 goto CLEANUP;
615 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
617 termination_env_string = ap_psprintf(tp,
618 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
620 if (fs->socket_path)
622 SECURITY_ATTRIBUTES sa;
624 sa.lpSecurityDescriptor = NULL;
625 sa.bInheritHandle = TRUE;
626 sa.nLength = sizeof(sa);
628 listen_handle = CreateNamedPipe(fs->socket_path,
629 PIPE_ACCESS_DUPLEX,
630 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
631 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
633 if (listen_handle == INVALID_HANDLE_VALUE)
635 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
636 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
637 goto CLEANUP;
640 else
642 listen_handle = (HANDLE) fs->listenFd;
645 memset(&si, 0, sizeof(si));
646 memset(&pi, 0, sizeof(pi));
647 memset(&r, 0, sizeof(r));
649 /* Can up a fake request to pass to ap_get_win32_interpreter() */
650 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
651 r.server = fcgi_apache_main_server;
652 r.filename = (char *) fs->fs_path;
653 r.pool = tp;
655 fileType = ap_get_win32_interpreter(&r, &interpreter);
657 if (fileType == eFileTypeUNKNOWN) {
658 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
659 "FastCGI: %s is not executable; ensure interpreted scripts have "
660 "\"#!\" as their first line",
661 fs->fs_path);
662 ap_destroy_pool(tp);
663 goto CLEANUP;
667 * We have the interpreter (if there is one) and we have
668 * the arguments (if there are any).
669 * Build the command string to pass to CreateProcess.
671 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
672 if (interpreter && *interpreter) {
673 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
675 else {
676 pCommand = quoted_filename;
680 * Make child process use hPipeOutputWrite as standard out,
681 * and make sure it does not show on screen.
683 si.cb = sizeof(si);
684 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
685 si.wShowWindow = SW_HIDE;
686 si.hStdInput = listen_handle;
688 /* XXX These should be open to the error_log */
689 si.hStdOutput = INVALID_HANDLE_VALUE;
690 si.hStdError = INVALID_HANDLE_VALUE;
693 * Win32's CreateProcess call requires that the environment
694 * be passed in an environment block, a null terminated block of
695 * null terminated strings.
696 * @todo we should store the env in this format for win32.
698 while (fs->envp[i])
700 iEnvBlockLen += strlen(fs->envp[i]) + 1;
701 i++;
704 iEnvBlockLen += strlen(termination_env_string) + 1;
705 iEnvBlockLen += strlen(fs->mutex_env_string) + 1;
707 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
709 i = 0;
710 pNext = pEnvBlock;
711 while (fs->envp[i])
713 strcpy(pNext, fs->envp[i]);
714 pNext += strlen(pNext) + 1;
715 i++;
718 strcpy(pNext, termination_env_string);
719 pNext += strlen(pNext) + 1;
720 strcpy(pNext, fs->mutex_env_string);
722 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
724 pEnvBlock,
725 ap_make_dirstr_parent(tp, fs->fs_path),
726 &si, &pi))
728 /* Hack to get 16-bit CGI's working. It works for all the
729 * standard modules shipped with Apache. pi.dwProcessId is 0
730 * for 16-bit CGIs and all the Unix specific code that calls
731 * ap_call_exec interprets this as a failure case. And we can't
732 * use -1 either because it is mapped to 0 by the caller.
734 pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId;
736 process->handle = pi.hProcess;
737 CloseHandle(pi.hThread);
740 CLEANUP:
742 if (fs->socket_path && listen_handle != INVALID_HANDLE_VALUE)
744 CloseHandle(listen_handle);
747 ap_destroy_pool(tp);
749 return pid;
751 #endif /* !APACHE2 */
752 #endif /* WIN32 */
755 #ifndef WIN32
756 static void reduce_privileges(void)
758 const char *name;
760 if (geteuid() != 0)
761 return;
763 #ifndef __EMX__
764 /* Get username if passed as a uid */
765 if (ap_user_name[0] == '#') {
766 uid_t uid = atoi(&ap_user_name[1]);
767 struct passwd *ent = getpwuid(uid);
769 if (ent == NULL) {
770 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
771 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
772 "you probably need to modify the User directive", (unsigned)uid);
773 exit(1);
775 name = ent->pw_name;
777 else
778 name = ap_user_name;
780 /* Change Group */
781 if (setgid(ap_group_id) == -1) {
782 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
783 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
784 exit(1);
787 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
789 /* Initialize supplementary groups */
790 if (initgroups(name, ap_group_id) == -1) {
791 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
792 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
793 name, (unsigned)ap_group_id);
794 exit(1);
796 #endif /* __EMX__ */
798 /* Change User */
799 if (fcgi_wrapper) {
800 if (seteuid_user() == -1) {
801 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
802 "FastCGI: process manager exiting, failed to reduce privileges");
803 exit(1);
806 else {
807 if (setuid(ap_user_id) == -1) {
808 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
809 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
810 exit(1);
815 /*************
816 * Change the name of this process - best we can easily.
818 static void change_process_name(const char * const name)
820 /* under Apache2, ap_server_argv0 is const */
821 strncpy((char *) ap_server_argv0, name, strlen(ap_server_argv0));
823 #endif /* !WIN32 */
825 static void schedule_start(fcgi_server *s, int proc)
827 /* If we've started one recently, don't register another */
828 time_t time_passed = now - s->restartTime;
830 if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay))
831 || ((s->procs[proc].pid == 0) && (time_passed < s->initStartDelay)))
833 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);
834 return;
837 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
838 s->procs[proc].state = FCGI_START_STATE;
839 if (proc == dynamicMaxClassProcs - 1) {
840 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
841 "FastCGI: scheduled the %sstart of the last (dynamic) server "
842 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
843 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
848 *----------------------------------------------------------------------
850 * dynamic_read_msgs
852 * Removes the records written by request handlers and decodes them.
853 * We also update the data structures to reflect the changes.
855 *----------------------------------------------------------------------
858 static void dynamic_read_msgs(int read_ready)
860 fcgi_server *s;
861 int rc;
863 #ifndef WIN32
864 static int buflen = 0;
865 static char buf[FCGI_MSGS_BUFSIZE + 1];
866 char *ptr1, *ptr2, opcode;
867 char execName[FCGI_MAXPATH + 1];
868 char user[MAX_USER_NAME_LEN + 2];
869 char group[MAX_GID_CHAR_LEN + 1];
870 unsigned long q_usec = 0UL, req_usec = 0UL;
871 #else
872 fcgi_pm_job *joblist = NULL;
873 fcgi_pm_job *cjob = NULL;
874 #endif
876 pool *sp = NULL, *tp;
878 #ifndef WIN32
879 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
880 #endif
883 * To prevent the idle application from running indefinitely, we
884 * check the timer and if it is expired, we recompute the values
885 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
886 * message is received, only updates are made to the data structures.
888 if (fcgi_dynamic_last_analyzed == 0) {
889 fcgi_dynamic_last_analyzed = now;
891 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
892 for (s = fcgi_servers; s != NULL; s = s->next) {
893 if (s->directive != APP_CLASS_DYNAMIC)
894 break;
896 /* Advance the last analyzed timestamp by the elapsed time since
897 * it was last set. Round the increase down to the nearest
898 * multiple of dynamicUpdateInterval */
900 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
901 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
902 s->totalConnTime = 0UL;
903 s->totalQueueTime = 0UL;
907 if (read_ready <= 0) {
908 return;
911 #ifndef WIN32
912 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
913 if (rc <= 0) {
914 if (!caughtSigTerm) {
915 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
916 "FastCGI: read() from pipe failed (%d)", rc);
917 if (rc == 0) {
918 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
919 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
920 caughtSigTerm = TRUE;
923 return;
925 buflen += rc;
926 buf[buflen] = '\0';
928 #else
930 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
931 * request to do something) and/or when a timeout expires.
932 * There really should be no reason why this wait would get stuck
933 * but there's no point in waiting forever. */
935 rc = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
937 if (rc != WAIT_OBJECT_0 && rc != WAIT_ABANDONED)
939 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
940 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
941 return;
944 joblist = fcgi_dynamic_mbox;
945 fcgi_dynamic_mbox = NULL;
947 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
949 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
950 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
953 cjob = joblist;
954 #endif
956 #ifdef APACHE2
957 apr_pool_create(&tp, fcgi_config_pool);
958 #else
959 tp = ap_make_sub_pool(fcgi_config_pool);
960 #endif
962 #ifndef WIN32
963 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
964 int scan_failed = 0;
966 ptr2 = strchr(ptr1, '*');
967 if (ptr2) {
968 *ptr2++ = '\0';
970 else {
971 break;
974 opcode = *ptr1;
976 switch (opcode)
978 case FCGI_SERVER_START_JOB:
979 case FCGI_SERVER_RESTART_JOB:
981 if (sscanf(ptr1, "%c %s %16s %15s",
982 &opcode, execName, user, group) != 4)
984 scan_failed = 1;
986 break;
988 case FCGI_REQUEST_TIMEOUT_JOB:
990 if (sscanf(ptr1, "%c %s %16s %15s",
991 &opcode, execName, user, group) != 4)
993 scan_failed = 1;
995 break;
997 case FCGI_REQUEST_COMPLETE_JOB:
999 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
1000 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
1002 scan_failed = 1;
1004 break;
1006 default:
1008 scan_failed = 1;
1009 break;
1012 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode, execName, user, group, q_usec, req_usec);
1014 if (scan_failed) {
1015 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1016 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
1017 goto NextJob;
1019 #else
1020 /* Update data structures for processing */
1021 while (cjob != NULL) {
1022 joblist = cjob->next;
1023 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
1024 #endif
1026 #ifndef WIN32
1027 s = fcgi_util_fs_get(execName, user, group);
1028 #else
1029 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
1030 #endif
1032 #ifndef WIN32
1033 if (s==NULL && opcode != FCGI_REQUEST_COMPLETE_JOB)
1034 #else
1035 if (s==NULL && cjob->id != FCGI_REQUEST_COMPLETE_JOB)
1036 #endif
1038 #ifdef WIN32
1040 HANDLE mutex = CreateMutex(NULL, FALSE, cjob->fs_path);
1042 if (mutex == NULL)
1044 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1045 "FastCGI: can't create accept mutex "
1046 "for (dynamic) server \"%s\"", cjob->fs_path);
1047 goto BagNewServer;
1050 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
1051 #else
1052 const char *err;
1053 #endif
1055 /* Create a perm subpool to hold the new server data,
1056 * we can destroy it if something doesn't pan out */
1057 #ifdef APACHE2
1058 apr_pool_create(&sp, fcgi_config_pool);
1059 #else
1060 sp = ap_make_sub_pool(fcgi_config_pool);
1061 #endif
1063 /* Create a new "dynamic" server */
1064 s = fcgi_util_fs_new(sp);
1066 s->directive = APP_CLASS_DYNAMIC;
1067 s->restartDelay = dynamicRestartDelay;
1068 s->listenQueueDepth = dynamicListenQueueDepth;
1069 s->initStartDelay = dynamicInitStartDelay;
1070 s->envp = dynamicEnvp;
1071 s->flush = dynamicFlush;
1073 #ifdef WIN32
1074 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
1075 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
1076 #else
1077 s->fs_path = ap_pstrdup(sp, execName);
1078 #endif
1079 ap_getparents(s->fs_path);
1080 ap_no2slash(s->fs_path);
1081 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
1083 /* XXX the socket_path (both Unix and Win) *is* deducible and
1084 * thus can and will be used by other apache instances without
1085 * the use of shared data regarding the processes serving the
1086 * requests. This can result in slightly unintuitive process
1087 * counts and security implications. This is prevented
1088 * if suexec (Unix) is in use. This is both a feature and a flaw.
1089 * Changing it now would break existing installations. */
1091 #ifndef WIN32
1092 /* Create socket file's path */
1093 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
1094 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1096 /* Create sockaddr, prealloc it so it won't get created in tp */
1097 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
1098 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
1099 &s->socket_addr_len, s->socket_path);
1100 if (err) {
1101 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1102 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
1103 goto BagNewServer;
1106 if (init_listen_sock(s)) {
1107 goto BagNewServer;
1110 /* If a wrapper is being used, config user/group info */
1111 if (fcgi_wrapper) {
1112 if (user[0] == '~') {
1113 /* its a user dir uri, the rest is a username, not a uid */
1114 struct passwd *pw = getpwnam(&user[1]);
1116 if (!pw) {
1117 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1118 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1119 execName, &user[1]);
1120 goto BagNewServer;
1122 s->uid = pw->pw_uid;
1123 s->user = ap_pstrdup(sp, user);
1124 s->username = s->user;
1126 s->gid = pw->pw_gid;
1127 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
1129 else {
1130 struct passwd *pw;
1132 s->uid = (uid_t)atol(user);
1133 pw = getpwuid(s->uid);
1134 if (!pw) {
1135 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1136 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1137 execName, (long)s->uid);
1138 goto BagNewServer;
1140 s->user = ap_pstrdup(sp, user);
1141 s->username = ap_pstrdup(sp, pw->pw_name);
1143 s->gid = (gid_t)atol(group);
1144 s->group = ap_pstrdup(sp, group);
1147 #else
1148 /* Create socket file's path */
1149 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
1150 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1151 s->listenFd = 0;
1152 #endif
1154 fcgi_util_fs_add(s);
1156 else {
1157 #ifndef WIN32
1158 if (opcode == FCGI_SERVER_RESTART_JOB) {
1159 #else
1160 if (cjob->id==FCGI_SERVER_RESTART_JOB) {
1161 #endif
1162 /* Check to see if the binary has changed. If so,
1163 * kill the FCGI application processes, and
1164 * restart them.
1166 struct stat stbuf;
1167 int i;
1168 #ifdef WIN32
1169 char * app_path = cjob->fs_path;
1170 #else
1171 char * app_path = execName;
1172 #endif
1174 if (stat(app_path, &stbuf) == 0 && stbuf.st_mtime > s->startTime)
1176 int do_restart = 0;
1178 /* prevent addition restart requests */
1179 s->startTime = now;
1180 #ifndef WIN32
1181 utime(s->socket_path, NULL);
1182 #endif
1184 /* kill old server(s) */
1185 for (i = 0; i < dynamicMaxClassProcs; i++)
1187 if (s->procs[i].pid > 0
1188 && stbuf.st_mtime > s->procs[i].start_time)
1190 fcgi_kill(&s->procs[i], SIGTERM);
1191 do_restart++;
1195 if (do_restart)
1197 ap_log_error(FCGI_LOG_WARN_NOERRNO,
1198 fcgi_apache_main_server, "FastCGI: restarting "
1199 "old server \"%s\" processes, newer version "
1200 "found", app_path);
1204 /* If dynamicAutoRestart, don't mark any new processes
1205 * for starting because we probably got the
1206 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1207 * will be restarting all of those we just killed.
1209 if (dynamicAutoRestart)
1210 goto NextJob;
1212 #ifndef WIN32
1213 else if (opcode == FCGI_SERVER_START_JOB) {
1214 #else
1215 else if (cjob->id==FCGI_SERVER_START_JOB) {
1216 #endif
1217 /* we've been asked to start a process--only start
1218 * it if we're not already running at least one
1219 * instance.
1221 int i;
1223 for (i = 0; i < dynamicMaxClassProcs; i++) {
1224 if (s->procs[i].state == FCGI_RUNNING_STATE)
1225 break;
1227 /* if already running, don't start another one */
1228 if (i < dynamicMaxClassProcs) {
1229 goto NextJob;
1234 #ifndef WIN32
1235 switch (opcode)
1236 #else
1237 switch (cjob->id)
1238 #endif
1240 int i, start;
1242 case FCGI_SERVER_RESTART_JOB:
1244 start = FALSE;
1246 /* We just waxed 'em all. Try to find an idle slot. */
1248 for (i = 0; i < dynamicMaxClassProcs; ++i)
1250 if (s->procs[i].state == FCGI_START_STATE
1251 || s->procs[i].state == FCGI_RUNNING_STATE)
1253 break;
1255 else if (s->procs[i].state == FCGI_KILLED_STATE
1256 || s->procs[i].state == FCGI_READY_STATE)
1258 start = TRUE;
1259 break;
1263 /* Nope, just use the first slot */
1264 if (i == dynamicMaxClassProcs)
1266 start = TRUE;
1267 i = 0;
1270 if (start)
1272 schedule_start(s, i);
1275 break;
1277 case FCGI_SERVER_START_JOB:
1278 case FCGI_REQUEST_TIMEOUT_JOB:
1280 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1282 * Extra instances should have been
1283 * terminated beforehand, probably need
1284 * to increase ProcessSlack parameter
1286 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1287 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1288 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1289 goto NextJob;
1292 /* find next free slot */
1293 for (i = 0; i < dynamicMaxClassProcs; i++)
1295 if (s->procs[i].state == FCGI_START_STATE)
1297 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1298 break;
1300 else if (s->procs[i].state == FCGI_RUNNING_STATE)
1302 continue;
1305 schedule_start(s, i);
1306 break;
1309 #ifdef FCGI_DEBUG
1310 if (i >= dynamicMaxClassProcs) {
1311 FCGIDBG1("ignore_job: slots are max'd");
1313 #endif
1314 break;
1315 case FCGI_REQUEST_COMPLETE_JOB:
1316 /* only record stats if we have a structure */
1317 if (s) {
1318 #ifndef WIN32
1319 s->totalConnTime += req_usec;
1320 s->totalQueueTime += q_usec;
1321 #else
1322 s->totalConnTime += cjob->start_time;
1323 s->totalQueueTime += cjob->qsec;
1324 #endif
1326 break;
1329 NextJob:
1331 #ifdef WIN32
1332 /* Cleanup job data */
1333 free(cjob->fs_path);
1334 free(cjob->user);
1335 free(cjob->group);
1336 free(cjob);
1337 cjob = joblist;
1338 #endif
1340 continue;
1342 BagNewServer:
1343 if (sp) ap_destroy_pool(sp);
1345 #ifdef WIN32
1346 free(cjob->fs_path);
1347 free(cjob);
1348 cjob = joblist;
1349 #endif
1352 #ifndef WIN32
1353 if (ptr1 == buf) {
1354 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1355 "FastCGI: really bogus message: \"%s\"", ptr1);
1356 ptr1 += strlen(buf);
1359 buflen -= ptr1 - buf;
1360 if (buflen) {
1361 memmove(buf, ptr1, buflen);
1363 #endif
1365 ap_destroy_pool(tp);
1369 *----------------------------------------------------------------------
1371 * dynamic_kill_idle_fs_procs
1373 * Implement a kill policy for the dynamic FastCGI applications.
1374 * We also update the data structures to reflect the changes.
1376 * Side effects:
1377 * Processes are marked for deletion possibly killed.
1379 *----------------------------------------------------------------------
1381 static void dynamic_kill_idle_fs_procs(void)
1383 fcgi_server *s;
1384 int victims = 0;
1386 for (s = fcgi_servers; s != NULL; s = s->next)
1389 * server's smoothed running time, or if that's 0, the current total
1391 unsigned long connTime;
1394 * maximum number of microseconds that all of a server's running
1395 * processes together could have spent running since the last check
1397 unsigned long totalTime;
1400 * percentage, 0-100, of totalTime that the processes actually used
1402 int loadFactor;
1404 int i;
1405 int really_running = 0;
1407 if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1409 continue;
1412 /* s->numProcesses includes pending kills so get the "active" count */
1413 for (i = 0; i < dynamicMaxClassProcs; ++i)
1415 if (s->procs[i].state == FCGI_RUNNING_STATE) ++really_running;
1418 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1419 totalTime = really_running * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1421 loadFactor = 100 * connTime / totalTime;
1423 if (really_running == 1)
1425 if (loadFactor >= dynamicThreshold1)
1427 continue;
1430 else
1432 int load = really_running / ( really_running - 1) * loadFactor;
1434 if (load >= dynamicThresholdN)
1436 continue;
1441 * Run through the procs to see if we can get away w/o waxing one.
1443 for (i = 0; i < dynamicMaxClassProcs; ++i)
1445 if (s->procs[i].state == FCGI_START_STATE)
1447 s->procs[i].state = FCGI_READY_STATE;
1448 break;
1450 else if (s->procs[i].state == FCGI_VICTIM_STATE)
1452 break;
1456 if (i >= dynamicMaxClassProcs)
1458 ServerProcess * procs = s->procs;
1459 int youngest = -1;
1461 for (i = 0; i < dynamicMaxClassProcs; ++i)
1463 if (procs[i].state == FCGI_RUNNING_STATE)
1465 if (youngest == -1 || procs[i].start_time >= procs[youngest].start_time)
1467 youngest = i;
1472 if (youngest != -1)
1474 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1475 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1476 s->fs_path, (long) s->procs[youngest].pid);
1478 fcgi_kill(&s->procs[youngest], SIGTERM);
1480 victims++;
1484 * If the number of non-victims is less than or equal to
1485 * the minimum that may be running without being killed off,
1486 * don't select any more victims.
1488 if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1490 break;
1496 #ifdef WIN32
1498 /* This is a little bogus, there's gotta be a better way to do this
1499 * Can we use WaitForMultipleObjects() */
1500 #define FCGI_PROC_WAIT_TIME 100
1502 void child_wait_thread_main(void *dummy) {
1503 fcgi_server *s;
1504 DWORD dwRet = WAIT_TIMEOUT;
1505 int numChildren;
1506 int i;
1507 int waited;
1509 while (!bTimeToDie) {
1510 waited = 0;
1512 for (s = fcgi_servers; s != NULL; s = s->next) {
1513 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1514 continue;
1516 if (s->directive == APP_CLASS_DYNAMIC) {
1517 numChildren = dynamicMaxClassProcs;
1519 else {
1520 numChildren = s->numProcesses;
1523 for (i=0; i < numChildren; i++) {
1524 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1526 DWORD exitStatus = 0;
1528 /* timeout is currently set for 100 miliecond */
1529 /* it may need to be longer or user customizable */
1530 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1532 waited = 1;
1534 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1535 /* a child fs has died */
1536 /* mark the child as dead */
1538 if (s->directive == APP_CLASS_STANDARD) {
1539 /* restart static app */
1540 s->procs[i].state = FCGI_START_STATE;
1541 s->numFailures++;
1543 else {
1544 s->numProcesses--;
1545 fcgi_dynamic_total_proc_count--;
1546 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1548 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1549 s->procs[i].state = FCGI_KILLED_STATE;
1551 else {
1552 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1553 s->numFailures++;
1555 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1556 s->procs[i].state = FCGI_START_STATE;
1558 else {
1559 s->procs[i].state = FCGI_READY_STATE;
1564 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1566 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1567 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1568 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1569 s->fs_path, (long) s->procs[i].pid, exitStatus);
1571 CloseHandle(s->procs[i].handle);
1572 CloseHandle(s->procs[i].terminationEvent);
1573 s->procs[i].handle = INVALID_HANDLE_VALUE;
1574 s->procs[i].terminationEvent = INVALID_HANDLE_VALUE;
1575 s->procs[i].pid = -1;
1577 /* wake up the main thread */
1578 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1583 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1586 #endif
1588 #ifndef WIN32
1589 static void setup_signals(void)
1591 struct sigaction sa;
1593 /* Setup handlers */
1595 sa.sa_handler = signal_handler;
1596 sigemptyset(&sa.sa_mask);
1597 sa.sa_flags = 0;
1599 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1600 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1601 "sigaction(SIGTERM) failed");
1603 /* httpd restart */
1604 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1605 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1606 "sigaction(SIGHUP) failed");
1608 /* httpd graceful restart */
1609 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1610 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1611 "sigaction(SIGUSR1) failed");
1613 /* read messages from request handlers - kill interval expired */
1614 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1615 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1616 "sigaction(SIGALRM) failed");
1618 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1619 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1620 "sigaction(SIGCHLD) failed");
1623 #endif
1625 #if !defined(WIN32) && !defined(APACHE2)
1626 int fcgi_pm_main(void *dummy, child_info *info)
1627 #else
1628 void fcgi_pm_main(void *dummy)
1629 #endif
1631 fcgi_server *s;
1632 unsigned int i;
1633 int read_ready = 0;
1634 int alarmLeft = 0;
1636 #ifdef WIN32
1637 DWORD dwRet;
1638 HANDLE child_wait_thread = INVALID_HANDLE_VALUE;
1639 #else
1640 int callWaitPid, callDynamicProcs;
1641 #endif
1643 #ifdef WIN32
1644 /* Add SystemRoot to the dynamic environment */
1645 char ** envp = dynamicEnvp;
1646 for (i = 0; *envp; ++i) {
1647 ++envp;
1649 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1651 #else
1653 reduce_privileges();
1654 change_process_name("fcgi-pm");
1656 close(fcgi_pm_pipe[1]);
1657 setup_signals();
1659 if (fcgi_wrapper) {
1660 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1661 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1663 #endif
1665 /* Initialize AppClass */
1666 for (s = fcgi_servers; s != NULL; s = s->next)
1668 if (s->directive != APP_CLASS_STANDARD)
1669 continue;
1671 #ifdef WIN32
1672 if (s->socket_path)
1673 s->listenFd = 0;
1674 #endif
1676 for (i = 0; i < s->numProcesses; ++i)
1677 s->procs[i].state = FCGI_START_STATE;
1680 #ifdef WIN32
1681 child_wait_thread = (HANDLE) _beginthread(child_wait_thread_main, 0, NULL);
1683 if (child_wait_thread == (HANDLE) -1)
1685 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1686 "FastCGI: failed to create process manager's wait thread!");
1689 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1690 "FastCGI: process manager initialized");
1691 #else
1692 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1693 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1694 #endif
1696 now = time(NULL);
1699 * Loop until SIGTERM
1701 for (;;) {
1702 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1703 #ifdef WIN32
1704 time_t expire;
1705 #else
1706 pid_t childPid;
1707 int waitStatus;
1708 #endif
1709 unsigned int numChildren;
1712 * If we came out of sigsuspend() for any reason other than
1713 * SIGALRM, pick up where we left off.
1715 if (alarmLeft)
1716 sleepSeconds = alarmLeft;
1719 * Examine each configured AppClass for a process that needs
1720 * starting. Compute the earliest time when the start should
1721 * be attempted, starting it now if the time has passed. Also,
1722 * remember that we do NOT need to restart externally managed
1723 * FastCGI applications.
1725 for (s = fcgi_servers; s != NULL; s = s->next)
1727 if (s->directive == APP_CLASS_EXTERNAL)
1728 continue;
1730 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1731 ? dynamicMaxClassProcs
1732 : s->numProcesses;
1734 for (i = 0; i < numChildren; ++i)
1736 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1738 int restart = (s->procs[i].pid < 0);
1739 time_t restartTime = s->restartTime;
1741 if (s->bad)
1743 /* we've gone to using the badDelay, the only thing that
1744 resets bad is when badDelay has expired. but numFailures
1745 is only just set below its threshold. the proc's
1746 start_times are all reset when the bad is. the numFailures
1747 is reset when we see an app run for a period */
1749 s->procs[i].start_time = 0;
1752 if (s->numFailures > MAX_FAILED_STARTS)
1754 time_t last_start_time = s->procs[i].start_time;
1756 if (last_start_time && now - last_start_time > RUNTIME_SUCCESS_INTERVAL)
1758 s->bad = 0;
1759 s->numFailures = 0;
1760 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1761 "FastCGI:%s server \"%s\" has remained"
1762 " running for more than %d seconds, its restart"
1763 " interval has been restored to %d seconds",
1764 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1765 s->fs_path, RUNTIME_SUCCESS_INTERVAL, s->restartDelay);
1767 else
1769 unsigned int j;
1771 for (j = 0; j < numChildren; ++j)
1773 if (s->procs[j].pid <= 0) continue;
1774 if (s->procs[j].state != FCGI_RUNNING_STATE) continue;
1775 if (s->procs[j].start_time == 0) continue;
1776 if (now - s->procs[j].start_time > RUNTIME_SUCCESS_INTERVAL) break;
1779 if (j >= numChildren)
1781 s->bad = 1;
1782 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1783 "FastCGI:%s server \"%s\" has failed to remain"
1784 " running for %d seconds given %d attempts, its restart"
1785 " interval has been backed off to %d seconds",
1786 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1787 s->fs_path, RUNTIME_SUCCESS_INTERVAL, MAX_FAILED_STARTS,
1788 FAILED_STARTS_DELAY);
1790 else
1792 s->bad = 0;
1793 s->numFailures = 0;
1794 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1795 "FastCGI:%s server \"%s\" has remained"
1796 " running for more than %d seconds, its restart"
1797 " interval has been restored to %d seconds",
1798 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1799 s->fs_path, RUNTIME_SUCCESS_INTERVAL, s->restartDelay);
1804 if (s->bad)
1806 restartTime += FAILED_STARTS_DELAY;
1808 else
1810 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1813 if (restartTime <= now)
1815 if (s->bad)
1817 s->bad = 0;
1818 s->numFailures = MAX_FAILED_STARTS;
1821 if (s->listenFd < 0 && init_listen_sock(s))
1823 if (sleepSeconds > s->initStartDelay)
1824 sleepSeconds = s->initStartDelay;
1825 break;
1827 #ifndef WIN32
1828 if (caughtSigTerm) {
1829 goto ProcessSigTerm;
1831 #endif
1832 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1833 if (s->procs[i].pid <= 0) {
1834 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1835 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1836 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1837 s->fs_path);
1839 sleepSeconds = min(sleepSeconds,
1840 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1842 s->procs[i].pid = -1;
1843 break;
1846 s->procs[i].start_time = now;
1847 s->restartTime = now;
1849 if (s->startTime == 0) {
1850 s->startTime = now;
1853 if (s->directive == APP_CLASS_DYNAMIC) {
1854 s->numProcesses++;
1855 fcgi_dynamic_total_proc_count++;
1856 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1859 s->procs[i].state = FCGI_RUNNING_STATE;
1861 if (fcgi_wrapper) {
1862 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1863 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1864 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1865 s->fs_path, (long) s->uid, (long) s->gid,
1866 restart ? "re" : "", (long) s->procs[i].pid);
1868 else {
1869 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1870 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1871 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1872 s->fs_path, restart ? "re" : "", (long) s->procs[i].pid);
1874 ap_assert(s->procs[i].pid > 0);
1875 } else {
1876 sleepSeconds = min(sleepSeconds, restartTime - now);
1882 #ifndef WIN32
1884 if(caughtSigTerm) {
1885 goto ProcessSigTerm;
1887 if((!caughtSigChld) && (!caughtSigAlarm)) {
1888 fd_set rfds;
1890 alarm(sleepSeconds);
1892 FD_ZERO(&rfds);
1893 FD_SET(fcgi_pm_pipe[0], &rfds);
1894 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1896 alarmLeft = alarm(0);
1898 callWaitPid = caughtSigChld;
1899 caughtSigChld = FALSE;
1900 callDynamicProcs = caughtSigAlarm;
1901 caughtSigAlarm = FALSE;
1903 now = time(NULL);
1906 * Dynamic fcgi process management
1908 if((callDynamicProcs) || (!callWaitPid)) {
1909 dynamic_read_msgs(read_ready);
1910 if(fcgi_dynamic_epoch == 0) {
1911 fcgi_dynamic_epoch = now;
1913 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1914 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1915 dynamic_kill_idle_fs_procs();
1916 fcgi_dynamic_epoch = now;
1920 if(!callWaitPid) {
1921 continue;
1924 /* We've caught SIGCHLD, so find out who it was using waitpid,
1925 * write a log message and update its data structure. */
1927 for (;;) {
1928 if (caughtSigTerm)
1929 goto ProcessSigTerm;
1931 childPid = waitpid(-1, &waitStatus, WNOHANG);
1933 if (childPid == -1 || childPid == 0)
1934 break;
1936 for (s = fcgi_servers; s != NULL; s = s->next) {
1937 if (s->directive == APP_CLASS_EXTERNAL)
1938 continue;
1940 if (s->directive == APP_CLASS_DYNAMIC)
1941 numChildren = dynamicMaxClassProcs;
1942 else
1943 numChildren = s->numProcesses;
1945 for (i = 0; i < numChildren; i++) {
1946 if (s->procs[i].pid == childPid)
1947 goto ChildFound;
1951 /* TODO: print something about this unknown child */
1952 continue;
1954 ChildFound:
1955 s->procs[i].pid = -1;
1957 if (s->directive == APP_CLASS_STANDARD) {
1958 /* Always restart static apps */
1959 s->procs[i].state = FCGI_START_STATE;
1960 s->numFailures++;
1962 else {
1963 s->numProcesses--;
1964 fcgi_dynamic_total_proc_count--;
1966 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1967 s->procs[i].state = FCGI_KILLED_STATE;
1969 else {
1970 /* A dynamic app died or exited without provocation from the PM */
1971 s->numFailures++;
1973 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1974 s->procs[i].state = FCGI_START_STATE;
1975 else
1976 s->procs[i].state = FCGI_READY_STATE;
1980 if (WIFEXITED(waitStatus)) {
1981 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1982 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1983 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1984 s->fs_path, (long) childPid, WEXITSTATUS(waitStatus));
1986 else if (WIFSIGNALED(waitStatus)) {
1987 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1988 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1989 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1990 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus),
1991 #ifdef WCOREDUMP
1992 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1993 #else
1994 "");
1995 #endif
1997 else if (WIFSTOPPED(waitStatus)) {
1998 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1999 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
2000 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
2001 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus));
2003 } /* for (;;), waitpid() */
2005 #else /* WIN32 */
2007 /* wait for an event to occur or timer expires */
2008 expire = time(NULL) + sleepSeconds;
2009 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
2011 if (dwRet == WAIT_FAILED) {
2012 /* There is something seriously wrong here */
2013 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
2014 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
2015 bTimeToDie = TRUE;
2018 if (dwRet != WAIT_TIMEOUT) {
2019 now = time(NULL);
2021 if (now < expire)
2022 alarmLeft = expire - now;
2026 * Dynamic fcgi process management
2028 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
2029 if (dwRet == MBOX_EVENT) {
2030 read_ready = 1;
2033 now = time(NULL);
2035 dynamic_read_msgs(read_ready);
2037 if(fcgi_dynamic_epoch == 0) {
2038 fcgi_dynamic_epoch = now;
2041 if ((now-fcgi_dynamic_epoch >= (int) dynamicKillInterval) ||
2042 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
2043 dynamic_kill_idle_fs_procs();
2044 fcgi_dynamic_epoch = now;
2046 read_ready = 0;
2048 else if (dwRet == WAKE_EVENT) {
2049 continue;
2051 else if (dwRet == TERM_EVENT) {
2052 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
2053 "FastCGI: Termination event received process manager shutting down");
2055 bTimeToDie = TRUE;
2056 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2058 goto ProcessSigTerm;
2060 else {
2061 /* Have an received an unknown event - should not happen */
2062 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
2063 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
2065 bTimeToDie = TRUE;
2066 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2068 goto ProcessSigTerm;
2071 #endif /* WIN32 */
2073 } /* for (;;), the whole shoot'n match */
2075 ProcessSigTerm:
2077 * Kill off the children, then exit.
2079 shutdown_all();
2081 #ifdef WIN32
2082 return;
2083 #else
2084 exit(0);
2085 #endif
2088 #ifdef WIN32
2089 int fcgi_pm_add_job(fcgi_pm_job *new_job)
2091 int rv = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
2093 if (rv != WAIT_OBJECT_0 && rv != WAIT_ABANDONED)
2095 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2096 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2097 return -1;
2100 new_job->next = fcgi_dynamic_mbox;
2101 fcgi_dynamic_mbox = new_job;
2103 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
2105 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2106 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
2109 return 0;
2111 #endif