Provide a NO_SUEXEC_FOR_AP_USER_N_GROUP macro for building
[mod_fastcgi.git] / fcgi_pm.c
blob4a144f118836bf72fb45bb06f99e387747d930f6
1 /*
2 * $Id: fcgi_pm.c,v 1.85 2003/02/04 01:14:10 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 AP2 approach better
414 * (fcgi_pm.c v1.42 incorporated the 1.3 behaviour, v1.84 reverted it,
415 * v1.85 added the compile time option to use the old behaviour). */
417 #ifdef NO_SUEXEC_FOR_AP_USER_N_GROUP
419 if (fcgi_user_id == fs->uid && fcgi_group_id == fs->gid)
421 goto NO_SUEXEC;
424 #endif
425 shortName = strrchr(fs->fs_path, '/') + 1;
427 do {
428 execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group,
429 shortName, NULL, fs->envp);
430 } while (errno == EINTR);
432 else
435 NO_SUEXEC:
436 do {
437 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
438 } while (errno == EINTR);
441 failedSysCall = "execle()";
443 FailedSystemCallExit:
444 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
445 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
446 exit(-1);
448 /* avoid an irrelevant compiler warning */
449 return(0);
451 #else /* WIN32 */
453 #ifdef APACHE2
455 /* based on mod_cgi.c:run_cgi_child() */
457 apr_pool_t * tp;
458 char * termination_env_string;
459 HANDLE listen_handle = INVALID_HANDLE_VALUE;
460 apr_procattr_t * procattr;
461 apr_proc_t proc = { 0 };
462 apr_file_t * file;
463 int i = 0;
465 if (apr_pool_create(&tp, fcgi_config_pool))
466 return 0;
468 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
469 if (process->terminationEvent == NULL)
470 goto CLEANUP;
472 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
474 termination_env_string = ap_psprintf(tp,
475 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
477 while (fs->envp[i]) i++;
478 fs->envp[i++] = termination_env_string;
479 fs->envp[i] = (char *) fs->mutex_env_string;
481 ap_assert(fs->envp[i + 1] == NULL);
483 if (fs->socket_path)
485 SECURITY_ATTRIBUTES sa = { 0 };
487 sa.bInheritHandle = TRUE;
488 sa.nLength = sizeof(sa);
490 listen_handle = CreateNamedPipe(fs->socket_path,
491 PIPE_ACCESS_DUPLEX,
492 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
493 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
495 if (listen_handle == INVALID_HANDLE_VALUE)
497 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
498 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
499 fs->fs_path);
500 goto CLEANUP;
503 else
505 listen_handle = (HANDLE) fs->listenFd;
508 if (apr_procattr_create(&procattr, tp))
509 goto CLEANUP;
511 if (apr_procattr_dir_set(procattr, ap_make_dirstr_parent(tp, fs->fs_path)))
512 goto CLEANUP;
514 if (apr_procattr_detach_set(procattr, 1))
515 goto CLEANUP;
517 if (apr_os_file_put(&file, &listen_handle, 0, tp))
518 goto CLEANUP;
520 /* procattr is opaque so we have to use this - unfortuantely it dups */
521 if (apr_procattr_child_in_set(procattr, file, NULL))
522 goto CLEANUP;
524 if (apr_proc_create(&proc, fs->fs_path, NULL, fs->envp, procattr, tp))
525 goto CLEANUP;
527 process->handle = proc.hproc;
530 CLEANUP:
532 if (i)
534 fs->envp[i - 1] = NULL;
537 ap_destroy_pool(tp);
539 return proc.pid;
541 #else /* WIN32 && !APACHE2 */
543 /* Adapted from Apache's util_script.c ap_call_exec() */
544 char *interpreter = NULL;
545 char *quoted_filename;
546 char *pCommand;
547 char *pEnvBlock, *pNext;
549 int i = 0;
550 int iEnvBlockLen = 1;
552 file_type_e fileType;
554 STARTUPINFO si;
555 PROCESS_INFORMATION pi;
557 request_rec r;
558 pid_t pid = -1;
560 pool * tp = ap_make_sub_pool(fcgi_config_pool);
562 HANDLE listen_handle;
563 char * termination_env_string = NULL;
565 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
566 if (process->terminationEvent == NULL)
568 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
569 "FastCGI: can't create termination event for server \"%s\", "
570 "CreateEvent() failed", fs->fs_path);
571 goto CLEANUP;
573 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
575 termination_env_string = ap_psprintf(tp,
576 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
578 if (fs->socket_path)
580 SECURITY_ATTRIBUTES sa;
582 sa.lpSecurityDescriptor = NULL;
583 sa.bInheritHandle = TRUE;
584 sa.nLength = sizeof(sa);
586 listen_handle = CreateNamedPipe(fs->socket_path,
587 PIPE_ACCESS_DUPLEX,
588 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
589 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
591 if (listen_handle == INVALID_HANDLE_VALUE)
593 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
594 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
595 goto CLEANUP;
598 else
600 listen_handle = (HANDLE) fs->listenFd;
603 memset(&si, 0, sizeof(si));
604 memset(&pi, 0, sizeof(pi));
605 memset(&r, 0, sizeof(r));
607 // Can up a fake request to pass to ap_get_win32_interpreter()
608 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
609 r.server = fcgi_apache_main_server;
610 r.filename = (char *) fs->fs_path;
611 r.pool = tp;
613 fileType = ap_get_win32_interpreter(&r, &interpreter);
615 if (fileType == eFileTypeUNKNOWN) {
616 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
617 "FastCGI: %s is not executable; ensure interpreted scripts have "
618 "\"#!\" as their first line",
619 fs->fs_path);
620 ap_destroy_pool(tp);
621 goto CLEANUP;
625 * We have the interpreter (if there is one) and we have
626 * the arguments (if there are any).
627 * Build the command string to pass to CreateProcess.
629 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
630 if (interpreter && *interpreter) {
631 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
633 else {
634 pCommand = quoted_filename;
638 * Make child process use hPipeOutputWrite as standard out,
639 * and make sure it does not show on screen.
641 si.cb = sizeof(si);
642 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
643 si.wShowWindow = SW_HIDE;
644 si.hStdInput = listen_handle;
646 // XXX These should be open to the error_log
647 si.hStdOutput = INVALID_HANDLE_VALUE;
648 si.hStdError = INVALID_HANDLE_VALUE;
651 * Win32's CreateProcess call requires that the environment
652 * be passed in an environment block, a null terminated block of
653 * null terminated strings.
654 * @todo we should store the env in this format for win32.
656 while (fs->envp[i])
658 iEnvBlockLen += strlen(fs->envp[i]) + 1;
659 i++;
662 iEnvBlockLen += strlen(termination_env_string) + 1;
663 iEnvBlockLen += strlen(fs->mutex_env_string) + 1;
665 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
667 i = 0;
668 pNext = pEnvBlock;
669 while (fs->envp[i])
671 strcpy(pNext, fs->envp[i]);
672 pNext += strlen(pNext) + 1;
673 i++;
676 strcpy(pNext, termination_env_string);
677 pNext += strlen(pNext) + 1;
678 strcpy(pNext, fs->mutex_env_string);
680 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
682 pEnvBlock,
683 ap_make_dirstr_parent(tp, fs->fs_path),
684 &si, &pi))
686 /* Hack to get 16-bit CGI's working. It works for all the
687 * standard modules shipped with Apache. pi.dwProcessId is 0
688 * for 16-bit CGIs and all the Unix specific code that calls
689 * ap_call_exec interprets this as a failure case. And we can't
690 * use -1 either because it is mapped to 0 by the caller.
692 pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId;
694 process->handle = pi.hProcess;
695 CloseHandle(pi.hThread);
698 if (fs->socket_path)
700 CloseHandle(listen_handle);
703 CLEANUP:
705 ap_destroy_pool(tp);
707 return pid;
709 #endif /* !APACHE2 */
710 #endif /* WIN32 */
713 #ifndef WIN32
714 static void reduce_privileges(void)
716 const char *name;
718 if (geteuid() != 0)
719 return;
721 #ifndef __EMX__
722 /* Get username if passed as a uid */
723 if (ap_user_name[0] == '#') {
724 uid_t uid = atoi(&ap_user_name[1]);
725 struct passwd *ent = getpwuid(uid);
727 if (ent == NULL) {
728 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
729 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
730 "you probably need to modify the User directive", (unsigned)uid);
731 exit(1);
733 name = ent->pw_name;
735 else
736 name = ap_user_name;
738 /* Change Group */
739 if (setgid(ap_group_id) == -1) {
740 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
741 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
742 exit(1);
745 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
747 /* Initialize supplementary groups */
748 if (initgroups(name, ap_group_id) == -1) {
749 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
750 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
751 name, (unsigned)ap_group_id);
752 exit(1);
754 #endif /* __EMX__ */
756 /* Change User */
757 if (fcgi_wrapper) {
758 if (seteuid_user() == -1) {
759 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
760 "FastCGI: process manager exiting, failed to reduce privileges");
761 exit(1);
764 else {
765 if (setuid(ap_user_id) == -1) {
766 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
767 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
768 exit(1);
773 /*************
774 * Change the name of this process - best we can easily.
776 static void change_process_name(const char * const name)
778 /* under Apache2, ap_server_argv0 is const */
779 strncpy((char *) ap_server_argv0, name, strlen(ap_server_argv0));
781 #endif /* !WIN32 */
783 static void schedule_start(fcgi_server *s, int proc)
785 /* If we've started one recently, don't register another */
786 time_t time_passed = now - s->restartTime;
788 if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay))
789 || ((s->procs[proc].pid == 0) && (time_passed < s->initStartDelay)))
791 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);
792 return;
795 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
796 s->procs[proc].state = FCGI_START_STATE;
797 if (proc == dynamicMaxClassProcs - 1) {
798 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
799 "FastCGI: scheduled the %sstart of the last (dynamic) server "
800 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
801 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
806 *----------------------------------------------------------------------
808 * dynamic_read_msgs
810 * Removes the records written by request handlers and decodes them.
811 * We also update the data structures to reflect the changes.
813 *----------------------------------------------------------------------
816 static void dynamic_read_msgs(int read_ready)
818 fcgi_server *s;
819 int rc;
821 #ifndef WIN32
822 static int buflen = 0;
823 static char buf[FCGI_MSGS_BUFSIZE + 1];
824 char *ptr1, *ptr2, opcode;
825 char execName[FCGI_MAXPATH + 1];
826 char user[MAX_USER_NAME_LEN + 2];
827 char group[MAX_GID_CHAR_LEN + 1];
828 unsigned long q_usec = 0UL, req_usec = 0UL;
829 #else
830 fcgi_pm_job *joblist = NULL;
831 fcgi_pm_job *cjob = NULL;
832 #endif
834 pool *sp = NULL, *tp;
836 #ifndef WIN32
837 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
838 #endif
841 * To prevent the idle application from running indefinitely, we
842 * check the timer and if it is expired, we recompute the values
843 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
844 * message is received, only updates are made to the data structures.
846 if (fcgi_dynamic_last_analyzed == 0) {
847 fcgi_dynamic_last_analyzed = now;
849 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
850 for (s = fcgi_servers; s != NULL; s = s->next) {
851 if (s->directive != APP_CLASS_DYNAMIC)
852 break;
854 /* Advance the last analyzed timestamp by the elapsed time since
855 * it was last set. Round the increase down to the nearest
856 * multiple of dynamicUpdateInterval */
858 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
859 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
860 s->totalConnTime = 0UL;
861 s->totalQueueTime = 0UL;
865 if (read_ready <= 0) {
866 return;
869 #ifndef WIN32
870 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
871 if (rc <= 0) {
872 if (!caughtSigTerm) {
873 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
874 "FastCGI: read() from pipe failed (%d)", rc);
875 if (rc == 0) {
876 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
877 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
878 caughtSigTerm = TRUE;
881 return;
883 buflen += rc;
884 buf[buflen] = '\0';
886 #else
888 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
889 * request to do something) and/or when a timeout expires.
890 * There really should be no reason why this wait would get stuck
891 * but there's no point in waiting forever. */
893 rc = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
895 if (rc != WAIT_OBJECT_0 && rc != WAIT_ABANDONED)
897 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
898 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
899 return;
902 joblist = fcgi_dynamic_mbox;
903 fcgi_dynamic_mbox = NULL;
905 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
907 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
908 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
911 cjob = joblist;
912 #endif
914 #ifdef APACHE2
915 apr_pool_create(&tp, fcgi_config_pool);
916 #else
917 tp = ap_make_sub_pool(fcgi_config_pool);
918 #endif
920 #ifndef WIN32
921 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
922 int scan_failed = 0;
924 ptr2 = strchr(ptr1, '*');
925 if (ptr2) {
926 *ptr2++ = '\0';
928 else {
929 break;
932 opcode = *ptr1;
934 switch (opcode)
936 case FCGI_SERVER_START_JOB:
937 case FCGI_SERVER_RESTART_JOB:
939 if (sscanf(ptr1, "%c %s %16s %15s",
940 &opcode, execName, user, group) != 4)
942 scan_failed = 1;
944 break;
946 case FCGI_REQUEST_TIMEOUT_JOB:
948 if (sscanf(ptr1, "%c %s %16s %15s",
949 &opcode, execName, user, group) != 4)
951 scan_failed = 1;
953 break;
955 case FCGI_REQUEST_COMPLETE_JOB:
957 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
958 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
960 scan_failed = 1;
962 break;
964 default:
966 scan_failed = 1;
967 break;
970 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode, execName, user, group, q_usec, req_usec);
972 if (scan_failed) {
973 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
974 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
975 goto NextJob;
977 #else
978 /* Update data structures for processing */
979 while (cjob != NULL) {
980 joblist = cjob->next;
981 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
982 #endif
984 #ifndef WIN32
985 s = fcgi_util_fs_get(execName, user, group);
986 #else
987 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
988 #endif
990 #ifndef WIN32
991 if (s==NULL && opcode != FCGI_REQUEST_COMPLETE_JOB)
992 #else
993 if (s==NULL && cjob->id != FCGI_REQUEST_COMPLETE_JOB)
994 #endif
996 #ifdef WIN32
998 HANDLE mutex = CreateMutex(NULL, FALSE, cjob->fs_path);
1000 if (mutex == NULL)
1002 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1003 "FastCGI: can't create accept mutex "
1004 "for (dynamic) server \"%s\"", cjob->fs_path);
1005 goto BagNewServer;
1008 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
1009 #else
1010 const char *err;
1011 #endif
1013 /* Create a perm subpool to hold the new server data,
1014 * we can destroy it if something doesn't pan out */
1015 #ifdef APACHE2
1016 apr_pool_create(&sp, fcgi_config_pool);
1017 #else
1018 sp = ap_make_sub_pool(fcgi_config_pool);
1019 #endif
1021 /* Create a new "dynamic" server */
1022 s = fcgi_util_fs_new(sp);
1024 s->directive = APP_CLASS_DYNAMIC;
1025 s->restartDelay = dynamicRestartDelay;
1026 s->listenQueueDepth = dynamicListenQueueDepth;
1027 s->initStartDelay = dynamicInitStartDelay;
1028 s->envp = dynamicEnvp;
1029 s->flush = dynamicFlush;
1031 #ifdef WIN32
1032 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
1033 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
1034 #else
1035 s->fs_path = ap_pstrdup(sp, execName);
1036 #endif
1037 ap_getparents(s->fs_path);
1038 ap_no2slash(s->fs_path);
1039 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
1041 /* XXX the socket_path (both Unix and Win) *is* deducible and
1042 * thus can and will be used by other apache instances without
1043 * the use of shared data regarding the processes serving the
1044 * requests. This can result in slightly unintuitive process
1045 * counts and security implications. This is prevented
1046 * if suexec (Unix) is in use. This is both a feature and a flaw.
1047 * Changing it now would break existing installations. */
1049 #ifndef WIN32
1050 /* Create socket file's path */
1051 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
1052 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1054 /* Create sockaddr, prealloc it so it won't get created in tp */
1055 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
1056 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
1057 &s->socket_addr_len, s->socket_path);
1058 if (err) {
1059 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1060 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
1061 goto BagNewServer;
1064 if (init_listen_sock(s)) {
1065 goto BagNewServer;
1068 /* If a wrapper is being used, config user/group info */
1069 if (fcgi_wrapper) {
1070 if (user[0] == '~') {
1071 /* its a user dir uri, the rest is a username, not a uid */
1072 struct passwd *pw = getpwnam(&user[1]);
1074 if (!pw) {
1075 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1076 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1077 execName, &user[1]);
1078 goto BagNewServer;
1080 s->uid = pw->pw_uid;
1081 s->user = ap_pstrdup(sp, user);
1082 s->username = s->user;
1084 s->gid = pw->pw_gid;
1085 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
1087 else {
1088 struct passwd *pw;
1090 s->uid = (uid_t)atol(user);
1091 pw = getpwuid(s->uid);
1092 if (!pw) {
1093 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1094 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1095 execName, (long)s->uid);
1096 goto BagNewServer;
1098 s->user = ap_pstrdup(sp, user);
1099 s->username = ap_pstrdup(sp, pw->pw_name);
1101 s->gid = (gid_t)atol(group);
1102 s->group = ap_pstrdup(sp, group);
1105 #else
1106 /* Create socket file's path */
1107 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
1108 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1109 s->listenFd = 0;
1110 #endif
1112 fcgi_util_fs_add(s);
1114 else {
1115 #ifndef WIN32
1116 if (opcode == FCGI_SERVER_RESTART_JOB) {
1117 #else
1118 if (cjob->id==FCGI_SERVER_RESTART_JOB) {
1119 #endif
1120 /* Check to see if the binary has changed. If so,
1121 * kill the FCGI application processes, and
1122 * restart them.
1124 struct stat stbuf;
1125 int i;
1126 #ifdef WIN32
1127 char * app_path = cjob->fs_path;
1128 #else
1129 char * app_path = execName;
1130 #endif
1132 if (stat(app_path, &stbuf) == 0 && stbuf.st_mtime > s->startTime)
1134 int do_restart = 0;
1136 /* prevent addition restart requests */
1137 s->startTime = now;
1138 #ifndef WIN32
1139 utime(s->socket_path, NULL);
1140 #endif
1142 /* kill old server(s) */
1143 for (i = 0; i < dynamicMaxClassProcs; i++)
1145 if (s->procs[i].pid > 0
1146 && stbuf.st_mtime > s->procs[i].start_time)
1148 fcgi_kill(&s->procs[i], SIGTERM);
1149 do_restart++;
1153 if (do_restart)
1155 ap_log_error(FCGI_LOG_WARN_NOERRNO,
1156 fcgi_apache_main_server, "FastCGI: restarting "
1157 "old server \"%s\" processes, newer version "
1158 "found", app_path);
1162 /* If dynamicAutoRestart, don't mark any new processes
1163 * for starting because we probably got the
1164 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1165 * will be restarting all of those we just killed.
1167 if (dynamicAutoRestart)
1168 goto NextJob;
1170 #ifndef WIN32
1171 else if (opcode == FCGI_SERVER_START_JOB) {
1172 #else
1173 else if (cjob->id==FCGI_SERVER_START_JOB) {
1174 #endif
1175 /* we've been asked to start a process--only start
1176 * it if we're not already running at least one
1177 * instance.
1179 int i;
1181 for (i = 0; i < dynamicMaxClassProcs; i++) {
1182 if (s->procs[i].state == FCGI_RUNNING_STATE)
1183 break;
1185 /* if already running, don't start another one */
1186 if (i < dynamicMaxClassProcs) {
1187 goto NextJob;
1192 #ifndef WIN32
1193 switch (opcode)
1194 #else
1195 switch (cjob->id)
1196 #endif
1198 int i, start;
1200 case FCGI_SERVER_RESTART_JOB:
1202 start = FALSE;
1204 /* We just waxed 'em all. Try to find an idle slot. */
1206 for (i = 0; i < dynamicMaxClassProcs; ++i)
1208 if (s->procs[i].state == FCGI_START_STATE
1209 || s->procs[i].state == FCGI_RUNNING_STATE)
1211 break;
1213 else if (s->procs[i].state == FCGI_KILLED_STATE
1214 || s->procs[i].state == FCGI_READY_STATE)
1216 start = TRUE;
1217 break;
1221 /* Nope, just use the first slot */
1222 if (i == dynamicMaxClassProcs)
1224 start = TRUE;
1225 i = 0;
1228 if (start)
1230 schedule_start(s, i);
1233 break;
1235 case FCGI_SERVER_START_JOB:
1236 case FCGI_REQUEST_TIMEOUT_JOB:
1238 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1240 * Extra instances should have been
1241 * terminated beforehand, probably need
1242 * to increase ProcessSlack parameter
1244 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1245 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1246 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1247 goto NextJob;
1250 /* find next free slot */
1251 for (i = 0; i < dynamicMaxClassProcs; i++)
1253 if (s->procs[i].state == FCGI_START_STATE)
1255 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1256 break;
1258 else if (s->procs[i].state == FCGI_RUNNING_STATE)
1260 continue;
1263 schedule_start(s, i);
1264 break;
1267 #ifdef FCGI_DEBUG
1268 if (i >= dynamicMaxClassProcs) {
1269 FCGIDBG1("ignore_job: slots are max'd");
1271 #endif
1272 break;
1273 case FCGI_REQUEST_COMPLETE_JOB:
1274 /* only record stats if we have a structure */
1275 if (s) {
1276 #ifndef WIN32
1277 s->totalConnTime += req_usec;
1278 s->totalQueueTime += q_usec;
1279 #else
1280 s->totalConnTime += cjob->start_time;
1281 s->totalQueueTime += cjob->qsec;
1282 #endif
1284 break;
1287 NextJob:
1289 #ifdef WIN32
1290 /* Cleanup job data */
1291 free(cjob->fs_path);
1292 free(cjob->user);
1293 free(cjob->group);
1294 free(cjob);
1295 cjob = joblist;
1296 #endif
1298 continue;
1300 BagNewServer:
1301 if (sp) ap_destroy_pool(sp);
1303 #ifdef WIN32
1304 free(cjob->fs_path);
1305 free(cjob);
1306 cjob = joblist;
1307 #endif
1310 #ifndef WIN32
1311 if (ptr1 == buf) {
1312 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1313 "FastCGI: really bogus message: \"%s\"", ptr1);
1314 ptr1 += strlen(buf);
1317 buflen -= ptr1 - buf;
1318 if (buflen) {
1319 memmove(buf, ptr1, buflen);
1321 #endif
1323 ap_destroy_pool(tp);
1327 *----------------------------------------------------------------------
1329 * dynamic_kill_idle_fs_procs
1331 * Implement a kill policy for the dynamic FastCGI applications.
1332 * We also update the data structures to reflect the changes.
1334 * Side effects:
1335 * Processes are marked for deletion possibly killed.
1337 *----------------------------------------------------------------------
1339 static void dynamic_kill_idle_fs_procs(void)
1341 fcgi_server *s;
1342 int victims = 0;
1344 for (s = fcgi_servers; s != NULL; s = s->next)
1347 * server's smoothed running time, or if that's 0, the current total
1349 unsigned long connTime;
1352 * maximum number of microseconds that all of a server's running
1353 * processes together could have spent running since the last check
1355 unsigned long totalTime;
1358 * percentage, 0-100, of totalTime that the processes actually used
1360 int loadFactor;
1362 int i;
1363 int really_running = 0;
1365 if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1367 continue;
1370 /* s->numProcesses includes pending kills so get the "active" count */
1371 for (i = 0; i < dynamicMaxClassProcs; ++i)
1373 if (s->procs[i].state == FCGI_RUNNING_STATE) ++really_running;
1376 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1377 totalTime = really_running * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1379 loadFactor = 100 * connTime / totalTime;
1381 if (really_running == 1)
1383 if (loadFactor >= dynamicThreshold1)
1385 continue;
1388 else
1390 int load = really_running / ( really_running - 1) * loadFactor;
1392 if (load >= dynamicThresholdN)
1394 continue;
1399 * Run through the procs to see if we can get away w/o waxing one.
1401 for (i = 0; i < dynamicMaxClassProcs; ++i)
1403 if (s->procs[i].state == FCGI_START_STATE)
1405 s->procs[i].state = FCGI_READY_STATE;
1406 break;
1408 else if (s->procs[i].state == FCGI_VICTIM_STATE)
1410 break;
1414 if (i >= dynamicMaxClassProcs)
1416 ServerProcess * procs = s->procs;
1417 int youngest = -1;
1419 for (i = 0; i < dynamicMaxClassProcs; ++i)
1421 if (procs[i].state == FCGI_RUNNING_STATE)
1423 if (youngest == -1 || procs[i].start_time >= procs[youngest].start_time)
1425 youngest = i;
1430 if (youngest != -1)
1432 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1433 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1434 s->fs_path, (long) s->procs[youngest].pid);
1436 fcgi_kill(&s->procs[youngest], SIGTERM);
1438 victims++;
1442 * If the number of non-victims is less than or equal to
1443 * the minimum that may be running without being killed off,
1444 * don't select any more victims.
1446 if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1448 break;
1454 #ifdef WIN32
1456 // This is a little bogus, there's gotta be a better way to do this
1457 // Can we use WaitForMultipleObjects()
1458 #define FCGI_PROC_WAIT_TIME 100
1460 void child_wait_thread_main(void *dummy) {
1461 fcgi_server *s;
1462 DWORD dwRet = WAIT_TIMEOUT;
1463 int numChildren;
1464 int i;
1465 int waited;
1467 while (!bTimeToDie) {
1468 waited = 0;
1470 for (s = fcgi_servers; s != NULL; s = s->next) {
1471 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1472 continue;
1474 if (s->directive == APP_CLASS_DYNAMIC) {
1475 numChildren = dynamicMaxClassProcs;
1477 else {
1478 numChildren = s->numProcesses;
1481 for (i=0; i < numChildren; i++) {
1482 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1484 DWORD exitStatus = 0;
1486 /* timeout is currently set for 100 miliecond */
1487 /* it may need to be longer or user customizable */
1488 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1490 waited = 1;
1492 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1493 /* a child fs has died */
1494 /* mark the child as dead */
1496 if (s->directive == APP_CLASS_STANDARD) {
1497 /* restart static app */
1498 s->procs[i].state = FCGI_START_STATE;
1499 s->numFailures++;
1501 else {
1502 s->numProcesses--;
1503 fcgi_dynamic_total_proc_count--;
1504 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1506 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1507 s->procs[i].state = FCGI_KILLED_STATE;
1509 else {
1510 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1511 s->numFailures++;
1513 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1514 s->procs[i].state = FCGI_START_STATE;
1516 else {
1517 s->procs[i].state = FCGI_READY_STATE;
1522 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1524 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1525 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1526 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1527 s->fs_path, (long) s->procs[i].pid, exitStatus);
1529 CloseHandle(s->procs[i].handle);
1530 s->procs[i].handle = INVALID_HANDLE_VALUE;
1531 s->procs[i].pid = -1;
1533 /* wake up the main thread */
1534 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1539 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1542 #endif
1544 #ifndef WIN32
1545 static void setup_signals(void)
1547 struct sigaction sa;
1549 /* Setup handlers */
1551 sa.sa_handler = signal_handler;
1552 sigemptyset(&sa.sa_mask);
1553 sa.sa_flags = 0;
1555 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1556 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1557 "sigaction(SIGTERM) failed");
1559 /* httpd restart */
1560 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1561 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1562 "sigaction(SIGHUP) failed");
1564 /* httpd graceful restart */
1565 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1566 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1567 "sigaction(SIGUSR1) failed");
1569 /* read messages from request handlers - kill interval expired */
1570 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1571 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1572 "sigaction(SIGALRM) failed");
1574 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1575 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1576 "sigaction(SIGCHLD) failed");
1579 #endif
1581 #if !defined(WIN32) && !defined(APACHE2)
1582 int fcgi_pm_main(void *dummy, child_info *info)
1583 #else
1584 void fcgi_pm_main(void *dummy)
1585 #endif
1587 fcgi_server *s;
1588 unsigned int i;
1589 int read_ready = 0;
1590 int alarmLeft = 0;
1592 #ifdef WIN32
1593 DWORD dwRet;
1594 HANDLE child_wait_thread = INVALID_HANDLE_VALUE;
1595 #else
1596 int callWaitPid, callDynamicProcs;
1597 #endif
1599 #ifdef WIN32
1600 // Add SystemRoot to the dynamic environment
1601 char ** envp = dynamicEnvp;
1602 for (i = 0; *envp; ++i) {
1603 ++envp;
1605 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1607 #else
1609 reduce_privileges();
1610 change_process_name("fcgi-pm");
1612 close(fcgi_pm_pipe[1]);
1613 setup_signals();
1615 if (fcgi_wrapper) {
1616 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1617 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1619 #endif
1621 /* Initialize AppClass */
1622 for (s = fcgi_servers; s != NULL; s = s->next)
1624 if (s->directive != APP_CLASS_STANDARD)
1625 continue;
1627 #ifdef WIN32
1628 if (s->socket_path)
1629 s->listenFd = 0;
1630 #endif
1632 for (i = 0; i < s->numProcesses; ++i)
1633 s->procs[i].state = FCGI_START_STATE;
1636 #ifdef WIN32
1637 child_wait_thread = (HANDLE) _beginthread(child_wait_thread_main, 0, NULL);
1639 if (child_wait_thread == (HANDLE) -1)
1641 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1642 "FastCGI: failed to create process manager's wait thread!");
1645 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1646 "FastCGI: process manager initialized");
1647 #else
1648 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1649 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1650 #endif
1652 now = time(NULL);
1655 * Loop until SIGTERM
1657 for (;;) {
1658 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1659 #ifdef WIN32
1660 time_t expire;
1661 #else
1662 pid_t childPid;
1663 int waitStatus;
1664 #endif
1665 unsigned int numChildren;
1668 * If we came out of sigsuspend() for any reason other than
1669 * SIGALRM, pick up where we left off.
1671 if (alarmLeft)
1672 sleepSeconds = alarmLeft;
1675 * Examine each configured AppClass for a process that needs
1676 * starting. Compute the earliest time when the start should
1677 * be attempted, starting it now if the time has passed. Also,
1678 * remember that we do NOT need to restart externally managed
1679 * FastCGI applications.
1681 for (s = fcgi_servers; s != NULL; s = s->next)
1683 if (s->directive == APP_CLASS_EXTERNAL)
1684 continue;
1686 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1687 ? dynamicMaxClassProcs
1688 : s->numProcesses;
1690 for (i = 0; i < numChildren; ++i)
1692 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1694 int restart = (s->procs[i].pid < 0);
1695 time_t restartTime = s->restartTime;
1697 if (s->bad)
1699 /* we've gone to using the badDelay, the only thing that
1700 resets bad is when badDelay has expired. but numFailures
1701 is only just set below its threshold. the proc's
1702 start_times are all reset when the bad is. the numFailures
1703 is reset when we see an app run for a period */
1705 s->procs[i].start_time = 0;
1708 if (s->numFailures > MAX_FAILED_STARTS)
1710 time_t last_start_time = s->procs[i].start_time;
1712 if (last_start_time && now - last_start_time > RUNTIME_SUCCESS_INTERVAL)
1714 s->bad = 0;
1715 s->numFailures = 0;
1716 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1717 "FastCGI:%s server \"%s\" has remained"
1718 " running for more than %d seconds, its restart"
1719 " interval has been restored to %d seconds",
1720 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1721 s->fs_path, RUNTIME_SUCCESS_INTERVAL, s->restartDelay);
1723 else
1725 unsigned int j;
1727 for (j = 0; j < numChildren; ++j)
1729 if (s->procs[j].pid <= 0) continue;
1730 if (s->procs[j].state != FCGI_RUNNING_STATE) continue;
1731 if (s->procs[j].start_time == 0) continue;
1732 if (now - s->procs[j].start_time > RUNTIME_SUCCESS_INTERVAL) break;
1735 if (j >= numChildren)
1737 s->bad = 1;
1738 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1739 "FastCGI:%s server \"%s\" has failed to remain"
1740 " running for %d seconds given %d attempts, its restart"
1741 " interval has been backed off to %d seconds",
1742 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1743 s->fs_path, RUNTIME_SUCCESS_INTERVAL, MAX_FAILED_STARTS,
1744 FAILED_STARTS_DELAY);
1746 else
1748 s->bad = 0;
1749 s->numFailures = 0;
1750 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1751 "FastCGI:%s server \"%s\" has remained"
1752 " running for more than %d seconds, its restart"
1753 " interval has been restored to %d seconds",
1754 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1755 s->fs_path, RUNTIME_SUCCESS_INTERVAL, s->restartDelay);
1760 if (s->bad)
1762 restartTime += FAILED_STARTS_DELAY;
1764 else
1766 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1769 if (restartTime <= now)
1771 if (s->bad)
1773 s->bad = 0;
1774 s->numFailures = MAX_FAILED_STARTS;
1777 if (s->listenFd < 0 && init_listen_sock(s))
1779 if (sleepSeconds > s->initStartDelay)
1780 sleepSeconds = s->initStartDelay;
1781 break;
1783 #ifndef WIN32
1784 if (caughtSigTerm) {
1785 goto ProcessSigTerm;
1787 #endif
1788 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1789 if (s->procs[i].pid <= 0) {
1790 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1791 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1792 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1793 s->fs_path);
1795 sleepSeconds = min(sleepSeconds,
1796 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1798 ap_assert(s->procs[i].pid < 0);
1799 break;
1802 s->procs[i].start_time = now;
1803 s->restartTime = now;
1805 if (s->startTime == 0) {
1806 s->startTime = now;
1809 if (s->directive == APP_CLASS_DYNAMIC) {
1810 s->numProcesses++;
1811 fcgi_dynamic_total_proc_count++;
1812 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1815 s->procs[i].state = FCGI_RUNNING_STATE;
1817 if (fcgi_wrapper) {
1818 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1819 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1820 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1821 s->fs_path, (long) s->uid, (long) s->gid,
1822 restart ? "re" : "", (long) s->procs[i].pid);
1824 else {
1825 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1826 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1827 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1828 s->fs_path, restart ? "re" : "", (long) s->procs[i].pid);
1830 ap_assert(s->procs[i].pid > 0);
1831 } else {
1832 sleepSeconds = min(sleepSeconds, restartTime - now);
1838 #ifndef WIN32
1840 if(caughtSigTerm) {
1841 goto ProcessSigTerm;
1843 if((!caughtSigChld) && (!caughtSigAlarm)) {
1844 fd_set rfds;
1846 alarm(sleepSeconds);
1848 FD_ZERO(&rfds);
1849 FD_SET(fcgi_pm_pipe[0], &rfds);
1850 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1852 alarmLeft = alarm(0);
1854 callWaitPid = caughtSigChld;
1855 caughtSigChld = FALSE;
1856 callDynamicProcs = caughtSigAlarm;
1857 caughtSigAlarm = FALSE;
1859 now = time(NULL);
1862 * Dynamic fcgi process management
1864 if((callDynamicProcs) || (!callWaitPid)) {
1865 dynamic_read_msgs(read_ready);
1866 if(fcgi_dynamic_epoch == 0) {
1867 fcgi_dynamic_epoch = now;
1869 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1870 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1871 dynamic_kill_idle_fs_procs();
1872 fcgi_dynamic_epoch = now;
1876 if(!callWaitPid) {
1877 continue;
1880 /* We've caught SIGCHLD, so find out who it was using waitpid,
1881 * write a log message and update its data structure. */
1883 for (;;) {
1884 if (caughtSigTerm)
1885 goto ProcessSigTerm;
1887 childPid = waitpid(-1, &waitStatus, WNOHANG);
1889 if (childPid == -1 || childPid == 0)
1890 break;
1892 for (s = fcgi_servers; s != NULL; s = s->next) {
1893 if (s->directive == APP_CLASS_EXTERNAL)
1894 continue;
1896 if (s->directive == APP_CLASS_DYNAMIC)
1897 numChildren = dynamicMaxClassProcs;
1898 else
1899 numChildren = s->numProcesses;
1901 for (i = 0; i < numChildren; i++) {
1902 if (s->procs[i].pid == childPid)
1903 goto ChildFound;
1907 /* TODO: print something about this unknown child */
1908 continue;
1910 ChildFound:
1911 s->procs[i].pid = -1;
1913 if (s->directive == APP_CLASS_STANDARD) {
1914 /* Always restart static apps */
1915 s->procs[i].state = FCGI_START_STATE;
1916 s->numFailures++;
1918 else {
1919 s->numProcesses--;
1920 fcgi_dynamic_total_proc_count--;
1922 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1923 s->procs[i].state = FCGI_KILLED_STATE;
1925 else {
1926 /* A dynamic app died or exited without provocation from the PM */
1927 s->numFailures++;
1929 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1930 s->procs[i].state = FCGI_START_STATE;
1931 else
1932 s->procs[i].state = FCGI_READY_STATE;
1936 if (WIFEXITED(waitStatus)) {
1937 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1938 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1939 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1940 s->fs_path, (long) childPid, WEXITSTATUS(waitStatus));
1942 else if (WIFSIGNALED(waitStatus)) {
1943 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1944 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1945 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1946 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus),
1947 #ifdef WCOREDUMP
1948 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1949 #else
1950 "");
1951 #endif
1953 else if (WIFSTOPPED(waitStatus)) {
1954 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1955 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1956 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1957 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus));
1959 } /* for (;;), waitpid() */
1961 #else /* WIN32 */
1963 /* wait for an event to occur or timer expires */
1964 expire = time(NULL) + sleepSeconds;
1965 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1967 if (dwRet == WAIT_FAILED) {
1968 /* There is something seriously wrong here */
1969 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1970 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1971 bTimeToDie = TRUE;
1974 if (dwRet != WAIT_TIMEOUT) {
1975 now = time(NULL);
1977 if (now < expire)
1978 alarmLeft = expire - now;
1982 * Dynamic fcgi process management
1984 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1985 if (dwRet == MBOX_EVENT) {
1986 read_ready = 1;
1989 now = time(NULL);
1991 dynamic_read_msgs(read_ready);
1993 if(fcgi_dynamic_epoch == 0) {
1994 fcgi_dynamic_epoch = now;
1997 if ((now-fcgi_dynamic_epoch >= (int) dynamicKillInterval) ||
1998 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1999 dynamic_kill_idle_fs_procs();
2000 fcgi_dynamic_epoch = now;
2002 read_ready = 0;
2004 else if (dwRet == WAKE_EVENT) {
2005 continue;
2007 else if (dwRet == TERM_EVENT) {
2008 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
2009 "FastCGI: Termination event received process manager shutting down");
2011 bTimeToDie = TRUE;
2012 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2014 goto ProcessSigTerm;
2016 else {
2017 // Have an received an unknown event - should not happen
2018 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
2019 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
2021 bTimeToDie = TRUE;
2022 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2024 goto ProcessSigTerm;
2027 #endif /* WIN32 */
2029 } /* for (;;), the whole shoot'n match */
2031 ProcessSigTerm:
2033 * Kill off the children, then exit.
2035 shutdown_all();
2037 #ifdef WIN32
2038 return;
2039 #else
2040 exit(0);
2041 #endif
2044 #ifdef WIN32
2045 int fcgi_pm_add_job(fcgi_pm_job *new_job)
2047 int rv = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
2049 if (rv != WAIT_OBJECT_0 && rv != WAIT_ABANDONED)
2051 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2052 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2053 return -1;
2056 new_job->next = fcgi_dynamic_mbox;
2057 fcgi_dynamic_mbox = new_job;
2059 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
2061 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2062 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
2065 return 0;
2067 #endif