Eliminate the need for SetHandler or AddHandler with static or
[mod_fastcgi.git] / fcgi_pm.c
blobc0abec79f64946e23df274317695ea75df8f7c6a
1 /*
2 * $Id: fcgi_pm.c,v 1.80 2002/10/11 00:07:03 robs Exp $
3 */
6 #include "fcgi.h"
8 #if defined(APACHE2) && !defined(WIN32)
9 #include <pwd.h>
10 #include "unixd.h"
11 #endif
13 #ifdef _HPUX_SOURCE
14 #include <unistd.h>
15 #define seteuid(arg) setresuid(-1, (arg), -1)
16 #endif
18 int fcgi_dynamic_total_proc_count = 0; /* number of running apps */
19 time_t fcgi_dynamic_epoch = 0; /* last time kill_procs was
20 * invoked by process mgr */
21 time_t fcgi_dynamic_last_analyzed = 0; /* last time calculation was
22 * made for the dynamic procs */
24 static time_t now = 0;
26 #ifdef WIN32
27 #pragma warning ( disable : 4100 4102 )
28 static BOOL bTimeToDie = FALSE; /* process termination flag */
29 HANDLE fcgi_event_handles[3];
30 #ifndef SIGKILL
31 #define SIGKILL 9
32 #endif
33 #endif
36 #ifndef WIN32
37 static int seteuid_root(void)
39 int rc = seteuid((uid_t)0);
40 if (rc == -1) {
41 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
42 "FastCGI: seteuid(0) failed");
44 return rc;
47 static int seteuid_user(void)
49 int rc = seteuid(ap_user_id);
50 if (rc == -1) {
51 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
52 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id);
54 return rc;
56 #endif
59 * Signal the process to exit. How (or if) the process responds
60 * depends on the FastCGI application library (esp. on Win32) and
61 * possibly application code (signal handlers and whether or not
62 * SA_RESTART is on). At any rate, we send the signal with the
63 * hopes that the process will exit on its own. Later, as we
64 * review the state of application processes, if we see one marked
65 * for death, but that hasn't died within a specified period of
66 * time, fcgi_kill() is called again with a KILL)
68 static void fcgi_kill(ServerProcess *process, int sig)
70 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process->pid, sig);
72 process->state = FCGI_VICTIM_STATE;
74 #ifdef WIN32
76 if (sig == SIGTERM)
78 SetEvent(process->terminationEvent);
80 else if (sig == SIGKILL)
82 TerminateProcess(process->handle, 1);
84 else
86 ap_assert(0);
89 #else /* !WIN32 */
91 if (fcgi_wrapper)
93 seteuid_root();
96 kill(process->pid, sig);
98 if (fcgi_wrapper)
100 seteuid_user();
103 #endif /* !WIN32 */
106 /*******************************************************************************
107 * Send SIGTERM to each process in the server class, remove socket
108 * file if appropriate. Currently this is only called when the PM is shutting
109 * down and thus memory isn't freed and sockets and files aren't closed.
111 static void shutdown_all()
113 fcgi_server *s = fcgi_servers;
115 while (s)
117 ServerProcess *proc = s->procs;
118 int i;
119 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
120 ? dynamicMaxClassProcs
121 : s->numProcesses;
123 #ifndef WIN32
124 if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL)
126 /* Remove the socket file */
127 if (unlink(s->socket_path) != 0 && errno != ENOENT) {
128 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
129 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
130 s->socket_path,
131 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path);
134 #endif
136 /* Send TERM to all processes */
137 for (i = 0; i < numChildren; i++, proc++)
139 if (proc->state == FCGI_RUNNING_STATE)
141 fcgi_kill(proc, SIGTERM);
145 s = s->next;
148 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
151 * WIN32 applications may not have support for the shutdown event
152 * depending on their application library version
155 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT);
156 s = fcgi_servers;
158 while (s)
160 ServerProcess *proc = s->procs;
161 int i;
162 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
163 ? dynamicMaxClassProcs
164 : s->numProcesses;
166 /* Send KILL to all processes */
167 for (i = 0; i < numChildren; i++, proc++)
169 if (proc->state == FCGI_RUNNING_STATE)
171 fcgi_kill(proc, SIGKILL);
175 s = s->next;
178 #endif /* WIN32 */
181 static int init_listen_sock(fcgi_server * fs)
183 ap_assert(fs->directive != APP_CLASS_EXTERNAL);
185 /* Create the socket */
186 if ((fs->listenFd = socket(fs->socket_addr->sa_family, SOCK_STREAM, 0)) < 0)
188 #ifdef WIN32
189 errno = WSAGetLastError(); // Not sure if this will work as expected
190 #endif
191 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
192 "FastCGI: can't create %sserver \"%s\": socket() failed",
193 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
194 fs->fs_path);
195 return -1;
198 #ifndef WIN32
199 if (fs->socket_addr->sa_family == AF_UNIX)
201 /* Remove any existing socket file.. just in case */
202 unlink(((struct sockaddr_un *)fs->socket_addr)->sun_path);
204 else
205 #endif
207 int flag = 1;
208 setsockopt(fs->listenFd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
211 /* Bind it to the socket_addr */
212 if (bind(fs->listenFd, fs->socket_addr, fs->socket_addr_len))
214 char port[11];
216 #ifdef WIN32
217 errno = WSAGetLastError();
218 #endif
219 ap_snprintf(port, sizeof(port), "port=%d",
220 ((struct sockaddr_in *)fs->socket_addr)->sin_port);
222 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
223 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
224 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
225 fs->fs_path,
226 #ifndef WIN32
227 (fs->socket_addr->sa_family == AF_UNIX) ?
228 ((struct sockaddr_un *)fs->socket_addr)->sun_path :
229 #endif
230 port);
233 #ifndef WIN32
234 /* Twiddle Unix socket permissions */
235 else if (fs->socket_addr->sa_family == AF_UNIX
236 && chmod(((struct sockaddr_un *)fs->socket_addr)->sun_path, S_IRUSR | S_IWUSR))
238 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
239 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
240 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
241 fs->fs_path);
243 #endif
245 /* Set to listen */
246 else if (listen(fs->listenFd, fs->listenQueueDepth))
248 #ifdef WIN32
249 errno = WSAGetLastError();
250 #endif
251 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
252 "FastCGI: can't create %sserver \"%s\": listen() failed",
253 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
254 fs->fs_path);
256 else
258 return 0;
261 #ifdef WIN32
262 closesocket(fs->listenFd);
263 #else
264 close(fs->listenFd);
265 #endif
267 fs->listenFd = -1;
269 return -2;
273 *----------------------------------------------------------------------
275 * pm_main
277 * The FastCGI process manager, which runs as a separate
278 * process responsible for:
279 * - Starting all the FastCGI proceses.
280 * - Restarting any of these processes that die (indicated
281 * by SIGCHLD).
282 * - Catching SIGTERM and relaying it to all the FastCGI
283 * processes before exiting.
285 * Inputs:
286 * Uses global variable fcgi_servers.
288 * Results:
289 * Does not return.
291 * Side effects:
292 * Described above.
294 *----------------------------------------------------------------------
296 #ifndef WIN32
297 static int caughtSigTerm = FALSE;
298 static int caughtSigChld = FALSE;
299 static int caughtSigAlarm = FALSE;
301 static void signal_handler(int signo)
303 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
304 /* SIGUSR1 & SIGHUP are sent by apache to its process group
305 * when apache get 'em. Apache follows up (1.2.x) with attacks
306 * on each of its child processes, but we've got the KillMgr
307 * sitting between us so we never see the KILL. The main loop
308 * in ProcMgr also checks to see if the KillMgr has terminated,
309 * and if it has, we handl it as if we should shutdown too. */
310 caughtSigTerm = TRUE;
311 } else if(signo == SIGCHLD) {
312 caughtSigChld = TRUE;
313 } else if(signo == SIGALRM) {
314 caughtSigAlarm = TRUE;
317 #endif
320 *----------------------------------------------------------------------
322 * spawn_fs_process --
324 * Fork and exec the specified fcgi process.
326 * Results:
327 * 0 for successful fork, -1 for failed fork.
329 * In case the child fails before or in the exec, the child
330 * obtains the error log by calling getErrLog, logs
331 * the error, and exits with exit status = errno of
332 * the failed system call.
334 * Side effects:
335 * Child process created.
337 *----------------------------------------------------------------------
339 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
341 #ifndef WIN32
343 pid_t child_pid;
344 int i;
345 char *dirName;
346 char *dnEnd, *failedSysCall;
348 child_pid = fork();
349 if (child_pid) {
350 return child_pid;
353 /* We're the child. We're gonna exec() so pools don't matter. */
355 dnEnd = strrchr(fs->fs_path, '/');
356 if (dnEnd == NULL) {
357 dirName = "./";
358 } else {
359 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
360 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
362 if (chdir(dirName) < 0) {
363 failedSysCall = "chdir()";
364 goto FailedSystemCallExit;
367 #ifndef __EMX__
368 /* OS/2 dosen't support nice() */
369 if (fs->processPriority != 0) {
370 if (nice(fs->processPriority) == -1) {
371 failedSysCall = "nice()";
372 goto FailedSystemCallExit;
375 #endif
377 /* Open the listenFd on spec'd fd */
378 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
379 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
381 /* Close all other open fds, except stdout/stderr. Leave these two open so
382 * FastCGI applications don't have to find and fix ALL 3rd party libs that
383 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
384 * main server error_log - @@@ provide a directive control where this goes.
386 ap_error_log2stderr(fcgi_apache_main_server);
387 dup2(2, 1);
388 for (i = 0; i < FCGI_MAX_FD; i++) {
389 if (i != FCGI_LISTENSOCK_FILENO && i != 2 && i != 1) {
390 close(i);
394 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
395 * install its own handler. */
396 signal(SIGPIPE, SIG_IGN);
398 if (fcgi_wrapper && (fcgi_user_id != fs->uid || fcgi_group_id != fs->gid)) {
399 char *shortName = strrchr(fs->fs_path, '/') + 1;
401 /* Relinquish our root real uid powers */
402 seteuid_root();
403 setuid(ap_user_id);
405 do {
406 execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group, shortName, NULL, fs->envp);
407 } while (errno == EINTR);
409 else {
410 do {
411 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
412 } while (errno == EINTR);
415 failedSysCall = "execle()";
417 FailedSystemCallExit:
418 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
419 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
420 exit(-1);
422 /* avoid an irrelevant compiler warning */
423 return(0);
425 #else /* WIN32 */
427 #ifdef APACHE2
429 /* based on mod_cgi.c:run_cgi_child() */
431 apr_pool_t * tp;
432 char * termination_env_string;
433 HANDLE listen_handle = INVALID_HANDLE_VALUE;
434 apr_procattr_t * procattr;
435 apr_proc_t proc = { 0 };
436 apr_file_t * file;
437 int i = 0;
439 if (apr_pool_create(&tp, fcgi_config_pool))
440 return 0;
442 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
443 if (process->terminationEvent == NULL)
444 goto CLEANUP;
446 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
448 termination_env_string = ap_psprintf(tp,
449 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
451 while (fs->envp[i]) i++;
452 fs->envp[i++] = termination_env_string;
453 fs->envp[i] = (char *) fs->mutex_env_string;
455 ap_assert(fs->envp[i + 1] == NULL);
457 if (fs->socket_path)
459 SECURITY_ATTRIBUTES sa = { 0 };
461 sa.bInheritHandle = TRUE;
462 sa.nLength = sizeof(sa);
464 listen_handle = CreateNamedPipe(fs->socket_path,
465 PIPE_ACCESS_DUPLEX,
466 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
467 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
469 if (listen_handle == INVALID_HANDLE_VALUE)
471 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
472 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
473 fs->fs_path);
474 goto CLEANUP;
477 else
479 listen_handle = (HANDLE) fs->listenFd;
482 if (apr_procattr_create(&procattr, tp))
483 goto CLEANUP;
485 if (apr_procattr_dir_set(procattr, ap_make_dirstr_parent(tp, fs->fs_path)))
486 goto CLEANUP;
488 if (apr_procattr_detach_set(procattr, 1))
489 goto CLEANUP;
491 if (apr_os_file_put(&file, &listen_handle, 0, tp))
492 goto CLEANUP;
494 /* procattr is opaque so we have to use this - unfortuantely it dups */
495 if (apr_procattr_child_in_set(procattr, file, NULL))
496 goto CLEANUP;
498 if (apr_proc_create(&proc, fs->fs_path, NULL, fs->envp, procattr, tp))
499 goto CLEANUP;
501 process->handle = proc.hproc;
504 CLEANUP:
506 if (i)
508 fs->envp[i - 1] = NULL;
511 ap_destroy_pool(tp);
513 return proc.pid;
515 #else /* WIN32 && !APACHE2 */
517 /* Adapted from Apache's util_script.c ap_call_exec() */
518 char *interpreter = NULL;
519 char *quoted_filename;
520 char *pCommand;
521 char *pEnvBlock, *pNext;
523 int i = 0;
524 int iEnvBlockLen = 1;
526 file_type_e fileType;
528 STARTUPINFO si;
529 PROCESS_INFORMATION pi;
531 request_rec r;
532 pid_t pid = -1;
534 pool * tp = ap_make_sub_pool(fcgi_config_pool);
536 HANDLE listen_handle;
537 char * termination_env_string = NULL;
539 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
540 if (process->terminationEvent == NULL)
542 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
543 "FastCGI: can't create termination event for server \"%s\", "
544 "CreateEvent() failed", fs->fs_path);
545 goto CLEANUP;
547 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
549 termination_env_string = ap_psprintf(tp,
550 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
552 if (fs->socket_path)
554 SECURITY_ATTRIBUTES sa;
556 sa.lpSecurityDescriptor = NULL;
557 sa.bInheritHandle = TRUE;
558 sa.nLength = sizeof(sa);
560 listen_handle = CreateNamedPipe(fs->socket_path,
561 PIPE_ACCESS_DUPLEX,
562 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
563 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
565 if (listen_handle == INVALID_HANDLE_VALUE)
567 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
568 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
569 goto CLEANUP;
572 else
574 listen_handle = (HANDLE) fs->listenFd;
577 memset(&si, 0, sizeof(si));
578 memset(&pi, 0, sizeof(pi));
579 memset(&r, 0, sizeof(r));
581 // Can up a fake request to pass to ap_get_win32_interpreter()
582 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
583 r.server = fcgi_apache_main_server;
584 r.filename = (char *) fs->fs_path;
585 r.pool = tp;
587 fileType = ap_get_win32_interpreter(&r, &interpreter);
589 if (fileType == eFileTypeUNKNOWN) {
590 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
591 "FastCGI: %s is not executable; ensure interpreted scripts have "
592 "\"#!\" as their first line",
593 fs->fs_path);
594 ap_destroy_pool(tp);
595 goto CLEANUP;
599 * We have the interpreter (if there is one) and we have
600 * the arguments (if there are any).
601 * Build the command string to pass to CreateProcess.
603 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
604 if (interpreter && *interpreter) {
605 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
607 else {
608 pCommand = quoted_filename;
612 * Make child process use hPipeOutputWrite as standard out,
613 * and make sure it does not show on screen.
615 si.cb = sizeof(si);
616 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
617 si.wShowWindow = SW_HIDE;
618 si.hStdInput = listen_handle;
620 // XXX These should be open to the error_log
621 si.hStdOutput = INVALID_HANDLE_VALUE;
622 si.hStdError = INVALID_HANDLE_VALUE;
625 * Win32's CreateProcess call requires that the environment
626 * be passed in an environment block, a null terminated block of
627 * null terminated strings.
628 * @todo we should store the env in this format for win32.
630 while (fs->envp[i])
632 iEnvBlockLen += strlen(fs->envp[i]) + 1;
633 i++;
636 iEnvBlockLen += strlen(termination_env_string) + 1;
637 iEnvBlockLen += strlen(fs->mutex_env_string) + 1;
639 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
641 i = 0;
642 pNext = pEnvBlock;
643 while (fs->envp[i])
645 strcpy(pNext, fs->envp[i]);
646 pNext += strlen(pNext) + 1;
647 i++;
650 strcpy(pNext, termination_env_string);
651 pNext += strlen(pNext) + 1;
652 strcpy(pNext, fs->mutex_env_string);
654 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
656 pEnvBlock,
657 ap_make_dirstr_parent(tp, fs->fs_path),
658 &si, &pi))
660 /* Hack to get 16-bit CGI's working. It works for all the
661 * standard modules shipped with Apache. pi.dwProcessId is 0
662 * for 16-bit CGIs and all the Unix specific code that calls
663 * ap_call_exec interprets this as a failure case. And we can't
664 * use -1 either because it is mapped to 0 by the caller.
666 pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId;
668 process->handle = pi.hProcess;
669 CloseHandle(pi.hThread);
672 if (fs->socket_path)
674 CloseHandle(listen_handle);
677 CLEANUP:
679 ap_destroy_pool(tp);
681 return pid;
683 #endif /* !APACHE2 */
684 #endif /* WIN32 */
687 #ifndef WIN32
688 static void reduce_privileges(void)
690 const char *name;
692 if (geteuid() != 0)
693 return;
695 #ifndef __EMX__
696 /* Get username if passed as a uid */
697 if (ap_user_name[0] == '#') {
698 uid_t uid = atoi(&ap_user_name[1]);
699 struct passwd *ent = getpwuid(uid);
701 if (ent == NULL) {
702 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
703 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
704 "you probably need to modify the User directive", (unsigned)uid);
705 exit(1);
707 name = ent->pw_name;
709 else
710 name = ap_user_name;
712 /* Change Group */
713 if (setgid(ap_group_id) == -1) {
714 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
715 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
716 exit(1);
719 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
721 /* Initialize supplementary groups */
722 if (initgroups(name, ap_group_id) == -1) {
723 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
724 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
725 name, (unsigned)ap_group_id);
726 exit(1);
728 #endif /* __EMX__ */
730 /* Change User */
731 if (fcgi_wrapper) {
732 if (seteuid_user() == -1) {
733 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
734 "FastCGI: process manager exiting, failed to reduce privileges");
735 exit(1);
738 else {
739 if (setuid(ap_user_id) == -1) {
740 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
741 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
742 exit(1);
747 /*************
748 * Change the name of this process - best we can easily.
750 static void change_process_name(const char * const name)
752 /* under Apache2, ap_server_argv0 is const */
753 strncpy((char *) ap_server_argv0, name, strlen(ap_server_argv0));
755 #endif /* !WIN32 */
757 static void schedule_start(fcgi_server *s, int proc)
759 /* If we've started one recently, don't register another */
760 time_t time_passed = now - s->restartTime;
762 if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay))
763 || ((s->procs[proc].pid == 0) && (time_passed < s->initStartDelay)))
765 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);
766 return;
769 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
770 s->procs[proc].state = FCGI_START_STATE;
771 if (proc == dynamicMaxClassProcs - 1) {
772 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
773 "FastCGI: scheduled the %sstart of the last (dynamic) server "
774 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
775 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
780 *----------------------------------------------------------------------
782 * dynamic_read_msgs
784 * Removes the records written by request handlers and decodes them.
785 * We also update the data structures to reflect the changes.
787 *----------------------------------------------------------------------
790 static void dynamic_read_msgs(int read_ready)
792 fcgi_server *s;
793 int rc;
795 #ifndef WIN32
796 static int buflen = 0;
797 static char buf[FCGI_MSGS_BUFSIZE + 1];
798 char *ptr1, *ptr2, opcode;
799 char execName[FCGI_MAXPATH + 1];
800 char user[MAX_USER_NAME_LEN + 2];
801 char group[MAX_GID_CHAR_LEN + 1];
802 unsigned long q_usec = 0UL, req_usec = 0UL;
803 #else
804 fcgi_pm_job *joblist = NULL;
805 fcgi_pm_job *cjob = NULL;
806 #endif
808 pool *sp = NULL, *tp;
810 #ifndef WIN32
811 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
812 #endif
815 * To prevent the idle application from running indefinitely, we
816 * check the timer and if it is expired, we recompute the values
817 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
818 * message is received, only updates are made to the data structures.
820 if (fcgi_dynamic_last_analyzed == 0) {
821 fcgi_dynamic_last_analyzed = now;
823 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
824 for (s = fcgi_servers; s != NULL; s = s->next) {
825 if (s->directive != APP_CLASS_DYNAMIC)
826 break;
828 /* Advance the last analyzed timestamp by the elapsed time since
829 * it was last set. Round the increase down to the nearest
830 * multiple of dynamicUpdateInterval */
832 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
833 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
834 s->totalConnTime = 0UL;
835 s->totalQueueTime = 0UL;
839 if (read_ready <= 0) {
840 return;
843 #ifndef WIN32
844 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
845 if (rc <= 0) {
846 if (!caughtSigTerm) {
847 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
848 "FastCGI: read() from pipe failed (%d)", rc);
849 if (rc == 0) {
850 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
851 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
852 caughtSigTerm = TRUE;
855 return;
857 buflen += rc;
858 buf[buflen] = '\0';
860 #else
862 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
863 * request to do something) and/or when a timeout expires.
864 * There really should be no reason why this wait would get stuck
865 * but there's no point in waiting forever. */
867 rc = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
869 if (rc != WAIT_OBJECT_0 && rc != WAIT_ABANDONED)
871 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
872 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
873 return;
876 joblist = fcgi_dynamic_mbox;
877 fcgi_dynamic_mbox = NULL;
879 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
881 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
882 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
885 cjob = joblist;
886 #endif
888 #ifdef APACHE2
889 apr_pool_create(&tp, fcgi_config_pool);
890 #else
891 tp = ap_make_sub_pool(fcgi_config_pool);
892 #endif
894 #ifndef WIN32
895 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
896 int scan_failed = 0;
898 ptr2 = strchr(ptr1, '*');
899 if (ptr2) {
900 *ptr2++ = '\0';
902 else {
903 break;
906 opcode = *ptr1;
908 switch (opcode)
910 case FCGI_SERVER_START_JOB:
911 case FCGI_SERVER_RESTART_JOB:
913 if (sscanf(ptr1, "%c %s %16s %15s",
914 &opcode, execName, user, group) != 4)
916 scan_failed = 1;
918 break;
920 case FCGI_REQUEST_TIMEOUT_JOB:
922 if (sscanf(ptr1, "%c %s %16s %15s",
923 &opcode, execName, user, group) != 4)
925 scan_failed = 1;
927 break;
929 case FCGI_REQUEST_COMPLETE_JOB:
931 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
932 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
934 scan_failed = 1;
936 break;
938 default:
940 scan_failed = 1;
941 break;
944 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode, execName, user, group, q_usec, req_usec);
946 if (scan_failed) {
947 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
948 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
949 goto NextJob;
951 #else
952 /* Update data structures for processing */
953 while (cjob != NULL) {
954 joblist = cjob->next;
955 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
956 #endif
958 #ifndef WIN32
959 s = fcgi_util_fs_get(execName, user, group);
960 #else
961 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
962 #endif
964 #ifndef WIN32
965 if (s==NULL && opcode != FCGI_REQUEST_COMPLETE_JOB)
966 #else
967 if (s==NULL && cjob->id != FCGI_REQUEST_COMPLETE_JOB)
968 #endif
970 #ifdef WIN32
972 HANDLE mutex = CreateMutex(NULL, FALSE, cjob->fs_path);
974 if (mutex == NULL)
976 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
977 "FastCGI: can't create accept mutex "
978 "for (dynamic) server \"%s\"", cjob->fs_path);
979 goto BagNewServer;
982 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
983 #else
984 const char *err;
985 #endif
987 /* Create a perm subpool to hold the new server data,
988 * we can destroy it if something doesn't pan out */
989 #ifdef APACHE2
990 apr_pool_create(&sp, fcgi_config_pool);
991 #else
992 sp = ap_make_sub_pool(fcgi_config_pool);
993 #endif
995 /* Create a new "dynamic" server */
996 s = fcgi_util_fs_new(sp);
998 s->directive = APP_CLASS_DYNAMIC;
999 s->restartDelay = dynamicRestartDelay;
1000 s->listenQueueDepth = dynamicListenQueueDepth;
1001 s->initStartDelay = dynamicInitStartDelay;
1002 s->envp = dynamicEnvp;
1003 s->flush = dynamicFlush;
1005 #ifdef WIN32
1006 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
1007 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
1008 #else
1009 s->fs_path = ap_pstrdup(sp, execName);
1010 #endif
1011 ap_getparents(s->fs_path);
1012 ap_no2slash(s->fs_path);
1013 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
1015 /* XXX the socket_path (both Unix and Win) *is* deducible and
1016 * thus can and will be used by other apache instances without
1017 * the use of shared data regarding the processes serving the
1018 * requests. This can result in slightly unintuitive process
1019 * counts and security implications. This is prevented
1020 * if suexec (Unix) is in use. This is both a feature and a flaw.
1021 * Changing it now would break existing installations. */
1023 #ifndef WIN32
1024 /* Create socket file's path */
1025 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
1026 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1028 /* Create sockaddr, prealloc it so it won't get created in tp */
1029 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
1030 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
1031 &s->socket_addr_len, s->socket_path);
1032 if (err) {
1033 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1034 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
1035 goto BagNewServer;
1038 if (init_listen_sock(s)) {
1039 goto BagNewServer;
1042 /* If a wrapper is being used, config user/group info */
1043 if (fcgi_wrapper) {
1044 if (user[0] == '~') {
1045 /* its a user dir uri, the rest is a username, not a uid */
1046 struct passwd *pw = getpwnam(&user[1]);
1048 if (!pw) {
1049 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1050 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1051 execName, &user[1]);
1052 goto BagNewServer;
1054 s->uid = pw->pw_uid;
1055 s->user = ap_pstrdup(sp, user);
1056 s->username = s->user;
1058 s->gid = pw->pw_gid;
1059 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
1061 else {
1062 struct passwd *pw;
1064 s->uid = (uid_t)atol(user);
1065 pw = getpwuid(s->uid);
1066 if (!pw) {
1067 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1068 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1069 execName, (long)s->uid);
1070 goto BagNewServer;
1072 s->user = ap_pstrdup(sp, user);
1073 s->username = ap_pstrdup(sp, pw->pw_name);
1075 s->gid = (gid_t)atol(group);
1076 s->group = ap_pstrdup(sp, group);
1079 #else
1080 /* Create socket file's path */
1081 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
1082 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1083 s->listenFd = 0;
1084 #endif
1086 fcgi_util_fs_add(s);
1088 else {
1089 #ifndef WIN32
1090 if (opcode == FCGI_SERVER_RESTART_JOB) {
1091 #else
1092 if (cjob->id==FCGI_SERVER_RESTART_JOB) {
1093 #endif
1094 /* Check to see if the binary has changed. If so,
1095 * kill the FCGI application processes, and
1096 * restart them.
1098 struct stat stbuf;
1099 int i;
1100 #ifdef WIN32
1101 char * app_path = cjob->fs_path;
1102 #else
1103 char * app_path = execName;
1104 #endif
1106 if (stat(app_path, &stbuf) == 0 && stbuf.st_mtime > s->startTime)
1108 int do_restart = 0;
1110 /* prevent addition restart requests */
1111 s->startTime = now;
1112 #ifndef WIN32
1113 utime(s->socket_path, NULL);
1114 #endif
1116 /* kill old server(s) */
1117 for (i = 0; i < dynamicMaxClassProcs; i++)
1119 if (s->procs[i].pid > 0
1120 && stbuf.st_mtime > s->procs[i].start_time)
1122 fcgi_kill(&s->procs[i], SIGTERM);
1123 do_restart++;
1127 if (do_restart)
1129 ap_log_error(FCGI_LOG_WARN_NOERRNO,
1130 fcgi_apache_main_server, "FastCGI: restarting "
1131 "old server \"%s\" processes, newer version "
1132 "found", app_path);
1136 /* If dynamicAutoRestart, don't mark any new processes
1137 * for starting because we probably got the
1138 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1139 * will be restarting all of those we just killed.
1141 if (dynamicAutoRestart)
1142 goto NextJob;
1144 #ifndef WIN32
1145 else if (opcode == FCGI_SERVER_START_JOB) {
1146 #else
1147 else if (cjob->id==FCGI_SERVER_START_JOB) {
1148 #endif
1149 /* we've been asked to start a process--only start
1150 * it if we're not already running at least one
1151 * instance.
1153 int i;
1155 for (i = 0; i < dynamicMaxClassProcs; i++) {
1156 if (s->procs[i].state == FCGI_RUNNING_STATE)
1157 break;
1159 /* if already running, don't start another one */
1160 if (i < dynamicMaxClassProcs) {
1161 goto NextJob;
1166 #ifndef WIN32
1167 switch (opcode)
1168 #else
1169 switch (cjob->id)
1170 #endif
1172 int i, start;
1174 case FCGI_SERVER_RESTART_JOB:
1176 start = FALSE;
1178 /* We just waxed 'em all. Try to find an idle slot. */
1180 for (i = 0; i < dynamicMaxClassProcs; ++i)
1182 if (s->procs[i].state == FCGI_START_STATE
1183 || s->procs[i].state == FCGI_RUNNING_STATE)
1185 break;
1187 else if (s->procs[i].state == FCGI_KILLED_STATE
1188 || s->procs[i].state == FCGI_READY_STATE)
1190 start = TRUE;
1191 break;
1195 /* Nope, just use the first slot */
1196 if (i == dynamicMaxClassProcs)
1198 start = TRUE;
1199 i = 0;
1202 if (start)
1204 schedule_start(s, i);
1207 break;
1209 case FCGI_SERVER_START_JOB:
1210 case FCGI_REQUEST_TIMEOUT_JOB:
1212 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1214 * Extra instances should have been
1215 * terminated beforehand, probably need
1216 * to increase ProcessSlack parameter
1218 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1219 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1220 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1221 goto NextJob;
1224 /* find next free slot */
1225 for (i = 0; i < dynamicMaxClassProcs; i++)
1227 if (s->procs[i].state == FCGI_START_STATE)
1229 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1230 break;
1232 else if (s->procs[i].state == FCGI_RUNNING_STATE)
1234 continue;
1237 schedule_start(s, i);
1238 break;
1241 #ifdef FCGI_DEBUG
1242 if (i >= dynamicMaxClassProcs) {
1243 FCGIDBG1("ignore_job: slots are max'd");
1245 #endif
1246 break;
1247 case FCGI_REQUEST_COMPLETE_JOB:
1248 /* only record stats if we have a structure */
1249 if (s) {
1250 #ifndef WIN32
1251 s->totalConnTime += req_usec;
1252 s->totalQueueTime += q_usec;
1253 #else
1254 s->totalConnTime += cjob->start_time;
1255 s->totalQueueTime += cjob->qsec;
1256 #endif
1258 break;
1261 NextJob:
1263 #ifdef WIN32
1264 /* Cleanup job data */
1265 free(cjob->fs_path);
1266 free(cjob->user);
1267 free(cjob->group);
1268 free(cjob);
1269 cjob = joblist;
1270 #endif
1272 continue;
1274 BagNewServer:
1275 if (sp) ap_destroy_pool(sp);
1277 #ifdef WIN32
1278 free(cjob->fs_path);
1279 free(cjob);
1280 cjob = joblist;
1281 #endif
1284 #ifndef WIN32
1285 if (ptr1 == buf) {
1286 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1287 "FastCGI: really bogus message: \"%s\"", ptr1);
1288 ptr1 += strlen(buf);
1291 buflen -= ptr1 - buf;
1292 if (buflen) {
1293 memmove(buf, ptr1, buflen);
1295 #endif
1297 ap_destroy_pool(tp);
1301 *----------------------------------------------------------------------
1303 * dynamic_kill_idle_fs_procs
1305 * Implement a kill policy for the dynamic FastCGI applications.
1306 * We also update the data structures to reflect the changes.
1308 * Side effects:
1309 * Processes are marked for deletion possibly killed.
1311 *----------------------------------------------------------------------
1313 static void dynamic_kill_idle_fs_procs(void)
1315 fcgi_server *s;
1316 int victims = 0;
1318 for (s = fcgi_servers; s != NULL; s = s->next)
1321 * server's smoothed running time, or if that's 0, the current total
1323 unsigned long connTime;
1326 * maximum number of microseconds that all of a server's running
1327 * processes together could have spent running since the last check
1329 unsigned long totalTime;
1332 * percentage, 0-100, of totalTime that the processes actually used
1334 int loadFactor;
1336 int i;
1337 int really_running = 0;
1339 if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1341 continue;
1344 /* s->numProcesses includes pending kills so get the "active" count */
1345 for (i = 0; i < dynamicMaxClassProcs; ++i)
1347 if (s->procs[i].state == FCGI_RUNNING_STATE) ++really_running;
1350 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1351 totalTime = really_running * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1353 loadFactor = 100 * connTime / totalTime;
1355 if (really_running == 1)
1357 if (loadFactor >= dynamicThreshold1)
1359 continue;
1362 else
1364 int load = really_running / ( really_running - 1) * loadFactor;
1366 if (load >= dynamicThresholdN)
1368 continue;
1373 * Run through the procs to see if we can get away w/o waxing one.
1375 for (i = 0; i < dynamicMaxClassProcs; ++i)
1377 if (s->procs[i].state == FCGI_START_STATE)
1379 s->procs[i].state = FCGI_READY_STATE;
1380 break;
1382 else if (s->procs[i].state == FCGI_VICTIM_STATE)
1384 break;
1388 if (i >= dynamicMaxClassProcs)
1390 ServerProcess * procs = s->procs;
1391 int youngest = -1;
1393 for (i = 0; i < dynamicMaxClassProcs; ++i)
1395 if (procs[i].state == FCGI_RUNNING_STATE)
1397 if (youngest == -1 || procs[i].start_time >= procs[youngest].start_time)
1399 youngest = i;
1404 if (youngest != -1)
1406 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1407 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1408 s->fs_path, (long) s->procs[youngest].pid);
1410 fcgi_kill(&s->procs[youngest], SIGTERM);
1412 victims++;
1416 * If the number of non-victims is less than or equal to
1417 * the minimum that may be running without being killed off,
1418 * don't select any more victims.
1420 if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1422 break;
1428 #ifdef WIN32
1430 // This is a little bogus, there's gotta be a better way to do this
1431 // Can we use WaitForMultipleObjects()
1432 #define FCGI_PROC_WAIT_TIME 100
1434 void child_wait_thread_main(void *dummy) {
1435 fcgi_server *s;
1436 DWORD dwRet = WAIT_TIMEOUT;
1437 int numChildren;
1438 int i;
1439 int waited;
1441 while (!bTimeToDie) {
1442 waited = 0;
1444 for (s = fcgi_servers; s != NULL; s = s->next) {
1445 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1446 continue;
1448 if (s->directive == APP_CLASS_DYNAMIC) {
1449 numChildren = dynamicMaxClassProcs;
1451 else {
1452 numChildren = s->numProcesses;
1455 for (i=0; i < numChildren; i++) {
1456 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1458 DWORD exitStatus = 0;
1460 /* timeout is currently set for 100 miliecond */
1461 /* it may need to be longer or user customizable */
1462 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1464 waited = 1;
1466 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1467 /* a child fs has died */
1468 /* mark the child as dead */
1470 if (s->directive == APP_CLASS_STANDARD) {
1471 /* restart static app */
1472 s->procs[i].state = FCGI_START_STATE;
1473 s->numFailures++;
1475 else {
1476 s->numProcesses--;
1477 fcgi_dynamic_total_proc_count--;
1478 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1480 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1481 s->procs[i].state = FCGI_KILLED_STATE;
1483 else {
1484 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1485 s->numFailures++;
1487 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1488 s->procs[i].state = FCGI_START_STATE;
1490 else {
1491 s->procs[i].state = FCGI_READY_STATE;
1496 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1498 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1499 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1500 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1501 s->fs_path, (long) s->procs[i].pid, exitStatus);
1503 CloseHandle(s->procs[i].handle);
1504 s->procs[i].handle = INVALID_HANDLE_VALUE;
1505 s->procs[i].pid = -1;
1507 /* wake up the main thread */
1508 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1513 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1516 #endif
1518 #ifndef WIN32
1519 static void setup_signals(void)
1521 struct sigaction sa;
1523 /* Setup handlers */
1525 sa.sa_handler = signal_handler;
1526 sigemptyset(&sa.sa_mask);
1527 sa.sa_flags = 0;
1529 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1530 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1531 "sigaction(SIGTERM) failed");
1533 /* httpd restart */
1534 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1535 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1536 "sigaction(SIGHUP) failed");
1538 /* httpd graceful restart */
1539 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1540 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1541 "sigaction(SIGUSR1) failed");
1543 /* read messages from request handlers - kill interval expired */
1544 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1545 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1546 "sigaction(SIGALRM) failed");
1548 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1549 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1550 "sigaction(SIGCHLD) failed");
1553 #endif
1555 #if !defined(WIN32) && !defined(APACHE2)
1556 int fcgi_pm_main(void *dummy, child_info *info)
1557 #else
1558 void fcgi_pm_main(void *dummy)
1559 #endif
1561 fcgi_server *s;
1562 unsigned int i;
1563 int read_ready = 0;
1564 int alarmLeft = 0;
1566 #ifdef WIN32
1567 DWORD dwRet;
1568 HANDLE child_wait_thread = INVALID_HANDLE_VALUE;
1569 #else
1570 int callWaitPid, callDynamicProcs;
1571 #endif
1573 #ifdef WIN32
1574 // Add SystemRoot to the dynamic environment
1575 char ** envp = dynamicEnvp;
1576 for (i = 0; *envp; ++i) {
1577 ++envp;
1579 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1581 #else
1583 reduce_privileges();
1584 change_process_name("fcgi-pm");
1586 close(fcgi_pm_pipe[1]);
1587 setup_signals();
1589 if (fcgi_wrapper) {
1590 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1591 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1593 #endif
1595 /* Initialize AppClass */
1596 for (s = fcgi_servers; s != NULL; s = s->next)
1598 if (s->directive != APP_CLASS_STANDARD)
1599 continue;
1601 #ifdef WIN32
1602 if (s->socket_path)
1603 s->listenFd = 0;
1604 #endif
1606 for (i = 0; i < s->numProcesses; ++i)
1607 s->procs[i].state = FCGI_START_STATE;
1610 #ifdef WIN32
1611 child_wait_thread = (HANDLE) _beginthread(child_wait_thread_main, 0, NULL);
1613 if (child_wait_thread == (HANDLE) -1)
1615 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1616 "FastCGI: failed to create process manager's wait thread!");
1619 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1620 "FastCGI: process manager initialized");
1621 #else
1622 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1623 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1624 #endif
1626 now = time(NULL);
1629 * Loop until SIGTERM
1631 for (;;) {
1632 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1633 #ifdef WIN32
1634 time_t expire;
1635 #else
1636 pid_t childPid;
1637 int waitStatus;
1638 #endif
1639 unsigned int numChildren;
1642 * If we came out of sigsuspend() for any reason other than
1643 * SIGALRM, pick up where we left off.
1645 if (alarmLeft)
1646 sleepSeconds = alarmLeft;
1649 * Examine each configured AppClass for a process that needs
1650 * starting. Compute the earliest time when the start should
1651 * be attempted, starting it now if the time has passed. Also,
1652 * remember that we do NOT need to restart externally managed
1653 * FastCGI applications.
1655 for (s = fcgi_servers; s != NULL; s = s->next)
1657 if (s->directive == APP_CLASS_EXTERNAL)
1658 continue;
1660 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1661 ? dynamicMaxClassProcs
1662 : s->numProcesses;
1664 for (i = 0; i < numChildren; ++i)
1666 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1668 int restart = (s->procs[i].pid < 0);
1669 time_t restartTime = s->restartTime;
1671 if (s->bad)
1673 /* we've gone to using the badDelay, the only thing that
1674 resets bad is when badDelay has expired. but numFailures
1675 is only just set below its threshold. the proc's
1676 start_times are all reset when the bad is. the numFailures
1677 is reset when we see an app run for a period */
1679 s->procs[i].start_time = 0;
1682 if (s->numFailures > MAX_FAILED_STARTS)
1684 time_t last_start_time = s->procs[i].start_time;
1686 if (last_start_time && now - last_start_time > RUNTIME_SUCCESS_INTERVAL)
1688 s->bad = 0;
1689 s->numFailures = 0;
1690 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1691 "FastCGI:%s server \"%s\" has remained"
1692 " running for more than %d seconds, its restart"
1693 " interval has been restored to %d seconds",
1694 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1695 s->fs_path, RUNTIME_SUCCESS_INTERVAL, s->restartDelay);
1697 else
1699 unsigned int j;
1701 for (j = 0; j < numChildren; ++j)
1703 if (s->procs[j].pid <= 0) continue;
1704 if (s->procs[j].state != FCGI_RUNNING_STATE) continue;
1705 if (s->procs[j].start_time == 0) continue;
1706 if (now - s->procs[j].start_time > RUNTIME_SUCCESS_INTERVAL) break;
1709 if (j >= numChildren)
1711 s->bad = 1;
1712 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1713 "FastCGI:%s server \"%s\" has failed to remain"
1714 " running for %d seconds given %d attempts, its restart"
1715 " interval has been backed off to %d seconds",
1716 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1717 s->fs_path, RUNTIME_SUCCESS_INTERVAL, MAX_FAILED_STARTS,
1718 FAILED_STARTS_DELAY);
1720 else
1722 s->bad = 0;
1723 s->numFailures = 0;
1724 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1725 "FastCGI:%s server \"%s\" has remained"
1726 " running for more than %d seconds, its restart"
1727 " interval has been restored to %d seconds",
1728 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1729 s->fs_path, RUNTIME_SUCCESS_INTERVAL, s->restartDelay);
1734 if (s->bad)
1736 restartTime += FAILED_STARTS_DELAY;
1738 else
1740 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1743 if (restartTime <= now)
1745 if (s->bad)
1747 s->bad = 0;
1748 s->numFailures = MAX_FAILED_STARTS;
1751 if (s->listenFd < 0 && init_listen_sock(s))
1753 if (sleepSeconds > s->initStartDelay)
1754 sleepSeconds = s->initStartDelay;
1755 break;
1757 #ifndef WIN32
1758 if (caughtSigTerm) {
1759 goto ProcessSigTerm;
1761 #endif
1762 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1763 if (s->procs[i].pid <= 0) {
1764 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1765 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1766 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1767 s->fs_path);
1769 sleepSeconds = min(sleepSeconds,
1770 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1772 ap_assert(s->procs[i].pid < 0);
1773 break;
1776 s->procs[i].start_time = now;
1777 s->restartTime = now;
1779 if (s->startTime == 0) {
1780 s->startTime = now;
1783 if (s->directive == APP_CLASS_DYNAMIC) {
1784 s->numProcesses++;
1785 fcgi_dynamic_total_proc_count++;
1786 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1789 s->procs[i].state = FCGI_RUNNING_STATE;
1791 if (fcgi_wrapper) {
1792 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1793 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1794 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1795 s->fs_path, (long) s->uid, (long) s->gid,
1796 restart ? "re" : "", (long) s->procs[i].pid);
1798 else {
1799 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1800 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1801 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1802 s->fs_path, restart ? "re" : "", (long) s->procs[i].pid);
1804 ap_assert(s->procs[i].pid > 0);
1805 } else {
1806 sleepSeconds = min(sleepSeconds, restartTime - now);
1812 #ifndef WIN32
1814 if(caughtSigTerm) {
1815 goto ProcessSigTerm;
1817 if((!caughtSigChld) && (!caughtSigAlarm)) {
1818 fd_set rfds;
1820 alarm(sleepSeconds);
1822 FD_ZERO(&rfds);
1823 FD_SET(fcgi_pm_pipe[0], &rfds);
1824 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1826 alarmLeft = alarm(0);
1828 callWaitPid = caughtSigChld;
1829 caughtSigChld = FALSE;
1830 callDynamicProcs = caughtSigAlarm;
1831 caughtSigAlarm = FALSE;
1833 now = time(NULL);
1836 * Dynamic fcgi process management
1838 if((callDynamicProcs) || (!callWaitPid)) {
1839 dynamic_read_msgs(read_ready);
1840 if(fcgi_dynamic_epoch == 0) {
1841 fcgi_dynamic_epoch = now;
1843 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1844 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1845 dynamic_kill_idle_fs_procs();
1846 fcgi_dynamic_epoch = now;
1850 if(!callWaitPid) {
1851 continue;
1854 /* We've caught SIGCHLD, so find out who it was using waitpid,
1855 * write a log message and update its data structure. */
1857 for (;;) {
1858 if (caughtSigTerm)
1859 goto ProcessSigTerm;
1861 childPid = waitpid(-1, &waitStatus, WNOHANG);
1863 if (childPid == -1 || childPid == 0)
1864 break;
1866 for (s = fcgi_servers; s != NULL; s = s->next) {
1867 if (s->directive == APP_CLASS_EXTERNAL)
1868 continue;
1870 if (s->directive == APP_CLASS_DYNAMIC)
1871 numChildren = dynamicMaxClassProcs;
1872 else
1873 numChildren = s->numProcesses;
1875 for (i = 0; i < numChildren; i++) {
1876 if (s->procs[i].pid == childPid)
1877 goto ChildFound;
1881 /* TODO: print something about this unknown child */
1882 continue;
1884 ChildFound:
1885 s->procs[i].pid = -1;
1887 if (s->directive == APP_CLASS_STANDARD) {
1888 /* Always restart static apps */
1889 s->procs[i].state = FCGI_START_STATE;
1890 s->numFailures++;
1892 else {
1893 s->numProcesses--;
1894 fcgi_dynamic_total_proc_count--;
1896 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1897 s->procs[i].state = FCGI_KILLED_STATE;
1899 else {
1900 /* A dynamic app died or exited without provocation from the PM */
1901 s->numFailures++;
1903 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1904 s->procs[i].state = FCGI_START_STATE;
1905 else
1906 s->procs[i].state = FCGI_READY_STATE;
1910 if (WIFEXITED(waitStatus)) {
1911 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1912 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1913 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1914 s->fs_path, (long) childPid, WEXITSTATUS(waitStatus));
1916 else if (WIFSIGNALED(waitStatus)) {
1917 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1918 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1919 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1920 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus),
1921 #ifdef WCOREDUMP
1922 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1923 #else
1924 "");
1925 #endif
1927 else if (WIFSTOPPED(waitStatus)) {
1928 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1929 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1930 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1931 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus));
1933 } /* for (;;), waitpid() */
1935 #else /* WIN32 */
1937 /* wait for an event to occur or timer expires */
1938 expire = time(NULL) + sleepSeconds;
1939 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1941 if (dwRet == WAIT_FAILED) {
1942 /* There is something seriously wrong here */
1943 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1944 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1945 bTimeToDie = TRUE;
1948 if (dwRet != WAIT_TIMEOUT) {
1949 now = time(NULL);
1951 if (now < expire)
1952 alarmLeft = expire - now;
1956 * Dynamic fcgi process management
1958 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1959 if (dwRet == MBOX_EVENT) {
1960 read_ready = 1;
1963 now = time(NULL);
1965 dynamic_read_msgs(read_ready);
1967 if(fcgi_dynamic_epoch == 0) {
1968 fcgi_dynamic_epoch = now;
1971 if ((now-fcgi_dynamic_epoch >= (int) dynamicKillInterval) ||
1972 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1973 dynamic_kill_idle_fs_procs();
1974 fcgi_dynamic_epoch = now;
1976 read_ready = 0;
1978 else if (dwRet == WAKE_EVENT) {
1979 continue;
1981 else if (dwRet == TERM_EVENT) {
1982 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1983 "FastCGI: Termination event received process manager shutting down");
1985 bTimeToDie = TRUE;
1986 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
1988 goto ProcessSigTerm;
1990 else {
1991 // Have an received an unknown event - should not happen
1992 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1993 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1995 bTimeToDie = TRUE;
1996 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
1998 goto ProcessSigTerm;
2001 #endif /* WIN32 */
2003 } /* for (;;), the whole shoot'n match */
2005 ProcessSigTerm:
2007 * Kill off the children, then exit.
2009 shutdown_all();
2011 #ifdef WIN32
2012 return;
2013 #else
2014 exit(0);
2015 #endif
2018 #ifdef WIN32
2019 int fcgi_pm_add_job(fcgi_pm_job *new_job)
2021 int rv = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
2023 if (rv != WAIT_OBJECT_0 && rv != WAIT_ABANDONED)
2025 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2026 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2027 return -1;
2030 new_job->next = fcgi_dynamic_mbox;
2031 fcgi_dynamic_mbox = new_job;
2033 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
2035 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2036 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
2039 return 0;
2041 #endif