[*nix Security] - When FastCgiWrapper (FastCgiSuexec) was in use
[mod_fastcgi.git] / fcgi_pm.c
bloba453ccedf431a81d522a3eb3eaade4b1dcf59901
1 /*
2 * $Id: fcgi_pm.c,v 1.84 2003/01/19 16:33:51 robs Exp $
3 */
6 #include "fcgi.h"
8 #if defined(APACHE2) && !defined(WIN32)
9 #include <pwd.h>
10 #include <unistd.h>
11 #include "unixd.h"
12 #include "apr_signal.h"
13 #endif
15 #ifndef WIN32
16 #include <utime.h>
17 #endif
19 #ifdef _HPUX_SOURCE
20 #include <unistd.h>
21 #define seteuid(arg) setresuid(-1, (arg), -1)
22 #endif
24 int fcgi_dynamic_total_proc_count = 0; /* number of running apps */
25 time_t fcgi_dynamic_epoch = 0; /* last time kill_procs was
26 * invoked by process mgr */
27 time_t fcgi_dynamic_last_analyzed = 0; /* last time calculation was
28 * made for the dynamic procs */
30 static time_t now = 0;
32 #ifdef WIN32
33 #pragma warning ( disable : 4100 4102 )
34 static BOOL bTimeToDie = FALSE; /* process termination flag */
35 HANDLE fcgi_event_handles[3];
36 #ifndef SIGKILL
37 #define SIGKILL 9
38 #endif
39 #endif
42 #ifndef WIN32
43 static int seteuid_root(void)
45 int rc = seteuid(getuid());
46 if (rc) {
47 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
48 "FastCGI: seteuid(0) failed");
50 return rc;
53 static int seteuid_user(void)
55 int rc = seteuid(ap_user_id);
56 if (rc) {
57 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
58 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id);
60 return rc;
62 #endif
65 * Signal the process to exit. How (or if) the process responds
66 * depends on the FastCGI application library (esp. on Win32) and
67 * possibly application code (signal handlers and whether or not
68 * SA_RESTART is on). At any rate, we send the signal with the
69 * hopes that the process will exit on its own. Later, as we
70 * review the state of application processes, if we see one marked
71 * for death, but that hasn't died within a specified period of
72 * time, fcgi_kill() is called again with a KILL)
74 static void fcgi_kill(ServerProcess *process, int sig)
76 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process->pid, sig);
78 process->state = FCGI_VICTIM_STATE;
80 #ifdef WIN32
82 if (sig == SIGTERM)
84 SetEvent(process->terminationEvent);
86 else if (sig == SIGKILL)
88 TerminateProcess(process->handle, 1);
90 else
92 ap_assert(0);
95 #else /* !WIN32 */
97 if (fcgi_wrapper)
99 seteuid_root();
102 kill(process->pid, sig);
104 if (fcgi_wrapper)
106 seteuid_user();
109 #endif /* !WIN32 */
112 /*******************************************************************************
113 * Send SIGTERM to each process in the server class, remove socket
114 * file if appropriate. Currently this is only called when the PM is shutting
115 * down and thus memory isn't freed and sockets and files aren't closed.
117 static void shutdown_all()
119 fcgi_server *s = fcgi_servers;
121 while (s)
123 ServerProcess *proc = s->procs;
124 int i;
125 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
126 ? dynamicMaxClassProcs
127 : s->numProcesses;
129 #ifndef WIN32
130 if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL)
132 /* Remove the socket file */
133 if (unlink(s->socket_path) != 0 && errno != ENOENT) {
134 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
135 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
136 s->socket_path,
137 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path);
140 #endif
142 /* Send TERM to all processes */
143 for (i = 0; i < numChildren; i++, proc++)
145 if (proc->state == FCGI_RUNNING_STATE)
147 fcgi_kill(proc, SIGTERM);
151 s = s->next;
154 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
157 * WIN32 applications may not have support for the shutdown event
158 * depending on their application library version
161 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT);
162 s = fcgi_servers;
164 while (s)
166 ServerProcess *proc = s->procs;
167 int i;
168 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
169 ? dynamicMaxClassProcs
170 : s->numProcesses;
172 /* Send KILL to all processes */
173 for (i = 0; i < numChildren; i++, proc++)
175 if (proc->state == FCGI_RUNNING_STATE)
177 fcgi_kill(proc, SIGKILL);
181 s = s->next;
184 #endif /* WIN32 */
187 static int init_listen_sock(fcgi_server * fs)
189 ap_assert(fs->directive != APP_CLASS_EXTERNAL);
191 /* Create the socket */
192 if ((fs->listenFd = socket(fs->socket_addr->sa_family, SOCK_STREAM, 0)) < 0)
194 #ifdef WIN32
195 errno = WSAGetLastError(); // Not sure if this will work as expected
196 #endif
197 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
198 "FastCGI: can't create %sserver \"%s\": socket() failed",
199 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
200 fs->fs_path);
201 return -1;
204 #ifndef WIN32
205 if (fs->socket_addr->sa_family == AF_UNIX)
207 /* Remove any existing socket file.. just in case */
208 unlink(((struct sockaddr_un *)fs->socket_addr)->sun_path);
210 else
211 #endif
213 int flag = 1;
214 setsockopt(fs->listenFd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
217 /* Bind it to the socket_addr */
218 if (bind(fs->listenFd, fs->socket_addr, fs->socket_addr_len))
220 char port[11];
222 #ifdef WIN32
223 errno = WSAGetLastError();
224 #endif
225 ap_snprintf(port, sizeof(port), "port=%d",
226 ((struct sockaddr_in *)fs->socket_addr)->sin_port);
228 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
229 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
230 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
231 fs->fs_path,
232 #ifndef WIN32
233 (fs->socket_addr->sa_family == AF_UNIX) ?
234 ((struct sockaddr_un *)fs->socket_addr)->sun_path :
235 #endif
236 port);
239 #ifndef WIN32
240 /* Twiddle Unix socket permissions */
241 else if (fs->socket_addr->sa_family == AF_UNIX
242 && chmod(((struct sockaddr_un *)fs->socket_addr)->sun_path, S_IRUSR | S_IWUSR))
244 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
245 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
246 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
247 fs->fs_path);
249 #endif
251 /* Set to listen */
252 else if (listen(fs->listenFd, fs->listenQueueDepth))
254 #ifdef WIN32
255 errno = WSAGetLastError();
256 #endif
257 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
258 "FastCGI: can't create %sserver \"%s\": listen() failed",
259 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
260 fs->fs_path);
262 else
264 return 0;
267 #ifdef WIN32
268 closesocket(fs->listenFd);
269 #else
270 close(fs->listenFd);
271 #endif
273 fs->listenFd = -1;
275 return -2;
279 *----------------------------------------------------------------------
281 * pm_main
283 * The FastCGI process manager, which runs as a separate
284 * process responsible for:
285 * - Starting all the FastCGI proceses.
286 * - Restarting any of these processes that die (indicated
287 * by SIGCHLD).
288 * - Catching SIGTERM and relaying it to all the FastCGI
289 * processes before exiting.
291 * Inputs:
292 * Uses global variable fcgi_servers.
294 * Results:
295 * Does not return.
297 * Side effects:
298 * Described above.
300 *----------------------------------------------------------------------
302 #ifndef WIN32
303 static int caughtSigTerm = FALSE;
304 static int caughtSigChld = FALSE;
305 static int caughtSigAlarm = FALSE;
307 static void signal_handler(int signo)
309 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
310 /* SIGUSR1 & SIGHUP are sent by apache to its process group
311 * when apache get 'em. Apache follows up (1.2.x) with attacks
312 * on each of its child processes, but we've got the KillMgr
313 * sitting between us so we never see the KILL. The main loop
314 * in ProcMgr also checks to see if the KillMgr has terminated,
315 * and if it has, we handl it as if we should shutdown too. */
316 caughtSigTerm = TRUE;
317 } else if(signo == SIGCHLD) {
318 caughtSigChld = TRUE;
319 } else if(signo == SIGALRM) {
320 caughtSigAlarm = TRUE;
323 #endif
326 *----------------------------------------------------------------------
328 * spawn_fs_process --
330 * Fork and exec the specified fcgi process.
332 * Results:
333 * 0 for successful fork, -1 for failed fork.
335 * In case the child fails before or in the exec, the child
336 * obtains the error log by calling getErrLog, logs
337 * the error, and exits with exit status = errno of
338 * the failed system call.
340 * Side effects:
341 * Child process created.
343 *----------------------------------------------------------------------
345 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
347 #ifndef WIN32
349 pid_t child_pid;
350 int i;
351 char *dirName;
352 char *dnEnd, *failedSysCall;
354 child_pid = fork();
355 if (child_pid) {
356 return child_pid;
359 /* We're the child. We're gonna exec() so pools don't matter. */
361 dnEnd = strrchr(fs->fs_path, '/');
362 if (dnEnd == NULL) {
363 dirName = "./";
364 } else {
365 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
366 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
368 if (chdir(dirName) < 0) {
369 failedSysCall = "chdir()";
370 goto FailedSystemCallExit;
373 #ifndef __EMX__
374 /* OS/2 dosen't support nice() */
375 if (fs->processPriority != 0) {
376 if (nice(fs->processPriority) == -1) {
377 failedSysCall = "nice()";
378 goto FailedSystemCallExit;
381 #endif
383 /* Open the listenFd on spec'd fd */
384 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
385 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
387 /* Close all other open fds, except stdout/stderr. Leave these two open so
388 * FastCGI applications don't have to find and fix ALL 3rd party libs that
389 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
390 * main server error_log - @@@ provide a directive control where this goes.
392 ap_error_log2stderr(fcgi_apache_main_server);
393 dup2(2, 1);
394 for (i = 0; i < FCGI_MAX_FD; i++) {
395 if (i != FCGI_LISTENSOCK_FILENO && i != 2 && i != 1) {
396 close(i);
400 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
401 * install its own handler. */
402 signal(SIGPIPE, SIG_IGN);
404 if (fcgi_wrapper)
406 char *shortName;
408 /* Relinquish our root real uid powers */
409 seteuid_root();
410 setuid(ap_user_id);
412 /* AP13 does not use suexec if the target uid/gid is the same as the
413 * server's - AP20 does. I (now) consider the latter approach better
414 * (fcgi_pm.c v1.42 incorporated the 1.3 behaviour, v1.84 reverted it). */
416 shortName = strrchr(fs->fs_path, '/') + 1;
418 do {
419 execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group,
420 shortName, NULL, fs->envp);
421 } while (errno == EINTR);
423 else {
424 do {
425 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
426 } while (errno == EINTR);
429 failedSysCall = "execle()";
431 FailedSystemCallExit:
432 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
433 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
434 exit(-1);
436 /* avoid an irrelevant compiler warning */
437 return(0);
439 #else /* WIN32 */
441 #ifdef APACHE2
443 /* based on mod_cgi.c:run_cgi_child() */
445 apr_pool_t * tp;
446 char * termination_env_string;
447 HANDLE listen_handle = INVALID_HANDLE_VALUE;
448 apr_procattr_t * procattr;
449 apr_proc_t proc = { 0 };
450 apr_file_t * file;
451 int i = 0;
453 if (apr_pool_create(&tp, fcgi_config_pool))
454 return 0;
456 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
457 if (process->terminationEvent == NULL)
458 goto CLEANUP;
460 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
462 termination_env_string = ap_psprintf(tp,
463 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
465 while (fs->envp[i]) i++;
466 fs->envp[i++] = termination_env_string;
467 fs->envp[i] = (char *) fs->mutex_env_string;
469 ap_assert(fs->envp[i + 1] == NULL);
471 if (fs->socket_path)
473 SECURITY_ATTRIBUTES sa = { 0 };
475 sa.bInheritHandle = TRUE;
476 sa.nLength = sizeof(sa);
478 listen_handle = CreateNamedPipe(fs->socket_path,
479 PIPE_ACCESS_DUPLEX,
480 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
481 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
483 if (listen_handle == INVALID_HANDLE_VALUE)
485 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
486 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
487 fs->fs_path);
488 goto CLEANUP;
491 else
493 listen_handle = (HANDLE) fs->listenFd;
496 if (apr_procattr_create(&procattr, tp))
497 goto CLEANUP;
499 if (apr_procattr_dir_set(procattr, ap_make_dirstr_parent(tp, fs->fs_path)))
500 goto CLEANUP;
502 if (apr_procattr_detach_set(procattr, 1))
503 goto CLEANUP;
505 if (apr_os_file_put(&file, &listen_handle, 0, tp))
506 goto CLEANUP;
508 /* procattr is opaque so we have to use this - unfortuantely it dups */
509 if (apr_procattr_child_in_set(procattr, file, NULL))
510 goto CLEANUP;
512 if (apr_proc_create(&proc, fs->fs_path, NULL, fs->envp, procattr, tp))
513 goto CLEANUP;
515 process->handle = proc.hproc;
518 CLEANUP:
520 if (i)
522 fs->envp[i - 1] = NULL;
525 ap_destroy_pool(tp);
527 return proc.pid;
529 #else /* WIN32 && !APACHE2 */
531 /* Adapted from Apache's util_script.c ap_call_exec() */
532 char *interpreter = NULL;
533 char *quoted_filename;
534 char *pCommand;
535 char *pEnvBlock, *pNext;
537 int i = 0;
538 int iEnvBlockLen = 1;
540 file_type_e fileType;
542 STARTUPINFO si;
543 PROCESS_INFORMATION pi;
545 request_rec r;
546 pid_t pid = -1;
548 pool * tp = ap_make_sub_pool(fcgi_config_pool);
550 HANDLE listen_handle;
551 char * termination_env_string = NULL;
553 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
554 if (process->terminationEvent == NULL)
556 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
557 "FastCGI: can't create termination event for server \"%s\", "
558 "CreateEvent() failed", fs->fs_path);
559 goto CLEANUP;
561 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
563 termination_env_string = ap_psprintf(tp,
564 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
566 if (fs->socket_path)
568 SECURITY_ATTRIBUTES sa;
570 sa.lpSecurityDescriptor = NULL;
571 sa.bInheritHandle = TRUE;
572 sa.nLength = sizeof(sa);
574 listen_handle = CreateNamedPipe(fs->socket_path,
575 PIPE_ACCESS_DUPLEX,
576 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
577 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
579 if (listen_handle == INVALID_HANDLE_VALUE)
581 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
582 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
583 goto CLEANUP;
586 else
588 listen_handle = (HANDLE) fs->listenFd;
591 memset(&si, 0, sizeof(si));
592 memset(&pi, 0, sizeof(pi));
593 memset(&r, 0, sizeof(r));
595 // Can up a fake request to pass to ap_get_win32_interpreter()
596 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
597 r.server = fcgi_apache_main_server;
598 r.filename = (char *) fs->fs_path;
599 r.pool = tp;
601 fileType = ap_get_win32_interpreter(&r, &interpreter);
603 if (fileType == eFileTypeUNKNOWN) {
604 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
605 "FastCGI: %s is not executable; ensure interpreted scripts have "
606 "\"#!\" as their first line",
607 fs->fs_path);
608 ap_destroy_pool(tp);
609 goto CLEANUP;
613 * We have the interpreter (if there is one) and we have
614 * the arguments (if there are any).
615 * Build the command string to pass to CreateProcess.
617 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
618 if (interpreter && *interpreter) {
619 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
621 else {
622 pCommand = quoted_filename;
626 * Make child process use hPipeOutputWrite as standard out,
627 * and make sure it does not show on screen.
629 si.cb = sizeof(si);
630 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
631 si.wShowWindow = SW_HIDE;
632 si.hStdInput = listen_handle;
634 // XXX These should be open to the error_log
635 si.hStdOutput = INVALID_HANDLE_VALUE;
636 si.hStdError = INVALID_HANDLE_VALUE;
639 * Win32's CreateProcess call requires that the environment
640 * be passed in an environment block, a null terminated block of
641 * null terminated strings.
642 * @todo we should store the env in this format for win32.
644 while (fs->envp[i])
646 iEnvBlockLen += strlen(fs->envp[i]) + 1;
647 i++;
650 iEnvBlockLen += strlen(termination_env_string) + 1;
651 iEnvBlockLen += strlen(fs->mutex_env_string) + 1;
653 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
655 i = 0;
656 pNext = pEnvBlock;
657 while (fs->envp[i])
659 strcpy(pNext, fs->envp[i]);
660 pNext += strlen(pNext) + 1;
661 i++;
664 strcpy(pNext, termination_env_string);
665 pNext += strlen(pNext) + 1;
666 strcpy(pNext, fs->mutex_env_string);
668 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
670 pEnvBlock,
671 ap_make_dirstr_parent(tp, fs->fs_path),
672 &si, &pi))
674 /* Hack to get 16-bit CGI's working. It works for all the
675 * standard modules shipped with Apache. pi.dwProcessId is 0
676 * for 16-bit CGIs and all the Unix specific code that calls
677 * ap_call_exec interprets this as a failure case. And we can't
678 * use -1 either because it is mapped to 0 by the caller.
680 pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId;
682 process->handle = pi.hProcess;
683 CloseHandle(pi.hThread);
686 if (fs->socket_path)
688 CloseHandle(listen_handle);
691 CLEANUP:
693 ap_destroy_pool(tp);
695 return pid;
697 #endif /* !APACHE2 */
698 #endif /* WIN32 */
701 #ifndef WIN32
702 static void reduce_privileges(void)
704 const char *name;
706 if (geteuid() != 0)
707 return;
709 #ifndef __EMX__
710 /* Get username if passed as a uid */
711 if (ap_user_name[0] == '#') {
712 uid_t uid = atoi(&ap_user_name[1]);
713 struct passwd *ent = getpwuid(uid);
715 if (ent == NULL) {
716 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
717 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
718 "you probably need to modify the User directive", (unsigned)uid);
719 exit(1);
721 name = ent->pw_name;
723 else
724 name = ap_user_name;
726 /* Change Group */
727 if (setgid(ap_group_id) == -1) {
728 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
729 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
730 exit(1);
733 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
735 /* Initialize supplementary groups */
736 if (initgroups(name, ap_group_id) == -1) {
737 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
738 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
739 name, (unsigned)ap_group_id);
740 exit(1);
742 #endif /* __EMX__ */
744 /* Change User */
745 if (fcgi_wrapper) {
746 if (seteuid_user() == -1) {
747 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
748 "FastCGI: process manager exiting, failed to reduce privileges");
749 exit(1);
752 else {
753 if (setuid(ap_user_id) == -1) {
754 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
755 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
756 exit(1);
761 /*************
762 * Change the name of this process - best we can easily.
764 static void change_process_name(const char * const name)
766 /* under Apache2, ap_server_argv0 is const */
767 strncpy((char *) ap_server_argv0, name, strlen(ap_server_argv0));
769 #endif /* !WIN32 */
771 static void schedule_start(fcgi_server *s, int proc)
773 /* If we've started one recently, don't register another */
774 time_t time_passed = now - s->restartTime;
776 if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay))
777 || ((s->procs[proc].pid == 0) && (time_passed < s->initStartDelay)))
779 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);
780 return;
783 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
784 s->procs[proc].state = FCGI_START_STATE;
785 if (proc == dynamicMaxClassProcs - 1) {
786 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
787 "FastCGI: scheduled the %sstart of the last (dynamic) server "
788 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
789 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
794 *----------------------------------------------------------------------
796 * dynamic_read_msgs
798 * Removes the records written by request handlers and decodes them.
799 * We also update the data structures to reflect the changes.
801 *----------------------------------------------------------------------
804 static void dynamic_read_msgs(int read_ready)
806 fcgi_server *s;
807 int rc;
809 #ifndef WIN32
810 static int buflen = 0;
811 static char buf[FCGI_MSGS_BUFSIZE + 1];
812 char *ptr1, *ptr2, opcode;
813 char execName[FCGI_MAXPATH + 1];
814 char user[MAX_USER_NAME_LEN + 2];
815 char group[MAX_GID_CHAR_LEN + 1];
816 unsigned long q_usec = 0UL, req_usec = 0UL;
817 #else
818 fcgi_pm_job *joblist = NULL;
819 fcgi_pm_job *cjob = NULL;
820 #endif
822 pool *sp = NULL, *tp;
824 #ifndef WIN32
825 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
826 #endif
829 * To prevent the idle application from running indefinitely, we
830 * check the timer and if it is expired, we recompute the values
831 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
832 * message is received, only updates are made to the data structures.
834 if (fcgi_dynamic_last_analyzed == 0) {
835 fcgi_dynamic_last_analyzed = now;
837 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
838 for (s = fcgi_servers; s != NULL; s = s->next) {
839 if (s->directive != APP_CLASS_DYNAMIC)
840 break;
842 /* Advance the last analyzed timestamp by the elapsed time since
843 * it was last set. Round the increase down to the nearest
844 * multiple of dynamicUpdateInterval */
846 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
847 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
848 s->totalConnTime = 0UL;
849 s->totalQueueTime = 0UL;
853 if (read_ready <= 0) {
854 return;
857 #ifndef WIN32
858 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
859 if (rc <= 0) {
860 if (!caughtSigTerm) {
861 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
862 "FastCGI: read() from pipe failed (%d)", rc);
863 if (rc == 0) {
864 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
865 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
866 caughtSigTerm = TRUE;
869 return;
871 buflen += rc;
872 buf[buflen] = '\0';
874 #else
876 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
877 * request to do something) and/or when a timeout expires.
878 * There really should be no reason why this wait would get stuck
879 * but there's no point in waiting forever. */
881 rc = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
883 if (rc != WAIT_OBJECT_0 && rc != WAIT_ABANDONED)
885 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
886 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
887 return;
890 joblist = fcgi_dynamic_mbox;
891 fcgi_dynamic_mbox = NULL;
893 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
895 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
896 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
899 cjob = joblist;
900 #endif
902 #ifdef APACHE2
903 apr_pool_create(&tp, fcgi_config_pool);
904 #else
905 tp = ap_make_sub_pool(fcgi_config_pool);
906 #endif
908 #ifndef WIN32
909 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
910 int scan_failed = 0;
912 ptr2 = strchr(ptr1, '*');
913 if (ptr2) {
914 *ptr2++ = '\0';
916 else {
917 break;
920 opcode = *ptr1;
922 switch (opcode)
924 case FCGI_SERVER_START_JOB:
925 case FCGI_SERVER_RESTART_JOB:
927 if (sscanf(ptr1, "%c %s %16s %15s",
928 &opcode, execName, user, group) != 4)
930 scan_failed = 1;
932 break;
934 case FCGI_REQUEST_TIMEOUT_JOB:
936 if (sscanf(ptr1, "%c %s %16s %15s",
937 &opcode, execName, user, group) != 4)
939 scan_failed = 1;
941 break;
943 case FCGI_REQUEST_COMPLETE_JOB:
945 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
946 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
948 scan_failed = 1;
950 break;
952 default:
954 scan_failed = 1;
955 break;
958 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode, execName, user, group, q_usec, req_usec);
960 if (scan_failed) {
961 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
962 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
963 goto NextJob;
965 #else
966 /* Update data structures for processing */
967 while (cjob != NULL) {
968 joblist = cjob->next;
969 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
970 #endif
972 #ifndef WIN32
973 s = fcgi_util_fs_get(execName, user, group);
974 #else
975 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
976 #endif
978 #ifndef WIN32
979 if (s==NULL && opcode != FCGI_REQUEST_COMPLETE_JOB)
980 #else
981 if (s==NULL && cjob->id != FCGI_REQUEST_COMPLETE_JOB)
982 #endif
984 #ifdef WIN32
986 HANDLE mutex = CreateMutex(NULL, FALSE, cjob->fs_path);
988 if (mutex == NULL)
990 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
991 "FastCGI: can't create accept mutex "
992 "for (dynamic) server \"%s\"", cjob->fs_path);
993 goto BagNewServer;
996 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
997 #else
998 const char *err;
999 #endif
1001 /* Create a perm subpool to hold the new server data,
1002 * we can destroy it if something doesn't pan out */
1003 #ifdef APACHE2
1004 apr_pool_create(&sp, fcgi_config_pool);
1005 #else
1006 sp = ap_make_sub_pool(fcgi_config_pool);
1007 #endif
1009 /* Create a new "dynamic" server */
1010 s = fcgi_util_fs_new(sp);
1012 s->directive = APP_CLASS_DYNAMIC;
1013 s->restartDelay = dynamicRestartDelay;
1014 s->listenQueueDepth = dynamicListenQueueDepth;
1015 s->initStartDelay = dynamicInitStartDelay;
1016 s->envp = dynamicEnvp;
1017 s->flush = dynamicFlush;
1019 #ifdef WIN32
1020 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
1021 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
1022 #else
1023 s->fs_path = ap_pstrdup(sp, execName);
1024 #endif
1025 ap_getparents(s->fs_path);
1026 ap_no2slash(s->fs_path);
1027 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
1029 /* XXX the socket_path (both Unix and Win) *is* deducible and
1030 * thus can and will be used by other apache instances without
1031 * the use of shared data regarding the processes serving the
1032 * requests. This can result in slightly unintuitive process
1033 * counts and security implications. This is prevented
1034 * if suexec (Unix) is in use. This is both a feature and a flaw.
1035 * Changing it now would break existing installations. */
1037 #ifndef WIN32
1038 /* Create socket file's path */
1039 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
1040 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1042 /* Create sockaddr, prealloc it so it won't get created in tp */
1043 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
1044 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
1045 &s->socket_addr_len, s->socket_path);
1046 if (err) {
1047 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1048 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
1049 goto BagNewServer;
1052 if (init_listen_sock(s)) {
1053 goto BagNewServer;
1056 /* If a wrapper is being used, config user/group info */
1057 if (fcgi_wrapper) {
1058 if (user[0] == '~') {
1059 /* its a user dir uri, the rest is a username, not a uid */
1060 struct passwd *pw = getpwnam(&user[1]);
1062 if (!pw) {
1063 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1064 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1065 execName, &user[1]);
1066 goto BagNewServer;
1068 s->uid = pw->pw_uid;
1069 s->user = ap_pstrdup(sp, user);
1070 s->username = s->user;
1072 s->gid = pw->pw_gid;
1073 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
1075 else {
1076 struct passwd *pw;
1078 s->uid = (uid_t)atol(user);
1079 pw = getpwuid(s->uid);
1080 if (!pw) {
1081 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1082 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1083 execName, (long)s->uid);
1084 goto BagNewServer;
1086 s->user = ap_pstrdup(sp, user);
1087 s->username = ap_pstrdup(sp, pw->pw_name);
1089 s->gid = (gid_t)atol(group);
1090 s->group = ap_pstrdup(sp, group);
1093 #else
1094 /* Create socket file's path */
1095 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
1096 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1097 s->listenFd = 0;
1098 #endif
1100 fcgi_util_fs_add(s);
1102 else {
1103 #ifndef WIN32
1104 if (opcode == FCGI_SERVER_RESTART_JOB) {
1105 #else
1106 if (cjob->id==FCGI_SERVER_RESTART_JOB) {
1107 #endif
1108 /* Check to see if the binary has changed. If so,
1109 * kill the FCGI application processes, and
1110 * restart them.
1112 struct stat stbuf;
1113 int i;
1114 #ifdef WIN32
1115 char * app_path = cjob->fs_path;
1116 #else
1117 char * app_path = execName;
1118 #endif
1120 if (stat(app_path, &stbuf) == 0 && stbuf.st_mtime > s->startTime)
1122 int do_restart = 0;
1124 /* prevent addition restart requests */
1125 s->startTime = now;
1126 #ifndef WIN32
1127 utime(s->socket_path, NULL);
1128 #endif
1130 /* kill old server(s) */
1131 for (i = 0; i < dynamicMaxClassProcs; i++)
1133 if (s->procs[i].pid > 0
1134 && stbuf.st_mtime > s->procs[i].start_time)
1136 fcgi_kill(&s->procs[i], SIGTERM);
1137 do_restart++;
1141 if (do_restart)
1143 ap_log_error(FCGI_LOG_WARN_NOERRNO,
1144 fcgi_apache_main_server, "FastCGI: restarting "
1145 "old server \"%s\" processes, newer version "
1146 "found", app_path);
1150 /* If dynamicAutoRestart, don't mark any new processes
1151 * for starting because we probably got the
1152 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1153 * will be restarting all of those we just killed.
1155 if (dynamicAutoRestart)
1156 goto NextJob;
1158 #ifndef WIN32
1159 else if (opcode == FCGI_SERVER_START_JOB) {
1160 #else
1161 else if (cjob->id==FCGI_SERVER_START_JOB) {
1162 #endif
1163 /* we've been asked to start a process--only start
1164 * it if we're not already running at least one
1165 * instance.
1167 int i;
1169 for (i = 0; i < dynamicMaxClassProcs; i++) {
1170 if (s->procs[i].state == FCGI_RUNNING_STATE)
1171 break;
1173 /* if already running, don't start another one */
1174 if (i < dynamicMaxClassProcs) {
1175 goto NextJob;
1180 #ifndef WIN32
1181 switch (opcode)
1182 #else
1183 switch (cjob->id)
1184 #endif
1186 int i, start;
1188 case FCGI_SERVER_RESTART_JOB:
1190 start = FALSE;
1192 /* We just waxed 'em all. Try to find an idle slot. */
1194 for (i = 0; i < dynamicMaxClassProcs; ++i)
1196 if (s->procs[i].state == FCGI_START_STATE
1197 || s->procs[i].state == FCGI_RUNNING_STATE)
1199 break;
1201 else if (s->procs[i].state == FCGI_KILLED_STATE
1202 || s->procs[i].state == FCGI_READY_STATE)
1204 start = TRUE;
1205 break;
1209 /* Nope, just use the first slot */
1210 if (i == dynamicMaxClassProcs)
1212 start = TRUE;
1213 i = 0;
1216 if (start)
1218 schedule_start(s, i);
1221 break;
1223 case FCGI_SERVER_START_JOB:
1224 case FCGI_REQUEST_TIMEOUT_JOB:
1226 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1228 * Extra instances should have been
1229 * terminated beforehand, probably need
1230 * to increase ProcessSlack parameter
1232 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1233 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1234 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1235 goto NextJob;
1238 /* find next free slot */
1239 for (i = 0; i < dynamicMaxClassProcs; i++)
1241 if (s->procs[i].state == FCGI_START_STATE)
1243 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1244 break;
1246 else if (s->procs[i].state == FCGI_RUNNING_STATE)
1248 continue;
1251 schedule_start(s, i);
1252 break;
1255 #ifdef FCGI_DEBUG
1256 if (i >= dynamicMaxClassProcs) {
1257 FCGIDBG1("ignore_job: slots are max'd");
1259 #endif
1260 break;
1261 case FCGI_REQUEST_COMPLETE_JOB:
1262 /* only record stats if we have a structure */
1263 if (s) {
1264 #ifndef WIN32
1265 s->totalConnTime += req_usec;
1266 s->totalQueueTime += q_usec;
1267 #else
1268 s->totalConnTime += cjob->start_time;
1269 s->totalQueueTime += cjob->qsec;
1270 #endif
1272 break;
1275 NextJob:
1277 #ifdef WIN32
1278 /* Cleanup job data */
1279 free(cjob->fs_path);
1280 free(cjob->user);
1281 free(cjob->group);
1282 free(cjob);
1283 cjob = joblist;
1284 #endif
1286 continue;
1288 BagNewServer:
1289 if (sp) ap_destroy_pool(sp);
1291 #ifdef WIN32
1292 free(cjob->fs_path);
1293 free(cjob);
1294 cjob = joblist;
1295 #endif
1298 #ifndef WIN32
1299 if (ptr1 == buf) {
1300 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1301 "FastCGI: really bogus message: \"%s\"", ptr1);
1302 ptr1 += strlen(buf);
1305 buflen -= ptr1 - buf;
1306 if (buflen) {
1307 memmove(buf, ptr1, buflen);
1309 #endif
1311 ap_destroy_pool(tp);
1315 *----------------------------------------------------------------------
1317 * dynamic_kill_idle_fs_procs
1319 * Implement a kill policy for the dynamic FastCGI applications.
1320 * We also update the data structures to reflect the changes.
1322 * Side effects:
1323 * Processes are marked for deletion possibly killed.
1325 *----------------------------------------------------------------------
1327 static void dynamic_kill_idle_fs_procs(void)
1329 fcgi_server *s;
1330 int victims = 0;
1332 for (s = fcgi_servers; s != NULL; s = s->next)
1335 * server's smoothed running time, or if that's 0, the current total
1337 unsigned long connTime;
1340 * maximum number of microseconds that all of a server's running
1341 * processes together could have spent running since the last check
1343 unsigned long totalTime;
1346 * percentage, 0-100, of totalTime that the processes actually used
1348 int loadFactor;
1350 int i;
1351 int really_running = 0;
1353 if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1355 continue;
1358 /* s->numProcesses includes pending kills so get the "active" count */
1359 for (i = 0; i < dynamicMaxClassProcs; ++i)
1361 if (s->procs[i].state == FCGI_RUNNING_STATE) ++really_running;
1364 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1365 totalTime = really_running * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1367 loadFactor = 100 * connTime / totalTime;
1369 if (really_running == 1)
1371 if (loadFactor >= dynamicThreshold1)
1373 continue;
1376 else
1378 int load = really_running / ( really_running - 1) * loadFactor;
1380 if (load >= dynamicThresholdN)
1382 continue;
1387 * Run through the procs to see if we can get away w/o waxing one.
1389 for (i = 0; i < dynamicMaxClassProcs; ++i)
1391 if (s->procs[i].state == FCGI_START_STATE)
1393 s->procs[i].state = FCGI_READY_STATE;
1394 break;
1396 else if (s->procs[i].state == FCGI_VICTIM_STATE)
1398 break;
1402 if (i >= dynamicMaxClassProcs)
1404 ServerProcess * procs = s->procs;
1405 int youngest = -1;
1407 for (i = 0; i < dynamicMaxClassProcs; ++i)
1409 if (procs[i].state == FCGI_RUNNING_STATE)
1411 if (youngest == -1 || procs[i].start_time >= procs[youngest].start_time)
1413 youngest = i;
1418 if (youngest != -1)
1420 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1421 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1422 s->fs_path, (long) s->procs[youngest].pid);
1424 fcgi_kill(&s->procs[youngest], SIGTERM);
1426 victims++;
1430 * If the number of non-victims is less than or equal to
1431 * the minimum that may be running without being killed off,
1432 * don't select any more victims.
1434 if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1436 break;
1442 #ifdef WIN32
1444 // This is a little bogus, there's gotta be a better way to do this
1445 // Can we use WaitForMultipleObjects()
1446 #define FCGI_PROC_WAIT_TIME 100
1448 void child_wait_thread_main(void *dummy) {
1449 fcgi_server *s;
1450 DWORD dwRet = WAIT_TIMEOUT;
1451 int numChildren;
1452 int i;
1453 int waited;
1455 while (!bTimeToDie) {
1456 waited = 0;
1458 for (s = fcgi_servers; s != NULL; s = s->next) {
1459 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1460 continue;
1462 if (s->directive == APP_CLASS_DYNAMIC) {
1463 numChildren = dynamicMaxClassProcs;
1465 else {
1466 numChildren = s->numProcesses;
1469 for (i=0; i < numChildren; i++) {
1470 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1472 DWORD exitStatus = 0;
1474 /* timeout is currently set for 100 miliecond */
1475 /* it may need to be longer or user customizable */
1476 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1478 waited = 1;
1480 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1481 /* a child fs has died */
1482 /* mark the child as dead */
1484 if (s->directive == APP_CLASS_STANDARD) {
1485 /* restart static app */
1486 s->procs[i].state = FCGI_START_STATE;
1487 s->numFailures++;
1489 else {
1490 s->numProcesses--;
1491 fcgi_dynamic_total_proc_count--;
1492 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1494 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1495 s->procs[i].state = FCGI_KILLED_STATE;
1497 else {
1498 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1499 s->numFailures++;
1501 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1502 s->procs[i].state = FCGI_START_STATE;
1504 else {
1505 s->procs[i].state = FCGI_READY_STATE;
1510 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1512 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1513 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1514 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1515 s->fs_path, (long) s->procs[i].pid, exitStatus);
1517 CloseHandle(s->procs[i].handle);
1518 s->procs[i].handle = INVALID_HANDLE_VALUE;
1519 s->procs[i].pid = -1;
1521 /* wake up the main thread */
1522 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1527 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1530 #endif
1532 #ifndef WIN32
1533 static void setup_signals(void)
1535 struct sigaction sa;
1537 /* Setup handlers */
1539 sa.sa_handler = signal_handler;
1540 sigemptyset(&sa.sa_mask);
1541 sa.sa_flags = 0;
1543 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1544 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1545 "sigaction(SIGTERM) failed");
1547 /* httpd restart */
1548 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1549 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1550 "sigaction(SIGHUP) failed");
1552 /* httpd graceful restart */
1553 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1554 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1555 "sigaction(SIGUSR1) failed");
1557 /* read messages from request handlers - kill interval expired */
1558 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1559 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1560 "sigaction(SIGALRM) failed");
1562 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1563 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1564 "sigaction(SIGCHLD) failed");
1567 #endif
1569 #if !defined(WIN32) && !defined(APACHE2)
1570 int fcgi_pm_main(void *dummy, child_info *info)
1571 #else
1572 void fcgi_pm_main(void *dummy)
1573 #endif
1575 fcgi_server *s;
1576 unsigned int i;
1577 int read_ready = 0;
1578 int alarmLeft = 0;
1580 #ifdef WIN32
1581 DWORD dwRet;
1582 HANDLE child_wait_thread = INVALID_HANDLE_VALUE;
1583 #else
1584 int callWaitPid, callDynamicProcs;
1585 #endif
1587 #ifdef WIN32
1588 // Add SystemRoot to the dynamic environment
1589 char ** envp = dynamicEnvp;
1590 for (i = 0; *envp; ++i) {
1591 ++envp;
1593 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1595 #else
1597 reduce_privileges();
1598 change_process_name("fcgi-pm");
1600 close(fcgi_pm_pipe[1]);
1601 setup_signals();
1603 if (fcgi_wrapper) {
1604 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1605 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1607 #endif
1609 /* Initialize AppClass */
1610 for (s = fcgi_servers; s != NULL; s = s->next)
1612 if (s->directive != APP_CLASS_STANDARD)
1613 continue;
1615 #ifdef WIN32
1616 if (s->socket_path)
1617 s->listenFd = 0;
1618 #endif
1620 for (i = 0; i < s->numProcesses; ++i)
1621 s->procs[i].state = FCGI_START_STATE;
1624 #ifdef WIN32
1625 child_wait_thread = (HANDLE) _beginthread(child_wait_thread_main, 0, NULL);
1627 if (child_wait_thread == (HANDLE) -1)
1629 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1630 "FastCGI: failed to create process manager's wait thread!");
1633 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1634 "FastCGI: process manager initialized");
1635 #else
1636 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1637 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1638 #endif
1640 now = time(NULL);
1643 * Loop until SIGTERM
1645 for (;;) {
1646 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1647 #ifdef WIN32
1648 time_t expire;
1649 #else
1650 pid_t childPid;
1651 int waitStatus;
1652 #endif
1653 unsigned int numChildren;
1656 * If we came out of sigsuspend() for any reason other than
1657 * SIGALRM, pick up where we left off.
1659 if (alarmLeft)
1660 sleepSeconds = alarmLeft;
1663 * Examine each configured AppClass for a process that needs
1664 * starting. Compute the earliest time when the start should
1665 * be attempted, starting it now if the time has passed. Also,
1666 * remember that we do NOT need to restart externally managed
1667 * FastCGI applications.
1669 for (s = fcgi_servers; s != NULL; s = s->next)
1671 if (s->directive == APP_CLASS_EXTERNAL)
1672 continue;
1674 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1675 ? dynamicMaxClassProcs
1676 : s->numProcesses;
1678 for (i = 0; i < numChildren; ++i)
1680 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1682 int restart = (s->procs[i].pid < 0);
1683 time_t restartTime = s->restartTime;
1685 if (s->bad)
1687 /* we've gone to using the badDelay, the only thing that
1688 resets bad is when badDelay has expired. but numFailures
1689 is only just set below its threshold. the proc's
1690 start_times are all reset when the bad is. the numFailures
1691 is reset when we see an app run for a period */
1693 s->procs[i].start_time = 0;
1696 if (s->numFailures > MAX_FAILED_STARTS)
1698 time_t last_start_time = s->procs[i].start_time;
1700 if (last_start_time && now - last_start_time > RUNTIME_SUCCESS_INTERVAL)
1702 s->bad = 0;
1703 s->numFailures = 0;
1704 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1705 "FastCGI:%s server \"%s\" has remained"
1706 " running for more than %d seconds, its restart"
1707 " interval has been restored to %d seconds",
1708 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1709 s->fs_path, RUNTIME_SUCCESS_INTERVAL, s->restartDelay);
1711 else
1713 unsigned int j;
1715 for (j = 0; j < numChildren; ++j)
1717 if (s->procs[j].pid <= 0) continue;
1718 if (s->procs[j].state != FCGI_RUNNING_STATE) continue;
1719 if (s->procs[j].start_time == 0) continue;
1720 if (now - s->procs[j].start_time > RUNTIME_SUCCESS_INTERVAL) break;
1723 if (j >= numChildren)
1725 s->bad = 1;
1726 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1727 "FastCGI:%s server \"%s\" has failed to remain"
1728 " running for %d seconds given %d attempts, its restart"
1729 " interval has been backed off to %d seconds",
1730 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1731 s->fs_path, RUNTIME_SUCCESS_INTERVAL, MAX_FAILED_STARTS,
1732 FAILED_STARTS_DELAY);
1734 else
1736 s->bad = 0;
1737 s->numFailures = 0;
1738 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1739 "FastCGI:%s server \"%s\" has remained"
1740 " running for more than %d seconds, its restart"
1741 " interval has been restored to %d seconds",
1742 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1743 s->fs_path, RUNTIME_SUCCESS_INTERVAL, s->restartDelay);
1748 if (s->bad)
1750 restartTime += FAILED_STARTS_DELAY;
1752 else
1754 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1757 if (restartTime <= now)
1759 if (s->bad)
1761 s->bad = 0;
1762 s->numFailures = MAX_FAILED_STARTS;
1765 if (s->listenFd < 0 && init_listen_sock(s))
1767 if (sleepSeconds > s->initStartDelay)
1768 sleepSeconds = s->initStartDelay;
1769 break;
1771 #ifndef WIN32
1772 if (caughtSigTerm) {
1773 goto ProcessSigTerm;
1775 #endif
1776 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1777 if (s->procs[i].pid <= 0) {
1778 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1779 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1780 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1781 s->fs_path);
1783 sleepSeconds = min(sleepSeconds,
1784 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1786 ap_assert(s->procs[i].pid < 0);
1787 break;
1790 s->procs[i].start_time = now;
1791 s->restartTime = now;
1793 if (s->startTime == 0) {
1794 s->startTime = now;
1797 if (s->directive == APP_CLASS_DYNAMIC) {
1798 s->numProcesses++;
1799 fcgi_dynamic_total_proc_count++;
1800 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1803 s->procs[i].state = FCGI_RUNNING_STATE;
1805 if (fcgi_wrapper) {
1806 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1807 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1808 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1809 s->fs_path, (long) s->uid, (long) s->gid,
1810 restart ? "re" : "", (long) s->procs[i].pid);
1812 else {
1813 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1814 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1815 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1816 s->fs_path, restart ? "re" : "", (long) s->procs[i].pid);
1818 ap_assert(s->procs[i].pid > 0);
1819 } else {
1820 sleepSeconds = min(sleepSeconds, restartTime - now);
1826 #ifndef WIN32
1828 if(caughtSigTerm) {
1829 goto ProcessSigTerm;
1831 if((!caughtSigChld) && (!caughtSigAlarm)) {
1832 fd_set rfds;
1834 alarm(sleepSeconds);
1836 FD_ZERO(&rfds);
1837 FD_SET(fcgi_pm_pipe[0], &rfds);
1838 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1840 alarmLeft = alarm(0);
1842 callWaitPid = caughtSigChld;
1843 caughtSigChld = FALSE;
1844 callDynamicProcs = caughtSigAlarm;
1845 caughtSigAlarm = FALSE;
1847 now = time(NULL);
1850 * Dynamic fcgi process management
1852 if((callDynamicProcs) || (!callWaitPid)) {
1853 dynamic_read_msgs(read_ready);
1854 if(fcgi_dynamic_epoch == 0) {
1855 fcgi_dynamic_epoch = now;
1857 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1858 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1859 dynamic_kill_idle_fs_procs();
1860 fcgi_dynamic_epoch = now;
1864 if(!callWaitPid) {
1865 continue;
1868 /* We've caught SIGCHLD, so find out who it was using waitpid,
1869 * write a log message and update its data structure. */
1871 for (;;) {
1872 if (caughtSigTerm)
1873 goto ProcessSigTerm;
1875 childPid = waitpid(-1, &waitStatus, WNOHANG);
1877 if (childPid == -1 || childPid == 0)
1878 break;
1880 for (s = fcgi_servers; s != NULL; s = s->next) {
1881 if (s->directive == APP_CLASS_EXTERNAL)
1882 continue;
1884 if (s->directive == APP_CLASS_DYNAMIC)
1885 numChildren = dynamicMaxClassProcs;
1886 else
1887 numChildren = s->numProcesses;
1889 for (i = 0; i < numChildren; i++) {
1890 if (s->procs[i].pid == childPid)
1891 goto ChildFound;
1895 /* TODO: print something about this unknown child */
1896 continue;
1898 ChildFound:
1899 s->procs[i].pid = -1;
1901 if (s->directive == APP_CLASS_STANDARD) {
1902 /* Always restart static apps */
1903 s->procs[i].state = FCGI_START_STATE;
1904 s->numFailures++;
1906 else {
1907 s->numProcesses--;
1908 fcgi_dynamic_total_proc_count--;
1910 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1911 s->procs[i].state = FCGI_KILLED_STATE;
1913 else {
1914 /* A dynamic app died or exited without provocation from the PM */
1915 s->numFailures++;
1917 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1918 s->procs[i].state = FCGI_START_STATE;
1919 else
1920 s->procs[i].state = FCGI_READY_STATE;
1924 if (WIFEXITED(waitStatus)) {
1925 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1926 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1927 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1928 s->fs_path, (long) childPid, WEXITSTATUS(waitStatus));
1930 else if (WIFSIGNALED(waitStatus)) {
1931 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1932 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1933 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1934 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus),
1935 #ifdef WCOREDUMP
1936 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1937 #else
1938 "");
1939 #endif
1941 else if (WIFSTOPPED(waitStatus)) {
1942 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1943 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1944 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1945 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus));
1947 } /* for (;;), waitpid() */
1949 #else /* WIN32 */
1951 /* wait for an event to occur or timer expires */
1952 expire = time(NULL) + sleepSeconds;
1953 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1955 if (dwRet == WAIT_FAILED) {
1956 /* There is something seriously wrong here */
1957 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1958 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1959 bTimeToDie = TRUE;
1962 if (dwRet != WAIT_TIMEOUT) {
1963 now = time(NULL);
1965 if (now < expire)
1966 alarmLeft = expire - now;
1970 * Dynamic fcgi process management
1972 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1973 if (dwRet == MBOX_EVENT) {
1974 read_ready = 1;
1977 now = time(NULL);
1979 dynamic_read_msgs(read_ready);
1981 if(fcgi_dynamic_epoch == 0) {
1982 fcgi_dynamic_epoch = now;
1985 if ((now-fcgi_dynamic_epoch >= (int) dynamicKillInterval) ||
1986 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1987 dynamic_kill_idle_fs_procs();
1988 fcgi_dynamic_epoch = now;
1990 read_ready = 0;
1992 else if (dwRet == WAKE_EVENT) {
1993 continue;
1995 else if (dwRet == TERM_EVENT) {
1996 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1997 "FastCGI: Termination event received process manager shutting down");
1999 bTimeToDie = TRUE;
2000 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2002 goto ProcessSigTerm;
2004 else {
2005 // Have an received an unknown event - should not happen
2006 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
2007 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
2009 bTimeToDie = TRUE;
2010 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2012 goto ProcessSigTerm;
2015 #endif /* WIN32 */
2017 } /* for (;;), the whole shoot'n match */
2019 ProcessSigTerm:
2021 * Kill off the children, then exit.
2023 shutdown_all();
2025 #ifdef WIN32
2026 return;
2027 #else
2028 exit(0);
2029 #endif
2032 #ifdef WIN32
2033 int fcgi_pm_add_job(fcgi_pm_job *new_job)
2035 int rv = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
2037 if (rv != WAIT_OBJECT_0 && rv != WAIT_ABANDONED)
2039 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2040 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2041 return -1;
2044 new_job->next = fcgi_dynamic_mbox;
2045 fcgi_dynamic_mbox = new_job;
2047 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
2049 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2050 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
2053 return 0;
2055 #endif