Remove page breaks.
[mod_fastcgi.git] / fcgi_pm.c
blob757746e8e15e68ada5c556db485f1b6bac45d4c0
1 /*
2 * $Id: fcgi_pm.c,v 1.52 2001/03/26 15:35:39 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)", process->pid, sig);
67 process->state = FCGI_VICTIM;
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 /* Remove the socket file */
115 if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL) {
116 #ifndef WIN32
117 if (unlink(s->socket_path) != 0) {
118 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
119 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
120 s->socket_path,
121 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path);
123 #else
124 CloseHandle((HANDLE)s->listenFd);
125 #endif
128 /* Send TERM to all processes */
129 for (i = 0; i < numChildren; i++, proc++)
131 if (proc->state == FCGI_RUNNING)
133 fcgi_kill(proc, SIGTERM);
137 fcgi_servers = s->next;
141 static int init_listen_sock(fcgi_server * fs)
143 ap_assert(fs->directive != APP_CLASS_EXTERNAL);
145 /* Create the socket */
146 if ((fs->listenFd = ap_psocket(fcgi_config_pool, fs->socket_addr->sa_family, SOCK_STREAM, 0)) < 0)
148 #ifdef WIN32
149 errno = WSAGetLastError(); // Not sure if this will work as expected
150 #endif
151 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
152 "FastCGI: can't create %sserver \"%s\": socket() failed",
153 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
154 fs->fs_path);
155 return -1;
158 #ifndef WIN32
159 if (fs->socket_addr->sa_family == AF_UNIX)
161 /* Remove any existing socket file.. just in case */
162 unlink(((struct sockaddr_un *)fs->socket_addr)->sun_path);
164 else
165 #endif
167 int flag = 1;
168 setsockopt(fs->listenFd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
171 /* Bind it to the socket_addr */
172 if (bind(fs->listenFd, fs->socket_addr, fs->socket_addr_len))
174 char port[11];
176 #ifdef WIN32
177 errno = WSAGetLastError();
178 #endif
179 ap_snprintf(port, sizeof(port), "port=%d",
180 ((struct sockaddr_in *)fs->socket_addr)->sin_port);
182 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
183 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
184 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
185 fs->fs_path,
186 #ifndef WIN32
187 (fs->socket_addr->sa_family == AF_UNIX) ?
188 ((struct sockaddr_un *)fs->socket_addr)->sun_path :
189 #endif
190 port);
193 #ifndef WIN32
194 /* Twiddle Unix socket permissions */
195 else if (fs->socket_addr->sa_family == AF_UNIX
196 && chmod(((struct sockaddr_un *)fs->socket_addr)->sun_path, S_IRUSR | S_IWUSR))
198 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
199 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
200 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
201 fs->fs_path);
203 #endif
205 /* Set to listen */
206 else if (listen(fs->listenFd, fs->listenQueueDepth))
208 #ifdef WIN32
209 errno = WSAGetLastError();
210 #endif
211 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
212 "FastCGI: can't create %sserver \"%s\": listen() failed",
213 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
214 fs->fs_path);
216 else
218 return 0;
221 ap_pclosesocket(fcgi_config_pool, fs->listenFd);
222 fs->listenFd = -1;
223 return -2;
227 *----------------------------------------------------------------------
229 * pm_main
231 * The FastCGI process manager, which runs as a separate
232 * process responsible for:
233 * - Starting all the FastCGI proceses.
234 * - Restarting any of these processes that die (indicated
235 * by SIGCHLD).
236 * - Catching SIGTERM and relaying it to all the FastCGI
237 * processes before exiting.
239 * Inputs:
240 * Uses global variable fcgi_servers.
242 * Results:
243 * Does not return.
245 * Side effects:
246 * Described above.
248 *----------------------------------------------------------------------
250 #ifndef WIN32
251 static int caughtSigTerm = FALSE;
252 static int caughtSigChld = FALSE;
253 static int caughtSigUsr2 = FALSE;
255 static void signal_handler(int signo)
257 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
258 /* SIGUSR1 & SIGHUP are sent by apache to its process group
259 * when apache get 'em. Apache follows up (1.2.x) with attacks
260 * on each of its child processes, but we've got the KillMgr
261 * sitting between us so we never see the KILL. The main loop
262 * in ProcMgr also checks to see if the KillMgr has terminated,
263 * and if it has, we handl it as if we should shutdown too. */
264 caughtSigTerm = TRUE;
265 } else if(signo == SIGCHLD) {
266 caughtSigChld = TRUE;
267 } else if(signo == SIGALRM) {
268 caughtSigUsr2 = TRUE;
271 #endif
274 *----------------------------------------------------------------------
276 * spawn_fs_process --
278 * Fork and exec the specified fcgi process.
280 * Results:
281 * 0 for successful fork, -1 for failed fork.
283 * In case the child fails before or in the exec, the child
284 * obtains the error log by calling getErrLog, logs
285 * the error, and exits with exit status = errno of
286 * the failed system call.
288 * Side effects:
289 * Child process created.
291 *----------------------------------------------------------------------
294 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
296 #ifndef WIN32
297 pid_t child_pid;
298 int i;
299 char *dirName;
300 char *dnEnd, *failedSysCall;
302 child_pid = fork();
303 if (child_pid) {
304 return child_pid;
307 /* We're the child. We're gonna exec() so pools don't matter. */
309 dnEnd = strrchr(fs->fs_path, '/');
310 if (dnEnd == NULL) {
311 dirName = "./";
312 } else {
313 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
314 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
316 if (chdir(dirName) < 0) {
317 failedSysCall = "chdir()";
318 goto FailedSystemCallExit;
321 #ifndef __EMX__
322 /* OS/2 dosen't support nice() */
323 if (fs->processPriority != 0) {
324 if (nice(fs->processPriority) == -1) {
325 failedSysCall = "nice()";
326 goto FailedSystemCallExit;
329 #endif
331 /* Open the listenFd on spec'd fd */
332 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
333 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
335 /* Close all other open fds, except stdout/stderr. Leave these two open so
336 * FastCGI applications don't have to find and fix ALL 3rd party libs that
337 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
338 * main server error_log - @@@ provide a directive control where this goes.
340 ap_error_log2stderr(fcgi_apache_main_server);
341 dup2(STDERR_FILENO, STDOUT_FILENO);
342 for (i = 0; i < FCGI_MAX_FD; i++) {
343 if (i != FCGI_LISTENSOCK_FILENO && i != STDERR_FILENO && i != STDOUT_FILENO) {
344 close(i);
348 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
349 * install its own handler. */
350 signal(SIGPIPE, SIG_IGN);
352 if (fcgi_wrapper && (fcgi_user_id != fs->uid || fcgi_group_id != fs->gid)) {
353 char *shortName = strrchr(fs->fs_path, '/') + 1;
355 /* Relinquish our root real uid powers */
356 seteuid_root();
357 setuid(ap_user_id);
359 do {
360 execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group, shortName, NULL, fs->envp);
361 } while (errno == EINTR);
363 else {
364 do {
365 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
366 } while (errno == EINTR);
369 failedSysCall = "execle()";
371 FailedSystemCallExit:
372 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
373 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
374 exit(-1);
376 /* avoid an irrelevant compiler warning */
377 return(0);
379 #else
381 /* Adapted from Apache's util_script.c ap_call_exec() */
382 char *interpreter = NULL;
383 char *quoted_filename;
384 char *pCommand;
385 char *pEnvBlock, *pNext;
387 int i = 0;
388 int iEnvBlockLen = 1;
390 file_type_e fileType;
392 STARTUPINFO si;
393 PROCESS_INFORMATION pi;
395 request_rec r;
396 pid_t pid = -1;
398 pool * tp = ap_make_sub_pool(fcgi_config_pool);
400 HANDLE listen_handle;
401 char * termination_env_string = NULL;
403 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
404 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
406 termination_env_string = ap_psprintf(tp,
407 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
409 if (fs->socket_path)
411 SECURITY_ATTRIBUTES sa;
413 sa.lpSecurityDescriptor = NULL;
414 sa.bInheritHandle = TRUE;
415 sa.nLength = sizeof(sa);
417 listen_handle = CreateNamedPipe(fs->socket_path,
418 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
419 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
420 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
422 if (listen_handle == INVALID_HANDLE_VALUE)
424 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
425 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
426 exit(0);
429 // SetHandleInformation(listen_handle, HANDLE_FLAG_INHERIT, TRUE);
431 else
433 listen_handle = (HANDLE) fs->listenFd;
436 memset(&si, 0, sizeof(si));
437 memset(&pi, 0, sizeof(pi));
438 memset(&r, 0, sizeof(r));
440 // Can up a fake request to pass to ap_get_win32_interpreter()
441 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
442 r.server = fcgi_apache_main_server;
443 r.filename = (char *) fs->fs_path;
444 r.pool = tp;
446 fileType = ap_get_win32_interpreter(&r, &interpreter);
448 if (fileType == eFileTypeUNKNOWN) {
449 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
450 "FastCGI: %s is not executable; ensure interpreted scripts have "
451 "\"#!\" as their first line",
452 fs->fs_path);
453 ap_destroy_pool(tp);
454 return (pid);
458 * We have the interpreter (if there is one) and we have
459 * the arguments (if there are any).
460 * Build the command string to pass to CreateProcess.
462 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
463 if (interpreter && *interpreter) {
464 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
466 else {
467 pCommand = quoted_filename;
471 * Make child process use hPipeOutputWrite as standard out,
472 * and make sure it does not show on screen.
474 si.cb = sizeof(si);
475 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
476 si.wShowWindow = SW_HIDE;
477 si.hStdInput = listen_handle;
479 // XXX These should be open to the error_log
480 si.hStdOutput = INVALID_HANDLE_VALUE;
481 si.hStdError = INVALID_HANDLE_VALUE;
484 * Win32's CreateProcess call requires that the environment
485 * be passed in an environment block, a null terminated block of
486 * null terminated strings.
487 * @todo we should store the env in this format for win32.
489 while (fs->envp[i])
491 iEnvBlockLen += strlen(fs->envp[i]) + 1;
492 i++;
495 iEnvBlockLen += strlen(termination_env_string) + 1;
496 iEnvBlockLen += strlen(fs->mutex_env_string) + 1;
498 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
500 i = 0;
501 pNext = pEnvBlock;
502 while (fs->envp[i])
504 strcpy(pNext, fs->envp[i]);
505 pNext += strlen(pNext) + 1;
506 i++;
509 strcpy(pNext, termination_env_string);
510 pNext += strlen(pNext) + 1;
511 strcpy(pNext, fs->mutex_env_string);
513 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
515 pEnvBlock,
516 ap_make_dirstr_parent(tp, fs->fs_path),
517 &si, &pi))
519 /* Hack to get 16-bit CGI's working. It works for all the
520 * standard modules shipped with Apache. pi.dwProcessId is 0
521 * for 16-bit CGIs and all the Unix specific code that calls
522 * ap_call_exec interprets this as a failure case. And we can't
523 * use -1 either because it is mapped to 0 by the caller.
525 pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId;
527 process->handle = pi.hProcess;
528 CloseHandle(pi.hThread);
531 if (fs->socket_path)
533 CloseHandle(listen_handle);
536 ap_destroy_pool(tp);
538 return pid;
540 #endif
543 #ifndef WIN32
544 static void reduce_privileges(void)
546 char *name;
548 if (geteuid() != 0)
549 return;
551 #ifndef __EMX__
552 /* Get username if passed as a uid */
553 if (ap_user_name[0] == '#') {
554 uid_t uid = atoi(&ap_user_name[1]);
555 struct passwd *ent = getpwuid(uid);
557 if (ent == NULL) {
558 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
559 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
560 "you probably need to modify the User directive", (unsigned)uid);
561 exit(1);
563 name = ent->pw_name;
565 else
566 name = ap_user_name;
568 /* Change Group */
569 if (setgid(ap_group_id) == -1) {
570 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
571 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
572 exit(1);
575 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
577 /* Initialize supplementary groups */
578 if (initgroups(name, ap_group_id) == -1) {
579 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
580 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
581 name, (unsigned)ap_group_id);
582 exit(1);
584 #endif /* __EMX__ */
586 /* Change User */
587 if (fcgi_wrapper) {
588 if (seteuid_user() == -1) {
589 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
590 "FastCGI: process manager exiting, failed to reduce privileges");
591 exit(1);
594 else {
595 if (setuid(ap_user_id) == -1) {
596 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
597 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
598 exit(1);
603 /*************
604 * Change the name of this process - best we can easily.
606 static void change_process_name(const char * const name)
608 strncpy(ap_server_argv0, name, strlen(ap_server_argv0));
610 #endif
612 static void schedule_start(fcgi_server *s, int proc)
614 /* If we've started one recently, don't register another */
615 time_t time_passed = now - s->restartTime;
617 if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay))
618 || ((s->procs[proc].pid == 0) && (time_passed < (int) s->initStartDelay)))
620 FCGIDBG6("ignore_job: slot=%d, pid=%ld, time_passed=%ld, initStartDelay=%ld, restartDelay=%ld", proc, s->procs[proc].pid, time_passed, s->initStartDelay, s->restartDelay);
621 return;
624 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
625 s->procs[proc].state = FCGI_START;
626 if (proc == (int)dynamicMaxClassProcs - 1) {
627 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
628 "FastCGI: scheduled the %sstart of the last (dynamic) server "
629 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
630 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
635 *----------------------------------------------------------------------
637 * dynamic_read_msgs
639 * Removes the records written by request handlers and decodes them.
640 * We also update the data structures to reflect the changes.
642 *----------------------------------------------------------------------
645 static void dynamic_read_msgs(int read_ready)
647 fcgi_server *s;
649 #ifndef WIN32
650 int rc;
651 static int buflen = 0;
652 static char buf[FCGI_MSGS_BUFSIZE + 1];
653 char *ptr1, *ptr2, opcode;
654 char execName[FCGI_MAXPATH + 1];
655 char user[MAX_USER_NAME_LEN + 2];
656 char group[MAX_GID_CHAR_LEN + 1];
657 unsigned long q_usec = 0UL, req_usec = 0UL;
658 #else
659 fcgi_pm_job *joblist = NULL;
660 fcgi_pm_job *cjob = NULL;
661 #endif
663 pool *sp, *tp;
665 #ifndef WIN32
666 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
667 #endif
670 * To prevent the idle application from running indefinitely, we
671 * check the timer and if it is expired, we recompute the values
672 * for each running application class. Then, when REQ_COMPLETE
673 * message is received, only updates are made to the data structures.
675 if (fcgi_dynamic_last_analyzed == 0) {
676 fcgi_dynamic_last_analyzed = now;
678 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
679 for (s = fcgi_servers; s != NULL; s = s->next) {
680 if (s->directive != APP_CLASS_DYNAMIC)
681 break;
683 /* Advance the last analyzed timestamp by the elapsed time since
684 * it was last set. Round the increase down to the nearest
685 * multiple of dynamicUpdateInterval */
687 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
688 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
689 s->totalConnTime = 0UL;
690 s->totalQueueTime = 0UL;
694 if (read_ready <= 0) {
695 return;
698 #ifndef WIN32
699 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
700 if (rc <= 0) {
701 if (!caughtSigTerm) {
702 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
703 "FastCGI: read() from pipe failed (%d)", rc);
704 if (rc == 0) {
705 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
706 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
707 caughtSigTerm = TRUE;
710 return;
712 buflen += rc;
713 buf[buflen] = '\0';
714 #else
715 if (ap_acquire_mutex(fcgi_dynamic_mbox_mutex) != MULTI_OK) {
716 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
717 "FastCGI: Failed to aquire the dynamic mbox mutex!");
720 joblist = fcgi_dynamic_mbox;
721 fcgi_dynamic_mbox = NULL;
723 if (! ap_release_mutex(fcgi_dynamic_mbox_mutex)) {
724 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
725 "FastCGI: Failed to release the dynamic mbox mutex!");
728 cjob = joblist;
729 #endif
731 tp = ap_make_sub_pool(fcgi_config_pool);
733 #ifndef WIN32
734 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
735 int scan_failed = 0;
737 ptr2 = strchr(ptr1, '*');
738 if (ptr2) {
739 *ptr2++ = '\0';
741 else {
742 break;
745 opcode = *ptr1;
747 switch (opcode) {
748 case PLEASE_START:
749 if (sscanf(ptr1, "%c %s %16s %15s",
750 &opcode, execName, user, group) != 4)
752 scan_failed = 1;
754 break;
755 case CONN_TIMEOUT:
756 if (sscanf(ptr1, "%c %s %16s %15s",
757 &opcode, execName, user, group) != 4)
759 scan_failed = 1;
761 break;
762 case REQ_COMPLETE:
763 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
764 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
766 scan_failed = 1;
768 break;
769 default:
770 scan_failed = 1;
771 break;
774 if (scan_failed) {
775 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
776 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
777 goto NextJob;
779 #else
780 /* Update data structures for processing */
781 while (cjob != NULL) {
782 joblist = cjob->next;
783 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
784 #endif
786 #ifndef WIN32
787 s = fcgi_util_fs_get(execName, user, group);
788 #else
789 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
790 #endif
792 #ifndef WIN32
793 if (s==NULL && opcode != FCGI_COMPLETE)
794 #else
795 if (s==NULL && cjob->id != FCGI_COMPLETE)
796 #endif
798 #ifdef WIN32
799 HANDLE mutex = ap_create_mutex(NULL);
801 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
802 #else
803 int fd;
804 const char *err;
805 #endif
807 /* Create a perm subpool to hold the new server data,
808 * we can destroy it if something doesn't pan out */
809 sp = ap_make_sub_pool(fcgi_config_pool);
811 /* Create a new "dynamic" server */
812 s = fcgi_util_fs_new(sp);
814 s->directive = APP_CLASS_DYNAMIC;
815 s->restartDelay = dynamicRestartDelay;
816 s->listenQueueDepth = dynamicListenQueueDepth;
817 s->initStartDelay = dynamicInitStartDelay;
818 s->envp = dynamicEnvp;
820 #ifdef WIN32
821 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
822 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
823 #else
824 s->fs_path = ap_pstrdup(sp, execName);
825 #endif
826 ap_getparents(s->fs_path);
827 ap_no2slash(s->fs_path);
828 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
830 #ifndef WIN32
831 /* Create socket file's path */
832 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
833 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
835 /* Create sockaddr, prealloc it so it won't get created in tp */
836 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
837 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
838 &s->socket_addr_len, s->socket_path);
839 if (err) {
840 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
841 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
842 goto BagNewServer;
845 if (init_listen_sock(s)) {
846 goto BagNewServer;
849 /* If a wrapper is being used, config user/group info */
850 if (fcgi_wrapper) {
851 if (user[0] == '~') {
852 /* its a user dir uri, the rest is a username, not a uid */
853 struct passwd *pw = getpwnam(&user[1]);
855 if (!pw) {
856 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
857 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
858 execName, &user[1]);
859 goto BagNewServer;
861 s->uid = pw->pw_uid;
862 s->user = ap_pstrdup(sp, user);
863 s->username = s->user;
865 s->gid = pw->pw_gid;
866 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
868 else {
869 struct passwd *pw;
871 s->uid = (uid_t)atol(user);
872 pw = getpwuid(s->uid);
873 if (!pw) {
874 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
875 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
876 execName, (long)s->uid);
877 goto BagNewServer;
879 s->user = ap_pstrdup(sp, user);
880 s->username = ap_pstrdup(sp, pw->pw_name);
882 s->gid = (gid_t)atol(group);
883 s->group = ap_pstrdup(sp, group);
886 #else
887 /* Create socket file's path */
888 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
889 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
890 s->listenFd = 0;
891 #endif
893 fcgi_util_fs_add(s);
895 else {
896 #ifndef WIN32
897 if (opcode == FCGI_START) {
898 #else
899 if (cjob->id==FCGI_START) {
900 #endif
901 if (dynamicAutoUpdate) {
902 /* Check to see if the binary has changed. If so,
903 * kill the FCGI application processes, and
904 * restart them.
906 struct stat stbuf;
907 int i;
909 #ifndef WIN32
910 if ((stat(execName, &stbuf)==0) &&
911 #else
912 if ((stat(cjob->fs_path, &stbuf)==0) &&
913 #endif
914 (stbuf.st_mtime > s->restartTime)) {
915 /* kill old server(s) */
916 for (i = 0; i < dynamicMaxClassProcs; i++) {
917 if (s->procs[i].pid > 0) {
918 fcgi_kill(&s->procs[i], SIGTERM);
922 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
923 "FastCGI: restarting server \"%s\" processes, newer version found",
924 #ifndef WIN32
925 execName);
926 #else
927 cjob->fs_path);
928 #endif
931 /* If dynamicAutoRestart, don't mark any new processes
932 * for starting because we probably got the
933 * PLEASE_START due to dynamicAutoUpdate and the ProcMgr
934 * will be restarting all of those we just killed.
936 if (dynamicAutoRestart)
937 goto NextJob;
938 } else {
939 /* we've been asked to start a process--only start
940 * it if we're not already running at least one
941 * instance.
943 int i;
945 for (i = 0; i < dynamicMaxClassProcs; i++) {
946 if (s->procs[i].state == FCGI_RUNNING)
947 break;
949 /* if already running, don't start another one */
950 if (i < dynamicMaxClassProcs) {
951 goto NextJob;
956 #ifndef WIN32
957 switch (opcode)
958 #else
959 switch (cjob->id)
960 #endif
962 int i;
964 case FCGI_START:
965 case FCGI_TIMEOUT:
967 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
969 * Extra instances should have been
970 * terminated beforehand, probably need
971 * to increase ProcessSlack parameter
973 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
974 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
975 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
976 goto NextJob;
979 /* find next free slot */
980 for (i = 0; i < dynamicMaxClassProcs; i++)
982 if (s->procs[i].state == FCGI_START)
984 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
985 break;
987 else if (s->procs[i].state == FCGI_RUNNING)
989 continue;
992 schedule_start(s, i);
993 break;
996 #ifdef FCGI_DEBUG
997 if (i >= dynamicMaxClassProcs) {
998 FCGIDBG1("ignore_job: slots are max'd");
1000 #endif
1001 break;
1002 case FCGI_COMPLETE:
1003 /* only record stats if we have a structure */
1004 if (s) {
1005 #ifndef WIN32
1006 s->totalConnTime += req_usec;
1007 s->totalQueueTime += q_usec;
1008 #else
1009 s->totalConnTime += cjob->start_time;
1010 s->totalQueueTime += cjob->qsec;
1011 #endif
1013 break;
1016 NextJob:
1018 #ifdef WIN32
1019 /* Cleanup job data */
1020 free(cjob->fs_path);
1021 free(cjob->user);
1022 free(cjob->group);
1023 free(cjob);
1024 cjob = joblist;
1025 #endif
1027 continue;
1029 BagNewServer:
1030 ap_destroy_pool(sp);
1032 #ifdef WIN32
1033 free(cjob->fs_path);
1034 free(cjob);
1035 cjob = joblist;
1036 #endif
1039 #ifndef WIN32
1040 if (ptr1 == buf) {
1041 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1042 "FastCGI: really bogus message: \"%s\"", ptr1);
1043 ptr1 += strlen(buf);
1046 buflen -= ptr1 - buf;
1047 if (buflen) {
1048 memmove(buf, ptr1, buflen);
1050 #endif
1052 ap_destroy_pool(tp);
1056 *----------------------------------------------------------------------
1058 * dynamic_kill_idle_fs_procs
1060 * Implement a kill policy for the dynamic FastCGI applications.
1061 * We also update the data structures to reflect the changes.
1063 * Side effects:
1064 * Processes are marked for deletion possibly killed.
1066 *----------------------------------------------------------------------
1068 static void dynamic_kill_idle_fs_procs(void)
1070 fcgi_server *s;
1071 int victims = 0;
1073 for (s = fcgi_servers; s != NULL; s = s->next)
1076 * server's smoothed running time, or if that's 0, the current total
1078 unsigned long connTime;
1081 * maximum number of microseconds that all of a server's running
1082 * processes together could have spent running since the last check
1084 unsigned long totalTime;
1087 * percentage, 0-100, of totalTime that the processes actually used
1089 int loadFactor;
1091 int i;
1093 if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1095 continue;
1098 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1099 totalTime = s->numProcesses * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1101 loadFactor = 100 * connTime / totalTime;
1103 if (s->numProcesses == 1)
1105 if (loadFactor >= dynamicThreshold1)
1107 continue;
1110 else
1112 int load = s->numProcesses / (s->numProcesses - 1) * loadFactor;
1114 if (load >= dynamicThresholdN)
1116 continue;
1121 * Run through the procs to see if we can get away w/o waxing one.
1123 for (i = 0; i < dynamicMaxClassProcs; ++i)
1125 if (s->procs[i].state == FCGI_START)
1127 s->procs[i].state = FCGI_READY;
1128 break;
1130 else if (s->procs[i].state == FCGI_VICTIM)
1132 break;
1136 if (i < dynamicMaxClassProcs)
1138 continue;
1141 for (i = 0; i < dynamicMaxClassProcs; ++i)
1143 if (s->procs[i].state != FCGI_RUNNING)
1145 continue;
1148 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1149 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1150 s->fs_path, s->procs[i].pid);
1152 fcgi_kill(&s->procs[i], SIGTERM);
1154 victims++;
1155 break;
1159 * If the number of non-victims is less than or equal to
1160 * the minimum that may be running without being killed off,
1161 * don't select any more victims.
1163 if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1165 break;
1170 #ifdef WIN32
1172 // This is a little bogus, there's gotta be a better way to do this
1173 // Can we use WaitForMultipleObjects()
1174 #define FCGI_PROC_WAIT_TIME 100
1176 void child_wait_thread(void *dummy) {
1177 fcgi_server *s;
1178 DWORD dwRet = WAIT_TIMEOUT;
1179 int numChildren;
1180 int i;
1181 int waited;
1183 while (!bTimeToDie) {
1184 waited = 0;
1186 for (s = fcgi_servers; s != NULL; s = s->next) {
1187 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1188 continue;
1190 if (s->directive == APP_CLASS_DYNAMIC) {
1191 numChildren = dynamicMaxClassProcs;
1193 else {
1194 numChildren = s->numProcesses;
1197 for (i=0; i < numChildren; i++) {
1198 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1200 DWORD exitStatus = 0;
1202 /* timeout is currently set for 100 miliecond */
1203 /* it may need t longer or user customizable */
1204 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1206 waited = 1;
1208 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1209 /* a child fs has died */
1210 /* mark the child as dead */
1212 if (s->directive == APP_CLASS_STANDARD) {
1213 /* restart static app */
1214 s->procs[i].state = FCGI_START;
1215 s->numFailures++;
1217 else {
1218 s->numProcesses--;
1219 fcgi_dynamic_total_proc_count--;
1220 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1222 if (s->procs[i].state == FCGI_VICTIM) {
1223 s->procs[i].state = FCGI_KILLED;
1225 else {
1226 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1227 s->numFailures++;
1229 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1230 s->procs[i].state = FCGI_START;
1232 else {
1233 s->procs[i].state = FCGI_READY;
1238 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1240 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1241 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1242 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1243 s->fs_path, s->procs[i].pid, exitStatus);
1245 CloseHandle(s->procs[i].handle);
1246 s->procs[i].handle = INVALID_HANDLE_VALUE;
1247 s->procs[i].pid = -1;
1249 /* wake up the main thread */
1250 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1255 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1258 #endif
1260 #ifndef WIN32
1261 static void setup_signals(void)
1263 sigset_t mask;
1264 struct sigaction sa;
1266 /* Ignore USR2 */
1267 sigemptyset(&mask);
1268 sigaddset(&mask, SIGUSR2);
1269 sigprocmask(SIG_BLOCK, &mask, NULL);
1271 /* Setup handlers */
1273 sa.sa_handler = signal_handler;
1274 sigemptyset(&sa.sa_mask);
1275 sa.sa_flags = 0;
1277 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1278 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1279 "sigaction(SIGTERM) failed");
1281 /* httpd restart */
1282 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1283 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1284 "sigaction(SIGHUP) failed");
1286 /* httpd graceful restart */
1287 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1288 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1289 "sigaction(SIGUSR1) failed");
1291 /* read messages from request handlers - kill interval expired */
1292 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1293 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1294 "sigaction(SIGALRM) failed");
1296 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1297 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1298 "sigaction(SIGCHLD) failed");
1301 #endif
1303 #ifndef WIN32
1304 int fcgi_pm_main(void *dummy, child_info *info)
1305 #else
1306 void fcgi_pm_main(void *dummy)
1307 #endif
1309 fcgi_server *s;
1310 unsigned int i;
1311 int read_ready = 0;
1312 int alarmLeft = 0;
1314 #ifdef WIN32
1315 DWORD dwRet;
1316 int first_time = 1;
1317 HANDLE wait_thread = INVALID_HANDLE_VALUE;
1318 #else
1319 int callWaitPid, callDynamicProcs;
1320 #endif
1322 #ifdef WIN32
1323 // Add SystemRoot to the dynamic environment
1324 char ** envp = dynamicEnvp;
1325 for (i = 0; *envp; ++i) {
1326 ++envp;
1328 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1330 #else
1331 reduce_privileges();
1333 close(fcgi_pm_pipe[1]);
1334 change_process_name("fcgi-pm");
1335 setup_signals();
1337 if (fcgi_wrapper) {
1338 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1339 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1341 #endif
1343 /* Initialize AppClass */
1344 for (s = fcgi_servers; s != NULL; s = s->next)
1346 if (s->directive != APP_CLASS_STANDARD)
1347 continue;
1349 #ifdef WIN32
1350 if (s->socket_path)
1351 s->listenFd = 0;
1352 #endif
1354 for (i = 0; i < s->numProcesses; ++i)
1355 s->procs[i].state = FCGI_START;
1358 #ifdef WIN32
1359 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1360 "FastCGI: process manager initialized");
1361 #else
1362 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1363 "FastCGI: process manager initialized (pid %ld)", (long)getpid());
1364 #endif
1366 now = time(NULL);
1369 * Loop until SIGTERM
1371 for (;;) {
1372 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1373 #ifdef WIN32
1374 time_t expire;
1375 #else
1376 pid_t childPid;
1377 int waitStatus;
1378 #endif
1379 unsigned int numChildren;
1382 * If we came out of sigsuspend() for any reason other than
1383 * SIGALRM, pick up where we left off.
1385 if (alarmLeft)
1386 sleepSeconds = alarmLeft;
1389 * Examine each configured AppClass for a process that needs
1390 * starting. Compute the earliest time when the start should
1391 * be attempted, starting it now if the time has passed. Also,
1392 * remember that we do NOT need to restart externally managed
1393 * FastCGI applications.
1395 for (s = fcgi_servers; s != NULL; s = s->next)
1397 if (s->directive == APP_CLASS_EXTERNAL)
1398 continue;
1400 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1401 ? dynamicMaxClassProcs
1402 : s->numProcesses;
1404 for (i = 0; i < numChildren; ++i)
1406 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START)
1408 int restart = (s->procs[i].pid < 0);
1409 time_t restartTime = s->restartTime;
1411 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1413 if (restartTime <= now)
1415 if (s->listenFd < 0 && init_listen_sock(s))
1417 if (sleepSeconds > s->initStartDelay)
1418 sleepSeconds = s->initStartDelay;
1419 break;
1421 #ifndef WIN32
1422 if (caughtSigTerm) {
1423 goto ProcessSigTerm;
1425 #endif
1426 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1427 if (s->procs[i].pid <= 0) {
1428 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1429 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1430 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1431 s->fs_path);
1433 sleepSeconds = min(sleepSeconds,
1434 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1436 ap_assert(s->procs[i].pid < 0);
1437 break;
1440 s->restartTime = now;
1442 if (s->directive == APP_CLASS_DYNAMIC) {
1443 s->numProcesses++;
1444 fcgi_dynamic_total_proc_count++;
1445 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1448 s->procs[i].state = FCGI_RUNNING;
1450 if (restart)
1451 s->numRestarts++;
1453 if (fcgi_wrapper) {
1454 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1455 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1456 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1457 s->fs_path, (long)s->uid, (long)s->gid,
1458 restart ? "re" : "", (long)s->procs[i].pid);
1460 else {
1461 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1462 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1463 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1464 s->fs_path, restart ? "re" : "", (long)s->procs[i].pid);
1466 ap_assert(s->procs[i].pid > 0);
1467 } else {
1468 sleepSeconds = min(sleepSeconds, restartTime - now);
1474 #ifndef WIN32
1475 if(caughtSigTerm) {
1476 goto ProcessSigTerm;
1478 if((!caughtSigChld) && (!caughtSigUsr2)) {
1479 fd_set rfds;
1481 alarm(sleepSeconds);
1483 FD_ZERO(&rfds);
1484 FD_SET(fcgi_pm_pipe[0], &rfds);
1485 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1487 alarmLeft = alarm(0);
1489 callWaitPid = caughtSigChld;
1490 caughtSigChld = FALSE;
1491 callDynamicProcs = caughtSigUsr2;
1492 caughtSigUsr2 = FALSE;
1494 now = time(NULL);
1497 * Dynamic fcgi process management
1499 if((callDynamicProcs) || (!callWaitPid)) {
1500 dynamic_read_msgs(read_ready);
1501 if(fcgi_dynamic_epoch == 0) {
1502 fcgi_dynamic_epoch = now;
1504 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1505 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1506 dynamic_kill_idle_fs_procs();
1507 fcgi_dynamic_epoch = now;
1511 if(!callWaitPid) {
1512 continue;
1515 /* We've caught SIGCHLD, so find out who it was using waitpid,
1516 * write a log message and update its data structure. */
1518 for (;;) {
1519 if (caughtSigTerm)
1520 goto ProcessSigTerm;
1522 childPid = waitpid(-1, &waitStatus, WNOHANG);
1524 if (childPid == -1 || childPid == 0)
1525 break;
1527 for (s = fcgi_servers; s != NULL; s = s->next) {
1528 if (s->directive == APP_CLASS_EXTERNAL)
1529 continue;
1531 if (s->directive == APP_CLASS_DYNAMIC)
1532 numChildren = dynamicMaxClassProcs;
1533 else
1534 numChildren = s->numProcesses;
1536 for (i = 0; i < numChildren; i++) {
1537 if (s->procs[i].pid == childPid)
1538 goto ChildFound;
1542 /* TODO: print something about this unknown child */
1543 continue;
1545 ChildFound:
1546 s->procs[i].pid = -1;
1548 if (s->directive == APP_CLASS_STANDARD) {
1549 /* Always restart static apps */
1550 s->procs[i].state = STATE_NEEDS_STARTING;
1551 s->numFailures++;
1553 else {
1554 s->numProcesses--;
1555 fcgi_dynamic_total_proc_count--;
1557 if (s->procs[i].state == STATE_VICTIM) {
1558 s->procs[i].state = STATE_KILLED;
1560 else {
1561 /* A dynamic app died or exited without provocation from the PM */
1562 s->numFailures++;
1564 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1565 s->procs[i].state = STATE_NEEDS_STARTING;
1566 else
1567 s->procs[i].state = STATE_READY;
1571 if (WIFEXITED(waitStatus)) {
1572 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1573 "FastCGI:%s server \"%s\" (pid %d) terminated by calling exit with status '%d'",
1574 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1575 s->fs_path, (int)childPid, WEXITSTATUS(waitStatus));
1577 else if (WIFSIGNALED(waitStatus)) {
1578 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1579 "FastCGI:%s server \"%s\" (pid %d) terminated due to uncaught signal '%d' (%s)%s",
1580 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1581 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)],
1582 #ifdef WCOREDUMP
1583 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1584 #else
1585 "");
1586 #endif
1588 else if (WIFSTOPPED(waitStatus)) {
1589 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1590 "FastCGI:%s server \"%s\" (pid %d) stopped due to uncaught signal '%d' (%s)",
1591 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1592 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)]);
1594 } /* for (;;), waitpid() */
1595 #else
1596 if (first_time) {
1597 /* Start the child wait thread */
1598 wait_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)child_wait_thread, NULL, 0, NULL);
1599 first_time = 0;
1602 /* wait for an event to occur or timer expires */
1603 expire = time(NULL) + sleepSeconds;
1604 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1606 if (dwRet == WAIT_FAILED) {
1607 /* There is something seriously wrong here */
1608 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1609 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1610 bTimeToDie = TRUE;
1613 if (dwRet != WAIT_TIMEOUT) {
1614 now = time(NULL);
1616 if (now < expire)
1617 alarmLeft = expire - now;
1621 * Dynamic fcgi process management
1623 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1624 if (dwRet == MBOX_EVENT) {
1625 read_ready = 1;
1628 now = time(NULL);
1630 dynamic_read_msgs(read_ready);
1632 if(fcgi_dynamic_epoch == 0) {
1633 fcgi_dynamic_epoch = now;
1636 if (((long)(now-fcgi_dynamic_epoch) >= (int)dynamicKillInterval) ||
1637 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1638 dynamic_kill_idle_fs_procs();
1639 fcgi_dynamic_epoch = now;
1641 read_ready = 0;
1643 else if (dwRet == WAKE_EVENT) {
1644 continue;
1646 else if (dwRet == TERM_EVENT) {
1647 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1648 "FastCGI: Termination event received process manager shutting down");
1649 bTimeToDie = TRUE;
1651 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1652 goto ProcessSigTerm;
1654 else {
1655 // Have an received an unknown event - should not happen
1656 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1657 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1658 bTimeToDie = TRUE;
1659 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1660 goto ProcessSigTerm;
1662 #endif
1663 } /* for (;;), the whole shoot'n match */
1665 ProcessSigTerm:
1667 * Kill off the children, then exit.
1669 shutdown_all();
1671 #ifdef WIN32
1672 return;
1673 #else
1674 exit(0);
1675 #endif
1678 #ifdef WIN32
1679 int fcgi_pm_add_job(fcgi_pm_job *new_job) {
1681 if (new_job == NULL)
1682 return 0;
1684 if (ap_acquire_mutex(fcgi_dynamic_mbox_mutex) != MULTI_OK) {
1685 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1686 "FastCGI: Failed to aquire the dynamic mbox mutex!");
1689 new_job->next = fcgi_dynamic_mbox;
1690 fcgi_dynamic_mbox = new_job;
1692 if (! ap_release_mutex(fcgi_dynamic_mbox_mutex)) {
1693 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1694 "FastCGI: Failed to release the dynamic mbox mutex!");
1697 return 1;
1699 #endif