add a debug log entry
[mod_fastcgi.git] / fcgi_pm.c
blob4de7fd112c1f7729437b4efcb483fcf068a0686a
1 /*
2 * $Id: fcgi_pm.c,v 1.71 2002/03/13 23:31:08 robs Exp $
3 */
6 #include "fcgi.h"
8 #ifdef _HPUX_SOURCE
9 #include <unistd.h>
10 #define seteuid(arg) setresuid(-1, (arg), -1)
11 #endif
13 int fcgi_dynamic_total_proc_count = 0; /* number of running apps */
14 time_t fcgi_dynamic_epoch = 0; /* last time kill_procs was
15 * invoked by process mgr */
16 time_t fcgi_dynamic_last_analyzed = 0; /* last time calculation was
17 * made for the dynamic procs */
19 static time_t now = 0;
21 #ifdef WIN32
22 #pragma warning ( disable : 4100 4102 )
23 static BOOL bTimeToDie = FALSE; /* process termination flag */
24 HANDLE fcgi_event_handles[3];
25 #ifndef SIGKILL
26 #define SIGKILL 9
27 #endif
28 #endif
31 #ifndef WIN32
32 static int seteuid_root(void)
34 int rc = seteuid((uid_t)0);
35 if (rc == -1) {
36 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
37 "FastCGI: seteuid(0) failed");
39 return rc;
42 static int seteuid_user(void)
44 int rc = seteuid(ap_user_id);
45 if (rc == -1) {
46 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
47 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id);
49 return rc;
51 #endif
54 * Signal the process to exit. How (or if) the process responds
55 * depends on the FastCGI application library (esp. on Win32) and
56 * possibly application code (signal handlers and whether or not
57 * SA_RESTART is on). At any rate, we send the signal with the
58 * hopes that the process will exit on its own. Later, as we
59 * review the state of application processes, if we see one marked
60 * for death, but that hasn't died within a specified period of
61 * time, fcgi_kill() is called again with a KILL)
63 static void fcgi_kill(ServerProcess *process, int sig)
65 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process->pid, sig);
67 process->state = FCGI_VICTIM_STATE;
69 #ifdef WIN32
70 if (sig == SIGTERM)
72 SetEvent(process->terminationEvent);
74 else if (sig == SIGKILL)
76 TerminateProcess(process->handle, 1);
78 else
80 ap_assert(0);
82 #else
83 if (fcgi_wrapper)
85 seteuid_root();
88 kill(process->pid, sig);
90 if (fcgi_wrapper)
92 seteuid_user();
94 #endif
97 /*******************************************************************************
98 * Send SIGTERM to each process in the server class, remove socket
99 * file if appropriate. Currently this is only called when the PM is shutting
100 * down and thus memory isn't freed and sockets and files aren't closed.
102 static void shutdown_all()
104 fcgi_server *s = fcgi_servers;
106 while (s)
108 ServerProcess *proc = s->procs;
109 int i;
110 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
111 ? dynamicMaxClassProcs
112 : s->numProcesses;
114 #ifndef WIN32
115 if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL)
117 /* Remove the socket file */
118 if (unlink(s->socket_path) != 0 && errno != ENOENT) {
119 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
120 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
121 s->socket_path,
122 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path);
125 #endif
127 /* Send TERM to all processes */
128 for (i = 0; i < numChildren; i++, proc++)
130 if (proc->state == FCGI_RUNNING_STATE)
132 fcgi_kill(proc, SIGTERM);
136 s = s->next;
139 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
142 * WIN32 applications may not have support for the shutdown event
143 * depending on their application library version
146 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT);
147 s = fcgi_servers;
149 while (s)
151 ServerProcess *proc = s->procs;
152 int i;
153 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
154 ? dynamicMaxClassProcs
155 : s->numProcesses;
157 /* Send KILL to all processes */
158 for (i = 0; i < numChildren; i++, proc++)
160 if (proc->state == FCGI_RUNNING_STATE)
162 fcgi_kill(proc, SIGKILL);
166 s = s->next;
169 #endif /* WIN32 */
172 static int init_listen_sock(fcgi_server * fs)
174 ap_assert(fs->directive != APP_CLASS_EXTERNAL);
176 /* Create the socket */
177 if ((fs->listenFd = socket(fs->socket_addr->sa_family, SOCK_STREAM, 0)) < 0)
179 #ifdef WIN32
180 errno = WSAGetLastError(); // Not sure if this will work as expected
181 #endif
182 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
183 "FastCGI: can't create %sserver \"%s\": socket() failed",
184 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
185 fs->fs_path);
186 return -1;
189 #ifndef WIN32
190 if (fs->socket_addr->sa_family == AF_UNIX)
192 /* Remove any existing socket file.. just in case */
193 unlink(((struct sockaddr_un *)fs->socket_addr)->sun_path);
195 else
196 #endif
198 int flag = 1;
199 setsockopt(fs->listenFd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
202 /* Bind it to the socket_addr */
203 if (bind(fs->listenFd, fs->socket_addr, fs->socket_addr_len))
205 char port[11];
207 #ifdef WIN32
208 errno = WSAGetLastError();
209 #endif
210 ap_snprintf(port, sizeof(port), "port=%d",
211 ((struct sockaddr_in *)fs->socket_addr)->sin_port);
213 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
214 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
215 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
216 fs->fs_path,
217 #ifndef WIN32
218 (fs->socket_addr->sa_family == AF_UNIX) ?
219 ((struct sockaddr_un *)fs->socket_addr)->sun_path :
220 #endif
221 port);
224 #ifndef WIN32
225 /* Twiddle Unix socket permissions */
226 else if (fs->socket_addr->sa_family == AF_UNIX
227 && chmod(((struct sockaddr_un *)fs->socket_addr)->sun_path, S_IRUSR | S_IWUSR))
229 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
230 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
231 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
232 fs->fs_path);
234 #endif
236 /* Set to listen */
237 else if (listen(fs->listenFd, fs->listenQueueDepth))
239 #ifdef WIN32
240 errno = WSAGetLastError();
241 #endif
242 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
243 "FastCGI: can't create %sserver \"%s\": listen() failed",
244 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
245 fs->fs_path);
247 else
249 return 0;
252 ap_pclosesocket(fcgi_config_pool, fs->listenFd);
253 fs->listenFd = -1;
254 return -2;
258 *----------------------------------------------------------------------
260 * pm_main
262 * The FastCGI process manager, which runs as a separate
263 * process responsible for:
264 * - Starting all the FastCGI proceses.
265 * - Restarting any of these processes that die (indicated
266 * by SIGCHLD).
267 * - Catching SIGTERM and relaying it to all the FastCGI
268 * processes before exiting.
270 * Inputs:
271 * Uses global variable fcgi_servers.
273 * Results:
274 * Does not return.
276 * Side effects:
277 * Described above.
279 *----------------------------------------------------------------------
281 #ifndef WIN32
282 static int caughtSigTerm = FALSE;
283 static int caughtSigChld = FALSE;
284 static int caughtSigAlarm = FALSE;
286 static void signal_handler(int signo)
288 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
289 /* SIGUSR1 & SIGHUP are sent by apache to its process group
290 * when apache get 'em. Apache follows up (1.2.x) with attacks
291 * on each of its child processes, but we've got the KillMgr
292 * sitting between us so we never see the KILL. The main loop
293 * in ProcMgr also checks to see if the KillMgr has terminated,
294 * and if it has, we handl it as if we should shutdown too. */
295 caughtSigTerm = TRUE;
296 } else if(signo == SIGCHLD) {
297 caughtSigChld = TRUE;
298 } else if(signo == SIGALRM) {
299 caughtSigAlarm = TRUE;
302 #endif
305 *----------------------------------------------------------------------
307 * spawn_fs_process --
309 * Fork and exec the specified fcgi process.
311 * Results:
312 * 0 for successful fork, -1 for failed fork.
314 * In case the child fails before or in the exec, the child
315 * obtains the error log by calling getErrLog, logs
316 * the error, and exits with exit status = errno of
317 * the failed system call.
319 * Side effects:
320 * Child process created.
322 *----------------------------------------------------------------------
324 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
326 #ifndef WIN32
327 pid_t child_pid;
328 int i;
329 char *dirName;
330 char *dnEnd, *failedSysCall;
332 child_pid = fork();
333 if (child_pid) {
334 return child_pid;
337 /* We're the child. We're gonna exec() so pools don't matter. */
339 dnEnd = strrchr(fs->fs_path, '/');
340 if (dnEnd == NULL) {
341 dirName = "./";
342 } else {
343 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
344 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
346 if (chdir(dirName) < 0) {
347 failedSysCall = "chdir()";
348 goto FailedSystemCallExit;
351 #ifndef __EMX__
352 /* OS/2 dosen't support nice() */
353 if (fs->processPriority != 0) {
354 if (nice(fs->processPriority) == -1) {
355 failedSysCall = "nice()";
356 goto FailedSystemCallExit;
359 #endif
361 /* Open the listenFd on spec'd fd */
362 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
363 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
365 /* Close all other open fds, except stdout/stderr. Leave these two open so
366 * FastCGI applications don't have to find and fix ALL 3rd party libs that
367 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
368 * main server error_log - @@@ provide a directive control where this goes.
370 ap_error_log2stderr(fcgi_apache_main_server);
371 dup2(STDERR_FILENO, STDOUT_FILENO);
372 for (i = 0; i < FCGI_MAX_FD; i++) {
373 if (i != FCGI_LISTENSOCK_FILENO && i != STDERR_FILENO && i != STDOUT_FILENO) {
374 close(i);
378 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
379 * install its own handler. */
380 signal(SIGPIPE, SIG_IGN);
382 if (fcgi_wrapper && (fcgi_user_id != fs->uid || fcgi_group_id != fs->gid)) {
383 char *shortName = strrchr(fs->fs_path, '/') + 1;
385 /* Relinquish our root real uid powers */
386 seteuid_root();
387 setuid(ap_user_id);
389 do {
390 execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group, shortName, NULL, fs->envp);
391 } while (errno == EINTR);
393 else {
394 do {
395 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
396 } while (errno == EINTR);
399 failedSysCall = "execle()";
401 FailedSystemCallExit:
402 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
403 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
404 exit(-1);
406 /* avoid an irrelevant compiler warning */
407 return(0);
409 #else
411 /* Adapted from Apache's util_script.c ap_call_exec() */
412 char *interpreter = NULL;
413 char *quoted_filename;
414 char *pCommand;
415 char *pEnvBlock, *pNext;
417 int i = 0;
418 int iEnvBlockLen = 1;
420 file_type_e fileType;
422 STARTUPINFO si;
423 PROCESS_INFORMATION pi;
425 request_rec r;
426 pid_t pid = -1;
428 pool * tp = ap_make_sub_pool(fcgi_config_pool);
430 HANDLE listen_handle;
431 char * termination_env_string = NULL;
433 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
434 if (process->terminationEvent == NULL)
436 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
437 "FastCGI: can't create termination event for server \"%s\", "
438 "CreateEvent() failed", fs->fs_path);
439 exit(0);
441 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
443 termination_env_string = ap_psprintf(tp,
444 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
446 if (fs->socket_path)
448 SECURITY_ATTRIBUTES sa;
450 sa.lpSecurityDescriptor = NULL;
451 sa.bInheritHandle = TRUE;
452 sa.nLength = sizeof(sa);
454 listen_handle = CreateNamedPipe(fs->socket_path,
455 PIPE_ACCESS_DUPLEX,
456 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
457 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
459 if (listen_handle == INVALID_HANDLE_VALUE)
461 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
462 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
463 exit(0);
466 else
468 listen_handle = (HANDLE) fs->listenFd;
471 memset(&si, 0, sizeof(si));
472 memset(&pi, 0, sizeof(pi));
473 memset(&r, 0, sizeof(r));
475 // Can up a fake request to pass to ap_get_win32_interpreter()
476 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
477 r.server = fcgi_apache_main_server;
478 r.filename = (char *) fs->fs_path;
479 r.pool = tp;
481 fileType = ap_get_win32_interpreter(&r, &interpreter);
483 if (fileType == eFileTypeUNKNOWN) {
484 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
485 "FastCGI: %s is not executable; ensure interpreted scripts have "
486 "\"#!\" as their first line",
487 fs->fs_path);
488 ap_destroy_pool(tp);
489 return (pid);
493 * We have the interpreter (if there is one) and we have
494 * the arguments (if there are any).
495 * Build the command string to pass to CreateProcess.
497 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
498 if (interpreter && *interpreter) {
499 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
501 else {
502 pCommand = quoted_filename;
506 * Make child process use hPipeOutputWrite as standard out,
507 * and make sure it does not show on screen.
509 si.cb = sizeof(si);
510 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
511 si.wShowWindow = SW_HIDE;
512 si.hStdInput = listen_handle;
514 // XXX These should be open to the error_log
515 si.hStdOutput = INVALID_HANDLE_VALUE;
516 si.hStdError = INVALID_HANDLE_VALUE;
519 * Win32's CreateProcess call requires that the environment
520 * be passed in an environment block, a null terminated block of
521 * null terminated strings.
522 * @todo we should store the env in this format for win32.
524 while (fs->envp[i])
526 iEnvBlockLen += strlen(fs->envp[i]) + 1;
527 i++;
530 iEnvBlockLen += strlen(termination_env_string) + 1;
531 iEnvBlockLen += strlen(fs->mutex_env_string) + 1;
533 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
535 i = 0;
536 pNext = pEnvBlock;
537 while (fs->envp[i])
539 strcpy(pNext, fs->envp[i]);
540 pNext += strlen(pNext) + 1;
541 i++;
544 strcpy(pNext, termination_env_string);
545 pNext += strlen(pNext) + 1;
546 strcpy(pNext, fs->mutex_env_string);
548 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
550 pEnvBlock,
551 ap_make_dirstr_parent(tp, fs->fs_path),
552 &si, &pi))
554 /* Hack to get 16-bit CGI's working. It works for all the
555 * standard modules shipped with Apache. pi.dwProcessId is 0
556 * for 16-bit CGIs and all the Unix specific code that calls
557 * ap_call_exec interprets this as a failure case. And we can't
558 * use -1 either because it is mapped to 0 by the caller.
560 pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId;
562 process->handle = pi.hProcess;
563 CloseHandle(pi.hThread);
566 if (fs->socket_path)
568 CloseHandle(listen_handle);
571 ap_destroy_pool(tp);
573 return pid;
575 #endif
578 #ifndef WIN32
579 static void reduce_privileges(void)
581 char *name;
583 if (geteuid() != 0)
584 return;
586 #ifndef __EMX__
587 /* Get username if passed as a uid */
588 if (ap_user_name[0] == '#') {
589 uid_t uid = atoi(&ap_user_name[1]);
590 struct passwd *ent = getpwuid(uid);
592 if (ent == NULL) {
593 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
594 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
595 "you probably need to modify the User directive", (unsigned)uid);
596 exit(1);
598 name = ent->pw_name;
600 else
601 name = ap_user_name;
603 /* Change Group */
604 if (setgid(ap_group_id) == -1) {
605 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
606 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
607 exit(1);
610 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
612 /* Initialize supplementary groups */
613 if (initgroups(name, ap_group_id) == -1) {
614 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
615 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
616 name, (unsigned)ap_group_id);
617 exit(1);
619 #endif /* __EMX__ */
621 /* Change User */
622 if (fcgi_wrapper) {
623 if (seteuid_user() == -1) {
624 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
625 "FastCGI: process manager exiting, failed to reduce privileges");
626 exit(1);
629 else {
630 if (setuid(ap_user_id) == -1) {
631 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
632 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
633 exit(1);
638 /*************
639 * Change the name of this process - best we can easily.
641 static void change_process_name(const char * const name)
643 strncpy(ap_server_argv0, name, strlen(ap_server_argv0));
645 #endif
647 static void schedule_start(fcgi_server *s, int proc)
649 /* If we've started one recently, don't register another */
650 time_t time_passed = now - s->restartTime;
652 if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay))
653 || ((s->procs[proc].pid == 0) && (time_passed < s->initStartDelay)))
655 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);
656 return;
659 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
660 s->procs[proc].state = FCGI_START_STATE;
661 if (proc == dynamicMaxClassProcs - 1) {
662 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
663 "FastCGI: scheduled the %sstart of the last (dynamic) server "
664 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
665 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
670 *----------------------------------------------------------------------
672 * dynamic_read_msgs
674 * Removes the records written by request handlers and decodes them.
675 * We also update the data structures to reflect the changes.
677 *----------------------------------------------------------------------
680 static void dynamic_read_msgs(int read_ready)
682 fcgi_server *s;
683 int rc;
685 #ifndef WIN32
686 static int buflen = 0;
687 static char buf[FCGI_MSGS_BUFSIZE + 1];
688 char *ptr1, *ptr2, opcode;
689 char execName[FCGI_MAXPATH + 1];
690 char user[MAX_USER_NAME_LEN + 2];
691 char group[MAX_GID_CHAR_LEN + 1];
692 unsigned long q_usec = 0UL, req_usec = 0UL;
693 #else
694 fcgi_pm_job *joblist = NULL;
695 fcgi_pm_job *cjob = NULL;
696 #endif
698 pool *sp = NULL, *tp;
700 #ifndef WIN32
701 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
702 #endif
705 * To prevent the idle application from running indefinitely, we
706 * check the timer and if it is expired, we recompute the values
707 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
708 * message is received, only updates are made to the data structures.
710 if (fcgi_dynamic_last_analyzed == 0) {
711 fcgi_dynamic_last_analyzed = now;
713 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
714 for (s = fcgi_servers; s != NULL; s = s->next) {
715 if (s->directive != APP_CLASS_DYNAMIC)
716 break;
718 /* Advance the last analyzed timestamp by the elapsed time since
719 * it was last set. Round the increase down to the nearest
720 * multiple of dynamicUpdateInterval */
722 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
723 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
724 s->totalConnTime = 0UL;
725 s->totalQueueTime = 0UL;
729 if (read_ready <= 0) {
730 return;
733 #ifndef WIN32
734 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
735 if (rc <= 0) {
736 if (!caughtSigTerm) {
737 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
738 "FastCGI: read() from pipe failed (%d)", rc);
739 if (rc == 0) {
740 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
741 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
742 caughtSigTerm = TRUE;
745 return;
747 buflen += rc;
748 buf[buflen] = '\0';
750 #else
752 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
753 * request to do something) and/or when a timeout expires.
754 * There really should be no reason why this wait would get stuck
755 * but there's no point in waiting forever. */
757 rc = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
759 if (rc != WAIT_OBJECT_0 && rc != WAIT_ABANDONED)
761 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
762 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
763 return;
766 joblist = fcgi_dynamic_mbox;
767 fcgi_dynamic_mbox = NULL;
769 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
771 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
772 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
775 cjob = joblist;
776 #endif
778 tp = ap_make_sub_pool(fcgi_config_pool);
780 #ifndef WIN32
781 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
782 int scan_failed = 0;
784 ptr2 = strchr(ptr1, '*');
785 if (ptr2) {
786 *ptr2++ = '\0';
788 else {
789 break;
792 opcode = *ptr1;
794 switch (opcode)
796 case FCGI_SERVER_START_JOB:
797 case FCGI_SERVER_RESTART_JOB:
799 if (sscanf(ptr1, "%c %s %16s %15s",
800 &opcode, execName, user, group) != 4)
802 scan_failed = 1;
804 break;
806 case FCGI_REQUEST_TIMEOUT_JOB:
808 if (sscanf(ptr1, "%c %s %16s %15s",
809 &opcode, execName, user, group) != 4)
811 scan_failed = 1;
813 break;
815 case FCGI_REQUEST_COMPLETE_JOB:
817 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
818 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
820 scan_failed = 1;
822 break;
824 default:
826 scan_failed = 1;
827 break;
830 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode, execName, user, group, q_usec, req_usec);
832 if (scan_failed) {
833 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
834 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
835 goto NextJob;
837 #else
838 /* Update data structures for processing */
839 while (cjob != NULL) {
840 joblist = cjob->next;
841 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
842 #endif
844 #ifndef WIN32
845 s = fcgi_util_fs_get(execName, user, group);
846 #else
847 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
848 #endif
850 #ifndef WIN32
851 if (s==NULL && opcode != FCGI_REQUEST_COMPLETE_JOB)
852 #else
853 if (s==NULL && cjob->id != FCGI_REQUEST_COMPLETE_JOB)
854 #endif
856 #ifdef WIN32
858 HANDLE mutex = CreateMutex(NULL, FALSE, cjob->fs_path);
860 if (mutex == NULL)
862 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
863 "FastCGI: can't create accept mutex "
864 "for (dynamic) server \"%s\"", cjob->fs_path);
865 goto BagNewServer;
868 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
869 #else
870 const char *err;
871 #endif
873 /* Create a perm subpool to hold the new server data,
874 * we can destroy it if something doesn't pan out */
875 sp = ap_make_sub_pool(fcgi_config_pool);
877 /* Create a new "dynamic" server */
878 s = fcgi_util_fs_new(sp);
880 s->directive = APP_CLASS_DYNAMIC;
881 s->restartDelay = dynamicRestartDelay;
882 s->listenQueueDepth = dynamicListenQueueDepth;
883 s->initStartDelay = dynamicInitStartDelay;
884 s->envp = dynamicEnvp;
885 s->flush = dynamicFlush;
887 #ifdef WIN32
888 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
889 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
890 #else
891 s->fs_path = ap_pstrdup(sp, execName);
892 #endif
893 ap_getparents(s->fs_path);
894 ap_no2slash(s->fs_path);
895 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
897 /* XXX the socket_path (both Unix and Win) *is* deducible and
898 * thus can and will be used by other apache instances without
899 * the use of shared data regarding the processes serving the
900 * requests. This can result in slightly unintuitive process
901 * counts and security implications. This is prevented
902 * if suexec (Unix) is in use. This is both a feature and a flaw.
903 * Changing it now would break existing installations. */
905 #ifndef WIN32
906 /* Create socket file's path */
907 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
908 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
910 /* Create sockaddr, prealloc it so it won't get created in tp */
911 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
912 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
913 &s->socket_addr_len, s->socket_path);
914 if (err) {
915 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
916 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
917 goto BagNewServer;
920 if (init_listen_sock(s)) {
921 goto BagNewServer;
924 /* If a wrapper is being used, config user/group info */
925 if (fcgi_wrapper) {
926 if (user[0] == '~') {
927 /* its a user dir uri, the rest is a username, not a uid */
928 struct passwd *pw = getpwnam(&user[1]);
930 if (!pw) {
931 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
932 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
933 execName, &user[1]);
934 goto BagNewServer;
936 s->uid = pw->pw_uid;
937 s->user = ap_pstrdup(sp, user);
938 s->username = s->user;
940 s->gid = pw->pw_gid;
941 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
943 else {
944 struct passwd *pw;
946 s->uid = (uid_t)atol(user);
947 pw = getpwuid(s->uid);
948 if (!pw) {
949 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
950 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
951 execName, (long)s->uid);
952 goto BagNewServer;
954 s->user = ap_pstrdup(sp, user);
955 s->username = ap_pstrdup(sp, pw->pw_name);
957 s->gid = (gid_t)atol(group);
958 s->group = ap_pstrdup(sp, group);
961 #else
962 /* Create socket file's path */
963 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
964 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
965 s->listenFd = 0;
966 #endif
968 fcgi_util_fs_add(s);
970 else {
971 #ifndef WIN32
972 if (opcode == FCGI_SERVER_RESTART_JOB) {
973 #else
974 if (cjob->id==FCGI_SERVER_RESTART_JOB) {
975 #endif
976 /* Check to see if the binary has changed. If so,
977 * kill the FCGI application processes, and
978 * restart them.
980 struct stat stbuf;
981 int i;
983 #ifndef WIN32
984 if ((stat(execName, &stbuf)==0) &&
985 #else
986 if ((stat(cjob->fs_path, &stbuf)==0) &&
987 #endif
988 (stbuf.st_mtime > s->restartTime)) {
989 /* kill old server(s) */
990 for (i = 0; i < dynamicMaxClassProcs; i++) {
991 if (s->procs[i].pid > 0) {
992 fcgi_kill(&s->procs[i], SIGTERM);
996 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
997 "FastCGI: restarting server \"%s\" processes, newer version found",
998 #ifndef WIN32
999 execName);
1000 #else
1001 cjob->fs_path);
1002 #endif
1005 /* If dynamicAutoRestart, don't mark any new processes
1006 * for starting because we probably got the
1007 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1008 * will be restarting all of those we just killed.
1010 if (dynamicAutoRestart)
1011 goto NextJob;
1013 #ifndef WIN32
1014 else if (opcode == FCGI_SERVER_START_JOB) {
1015 #else
1016 else if (cjob->id==FCGI_SERVER_START_JOB) {
1017 #endif
1018 /* we've been asked to start a process--only start
1019 * it if we're not already running at least one
1020 * instance.
1022 int i;
1024 for (i = 0; i < dynamicMaxClassProcs; i++) {
1025 if (s->procs[i].state == FCGI_RUNNING_STATE)
1026 break;
1028 /* if already running, don't start another one */
1029 if (i < dynamicMaxClassProcs) {
1030 goto NextJob;
1035 #ifndef WIN32
1036 switch (opcode)
1037 #else
1038 switch (cjob->id)
1039 #endif
1041 int i, start;
1043 case FCGI_SERVER_RESTART_JOB:
1045 start = FALSE;
1047 /* We just waxed 'em all. Try to find an idle slot. */
1049 for (i = 0; i < dynamicMaxClassProcs; ++i)
1051 if (s->procs[i].state == FCGI_START_STATE
1052 || s->procs[i].state == FCGI_RUNNING_STATE)
1054 break;
1056 else if (s->procs[i].state == FCGI_KILLED_STATE
1057 || s->procs[i].state == FCGI_READY_STATE)
1059 start = TRUE;
1060 break;
1064 /* Nope, just use the first slot */
1065 if (i == dynamicMaxClassProcs)
1067 start = TRUE;
1068 i = 0;
1071 if (start)
1073 schedule_start(s, i);
1076 break;
1078 case FCGI_SERVER_START_JOB:
1079 case FCGI_REQUEST_TIMEOUT_JOB:
1081 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1083 * Extra instances should have been
1084 * terminated beforehand, probably need
1085 * to increase ProcessSlack parameter
1087 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1088 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1089 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1090 goto NextJob;
1093 /* find next free slot */
1094 for (i = 0; i < dynamicMaxClassProcs; i++)
1096 if (s->procs[i].state == FCGI_START_STATE)
1098 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1099 break;
1101 else if (s->procs[i].state == FCGI_RUNNING_STATE)
1103 continue;
1106 schedule_start(s, i);
1107 break;
1110 #ifdef FCGI_DEBUG
1111 if (i >= dynamicMaxClassProcs) {
1112 FCGIDBG1("ignore_job: slots are max'd");
1114 #endif
1115 break;
1116 case FCGI_REQUEST_COMPLETE_JOB:
1117 /* only record stats if we have a structure */
1118 if (s) {
1119 #ifndef WIN32
1120 s->totalConnTime += req_usec;
1121 s->totalQueueTime += q_usec;
1122 #else
1123 s->totalConnTime += cjob->start_time;
1124 s->totalQueueTime += cjob->qsec;
1125 #endif
1127 break;
1130 NextJob:
1132 #ifdef WIN32
1133 /* Cleanup job data */
1134 free(cjob->fs_path);
1135 free(cjob->user);
1136 free(cjob->group);
1137 free(cjob);
1138 cjob = joblist;
1139 #endif
1141 continue;
1143 BagNewServer:
1144 if (sp) ap_destroy_pool(sp);
1146 #ifdef WIN32
1147 free(cjob->fs_path);
1148 free(cjob);
1149 cjob = joblist;
1150 #endif
1153 #ifndef WIN32
1154 if (ptr1 == buf) {
1155 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1156 "FastCGI: really bogus message: \"%s\"", ptr1);
1157 ptr1 += strlen(buf);
1160 buflen -= ptr1 - buf;
1161 if (buflen) {
1162 memmove(buf, ptr1, buflen);
1164 #endif
1166 ap_destroy_pool(tp);
1170 *----------------------------------------------------------------------
1172 * dynamic_kill_idle_fs_procs
1174 * Implement a kill policy for the dynamic FastCGI applications.
1175 * We also update the data structures to reflect the changes.
1177 * Side effects:
1178 * Processes are marked for deletion possibly killed.
1180 *----------------------------------------------------------------------
1182 static void dynamic_kill_idle_fs_procs(void)
1184 fcgi_server *s;
1185 int victims = 0;
1187 for (s = fcgi_servers; s != NULL; s = s->next)
1190 * server's smoothed running time, or if that's 0, the current total
1192 unsigned long connTime;
1195 * maximum number of microseconds that all of a server's running
1196 * processes together could have spent running since the last check
1198 unsigned long totalTime;
1201 * percentage, 0-100, of totalTime that the processes actually used
1203 int loadFactor;
1205 int i;
1206 int really_running = 0;
1208 if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1210 continue;
1213 /* s->numProcesses includes pending kills so get the "active" count */
1214 for (i = 0; i < dynamicMaxClassProcs; ++i)
1216 if (s->procs[i].state == FCGI_RUNNING_STATE) ++really_running;
1219 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1220 totalTime = really_running * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1222 loadFactor = 100 * connTime / totalTime;
1224 if (really_running == 1)
1226 if (loadFactor >= dynamicThreshold1)
1228 continue;
1231 else
1233 int load = really_running / ( really_running - 1) * loadFactor;
1235 if (load >= dynamicThresholdN)
1237 continue;
1242 * Run through the procs to see if we can get away w/o waxing one.
1244 for (i = 0; i < dynamicMaxClassProcs; ++i)
1246 if (s->procs[i].state == FCGI_START_STATE)
1248 s->procs[i].state = FCGI_READY_STATE;
1249 break;
1251 else if (s->procs[i].state == FCGI_VICTIM_STATE)
1253 break;
1257 if (i >= dynamicMaxClassProcs)
1259 ServerProcess * procs = s->procs;
1260 int youngest = -1;
1262 for (i = 0; i < dynamicMaxClassProcs; ++i)
1264 if (procs[i].state == FCGI_RUNNING_STATE)
1266 if (youngest == -1 || procs[i].start_time >= procs[youngest].start_time)
1268 youngest = i;
1273 if (youngest != -1)
1275 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1276 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1277 s->fs_path, (long) s->procs[youngest].pid);
1279 fcgi_kill(&s->procs[youngest], SIGTERM);
1281 victims++;
1285 * If the number of non-victims is less than or equal to
1286 * the minimum that may be running without being killed off,
1287 * don't select any more victims.
1289 if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1291 break;
1297 #ifdef WIN32
1299 // This is a little bogus, there's gotta be a better way to do this
1300 // Can we use WaitForMultipleObjects()
1301 #define FCGI_PROC_WAIT_TIME 100
1303 void child_wait_thread_main(void *dummy) {
1304 fcgi_server *s;
1305 DWORD dwRet = WAIT_TIMEOUT;
1306 int numChildren;
1307 int i;
1308 int waited;
1310 while (!bTimeToDie) {
1311 waited = 0;
1313 for (s = fcgi_servers; s != NULL; s = s->next) {
1314 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1315 continue;
1317 if (s->directive == APP_CLASS_DYNAMIC) {
1318 numChildren = dynamicMaxClassProcs;
1320 else {
1321 numChildren = s->numProcesses;
1324 for (i=0; i < numChildren; i++) {
1325 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1327 DWORD exitStatus = 0;
1329 /* timeout is currently set for 100 miliecond */
1330 /* it may need to be longer or user customizable */
1331 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1333 waited = 1;
1335 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1336 /* a child fs has died */
1337 /* mark the child as dead */
1339 if (s->directive == APP_CLASS_STANDARD) {
1340 /* restart static app */
1341 s->procs[i].state = FCGI_START_STATE;
1342 s->numFailures++;
1344 else {
1345 s->numProcesses--;
1346 fcgi_dynamic_total_proc_count--;
1347 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1349 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1350 s->procs[i].state = FCGI_KILLED_STATE;
1352 else {
1353 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1354 s->numFailures++;
1356 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1357 s->procs[i].state = FCGI_START_STATE;
1359 else {
1360 s->procs[i].state = FCGI_READY_STATE;
1365 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1367 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1368 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1369 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1370 s->fs_path, (long) s->procs[i].pid, exitStatus);
1372 CloseHandle(s->procs[i].handle);
1373 s->procs[i].handle = INVALID_HANDLE_VALUE;
1374 s->procs[i].pid = -1;
1376 /* wake up the main thread */
1377 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1382 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1385 #endif
1387 #ifndef WIN32
1388 static void setup_signals(void)
1390 struct sigaction sa;
1392 /* Setup handlers */
1394 sa.sa_handler = signal_handler;
1395 sigemptyset(&sa.sa_mask);
1396 sa.sa_flags = 0;
1398 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1399 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1400 "sigaction(SIGTERM) failed");
1402 /* httpd restart */
1403 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1404 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1405 "sigaction(SIGHUP) failed");
1407 /* httpd graceful restart */
1408 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1409 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1410 "sigaction(SIGUSR1) failed");
1412 /* read messages from request handlers - kill interval expired */
1413 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1414 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1415 "sigaction(SIGALRM) failed");
1417 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1418 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1419 "sigaction(SIGCHLD) failed");
1422 #endif
1424 #ifndef WIN32
1425 int fcgi_pm_main(void *dummy, child_info *info)
1426 #else
1427 void fcgi_pm_main(void *dummy)
1428 #endif
1430 fcgi_server *s;
1431 unsigned int i;
1432 int read_ready = 0;
1433 int alarmLeft = 0;
1435 #ifdef WIN32
1436 DWORD dwRet;
1437 HANDLE child_wait_thread = INVALID_HANDLE_VALUE;
1438 #else
1439 int callWaitPid, callDynamicProcs;
1440 #endif
1442 #ifdef WIN32
1443 // Add SystemRoot to the dynamic environment
1444 char ** envp = dynamicEnvp;
1445 for (i = 0; *envp; ++i) {
1446 ++envp;
1448 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1450 #else
1451 reduce_privileges();
1453 close(fcgi_pm_pipe[1]);
1454 change_process_name("fcgi-pm");
1455 setup_signals();
1457 if (fcgi_wrapper) {
1458 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1459 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1461 #endif
1463 /* Initialize AppClass */
1464 for (s = fcgi_servers; s != NULL; s = s->next)
1466 if (s->directive != APP_CLASS_STANDARD)
1467 continue;
1469 #ifdef WIN32
1470 if (s->socket_path)
1471 s->listenFd = 0;
1472 #endif
1474 for (i = 0; i < s->numProcesses; ++i)
1475 s->procs[i].state = FCGI_START_STATE;
1478 #ifdef WIN32
1479 child_wait_thread = (HANDLE) _beginthread(child_wait_thread_main, 0, NULL);
1481 if (child_wait_thread == (HANDLE) -1)
1483 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1484 "FastCGI: failed to create process manager's wait thread!");
1487 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1488 "FastCGI: process manager initialized");
1489 #else
1490 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1491 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1492 #endif
1494 now = time(NULL);
1497 * Loop until SIGTERM
1499 for (;;) {
1500 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1501 #ifdef WIN32
1502 time_t expire;
1503 #else
1504 pid_t childPid;
1505 int waitStatus;
1506 #endif
1507 unsigned int numChildren;
1510 * If we came out of sigsuspend() for any reason other than
1511 * SIGALRM, pick up where we left off.
1513 if (alarmLeft)
1514 sleepSeconds = alarmLeft;
1517 * Examine each configured AppClass for a process that needs
1518 * starting. Compute the earliest time when the start should
1519 * be attempted, starting it now if the time has passed. Also,
1520 * remember that we do NOT need to restart externally managed
1521 * FastCGI applications.
1523 for (s = fcgi_servers; s != NULL; s = s->next)
1525 if (s->directive == APP_CLASS_EXTERNAL)
1526 continue;
1528 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1529 ? dynamicMaxClassProcs
1530 : s->numProcesses;
1532 for (i = 0; i < numChildren; ++i)
1534 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1536 int restart = (s->procs[i].pid < 0);
1537 time_t restartTime = s->restartTime;
1539 if (s->bad)
1541 /* we've gone to using the badDelay, the only thing that
1542 resets bad is when badDelay has expired. but numFailures
1543 is only just set below its threshold. the proc's
1544 start_times are all reset when the bad is. the numFailures
1545 is reset when we see an app run for a period */
1547 s->procs[i].start_time = 0;
1550 if (s->numFailures > MAX_FAILED_STARTS)
1552 time_t last_start_time = s->procs[i].start_time;
1554 if (last_start_time && now - last_start_time > RUNTIME_SUCCESS_INTERVAL)
1556 s->bad = 0;
1557 s->numFailures = 0;
1559 else
1561 unsigned int j;
1563 for (j = 0; j < numChildren; ++j)
1565 if (s->procs[j].pid <= 0) continue;
1566 if (s->procs[j].state != FCGI_RUNNING_STATE) continue;
1567 if (s->procs[j].start_time == 0) continue;
1568 if (now - s->procs[j].start_time > RUNTIME_SUCCESS_INTERVAL) break;
1571 if (j >= numChildren)
1573 s->bad = 1;
1575 else
1577 s->bad = 0;
1578 s->numFailures = 0;
1583 if (s->bad)
1585 restartTime += FAILED_STARTS_DELAY;
1587 else
1589 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1592 if (restartTime <= now)
1594 if (s->bad)
1596 s->bad = 0;
1597 s->numFailures = MAX_FAILED_STARTS;
1600 if (s->listenFd < 0 && init_listen_sock(s))
1602 if (sleepSeconds > s->initStartDelay)
1603 sleepSeconds = s->initStartDelay;
1604 break;
1606 #ifndef WIN32
1607 if (caughtSigTerm) {
1608 goto ProcessSigTerm;
1610 #endif
1611 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1612 if (s->procs[i].pid <= 0) {
1613 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1614 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1615 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1616 s->fs_path);
1618 sleepSeconds = min(sleepSeconds,
1619 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1621 ap_assert(s->procs[i].pid < 0);
1622 break;
1625 s->procs[i].start_time = now;
1626 s->restartTime = now;
1628 if (s->directive == APP_CLASS_DYNAMIC) {
1629 s->numProcesses++;
1630 fcgi_dynamic_total_proc_count++;
1631 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1634 s->procs[i].state = FCGI_RUNNING_STATE;
1636 if (fcgi_wrapper) {
1637 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1638 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1639 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1640 s->fs_path, (long) s->uid, (long) s->gid,
1641 restart ? "re" : "", (long) s->procs[i].pid);
1643 else {
1644 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1645 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1646 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1647 s->fs_path, restart ? "re" : "", (long) s->procs[i].pid);
1649 ap_assert(s->procs[i].pid > 0);
1650 } else {
1651 sleepSeconds = min(sleepSeconds, restartTime - now);
1657 #ifndef WIN32
1659 if(caughtSigTerm) {
1660 goto ProcessSigTerm;
1662 if((!caughtSigChld) && (!caughtSigAlarm)) {
1663 fd_set rfds;
1665 alarm(sleepSeconds);
1667 FD_ZERO(&rfds);
1668 FD_SET(fcgi_pm_pipe[0], &rfds);
1669 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1671 alarmLeft = alarm(0);
1673 callWaitPid = caughtSigChld;
1674 caughtSigChld = FALSE;
1675 callDynamicProcs = caughtSigAlarm;
1676 caughtSigAlarm = FALSE;
1678 now = time(NULL);
1681 * Dynamic fcgi process management
1683 if((callDynamicProcs) || (!callWaitPid)) {
1684 dynamic_read_msgs(read_ready);
1685 if(fcgi_dynamic_epoch == 0) {
1686 fcgi_dynamic_epoch = now;
1688 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1689 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1690 dynamic_kill_idle_fs_procs();
1691 fcgi_dynamic_epoch = now;
1695 if(!callWaitPid) {
1696 continue;
1699 /* We've caught SIGCHLD, so find out who it was using waitpid,
1700 * write a log message and update its data structure. */
1702 for (;;) {
1703 if (caughtSigTerm)
1704 goto ProcessSigTerm;
1706 childPid = waitpid(-1, &waitStatus, WNOHANG);
1708 if (childPid == -1 || childPid == 0)
1709 break;
1711 for (s = fcgi_servers; s != NULL; s = s->next) {
1712 if (s->directive == APP_CLASS_EXTERNAL)
1713 continue;
1715 if (s->directive == APP_CLASS_DYNAMIC)
1716 numChildren = dynamicMaxClassProcs;
1717 else
1718 numChildren = s->numProcesses;
1720 for (i = 0; i < numChildren; i++) {
1721 if (s->procs[i].pid == childPid)
1722 goto ChildFound;
1726 /* TODO: print something about this unknown child */
1727 continue;
1729 ChildFound:
1730 s->procs[i].pid = -1;
1732 if (s->directive == APP_CLASS_STANDARD) {
1733 /* Always restart static apps */
1734 s->procs[i].state = FCGI_START_STATE;
1735 s->numFailures++;
1737 else {
1738 s->numProcesses--;
1739 fcgi_dynamic_total_proc_count--;
1741 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1742 s->procs[i].state = FCGI_KILLED_STATE;
1744 else {
1745 /* A dynamic app died or exited without provocation from the PM */
1746 s->numFailures++;
1748 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1749 s->procs[i].state = FCGI_START_STATE;
1750 else
1751 s->procs[i].state = FCGI_READY_STATE;
1755 if (WIFEXITED(waitStatus)) {
1756 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1757 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1758 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1759 s->fs_path, (long) childPid, WEXITSTATUS(waitStatus));
1761 else if (WIFSIGNALED(waitStatus)) {
1762 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1763 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1764 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1765 s->fs_path, (long) childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)],
1766 #ifdef WCOREDUMP
1767 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1768 #else
1769 "");
1770 #endif
1772 else if (WIFSTOPPED(waitStatus)) {
1773 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1774 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1775 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1776 s->fs_path, (long) childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)]);
1778 } /* for (;;), waitpid() */
1780 #else /* WIN32 */
1782 /* wait for an event to occur or timer expires */
1783 expire = time(NULL) + sleepSeconds;
1784 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1786 if (dwRet == WAIT_FAILED) {
1787 /* There is something seriously wrong here */
1788 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1789 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1790 bTimeToDie = TRUE;
1793 if (dwRet != WAIT_TIMEOUT) {
1794 now = time(NULL);
1796 if (now < expire)
1797 alarmLeft = expire - now;
1801 * Dynamic fcgi process management
1803 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1804 if (dwRet == MBOX_EVENT) {
1805 read_ready = 1;
1808 now = time(NULL);
1810 dynamic_read_msgs(read_ready);
1812 if(fcgi_dynamic_epoch == 0) {
1813 fcgi_dynamic_epoch = now;
1816 if ((now-fcgi_dynamic_epoch >= (int) dynamicKillInterval) ||
1817 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1818 dynamic_kill_idle_fs_procs();
1819 fcgi_dynamic_epoch = now;
1821 read_ready = 0;
1823 else if (dwRet == WAKE_EVENT) {
1824 continue;
1826 else if (dwRet == TERM_EVENT) {
1827 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1828 "FastCGI: Termination event received process manager shutting down");
1830 bTimeToDie = TRUE;
1831 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
1833 goto ProcessSigTerm;
1835 else {
1836 // Have an received an unknown event - should not happen
1837 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1838 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1840 bTimeToDie = TRUE;
1841 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
1843 goto ProcessSigTerm;
1846 #endif /* WIN32 */
1848 } /* for (;;), the whole shoot'n match */
1850 ProcessSigTerm:
1852 * Kill off the children, then exit.
1854 shutdown_all();
1856 #ifdef WIN32
1857 return;
1858 #else
1859 exit(0);
1860 #endif
1863 #ifdef WIN32
1864 int fcgi_pm_add_job(fcgi_pm_job *new_job)
1866 int rv = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
1868 if (rv != WAIT_OBJECT_0 && rv != WAIT_ABANDONED)
1870 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1871 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
1872 return -1;
1875 new_job->next = fcgi_dynamic_mbox;
1876 fcgi_dynamic_mbox = new_job;
1878 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
1880 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1881 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
1884 return 0;
1886 #endif