Cleanup the STATE vs. JOB naming (UNIX).
[mod_fastcgi.git] / fcgi_pm.c
blob410d48e510ff7f7861a017bc371a0f868eca73e4
1 /*
2 * $Id: fcgi_pm.c,v 1.54 2001/05/03 22:03:53 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_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 /* 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_STATE)
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_STATE;
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)
749 case FCGI_SERVER_START_JOB:
750 case FCGI_SERVER_RESTART_JOB:
752 if (sscanf(ptr1, "%c %s %16s %15s",
753 &opcode, execName, user, group) != 4)
755 scan_failed = 1;
757 break;
759 case FCGI_REQUEST_TIMEOUT_JOB:
761 if (sscanf(ptr1, "%c %s %16s %15s",
762 &opcode, execName, user, group) != 4)
764 scan_failed = 1;
766 break;
768 case REQ_COMPLETE:
770 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
771 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
773 scan_failed = 1;
775 break;
777 default:
779 scan_failed = 1;
780 break;
783 if (scan_failed) {
784 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
785 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
786 goto NextJob;
788 #else
789 /* Update data structures for processing */
790 while (cjob != NULL) {
791 joblist = cjob->next;
792 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
793 #endif
795 #ifndef WIN32
796 s = fcgi_util_fs_get(execName, user, group);
797 #else
798 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
799 #endif
801 #ifndef WIN32
802 if (s==NULL && opcode != FCGI_REQUEST_COMPLETE_JOB)
803 #else
804 if (s==NULL && cjob->id != FCGI_REQUEST_COMPLETE_JOB)
805 #endif
807 #ifdef WIN32
808 HANDLE mutex = ap_create_mutex(NULL);
810 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
811 #else
812 int fd;
813 const char *err;
814 #endif
816 /* Create a perm subpool to hold the new server data,
817 * we can destroy it if something doesn't pan out */
818 sp = ap_make_sub_pool(fcgi_config_pool);
820 /* Create a new "dynamic" server */
821 s = fcgi_util_fs_new(sp);
823 s->directive = APP_CLASS_DYNAMIC;
824 s->restartDelay = dynamicRestartDelay;
825 s->listenQueueDepth = dynamicListenQueueDepth;
826 s->initStartDelay = dynamicInitStartDelay;
827 s->envp = dynamicEnvp;
829 #ifdef WIN32
830 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
831 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
832 #else
833 s->fs_path = ap_pstrdup(sp, execName);
834 #endif
835 ap_getparents(s->fs_path);
836 ap_no2slash(s->fs_path);
837 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
839 #ifndef WIN32
840 /* Create socket file's path */
841 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
842 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
844 /* Create sockaddr, prealloc it so it won't get created in tp */
845 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
846 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
847 &s->socket_addr_len, s->socket_path);
848 if (err) {
849 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
850 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
851 goto BagNewServer;
854 if (init_listen_sock(s)) {
855 goto BagNewServer;
858 /* If a wrapper is being used, config user/group info */
859 if (fcgi_wrapper) {
860 if (user[0] == '~') {
861 /* its a user dir uri, the rest is a username, not a uid */
862 struct passwd *pw = getpwnam(&user[1]);
864 if (!pw) {
865 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
866 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
867 execName, &user[1]);
868 goto BagNewServer;
870 s->uid = pw->pw_uid;
871 s->user = ap_pstrdup(sp, user);
872 s->username = s->user;
874 s->gid = pw->pw_gid;
875 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
877 else {
878 struct passwd *pw;
880 s->uid = (uid_t)atol(user);
881 pw = getpwuid(s->uid);
882 if (!pw) {
883 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
884 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
885 execName, (long)s->uid);
886 goto BagNewServer;
888 s->user = ap_pstrdup(sp, user);
889 s->username = ap_pstrdup(sp, pw->pw_name);
891 s->gid = (gid_t)atol(group);
892 s->group = ap_pstrdup(sp, group);
895 #else
896 /* Create socket file's path */
897 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
898 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
899 s->listenFd = 0;
900 #endif
902 fcgi_util_fs_add(s);
904 else {
905 #ifndef WIN32
906 if (opcode == FCGI_SERVER_RESTART_JOB) {
907 #else
908 if (cjob->id==FCGI_SERVER_RESTART_JOB) {
909 #endif
910 /* Check to see if the binary has changed. If so,
911 * kill the FCGI application processes, and
912 * restart them.
914 struct stat stbuf;
915 int i;
917 #ifndef WIN32
918 if ((stat(execName, &stbuf)==0) &&
919 #else
920 if ((stat(cjob->fs_path, &stbuf)==0) &&
921 #endif
922 (stbuf.st_mtime > s->restartTime)) {
923 /* kill old server(s) */
924 for (i = 0; i < dynamicMaxClassProcs; i++) {
925 if (s->procs[i].pid > 0) {
926 fcgi_kill(&s->procs[i], SIGTERM);
930 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
931 "FastCGI: restarting server \"%s\" processes, newer version found",
932 #ifndef WIN32
933 execName);
934 #else
935 cjob->fs_path);
936 #endif
939 /* If dynamicAutoRestart, don't mark any new processes
940 * for starting because we probably got the
941 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
942 * will be restarting all of those we just killed.
944 if (dynamicAutoRestart)
945 goto NextJob;
947 #ifndef WIN32
948 else if (opcode == FCGI_SERVER_START_JOB) {
949 #else
950 else if (cjob->id==FCGI_SERVER_START_JOB) {
951 #endif
952 /* we've been asked to start a process--only start
953 * it if we're not already running at least one
954 * instance.
956 int i;
958 for (i = 0; i < dynamicMaxClassProcs; i++) {
959 if (s->procs[i].state == FCGI_RUNNING_STATE)
960 break;
962 /* if already running, don't start another one */
963 if (i < dynamicMaxClassProcs) {
964 goto NextJob;
969 #ifndef WIN32
970 switch (opcode)
971 #else
972 switch (cjob->id)
973 #endif
975 int i, start;
977 case FCGI_SERVER_RESTART_JOB:
979 start = FALSE;
981 /* We just waxed 'em all. Try to find an idle slot. */
983 for (i = 0; i < dynamicMaxClassProcs; ++i)
985 if (s->procs[i].state == FCGI_START_STATE
986 || s->procs[i].state == FCGI_RUNNING_STATE)
988 break;
990 else if (s->procs[i].state == FCGI_KILLED_STATE
991 || s->procs[i].state == FCGI_READY_STATE)
993 start = TRUE;
994 break;
998 /* Nope, just use the first slot */
999 if (i == dynamicMaxClassProcs)
1001 start = TRUE;
1002 i = 0;
1005 if (start)
1007 schedule_start(s, i);
1010 break;
1012 case FCGI_SERVER_START_JOB:
1013 case FCGI_REQUEST_TIMEOUT_JOB:
1015 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1017 * Extra instances should have been
1018 * terminated beforehand, probably need
1019 * to increase ProcessSlack parameter
1021 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1022 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1023 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1024 goto NextJob;
1027 /* find next free slot */
1028 for (i = 0; i < dynamicMaxClassProcs; i++)
1030 if (s->procs[i].state == FCGI_START_STATE)
1032 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1033 break;
1035 else if (s->procs[i].state == FCGI_RUNNING_STATE)
1037 continue;
1040 schedule_start(s, i);
1041 break;
1044 #ifdef FCGI_DEBUG
1045 if (i >= dynamicMaxClassProcs) {
1046 FCGIDBG1("ignore_job: slots are max'd");
1048 #endif
1049 break;
1050 case FCGI_REQUEST_COMPLETE_JOB:
1051 /* only record stats if we have a structure */
1052 if (s) {
1053 #ifndef WIN32
1054 s->totalConnTime += req_usec;
1055 s->totalQueueTime += q_usec;
1056 #else
1057 s->totalConnTime += cjob->start_time;
1058 s->totalQueueTime += cjob->qsec;
1059 #endif
1061 break;
1064 NextJob:
1066 #ifdef WIN32
1067 /* Cleanup job data */
1068 free(cjob->fs_path);
1069 free(cjob->user);
1070 free(cjob->group);
1071 free(cjob);
1072 cjob = joblist;
1073 #endif
1075 continue;
1077 BagNewServer:
1078 ap_destroy_pool(sp);
1080 #ifdef WIN32
1081 free(cjob->fs_path);
1082 free(cjob);
1083 cjob = joblist;
1084 #endif
1087 #ifndef WIN32
1088 if (ptr1 == buf) {
1089 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1090 "FastCGI: really bogus message: \"%s\"", ptr1);
1091 ptr1 += strlen(buf);
1094 buflen -= ptr1 - buf;
1095 if (buflen) {
1096 memmove(buf, ptr1, buflen);
1098 #endif
1100 ap_destroy_pool(tp);
1104 *----------------------------------------------------------------------
1106 * dynamic_kill_idle_fs_procs
1108 * Implement a kill policy for the dynamic FastCGI applications.
1109 * We also update the data structures to reflect the changes.
1111 * Side effects:
1112 * Processes are marked for deletion possibly killed.
1114 *----------------------------------------------------------------------
1116 static void dynamic_kill_idle_fs_procs(void)
1118 fcgi_server *s;
1119 int victims = 0;
1121 for (s = fcgi_servers; s != NULL; s = s->next)
1124 * server's smoothed running time, or if that's 0, the current total
1126 unsigned long connTime;
1129 * maximum number of microseconds that all of a server's running
1130 * processes together could have spent running since the last check
1132 unsigned long totalTime;
1135 * percentage, 0-100, of totalTime that the processes actually used
1137 int loadFactor;
1139 int i;
1141 if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1143 continue;
1146 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1147 totalTime = s->numProcesses * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1149 loadFactor = 100 * connTime / totalTime;
1151 if (s->numProcesses == 1)
1153 if (loadFactor >= dynamicThreshold1)
1155 continue;
1158 else
1160 int load = s->numProcesses / (s->numProcesses - 1) * loadFactor;
1162 if (load >= dynamicThresholdN)
1164 continue;
1169 * Run through the procs to see if we can get away w/o waxing one.
1171 for (i = 0; i < dynamicMaxClassProcs; ++i)
1173 if (s->procs[i].state == FCGI_START_STATE)
1175 s->procs[i].state = FCGI_READY_STATE;
1176 break;
1178 else if (s->procs[i].state == FCGI_VICTIM_STATE)
1180 break;
1184 if (i < dynamicMaxClassProcs)
1186 continue;
1189 for (i = 0; i < dynamicMaxClassProcs; ++i)
1191 if (s->procs[i].state != FCGI_RUNNING_STATE)
1193 continue;
1196 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1197 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1198 s->fs_path, s->procs[i].pid);
1200 fcgi_kill(&s->procs[i], SIGTERM);
1202 victims++;
1203 break;
1207 * If the number of non-victims is less than or equal to
1208 * the minimum that may be running without being killed off,
1209 * don't select any more victims.
1211 if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1213 break;
1218 #ifdef WIN32
1220 // This is a little bogus, there's gotta be a better way to do this
1221 // Can we use WaitForMultipleObjects()
1222 #define FCGI_PROC_WAIT_TIME 100
1224 void child_wait_thread(void *dummy) {
1225 fcgi_server *s;
1226 DWORD dwRet = WAIT_TIMEOUT;
1227 int numChildren;
1228 int i;
1229 int waited;
1231 while (!bTimeToDie) {
1232 waited = 0;
1234 for (s = fcgi_servers; s != NULL; s = s->next) {
1235 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1236 continue;
1238 if (s->directive == APP_CLASS_DYNAMIC) {
1239 numChildren = dynamicMaxClassProcs;
1241 else {
1242 numChildren = s->numProcesses;
1245 for (i=0; i < numChildren; i++) {
1246 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1248 DWORD exitStatus = 0;
1250 /* timeout is currently set for 100 miliecond */
1251 /* it may need t longer or user customizable */
1252 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1254 waited = 1;
1256 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1257 /* a child fs has died */
1258 /* mark the child as dead */
1260 if (s->directive == APP_CLASS_STANDARD) {
1261 /* restart static app */
1262 s->procs[i].state = FCGI_START_STATE;
1263 s->numFailures++;
1265 else {
1266 s->numProcesses--;
1267 fcgi_dynamic_total_proc_count--;
1268 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1270 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1271 s->procs[i].state = FCGI_KILLED_STATE;
1273 else {
1274 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1275 s->numFailures++;
1277 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1278 s->procs[i].state = FCGI_START_STATE;
1280 else {
1281 s->procs[i].state = FCGI_READY_STATE;
1286 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1288 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1289 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1290 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1291 s->fs_path, s->procs[i].pid, exitStatus);
1293 CloseHandle(s->procs[i].handle);
1294 s->procs[i].handle = INVALID_HANDLE_VALUE;
1295 s->procs[i].pid = -1;
1297 /* wake up the main thread */
1298 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1303 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1306 #endif
1308 #ifndef WIN32
1309 static void setup_signals(void)
1311 sigset_t mask;
1312 struct sigaction sa;
1314 /* Ignore USR2 */
1315 sigemptyset(&mask);
1316 sigaddset(&mask, SIGUSR2);
1317 sigprocmask(SIG_BLOCK, &mask, NULL);
1319 /* Setup handlers */
1321 sa.sa_handler = signal_handler;
1322 sigemptyset(&sa.sa_mask);
1323 sa.sa_flags = 0;
1325 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1326 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1327 "sigaction(SIGTERM) failed");
1329 /* httpd restart */
1330 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1331 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1332 "sigaction(SIGHUP) failed");
1334 /* httpd graceful restart */
1335 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1336 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1337 "sigaction(SIGUSR1) failed");
1339 /* read messages from request handlers - kill interval expired */
1340 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1341 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1342 "sigaction(SIGALRM) failed");
1344 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1345 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1346 "sigaction(SIGCHLD) failed");
1349 #endif
1351 #ifndef WIN32
1352 int fcgi_pm_main(void *dummy, child_info *info)
1353 #else
1354 void fcgi_pm_main(void *dummy)
1355 #endif
1357 fcgi_server *s;
1358 unsigned int i;
1359 int read_ready = 0;
1360 int alarmLeft = 0;
1362 #ifdef WIN32
1363 DWORD dwRet;
1364 int first_time = 1;
1365 HANDLE wait_thread = INVALID_HANDLE_VALUE;
1366 #else
1367 int callWaitPid, callDynamicProcs;
1368 #endif
1370 #ifdef WIN32
1371 // Add SystemRoot to the dynamic environment
1372 char ** envp = dynamicEnvp;
1373 for (i = 0; *envp; ++i) {
1374 ++envp;
1376 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1378 #else
1379 reduce_privileges();
1381 close(fcgi_pm_pipe[1]);
1382 change_process_name("fcgi-pm");
1383 setup_signals();
1385 if (fcgi_wrapper) {
1386 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1387 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1389 #endif
1391 /* Initialize AppClass */
1392 for (s = fcgi_servers; s != NULL; s = s->next)
1394 if (s->directive != APP_CLASS_STANDARD)
1395 continue;
1397 #ifdef WIN32
1398 if (s->socket_path)
1399 s->listenFd = 0;
1400 #endif
1402 for (i = 0; i < s->numProcesses; ++i)
1403 s->procs[i].state = FCGI_START_STATE;
1406 #ifdef WIN32
1407 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1408 "FastCGI: process manager initialized");
1409 #else
1410 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1411 "FastCGI: process manager initialized (pid %ld)", (long)getpid());
1412 #endif
1414 now = time(NULL);
1417 * Loop until SIGTERM
1419 for (;;) {
1420 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1421 #ifdef WIN32
1422 time_t expire;
1423 #else
1424 pid_t childPid;
1425 int waitStatus;
1426 #endif
1427 unsigned int numChildren;
1430 * If we came out of sigsuspend() for any reason other than
1431 * SIGALRM, pick up where we left off.
1433 if (alarmLeft)
1434 sleepSeconds = alarmLeft;
1437 * Examine each configured AppClass for a process that needs
1438 * starting. Compute the earliest time when the start should
1439 * be attempted, starting it now if the time has passed. Also,
1440 * remember that we do NOT need to restart externally managed
1441 * FastCGI applications.
1443 for (s = fcgi_servers; s != NULL; s = s->next)
1445 if (s->directive == APP_CLASS_EXTERNAL)
1446 continue;
1448 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1449 ? dynamicMaxClassProcs
1450 : s->numProcesses;
1452 for (i = 0; i < numChildren; ++i)
1454 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1456 int restart = (s->procs[i].pid < 0);
1457 time_t restartTime = s->restartTime;
1459 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1461 if (restartTime <= now)
1463 if (s->listenFd < 0 && init_listen_sock(s))
1465 if (sleepSeconds > s->initStartDelay)
1466 sleepSeconds = s->initStartDelay;
1467 break;
1469 #ifndef WIN32
1470 if (caughtSigTerm) {
1471 goto ProcessSigTerm;
1473 #endif
1474 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1475 if (s->procs[i].pid <= 0) {
1476 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1477 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1478 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1479 s->fs_path);
1481 sleepSeconds = min(sleepSeconds,
1482 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1484 ap_assert(s->procs[i].pid < 0);
1485 break;
1488 s->restartTime = now;
1490 if (s->directive == APP_CLASS_DYNAMIC) {
1491 s->numProcesses++;
1492 fcgi_dynamic_total_proc_count++;
1493 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1496 s->procs[i].state = FCGI_RUNNING_STATE;
1498 if (restart)
1499 s->numRestarts++;
1501 if (fcgi_wrapper) {
1502 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1503 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1504 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1505 s->fs_path, (long)s->uid, (long)s->gid,
1506 restart ? "re" : "", (long)s->procs[i].pid);
1508 else {
1509 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1510 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1511 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1512 s->fs_path, restart ? "re" : "", (long)s->procs[i].pid);
1514 ap_assert(s->procs[i].pid > 0);
1515 } else {
1516 sleepSeconds = min(sleepSeconds, restartTime - now);
1522 #ifndef WIN32
1523 if(caughtSigTerm) {
1524 goto ProcessSigTerm;
1526 if((!caughtSigChld) && (!caughtSigUsr2)) {
1527 fd_set rfds;
1529 alarm(sleepSeconds);
1531 FD_ZERO(&rfds);
1532 FD_SET(fcgi_pm_pipe[0], &rfds);
1533 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1535 alarmLeft = alarm(0);
1537 callWaitPid = caughtSigChld;
1538 caughtSigChld = FALSE;
1539 callDynamicProcs = caughtSigUsr2;
1540 caughtSigUsr2 = FALSE;
1542 now = time(NULL);
1545 * Dynamic fcgi process management
1547 if((callDynamicProcs) || (!callWaitPid)) {
1548 dynamic_read_msgs(read_ready);
1549 if(fcgi_dynamic_epoch == 0) {
1550 fcgi_dynamic_epoch = now;
1552 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1553 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1554 dynamic_kill_idle_fs_procs();
1555 fcgi_dynamic_epoch = now;
1559 if(!callWaitPid) {
1560 continue;
1563 /* We've caught SIGCHLD, so find out who it was using waitpid,
1564 * write a log message and update its data structure. */
1566 for (;;) {
1567 if (caughtSigTerm)
1568 goto ProcessSigTerm;
1570 childPid = waitpid(-1, &waitStatus, WNOHANG);
1572 if (childPid == -1 || childPid == 0)
1573 break;
1575 for (s = fcgi_servers; s != NULL; s = s->next) {
1576 if (s->directive == APP_CLASS_EXTERNAL)
1577 continue;
1579 if (s->directive == APP_CLASS_DYNAMIC)
1580 numChildren = dynamicMaxClassProcs;
1581 else
1582 numChildren = s->numProcesses;
1584 for (i = 0; i < numChildren; i++) {
1585 if (s->procs[i].pid == childPid)
1586 goto ChildFound;
1590 /* TODO: print something about this unknown child */
1591 continue;
1593 ChildFound:
1594 s->procs[i].pid = -1;
1596 if (s->directive == APP_CLASS_STANDARD) {
1597 /* Always restart static apps */
1598 s->procs[i].state = FCGI_START_STATE;
1599 s->numFailures++;
1601 else {
1602 s->numProcesses--;
1603 fcgi_dynamic_total_proc_count--;
1605 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1606 s->procs[i].state = FCGI_KILLED_STATE;
1608 else {
1609 /* A dynamic app died or exited without provocation from the PM */
1610 s->numFailures++;
1612 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1613 s->procs[i].state = FCGI_START_STATE;
1614 else
1615 s->procs[i].state = FCGI_READY_STATE;
1619 if (WIFEXITED(waitStatus)) {
1620 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1621 "FastCGI:%s server \"%s\" (pid %d) terminated by calling exit with status '%d'",
1622 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1623 s->fs_path, (int)childPid, WEXITSTATUS(waitStatus));
1625 else if (WIFSIGNALED(waitStatus)) {
1626 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1627 "FastCGI:%s server \"%s\" (pid %d) terminated due to uncaught signal '%d' (%s)%s",
1628 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1629 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)],
1630 #ifdef WCOREDUMP
1631 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1632 #else
1633 "");
1634 #endif
1636 else if (WIFSTOPPED(waitStatus)) {
1637 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1638 "FastCGI:%s server \"%s\" (pid %d) stopped due to uncaught signal '%d' (%s)",
1639 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1640 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)]);
1642 } /* for (;;), waitpid() */
1643 #else
1644 if (first_time) {
1645 /* Start the child wait thread */
1646 wait_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)child_wait_thread, NULL, 0, NULL);
1647 first_time = 0;
1650 /* wait for an event to occur or timer expires */
1651 expire = time(NULL) + sleepSeconds;
1652 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1654 if (dwRet == WAIT_FAILED) {
1655 /* There is something seriously wrong here */
1656 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1657 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1658 bTimeToDie = TRUE;
1661 if (dwRet != WAIT_TIMEOUT) {
1662 now = time(NULL);
1664 if (now < expire)
1665 alarmLeft = expire - now;
1669 * Dynamic fcgi process management
1671 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1672 if (dwRet == MBOX_EVENT) {
1673 read_ready = 1;
1676 now = time(NULL);
1678 dynamic_read_msgs(read_ready);
1680 if(fcgi_dynamic_epoch == 0) {
1681 fcgi_dynamic_epoch = now;
1684 if (((long)(now-fcgi_dynamic_epoch) >= (int)dynamicKillInterval) ||
1685 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1686 dynamic_kill_idle_fs_procs();
1687 fcgi_dynamic_epoch = now;
1689 read_ready = 0;
1691 else if (dwRet == WAKE_EVENT) {
1692 continue;
1694 else if (dwRet == TERM_EVENT) {
1695 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1696 "FastCGI: Termination event received process manager shutting down");
1697 bTimeToDie = TRUE;
1699 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1700 goto ProcessSigTerm;
1702 else {
1703 // Have an received an unknown event - should not happen
1704 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1705 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1706 bTimeToDie = TRUE;
1707 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1708 goto ProcessSigTerm;
1710 #endif
1711 } /* for (;;), the whole shoot'n match */
1713 ProcessSigTerm:
1715 * Kill off the children, then exit.
1717 shutdown_all();
1719 #ifdef WIN32
1720 return;
1721 #else
1722 exit(0);
1723 #endif
1726 #ifdef WIN32
1727 int fcgi_pm_add_job(fcgi_pm_job *new_job) {
1729 if (new_job == NULL)
1730 return 0;
1732 if (ap_acquire_mutex(fcgi_dynamic_mbox_mutex) != MULTI_OK) {
1733 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1734 "FastCGI: Failed to aquire the dynamic mbox mutex!");
1737 new_job->next = fcgi_dynamic_mbox;
1738 fcgi_dynamic_mbox = new_job;
1740 if (! ap_release_mutex(fcgi_dynamic_mbox_mutex)) {
1741 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1742 "FastCGI: Failed to release the dynamic mbox mutex!");
1745 return 1;
1747 #endif