2 * $Id: fcgi_pm.c,v 1.69 2002/02/28 15:58:11 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
;
1537 restartTime
+= (restart
) ? s
->restartDelay
: s
->initStartDelay
;
1539 if (restartTime
<= now
)
1541 if (s
->listenFd
< 0 && init_listen_sock(s
))
1543 if (sleepSeconds
> s
->initStartDelay
)
1544 sleepSeconds
= s
->initStartDelay
;
1548 if (caughtSigTerm
) {
1549 goto ProcessSigTerm
;
1552 s
->procs
[i
].pid
= spawn_fs_process(s
, &s
->procs
[i
]);
1553 if (s
->procs
[i
].pid
<= 0) {
1554 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1555 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1556 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1559 sleepSeconds
= min(sleepSeconds
,
1560 max((int) s
->restartDelay
, FCGI_MIN_EXEC_RETRY_DELAY
));
1562 ap_assert(s
->procs
[i
].pid
< 0);
1566 s
->procs
[i
].start_time
= now
;
1567 s
->restartTime
= now
;
1569 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1571 fcgi_dynamic_total_proc_count
++;
1572 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1575 s
->procs
[i
].state
= FCGI_RUNNING_STATE
;
1581 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1582 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1583 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1584 s
->fs_path
, (long) s
->uid
, (long) s
->gid
,
1585 restart
? "re" : "", (long) s
->procs
[i
].pid
);
1588 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1589 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1590 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1591 s
->fs_path
, restart
? "re" : "", (long) s
->procs
[i
].pid
);
1593 ap_assert(s
->procs
[i
].pid
> 0);
1595 sleepSeconds
= min(sleepSeconds
, restartTime
- now
);
1604 goto ProcessSigTerm
;
1606 if((!caughtSigChld
) && (!caughtSigAlarm
)) {
1609 alarm(sleepSeconds
);
1612 FD_SET(fcgi_pm_pipe
[0], &rfds
);
1613 read_ready
= ap_select(fcgi_pm_pipe
[0] + 1, &rfds
, NULL
, NULL
, NULL
);
1615 alarmLeft
= alarm(0);
1617 callWaitPid
= caughtSigChld
;
1618 caughtSigChld
= FALSE
;
1619 callDynamicProcs
= caughtSigAlarm
;
1620 caughtSigAlarm
= FALSE
;
1625 * Dynamic fcgi process management
1627 if((callDynamicProcs
) || (!callWaitPid
)) {
1628 dynamic_read_msgs(read_ready
);
1629 if(fcgi_dynamic_epoch
== 0) {
1630 fcgi_dynamic_epoch
= now
;
1632 if(((long)(now
-fcgi_dynamic_epoch
)>=dynamicKillInterval
) ||
1633 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
)>=dynamicMaxProcs
)) {
1634 dynamic_kill_idle_fs_procs();
1635 fcgi_dynamic_epoch
= now
;
1643 /* We've caught SIGCHLD, so find out who it was using waitpid,
1644 * write a log message and update its data structure. */
1648 goto ProcessSigTerm
;
1650 childPid
= waitpid(-1, &waitStatus
, WNOHANG
);
1652 if (childPid
== -1 || childPid
== 0)
1655 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1656 if (s
->directive
== APP_CLASS_EXTERNAL
)
1659 if (s
->directive
== APP_CLASS_DYNAMIC
)
1660 numChildren
= dynamicMaxClassProcs
;
1662 numChildren
= s
->numProcesses
;
1664 for (i
= 0; i
< numChildren
; i
++) {
1665 if (s
->procs
[i
].pid
== childPid
)
1670 /* TODO: print something about this unknown child */
1674 s
->procs
[i
].pid
= -1;
1676 if (s
->directive
== APP_CLASS_STANDARD
) {
1677 /* Always restart static apps */
1678 s
->procs
[i
].state
= FCGI_START_STATE
;
1683 fcgi_dynamic_total_proc_count
--;
1685 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1686 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1689 /* A dynamic app died or exited without provocation from the PM */
1692 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0))
1693 s
->procs
[i
].state
= FCGI_START_STATE
;
1695 s
->procs
[i
].state
= FCGI_READY_STATE
;
1699 if (WIFEXITED(waitStatus
)) {
1700 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1701 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1702 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1703 s
->fs_path
, (long) childPid
, WEXITSTATUS(waitStatus
));
1705 else if (WIFSIGNALED(waitStatus
)) {
1706 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1707 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1708 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1709 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), SYS_SIGLIST
[WTERMSIG(waitStatus
)],
1711 WCOREDUMP(waitStatus
) ? ", a core file may have been generated" : "");
1716 else if (WIFSTOPPED(waitStatus
)) {
1717 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1718 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1719 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1720 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), SYS_SIGLIST
[WTERMSIG(waitStatus
)]);
1722 } /* for (;;), waitpid() */
1726 /* wait for an event to occur or timer expires */
1727 expire
= time(NULL
) + sleepSeconds
;
1728 dwRet
= WaitForMultipleObjects(3, (HANDLE
*) fcgi_event_handles
, FALSE
, sleepSeconds
* 1000);
1730 if (dwRet
== WAIT_FAILED
) {
1731 /* There is something seriously wrong here */
1732 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1733 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1737 if (dwRet
!= WAIT_TIMEOUT
) {
1741 alarmLeft
= expire
- now
;
1745 * Dynamic fcgi process management
1747 if ((dwRet
== MBOX_EVENT
) || (dwRet
== WAIT_TIMEOUT
)) {
1748 if (dwRet
== MBOX_EVENT
) {
1754 dynamic_read_msgs(read_ready
);
1756 if(fcgi_dynamic_epoch
== 0) {
1757 fcgi_dynamic_epoch
= now
;
1760 if ((now
-fcgi_dynamic_epoch
>= (int) dynamicKillInterval
) ||
1761 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
) >= dynamicMaxProcs
)) {
1762 dynamic_kill_idle_fs_procs();
1763 fcgi_dynamic_epoch
= now
;
1767 else if (dwRet
== WAKE_EVENT
) {
1770 else if (dwRet
== TERM_EVENT
) {
1771 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
1772 "FastCGI: Termination event received process manager shutting down");
1775 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
1777 goto ProcessSigTerm
;
1780 // Have an received an unknown event - should not happen
1781 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1782 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1785 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
1787 goto ProcessSigTerm
;
1792 } /* for (;;), the whole shoot'n match */
1796 * Kill off the children, then exit.
1808 int fcgi_pm_add_job(fcgi_pm_job
*new_job
)
1810 int rv
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
1812 if (rv
!= WAIT_OBJECT_0
&& rv
!= WAIT_ABANDONED
)
1814 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1815 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
1819 new_job
->next
= fcgi_dynamic_mbox
;
1820 fcgi_dynamic_mbox
= new_job
;
1822 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
1824 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1825 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");