no message
[mod_fastcgi.git] / fcgi_pm.c
blobb181ed3397f6e86d270313e0a81a6f7110e60f28
1 /*
2 * $Id: fcgi_pm.c,v 1.60 2001/08/24 02:14:17 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 && errno != ENOENT) {
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 s = 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 caughtSigAlarm = 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 caughtSigAlarm = 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 FCGI_REQUEST_COMPLETE_JOB
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 FCGI_REQUEST_COMPLETE_JOB:
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;
828 s->flush = dynamicFlush;
830 #ifdef WIN32
831 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
832 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
833 #else
834 s->fs_path = ap_pstrdup(sp, execName);
835 #endif
836 ap_getparents(s->fs_path);
837 ap_no2slash(s->fs_path);
838 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
840 #ifndef WIN32
841 /* Create socket file's path */
842 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
843 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
845 /* Create sockaddr, prealloc it so it won't get created in tp */
846 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
847 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
848 &s->socket_addr_len, s->socket_path);
849 if (err) {
850 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
851 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
852 goto BagNewServer;
855 if (init_listen_sock(s)) {
856 goto BagNewServer;
859 /* If a wrapper is being used, config user/group info */
860 if (fcgi_wrapper) {
861 if (user[0] == '~') {
862 /* its a user dir uri, the rest is a username, not a uid */
863 struct passwd *pw = getpwnam(&user[1]);
865 if (!pw) {
866 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
867 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
868 execName, &user[1]);
869 goto BagNewServer;
871 s->uid = pw->pw_uid;
872 s->user = ap_pstrdup(sp, user);
873 s->username = s->user;
875 s->gid = pw->pw_gid;
876 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
878 else {
879 struct passwd *pw;
881 s->uid = (uid_t)atol(user);
882 pw = getpwuid(s->uid);
883 if (!pw) {
884 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
885 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
886 execName, (long)s->uid);
887 goto BagNewServer;
889 s->user = ap_pstrdup(sp, user);
890 s->username = ap_pstrdup(sp, pw->pw_name);
892 s->gid = (gid_t)atol(group);
893 s->group = ap_pstrdup(sp, group);
896 #else
897 /* Create socket file's path */
898 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
899 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
900 s->listenFd = 0;
901 #endif
903 fcgi_util_fs_add(s);
905 else {
906 #ifndef WIN32
907 if (opcode == FCGI_SERVER_RESTART_JOB) {
908 #else
909 if (cjob->id==FCGI_SERVER_RESTART_JOB) {
910 #endif
911 /* Check to see if the binary has changed. If so,
912 * kill the FCGI application processes, and
913 * restart them.
915 struct stat stbuf;
916 int i;
918 #ifndef WIN32
919 if ((stat(execName, &stbuf)==0) &&
920 #else
921 if ((stat(cjob->fs_path, &stbuf)==0) &&
922 #endif
923 (stbuf.st_mtime > s->restartTime)) {
924 /* kill old server(s) */
925 for (i = 0; i < dynamicMaxClassProcs; i++) {
926 if (s->procs[i].pid > 0) {
927 fcgi_kill(&s->procs[i], SIGTERM);
931 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
932 "FastCGI: restarting server \"%s\" processes, newer version found",
933 #ifndef WIN32
934 execName);
935 #else
936 cjob->fs_path);
937 #endif
940 /* If dynamicAutoRestart, don't mark any new processes
941 * for starting because we probably got the
942 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
943 * will be restarting all of those we just killed.
945 if (dynamicAutoRestart)
946 goto NextJob;
948 #ifndef WIN32
949 else if (opcode == FCGI_SERVER_START_JOB) {
950 #else
951 else if (cjob->id==FCGI_SERVER_START_JOB) {
952 #endif
953 /* we've been asked to start a process--only start
954 * it if we're not already running at least one
955 * instance.
957 int i;
959 for (i = 0; i < dynamicMaxClassProcs; i++) {
960 if (s->procs[i].state == FCGI_RUNNING_STATE)
961 break;
963 /* if already running, don't start another one */
964 if (i < dynamicMaxClassProcs) {
965 goto NextJob;
970 #ifndef WIN32
971 switch (opcode)
972 #else
973 switch (cjob->id)
974 #endif
976 int i, start;
978 case FCGI_SERVER_RESTART_JOB:
980 start = FALSE;
982 /* We just waxed 'em all. Try to find an idle slot. */
984 for (i = 0; i < dynamicMaxClassProcs; ++i)
986 if (s->procs[i].state == FCGI_START_STATE
987 || s->procs[i].state == FCGI_RUNNING_STATE)
989 break;
991 else if (s->procs[i].state == FCGI_KILLED_STATE
992 || s->procs[i].state == FCGI_READY_STATE)
994 start = TRUE;
995 break;
999 /* Nope, just use the first slot */
1000 if (i == dynamicMaxClassProcs)
1002 start = TRUE;
1003 i = 0;
1006 if (start)
1008 schedule_start(s, i);
1011 break;
1013 case FCGI_SERVER_START_JOB:
1014 case FCGI_REQUEST_TIMEOUT_JOB:
1016 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1018 * Extra instances should have been
1019 * terminated beforehand, probably need
1020 * to increase ProcessSlack parameter
1022 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1023 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1024 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1025 goto NextJob;
1028 /* find next free slot */
1029 for (i = 0; i < dynamicMaxClassProcs; i++)
1031 if (s->procs[i].state == FCGI_START_STATE)
1033 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1034 break;
1036 else if (s->procs[i].state == FCGI_RUNNING_STATE)
1038 continue;
1041 schedule_start(s, i);
1042 break;
1045 #ifdef FCGI_DEBUG
1046 if (i >= dynamicMaxClassProcs) {
1047 FCGIDBG1("ignore_job: slots are max'd");
1049 #endif
1050 break;
1051 case FCGI_REQUEST_COMPLETE_JOB:
1052 /* only record stats if we have a structure */
1053 if (s) {
1054 #ifndef WIN32
1055 s->totalConnTime += req_usec;
1056 s->totalQueueTime += q_usec;
1057 #else
1058 s->totalConnTime += cjob->start_time;
1059 s->totalQueueTime += cjob->qsec;
1060 #endif
1062 break;
1065 NextJob:
1067 #ifdef WIN32
1068 /* Cleanup job data */
1069 free(cjob->fs_path);
1070 free(cjob->user);
1071 free(cjob->group);
1072 free(cjob);
1073 cjob = joblist;
1074 #endif
1076 continue;
1078 BagNewServer:
1079 ap_destroy_pool(sp);
1081 #ifdef WIN32
1082 free(cjob->fs_path);
1083 free(cjob);
1084 cjob = joblist;
1085 #endif
1088 #ifndef WIN32
1089 if (ptr1 == buf) {
1090 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1091 "FastCGI: really bogus message: \"%s\"", ptr1);
1092 ptr1 += strlen(buf);
1095 buflen -= ptr1 - buf;
1096 if (buflen) {
1097 memmove(buf, ptr1, buflen);
1099 #endif
1101 ap_destroy_pool(tp);
1105 *----------------------------------------------------------------------
1107 * dynamic_kill_idle_fs_procs
1109 * Implement a kill policy for the dynamic FastCGI applications.
1110 * We also update the data structures to reflect the changes.
1112 * Side effects:
1113 * Processes are marked for deletion possibly killed.
1115 *----------------------------------------------------------------------
1117 static void dynamic_kill_idle_fs_procs(void)
1119 fcgi_server *s;
1120 int victims = 0;
1122 for (s = fcgi_servers; s != NULL; s = s->next)
1125 * server's smoothed running time, or if that's 0, the current total
1127 unsigned long connTime;
1130 * maximum number of microseconds that all of a server's running
1131 * processes together could have spent running since the last check
1133 unsigned long totalTime;
1136 * percentage, 0-100, of totalTime that the processes actually used
1138 int loadFactor;
1140 int i;
1142 if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1144 continue;
1147 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1148 totalTime = s->numProcesses * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1150 loadFactor = 100 * connTime / totalTime;
1152 if (s->numProcesses == 1)
1154 if (loadFactor >= dynamicThreshold1)
1156 continue;
1159 else
1161 int load = s->numProcesses / (s->numProcesses - 1) * loadFactor;
1163 if (load >= dynamicThresholdN)
1165 continue;
1170 * Run through the procs to see if we can get away w/o waxing one.
1172 for (i = 0; i < dynamicMaxClassProcs; ++i)
1174 if (s->procs[i].state == FCGI_START_STATE)
1176 s->procs[i].state = FCGI_READY_STATE;
1177 break;
1179 else if (s->procs[i].state == FCGI_VICTIM_STATE)
1181 break;
1185 if (i < dynamicMaxClassProcs)
1187 continue;
1190 for (i = 0; i < dynamicMaxClassProcs; ++i)
1192 if (s->procs[i].state != FCGI_RUNNING_STATE)
1194 continue;
1197 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1198 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1199 s->fs_path, s->procs[i].pid);
1201 fcgi_kill(&s->procs[i], SIGTERM);
1203 victims++;
1204 break;
1208 * If the number of non-victims is less than or equal to
1209 * the minimum that may be running without being killed off,
1210 * don't select any more victims.
1212 if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1214 break;
1219 #ifdef WIN32
1221 // This is a little bogus, there's gotta be a better way to do this
1222 // Can we use WaitForMultipleObjects()
1223 #define FCGI_PROC_WAIT_TIME 100
1225 void child_wait_thread(void *dummy) {
1226 fcgi_server *s;
1227 DWORD dwRet = WAIT_TIMEOUT;
1228 int numChildren;
1229 int i;
1230 int waited;
1232 while (!bTimeToDie) {
1233 waited = 0;
1235 for (s = fcgi_servers; s != NULL; s = s->next) {
1236 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1237 continue;
1239 if (s->directive == APP_CLASS_DYNAMIC) {
1240 numChildren = dynamicMaxClassProcs;
1242 else {
1243 numChildren = s->numProcesses;
1246 for (i=0; i < numChildren; i++) {
1247 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1249 DWORD exitStatus = 0;
1251 /* timeout is currently set for 100 miliecond */
1252 /* it may need t longer or user customizable */
1253 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1255 waited = 1;
1257 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1258 /* a child fs has died */
1259 /* mark the child as dead */
1261 if (s->directive == APP_CLASS_STANDARD) {
1262 /* restart static app */
1263 s->procs[i].state = FCGI_START_STATE;
1264 s->numFailures++;
1266 else {
1267 s->numProcesses--;
1268 fcgi_dynamic_total_proc_count--;
1269 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1271 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1272 s->procs[i].state = FCGI_KILLED_STATE;
1274 else {
1275 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1276 s->numFailures++;
1278 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1279 s->procs[i].state = FCGI_START_STATE;
1281 else {
1282 s->procs[i].state = FCGI_READY_STATE;
1287 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1289 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1290 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1291 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1292 s->fs_path, s->procs[i].pid, exitStatus);
1294 CloseHandle(s->procs[i].handle);
1295 s->procs[i].handle = INVALID_HANDLE_VALUE;
1296 s->procs[i].pid = -1;
1298 /* wake up the main thread */
1299 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1304 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1307 #endif
1309 #ifndef WIN32
1310 static void setup_signals(void)
1312 struct sigaction sa;
1314 /* Setup handlers */
1316 sa.sa_handler = signal_handler;
1317 sigemptyset(&sa.sa_mask);
1318 sa.sa_flags = 0;
1320 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1321 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1322 "sigaction(SIGTERM) failed");
1324 /* httpd restart */
1325 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1326 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1327 "sigaction(SIGHUP) failed");
1329 /* httpd graceful restart */
1330 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1331 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1332 "sigaction(SIGUSR1) failed");
1334 /* read messages from request handlers - kill interval expired */
1335 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1336 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1337 "sigaction(SIGALRM) failed");
1339 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1340 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1341 "sigaction(SIGCHLD) failed");
1344 #endif
1346 #ifndef WIN32
1347 int fcgi_pm_main(void *dummy, child_info *info)
1348 #else
1349 void fcgi_pm_main(void *dummy)
1350 #endif
1352 fcgi_server *s;
1353 unsigned int i;
1354 int read_ready = 0;
1355 int alarmLeft = 0;
1357 #ifdef WIN32
1358 DWORD dwRet;
1359 int first_time = 1;
1360 HANDLE wait_thread = INVALID_HANDLE_VALUE;
1361 #else
1362 int callWaitPid, callDynamicProcs;
1363 #endif
1365 #ifdef WIN32
1366 // Add SystemRoot to the dynamic environment
1367 char ** envp = dynamicEnvp;
1368 for (i = 0; *envp; ++i) {
1369 ++envp;
1371 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1373 #else
1374 reduce_privileges();
1376 close(fcgi_pm_pipe[1]);
1377 change_process_name("fcgi-pm");
1378 setup_signals();
1380 if (fcgi_wrapper) {
1381 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1382 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1384 #endif
1386 /* Initialize AppClass */
1387 for (s = fcgi_servers; s != NULL; s = s->next)
1389 if (s->directive != APP_CLASS_STANDARD)
1390 continue;
1392 #ifdef WIN32
1393 if (s->socket_path)
1394 s->listenFd = 0;
1395 #endif
1397 for (i = 0; i < s->numProcesses; ++i)
1398 s->procs[i].state = FCGI_START_STATE;
1401 #ifdef WIN32
1402 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1403 "FastCGI: process manager initialized");
1404 #else
1405 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1406 "FastCGI: process manager initialized (pid %ld)", (long)getpid());
1407 #endif
1409 now = time(NULL);
1412 * Loop until SIGTERM
1414 for (;;) {
1415 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1416 #ifdef WIN32
1417 time_t expire;
1418 #else
1419 pid_t childPid;
1420 int waitStatus;
1421 #endif
1422 unsigned int numChildren;
1425 * If we came out of sigsuspend() for any reason other than
1426 * SIGALRM, pick up where we left off.
1428 if (alarmLeft)
1429 sleepSeconds = alarmLeft;
1432 * Examine each configured AppClass for a process that needs
1433 * starting. Compute the earliest time when the start should
1434 * be attempted, starting it now if the time has passed. Also,
1435 * remember that we do NOT need to restart externally managed
1436 * FastCGI applications.
1438 for (s = fcgi_servers; s != NULL; s = s->next)
1440 if (s->directive == APP_CLASS_EXTERNAL)
1441 continue;
1443 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1444 ? dynamicMaxClassProcs
1445 : s->numProcesses;
1447 for (i = 0; i < numChildren; ++i)
1449 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1451 int restart = (s->procs[i].pid < 0);
1452 time_t restartTime = s->restartTime;
1454 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1456 if (restartTime <= now)
1458 if (s->listenFd < 0 && init_listen_sock(s))
1460 if (sleepSeconds > s->initStartDelay)
1461 sleepSeconds = s->initStartDelay;
1462 break;
1464 #ifndef WIN32
1465 if (caughtSigTerm) {
1466 goto ProcessSigTerm;
1468 #endif
1469 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1470 if (s->procs[i].pid <= 0) {
1471 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1472 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1473 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1474 s->fs_path);
1476 sleepSeconds = min(sleepSeconds,
1477 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1479 ap_assert(s->procs[i].pid < 0);
1480 break;
1483 s->restartTime = now;
1485 if (s->directive == APP_CLASS_DYNAMIC) {
1486 s->numProcesses++;
1487 fcgi_dynamic_total_proc_count++;
1488 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1491 s->procs[i].state = FCGI_RUNNING_STATE;
1493 if (restart)
1494 s->numRestarts++;
1496 if (fcgi_wrapper) {
1497 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1498 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1499 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1500 s->fs_path, (long)s->uid, (long)s->gid,
1501 restart ? "re" : "", (long)s->procs[i].pid);
1503 else {
1504 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1505 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1506 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1507 s->fs_path, restart ? "re" : "", (long)s->procs[i].pid);
1509 ap_assert(s->procs[i].pid > 0);
1510 } else {
1511 sleepSeconds = min(sleepSeconds, restartTime - now);
1517 #ifndef WIN32
1518 if(caughtSigTerm) {
1519 goto ProcessSigTerm;
1521 if((!caughtSigChld) && (!caughtSigAlarm)) {
1522 fd_set rfds;
1524 alarm(sleepSeconds);
1526 FD_ZERO(&rfds);
1527 FD_SET(fcgi_pm_pipe[0], &rfds);
1528 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1530 alarmLeft = alarm(0);
1532 callWaitPid = caughtSigChld;
1533 caughtSigChld = FALSE;
1534 callDynamicProcs = caughtSigAlarm;
1535 caughtSigAlarm = FALSE;
1537 now = time(NULL);
1540 * Dynamic fcgi process management
1542 if((callDynamicProcs) || (!callWaitPid)) {
1543 dynamic_read_msgs(read_ready);
1544 if(fcgi_dynamic_epoch == 0) {
1545 fcgi_dynamic_epoch = now;
1547 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1548 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1549 dynamic_kill_idle_fs_procs();
1550 fcgi_dynamic_epoch = now;
1554 if(!callWaitPid) {
1555 continue;
1558 /* We've caught SIGCHLD, so find out who it was using waitpid,
1559 * write a log message and update its data structure. */
1561 for (;;) {
1562 if (caughtSigTerm)
1563 goto ProcessSigTerm;
1565 childPid = waitpid(-1, &waitStatus, WNOHANG);
1567 if (childPid == -1 || childPid == 0)
1568 break;
1570 for (s = fcgi_servers; s != NULL; s = s->next) {
1571 if (s->directive == APP_CLASS_EXTERNAL)
1572 continue;
1574 if (s->directive == APP_CLASS_DYNAMIC)
1575 numChildren = dynamicMaxClassProcs;
1576 else
1577 numChildren = s->numProcesses;
1579 for (i = 0; i < numChildren; i++) {
1580 if (s->procs[i].pid == childPid)
1581 goto ChildFound;
1585 /* TODO: print something about this unknown child */
1586 continue;
1588 ChildFound:
1589 s->procs[i].pid = -1;
1591 if (s->directive == APP_CLASS_STANDARD) {
1592 /* Always restart static apps */
1593 s->procs[i].state = FCGI_START_STATE;
1594 s->numFailures++;
1596 else {
1597 s->numProcesses--;
1598 fcgi_dynamic_total_proc_count--;
1600 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1601 s->procs[i].state = FCGI_KILLED_STATE;
1603 else {
1604 /* A dynamic app died or exited without provocation from the PM */
1605 s->numFailures++;
1607 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1608 s->procs[i].state = FCGI_START_STATE;
1609 else
1610 s->procs[i].state = FCGI_READY_STATE;
1614 if (WIFEXITED(waitStatus)) {
1615 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1616 "FastCGI:%s server \"%s\" (pid %d) terminated by calling exit with status '%d'",
1617 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1618 s->fs_path, (int)childPid, WEXITSTATUS(waitStatus));
1620 else if (WIFSIGNALED(waitStatus)) {
1621 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1622 "FastCGI:%s server \"%s\" (pid %d) terminated due to uncaught signal '%d' (%s)%s",
1623 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1624 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)],
1625 #ifdef WCOREDUMP
1626 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1627 #else
1628 "");
1629 #endif
1631 else if (WIFSTOPPED(waitStatus)) {
1632 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1633 "FastCGI:%s server \"%s\" (pid %d) stopped due to uncaught signal '%d' (%s)",
1634 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1635 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)]);
1637 } /* for (;;), waitpid() */
1638 #else
1639 if (first_time) {
1640 /* Start the child wait thread */
1641 wait_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)child_wait_thread, NULL, 0, NULL);
1642 first_time = 0;
1645 /* wait for an event to occur or timer expires */
1646 expire = time(NULL) + sleepSeconds;
1647 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1649 if (dwRet == WAIT_FAILED) {
1650 /* There is something seriously wrong here */
1651 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1652 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1653 bTimeToDie = TRUE;
1656 if (dwRet != WAIT_TIMEOUT) {
1657 now = time(NULL);
1659 if (now < expire)
1660 alarmLeft = expire - now;
1664 * Dynamic fcgi process management
1666 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1667 if (dwRet == MBOX_EVENT) {
1668 read_ready = 1;
1671 now = time(NULL);
1673 dynamic_read_msgs(read_ready);
1675 if(fcgi_dynamic_epoch == 0) {
1676 fcgi_dynamic_epoch = now;
1679 if (((long)(now-fcgi_dynamic_epoch) >= (int)dynamicKillInterval) ||
1680 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1681 dynamic_kill_idle_fs_procs();
1682 fcgi_dynamic_epoch = now;
1684 read_ready = 0;
1686 else if (dwRet == WAKE_EVENT) {
1687 continue;
1689 else if (dwRet == TERM_EVENT) {
1690 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1691 "FastCGI: Termination event received process manager shutting down");
1692 bTimeToDie = TRUE;
1694 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1695 goto ProcessSigTerm;
1697 else {
1698 // Have an received an unknown event - should not happen
1699 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1700 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1701 bTimeToDie = TRUE;
1702 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1703 goto ProcessSigTerm;
1705 #endif
1706 } /* for (;;), the whole shoot'n match */
1708 ProcessSigTerm:
1710 * Kill off the children, then exit.
1712 shutdown_all();
1714 #ifdef WIN32
1715 return;
1716 #else
1717 exit(0);
1718 #endif
1721 #ifdef WIN32
1722 int fcgi_pm_add_job(fcgi_pm_job *new_job) {
1724 if (new_job == NULL)
1725 return 0;
1727 if (ap_acquire_mutex(fcgi_dynamic_mbox_mutex) != MULTI_OK) {
1728 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1729 "FastCGI: Failed to aquire the dynamic mbox mutex!");
1732 new_job->next = fcgi_dynamic_mbox;
1733 fcgi_dynamic_mbox = new_job;
1735 if (! ap_release_mutex(fcgi_dynamic_mbox_mutex)) {
1736 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1737 "FastCGI: Failed to release the dynamic mbox mutex!");
1740 return 1;
1742 #endif