"Fossies" - the Fresh Open Source Software Archive

Member "mod_fastcgi-2.4.7-0910052141/fcgi_pm.c" (10 Apr 2012, 69098 Bytes) of package /linux/www/apache_httpd_modules/old/mod_fastcgi-2.4.7-0910052141.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "fcgi_pm.c": 2.4.6_vs_2.4.7-0910052141.

    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
   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 
   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  */
   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  */
  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 
  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 
  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  */
  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
  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  */
  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 
  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 
  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