1 /*
2  * $Id: fcgi_pm.c,v 1.96 2009/09/29 00:34:10 robs Exp $
3  */
4 
5 
6 #include "fcgi.h"
7 
8 #if defined(APACHE2) && !defined(WIN32)
9 #include <pwd.h>
10 #include <unistd.h>
11 #include "unixd.h"
12 #include "apr_signal.h"
13 #endif
14 
15 #ifndef WIN32
16 #include <utime.h>
17 #endif
18 
19 #ifdef _HPUX_SOURCE
20 #include <unistd.h>
21 #define seteuid(arg) setresuid(-1, (arg), -1)
22 #endif
23 
24 int fcgi_dynamic_total_proc_count = 0;    /* number of running apps */
25 time_t fcgi_dynamic_epoch = 0;            /* last time kill_procs was
26                                            * invoked by process mgr */
27 time_t fcgi_dynamic_last_analyzed = 0;    /* last time calculation was
28                                            * made for the dynamic procs */
29 
30 static time_t now = 0;
31 
32 #ifdef WIN32
33 #ifdef APACHE2
34 #include "mod_cgi.h"
35 #include "apr_version.h"
36 #endif
37 #pragma warning ( disable : 4100 4102 )
38 static BOOL bTimeToDie = FALSE;  /* process termination flag */
39 HANDLE fcgi_event_handles[3];
40 #ifndef SIGKILL
41 #define SIGKILL 9
42 #endif
43 #endif
44 
45 
46 #ifndef WIN32
seteuid_root(void)47 static int seteuid_root(void)
48 {
49     int rc = seteuid(getuid());
50     if (rc) {
51         ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
52             "FastCGI: seteuid(0) failed");
53     }
54     return rc;
55 }
56 
seteuid_user(void)57 static int seteuid_user(void)
58 {
59     int rc = seteuid(ap_user_id);
60     if (rc) {
61         ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
62             "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id);
63     }
64     return rc;
65 }
66 #endif
67 
68 /*
69  * Signal the process to exit.  How (or if) the process responds
70  * depends on the FastCGI application library (esp. on Win32) and
71  * possibly application code (signal handlers and whether or not
72  * SA_RESTART is on).  At any rate, we send the signal with the
73  * hopes that the process will exit on its own.  Later, as we
74  * review the state of application processes, if we see one marked
75  * for death, but that hasn't died within a specified period of
76  * time, fcgi_kill() is called again with a KILL)
77  */
fcgi_kill(ServerProcess * process,int sig)78 static void fcgi_kill(ServerProcess *process, int sig)
79 {
80     FCGIDBG3("fcgi_kill(%ld, %d)", (long) process->pid, sig);
81 
82     process->state = FCGI_VICTIM_STATE;
83 
84 #ifdef WIN32
85 
86     if (sig == SIGTERM)
87     {
88         SetEvent(process->terminationEvent);
89     }
90     else if (sig == SIGKILL)
91     {
92         TerminateProcess(process->handle, 1);
93     }
94     else
95     {
96         ap_assert(0);
97     }
98 
99 #else /* !WIN32 */
100 
101     if (fcgi_wrapper)
102     {
103         seteuid_root();
104     }
105 
106     kill(process->pid, sig);
107 
108     if (fcgi_wrapper)
109     {
110         seteuid_user();
111     }
112 
113 #endif /* !WIN32 */
114 }
115 
116 /*******************************************************************************
117  * Send SIGTERM to each process in the server class, remove socket
118  * file if appropriate.  Currently this is only called when the PM is shutting
119  * down and thus memory isn't freed and sockets and files aren't closed.
120  */
shutdown_all()121 static void shutdown_all()
122 {
123     fcgi_server *s = fcgi_servers;
124 
125     while (s)
126     {
127         ServerProcess *proc = s->procs;
128         int i;
129         int numChildren = (s->directive == APP_CLASS_DYNAMIC)
130             ? dynamicMaxClassProcs
131             : s->numProcesses;
132 
133         /* Send TERM to all processes */
134         for (i = 0; i < numChildren; i++, proc++)
135         {
136             if (proc->state == FCGI_RUNNING_STATE)
137             {
138                 fcgi_kill(proc, SIGTERM);
139             }
140         }
141 
142         s = s->next;
143     }
144 
145 #ifndef WIN32
146 
147     s = fcgi_servers;
148     while (s)
149     {
150     	if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL)
151     	{
152     	    struct timeval tv;
153 
154     	    /* sleep two seconds to let the children terminate themselves */
155     	    tv.tv_sec = 2;
156     	    tv.tv_usec = 0;
157     	    ap_select(0, NULL, NULL, NULL, &tv);
158 
159     	    while (s)
160     	    {
161         if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL)
162         {
163             /* Remove the socket file */
164             if (unlink(s->socket_path) != 0 && errno != ENOENT) {
165                 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
166                     "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
167                     s->socket_path,
168                     (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path);
169             }
170         }
171 
172     	        s = s->next;
173             }
174 
175     	    break;
176         }
177 
178         s = s->next;
179     }
180 
181 #endif
182 
183 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
184 
185     /*
186      * WIN32 applications may not have support for the shutdown event
187      * depending on their application library version
188      */
189 
190     Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT);
191     s = fcgi_servers;
192 
193     while (s)
194     {
195         ServerProcess *proc = s->procs;
196         int i;
197         int numChildren = (s->directive == APP_CLASS_DYNAMIC)
198             ? dynamicMaxClassProcs
199             : s->numProcesses;
200 
201         /* Send KILL to all processes */
202         for (i = 0; i < numChildren; i++, proc++)
203         {
204             if (proc->state == FCGI_RUNNING_STATE)
205             {
206                 fcgi_kill(proc, SIGKILL);
207             }
208         }
209 
210         s = s->next;
211     }
212 
213 #endif /* WIN32 */
214 }
215 
init_listen_sock(fcgi_server * fs)216 static int init_listen_sock(fcgi_server * fs)
217 {
218     ap_assert(fs->directive != APP_CLASS_EXTERNAL);
219 
220     /* Create the socket */
221     if ((fs->listenFd = socket(fs->socket_addr->sa_family, SOCK_STREAM, 0)) < 0)
222     {
223 #ifdef WIN32
224         errno = WSAGetLastError();  /* Not sure if this will work as expected */
225 #endif
226         ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
227             "FastCGI: can't create %sserver \"%s\": socket() failed",
228             (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
229             fs->fs_path);
230         return -1;
231     }
232 
233 #ifndef WIN32
234     if (fs->socket_addr->sa_family == AF_UNIX)
235     {
236         /* Remove any existing socket file.. just in case */
237         unlink(((struct sockaddr_un *)fs->socket_addr)->sun_path);
238     }
239     else
240 #endif
241     {
242         int flag = 1;
243         setsockopt(fs->listenFd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
244     }
245 
246     /* Bind it to the socket_addr */
247     if (bind(fs->listenFd, fs->socket_addr, fs->socket_addr_len))
248     {
249         char port[11];
250 
251 #ifdef WIN32
252         errno = WSAGetLastError();
253 #endif
254         ap_snprintf(port, sizeof(port), "port=%d",
255             ((struct sockaddr_in *)fs->socket_addr)->sin_port);
256 
257         ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
258             "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
259             (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
260             fs->fs_path,
261 #ifndef WIN32
262             (fs->socket_addr->sa_family == AF_UNIX) ?
263                 ((struct sockaddr_un *)fs->socket_addr)->sun_path :
264 #endif
265                 port);
266     }
267 
268 #ifndef WIN32
269     /* Twiddle Unix socket permissions */
270     else if (fs->socket_addr->sa_family == AF_UNIX
271         && chmod(((struct sockaddr_un *)fs->socket_addr)->sun_path, S_IRUSR | S_IWUSR))
272     {
273         ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
274             "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
275             (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
276             fs->fs_path);
277     }
278 #endif
279 
280     /* Set to listen */
281     else if (listen(fs->listenFd, fs->listenQueueDepth))
282     {
283 #ifdef WIN32
284         errno = WSAGetLastError();
285 #endif
286         ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
287             "FastCGI: can't create %sserver \"%s\": listen() failed",
288             (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
289             fs->fs_path);
290     }
291     else
292     {
293         return 0;
294     }
295 
296 #ifdef WIN32
297     closesocket(fs->listenFd);
298 #else
299     close(fs->listenFd);
300 #endif
301 
302     fs->listenFd = -1;
303 
304     return -2;
305 }
306 
307 /*
308  *----------------------------------------------------------------------
309  *
310  * pm_main
311  *
312  *      The FastCGI process manager, which runs as a separate
313  *      process responsible for:
314  *        - Starting all the FastCGI proceses.
315  *        - Restarting any of these processes that die (indicated
316  *          by SIGCHLD).
317  *        - Catching SIGTERM and relaying it to all the FastCGI
318  *          processes before exiting.
319  *
320  * Inputs:
321  *      Uses global variable fcgi_servers.
322  *
323  * Results:
324  *      Does not return.
325  *
326  * Side effects:
327  *      Described above.
328  *
329  *----------------------------------------------------------------------
330  */
331 #ifndef WIN32
332 static int caughtSigTerm = FALSE;
333 static int caughtSigChld = FALSE;
334 static int caughtSigAlarm = FALSE;
335 
signal_handler(int signo)336 static void signal_handler(int signo)
337 {
338     if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
339         /* SIGUSR1 & SIGHUP are sent by apache to its process group
340          * when apache get 'em.  Apache follows up (1.2.x) with attacks
341          * on each of its child processes, but we've got the KillMgr
342          * sitting between us so we never see the KILL.  The main loop
343          * in ProcMgr also checks to see if the KillMgr has terminated,
344          * and if it has, we handl it as if we should shutdown too. */
345         caughtSigTerm = TRUE;
346     } else if(signo == SIGCHLD) {
347         caughtSigChld = TRUE;
348     } else if(signo == SIGALRM) {
349         caughtSigAlarm = TRUE;
350     }
351 }
352 #endif
353 
354 /*
355  *----------------------------------------------------------------------
356  *
357  * spawn_fs_process --
358  *
359  *      Fork and exec the specified fcgi process.
360  *
361  * Results:
362  *      0 for successful fork, -1 for failed fork.
363  *
364  *      In case the child fails before or in the exec, the child
365  *      obtains the error log by calling getErrLog, logs
366  *      the error, and exits with exit status = errno of
367  *      the failed system call.
368  *
369  * Side effects:
370  *      Child process created.
371  *
372  *----------------------------------------------------------------------
373  */
spawn_fs_process(fcgi_server * fs,ServerProcess * process)374 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
375 {
376 #ifndef WIN32
377 
378     pid_t child_pid;
379     int i;
380     char *dirName;
381     char *dnEnd, *failedSysCall;
382 
383     child_pid = fork();
384     if (child_pid) {
385         return child_pid;
386     }
387 
388     /* We're the child.  We're gonna exec() so pools don't matter. */
389 
390     dnEnd = strrchr(fs->fs_path, '/');
391     if (dnEnd == NULL) {
392         dirName = "./";
393     } else {
394         dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
395         dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
396     }
397     if (chdir(dirName) < 0) {
398         failedSysCall = "chdir()";
399         goto FailedSystemCallExit;
400     }
401 
402 #ifndef __EMX__
403      /* OS/2 dosen't support nice() */
404     if (fs->processPriority != 0) {
405         if (nice(fs->processPriority) == -1) {
406             failedSysCall = "nice()";
407             goto FailedSystemCallExit;
408         }
409     }
410 #endif
411 
412     /* Open the listenFd on spec'd fd */
413     if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
414         dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
415 
416     /* Close all other open fds, except stdout/stderr.  Leave these two open so
417      * FastCGI applications don't have to find and fix ALL 3rd party libs that
418      * write to stdout/stderr inadvertantly.  For now, just leave 'em open to the
419      * main server error_log - @@@ provide a directive control where this goes.
420      */
421     ap_error_log2stderr(fcgi_apache_main_server);
422     dup2(2, 1);
423     for (i = 0; i < FCGI_MAX_FD; i++) {
424         if (i != FCGI_LISTENSOCK_FILENO && i != 2 && i != 1) {
425             close(i);
426         }
427     }
428 
429     /* Ignore SIGPIPE by default rather than terminate.  The fs SHOULD
430      * install its own handler. */
431     signal(SIGPIPE, SIG_IGN);
432 
433     if (fcgi_wrapper)
434     {
435         char *shortName;
436 
437         /* Relinquish our root real uid powers */
438         seteuid_root();
439         setuid(ap_user_id);
440 
441         /* Apache (2 anyway) doesn't use suexec if there is no user/group in
442          * effect - this translates to a uid/gid of 0/0 (which should never
443          * be a valid uid/gid for an suexec invocation so it should be safe */
444         if (fs->uid == 0 && fs->gid == 0) {
445             goto NO_SUEXEC;
446         }
447 
448 #ifdef NO_SUEXEC_FOR_AP_USER_N_GROUP
449 
450         /* AP13 does not use suexec if the target uid/gid is the same as the
451          * server's - AP20 does.  I (now) consider the AP2 approach better
452          * (fcgi_pm.c v1.42 incorporated the 1.3 behaviour, v1.84 reverted it,
453          * v1.85 added the compile time option to use the old behaviour). */
454         if (fcgi_user_id == fs->uid && fcgi_group_id == fs->gid) {
455             goto NO_SUEXEC;
456         }
457 
458 #endif
459         shortName = strrchr(fs->fs_path, '/') + 1;
460 
461         do {
462             execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group,
463                    shortName, NULL, fs->envp);
464         } while (errno == EINTR);
465     }
466     else
467     {
468 NO_SUEXEC:
469         do {
470             execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
471         } while (errno == EINTR);
472     }
473 
474     failedSysCall = "execle()";
475 
476 FailedSystemCallExit:
477     fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
478         fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
479     exit(-1);
480 
481     /* avoid an irrelevant compiler warning */
482     return(0);
483 
484 #else /* WIN32 */
485 
486 #ifdef APACHE2
487 
488     /* based on mod_cgi.c:run_cgi_child() */
489 
490     apr_pool_t * tp;
491     char * termination_env_string;
492     HANDLE listen_handle = INVALID_HANDLE_VALUE;
493     apr_procattr_t * procattr;
494     apr_proc_t proc = { 0 };
495     apr_file_t * file;
496     int i = 0;
497     cgi_exec_info_t e_info = { 0 };
498     request_rec r = { 0 };
499     const char *command;
500     const char **argv;
501     int rv;
502     APR_OPTIONAL_FN_TYPE(ap_cgi_build_command) *cgi_build_command;
503 
504     cgi_build_command = APR_RETRIEVE_OPTIONAL_FN(ap_cgi_build_command);
505     if (cgi_build_command == NULL)
506     {
507         ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
508             "FastCGI: can't exec server \"%s\", mod_cgi isn't loaded",
509             fs->fs_path);
510         return 0;
511     }
512 
513     if (apr_pool_create(&tp, fcgi_config_pool))
514         return 0;
515 
516     process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
517     if (process->terminationEvent == NULL)
518         goto CLEANUP;
519 
520     SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
521 
522     termination_env_string = ap_psprintf(tp,
523         "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
524 
525     while (fs->envp[i]) i++;
526     fs->envp[i++] = termination_env_string;
527     fs->envp[i] = (char *) fs->mutex_env_string;
528 
529     ap_assert(fs->envp[i + 1] == NULL);
530 
531     if (fs->socket_path)
532     {
533         SECURITY_ATTRIBUTES sa = { 0 };
534 
535         sa.bInheritHandle = TRUE;
536         sa.nLength = sizeof(sa);
537 
538         listen_handle = CreateNamedPipe(fs->socket_path,
539             PIPE_ACCESS_DUPLEX,
540             PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
541             PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
542 
543         if (listen_handle == INVALID_HANDLE_VALUE)
544         {
545             ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
546                 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
547                 fs->fs_path);
548             goto CLEANUP;
549         }
550     }
551     else
552     {
553         listen_handle = (HANDLE) fs->listenFd;
554     }
555 
556     r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
557     r.server = fcgi_apache_main_server;
558     r.filename = (char *) fs->fs_path;
559     r.pool = tp;
560     r.subprocess_env = apr_table_make(tp, 0);
561 
562     e_info.cmd_type = APR_PROGRAM;
563 
564     rv = cgi_build_command(&command, &argv, &r, tp, &e_info);
565     if (rv != APR_SUCCESS)
566     {
567         ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
568             "FastCGI: don't know how to spawn cmd child process: %s",
569             fs->fs_path);
570         goto CLEANUP;
571     }
572 
573     if (apr_procattr_create(&procattr, tp))
574         goto CLEANUP;
575 
576     if (apr_procattr_dir_set(procattr, ap_make_dirstr_parent(tp, fs->fs_path)))
577         goto CLEANUP;
578 
579     if (apr_procattr_cmdtype_set(procattr, e_info.cmd_type))
580         goto CLEANUP;
581 
582     if (apr_procattr_detach_set(procattr, 1))
583         goto CLEANUP;
584 
585     if (apr_os_file_put(&file, &listen_handle, 0, tp))
586         goto CLEANUP;
587 
588 #if (APR_MAJOR_VERSION >= 1) && (APR_MINOR_VERSION >= 3)
589     if (apr_procattr_io_set(procattr, APR_FULL_BLOCK, APR_NO_FILE, APR_NO_FILE))
590         goto CLEANUP;
591 #endif
592 
593     /* procattr is opaque so we have to use this - unfortuantely it dups */
594     if (apr_procattr_child_in_set(procattr, file, NULL))
595         goto CLEANUP;
596 
597     if (apr_proc_create(&proc, command, argv, fs->envp, procattr, tp))
598         goto CLEANUP;
599 
600     process->handle = proc.hproc;
601 
602 CLEANUP:
603 
604     if (fs->socket_path && listen_handle != INVALID_HANDLE_VALUE)
605     {
606         CloseHandle(listen_handle);
607     }
608 
609     if (i)
610     {
611         fs->envp[i - 1] = NULL;
612     }
613 
614     ap_destroy_pool(tp);
615 
616     return proc.pid;
617 
618 #else /* WIN32 && !APACHE2 */
619 
620     /* Adapted from Apache's util_script.c ap_call_exec() */
621     char *interpreter = NULL;
622     char *quoted_filename;
623     char *pCommand;
624     char *pEnvBlock, *pNext;
625 
626     int i = 0;
627     int iEnvBlockLen = 1;
628 
629     file_type_e fileType;
630 
631     STARTUPINFO si;
632     PROCESS_INFORMATION pi;
633 
634     request_rec r;
635     pid_t pid = -1;
636 
637     pool * tp = ap_make_sub_pool(fcgi_config_pool);
638 
639     HANDLE listen_handle = INVALID_HANDLE_VALUE;
640     char * termination_env_string = NULL;
641 
642     process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
643     if (process->terminationEvent == NULL)
644     {
645         ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
646             "FastCGI: can't create termination event for server \"%s\", "
647             "CreateEvent() failed", fs->fs_path);
648         goto CLEANUP;
649     }
650     SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
651 
652     termination_env_string = ap_psprintf(tp,
653         "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
654 
655     if (fs->socket_path)
656     {
657         SECURITY_ATTRIBUTES sa;
658 
659         sa.lpSecurityDescriptor = NULL;
660         sa.bInheritHandle = TRUE;
661         sa.nLength = sizeof(sa);
662 
663         listen_handle = CreateNamedPipe(fs->socket_path,
664             PIPE_ACCESS_DUPLEX,
665             PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
666             PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
667 
668         if (listen_handle == INVALID_HANDLE_VALUE)
669         {
670             ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
671                 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
672             goto CLEANUP;
673         }
674     }
675     else
676     {
677         listen_handle = (HANDLE) fs->listenFd;
678     }
679 
680     memset(&si, 0, sizeof(si));
681     memset(&pi, 0, sizeof(pi));
682     memset(&r,  0, sizeof(r));
683 
684     /* Can up a fake request to pass to ap_get_win32_interpreter() */
685     r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
686     r.server = fcgi_apache_main_server;
687     r.filename = (char *) fs->fs_path;
688     r.pool = tp;
689 
690     fileType = ap_get_win32_interpreter(&r, &interpreter);
691 
692     if (fileType == eFileTypeUNKNOWN) {
693         ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
694             "FastCGI: %s is not executable; ensure interpreted scripts have "
695             "\"#!\" as their first line",
696             fs->fs_path);
697         ap_destroy_pool(tp);
698         goto CLEANUP;
699     }
700 
701     /*
702      * We have the interpreter (if there is one) and we have
703      * the arguments (if there are any).
704      * Build the command string to pass to CreateProcess.
705      */
706     quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
707     if (interpreter && *interpreter) {
708         pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
709     }
710     else {
711         pCommand = quoted_filename;
712     }
713 
714     /*
715      * Make child process use hPipeOutputWrite as standard out,
716      * and make sure it does not show on screen.
717      */
718     si.cb = sizeof(si);
719     si.dwFlags     = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
720     si.wShowWindow = SW_HIDE;
721     si.hStdInput   = listen_handle;
722 
723     /* XXX These should be open to the error_log */
724     si.hStdOutput  = INVALID_HANDLE_VALUE;
725     si.hStdError   = INVALID_HANDLE_VALUE;
726 
727     /*
728      * Win32's CreateProcess call requires that the environment
729      * be passed in an environment block, a null terminated block of
730      * null terminated strings.
731      * @todo we should store the env in this format for win32.
732      */
733     while (fs->envp[i])
734     {
735         iEnvBlockLen += strlen(fs->envp[i]) + 1;
736         i++;
737     }
738 
739     iEnvBlockLen += strlen(termination_env_string) + 1;
740     iEnvBlockLen += strlen(fs->mutex_env_string) + 1;
741 
742     pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
743 
744     i = 0;
745     pNext = pEnvBlock;
746     while (fs->envp[i])
747     {
748         strcpy(pNext, fs->envp[i]);
749         pNext += strlen(pNext) + 1;
750         i++;
751     }
752 
753     strcpy(pNext, termination_env_string);
754     pNext += strlen(pNext) + 1;
755     strcpy(pNext, fs->mutex_env_string);
756 
757     if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
758                       0,
759                       pEnvBlock,
760                       ap_make_dirstr_parent(tp, fs->fs_path),
761                       &si, &pi))
762     {
763         /* Hack to get 16-bit CGI's working. It works for all the
764          * standard modules shipped with Apache. pi.dwProcessId is 0
765          * for 16-bit CGIs and all the Unix specific code that calls
766          * ap_call_exec interprets this as a failure case. And we can't
767          * use -1 either because it is mapped to 0 by the caller.
768          */
769         pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId;
770 
771         process->handle = pi.hProcess;
772         CloseHandle(pi.hThread);
773     }
774 
775 CLEANUP:
776 
777     if (fs->socket_path && listen_handle != INVALID_HANDLE_VALUE)
778     {
779         CloseHandle(listen_handle);
780     }
781 
782     ap_destroy_pool(tp);
783 
784     return pid;
785 
786 #endif /* !APACHE2 */
787 #endif /* WIN32 */
788 }
789 
790 #ifndef WIN32
reduce_privileges(void)791 static void reduce_privileges(void)
792 {
793     const char *name;
794 
795     if (geteuid() != 0)
796         return;
797 
798 #ifndef __EMX__
799     /* Get username if passed as a uid */
800     if (ap_user_name[0] == '#') {
801         uid_t uid = atoi(&ap_user_name[1]);
802         struct passwd *ent = getpwuid(uid);
803 
804         if (ent == NULL) {
805             ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
806                 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
807                 "you probably need to modify the User directive", (unsigned)uid);
808             exit(1);
809         }
810         name = ent->pw_name;
811     }
812     else
813         name = ap_user_name;
814 
815     /* Change Group */
816     if (setgid(ap_group_id) == -1) {
817         ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
818             "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
819         exit(1);
820     }
821 
822     /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
823 
824     /* Initialize supplementary groups */
825     if (initgroups(name, ap_group_id) == -1) {
826         ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
827             "FastCGI: process manager exiting, initgroups(%s,%u) failed",
828             name, (unsigned)ap_group_id);
829         exit(1);
830     }
831 #endif /* __EMX__ */
832 
833     /* Change User */
834     if (fcgi_wrapper) {
835         if (seteuid_user() == -1) {
836             ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
837                 "FastCGI: process manager exiting, failed to reduce privileges");
838             exit(1);
839         }
840     }
841     else {
842         if (setuid(ap_user_id) == -1) {
843             ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
844                 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
845             exit(1);
846         }
847     }
848 }
849 
850 /*************
851  * Change the name of this process - best we can easily.
852  */
change_process_name(const char * const name)853 static void change_process_name(const char * const name)
854 {
855     /* under Apache2, ap_server_argv0 is const */
856     strncpy((char *) ap_server_argv0, name, strlen(ap_server_argv0));
857 }
858 #endif /* !WIN32 */
859 
schedule_start(fcgi_server * s,int proc)860 static void schedule_start(fcgi_server *s, int proc)
861 {
862     /* If we've started one recently, don't register another */
863     time_t time_passed = now - s->restartTime;
864 
865     if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay))
866         || ((s->procs[proc].pid == 0) && (time_passed < s->initStartDelay)))
867     {
868         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);
869         return;
870     }
871 
872     FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
873     s->procs[proc].state = FCGI_START_STATE;
874     if (proc == dynamicMaxClassProcs - 1) {
875         ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
876             "FastCGI: scheduled the %sstart of the last (dynamic) server "
877             "\"%s\" process: reached dynamicMaxClassProcs (%d)",
878             s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
879     }
880 }
881 
882 /*
883  *----------------------------------------------------------------------
884  *
885  * dynamic_read_msgs
886  *
887  *      Removes the records written by request handlers and decodes them.
888  *      We also update the data structures to reflect the changes.
889  *
890  *----------------------------------------------------------------------
891  */
892 
dynamic_read_msgs(int read_ready)893 static void dynamic_read_msgs(int read_ready)
894 {
895     fcgi_server *s;
896     int rc;
897 
898 #ifndef WIN32
899     static int buflen = 0;
900     static char buf[FCGI_MSGS_BUFSIZE + 1];
901     char *ptr1, *ptr2, opcode;
902     char execName[FCGI_MAXPATH + 1];
903     char user[MAX_USER_NAME_LEN + 2];
904     char group[MAX_GID_CHAR_LEN + 1];
905     unsigned long q_usec = 0UL, req_usec = 0UL;
906 #else
907     fcgi_pm_job *joblist = NULL;
908     fcgi_pm_job *cjob = NULL;
909 #endif
910 
911     pool *sp = NULL, *tp;
912 
913 #ifndef WIN32
914     user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
915 #endif
916 
917     /*
918      * To prevent the idle application from running indefinitely, we
919      * check the timer and if it is expired, we recompute the values
920      * for each running application class.  Then, when FCGI_REQUEST_COMPLETE_JOB
921      * message is received, only updates are made to the data structures.
922      */
923     if (fcgi_dynamic_last_analyzed == 0) {
924         fcgi_dynamic_last_analyzed = now;
925     }
926     if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
927         for (s = fcgi_servers; s != NULL; s = s->next) {
928             if (s->directive != APP_CLASS_DYNAMIC)
929                 break;
930 
931             /* Advance the last analyzed timestamp by the elapsed time since
932              * it was last set. Round the increase down to the nearest
933              * multiple of dynamicUpdateInterval */
934 
935             fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
936             s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
937             s->totalConnTime = 0UL;
938             s->totalQueueTime = 0UL;
939         }
940     }
941 
942     if (read_ready <= 0) {
943         return;
944     }
945 
946 #ifndef WIN32
947     rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
948     if (rc <= 0) {
949         if (!caughtSigTerm) {
950             ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
951                 "FastCGI: read() from pipe failed (%d)", rc);
952             if (rc == 0) {
953                 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
954                     "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
955                 caughtSigTerm = TRUE;
956             }
957         }
958         return;
959     }
960     buflen += rc;
961     buf[buflen] = '\0';
962 
963 #else
964 
965     /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
966      * request to do something) and/or when a timeout expires.
967      * There really should be no reason why this wait would get stuck
968      * but there's no point in waiting forever. */
969 
970     rc = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
971 
972     if (rc != WAIT_OBJECT_0 && rc != WAIT_ABANDONED)
973     {
974         ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
975             "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
976         return;
977     }
978 
979     joblist = fcgi_dynamic_mbox;
980     fcgi_dynamic_mbox = NULL;
981 
982     if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
983     {
984         ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
985             "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
986     }
987 
988     cjob = joblist;
989 #endif
990 
991 #ifdef APACHE2
992     apr_pool_create(&tp, fcgi_config_pool);
993 #else
994     tp = ap_make_sub_pool(fcgi_config_pool);
995 #endif
996 
997 #ifndef WIN32
998     for (ptr1 = buf; ptr1; ptr1 = ptr2) {
999         int scan_failed = 0;
1000 
1001         ptr2 = strchr(ptr1, '*');
1002         if (ptr2) {
1003             *ptr2++ = '\0';
1004         }
1005         else {
1006             break;
1007         }
1008 
1009         opcode = *ptr1;
1010 
1011         switch (opcode)
1012         {
1013         case FCGI_SERVER_START_JOB:
1014         case FCGI_SERVER_RESTART_JOB:
1015 
1016             if (sscanf(ptr1, "%c %s %16s %15s",
1017                 &opcode, execName, user, group) != 4)
1018             {
1019                 scan_failed = 1;
1020             }
1021             break;
1022 
1023         case FCGI_REQUEST_TIMEOUT_JOB:
1024 
1025             if (sscanf(ptr1, "%c %s %16s %15s",
1026                 &opcode, execName, user, group) != 4)
1027             {
1028                 scan_failed = 1;
1029             }
1030             break;
1031 
1032         case FCGI_REQUEST_COMPLETE_JOB:
1033 
1034             if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
1035                 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
1036             {
1037                 scan_failed = 1;
1038             }
1039             break;
1040 
1041         default:
1042 
1043             scan_failed = 1;
1044             break;
1045         }
1046 
1047 	FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode, execName, user, group, q_usec, req_usec);
1048 
1049         if (scan_failed) {
1050             ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1051                 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
1052             goto NextJob;
1053         }
1054 #else
1055     /* Update data structures for processing */
1056     while (cjob != NULL) {
1057         joblist = cjob->next;
1058         FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
1059 #endif
1060 
1061 #ifndef WIN32
1062         s = fcgi_util_fs_get(execName, user, group);
1063 #else
1064         s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
1065 #endif
1066 
1067 #ifndef WIN32
1068         if (s==NULL && opcode != FCGI_REQUEST_COMPLETE_JOB)
1069 #else
1070         if (s==NULL && cjob->id != FCGI_REQUEST_COMPLETE_JOB)
1071 #endif
1072         {
1073 #ifdef WIN32
1074 
1075             HANDLE mutex = CreateMutex(NULL, FALSE, cjob->fs_path);
1076 
1077             if (mutex == NULL)
1078             {
1079                 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1080                     "FastCGI: can't create accept mutex "
1081                     "for (dynamic) server \"%s\"", cjob->fs_path);
1082                 goto BagNewServer;
1083             }
1084 
1085             SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
1086 #else
1087             const char *err;
1088 #endif
1089 
1090             /* Create a perm subpool to hold the new server data,
1091              * we can destroy it if something doesn't pan out */
1092 #ifdef APACHE2
1093             apr_pool_create(&sp, fcgi_config_pool);
1094 #else
1095             sp = ap_make_sub_pool(fcgi_config_pool);
1096 #endif
1097 
1098             /* Create a new "dynamic" server */
1099             s = fcgi_util_fs_new(sp);
1100 
1101             s->directive = APP_CLASS_DYNAMIC;
1102             s->restartDelay = dynamicRestartDelay;
1103             s->listenQueueDepth = dynamicListenQueueDepth;
1104             s->initStartDelay = dynamicInitStartDelay;
1105             s->envp = dynamicEnvp;
1106             s->flush = dynamicFlush;
1107 
1108 #ifdef WIN32
1109             s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
1110             s->fs_path = ap_pstrdup(sp, cjob->fs_path);
1111 #else
1112             s->fs_path = ap_pstrdup(sp, execName);
1113 #endif
1114             ap_getparents(s->fs_path);
1115             ap_no2slash(s->fs_path);
1116             s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
1117 
1118             /* XXX the socket_path (both Unix and Win) *is* deducible and
1119              * thus can and will be used by other apache instances without
1120              * the use of shared data regarding the processes serving the
1121              * requests.  This can result in slightly unintuitive process
1122              * counts and security implications.  This is prevented
1123              * if suexec (Unix) is in use.  This is both a feature and a flaw.
1124              * Changing it now would break existing installations. */
1125 
1126 #ifndef WIN32
1127             /* Create socket file's path */
1128             s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
1129             s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1130 
1131             /* Create sockaddr, prealloc it so it won't get created in tp */
1132             s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
1133             err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
1134                                           &s->socket_addr_len, s->socket_path);
1135             if (err) {
1136                 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1137                     "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
1138                 goto BagNewServer;
1139             }
1140 
1141             if (init_listen_sock(s)) {
1142                 goto BagNewServer;
1143             }
1144 
1145             /* If a wrapper is being used, config user/group info */
1146             if (fcgi_wrapper) {
1147                 if (user[0] == '~') {
1148                     /* its a user dir uri, the rest is a username, not a uid */
1149                     struct passwd *pw = getpwnam(&user[1]);
1150 
1151                     if (!pw) {
1152                         ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1153                             "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1154                             execName, &user[1]);
1155                         goto BagNewServer;
1156                     }
1157                     s->uid = pw->pw_uid;
1158                     s->user = ap_pstrdup(sp, user);
1159                     s->username = s->user;
1160 
1161                     s->gid = pw->pw_gid;
1162                     s->group = ap_psprintf(sp, "%ld", (long)s->gid);
1163                 }
1164                 else {
1165                     struct passwd *pw;
1166 
1167                     s->uid = (uid_t)atol(user);
1168                     pw = getpwuid(s->uid);
1169                     if (!pw) {
1170                         ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1171                             "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1172                             execName, (long)s->uid);
1173                         goto BagNewServer;
1174                     }
1175                     s->user = ap_pstrdup(sp, user);
1176                     s->username = ap_pstrdup(sp, pw->pw_name);
1177 
1178                     s->gid = (gid_t)atol(group);
1179                     s->group = ap_pstrdup(sp, group);
1180                 }
1181             }
1182 #else
1183             /* Create socket file's path */
1184             s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
1185             s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1186             s->listenFd = 0;
1187 #endif
1188 
1189             fcgi_util_fs_add(s);
1190         }
1191         else {
1192 #ifndef WIN32
1193             if (opcode == FCGI_SERVER_RESTART_JOB) {
1194 #else
1195             if (cjob->id==FCGI_SERVER_RESTART_JOB) {
1196 #endif
1197                 /* Check to see if the binary has changed.  If so,
1198                 * kill the FCGI application processes, and
1199                 * restart them.
1200                 */
1201                 struct stat stbuf;
1202                 int i;
1203 #ifdef WIN32
1204                 char * app_path = cjob->fs_path;
1205 #else
1206                 char * app_path = execName;
1207 #endif
1208 
1209                 if (stat(app_path, &stbuf) == 0 && stbuf.st_mtime > s->startTime)
1210                 {
1211                     int do_restart = 0;
1212 
1213                     /* prevent addition restart requests */
1214                     s->startTime = now;
1215 #ifndef WIN32
1216                     utime(s->socket_path, NULL);
1217 #endif
1218 
1219                     /* kill old server(s) */
1220                     for (i = 0; i < dynamicMaxClassProcs; i++)
1221                     {
1222                         if (s->procs[i].pid > 0
1223                             && stbuf.st_mtime > s->procs[i].start_time)
1224                         {
1225                             fcgi_kill(&s->procs[i], SIGTERM);
1226                             do_restart++;
1227                         }
1228                     }
1229 
1230                     if (do_restart)
1231                     {
1232                         ap_log_error(FCGI_LOG_WARN_NOERRNO,
1233                             fcgi_apache_main_server, "FastCGI: restarting "
1234                             "old server \"%s\" processes, newer version "
1235                             "found", app_path);
1236                     }
1237                 }
1238 
1239                 /* If dynamicAutoRestart, don't mark any new processes
1240                  * for  starting because we probably got the
1241                  * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1242                  * will be restarting all of those we just killed.
1243                  */
1244                 if (dynamicAutoRestart)
1245                     goto NextJob;
1246             }
1247 #ifndef WIN32
1248             else if (opcode == FCGI_SERVER_START_JOB) {
1249 #else
1250             else if (cjob->id==FCGI_SERVER_START_JOB) {
1251 #endif
1252                 /* we've been asked to start a process--only start
1253                 * it if we're not already running at least one
1254                 * instance.
1255                 */
1256                 int i;
1257 
1258                 for (i = 0; i < dynamicMaxClassProcs; i++) {
1259                    if (s->procs[i].state == FCGI_RUNNING_STATE)
1260                       break;
1261                 }
1262                 /* if already running, don't start another one */
1263                 if (i < dynamicMaxClassProcs) {
1264                     goto NextJob;
1265                 }
1266             }
1267         }
1268 
1269 #ifndef WIN32
1270         switch (opcode)
1271 #else
1272         switch (cjob->id)
1273 #endif
1274         {
1275             int i, start;
1276 
1277             case FCGI_SERVER_RESTART_JOB:
1278 
1279                 start = FALSE;
1280 
1281                 /* We just waxed 'em all.  Try to find an idle slot. */
1282 
1283                 for (i = 0; i < dynamicMaxClassProcs; ++i)
1284                 {
1285                     if (s->procs[i].state == FCGI_START_STATE
1286                         || s->procs[i].state == FCGI_RUNNING_STATE)
1287                     {
1288                         break;
1289                     }
1290                     else if (s->procs[i].state == FCGI_KILLED_STATE
1291                         || s->procs[i].state == FCGI_READY_STATE)
1292                     {
1293                         start = TRUE;
1294                         break;
1295                     }
1296                 }
1297 
1298                 /* Nope, just use the first slot */
1299                 if (i == dynamicMaxClassProcs)
1300                 {
1301                     start = TRUE;
1302                     i = 0;
1303                 }
1304 
1305                 if (start)
1306                 {
1307                     schedule_start(s, i);
1308                 }
1309 
1310                 break;
1311 
1312             case FCGI_SERVER_START_JOB:
1313             case FCGI_REQUEST_TIMEOUT_JOB:
1314 
1315                 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1316                     /*
1317                      * Extra instances should have been
1318                      * terminated beforehand, probably need
1319                      * to increase ProcessSlack parameter
1320                      */
1321                     ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1322                         "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1323                         "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1324                     goto NextJob;
1325                 }
1326 
1327                 /* find next free slot */
1328                 for (i = 0; i < dynamicMaxClassProcs; i++)
1329                 {
1330                     if (s->procs[i].state == FCGI_START_STATE)
1331                     {
1332                         FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1333                         break;
1334                     }
1335                     else if (s->procs[i].state == FCGI_RUNNING_STATE)
1336                     {
1337                         continue;
1338                     }
1339 
1340                     schedule_start(s, i);
1341                     break;
1342                 }
1343 
1344 #ifdef FCGI_DEBUG
1345                 if (i >= dynamicMaxClassProcs) {
1346                     FCGIDBG1("ignore_job: slots are max'd");
1347                 }
1348 #endif
1349                 break;
1350             case FCGI_REQUEST_COMPLETE_JOB:
1351                 /* only record stats if we have a structure */
1352                 if (s) {
1353 #ifndef WIN32
1354                     s->totalConnTime += req_usec;
1355                     s->totalQueueTime += q_usec;
1356 #else
1357                     s->totalConnTime += cjob->start_time;
1358                     s->totalQueueTime += cjob->qsec;
1359 #endif
1360                 }
1361                 break;
1362         }
1363 
1364 NextJob:
1365 
1366 #ifdef WIN32
1367         /* Cleanup job data */
1368         free(cjob->fs_path);
1369         free(cjob->user);
1370         free(cjob->group);
1371         free(cjob);
1372         cjob = joblist;
1373 #endif
1374 
1375         continue;
1376 
1377 BagNewServer:
1378         if (sp) ap_destroy_pool(sp);
1379 
1380 #ifdef WIN32
1381     free(cjob->fs_path);
1382     free(cjob);
1383     cjob = joblist;
1384 #endif
1385     }
1386 
1387 #ifndef WIN32
1388     if (ptr1 == buf) {
1389         ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1390             "FastCGI: really bogus message: \"%s\"", ptr1);
1391         ptr1 += strlen(buf);
1392     }
1393 
1394     buflen -= ptr1 - buf;
1395     if (buflen) {
1396         memmove(buf, ptr1, buflen);
1397     }
1398 #endif
1399 
1400     ap_destroy_pool(tp);
1401 }
1402 
1403 /*
1404  *----------------------------------------------------------------------
1405  *
1406  * dynamic_kill_idle_fs_procs
1407  *
1408  *      Implement a kill policy for the dynamic FastCGI applications.
1409  *      We also update the data structures to reflect the changes.
1410  *
1411  * Side effects:
1412  *      Processes are marked for deletion possibly killed.
1413  *
1414  *----------------------------------------------------------------------
1415  */
1416 static void dynamic_kill_idle_fs_procs(void)
1417 {
1418     fcgi_server *s;
1419     int victims = 0;
1420 
1421     for (s = fcgi_servers;  s != NULL; s = s->next)
1422     {
1423         /*
1424          * server's smoothed running time, or if that's 0, the current total
1425          */
1426         unsigned long connTime;
1427 
1428         /*
1429          * maximum number of microseconds that all of a server's running
1430          * processes together could have spent running since the last check
1431          */
1432         unsigned long totalTime;
1433 
1434         /*
1435          * percentage, 0-100, of totalTime that the processes actually used
1436          */
1437         int loadFactor;
1438 
1439         int i;
1440         int really_running = 0;
1441 
1442         if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1443         {
1444             continue;
1445         }
1446 
1447         /* s->numProcesses includes pending kills so get the "active" count */
1448         for (i = 0; i < dynamicMaxClassProcs; ++i)
1449         {
1450             if (s->procs[i].state == FCGI_RUNNING_STATE) ++really_running;
1451         }
1452 
1453         connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1454         totalTime = really_running * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1455 
1456         loadFactor = 100 * connTime / totalTime;
1457 
1458         if (really_running == 1)
1459         {
1460             if (loadFactor >= dynamicThreshold1)
1461             {
1462                 continue;
1463             }
1464         }
1465         else
1466         {
1467             int load = really_running / ( really_running - 1) * loadFactor;
1468 
1469             if (load >= dynamicThresholdN)
1470             {
1471                 continue;
1472             }
1473         }
1474 
1475         /*
1476          * Run through the procs to see if we can get away w/o waxing one.
1477          */
1478         for (i = 0; i < dynamicMaxClassProcs; ++i)
1479         {
1480             if (s->procs[i].state == FCGI_START_STATE)
1481             {
1482                 s->procs[i].state = FCGI_READY_STATE;
1483                 break;
1484             }
1485             else if (s->procs[i].state == FCGI_VICTIM_STATE)
1486             {
1487                 break;
1488             }
1489         }
1490 
1491         if (i >= dynamicMaxClassProcs)
1492         {
1493             ServerProcess * procs = s->procs;
1494             int youngest = -1;
1495 
1496             for (i = 0; i < dynamicMaxClassProcs; ++i)
1497             {
1498                 if (procs[i].state == FCGI_RUNNING_STATE)
1499                 {
1500                     if (youngest == -1 || procs[i].start_time >= procs[youngest].start_time)
1501                     {
1502                         youngest = i;
1503                     }
1504                 }
1505             }
1506 
1507             if (youngest != -1)
1508             {
1509                 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1510                     "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1511                     s->fs_path, (long) s->procs[youngest].pid);
1512 
1513                 fcgi_kill(&s->procs[youngest], SIGTERM);
1514 
1515                 victims++;
1516             }
1517 
1518             /*
1519              * If the number of non-victims is less than or equal to
1520              * the minimum that may be running without being killed off,
1521              * don't select any more victims.
1522              */
1523             if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1524             {
1525                 break;
1526             }
1527         }
1528     }
1529 }
1530 
1531 #ifdef WIN32
1532 
1533 /* This is a little bogus, there's gotta be a better way to do this
1534  * Can we use WaitForMultipleObjects() */
1535 #define FCGI_PROC_WAIT_TIME 100
1536 
1537 void child_wait_thread_main(void *dummy) {
1538     fcgi_server *s;
1539     DWORD dwRet = WAIT_TIMEOUT;
1540     int numChildren;
1541     int i;
1542     int waited;
1543 
1544     while (!bTimeToDie) {
1545         waited = 0;
1546 
1547         for (s = fcgi_servers; s != NULL; s = s->next) {
1548             if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1549                 continue;
1550             }
1551             if (s->directive == APP_CLASS_DYNAMIC) {
1552                 numChildren = dynamicMaxClassProcs;
1553             }
1554             else {
1555                 numChildren = s->numProcesses;
1556             }
1557 
1558             for (i=0; i < numChildren; i++) {
1559                 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1560                 {
1561                     DWORD exitStatus = 0;
1562 
1563                     /* timeout is currently set for 100 miliecond */
1564                     /* it may need to be longer or user customizable */
1565                     dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1566 
1567                     waited = 1;
1568 
1569                     if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1570                         /* a child fs has died */
1571                         /* mark the child as dead */
1572 
1573                         GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1574 
1575                         if (s->directive == APP_CLASS_STANDARD) {
1576                             /* restart static app */
1577                             s->procs[i].state = FCGI_START_STATE;
1578                             if (exitStatus != 0) {
1579                                 /* don't bump failure count on exit 0 */
1580                                 s->numFailures++;
1581                             }
1582                         }
1583                         else {
1584                             s->numProcesses--;
1585                             fcgi_dynamic_total_proc_count--;
1586                             FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1587 
1588                             if (s->procs[i].state == FCGI_VICTIM_STATE) {
1589                                 s->procs[i].state = FCGI_KILLED_STATE;
1590                             }
1591                             else {
1592                                 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1593                                 if (exitStatus != 0) {
1594                                     /* don't bump failure count on exit 0 */
1595                                     s->numFailures++;
1596                                 }
1597 
1598                                 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1599                                     s->procs[i].state = FCGI_START_STATE;
1600                                 }
1601                                 else {
1602                                     s->procs[i].state = FCGI_READY_STATE;
1603                                 }
1604                             }
1605                         }
1606 
1607                         ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1608                             "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1609                             (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1610                             s->fs_path, (long) s->procs[i].pid, exitStatus);
1611 
1612                         CloseHandle(s->procs[i].handle);
1613                         CloseHandle(s->procs[i].terminationEvent);
1614                         s->procs[i].handle = INVALID_HANDLE_VALUE;
1615                         s->procs[i].terminationEvent = INVALID_HANDLE_VALUE;
1616                         s->procs[i].pid = -1;
1617 
1618                         /* wake up the main thread */
1619                         SetEvent(fcgi_event_handles[WAKE_EVENT]);
1620                     }
1621                 }
1622             }
1623         }
1624         Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1625     }
1626 }
1627 #endif
1628 
1629 #ifndef WIN32
1630 static void setup_signals(void)
1631 {
1632     struct sigaction sa;
1633 
1634     /* Setup handlers */
1635 
1636     sa.sa_handler = signal_handler;
1637     sigemptyset(&sa.sa_mask);
1638     sa.sa_flags = 0;
1639 
1640     if (sigaction(SIGTERM, &sa, NULL) < 0) {
1641         ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1642         "sigaction(SIGTERM) failed");
1643     }
1644     /* httpd restart */
1645     if (sigaction(SIGHUP, &sa, NULL) < 0) {
1646         ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1647         "sigaction(SIGHUP) failed");
1648     }
1649     /* httpd graceful restart */
1650     if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1651         ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1652         "sigaction(SIGUSR1) failed");
1653     }
1654     /* read messages from request handlers - kill interval expired */
1655     if (sigaction(SIGALRM, &sa, NULL) < 0) {
1656         ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1657         "sigaction(SIGALRM) failed");
1658     }
1659     if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1660         ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1661         "sigaction(SIGCHLD) failed");
1662     }
1663 }
1664 #endif
1665 
1666 #if !defined(WIN32) && !defined(APACHE2)
1667 int fcgi_pm_main(void *dummy, child_info *info)
1668 #else
1669 void fcgi_pm_main(void *dummy)
1670 #endif
1671 {
1672     fcgi_server *s;
1673     unsigned int i;
1674     int read_ready = 0;
1675     int alarmLeft = 0;
1676 
1677 #ifdef WIN32
1678     DWORD dwRet;
1679     HANDLE child_wait_thread = INVALID_HANDLE_VALUE;
1680 #else
1681     int callWaitPid, callDynamicProcs;
1682 #endif
1683 
1684 #ifdef WIN32
1685     /* Add SystemRoot to the dynamic environment */
1686     char ** envp = dynamicEnvp;
1687     for (i = 0; *envp; ++i) {
1688         ++envp;
1689     }
1690     fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1691 
1692 #else
1693 
1694     reduce_privileges();
1695     change_process_name("fcgi-pm");
1696 
1697     close(fcgi_pm_pipe[1]);
1698     setup_signals();
1699 
1700     if (fcgi_wrapper) {
1701         ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1702             "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1703     }
1704 #endif
1705 
1706     /* Initialize AppClass */
1707     for (s = fcgi_servers; s != NULL; s = s->next)
1708     {
1709         if (s->directive != APP_CLASS_STANDARD)
1710             continue;
1711 
1712 #ifdef WIN32
1713         if (s->socket_path)
1714             s->listenFd = 0;
1715 #endif
1716 
1717         for (i = 0; i < s->numProcesses; ++i)
1718             s->procs[i].state = FCGI_START_STATE;
1719     }
1720 
1721 #ifdef WIN32
1722     child_wait_thread = (HANDLE) _beginthread(child_wait_thread_main, 0, NULL);
1723 
1724     if (child_wait_thread == (HANDLE) -1)
1725     {
1726         ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1727             "FastCGI: failed to create process manager's wait thread!");
1728     }
1729 
1730     ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1731         "FastCGI: process manager initialized");
1732 #else
1733     ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1734         "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1735 #endif
1736 
1737     now = time(NULL);
1738 
1739     /*
1740      * Loop until SIGTERM
1741      */
1742     for (;;) {
1743         int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1744 #ifdef WIN32
1745         time_t expire;
1746 #else
1747         pid_t childPid;
1748         int waitStatus;
1749 #endif
1750         unsigned int numChildren;
1751 		unsigned int minServerLife;
1752 
1753         /*
1754          * If we came out of sigsuspend() for any reason other than
1755          * SIGALRM, pick up where we left off.
1756          */
1757         if (alarmLeft)
1758             sleepSeconds = alarmLeft;
1759 
1760         /*
1761          * Examine each configured AppClass for a process that needs
1762          * starting.  Compute the earliest time when the start should
1763          * be attempted, starting it now if the time has passed.  Also,
1764          * remember that we do NOT need to restart externally managed
1765          * FastCGI applications.
1766          */
1767         for (s = fcgi_servers; s != NULL; s = s->next)
1768         {
1769             if (s->directive == APP_CLASS_EXTERNAL)
1770                 continue;
1771 
1772             numChildren = (s->directive == APP_CLASS_DYNAMIC)
1773                 ? dynamicMaxClassProcs
1774                 : s->numProcesses;
1775 
1776             minServerLife = (s->directive == APP_CLASS_DYNAMIC)
1777                 ? dynamicMinServerLife
1778                 : s->minServerLife;
1779 
1780             for (i = 0; i < numChildren; ++i)
1781             {
1782                 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1783                 {
1784                     int restart = (s->procs[i].pid < 0);
1785                     time_t restartTime = s->restartTime;
1786 
1787                     if (s->bad)
1788                     {
1789                         /* we've gone to using the badDelay, the only thing that
1790                            resets bad is when badDelay has expired.  but numFailures
1791                            is only just set below its threshold.  the proc's
1792                            start_times are all reset when the bad is.  the numFailures
1793                            is reset when we see an app run for a period */
1794 
1795                         s->procs[i].start_time = 0;
1796                     }
1797 
1798                     if (s->numFailures > MAX_FAILED_STARTS)
1799                     {
1800                         time_t last_start_time = s->procs[i].start_time;
1801 
1802                         if (last_start_time && now - last_start_time > minServerLife)
1803                         {
1804                             s->bad = 0;
1805                             s->numFailures = 0;
1806                             ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1807                                 "FastCGI:%s server \"%s\" has remained"
1808                                 " running for more than %d seconds, its restart"
1809                                 " interval has been restored to %d seconds",
1810                                 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1811                                 s->fs_path, minServerLife, s->restartDelay);
1812                         }
1813                         else
1814                         {
1815                             unsigned int j;
1816 
1817                             for (j = 0; j < numChildren; ++j)
1818                             {
1819                                 if (s->procs[j].pid <= 0) continue;
1820                                 if (s->procs[j].state != FCGI_RUNNING_STATE) continue;
1821                                 if (s->procs[j].start_time == 0) continue;
1822                                 if (now - s->procs[j].start_time > minServerLife) break;
1823                             }
1824 
1825                             if (j >= numChildren)
1826                             {
1827                                 s->bad = 1;
1828                                 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1829                                     "FastCGI:%s server \"%s\" has failed to remain"
1830                                     " running for %d seconds given %d attempts, its restart"
1831                                     " interval has been backed off to %d seconds",
1832                                     (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1833                                     s->fs_path, minServerLife, MAX_FAILED_STARTS,
1834                                     FAILED_STARTS_DELAY);
1835                             }
1836                             else
1837                             {
1838                                 s->bad = 0;
1839                                 s->numFailures = 0;
1840                                 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1841                                     "FastCGI:%s server \"%s\" has remained"
1842                                     " running for more than %d seconds, its restart"
1843                                     " interval has been restored to %d seconds",
1844                                     (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1845                                     s->fs_path, minServerLife, s->restartDelay);
1846                             }
1847                         }
1848                     }
1849 
1850                     if (s->bad)
1851                     {
1852                         restartTime += FAILED_STARTS_DELAY;
1853                     }
1854                     else
1855                     {
1856                         restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1857                     }
1858 
1859                     if (restartTime <= now)
1860                     {
1861                         if (s->bad)
1862                         {
1863                             s->bad = 0;
1864                             s->numFailures = MAX_FAILED_STARTS;
1865                         }
1866 
1867                         if (s->listenFd < 0 && init_listen_sock(s))
1868                         {
1869                             if (sleepSeconds > s->initStartDelay)
1870                                 sleepSeconds = s->initStartDelay;
1871                             break;
1872                         }
1873 #ifndef WIN32
1874                         if (caughtSigTerm) {
1875                             goto ProcessSigTerm;
1876                         }
1877 #endif
1878                         s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1879                         if (s->procs[i].pid <= 0) {
1880                             ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1881                                 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1882                                 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1883                                 s->fs_path);
1884 
1885                             sleepSeconds = min(sleepSeconds,
1886                                 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1887 
1888                             s->procs[i].pid = -1;
1889                             break;
1890                         }
1891 
1892                         s->procs[i].start_time = now;
1893                         s->restartTime = now;
1894 
1895                         if (s->startTime == 0) {
1896                             s->startTime = now;
1897                         }
1898 
1899                         if (s->directive == APP_CLASS_DYNAMIC) {
1900                             s->numProcesses++;
1901                             fcgi_dynamic_total_proc_count++;
1902                             FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1903                         }
1904 
1905                         s->procs[i].state = FCGI_RUNNING_STATE;
1906 
1907                         if (fcgi_wrapper) {
1908                             ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1909                                 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1910                                 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1911                                 s->fs_path, (long) s->uid, (long) s->gid,
1912                                 restart ? "re" : "", (long) s->procs[i].pid);
1913                         }
1914                         else {
1915                             ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1916                                 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1917                                 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1918                                 s->fs_path, restart ? "re" : "", (long) s->procs[i].pid);
1919                         }
1920                         ap_assert(s->procs[i].pid > 0);
1921                     } else {
1922                         sleepSeconds = min(sleepSeconds, restartTime - now);
1923                     }
1924                 }
1925             }
1926         }
1927 
1928 #ifndef WIN32
1929 
1930         if(caughtSigTerm) {
1931             goto ProcessSigTerm;
1932         }
1933         if((!caughtSigChld) && (!caughtSigAlarm)) {
1934             fd_set rfds;
1935 
1936             alarm(sleepSeconds);
1937 
1938             FD_ZERO(&rfds);
1939             FD_SET(fcgi_pm_pipe[0], &rfds);
1940             read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1941 
1942             alarmLeft = alarm(0);
1943         }
1944         callWaitPid = caughtSigChld;
1945         caughtSigChld = FALSE;
1946         callDynamicProcs = caughtSigAlarm;
1947         caughtSigAlarm = FALSE;
1948 
1949         now = time(NULL);
1950 
1951         /*
1952          * Dynamic fcgi process management
1953          */
1954         if((callDynamicProcs) || (!callWaitPid)) {
1955             dynamic_read_msgs(read_ready);
1956             if(fcgi_dynamic_epoch == 0) {
1957                 fcgi_dynamic_epoch = now;
1958             }
1959             if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1960                     ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1961                 dynamic_kill_idle_fs_procs();
1962                 fcgi_dynamic_epoch = now;
1963             }
1964         }
1965 
1966         if(!callWaitPid) {
1967             continue;
1968         }
1969 
1970         /* We've caught SIGCHLD, so find out who it was using waitpid,
1971          * write a log message and update its data structure. */
1972 
1973         for (;;) {
1974             if (caughtSigTerm)
1975                 goto ProcessSigTerm;
1976 
1977             childPid = waitpid(-1, &waitStatus, WNOHANG);
1978 
1979             if (childPid == -1 || childPid == 0)
1980                 break;
1981 
1982             for (s = fcgi_servers; s != NULL; s = s->next) {
1983                 if (s->directive == APP_CLASS_EXTERNAL)
1984                     continue;
1985 
1986                 if (s->directive == APP_CLASS_DYNAMIC)
1987                     numChildren = dynamicMaxClassProcs;
1988                 else
1989                     numChildren = s->numProcesses;
1990 
1991                 for (i = 0; i < numChildren; i++) {
1992                     if (s->procs[i].pid == childPid)
1993                         goto ChildFound;
1994                 }
1995             }
1996 
1997             /* TODO: print something about this unknown child */
1998             continue;
1999 
2000 ChildFound:
2001             s->procs[i].pid = -1;
2002 
2003             if (s->directive == APP_CLASS_STANDARD) {
2004                 /* Always restart static apps */
2005                 s->procs[i].state = FCGI_START_STATE;
2006                 if (! (WIFEXITED(waitStatus) && (WEXITSTATUS(waitStatus) == 0))) {
2007                     /* don't bump the failure count if the app exited with 0 */
2008                     s->numFailures++;
2009                 }
2010             }
2011             else {
2012                 s->numProcesses--;
2013                 fcgi_dynamic_total_proc_count--;
2014 
2015                 if (s->procs[i].state == FCGI_VICTIM_STATE) {
2016                     s->procs[i].state = FCGI_KILLED_STATE;
2017                 }
2018                 else {
2019                     /* A dynamic app died or exited without provocation from the PM */
2020 
2021                     if (! (WIFEXITED(waitStatus) && (WEXITSTATUS(waitStatus) == 0))) {
2022                         /* don't bump the failure count if the app exited with 0 */
2023                         s->numFailures++;
2024                     }
2025 
2026                     if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
2027                         s->procs[i].state = FCGI_START_STATE;
2028                     else
2029                         s->procs[i].state = FCGI_READY_STATE;
2030                 }
2031             }
2032 
2033             if (WIFEXITED(waitStatus)) {
2034                 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
2035                     "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
2036                     (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
2037                     s->fs_path, (long) childPid, WEXITSTATUS(waitStatus));
2038             }
2039             else if (WIFSIGNALED(waitStatus)) {
2040                 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
2041                     "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
2042                     (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
2043                     s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus),
2044 #ifdef WCOREDUMP
2045                     WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
2046 #else
2047                     "");
2048 #endif
2049             }
2050             else if (WIFSTOPPED(waitStatus)) {
2051                 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
2052                     "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
2053                     (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
2054                     s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus));
2055             }
2056         } /* for (;;), waitpid() */
2057 
2058 #else /* WIN32 */
2059 
2060         /* wait for an event to occur or timer expires */
2061         expire = time(NULL) + sleepSeconds;
2062         dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
2063 
2064         if (dwRet == WAIT_FAILED) {
2065             /* There is something seriously wrong here */
2066             ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
2067                 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
2068                 bTimeToDie = TRUE;
2069         }
2070 
2071         if (dwRet != WAIT_TIMEOUT) {
2072            now = time(NULL);
2073 
2074            if (now < expire)
2075                alarmLeft = expire - now;
2076         }
2077 
2078         /*
2079          * Dynamic fcgi process management
2080          */
2081         if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
2082             if (dwRet == MBOX_EVENT) {
2083                 read_ready = 1;
2084             }
2085 
2086             now = time(NULL);
2087 
2088             dynamic_read_msgs(read_ready);
2089 
2090             if(fcgi_dynamic_epoch == 0) {
2091                 fcgi_dynamic_epoch = now;
2092             }
2093 
2094             if ((now-fcgi_dynamic_epoch >= (int) dynamicKillInterval) ||
2095                ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
2096                 dynamic_kill_idle_fs_procs();
2097                 fcgi_dynamic_epoch = now;
2098             }
2099             read_ready = 0;
2100         }
2101         else if (dwRet == WAKE_EVENT) {
2102             continue;
2103         }
2104         else if (dwRet == TERM_EVENT) {
2105             ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
2106                 "FastCGI: Termination event received process manager shutting down");
2107 
2108             bTimeToDie = TRUE;
2109             dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2110 
2111             goto ProcessSigTerm;
2112         }
2113         else {
2114             /* Have an received an unknown event - should not happen */
2115             ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
2116                 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
2117 
2118             bTimeToDie = TRUE;
2119             dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2120 
2121             goto ProcessSigTerm;
2122         }
2123 
2124 #endif /* WIN32 */
2125 
2126     } /* for (;;), the whole shoot'n match */
2127 
2128 ProcessSigTerm:
2129     /*
2130      * Kill off the children, then exit.
2131      */
2132     shutdown_all();
2133 
2134 #ifdef WIN32
2135     return;
2136 #else
2137     exit(0);
2138 #endif
2139 }
2140 
2141 #ifdef WIN32
2142 int fcgi_pm_add_job(fcgi_pm_job *new_job)
2143 {
2144     int rv = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
2145 
2146     if (rv != WAIT_OBJECT_0 && rv != WAIT_ABANDONED)
2147     {
2148         ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2149             "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2150         return -1;
2151     }
2152 
2153     new_job->next = fcgi_dynamic_mbox;
2154     fcgi_dynamic_mbox = new_job;
2155 
2156     if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
2157     {
2158         ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2159             "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
2160     }
2161 
2162     return 0;
2163 }
2164 #endif
2165