2 * $Id: fcgi_pm.c,v 1.70 2002/02/28 22:52:50 robs Exp $
10 #define seteuid(arg) setresuid(-1, (arg), -1)
13 int fcgi_dynamic_total_proc_count
= 0; /* number of running apps */
14 time_t fcgi_dynamic_epoch
= 0; /* last time kill_procs was
15 * invoked by process mgr */
16 time_t fcgi_dynamic_last_analyzed
= 0; /* last time calculation was
17 * made for the dynamic procs */
19 static time_t now
= 0;
22 #pragma warning ( disable : 4100 4102 )
23 static BOOL bTimeToDie
= FALSE
; /* process termination flag */
24 HANDLE fcgi_event_handles
[3];
32 static int seteuid_root(void)
34 int rc
= seteuid((uid_t
)0);
36 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
37 "FastCGI: seteuid(0) failed");
42 static int seteuid_user(void)
44 int rc
= seteuid(ap_user_id
);
46 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
47 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id
);
54 * Signal the process to exit. How (or if) the process responds
55 * depends on the FastCGI application library (esp. on Win32) and
56 * possibly application code (signal handlers and whether or not
57 * SA_RESTART is on). At any rate, we send the signal with the
58 * hopes that the process will exit on its own. Later, as we
59 * review the state of application processes, if we see one marked
60 * for death, but that hasn't died within a specified period of
61 * time, fcgi_kill() is called again with a KILL)
63 static void fcgi_kill(ServerProcess
*process
, int sig
)
65 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process
->pid
, sig
);
67 process
->state
= FCGI_VICTIM_STATE
;
72 SetEvent(process
->terminationEvent
);
74 else if (sig
== SIGKILL
)
76 TerminateProcess(process
->handle
, 1);
88 kill(process
->pid
, sig
);
97 /*******************************************************************************
98 * Send SIGTERM to each process in the server class, remove socket
99 * file if appropriate. Currently this is only called when the PM is shutting
100 * down and thus memory isn't freed and sockets and files aren't closed.
102 static void shutdown_all()
104 fcgi_server
*s
= fcgi_servers
;
108 ServerProcess
*proc
= s
->procs
;
110 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
111 ? dynamicMaxClassProcs
115 if (s
->socket_path
!= NULL
&& s
->directive
!= APP_CLASS_EXTERNAL
)
117 /* Remove the socket file */
118 if (unlink(s
->socket_path
) != 0 && errno
!= ENOENT
) {
119 ap_log_error(FCGI_LOG_ERR
, fcgi_apache_main_server
,
120 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
122 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "", s
->fs_path
);
127 /* Send TERM to all processes */
128 for (i
= 0; i
< numChildren
; i
++, proc
++)
130 if (proc
->state
== FCGI_RUNNING_STATE
)
132 fcgi_kill(proc
, SIGTERM
);
139 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
142 * WIN32 applications may not have support for the shutdown event
143 * depending on their application library version
146 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT
);
151 ServerProcess
*proc
= s
->procs
;
153 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
154 ? dynamicMaxClassProcs
157 /* Send KILL to all processes */
158 for (i
= 0; i
< numChildren
; i
++, proc
++)
160 if (proc
->state
== FCGI_RUNNING_STATE
)
162 fcgi_kill(proc
, SIGKILL
);
172 static int init_listen_sock(fcgi_server
* fs
)
174 ap_assert(fs
->directive
!= APP_CLASS_EXTERNAL
);
176 /* Create the socket */
177 if ((fs
->listenFd
= socket(fs
->socket_addr
->sa_family
, SOCK_STREAM
, 0)) < 0)
180 errno
= WSAGetLastError(); // Not sure if this will work as expected
182 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
183 "FastCGI: can't create %sserver \"%s\": socket() failed",
184 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
190 if (fs
->socket_addr
->sa_family
== AF_UNIX
)
192 /* Remove any existing socket file.. just in case */
193 unlink(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
);
199 setsockopt(fs
->listenFd
, SOL_SOCKET
, SO_REUSEADDR
, (char *)&flag
, sizeof(flag
));
202 /* Bind it to the socket_addr */
203 if (bind(fs
->listenFd
, fs
->socket_addr
, fs
->socket_addr_len
))
208 errno
= WSAGetLastError();
210 ap_snprintf(port
, sizeof(port
), "port=%d",
211 ((struct sockaddr_in
*)fs
->socket_addr
)->sin_port
);
213 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
214 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
215 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
218 (fs
->socket_addr
->sa_family
== AF_UNIX
) ?
219 ((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
:
225 /* Twiddle Unix socket permissions */
226 else if (fs
->socket_addr
->sa_family
== AF_UNIX
227 && chmod(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
, S_IRUSR
| S_IWUSR
))
229 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
230 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
231 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
237 else if (listen(fs
->listenFd
, fs
->listenQueueDepth
))
240 errno
= WSAGetLastError();
242 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
243 "FastCGI: can't create %sserver \"%s\": listen() failed",
244 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
252 ap_pclosesocket(fcgi_config_pool
, fs
->listenFd
);
258 *----------------------------------------------------------------------
262 * The FastCGI process manager, which runs as a separate
263 * process responsible for:
264 * - Starting all the FastCGI proceses.
265 * - Restarting any of these processes that die (indicated
267 * - Catching SIGTERM and relaying it to all the FastCGI
268 * processes before exiting.
271 * Uses global variable fcgi_servers.
279 *----------------------------------------------------------------------
282 static int caughtSigTerm
= FALSE
;
283 static int caughtSigChld
= FALSE
;
284 static int caughtSigAlarm
= FALSE
;
286 static void signal_handler(int signo
)
288 if ((signo
== SIGTERM
) || (signo
== SIGUSR1
) || (signo
== SIGHUP
)) {
289 /* SIGUSR1 & SIGHUP are sent by apache to its process group
290 * when apache get 'em. Apache follows up (1.2.x) with attacks
291 * on each of its child processes, but we've got the KillMgr
292 * sitting between us so we never see the KILL. The main loop
293 * in ProcMgr also checks to see if the KillMgr has terminated,
294 * and if it has, we handl it as if we should shutdown too. */
295 caughtSigTerm
= TRUE
;
296 } else if(signo
== SIGCHLD
) {
297 caughtSigChld
= TRUE
;
298 } else if(signo
== SIGALRM
) {
299 caughtSigAlarm
= TRUE
;
305 *----------------------------------------------------------------------
307 * spawn_fs_process --
309 * Fork and exec the specified fcgi process.
312 * 0 for successful fork, -1 for failed fork.
314 * In case the child fails before or in the exec, the child
315 * obtains the error log by calling getErrLog, logs
316 * the error, and exits with exit status = errno of
317 * the failed system call.
320 * Child process created.
322 *----------------------------------------------------------------------
324 static pid_t
spawn_fs_process(fcgi_server
*fs
, ServerProcess
*process
)
330 char *dnEnd
, *failedSysCall
;
337 /* We're the child. We're gonna exec() so pools don't matter. */
339 dnEnd
= strrchr(fs
->fs_path
, '/');
343 dirName
= ap_pcalloc(fcgi_config_pool
, dnEnd
- fs
->fs_path
+ 1);
344 dirName
= memcpy(dirName
, fs
->fs_path
, dnEnd
- fs
->fs_path
);
346 if (chdir(dirName
) < 0) {
347 failedSysCall
= "chdir()";
348 goto FailedSystemCallExit
;
352 /* OS/2 dosen't support nice() */
353 if (fs
->processPriority
!= 0) {
354 if (nice(fs
->processPriority
) == -1) {
355 failedSysCall
= "nice()";
356 goto FailedSystemCallExit
;
361 /* Open the listenFd on spec'd fd */
362 if (fs
->listenFd
!= FCGI_LISTENSOCK_FILENO
)
363 dup2(fs
->listenFd
, FCGI_LISTENSOCK_FILENO
);
365 /* Close all other open fds, except stdout/stderr. Leave these two open so
366 * FastCGI applications don't have to find and fix ALL 3rd party libs that
367 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
368 * main server error_log - @@@ provide a directive control where this goes.
370 ap_error_log2stderr(fcgi_apache_main_server
);
371 dup2(STDERR_FILENO
, STDOUT_FILENO
);
372 for (i
= 0; i
< FCGI_MAX_FD
; i
++) {
373 if (i
!= FCGI_LISTENSOCK_FILENO
&& i
!= STDERR_FILENO
&& i
!= STDOUT_FILENO
) {
378 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
379 * install its own handler. */
380 signal(SIGPIPE
, SIG_IGN
);
382 if (fcgi_wrapper
&& (fcgi_user_id
!= fs
->uid
|| fcgi_group_id
!= fs
->gid
)) {
383 char *shortName
= strrchr(fs
->fs_path
, '/') + 1;
385 /* Relinquish our root real uid powers */
390 execle(fcgi_wrapper
, fcgi_wrapper
, fs
->username
, fs
->group
, shortName
, NULL
, fs
->envp
);
391 } while (errno
== EINTR
);
395 execle(fs
->fs_path
, fs
->fs_path
, NULL
, fs
->envp
);
396 } while (errno
== EINTR
);
399 failedSysCall
= "execle()";
401 FailedSystemCallExit
:
402 fprintf(stderr
, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
403 fs
->fs_path
, (long) getpid(), failedSysCall
, strerror(errno
));
406 /* avoid an irrelevant compiler warning */
411 /* Adapted from Apache's util_script.c ap_call_exec() */
412 char *interpreter
= NULL
;
413 char *quoted_filename
;
415 char *pEnvBlock
, *pNext
;
418 int iEnvBlockLen
= 1;
420 file_type_e fileType
;
423 PROCESS_INFORMATION pi
;
428 pool
* tp
= ap_make_sub_pool(fcgi_config_pool
);
430 HANDLE listen_handle
;
431 char * termination_env_string
= NULL
;
433 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
434 if (process
->terminationEvent
== NULL
)
436 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
437 "FastCGI: can't create termination event for server \"%s\", "
438 "CreateEvent() failed", fs
->fs_path
);
441 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
443 termination_env_string
= ap_psprintf(tp
,
444 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
448 SECURITY_ATTRIBUTES sa
;
450 sa
.lpSecurityDescriptor
= NULL
;
451 sa
.bInheritHandle
= TRUE
;
452 sa
.nLength
= sizeof(sa
);
454 listen_handle
= CreateNamedPipe(fs
->socket_path
,
456 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
457 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
459 if (listen_handle
== INVALID_HANDLE_VALUE
)
461 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
462 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs
->fs_path
);
468 listen_handle
= (HANDLE
) fs
->listenFd
;
471 memset(&si
, 0, sizeof(si
));
472 memset(&pi
, 0, sizeof(pi
));
473 memset(&r
, 0, sizeof(r
));
475 // Can up a fake request to pass to ap_get_win32_interpreter()
476 r
.per_dir_config
= fcgi_apache_main_server
->lookup_defaults
;
477 r
.server
= fcgi_apache_main_server
;
478 r
.filename
= (char *) fs
->fs_path
;
481 fileType
= ap_get_win32_interpreter(&r
, &interpreter
);
483 if (fileType
== eFileTypeUNKNOWN
) {
484 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
485 "FastCGI: %s is not executable; ensure interpreted scripts have "
486 "\"#!\" as their first line",
493 * We have the interpreter (if there is one) and we have
494 * the arguments (if there are any).
495 * Build the command string to pass to CreateProcess.
497 quoted_filename
= ap_pstrcat(tp
, "\"", fs
->fs_path
, "\"", NULL
);
498 if (interpreter
&& *interpreter
) {
499 pCommand
= ap_pstrcat(tp
, interpreter
, " ", quoted_filename
, NULL
);
502 pCommand
= quoted_filename
;
506 * Make child process use hPipeOutputWrite as standard out,
507 * and make sure it does not show on screen.
510 si
.dwFlags
= STARTF_USESHOWWINDOW
| STARTF_USESTDHANDLES
;
511 si
.wShowWindow
= SW_HIDE
;
512 si
.hStdInput
= listen_handle
;
514 // XXX These should be open to the error_log
515 si
.hStdOutput
= INVALID_HANDLE_VALUE
;
516 si
.hStdError
= INVALID_HANDLE_VALUE
;
519 * Win32's CreateProcess call requires that the environment
520 * be passed in an environment block, a null terminated block of
521 * null terminated strings.
522 * @todo we should store the env in this format for win32.
526 iEnvBlockLen
+= strlen(fs
->envp
[i
]) + 1;
530 iEnvBlockLen
+= strlen(termination_env_string
) + 1;
531 iEnvBlockLen
+= strlen(fs
->mutex_env_string
) + 1;
533 pEnvBlock
= (char *) ap_pcalloc(tp
, iEnvBlockLen
);
539 strcpy(pNext
, fs
->envp
[i
]);
540 pNext
+= strlen(pNext
) + 1;
544 strcpy(pNext
, termination_env_string
);
545 pNext
+= strlen(pNext
) + 1;
546 strcpy(pNext
, fs
->mutex_env_string
);
548 if (CreateProcess(NULL
, pCommand
, NULL
, NULL
, TRUE
,
551 ap_make_dirstr_parent(tp
, fs
->fs_path
),
554 /* Hack to get 16-bit CGI's working. It works for all the
555 * standard modules shipped with Apache. pi.dwProcessId is 0
556 * for 16-bit CGIs and all the Unix specific code that calls
557 * ap_call_exec interprets this as a failure case. And we can't
558 * use -1 either because it is mapped to 0 by the caller.
560 pid
= (fileType
== eFileTypeEXE16
) ? -2 : pi
.dwProcessId
;
562 process
->handle
= pi
.hProcess
;
563 CloseHandle(pi
.hThread
);
568 CloseHandle(listen_handle
);
579 static void reduce_privileges(void)
587 /* Get username if passed as a uid */
588 if (ap_user_name
[0] == '#') {
589 uid_t uid
= atoi(&ap_user_name
[1]);
590 struct passwd
*ent
= getpwuid(uid
);
593 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
594 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
595 "you probably need to modify the User directive", (unsigned)uid
);
604 if (setgid(ap_group_id
) == -1) {
605 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
606 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id
);
610 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
612 /* Initialize supplementary groups */
613 if (initgroups(name
, ap_group_id
) == -1) {
614 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
615 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
616 name
, (unsigned)ap_group_id
);
623 if (seteuid_user() == -1) {
624 ap_log_error(FCGI_LOG_ALERT_NOERRNO
, fcgi_apache_main_server
,
625 "FastCGI: process manager exiting, failed to reduce privileges");
630 if (setuid(ap_user_id
) == -1) {
631 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
632 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id
);
639 * Change the name of this process - best we can easily.
641 static void change_process_name(const char * const name
)
643 strncpy(ap_server_argv0
, name
, strlen(ap_server_argv0
));
647 static void schedule_start(fcgi_server
*s
, int proc
)
649 /* If we've started one recently, don't register another */
650 time_t time_passed
= now
- s
->restartTime
;
652 if ((s
->procs
[proc
].pid
&& (time_passed
< (int) s
->restartDelay
))
653 || ((s
->procs
[proc
].pid
== 0) && (time_passed
< s
->initStartDelay
)))
655 FCGIDBG6("ignore_job: slot=%d, pid=%ld, time_passed=%ld, initStartDelay=%ld, restartDelay=%ld", proc
, (long) s
->procs
[proc
].pid
, time_passed
, s
->initStartDelay
, s
->restartDelay
);
659 FCGIDBG3("scheduling_start: %s (%d)", s
->fs_path
, proc
);
660 s
->procs
[proc
].state
= FCGI_START_STATE
;
661 if (proc
== dynamicMaxClassProcs
- 1) {
662 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
663 "FastCGI: scheduled the %sstart of the last (dynamic) server "
664 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
665 s
->procs
[proc
].pid
? "re" : "", s
->fs_path
, dynamicMaxClassProcs
);
670 *----------------------------------------------------------------------
674 * Removes the records written by request handlers and decodes them.
675 * We also update the data structures to reflect the changes.
677 *----------------------------------------------------------------------
680 static void dynamic_read_msgs(int read_ready
)
686 static int buflen
= 0;
687 static char buf
[FCGI_MSGS_BUFSIZE
+ 1];
688 char *ptr1
, *ptr2
, opcode
;
689 char execName
[FCGI_MAXPATH
+ 1];
690 char user
[MAX_USER_NAME_LEN
+ 2];
691 char group
[MAX_GID_CHAR_LEN
+ 1];
692 unsigned long q_usec
= 0UL, req_usec
= 0UL;
694 fcgi_pm_job
*joblist
= NULL
;
695 fcgi_pm_job
*cjob
= NULL
;
698 pool
*sp
= NULL
, *tp
;
701 user
[MAX_USER_NAME_LEN
+ 1] = group
[MAX_GID_CHAR_LEN
] = '\0';
705 * To prevent the idle application from running indefinitely, we
706 * check the timer and if it is expired, we recompute the values
707 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
708 * message is received, only updates are made to the data structures.
710 if (fcgi_dynamic_last_analyzed
== 0) {
711 fcgi_dynamic_last_analyzed
= now
;
713 if ((now
- fcgi_dynamic_last_analyzed
) >= (int)dynamicUpdateInterval
) {
714 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
715 if (s
->directive
!= APP_CLASS_DYNAMIC
)
718 /* Advance the last analyzed timestamp by the elapsed time since
719 * it was last set. Round the increase down to the nearest
720 * multiple of dynamicUpdateInterval */
722 fcgi_dynamic_last_analyzed
+= (((long)(now
-fcgi_dynamic_last_analyzed
)/dynamicUpdateInterval
)*dynamicUpdateInterval
);
723 s
->smoothConnTime
= (unsigned long) ((1.0-dynamicGain
)*s
->smoothConnTime
+ dynamicGain
*s
->totalConnTime
);
724 s
->totalConnTime
= 0UL;
725 s
->totalQueueTime
= 0UL;
729 if (read_ready
<= 0) {
734 rc
= read(fcgi_pm_pipe
[0], (void *)(buf
+ buflen
), FCGI_MSGS_BUFSIZE
- buflen
);
736 if (!caughtSigTerm
) {
737 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
738 "FastCGI: read() from pipe failed (%d)", rc
);
740 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
741 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
742 caughtSigTerm
= TRUE
;
752 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
753 * request to do something) and/or when a timeout expires.
754 * There really should be no reason why this wait would get stuck
755 * but there's no point in waiting forever. */
757 rc
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
759 if (rc
!= WAIT_OBJECT_0
&& rc
!= WAIT_ABANDONED
)
761 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
762 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
766 joblist
= fcgi_dynamic_mbox
;
767 fcgi_dynamic_mbox
= NULL
;
769 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
771 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
772 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
778 tp
= ap_make_sub_pool(fcgi_config_pool
);
781 for (ptr1
= buf
; ptr1
; ptr1
= ptr2
) {
784 ptr2
= strchr(ptr1
, '*');
796 case FCGI_SERVER_START_JOB
:
797 case FCGI_SERVER_RESTART_JOB
:
799 if (sscanf(ptr1
, "%c %s %16s %15s",
800 &opcode
, execName
, user
, group
) != 4)
806 case FCGI_REQUEST_TIMEOUT_JOB
:
808 if (sscanf(ptr1
, "%c %s %16s %15s",
809 &opcode
, execName
, user
, group
) != 4)
815 case FCGI_REQUEST_COMPLETE_JOB
:
817 if (sscanf(ptr1
, "%c %s %16s %15s %lu %lu",
818 &opcode
, execName
, user
, group
, &q_usec
, &req_usec
) != 6)
831 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
832 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1
);
836 /* Update data structures for processing */
837 while (cjob
!= NULL
) {
838 joblist
= cjob
->next
;
839 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob
->id
, cjob
->fs_path
, cjob
->user
, cjob
->group
, cjob
->qsec
, cjob
->start_time
);
843 s
= fcgi_util_fs_get(execName
, user
, group
);
845 s
= fcgi_util_fs_get(cjob
->fs_path
, cjob
->user
, cjob
->group
);
849 if (s
==NULL
&& opcode
!= FCGI_REQUEST_COMPLETE_JOB
)
851 if (s
==NULL
&& cjob
->id
!= FCGI_REQUEST_COMPLETE_JOB
)
856 HANDLE mutex
= CreateMutex(NULL
, FALSE
, cjob
->fs_path
);
860 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
861 "FastCGI: can't create accept mutex "
862 "for (dynamic) server \"%s\"", cjob
->fs_path
);
866 SetHandleInformation(mutex
, HANDLE_FLAG_INHERIT
, TRUE
);
871 /* Create a perm subpool to hold the new server data,
872 * we can destroy it if something doesn't pan out */
873 sp
= ap_make_sub_pool(fcgi_config_pool
);
875 /* Create a new "dynamic" server */
876 s
= fcgi_util_fs_new(sp
);
878 s
->directive
= APP_CLASS_DYNAMIC
;
879 s
->restartDelay
= dynamicRestartDelay
;
880 s
->listenQueueDepth
= dynamicListenQueueDepth
;
881 s
->initStartDelay
= dynamicInitStartDelay
;
882 s
->envp
= dynamicEnvp
;
883 s
->flush
= dynamicFlush
;
886 s
->mutex_env_string
= ap_psprintf(sp
, "_FCGI_MUTEX_=%ld", mutex
);
887 s
->fs_path
= ap_pstrdup(sp
, cjob
->fs_path
);
889 s
->fs_path
= ap_pstrdup(sp
, execName
);
891 ap_getparents(s
->fs_path
);
892 ap_no2slash(s
->fs_path
);
893 s
->procs
= fcgi_util_fs_create_procs(sp
, dynamicMaxClassProcs
);
895 /* XXX the socket_path (both Unix and Win) *is* deducible and
896 * thus can and will be used by other apache instances without
897 * the use of shared data regarding the processes serving the
898 * requests. This can result in slightly unintuitive process
899 * counts and security implications. This is prevented
900 * if suexec (Unix) is in use. This is both a feature and a flaw.
901 * Changing it now would break existing installations. */
904 /* Create socket file's path */
905 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, execName
, user
, group
);
906 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
908 /* Create sockaddr, prealloc it so it won't get created in tp */
909 s
->socket_addr
= ap_pcalloc(sp
, sizeof(struct sockaddr_un
));
910 err
= fcgi_util_socket_make_domain_addr(tp
, (struct sockaddr_un
**)&s
->socket_addr
,
911 &s
->socket_addr_len
, s
->socket_path
);
913 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
914 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
918 if (init_listen_sock(s
)) {
922 /* If a wrapper is being used, config user/group info */
924 if (user
[0] == '~') {
925 /* its a user dir uri, the rest is a username, not a uid */
926 struct passwd
*pw
= getpwnam(&user
[1]);
929 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
930 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
935 s
->user
= ap_pstrdup(sp
, user
);
936 s
->username
= s
->user
;
939 s
->group
= ap_psprintf(sp
, "%ld", (long)s
->gid
);
944 s
->uid
= (uid_t
)atol(user
);
945 pw
= getpwuid(s
->uid
);
947 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
948 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
949 execName
, (long)s
->uid
);
952 s
->user
= ap_pstrdup(sp
, user
);
953 s
->username
= ap_pstrdup(sp
, pw
->pw_name
);
955 s
->gid
= (gid_t
)atol(group
);
956 s
->group
= ap_pstrdup(sp
, group
);
960 /* Create socket file's path */
961 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, cjob
->fs_path
, cjob
->user
, cjob
->group
);
962 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
970 if (opcode
== FCGI_SERVER_RESTART_JOB
) {
972 if (cjob
->id
==FCGI_SERVER_RESTART_JOB
) {
974 /* Check to see if the binary has changed. If so,
975 * kill the FCGI application processes, and
982 if ((stat(execName
, &stbuf
)==0) &&
984 if ((stat(cjob
->fs_path
, &stbuf
)==0) &&
986 (stbuf
.st_mtime
> s
->restartTime
)) {
987 /* kill old server(s) */
988 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
989 if (s
->procs
[i
].pid
> 0) {
990 fcgi_kill(&s
->procs
[i
], SIGTERM
);
994 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
995 "FastCGI: restarting server \"%s\" processes, newer version found",
1003 /* If dynamicAutoRestart, don't mark any new processes
1004 * for starting because we probably got the
1005 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1006 * will be restarting all of those we just killed.
1008 if (dynamicAutoRestart
)
1012 else if (opcode
== FCGI_SERVER_START_JOB
) {
1014 else if (cjob
->id
==FCGI_SERVER_START_JOB
) {
1016 /* we've been asked to start a process--only start
1017 * it if we're not already running at least one
1022 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1023 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1026 /* if already running, don't start another one */
1027 if (i
< dynamicMaxClassProcs
) {
1041 case FCGI_SERVER_RESTART_JOB
:
1045 /* We just waxed 'em all. Try to find an idle slot. */
1047 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1049 if (s
->procs
[i
].state
== FCGI_START_STATE
1050 || s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1054 else if (s
->procs
[i
].state
== FCGI_KILLED_STATE
1055 || s
->procs
[i
].state
== FCGI_READY_STATE
)
1062 /* Nope, just use the first slot */
1063 if (i
== dynamicMaxClassProcs
)
1071 schedule_start(s
, i
);
1076 case FCGI_SERVER_START_JOB
:
1077 case FCGI_REQUEST_TIMEOUT_JOB
:
1079 if ((fcgi_dynamic_total_proc_count
+ 1) > (int) dynamicMaxProcs
) {
1081 * Extra instances should have been
1082 * terminated beforehand, probably need
1083 * to increase ProcessSlack parameter
1085 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1086 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1087 "exceeded dynamicMaxProcs (%d)", s
->fs_path
, dynamicMaxProcs
);
1091 /* find next free slot */
1092 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1094 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1096 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i
);
1099 else if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1104 schedule_start(s
, i
);
1109 if (i
>= dynamicMaxClassProcs
) {
1110 FCGIDBG1("ignore_job: slots are max'd");
1114 case FCGI_REQUEST_COMPLETE_JOB
:
1115 /* only record stats if we have a structure */
1118 s
->totalConnTime
+= req_usec
;
1119 s
->totalQueueTime
+= q_usec
;
1121 s
->totalConnTime
+= cjob
->start_time
;
1122 s
->totalQueueTime
+= cjob
->qsec
;
1131 /* Cleanup job data */
1132 free(cjob
->fs_path
);
1142 if (sp
) ap_destroy_pool(sp
);
1145 free(cjob
->fs_path
);
1153 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1154 "FastCGI: really bogus message: \"%s\"", ptr1
);
1155 ptr1
+= strlen(buf
);
1158 buflen
-= ptr1
- buf
;
1160 memmove(buf
, ptr1
, buflen
);
1164 ap_destroy_pool(tp
);
1168 *----------------------------------------------------------------------
1170 * dynamic_kill_idle_fs_procs
1172 * Implement a kill policy for the dynamic FastCGI applications.
1173 * We also update the data structures to reflect the changes.
1176 * Processes are marked for deletion possibly killed.
1178 *----------------------------------------------------------------------
1180 static void dynamic_kill_idle_fs_procs(void)
1185 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1188 * server's smoothed running time, or if that's 0, the current total
1190 unsigned long connTime
;
1193 * maximum number of microseconds that all of a server's running
1194 * processes together could have spent running since the last check
1196 unsigned long totalTime
;
1199 * percentage, 0-100, of totalTime that the processes actually used
1204 int really_running
= 0;
1206 if (s
->directive
!= APP_CLASS_DYNAMIC
|| s
->numProcesses
== 0)
1211 /* s->numProcesses includes pending kills so get the "active" count */
1212 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1214 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
) ++really_running
;
1217 connTime
= s
->smoothConnTime
? s
->smoothConnTime
: s
->totalConnTime
;
1218 totalTime
= really_running
* (now
- fcgi_dynamic_epoch
) * 1000000 + 1;
1220 loadFactor
= 100 * connTime
/ totalTime
;
1222 if (really_running
== 1)
1224 if (loadFactor
>= dynamicThreshold1
)
1231 int load
= really_running
/ ( really_running
- 1) * loadFactor
;
1233 if (load
>= dynamicThresholdN
)
1240 * Run through the procs to see if we can get away w/o waxing one.
1242 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1244 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1246 s
->procs
[i
].state
= FCGI_READY_STATE
;
1249 else if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
)
1255 if (i
>= dynamicMaxClassProcs
)
1257 ServerProcess
* procs
= s
->procs
;
1260 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1262 if (procs
[i
].state
== FCGI_RUNNING_STATE
)
1264 if (youngest
== -1 || procs
[i
].start_time
>= procs
[youngest
].start_time
)
1273 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1274 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1275 s
->fs_path
, (long) s
->procs
[youngest
].pid
);
1277 fcgi_kill(&s
->procs
[youngest
], SIGTERM
);
1283 * If the number of non-victims is less than or equal to
1284 * the minimum that may be running without being killed off,
1285 * don't select any more victims.
1287 if (fcgi_dynamic_total_proc_count
- victims
<= dynamicMinProcs
)
1297 // This is a little bogus, there's gotta be a better way to do this
1298 // Can we use WaitForMultipleObjects()
1299 #define FCGI_PROC_WAIT_TIME 100
1301 void child_wait_thread_main(void *dummy
) {
1303 DWORD dwRet
= WAIT_TIMEOUT
;
1308 while (!bTimeToDie
) {
1311 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1312 if (s
->directive
== APP_CLASS_EXTERNAL
|| s
->listenFd
< 0) {
1315 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1316 numChildren
= dynamicMaxClassProcs
;
1319 numChildren
= s
->numProcesses
;
1322 for (i
=0; i
< numChildren
; i
++) {
1323 if (s
->procs
[i
].handle
!= INVALID_HANDLE_VALUE
)
1325 DWORD exitStatus
= 0;
1327 /* timeout is currently set for 100 miliecond */
1328 /* it may need to be longer or user customizable */
1329 dwRet
= WaitForSingleObject(s
->procs
[i
].handle
, FCGI_PROC_WAIT_TIME
);
1333 if (dwRet
!= WAIT_TIMEOUT
&& dwRet
!= WAIT_FAILED
) {
1334 /* a child fs has died */
1335 /* mark the child as dead */
1337 if (s
->directive
== APP_CLASS_STANDARD
) {
1338 /* restart static app */
1339 s
->procs
[i
].state
= FCGI_START_STATE
;
1344 fcgi_dynamic_total_proc_count
--;
1345 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1347 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1348 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1351 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1354 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0)) {
1355 s
->procs
[i
].state
= FCGI_START_STATE
;
1358 s
->procs
[i
].state
= FCGI_READY_STATE
;
1363 GetExitCodeProcess(s
->procs
[i
].handle
, &exitStatus
);
1365 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1366 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1367 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1368 s
->fs_path
, (long) s
->procs
[i
].pid
, exitStatus
);
1370 CloseHandle(s
->procs
[i
].handle
);
1371 s
->procs
[i
].handle
= INVALID_HANDLE_VALUE
;
1372 s
->procs
[i
].pid
= -1;
1374 /* wake up the main thread */
1375 SetEvent(fcgi_event_handles
[WAKE_EVENT
]);
1380 Sleep(waited
? 0 : FCGI_PROC_WAIT_TIME
);
1386 static void setup_signals(void)
1388 struct sigaction sa
;
1390 /* Setup handlers */
1392 sa
.sa_handler
= signal_handler
;
1393 sigemptyset(&sa
.sa_mask
);
1396 if (sigaction(SIGTERM
, &sa
, NULL
) < 0) {
1397 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1398 "sigaction(SIGTERM) failed");
1401 if (sigaction(SIGHUP
, &sa
, NULL
) < 0) {
1402 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1403 "sigaction(SIGHUP) failed");
1405 /* httpd graceful restart */
1406 if (sigaction(SIGUSR1
, &sa
, NULL
) < 0) {
1407 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1408 "sigaction(SIGUSR1) failed");
1410 /* read messages from request handlers - kill interval expired */
1411 if (sigaction(SIGALRM
, &sa
, NULL
) < 0) {
1412 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1413 "sigaction(SIGALRM) failed");
1415 if (sigaction(SIGCHLD
, &sa
, NULL
) < 0) {
1416 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1417 "sigaction(SIGCHLD) failed");
1423 int fcgi_pm_main(void *dummy
, child_info
*info
)
1425 void fcgi_pm_main(void *dummy
)
1435 HANDLE child_wait_thread
= INVALID_HANDLE_VALUE
;
1437 int callWaitPid
, callDynamicProcs
;
1441 // Add SystemRoot to the dynamic environment
1442 char ** envp
= dynamicEnvp
;
1443 for (i
= 0; *envp
; ++i
) {
1446 fcgi_config_set_env_var(fcgi_config_pool
, dynamicEnvp
, &i
, "SystemRoot");
1449 reduce_privileges();
1451 close(fcgi_pm_pipe
[1]);
1452 change_process_name("fcgi-pm");
1456 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1457 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper
);
1461 /* Initialize AppClass */
1462 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1464 if (s
->directive
!= APP_CLASS_STANDARD
)
1472 for (i
= 0; i
< s
->numProcesses
; ++i
)
1473 s
->procs
[i
].state
= FCGI_START_STATE
;
1477 child_wait_thread
= (HANDLE
) _beginthread(child_wait_thread_main
, 0, NULL
);
1479 if (child_wait_thread
== (HANDLE
) -1)
1481 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1482 "FastCGI: failed to create process manager's wait thread!");
1485 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1486 "FastCGI: process manager initialized");
1488 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1489 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1495 * Loop until SIGTERM
1498 int sleepSeconds
= min(dynamicKillInterval
, dynamicUpdateInterval
);
1505 unsigned int numChildren
;
1508 * If we came out of sigsuspend() for any reason other than
1509 * SIGALRM, pick up where we left off.
1512 sleepSeconds
= alarmLeft
;
1515 * Examine each configured AppClass for a process that needs
1516 * starting. Compute the earliest time when the start should
1517 * be attempted, starting it now if the time has passed. Also,
1518 * remember that we do NOT need to restart externally managed
1519 * FastCGI applications.
1521 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1523 if (s
->directive
== APP_CLASS_EXTERNAL
)
1526 numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
1527 ? dynamicMaxClassProcs
1530 for (i
= 0; i
< numChildren
; ++i
)
1532 if (s
->procs
[i
].pid
<= 0 && s
->procs
[i
].state
== FCGI_START_STATE
)
1534 int restart
= (s
->procs
[i
].pid
< 0);
1535 time_t restartTime
= s
->restartTime
;
1539 /* we've gone to using the badDelay, the only thing that
1540 resets bad is when badDelay has expired. but numFailures
1541 is only just set below its threshold. the proc's
1542 start_times are all reset when the bad is. the numFailures
1543 is reset when we see an app run for a period */
1545 s
->procs
[i
].start_time
= 0;
1548 if (s
->numFailures
> MAX_FAILED_STARTS
)
1550 time_t last_start_time
= s
->procs
[i
].start_time
;
1552 if (last_start_time
&& now
- last_start_time
> RUNTIME_SUCCESS_INTERVAL
)
1561 for (j
= 0; j
< numChildren
; ++j
)
1563 if (s
->procs
[j
].pid
<= 0) continue;
1564 if (s
->procs
[j
].state
!= FCGI_RUNNING_STATE
) continue;
1565 if (s
->procs
[j
].start_time
== 0) continue;
1566 if (now
- s
->procs
[j
].start_time
> RUNTIME_SUCCESS_INTERVAL
) break;
1569 if (j
>= numChildren
)
1583 restartTime
+= FAILED_STARTS_DELAY
;
1587 restartTime
+= (restart
) ? s
->restartDelay
: s
->initStartDelay
;
1590 if (restartTime
<= now
)
1595 s
->numFailures
= MAX_FAILED_STARTS
;
1598 if (s
->listenFd
< 0 && init_listen_sock(s
))
1600 if (sleepSeconds
> s
->initStartDelay
)
1601 sleepSeconds
= s
->initStartDelay
;
1605 if (caughtSigTerm
) {
1606 goto ProcessSigTerm
;
1609 s
->procs
[i
].pid
= spawn_fs_process(s
, &s
->procs
[i
]);
1610 if (s
->procs
[i
].pid
<= 0) {
1611 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1612 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1613 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1616 sleepSeconds
= min(sleepSeconds
,
1617 max((int) s
->restartDelay
, FCGI_MIN_EXEC_RETRY_DELAY
));
1619 ap_assert(s
->procs
[i
].pid
< 0);
1623 s
->procs
[i
].start_time
= now
;
1624 s
->restartTime
= now
;
1626 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1628 fcgi_dynamic_total_proc_count
++;
1629 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1632 s
->procs
[i
].state
= FCGI_RUNNING_STATE
;
1635 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1636 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1637 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1638 s
->fs_path
, (long) s
->uid
, (long) s
->gid
,
1639 restart
? "re" : "", (long) s
->procs
[i
].pid
);
1642 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1643 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1644 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1645 s
->fs_path
, restart
? "re" : "", (long) s
->procs
[i
].pid
);
1647 ap_assert(s
->procs
[i
].pid
> 0);
1649 sleepSeconds
= min(sleepSeconds
, restartTime
- now
);
1658 goto ProcessSigTerm
;
1660 if((!caughtSigChld
) && (!caughtSigAlarm
)) {
1663 alarm(sleepSeconds
);
1666 FD_SET(fcgi_pm_pipe
[0], &rfds
);
1667 read_ready
= ap_select(fcgi_pm_pipe
[0] + 1, &rfds
, NULL
, NULL
, NULL
);
1669 alarmLeft
= alarm(0);
1671 callWaitPid
= caughtSigChld
;
1672 caughtSigChld
= FALSE
;
1673 callDynamicProcs
= caughtSigAlarm
;
1674 caughtSigAlarm
= FALSE
;
1679 * Dynamic fcgi process management
1681 if((callDynamicProcs
) || (!callWaitPid
)) {
1682 dynamic_read_msgs(read_ready
);
1683 if(fcgi_dynamic_epoch
== 0) {
1684 fcgi_dynamic_epoch
= now
;
1686 if(((long)(now
-fcgi_dynamic_epoch
)>=dynamicKillInterval
) ||
1687 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
)>=dynamicMaxProcs
)) {
1688 dynamic_kill_idle_fs_procs();
1689 fcgi_dynamic_epoch
= now
;
1697 /* We've caught SIGCHLD, so find out who it was using waitpid,
1698 * write a log message and update its data structure. */
1702 goto ProcessSigTerm
;
1704 childPid
= waitpid(-1, &waitStatus
, WNOHANG
);
1706 if (childPid
== -1 || childPid
== 0)
1709 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1710 if (s
->directive
== APP_CLASS_EXTERNAL
)
1713 if (s
->directive
== APP_CLASS_DYNAMIC
)
1714 numChildren
= dynamicMaxClassProcs
;
1716 numChildren
= s
->numProcesses
;
1718 for (i
= 0; i
< numChildren
; i
++) {
1719 if (s
->procs
[i
].pid
== childPid
)
1724 /* TODO: print something about this unknown child */
1728 s
->procs
[i
].pid
= -1;
1730 if (s
->directive
== APP_CLASS_STANDARD
) {
1731 /* Always restart static apps */
1732 s
->procs
[i
].state
= FCGI_START_STATE
;
1737 fcgi_dynamic_total_proc_count
--;
1739 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1740 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1743 /* A dynamic app died or exited without provocation from the PM */
1746 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0))
1747 s
->procs
[i
].state
= FCGI_START_STATE
;
1749 s
->procs
[i
].state
= FCGI_READY_STATE
;
1753 if (WIFEXITED(waitStatus
)) {
1754 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1755 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1756 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1757 s
->fs_path
, (long) childPid
, WEXITSTATUS(waitStatus
));
1759 else if (WIFSIGNALED(waitStatus
)) {
1760 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1761 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1762 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1763 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), SYS_SIGLIST
[WTERMSIG(waitStatus
)],
1765 WCOREDUMP(waitStatus
) ? ", a core file may have been generated" : "");
1770 else if (WIFSTOPPED(waitStatus
)) {
1771 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1772 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1773 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1774 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), SYS_SIGLIST
[WTERMSIG(waitStatus
)]);
1776 } /* for (;;), waitpid() */
1780 /* wait for an event to occur or timer expires */
1781 expire
= time(NULL
) + sleepSeconds
;
1782 dwRet
= WaitForMultipleObjects(3, (HANDLE
*) fcgi_event_handles
, FALSE
, sleepSeconds
* 1000);
1784 if (dwRet
== WAIT_FAILED
) {
1785 /* There is something seriously wrong here */
1786 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1787 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1791 if (dwRet
!= WAIT_TIMEOUT
) {
1795 alarmLeft
= expire
- now
;
1799 * Dynamic fcgi process management
1801 if ((dwRet
== MBOX_EVENT
) || (dwRet
== WAIT_TIMEOUT
)) {
1802 if (dwRet
== MBOX_EVENT
) {
1808 dynamic_read_msgs(read_ready
);
1810 if(fcgi_dynamic_epoch
== 0) {
1811 fcgi_dynamic_epoch
= now
;
1814 if ((now
-fcgi_dynamic_epoch
>= (int) dynamicKillInterval
) ||
1815 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
) >= dynamicMaxProcs
)) {
1816 dynamic_kill_idle_fs_procs();
1817 fcgi_dynamic_epoch
= now
;
1821 else if (dwRet
== WAKE_EVENT
) {
1824 else if (dwRet
== TERM_EVENT
) {
1825 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
1826 "FastCGI: Termination event received process manager shutting down");
1829 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
1831 goto ProcessSigTerm
;
1834 // Have an received an unknown event - should not happen
1835 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1836 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1839 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
1841 goto ProcessSigTerm
;
1846 } /* for (;;), the whole shoot'n match */
1850 * Kill off the children, then exit.
1862 int fcgi_pm_add_job(fcgi_pm_job
*new_job
)
1864 int rv
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
1866 if (rv
!= WAIT_OBJECT_0
&& rv
!= WAIT_ABANDONED
)
1868 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1869 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
1873 new_job
->next
= fcgi_dynamic_mbox
;
1874 fcgi_dynamic_mbox
= new_job
;
1876 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
1878 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1879 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");