fix 'FastCgiConfig -autoUpdate'
[mod_fastcgi.git] / fcgi_pm.c
blobcbf1f8b4637ecbdc1de11e6edc743b5402fb6c31
1 /*
2 * $Id: fcgi_pm.c,v 1.66 2002/02/05 14:13:55 robs Exp $
3 */
6 #include "fcgi.h"
8 #ifdef _HPUX_SOURCE
9 #include <unistd.h>
10 #define seteuid(arg) setresuid(-1, (arg), -1)
11 #endif
13 int fcgi_dynamic_total_proc_count = 0; /* number of running apps */
14 time_t fcgi_dynamic_epoch = 0; /* last time kill_procs was
15 * invoked by process mgr */
16 time_t fcgi_dynamic_last_analyzed = 0; /* last time calculation was
17 * made for the dynamic procs */
19 static time_t now = 0;
21 #ifdef WIN32
22 #pragma warning ( disable : 4100 4102 )
23 static BOOL bTimeToDie = FALSE; /* process termination flag */
24 HANDLE fcgi_event_handles[3];
25 #ifndef SIGKILL
26 #define SIGKILL 9
27 #endif
28 #endif
31 #ifndef WIN32
32 static int seteuid_root(void)
34 int rc = seteuid((uid_t)0);
35 if (rc == -1) {
36 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
37 "FastCGI: seteuid(0) failed");
39 return rc;
42 static int seteuid_user(void)
44 int rc = seteuid(ap_user_id);
45 if (rc == -1) {
46 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
47 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id);
49 return rc;
51 #endif
54 * Signal the process to exit. How (or if) the process responds
55 * depends on the FastCGI application library (esp. on Win32) and
56 * possibly application code (signal handlers and whether or not
57 * SA_RESTART is on). At any rate, we send the signal with the
58 * hopes that the process will exit on its own. Later, as we
59 * review the state of application processes, if we see one marked
60 * for death, but that hasn't died within a specified period of
61 * time, fcgi_kill() is called again with a KILL)
63 static void fcgi_kill(ServerProcess *process, int sig)
65 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process->pid, sig);
67 process->state = FCGI_VICTIM_STATE;
69 #ifdef WIN32
70 if (sig == SIGTERM)
72 SetEvent(process->terminationEvent);
74 else if (sig == SIGKILL)
76 TerminateProcess(process->handle, 1);
78 else
80 ap_assert(0);
82 #else
83 if (fcgi_wrapper)
85 seteuid_root();
88 kill(process->pid, sig);
90 if (fcgi_wrapper)
92 seteuid_user();
94 #endif
97 /*******************************************************************************
98 * Send SIGTERM to each process in the server class, remove socket
99 * file if appropriate. Currently this is only called when the PM is shutting
100 * down and thus memory isn't freed and sockets and files aren't closed.
102 static void shutdown_all()
104 fcgi_server *s = fcgi_servers;
106 while (s)
108 ServerProcess *proc = s->procs;
109 int i;
110 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
111 ? dynamicMaxClassProcs
112 : s->numProcesses;
114 #ifndef WIN32
115 if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL)
117 /* Remove the socket file */
118 if (unlink(s->socket_path) != 0 && errno != ENOENT) {
119 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
120 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
121 s->socket_path,
122 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path);
125 #endif
127 /* Send TERM to all processes */
128 for (i = 0; i < numChildren; i++, proc++)
130 if (proc->state == FCGI_RUNNING_STATE)
132 fcgi_kill(proc, SIGTERM);
136 s = s->next;
140 static int init_listen_sock(fcgi_server * fs)
142 ap_assert(fs->directive != APP_CLASS_EXTERNAL);
144 /* Create the socket */
145 if ((fs->listenFd = socket(fs->socket_addr->sa_family, SOCK_STREAM, 0)) < 0)
147 #ifdef WIN32
148 errno = WSAGetLastError(); // Not sure if this will work as expected
149 #endif
150 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
151 "FastCGI: can't create %sserver \"%s\": socket() failed",
152 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
153 fs->fs_path);
154 return -1;
157 #ifndef WIN32
158 if (fs->socket_addr->sa_family == AF_UNIX)
160 /* Remove any existing socket file.. just in case */
161 unlink(((struct sockaddr_un *)fs->socket_addr)->sun_path);
163 else
164 #endif
166 int flag = 1;
167 setsockopt(fs->listenFd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
170 /* Bind it to the socket_addr */
171 if (bind(fs->listenFd, fs->socket_addr, fs->socket_addr_len))
173 char port[11];
175 #ifdef WIN32
176 errno = WSAGetLastError();
177 #endif
178 ap_snprintf(port, sizeof(port), "port=%d",
179 ((struct sockaddr_in *)fs->socket_addr)->sin_port);
181 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
182 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
183 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
184 fs->fs_path,
185 #ifndef WIN32
186 (fs->socket_addr->sa_family == AF_UNIX) ?
187 ((struct sockaddr_un *)fs->socket_addr)->sun_path :
188 #endif
189 port);
192 #ifndef WIN32
193 /* Twiddle Unix socket permissions */
194 else if (fs->socket_addr->sa_family == AF_UNIX
195 && chmod(((struct sockaddr_un *)fs->socket_addr)->sun_path, S_IRUSR | S_IWUSR))
197 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
198 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
199 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
200 fs->fs_path);
202 #endif
204 /* Set to listen */
205 else if (listen(fs->listenFd, fs->listenQueueDepth))
207 #ifdef WIN32
208 errno = WSAGetLastError();
209 #endif
210 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
211 "FastCGI: can't create %sserver \"%s\": listen() failed",
212 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
213 fs->fs_path);
215 else
217 return 0;
220 ap_pclosesocket(fcgi_config_pool, fs->listenFd);
221 fs->listenFd = -1;
222 return -2;
226 *----------------------------------------------------------------------
228 * pm_main
230 * The FastCGI process manager, which runs as a separate
231 * process responsible for:
232 * - Starting all the FastCGI proceses.
233 * - Restarting any of these processes that die (indicated
234 * by SIGCHLD).
235 * - Catching SIGTERM and relaying it to all the FastCGI
236 * processes before exiting.
238 * Inputs:
239 * Uses global variable fcgi_servers.
241 * Results:
242 * Does not return.
244 * Side effects:
245 * Described above.
247 *----------------------------------------------------------------------
249 #ifndef WIN32
250 static int caughtSigTerm = FALSE;
251 static int caughtSigChld = FALSE;
252 static int caughtSigAlarm = FALSE;
254 static void signal_handler(int signo)
256 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
257 /* SIGUSR1 & SIGHUP are sent by apache to its process group
258 * when apache get 'em. Apache follows up (1.2.x) with attacks
259 * on each of its child processes, but we've got the KillMgr
260 * sitting between us so we never see the KILL. The main loop
261 * in ProcMgr also checks to see if the KillMgr has terminated,
262 * and if it has, we handl it as if we should shutdown too. */
263 caughtSigTerm = TRUE;
264 } else if(signo == SIGCHLD) {
265 caughtSigChld = TRUE;
266 } else if(signo == SIGALRM) {
267 caughtSigAlarm = TRUE;
270 #endif
273 *----------------------------------------------------------------------
275 * spawn_fs_process --
277 * Fork and exec the specified fcgi process.
279 * Results:
280 * 0 for successful fork, -1 for failed fork.
282 * In case the child fails before or in the exec, the child
283 * obtains the error log by calling getErrLog, logs
284 * the error, and exits with exit status = errno of
285 * the failed system call.
287 * Side effects:
288 * Child process created.
290 *----------------------------------------------------------------------
292 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
294 #ifndef WIN32
295 pid_t child_pid;
296 int i;
297 char *dirName;
298 char *dnEnd, *failedSysCall;
300 child_pid = fork();
301 if (child_pid) {
302 return child_pid;
305 /* We're the child. We're gonna exec() so pools don't matter. */
307 dnEnd = strrchr(fs->fs_path, '/');
308 if (dnEnd == NULL) {
309 dirName = "./";
310 } else {
311 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
312 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
314 if (chdir(dirName) < 0) {
315 failedSysCall = "chdir()";
316 goto FailedSystemCallExit;
319 #ifndef __EMX__
320 /* OS/2 dosen't support nice() */
321 if (fs->processPriority != 0) {
322 if (nice(fs->processPriority) == -1) {
323 failedSysCall = "nice()";
324 goto FailedSystemCallExit;
327 #endif
329 /* Open the listenFd on spec'd fd */
330 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
331 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
333 /* Close all other open fds, except stdout/stderr. Leave these two open so
334 * FastCGI applications don't have to find and fix ALL 3rd party libs that
335 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
336 * main server error_log - @@@ provide a directive control where this goes.
338 ap_error_log2stderr(fcgi_apache_main_server);
339 dup2(STDERR_FILENO, STDOUT_FILENO);
340 for (i = 0; i < FCGI_MAX_FD; i++) {
341 if (i != FCGI_LISTENSOCK_FILENO && i != STDERR_FILENO && i != STDOUT_FILENO) {
342 close(i);
346 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
347 * install its own handler. */
348 signal(SIGPIPE, SIG_IGN);
350 if (fcgi_wrapper && (fcgi_user_id != fs->uid || fcgi_group_id != fs->gid)) {
351 char *shortName = strrchr(fs->fs_path, '/') + 1;
353 /* Relinquish our root real uid powers */
354 seteuid_root();
355 setuid(ap_user_id);
357 do {
358 execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group, shortName, NULL, fs->envp);
359 } while (errno == EINTR);
361 else {
362 do {
363 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
364 } while (errno == EINTR);
367 failedSysCall = "execle()";
369 FailedSystemCallExit:
370 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
371 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
372 exit(-1);
374 /* avoid an irrelevant compiler warning */
375 return(0);
377 #else
379 /* Adapted from Apache's util_script.c ap_call_exec() */
380 char *interpreter = NULL;
381 char *quoted_filename;
382 char *pCommand;
383 char *pEnvBlock, *pNext;
385 int i = 0;
386 int iEnvBlockLen = 1;
388 file_type_e fileType;
390 STARTUPINFO si;
391 PROCESS_INFORMATION pi;
393 request_rec r;
394 pid_t pid = -1;
396 pool * tp = ap_make_sub_pool(fcgi_config_pool);
398 HANDLE listen_handle;
399 char * termination_env_string = NULL;
401 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
402 if (process->terminationEvent == NULL)
404 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
405 "FastCGI: can't create termination event for server \"%s\", "
406 "CreateEvent() failed", fs->fs_path);
407 exit(0);
409 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
411 termination_env_string = ap_psprintf(tp,
412 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
414 if (fs->socket_path)
416 SECURITY_ATTRIBUTES sa;
418 sa.lpSecurityDescriptor = NULL;
419 sa.bInheritHandle = TRUE;
420 sa.nLength = sizeof(sa);
422 listen_handle = CreateNamedPipe(fs->socket_path,
423 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
424 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
425 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
427 if (listen_handle == INVALID_HANDLE_VALUE)
429 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
430 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
431 exit(0);
434 // SetHandleInformation(listen_handle, HANDLE_FLAG_INHERIT, TRUE);
436 else
438 listen_handle = (HANDLE) fs->listenFd;
441 memset(&si, 0, sizeof(si));
442 memset(&pi, 0, sizeof(pi));
443 memset(&r, 0, sizeof(r));
445 // Can up a fake request to pass to ap_get_win32_interpreter()
446 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
447 r.server = fcgi_apache_main_server;
448 r.filename = (char *) fs->fs_path;
449 r.pool = tp;
451 fileType = ap_get_win32_interpreter(&r, &interpreter);
453 if (fileType == eFileTypeUNKNOWN) {
454 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
455 "FastCGI: %s is not executable; ensure interpreted scripts have "
456 "\"#!\" as their first line",
457 fs->fs_path);
458 ap_destroy_pool(tp);
459 return (pid);
463 * We have the interpreter (if there is one) and we have
464 * the arguments (if there are any).
465 * Build the command string to pass to CreateProcess.
467 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
468 if (interpreter && *interpreter) {
469 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
471 else {
472 pCommand = quoted_filename;
476 * Make child process use hPipeOutputWrite as standard out,
477 * and make sure it does not show on screen.
479 si.cb = sizeof(si);
480 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
481 si.wShowWindow = SW_HIDE;
482 si.hStdInput = listen_handle;
484 // XXX These should be open to the error_log
485 si.hStdOutput = INVALID_HANDLE_VALUE;
486 si.hStdError = INVALID_HANDLE_VALUE;
489 * Win32's CreateProcess call requires that the environment
490 * be passed in an environment block, a null terminated block of
491 * null terminated strings.
492 * @todo we should store the env in this format for win32.
494 while (fs->envp[i])
496 iEnvBlockLen += strlen(fs->envp[i]) + 1;
497 i++;
500 iEnvBlockLen += strlen(termination_env_string) + 1;
501 iEnvBlockLen += strlen(fs->mutex_env_string) + 1;
503 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
505 i = 0;
506 pNext = pEnvBlock;
507 while (fs->envp[i])
509 strcpy(pNext, fs->envp[i]);
510 pNext += strlen(pNext) + 1;
511 i++;
514 strcpy(pNext, termination_env_string);
515 pNext += strlen(pNext) + 1;
516 strcpy(pNext, fs->mutex_env_string);
518 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
520 pEnvBlock,
521 ap_make_dirstr_parent(tp, fs->fs_path),
522 &si, &pi))
524 /* Hack to get 16-bit CGI's working. It works for all the
525 * standard modules shipped with Apache. pi.dwProcessId is 0
526 * for 16-bit CGIs and all the Unix specific code that calls
527 * ap_call_exec interprets this as a failure case. And we can't
528 * use -1 either because it is mapped to 0 by the caller.
530 pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId;
532 process->handle = pi.hProcess;
533 CloseHandle(pi.hThread);
536 if (fs->socket_path)
538 CloseHandle(listen_handle);
541 ap_destroy_pool(tp);
543 return pid;
545 #endif
548 #ifndef WIN32
549 static void reduce_privileges(void)
551 char *name;
553 if (geteuid() != 0)
554 return;
556 #ifndef __EMX__
557 /* Get username if passed as a uid */
558 if (ap_user_name[0] == '#') {
559 uid_t uid = atoi(&ap_user_name[1]);
560 struct passwd *ent = getpwuid(uid);
562 if (ent == NULL) {
563 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
564 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
565 "you probably need to modify the User directive", (unsigned)uid);
566 exit(1);
568 name = ent->pw_name;
570 else
571 name = ap_user_name;
573 /* Change Group */
574 if (setgid(ap_group_id) == -1) {
575 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
576 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
577 exit(1);
580 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
582 /* Initialize supplementary groups */
583 if (initgroups(name, ap_group_id) == -1) {
584 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
585 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
586 name, (unsigned)ap_group_id);
587 exit(1);
589 #endif /* __EMX__ */
591 /* Change User */
592 if (fcgi_wrapper) {
593 if (seteuid_user() == -1) {
594 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
595 "FastCGI: process manager exiting, failed to reduce privileges");
596 exit(1);
599 else {
600 if (setuid(ap_user_id) == -1) {
601 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
602 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
603 exit(1);
608 /*************
609 * Change the name of this process - best we can easily.
611 static void change_process_name(const char * const name)
613 strncpy(ap_server_argv0, name, strlen(ap_server_argv0));
615 #endif
617 static void schedule_start(fcgi_server *s, int proc)
619 /* If we've started one recently, don't register another */
620 time_t time_passed = now - s->restartTime;
622 if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay))
623 || ((s->procs[proc].pid == 0) && (time_passed < s->initStartDelay)))
625 FCGIDBG6("ignore_job: slot=%d, pid=%ld, time_passed=%ld, initStartDelay=%ld, restartDelay=%ld", proc, (long) s->procs[proc].pid, time_passed, s->initStartDelay, s->restartDelay);
626 return;
629 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
630 s->procs[proc].state = FCGI_START_STATE;
631 if (proc == dynamicMaxClassProcs - 1) {
632 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
633 "FastCGI: scheduled the %sstart of the last (dynamic) server "
634 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
635 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
640 *----------------------------------------------------------------------
642 * dynamic_read_msgs
644 * Removes the records written by request handlers and decodes them.
645 * We also update the data structures to reflect the changes.
647 *----------------------------------------------------------------------
650 static void dynamic_read_msgs(int read_ready)
652 fcgi_server *s;
653 int rc;
655 #ifndef WIN32
656 static int buflen = 0;
657 static char buf[FCGI_MSGS_BUFSIZE + 1];
658 char *ptr1, *ptr2, opcode;
659 char execName[FCGI_MAXPATH + 1];
660 char user[MAX_USER_NAME_LEN + 2];
661 char group[MAX_GID_CHAR_LEN + 1];
662 unsigned long q_usec = 0UL, req_usec = 0UL;
663 #else
664 fcgi_pm_job *joblist = NULL;
665 fcgi_pm_job *cjob = NULL;
666 #endif
668 pool *sp = NULL, *tp;
670 #ifndef WIN32
671 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
672 #endif
675 * To prevent the idle application from running indefinitely, we
676 * check the timer and if it is expired, we recompute the values
677 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
678 * message is received, only updates are made to the data structures.
680 if (fcgi_dynamic_last_analyzed == 0) {
681 fcgi_dynamic_last_analyzed = now;
683 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
684 for (s = fcgi_servers; s != NULL; s = s->next) {
685 if (s->directive != APP_CLASS_DYNAMIC)
686 break;
688 /* Advance the last analyzed timestamp by the elapsed time since
689 * it was last set. Round the increase down to the nearest
690 * multiple of dynamicUpdateInterval */
692 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
693 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
694 s->totalConnTime = 0UL;
695 s->totalQueueTime = 0UL;
699 if (read_ready <= 0) {
700 return;
703 #ifndef WIN32
704 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
705 if (rc <= 0) {
706 if (!caughtSigTerm) {
707 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
708 "FastCGI: read() from pipe failed (%d)", rc);
709 if (rc == 0) {
710 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
711 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
712 caughtSigTerm = TRUE;
715 return;
717 buflen += rc;
718 buf[buflen] = '\0';
720 #else
722 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
723 * request to do something) and/or when a timeout expires.
724 * There really should be no reason why this wait would get stuck
725 * but there's no point in waiting forever. */
727 rc = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
729 if (rc != WAIT_OBJECT_0 && rc != WAIT_ABANDONED)
731 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
732 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
733 return;
736 joblist = fcgi_dynamic_mbox;
737 fcgi_dynamic_mbox = NULL;
739 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
741 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
742 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
745 cjob = joblist;
746 #endif
748 tp = ap_make_sub_pool(fcgi_config_pool);
750 #ifndef WIN32
751 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
752 int scan_failed = 0;
754 ptr2 = strchr(ptr1, '*');
755 if (ptr2) {
756 *ptr2++ = '\0';
758 else {
759 break;
762 opcode = *ptr1;
764 switch (opcode)
766 case FCGI_SERVER_START_JOB:
767 case FCGI_SERVER_RESTART_JOB:
769 if (sscanf(ptr1, "%c %s %16s %15s",
770 &opcode, execName, user, group) != 4)
772 scan_failed = 1;
774 break;
776 case FCGI_REQUEST_TIMEOUT_JOB:
778 if (sscanf(ptr1, "%c %s %16s %15s",
779 &opcode, execName, user, group) != 4)
781 scan_failed = 1;
783 break;
785 case FCGI_REQUEST_COMPLETE_JOB:
787 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
788 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
790 scan_failed = 1;
792 break;
794 default:
796 scan_failed = 1;
797 break;
800 if (scan_failed) {
801 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
802 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
803 goto NextJob;
805 #else
806 /* Update data structures for processing */
807 while (cjob != NULL) {
808 joblist = cjob->next;
809 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
810 #endif
812 #ifndef WIN32
813 s = fcgi_util_fs_get(execName, user, group);
814 #else
815 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
816 #endif
818 #ifndef WIN32
819 if (s==NULL && opcode != FCGI_REQUEST_COMPLETE_JOB)
820 #else
821 if (s==NULL && cjob->id != FCGI_REQUEST_COMPLETE_JOB)
822 #endif
824 #ifdef WIN32
826 HANDLE mutex = CreateMutex(NULL, FALSE, "cjob->fs_path");
828 if (mutex == NULL)
830 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
831 "FastCGI: can't create accept mutex "
832 "for (dynamic) server \"%s\"", cjob->fs_path);
833 goto BagNewServer;
836 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
837 #else
838 const char *err;
839 #endif
841 /* Create a perm subpool to hold the new server data,
842 * we can destroy it if something doesn't pan out */
843 sp = ap_make_sub_pool(fcgi_config_pool);
845 /* Create a new "dynamic" server */
846 s = fcgi_util_fs_new(sp);
848 s->directive = APP_CLASS_DYNAMIC;
849 s->restartDelay = dynamicRestartDelay;
850 s->listenQueueDepth = dynamicListenQueueDepth;
851 s->initStartDelay = dynamicInitStartDelay;
852 s->envp = dynamicEnvp;
853 s->flush = dynamicFlush;
855 #ifdef WIN32
856 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
857 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
858 #else
859 s->fs_path = ap_pstrdup(sp, execName);
860 #endif
861 ap_getparents(s->fs_path);
862 ap_no2slash(s->fs_path);
863 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
865 /* XXX the socket_path (both Unix and Win) *is* deducible and
866 * thus can and will be used by other apache instances without
867 * the use of shared data regarding the processes serving the
868 * requests. This can result in slightly unintuitive process
869 * counts and security implications. This is prevented
870 * if suexec (Unix) is in use. This is both a feature and a flaw.
871 * Changing it now would break existing installations. */
873 #ifndef WIN32
874 /* Create socket file's path */
875 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
876 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
878 /* Create sockaddr, prealloc it so it won't get created in tp */
879 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
880 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
881 &s->socket_addr_len, s->socket_path);
882 if (err) {
883 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
884 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
885 goto BagNewServer;
888 if (init_listen_sock(s)) {
889 goto BagNewServer;
892 /* If a wrapper is being used, config user/group info */
893 if (fcgi_wrapper) {
894 if (user[0] == '~') {
895 /* its a user dir uri, the rest is a username, not a uid */
896 struct passwd *pw = getpwnam(&user[1]);
898 if (!pw) {
899 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
900 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
901 execName, &user[1]);
902 goto BagNewServer;
904 s->uid = pw->pw_uid;
905 s->user = ap_pstrdup(sp, user);
906 s->username = s->user;
908 s->gid = pw->pw_gid;
909 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
911 else {
912 struct passwd *pw;
914 s->uid = (uid_t)atol(user);
915 pw = getpwuid(s->uid);
916 if (!pw) {
917 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
918 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
919 execName, (long)s->uid);
920 goto BagNewServer;
922 s->user = ap_pstrdup(sp, user);
923 s->username = ap_pstrdup(sp, pw->pw_name);
925 s->gid = (gid_t)atol(group);
926 s->group = ap_pstrdup(sp, group);
929 #else
930 /* Create socket file's path */
931 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
932 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
933 s->listenFd = 0;
934 #endif
936 fcgi_util_fs_add(s);
938 else {
939 #ifndef WIN32
940 if (opcode == FCGI_SERVER_RESTART_JOB) {
941 #else
942 if (cjob->id==FCGI_SERVER_RESTART_JOB) {
943 #endif
944 /* Check to see if the binary has changed. If so,
945 * kill the FCGI application processes, and
946 * restart them.
948 struct stat stbuf;
949 int i;
951 #ifndef WIN32
952 if ((stat(execName, &stbuf)==0) &&
953 #else
954 if ((stat(cjob->fs_path, &stbuf)==0) &&
955 #endif
956 (stbuf.st_mtime > s->restartTime)) {
957 /* kill old server(s) */
958 for (i = 0; i < dynamicMaxClassProcs; i++) {
959 if (s->procs[i].pid > 0) {
960 fcgi_kill(&s->procs[i], SIGTERM);
964 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
965 "FastCGI: restarting server \"%s\" processes, newer version found",
966 #ifndef WIN32
967 execName);
968 #else
969 cjob->fs_path);
970 #endif
973 /* If dynamicAutoRestart, don't mark any new processes
974 * for starting because we probably got the
975 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
976 * will be restarting all of those we just killed.
978 if (dynamicAutoRestart)
979 goto NextJob;
981 #ifndef WIN32
982 else if (opcode == FCGI_SERVER_START_JOB) {
983 #else
984 else if (cjob->id==FCGI_SERVER_START_JOB) {
985 #endif
986 /* we've been asked to start a process--only start
987 * it if we're not already running at least one
988 * instance.
990 int i;
992 for (i = 0; i < dynamicMaxClassProcs; i++) {
993 if (s->procs[i].state == FCGI_RUNNING_STATE)
994 break;
996 /* if already running, don't start another one */
997 if (i < dynamicMaxClassProcs) {
998 goto NextJob;
1003 #ifndef WIN32
1004 switch (opcode)
1005 #else
1006 switch (cjob->id)
1007 #endif
1009 int i, start;
1011 case FCGI_SERVER_RESTART_JOB:
1013 start = FALSE;
1015 /* We just waxed 'em all. Try to find an idle slot. */
1017 for (i = 0; i < dynamicMaxClassProcs; ++i)
1019 if (s->procs[i].state == FCGI_START_STATE
1020 || s->procs[i].state == FCGI_RUNNING_STATE)
1022 break;
1024 else if (s->procs[i].state == FCGI_KILLED_STATE
1025 || s->procs[i].state == FCGI_READY_STATE)
1027 start = TRUE;
1028 break;
1032 /* Nope, just use the first slot */
1033 if (i == dynamicMaxClassProcs)
1035 start = TRUE;
1036 i = 0;
1039 if (start)
1041 schedule_start(s, i);
1044 break;
1046 case FCGI_SERVER_START_JOB:
1047 case FCGI_REQUEST_TIMEOUT_JOB:
1049 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1051 * Extra instances should have been
1052 * terminated beforehand, probably need
1053 * to increase ProcessSlack parameter
1055 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1056 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1057 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1058 goto NextJob;
1061 /* find next free slot */
1062 for (i = 0; i < dynamicMaxClassProcs; i++)
1064 if (s->procs[i].state == FCGI_START_STATE)
1066 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1067 break;
1069 else if (s->procs[i].state == FCGI_RUNNING_STATE)
1071 continue;
1074 schedule_start(s, i);
1075 break;
1078 #ifdef FCGI_DEBUG
1079 if (i >= dynamicMaxClassProcs) {
1080 FCGIDBG1("ignore_job: slots are max'd");
1082 #endif
1083 break;
1084 case FCGI_REQUEST_COMPLETE_JOB:
1085 /* only record stats if we have a structure */
1086 if (s) {
1087 #ifndef WIN32
1088 s->totalConnTime += req_usec;
1089 s->totalQueueTime += q_usec;
1090 #else
1091 s->totalConnTime += cjob->start_time;
1092 s->totalQueueTime += cjob->qsec;
1093 #endif
1095 break;
1098 NextJob:
1100 #ifdef WIN32
1101 /* Cleanup job data */
1102 free(cjob->fs_path);
1103 free(cjob->user);
1104 free(cjob->group);
1105 free(cjob);
1106 cjob = joblist;
1107 #endif
1109 continue;
1111 BagNewServer:
1112 if (sp) ap_destroy_pool(sp);
1114 #ifdef WIN32
1115 free(cjob->fs_path);
1116 free(cjob);
1117 cjob = joblist;
1118 #endif
1121 #ifndef WIN32
1122 if (ptr1 == buf) {
1123 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1124 "FastCGI: really bogus message: \"%s\"", ptr1);
1125 ptr1 += strlen(buf);
1128 buflen -= ptr1 - buf;
1129 if (buflen) {
1130 memmove(buf, ptr1, buflen);
1132 #endif
1134 ap_destroy_pool(tp);
1138 *----------------------------------------------------------------------
1140 * dynamic_kill_idle_fs_procs
1142 * Implement a kill policy for the dynamic FastCGI applications.
1143 * We also update the data structures to reflect the changes.
1145 * Side effects:
1146 * Processes are marked for deletion possibly killed.
1148 *----------------------------------------------------------------------
1150 static void dynamic_kill_idle_fs_procs(void)
1152 fcgi_server *s;
1153 int victims = 0;
1155 for (s = fcgi_servers; s != NULL; s = s->next)
1158 * server's smoothed running time, or if that's 0, the current total
1160 unsigned long connTime;
1163 * maximum number of microseconds that all of a server's running
1164 * processes together could have spent running since the last check
1166 unsigned long totalTime;
1169 * percentage, 0-100, of totalTime that the processes actually used
1171 int loadFactor;
1173 int i;
1174 int really_running = 0;
1176 if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1178 continue;
1181 /* s->numProcesses includes pending kills so get the "active" count */
1182 for (i = 0; i < dynamicMaxClassProcs; ++i)
1184 if (s->procs[i].state == FCGI_RUNNING_STATE) ++really_running;
1187 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1188 totalTime = really_running * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1190 loadFactor = 100 * connTime / totalTime;
1192 if (really_running == 1)
1194 if (loadFactor >= dynamicThreshold1)
1196 continue;
1199 else
1201 int load = really_running / ( really_running - 1) * loadFactor;
1203 if (load >= dynamicThresholdN)
1205 continue;
1210 * Run through the procs to see if we can get away w/o waxing one.
1212 for (i = 0; i < dynamicMaxClassProcs; ++i)
1214 if (s->procs[i].state == FCGI_START_STATE)
1216 s->procs[i].state = FCGI_READY_STATE;
1217 break;
1219 else if (s->procs[i].state == FCGI_VICTIM_STATE)
1221 break;
1225 if (i >= dynamicMaxClassProcs)
1227 ServerProcess * procs = s->procs;
1228 int youngest = -1;
1230 for (i = 0; i < dynamicMaxClassProcs; ++i)
1232 if (procs[i].state == FCGI_RUNNING_STATE)
1234 if (youngest == -1 || procs[i].start_time >= procs[youngest].start_time)
1236 youngest = i;
1241 if (youngest != -1)
1243 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1244 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1245 s->fs_path, (long) s->procs[youngest].pid);
1247 fcgi_kill(&s->procs[youngest], SIGTERM);
1249 victims++;
1253 * If the number of non-victims is less than or equal to
1254 * the minimum that may be running without being killed off,
1255 * don't select any more victims.
1257 if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1259 break;
1265 #ifdef WIN32
1267 // This is a little bogus, there's gotta be a better way to do this
1268 // Can we use WaitForMultipleObjects()
1269 #define FCGI_PROC_WAIT_TIME 100
1271 void child_wait_thread_main(void *dummy) {
1272 fcgi_server *s;
1273 DWORD dwRet = WAIT_TIMEOUT;
1274 int numChildren;
1275 int i;
1276 int waited;
1278 while (!bTimeToDie) {
1279 waited = 0;
1281 for (s = fcgi_servers; s != NULL; s = s->next) {
1282 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1283 continue;
1285 if (s->directive == APP_CLASS_DYNAMIC) {
1286 numChildren = dynamicMaxClassProcs;
1288 else {
1289 numChildren = s->numProcesses;
1292 for (i=0; i < numChildren; i++) {
1293 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1295 DWORD exitStatus = 0;
1297 /* timeout is currently set for 100 miliecond */
1298 /* it may need to be longer or user customizable */
1299 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1301 waited = 1;
1303 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1304 /* a child fs has died */
1305 /* mark the child as dead */
1307 if (s->directive == APP_CLASS_STANDARD) {
1308 /* restart static app */
1309 s->procs[i].state = FCGI_START_STATE;
1310 s->numFailures++;
1312 else {
1313 s->numProcesses--;
1314 fcgi_dynamic_total_proc_count--;
1315 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1317 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1318 s->procs[i].state = FCGI_KILLED_STATE;
1320 else {
1321 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1322 s->numFailures++;
1324 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1325 s->procs[i].state = FCGI_START_STATE;
1327 else {
1328 s->procs[i].state = FCGI_READY_STATE;
1333 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1335 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1336 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1337 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1338 s->fs_path, (long) s->procs[i].pid, exitStatus);
1340 CloseHandle(s->procs[i].handle);
1341 s->procs[i].handle = INVALID_HANDLE_VALUE;
1342 s->procs[i].pid = -1;
1344 /* wake up the main thread */
1345 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1350 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1353 #endif
1355 #ifndef WIN32
1356 static void setup_signals(void)
1358 struct sigaction sa;
1360 /* Setup handlers */
1362 sa.sa_handler = signal_handler;
1363 sigemptyset(&sa.sa_mask);
1364 sa.sa_flags = 0;
1366 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1367 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1368 "sigaction(SIGTERM) failed");
1370 /* httpd restart */
1371 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1372 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1373 "sigaction(SIGHUP) failed");
1375 /* httpd graceful restart */
1376 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1377 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1378 "sigaction(SIGUSR1) failed");
1380 /* read messages from request handlers - kill interval expired */
1381 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1382 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1383 "sigaction(SIGALRM) failed");
1385 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1386 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1387 "sigaction(SIGCHLD) failed");
1390 #endif
1392 #ifndef WIN32
1393 int fcgi_pm_main(void *dummy, child_info *info)
1394 #else
1395 void fcgi_pm_main(void *dummy)
1396 #endif
1398 fcgi_server *s;
1399 unsigned int i;
1400 int read_ready = 0;
1401 int alarmLeft = 0;
1403 #ifdef WIN32
1404 DWORD dwRet;
1405 HANDLE child_wait_thread = INVALID_HANDLE_VALUE;
1406 #else
1407 int callWaitPid, callDynamicProcs;
1408 #endif
1410 #ifdef WIN32
1411 // Add SystemRoot to the dynamic environment
1412 char ** envp = dynamicEnvp;
1413 for (i = 0; *envp; ++i) {
1414 ++envp;
1416 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1418 #else
1419 reduce_privileges();
1421 close(fcgi_pm_pipe[1]);
1422 change_process_name("fcgi-pm");
1423 setup_signals();
1425 if (fcgi_wrapper) {
1426 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1427 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1429 #endif
1431 /* Initialize AppClass */
1432 for (s = fcgi_servers; s != NULL; s = s->next)
1434 if (s->directive != APP_CLASS_STANDARD)
1435 continue;
1437 #ifdef WIN32
1438 if (s->socket_path)
1439 s->listenFd = 0;
1440 #endif
1442 for (i = 0; i < s->numProcesses; ++i)
1443 s->procs[i].state = FCGI_START_STATE;
1446 #ifdef WIN32
1447 child_wait_thread = CreateThread(NULL, 0,
1448 (LPTHREAD_START_ROUTINE) child_wait_thread_main, NULL, 0, NULL);
1449 if (child_wait_thread == NULL)
1451 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1452 "FastCGI: failed to create process manager's wait thread!");
1455 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1456 "FastCGI: process manager initialized");
1457 #else
1458 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1459 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1460 #endif
1462 now = time(NULL);
1465 * Loop until SIGTERM
1467 for (;;) {
1468 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1469 #ifdef WIN32
1470 time_t expire;
1471 #else
1472 pid_t childPid;
1473 int waitStatus;
1474 #endif
1475 unsigned int numChildren;
1478 * If we came out of sigsuspend() for any reason other than
1479 * SIGALRM, pick up where we left off.
1481 if (alarmLeft)
1482 sleepSeconds = alarmLeft;
1485 * Examine each configured AppClass for a process that needs
1486 * starting. Compute the earliest time when the start should
1487 * be attempted, starting it now if the time has passed. Also,
1488 * remember that we do NOT need to restart externally managed
1489 * FastCGI applications.
1491 for (s = fcgi_servers; s != NULL; s = s->next)
1493 if (s->directive == APP_CLASS_EXTERNAL)
1494 continue;
1496 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1497 ? dynamicMaxClassProcs
1498 : s->numProcesses;
1500 for (i = 0; i < numChildren; ++i)
1502 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1504 int restart = (s->procs[i].pid < 0);
1505 time_t restartTime = s->restartTime;
1507 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1509 if (restartTime <= now)
1511 if (s->listenFd < 0 && init_listen_sock(s))
1513 if (sleepSeconds > s->initStartDelay)
1514 sleepSeconds = s->initStartDelay;
1515 break;
1517 #ifndef WIN32
1518 if (caughtSigTerm) {
1519 goto ProcessSigTerm;
1521 #endif
1522 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1523 if (s->procs[i].pid <= 0) {
1524 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1525 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1526 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1527 s->fs_path);
1529 sleepSeconds = min(sleepSeconds,
1530 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1532 ap_assert(s->procs[i].pid < 0);
1533 break;
1536 s->procs[i].start_time = now;
1537 s->restartTime = now;
1539 if (s->directive == APP_CLASS_DYNAMIC) {
1540 s->numProcesses++;
1541 fcgi_dynamic_total_proc_count++;
1542 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1545 s->procs[i].state = FCGI_RUNNING_STATE;
1547 if (restart)
1548 s->numRestarts++;
1550 if (fcgi_wrapper) {
1551 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1552 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1553 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1554 s->fs_path, (long) s->uid, (long) s->gid,
1555 restart ? "re" : "", (long) s->procs[i].pid);
1557 else {
1558 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1559 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1560 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1561 s->fs_path, restart ? "re" : "", (long) s->procs[i].pid);
1563 ap_assert(s->procs[i].pid > 0);
1564 } else {
1565 sleepSeconds = min(sleepSeconds, restartTime - now);
1571 #ifndef WIN32
1573 if(caughtSigTerm) {
1574 goto ProcessSigTerm;
1576 if((!caughtSigChld) && (!caughtSigAlarm)) {
1577 fd_set rfds;
1579 alarm(sleepSeconds);
1581 FD_ZERO(&rfds);
1582 FD_SET(fcgi_pm_pipe[0], &rfds);
1583 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1585 alarmLeft = alarm(0);
1587 callWaitPid = caughtSigChld;
1588 caughtSigChld = FALSE;
1589 callDynamicProcs = caughtSigAlarm;
1590 caughtSigAlarm = FALSE;
1592 now = time(NULL);
1595 * Dynamic fcgi process management
1597 if((callDynamicProcs) || (!callWaitPid)) {
1598 dynamic_read_msgs(read_ready);
1599 if(fcgi_dynamic_epoch == 0) {
1600 fcgi_dynamic_epoch = now;
1602 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1603 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1604 dynamic_kill_idle_fs_procs();
1605 fcgi_dynamic_epoch = now;
1609 if(!callWaitPid) {
1610 continue;
1613 /* We've caught SIGCHLD, so find out who it was using waitpid,
1614 * write a log message and update its data structure. */
1616 for (;;) {
1617 if (caughtSigTerm)
1618 goto ProcessSigTerm;
1620 childPid = waitpid(-1, &waitStatus, WNOHANG);
1622 if (childPid == -1 || childPid == 0)
1623 break;
1625 for (s = fcgi_servers; s != NULL; s = s->next) {
1626 if (s->directive == APP_CLASS_EXTERNAL)
1627 continue;
1629 if (s->directive == APP_CLASS_DYNAMIC)
1630 numChildren = dynamicMaxClassProcs;
1631 else
1632 numChildren = s->numProcesses;
1634 for (i = 0; i < numChildren; i++) {
1635 if (s->procs[i].pid == childPid)
1636 goto ChildFound;
1640 /* TODO: print something about this unknown child */
1641 continue;
1643 ChildFound:
1644 s->procs[i].pid = -1;
1646 if (s->directive == APP_CLASS_STANDARD) {
1647 /* Always restart static apps */
1648 s->procs[i].state = FCGI_START_STATE;
1649 s->numFailures++;
1651 else {
1652 s->numProcesses--;
1653 fcgi_dynamic_total_proc_count--;
1655 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1656 s->procs[i].state = FCGI_KILLED_STATE;
1658 else {
1659 /* A dynamic app died or exited without provocation from the PM */
1660 s->numFailures++;
1662 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1663 s->procs[i].state = FCGI_START_STATE;
1664 else
1665 s->procs[i].state = FCGI_READY_STATE;
1669 if (WIFEXITED(waitStatus)) {
1670 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1671 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1672 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1673 s->fs_path, (long) childPid, WEXITSTATUS(waitStatus));
1675 else if (WIFSIGNALED(waitStatus)) {
1676 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1677 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1678 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1679 s->fs_path, (long) childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)],
1680 #ifdef WCOREDUMP
1681 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1682 #else
1683 "");
1684 #endif
1686 else if (WIFSTOPPED(waitStatus)) {
1687 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1688 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1689 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1690 s->fs_path, (long) childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)]);
1692 } /* for (;;), waitpid() */
1694 #else /* WIN32 */
1696 /* wait for an event to occur or timer expires */
1697 expire = time(NULL) + sleepSeconds;
1698 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1700 if (dwRet == WAIT_FAILED) {
1701 /* There is something seriously wrong here */
1702 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1703 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1704 bTimeToDie = TRUE;
1707 if (dwRet != WAIT_TIMEOUT) {
1708 now = time(NULL);
1710 if (now < expire)
1711 alarmLeft = expire - now;
1715 * Dynamic fcgi process management
1717 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1718 if (dwRet == MBOX_EVENT) {
1719 read_ready = 1;
1722 now = time(NULL);
1724 dynamic_read_msgs(read_ready);
1726 if(fcgi_dynamic_epoch == 0) {
1727 fcgi_dynamic_epoch = now;
1730 if ((now-fcgi_dynamic_epoch >= (int) dynamicKillInterval) ||
1731 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1732 dynamic_kill_idle_fs_procs();
1733 fcgi_dynamic_epoch = now;
1735 read_ready = 0;
1737 else if (dwRet == WAKE_EVENT) {
1738 continue;
1740 else if (dwRet == TERM_EVENT) {
1741 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1742 "FastCGI: Termination event received process manager shutting down");
1744 bTimeToDie = TRUE;
1745 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
1747 goto ProcessSigTerm;
1749 else {
1750 // Have an received an unknown event - should not happen
1751 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1752 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1754 bTimeToDie = TRUE;
1755 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
1757 goto ProcessSigTerm;
1760 #endif /* WIN32 */
1762 } /* for (;;), the whole shoot'n match */
1764 ProcessSigTerm:
1766 * Kill off the children, then exit.
1768 shutdown_all();
1770 #ifdef WIN32
1771 return;
1772 #else
1773 exit(0);
1774 #endif
1777 #ifdef WIN32
1778 int fcgi_pm_add_job(fcgi_pm_job *new_job)
1780 int rv = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
1782 if (rv != WAIT_OBJECT_0 && rv != WAIT_ABANDONED)
1784 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1785 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
1786 return -1;
1789 new_job->next = fcgi_dynamic_mbox;
1790 fcgi_dynamic_mbox = new_job;
1792 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
1794 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1795 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
1798 return 0;
1800 #endif