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