2 * $Id: fcgi_pm.c,v 1.75 2002/07/29 00:07:28 robs Exp $
8 #if defined(APACHE2) && !defined(WIN32)
14 #define seteuid(arg) setresuid(-1, (arg), -1)
17 int fcgi_dynamic_total_proc_count
= 0; /* number of running apps */
18 time_t fcgi_dynamic_epoch
= 0; /* last time kill_procs was
19 * invoked by process mgr */
20 time_t fcgi_dynamic_last_analyzed
= 0; /* last time calculation was
21 * made for the dynamic procs */
23 static time_t now
= 0;
26 #pragma warning ( disable : 4100 4102 )
27 static BOOL bTimeToDie
= FALSE
; /* process termination flag */
28 HANDLE fcgi_event_handles
[3];
35 #if !defined(WIN32) && !defined(APACHE2)
36 static int seteuid_root(void)
38 int rc
= seteuid((uid_t
)0);
40 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
41 "FastCGI: seteuid(0) failed");
46 static int seteuid_user(void)
48 int rc
= seteuid(ap_user_id
);
50 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
51 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id
);
58 * Signal the process to exit. How (or if) the process responds
59 * depends on the FastCGI application library (esp. on Win32) and
60 * possibly application code (signal handlers and whether or not
61 * SA_RESTART is on). At any rate, we send the signal with the
62 * hopes that the process will exit on its own. Later, as we
63 * review the state of application processes, if we see one marked
64 * for death, but that hasn't died within a specified period of
65 * time, fcgi_kill() is called again with a KILL)
67 static void fcgi_kill(ServerProcess
*process
, int sig
)
69 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process
->pid
, sig
);
71 process
->state
= FCGI_VICTIM_STATE
;
77 SetEvent(process
->terminationEvent
);
79 else if (sig
== SIGKILL
)
81 TerminateProcess(process
->handle
, 1);
97 kill(process
->pid
, sig
);
109 /*******************************************************************************
110 * Send SIGTERM to each process in the server class, remove socket
111 * file if appropriate. Currently this is only called when the PM is shutting
112 * down and thus memory isn't freed and sockets and files aren't closed.
114 static void shutdown_all()
116 fcgi_server
*s
= fcgi_servers
;
120 ServerProcess
*proc
= s
->procs
;
122 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
123 ? dynamicMaxClassProcs
127 if (s
->socket_path
!= NULL
&& s
->directive
!= APP_CLASS_EXTERNAL
)
129 /* Remove the socket file */
130 if (unlink(s
->socket_path
) != 0 && errno
!= ENOENT
) {
131 ap_log_error(FCGI_LOG_ERR
, fcgi_apache_main_server
,
132 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
134 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "", s
->fs_path
);
139 /* Send TERM to all processes */
140 for (i
= 0; i
< numChildren
; i
++, proc
++)
142 if (proc
->state
== FCGI_RUNNING_STATE
)
144 fcgi_kill(proc
, SIGTERM
);
151 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
154 * WIN32 applications may not have support for the shutdown event
155 * depending on their application library version
158 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT
);
163 ServerProcess
*proc
= s
->procs
;
165 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
166 ? dynamicMaxClassProcs
169 /* Send KILL to all processes */
170 for (i
= 0; i
< numChildren
; i
++, proc
++)
172 if (proc
->state
== FCGI_RUNNING_STATE
)
174 fcgi_kill(proc
, SIGKILL
);
184 static int init_listen_sock(fcgi_server
* fs
)
186 ap_assert(fs
->directive
!= APP_CLASS_EXTERNAL
);
188 /* Create the socket */
189 if ((fs
->listenFd
= socket(fs
->socket_addr
->sa_family
, SOCK_STREAM
, 0)) < 0)
192 errno
= WSAGetLastError(); // Not sure if this will work as expected
194 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
195 "FastCGI: can't create %sserver \"%s\": socket() failed",
196 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
202 if (fs
->socket_addr
->sa_family
== AF_UNIX
)
204 /* Remove any existing socket file.. just in case */
205 unlink(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
);
211 setsockopt(fs
->listenFd
, SOL_SOCKET
, SO_REUSEADDR
, (char *)&flag
, sizeof(flag
));
214 /* Bind it to the socket_addr */
215 if (bind(fs
->listenFd
, fs
->socket_addr
, fs
->socket_addr_len
))
220 errno
= WSAGetLastError();
222 ap_snprintf(port
, sizeof(port
), "port=%d",
223 ((struct sockaddr_in
*)fs
->socket_addr
)->sin_port
);
225 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
226 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
227 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
230 (fs
->socket_addr
->sa_family
== AF_UNIX
) ?
231 ((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
:
237 /* Twiddle Unix socket permissions */
238 else if (fs
->socket_addr
->sa_family
== AF_UNIX
239 && chmod(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
, S_IRUSR
| S_IWUSR
))
241 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
242 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
243 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
249 else if (listen(fs
->listenFd
, fs
->listenQueueDepth
))
252 errno
= WSAGetLastError();
254 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
255 "FastCGI: can't create %sserver \"%s\": listen() failed",
256 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
270 *----------------------------------------------------------------------
274 * The FastCGI process manager, which runs as a separate
275 * process responsible for:
276 * - Starting all the FastCGI proceses.
277 * - Restarting any of these processes that die (indicated
279 * - Catching SIGTERM and relaying it to all the FastCGI
280 * processes before exiting.
283 * Uses global variable fcgi_servers.
291 *----------------------------------------------------------------------
294 static int caughtSigTerm
= FALSE
;
295 static int caughtSigChld
= FALSE
;
296 static int caughtSigAlarm
= FALSE
;
298 static void signal_handler(int signo
)
300 if ((signo
== SIGTERM
) || (signo
== SIGUSR1
) || (signo
== SIGHUP
)) {
301 /* SIGUSR1 & SIGHUP are sent by apache to its process group
302 * when apache get 'em. Apache follows up (1.2.x) with attacks
303 * on each of its child processes, but we've got the KillMgr
304 * sitting between us so we never see the KILL. The main loop
305 * in ProcMgr also checks to see if the KillMgr has terminated,
306 * and if it has, we handl it as if we should shutdown too. */
307 caughtSigTerm
= TRUE
;
308 } else if(signo
== SIGCHLD
) {
309 caughtSigChld
= TRUE
;
310 } else if(signo
== SIGALRM
) {
311 caughtSigAlarm
= TRUE
;
317 *----------------------------------------------------------------------
319 * spawn_fs_process --
321 * Fork and exec the specified fcgi process.
324 * 0 for successful fork, -1 for failed fork.
326 * In case the child fails before or in the exec, the child
327 * obtains the error log by calling getErrLog, logs
328 * the error, and exits with exit status = errno of
329 * the failed system call.
332 * Child process created.
334 *----------------------------------------------------------------------
336 static pid_t
spawn_fs_process(fcgi_server
*fs
, ServerProcess
*process
)
343 char *dnEnd
, *failedSysCall
;
350 /* We're the child. We're gonna exec() so pools don't matter. */
352 dnEnd
= strrchr(fs
->fs_path
, '/');
356 dirName
= ap_pcalloc(fcgi_config_pool
, dnEnd
- fs
->fs_path
+ 1);
357 dirName
= memcpy(dirName
, fs
->fs_path
, dnEnd
- fs
->fs_path
);
359 if (chdir(dirName
) < 0) {
360 failedSysCall
= "chdir()";
361 goto FailedSystemCallExit
;
365 /* OS/2 dosen't support nice() */
366 if (fs
->processPriority
!= 0) {
367 if (nice(fs
->processPriority
) == -1) {
368 failedSysCall
= "nice()";
369 goto FailedSystemCallExit
;
374 /* Open the listenFd on spec'd fd */
375 if (fs
->listenFd
!= FCGI_LISTENSOCK_FILENO
)
376 dup2(fs
->listenFd
, FCGI_LISTENSOCK_FILENO
);
378 /* Close all other open fds, except stdout/stderr. Leave these two open so
379 * FastCGI applications don't have to find and fix ALL 3rd party libs that
380 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
381 * main server error_log - @@@ provide a directive control where this goes.
383 ap_error_log2stderr(fcgi_apache_main_server
);
385 for (i
= 0; i
< FCGI_MAX_FD
; i
++) {
386 if (i
!= FCGI_LISTENSOCK_FILENO
&& i
!= 2 && i
!= 1) {
391 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
392 * install its own handler. */
393 signal(SIGPIPE
, SIG_IGN
);
395 if (fcgi_wrapper
&& (fcgi_user_id
!= fs
->uid
|| fcgi_group_id
!= fs
->gid
)) {
396 char *shortName
= strrchr(fs
->fs_path
, '/') + 1;
399 /* Relinquish our root real uid powers */
405 execle(fcgi_wrapper
, fcgi_wrapper
, fs
->username
, fs
->group
, shortName
, NULL
, fs
->envp
);
406 } while (errno
== EINTR
);
410 execle(fs
->fs_path
, fs
->fs_path
, NULL
, fs
->envp
);
411 } while (errno
== EINTR
);
414 failedSysCall
= "execle()";
416 FailedSystemCallExit
:
417 fprintf(stderr
, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
418 fs
->fs_path
, (long) getpid(), failedSysCall
, strerror(errno
));
421 /* avoid an irrelevant compiler warning */
428 /* based on mod_cgi.c:run_cgi_child() */
431 char * termination_env_string
;
432 HANDLE listen_handle
= INVALID_HANDLE_VALUE
;
433 apr_procattr_t
* procattr
;
434 apr_proc_t proc
= { 0 };
438 if (apr_pool_create(&tp
, fcgi_config_pool
))
441 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
442 if (process
->terminationEvent
== NULL
)
445 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
447 termination_env_string
= ap_psprintf(tp
,
448 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
450 while (fs
->envp
[i
]) i
++;
451 fs
->envp
[i
++] = termination_env_string
;
452 fs
->envp
[i
] = (char *) fs
->mutex_env_string
;
454 ap_assert(fs
->envp
[i
+ 1] == NULL
);
458 SECURITY_ATTRIBUTES sa
= { 0 };
460 sa
.bInheritHandle
= TRUE
;
461 sa
.nLength
= sizeof(sa
);
463 listen_handle
= CreateNamedPipe(fs
->socket_path
,
465 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
466 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
468 if (listen_handle
== INVALID_HANDLE_VALUE
)
470 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
471 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
478 listen_handle
= (HANDLE
) fs
->listenFd
;
481 if (apr_procattr_create(&procattr
, tp
))
484 if (apr_procattr_dir_set(procattr
, ap_make_dirstr_parent(tp
, fs
->fs_path
)))
487 if (apr_procattr_detach_set(procattr
, 1))
490 if (apr_os_file_put(&file
, &listen_handle
, 0, tp
))
493 /* procattr is opaque so we have to use this - unfortuantely it dups */
494 if (apr_procattr_child_in_set(procattr
, file
, NULL
))
497 if (apr_proc_create(&proc
, fs
->fs_path
, NULL
, fs
->envp
, procattr
, tp
))
500 process
->handle
= proc
.hproc
;
507 fs
->envp
[i
- 1] = NULL
;
514 #else /* WIN32 && !APACHE2 */
516 /* Adapted from Apache's util_script.c ap_call_exec() */
517 char *interpreter
= NULL
;
518 char *quoted_filename
;
520 char *pEnvBlock
, *pNext
;
523 int iEnvBlockLen
= 1;
525 file_type_e fileType
;
528 PROCESS_INFORMATION pi
;
533 pool
* tp
= ap_make_sub_pool(fcgi_config_pool
);
535 HANDLE listen_handle
;
536 char * termination_env_string
= NULL
;
538 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
539 if (process
->terminationEvent
== NULL
)
541 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
542 "FastCGI: can't create termination event for server \"%s\", "
543 "CreateEvent() failed", fs
->fs_path
);
546 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
548 termination_env_string
= ap_psprintf(tp
,
549 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
553 SECURITY_ATTRIBUTES sa
;
555 sa
.lpSecurityDescriptor
= NULL
;
556 sa
.bInheritHandle
= TRUE
;
557 sa
.nLength
= sizeof(sa
);
559 listen_handle
= CreateNamedPipe(fs
->socket_path
,
561 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
562 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
564 if (listen_handle
== INVALID_HANDLE_VALUE
)
566 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
567 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs
->fs_path
);
573 listen_handle
= (HANDLE
) fs
->listenFd
;
576 memset(&si
, 0, sizeof(si
));
577 memset(&pi
, 0, sizeof(pi
));
578 memset(&r
, 0, sizeof(r
));
580 // Can up a fake request to pass to ap_get_win32_interpreter()
581 r
.per_dir_config
= fcgi_apache_main_server
->lookup_defaults
;
582 r
.server
= fcgi_apache_main_server
;
583 r
.filename
= (char *) fs
->fs_path
;
586 fileType
= ap_get_win32_interpreter(&r
, &interpreter
);
588 if (fileType
== eFileTypeUNKNOWN
) {
589 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
590 "FastCGI: %s is not executable; ensure interpreted scripts have "
591 "\"#!\" as their first line",
598 * We have the interpreter (if there is one) and we have
599 * the arguments (if there are any).
600 * Build the command string to pass to CreateProcess.
602 quoted_filename
= ap_pstrcat(tp
, "\"", fs
->fs_path
, "\"", NULL
);
603 if (interpreter
&& *interpreter
) {
604 pCommand
= ap_pstrcat(tp
, interpreter
, " ", quoted_filename
, NULL
);
607 pCommand
= quoted_filename
;
611 * Make child process use hPipeOutputWrite as standard out,
612 * and make sure it does not show on screen.
615 si
.dwFlags
= STARTF_USESHOWWINDOW
| STARTF_USESTDHANDLES
;
616 si
.wShowWindow
= SW_HIDE
;
617 si
.hStdInput
= listen_handle
;
619 // XXX These should be open to the error_log
620 si
.hStdOutput
= INVALID_HANDLE_VALUE
;
621 si
.hStdError
= INVALID_HANDLE_VALUE
;
624 * Win32's CreateProcess call requires that the environment
625 * be passed in an environment block, a null terminated block of
626 * null terminated strings.
627 * @todo we should store the env in this format for win32.
631 iEnvBlockLen
+= strlen(fs
->envp
[i
]) + 1;
635 iEnvBlockLen
+= strlen(termination_env_string
) + 1;
636 iEnvBlockLen
+= strlen(fs
->mutex_env_string
) + 1;
638 pEnvBlock
= (char *) ap_pcalloc(tp
, iEnvBlockLen
);
644 strcpy(pNext
, fs
->envp
[i
]);
645 pNext
+= strlen(pNext
) + 1;
649 strcpy(pNext
, termination_env_string
);
650 pNext
+= strlen(pNext
) + 1;
651 strcpy(pNext
, fs
->mutex_env_string
);
653 if (CreateProcess(NULL
, pCommand
, NULL
, NULL
, TRUE
,
656 ap_make_dirstr_parent(tp
, fs
->fs_path
),
659 /* Hack to get 16-bit CGI's working. It works for all the
660 * standard modules shipped with Apache. pi.dwProcessId is 0
661 * for 16-bit CGIs and all the Unix specific code that calls
662 * ap_call_exec interprets this as a failure case. And we can't
663 * use -1 either because it is mapped to 0 by the caller.
665 pid
= (fileType
== eFileTypeEXE16
) ? -2 : pi
.dwProcessId
;
667 process
->handle
= pi
.hProcess
;
668 CloseHandle(pi
.hThread
);
673 CloseHandle(listen_handle
);
682 #endif /* !APACHE2 */
686 #if !defined(WIN32) && !defined(APACHE2)
687 static void reduce_privileges(void)
695 /* Get username if passed as a uid */
696 if (ap_user_name
[0] == '#') {
697 uid_t uid
= atoi(&ap_user_name
[1]);
698 struct passwd
*ent
= getpwuid(uid
);
701 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
702 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
703 "you probably need to modify the User directive", (unsigned)uid
);
712 if (setgid(ap_group_id
) == -1) {
713 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
714 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id
);
718 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
720 /* Initialize supplementary groups */
721 if (initgroups(name
, ap_group_id
) == -1) {
722 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
723 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
724 name
, (unsigned)ap_group_id
);
731 if (seteuid_user() == -1) {
732 ap_log_error(FCGI_LOG_ALERT_NOERRNO
, fcgi_apache_main_server
,
733 "FastCGI: process manager exiting, failed to reduce privileges");
738 if (setuid(ap_user_id
) == -1) {
739 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
740 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id
);
747 * Change the name of this process - best we can easily.
749 static void change_process_name(const char * const name
)
751 strncpy(ap_server_argv0
, name
, strlen(ap_server_argv0
));
755 static void schedule_start(fcgi_server
*s
, int proc
)
757 /* If we've started one recently, don't register another */
758 time_t time_passed
= now
- s
->restartTime
;
760 if ((s
->procs
[proc
].pid
&& (time_passed
< (int) s
->restartDelay
))
761 || ((s
->procs
[proc
].pid
== 0) && (time_passed
< s
->initStartDelay
)))
763 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
);
767 FCGIDBG3("scheduling_start: %s (%d)", s
->fs_path
, proc
);
768 s
->procs
[proc
].state
= FCGI_START_STATE
;
769 if (proc
== dynamicMaxClassProcs
- 1) {
770 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
771 "FastCGI: scheduled the %sstart of the last (dynamic) server "
772 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
773 s
->procs
[proc
].pid
? "re" : "", s
->fs_path
, dynamicMaxClassProcs
);
778 *----------------------------------------------------------------------
782 * Removes the records written by request handlers and decodes them.
783 * We also update the data structures to reflect the changes.
785 *----------------------------------------------------------------------
788 static void dynamic_read_msgs(int read_ready
)
794 static int buflen
= 0;
795 static char buf
[FCGI_MSGS_BUFSIZE
+ 1];
796 char *ptr1
, *ptr2
, opcode
;
797 char execName
[FCGI_MAXPATH
+ 1];
798 char user
[MAX_USER_NAME_LEN
+ 2];
799 char group
[MAX_GID_CHAR_LEN
+ 1];
800 unsigned long q_usec
= 0UL, req_usec
= 0UL;
802 fcgi_pm_job
*joblist
= NULL
;
803 fcgi_pm_job
*cjob
= NULL
;
806 pool
*sp
= NULL
, *tp
;
809 user
[MAX_USER_NAME_LEN
+ 1] = group
[MAX_GID_CHAR_LEN
] = '\0';
813 * To prevent the idle application from running indefinitely, we
814 * check the timer and if it is expired, we recompute the values
815 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
816 * message is received, only updates are made to the data structures.
818 if (fcgi_dynamic_last_analyzed
== 0) {
819 fcgi_dynamic_last_analyzed
= now
;
821 if ((now
- fcgi_dynamic_last_analyzed
) >= (int)dynamicUpdateInterval
) {
822 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
823 if (s
->directive
!= APP_CLASS_DYNAMIC
)
826 /* Advance the last analyzed timestamp by the elapsed time since
827 * it was last set. Round the increase down to the nearest
828 * multiple of dynamicUpdateInterval */
830 fcgi_dynamic_last_analyzed
+= (((long)(now
-fcgi_dynamic_last_analyzed
)/dynamicUpdateInterval
)*dynamicUpdateInterval
);
831 s
->smoothConnTime
= (unsigned long) ((1.0-dynamicGain
)*s
->smoothConnTime
+ dynamicGain
*s
->totalConnTime
);
832 s
->totalConnTime
= 0UL;
833 s
->totalQueueTime
= 0UL;
837 if (read_ready
<= 0) {
842 rc
= read(fcgi_pm_pipe
[0], (void *)(buf
+ buflen
), FCGI_MSGS_BUFSIZE
- buflen
);
844 if (!caughtSigTerm
) {
845 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
846 "FastCGI: read() from pipe failed (%d)", rc
);
848 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
849 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
850 caughtSigTerm
= TRUE
;
860 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
861 * request to do something) and/or when a timeout expires.
862 * There really should be no reason why this wait would get stuck
863 * but there's no point in waiting forever. */
865 rc
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
867 if (rc
!= WAIT_OBJECT_0
&& rc
!= WAIT_ABANDONED
)
869 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
870 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
874 joblist
= fcgi_dynamic_mbox
;
875 fcgi_dynamic_mbox
= NULL
;
877 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
879 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
880 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
887 apr_pool_create(&tp
, fcgi_config_pool
);
889 tp
= ap_make_sub_pool(fcgi_config_pool
);
893 for (ptr1
= buf
; ptr1
; ptr1
= ptr2
) {
896 ptr2
= strchr(ptr1
, '*');
908 case FCGI_SERVER_START_JOB
:
909 case FCGI_SERVER_RESTART_JOB
:
911 if (sscanf(ptr1
, "%c %s %16s %15s",
912 &opcode
, execName
, user
, group
) != 4)
918 case FCGI_REQUEST_TIMEOUT_JOB
:
920 if (sscanf(ptr1
, "%c %s %16s %15s",
921 &opcode
, execName
, user
, group
) != 4)
927 case FCGI_REQUEST_COMPLETE_JOB
:
929 if (sscanf(ptr1
, "%c %s %16s %15s %lu %lu",
930 &opcode
, execName
, user
, group
, &q_usec
, &req_usec
) != 6)
942 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode
, execName
, user
, group
, q_usec
, req_usec
);
945 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
946 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1
);
950 /* Update data structures for processing */
951 while (cjob
!= NULL
) {
952 joblist
= cjob
->next
;
953 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob
->id
, cjob
->fs_path
, cjob
->user
, cjob
->group
, cjob
->qsec
, cjob
->start_time
);
957 s
= fcgi_util_fs_get(execName
, user
, group
);
959 s
= fcgi_util_fs_get(cjob
->fs_path
, cjob
->user
, cjob
->group
);
963 if (s
==NULL
&& opcode
!= FCGI_REQUEST_COMPLETE_JOB
)
965 if (s
==NULL
&& cjob
->id
!= FCGI_REQUEST_COMPLETE_JOB
)
970 HANDLE mutex
= CreateMutex(NULL
, FALSE
, cjob
->fs_path
);
974 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
975 "FastCGI: can't create accept mutex "
976 "for (dynamic) server \"%s\"", cjob
->fs_path
);
980 SetHandleInformation(mutex
, HANDLE_FLAG_INHERIT
, TRUE
);
985 /* Create a perm subpool to hold the new server data,
986 * we can destroy it if something doesn't pan out */
988 apr_pool_create(&sp
, fcgi_config_pool
);
990 sp
= ap_make_sub_pool(fcgi_config_pool
);
993 /* Create a new "dynamic" server */
994 s
= fcgi_util_fs_new(sp
);
996 s
->directive
= APP_CLASS_DYNAMIC
;
997 s
->restartDelay
= dynamicRestartDelay
;
998 s
->listenQueueDepth
= dynamicListenQueueDepth
;
999 s
->initStartDelay
= dynamicInitStartDelay
;
1000 s
->envp
= dynamicEnvp
;
1001 s
->flush
= dynamicFlush
;
1004 s
->mutex_env_string
= ap_psprintf(sp
, "_FCGI_MUTEX_=%ld", mutex
);
1005 s
->fs_path
= ap_pstrdup(sp
, cjob
->fs_path
);
1007 s
->fs_path
= ap_pstrdup(sp
, execName
);
1009 ap_getparents(s
->fs_path
);
1010 ap_no2slash(s
->fs_path
);
1011 s
->procs
= fcgi_util_fs_create_procs(sp
, dynamicMaxClassProcs
);
1013 /* XXX the socket_path (both Unix and Win) *is* deducible and
1014 * thus can and will be used by other apache instances without
1015 * the use of shared data regarding the processes serving the
1016 * requests. This can result in slightly unintuitive process
1017 * counts and security implications. This is prevented
1018 * if suexec (Unix) is in use. This is both a feature and a flaw.
1019 * Changing it now would break existing installations. */
1022 /* Create socket file's path */
1023 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, execName
, user
, group
);
1024 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1026 /* Create sockaddr, prealloc it so it won't get created in tp */
1027 s
->socket_addr
= ap_pcalloc(sp
, sizeof(struct sockaddr_un
));
1028 err
= fcgi_util_socket_make_domain_addr(tp
, (struct sockaddr_un
**)&s
->socket_addr
,
1029 &s
->socket_addr_len
, s
->socket_path
);
1031 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1032 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
1036 if (init_listen_sock(s
)) {
1040 /* If a wrapper is being used, config user/group info */
1042 if (user
[0] == '~') {
1043 /* its a user dir uri, the rest is a username, not a uid */
1044 struct passwd
*pw
= getpwnam(&user
[1]);
1047 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1048 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1049 execName
, &user
[1]);
1052 s
->uid
= pw
->pw_uid
;
1053 s
->user
= ap_pstrdup(sp
, user
);
1054 s
->username
= s
->user
;
1056 s
->gid
= pw
->pw_gid
;
1057 s
->group
= ap_psprintf(sp
, "%ld", (long)s
->gid
);
1062 s
->uid
= (uid_t
)atol(user
);
1063 pw
= getpwuid(s
->uid
);
1065 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1066 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1067 execName
, (long)s
->uid
);
1070 s
->user
= ap_pstrdup(sp
, user
);
1071 s
->username
= ap_pstrdup(sp
, pw
->pw_name
);
1073 s
->gid
= (gid_t
)atol(group
);
1074 s
->group
= ap_pstrdup(sp
, group
);
1078 /* Create socket file's path */
1079 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, cjob
->fs_path
, cjob
->user
, cjob
->group
);
1080 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1084 fcgi_util_fs_add(s
);
1088 if (opcode
== FCGI_SERVER_RESTART_JOB
) {
1090 if (cjob
->id
==FCGI_SERVER_RESTART_JOB
) {
1092 /* Check to see if the binary has changed. If so,
1093 * kill the FCGI application processes, and
1099 char * app_path
= cjob
->fs_path
;
1101 char * app_path
= execName
;
1104 if (stat(app_path
, &stbuf
) == 0 && stbuf
.st_mtime
> s
->startTime
)
1108 /* prevent addition restart requests */
1111 utime(s
->socket_path
, NULL
);
1114 /* kill old server(s) */
1115 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1117 if (s
->procs
[i
].pid
> 0
1118 && stbuf
.st_mtime
> s
->procs
[i
].start_time
)
1120 fcgi_kill(&s
->procs
[i
], SIGTERM
);
1127 ap_log_error(FCGI_LOG_WARN_NOERRNO
,
1128 fcgi_apache_main_server
, "FastCGI: restarting "
1129 "old server \"%s\" processes, newer version "
1134 /* If dynamicAutoRestart, don't mark any new processes
1135 * for starting because we probably got the
1136 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1137 * will be restarting all of those we just killed.
1139 if (dynamicAutoRestart
)
1143 else if (opcode
== FCGI_SERVER_START_JOB
) {
1145 else if (cjob
->id
==FCGI_SERVER_START_JOB
) {
1147 /* we've been asked to start a process--only start
1148 * it if we're not already running at least one
1153 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1154 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1157 /* if already running, don't start another one */
1158 if (i
< dynamicMaxClassProcs
) {
1172 case FCGI_SERVER_RESTART_JOB
:
1176 /* We just waxed 'em all. Try to find an idle slot. */
1178 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1180 if (s
->procs
[i
].state
== FCGI_START_STATE
1181 || s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1185 else if (s
->procs
[i
].state
== FCGI_KILLED_STATE
1186 || s
->procs
[i
].state
== FCGI_READY_STATE
)
1193 /* Nope, just use the first slot */
1194 if (i
== dynamicMaxClassProcs
)
1202 schedule_start(s
, i
);
1207 case FCGI_SERVER_START_JOB
:
1208 case FCGI_REQUEST_TIMEOUT_JOB
:
1210 if ((fcgi_dynamic_total_proc_count
+ 1) > (int) dynamicMaxProcs
) {
1212 * Extra instances should have been
1213 * terminated beforehand, probably need
1214 * to increase ProcessSlack parameter
1216 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1217 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1218 "exceeded dynamicMaxProcs (%d)", s
->fs_path
, dynamicMaxProcs
);
1222 /* find next free slot */
1223 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1225 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1227 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i
);
1230 else if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1235 schedule_start(s
, i
);
1240 if (i
>= dynamicMaxClassProcs
) {
1241 FCGIDBG1("ignore_job: slots are max'd");
1245 case FCGI_REQUEST_COMPLETE_JOB
:
1246 /* only record stats if we have a structure */
1249 s
->totalConnTime
+= req_usec
;
1250 s
->totalQueueTime
+= q_usec
;
1252 s
->totalConnTime
+= cjob
->start_time
;
1253 s
->totalQueueTime
+= cjob
->qsec
;
1262 /* Cleanup job data */
1263 free(cjob
->fs_path
);
1273 if (sp
) ap_destroy_pool(sp
);
1276 free(cjob
->fs_path
);
1284 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1285 "FastCGI: really bogus message: \"%s\"", ptr1
);
1286 ptr1
+= strlen(buf
);
1289 buflen
-= ptr1
- buf
;
1291 memmove(buf
, ptr1
, buflen
);
1295 ap_destroy_pool(tp
);
1299 *----------------------------------------------------------------------
1301 * dynamic_kill_idle_fs_procs
1303 * Implement a kill policy for the dynamic FastCGI applications.
1304 * We also update the data structures to reflect the changes.
1307 * Processes are marked for deletion possibly killed.
1309 *----------------------------------------------------------------------
1311 static void dynamic_kill_idle_fs_procs(void)
1316 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1319 * server's smoothed running time, or if that's 0, the current total
1321 unsigned long connTime
;
1324 * maximum number of microseconds that all of a server's running
1325 * processes together could have spent running since the last check
1327 unsigned long totalTime
;
1330 * percentage, 0-100, of totalTime that the processes actually used
1335 int really_running
= 0;
1337 if (s
->directive
!= APP_CLASS_DYNAMIC
|| s
->numProcesses
== 0)
1342 /* s->numProcesses includes pending kills so get the "active" count */
1343 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1345 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
) ++really_running
;
1348 connTime
= s
->smoothConnTime
? s
->smoothConnTime
: s
->totalConnTime
;
1349 totalTime
= really_running
* (now
- fcgi_dynamic_epoch
) * 1000000 + 1;
1351 loadFactor
= 100 * connTime
/ totalTime
;
1353 if (really_running
== 1)
1355 if (loadFactor
>= dynamicThreshold1
)
1362 int load
= really_running
/ ( really_running
- 1) * loadFactor
;
1364 if (load
>= dynamicThresholdN
)
1371 * Run through the procs to see if we can get away w/o waxing one.
1373 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1375 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1377 s
->procs
[i
].state
= FCGI_READY_STATE
;
1380 else if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
)
1386 if (i
>= dynamicMaxClassProcs
)
1388 ServerProcess
* procs
= s
->procs
;
1391 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1393 if (procs
[i
].state
== FCGI_RUNNING_STATE
)
1395 if (youngest
== -1 || procs
[i
].start_time
>= procs
[youngest
].start_time
)
1404 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1405 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1406 s
->fs_path
, (long) s
->procs
[youngest
].pid
);
1408 fcgi_kill(&s
->procs
[youngest
], SIGTERM
);
1414 * If the number of non-victims is less than or equal to
1415 * the minimum that may be running without being killed off,
1416 * don't select any more victims.
1418 if (fcgi_dynamic_total_proc_count
- victims
<= dynamicMinProcs
)
1428 // This is a little bogus, there's gotta be a better way to do this
1429 // Can we use WaitForMultipleObjects()
1430 #define FCGI_PROC_WAIT_TIME 100
1432 void child_wait_thread_main(void *dummy
) {
1434 DWORD dwRet
= WAIT_TIMEOUT
;
1439 while (!bTimeToDie
) {
1442 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1443 if (s
->directive
== APP_CLASS_EXTERNAL
|| s
->listenFd
< 0) {
1446 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1447 numChildren
= dynamicMaxClassProcs
;
1450 numChildren
= s
->numProcesses
;
1453 for (i
=0; i
< numChildren
; i
++) {
1454 if (s
->procs
[i
].handle
!= INVALID_HANDLE_VALUE
)
1456 DWORD exitStatus
= 0;
1458 /* timeout is currently set for 100 miliecond */
1459 /* it may need to be longer or user customizable */
1460 dwRet
= WaitForSingleObject(s
->procs
[i
].handle
, FCGI_PROC_WAIT_TIME
);
1464 if (dwRet
!= WAIT_TIMEOUT
&& dwRet
!= WAIT_FAILED
) {
1465 /* a child fs has died */
1466 /* mark the child as dead */
1468 if (s
->directive
== APP_CLASS_STANDARD
) {
1469 /* restart static app */
1470 s
->procs
[i
].state
= FCGI_START_STATE
;
1475 fcgi_dynamic_total_proc_count
--;
1476 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1478 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1479 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1482 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1485 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0)) {
1486 s
->procs
[i
].state
= FCGI_START_STATE
;
1489 s
->procs
[i
].state
= FCGI_READY_STATE
;
1494 GetExitCodeProcess(s
->procs
[i
].handle
, &exitStatus
);
1496 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1497 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1498 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1499 s
->fs_path
, (long) s
->procs
[i
].pid
, exitStatus
);
1501 CloseHandle(s
->procs
[i
].handle
);
1502 s
->procs
[i
].handle
= INVALID_HANDLE_VALUE
;
1503 s
->procs
[i
].pid
= -1;
1505 /* wake up the main thread */
1506 SetEvent(fcgi_event_handles
[WAKE_EVENT
]);
1511 Sleep(waited
? 0 : FCGI_PROC_WAIT_TIME
);
1517 static void setup_signals(void)
1519 struct sigaction sa
;
1521 /* Setup handlers */
1523 sa
.sa_handler
= signal_handler
;
1524 sigemptyset(&sa
.sa_mask
);
1527 if (sigaction(SIGTERM
, &sa
, NULL
) < 0) {
1528 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1529 "sigaction(SIGTERM) failed");
1532 if (sigaction(SIGHUP
, &sa
, NULL
) < 0) {
1533 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1534 "sigaction(SIGHUP) failed");
1536 /* httpd graceful restart */
1537 if (sigaction(SIGUSR1
, &sa
, NULL
) < 0) {
1538 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1539 "sigaction(SIGUSR1) failed");
1541 /* read messages from request handlers - kill interval expired */
1542 if (sigaction(SIGALRM
, &sa
, NULL
) < 0) {
1543 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1544 "sigaction(SIGALRM) failed");
1546 if (sigaction(SIGCHLD
, &sa
, NULL
) < 0) {
1547 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1548 "sigaction(SIGCHLD) failed");
1553 #if !defined(WIN32) && !defined(APACHE2)
1554 int fcgi_pm_main(void *dummy
, child_info
*info
)
1556 void fcgi_pm_main(void *dummy
)
1566 HANDLE child_wait_thread
= INVALID_HANDLE_VALUE
;
1568 int callWaitPid
, callDynamicProcs
;
1572 // Add SystemRoot to the dynamic environment
1573 char ** envp
= dynamicEnvp
;
1574 for (i
= 0; *envp
; ++i
) {
1577 fcgi_config_set_env_var(fcgi_config_pool
, dynamicEnvp
, &i
, "SystemRoot");
1582 reduce_privileges();
1583 change_process_name("fcgi-pm");
1586 close(fcgi_pm_pipe
[1]);
1590 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1591 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper
);
1595 /* Initialize AppClass */
1596 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1598 if (s
->directive
!= APP_CLASS_STANDARD
)
1606 for (i
= 0; i
< s
->numProcesses
; ++i
)
1607 s
->procs
[i
].state
= FCGI_START_STATE
;
1611 child_wait_thread
= (HANDLE
) _beginthread(child_wait_thread_main
, 0, NULL
);
1613 if (child_wait_thread
== (HANDLE
) -1)
1615 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1616 "FastCGI: failed to create process manager's wait thread!");
1619 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1620 "FastCGI: process manager initialized");
1622 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1623 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1629 * Loop until SIGTERM
1632 int sleepSeconds
= min(dynamicKillInterval
, dynamicUpdateInterval
);
1639 unsigned int numChildren
;
1642 * If we came out of sigsuspend() for any reason other than
1643 * SIGALRM, pick up where we left off.
1646 sleepSeconds
= alarmLeft
;
1649 * Examine each configured AppClass for a process that needs
1650 * starting. Compute the earliest time when the start should
1651 * be attempted, starting it now if the time has passed. Also,
1652 * remember that we do NOT need to restart externally managed
1653 * FastCGI applications.
1655 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1657 if (s
->directive
== APP_CLASS_EXTERNAL
)
1660 numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
1661 ? dynamicMaxClassProcs
1664 for (i
= 0; i
< numChildren
; ++i
)
1666 if (s
->procs
[i
].pid
<= 0 && s
->procs
[i
].state
== FCGI_START_STATE
)
1668 int restart
= (s
->procs
[i
].pid
< 0);
1669 time_t restartTime
= s
->restartTime
;
1673 /* we've gone to using the badDelay, the only thing that
1674 resets bad is when badDelay has expired. but numFailures
1675 is only just set below its threshold. the proc's
1676 start_times are all reset when the bad is. the numFailures
1677 is reset when we see an app run for a period */
1679 s
->procs
[i
].start_time
= 0;
1682 if (s
->numFailures
> MAX_FAILED_STARTS
)
1684 time_t last_start_time
= s
->procs
[i
].start_time
;
1686 if (last_start_time
&& now
- last_start_time
> RUNTIME_SUCCESS_INTERVAL
)
1695 for (j
= 0; j
< numChildren
; ++j
)
1697 if (s
->procs
[j
].pid
<= 0) continue;
1698 if (s
->procs
[j
].state
!= FCGI_RUNNING_STATE
) continue;
1699 if (s
->procs
[j
].start_time
== 0) continue;
1700 if (now
- s
->procs
[j
].start_time
> RUNTIME_SUCCESS_INTERVAL
) break;
1703 if (j
>= numChildren
)
1717 restartTime
+= FAILED_STARTS_DELAY
;
1721 restartTime
+= (restart
) ? s
->restartDelay
: s
->initStartDelay
;
1724 if (restartTime
<= now
)
1729 s
->numFailures
= MAX_FAILED_STARTS
;
1732 if (s
->listenFd
< 0 && init_listen_sock(s
))
1734 if (sleepSeconds
> s
->initStartDelay
)
1735 sleepSeconds
= s
->initStartDelay
;
1739 if (caughtSigTerm
) {
1740 goto ProcessSigTerm
;
1743 s
->procs
[i
].pid
= spawn_fs_process(s
, &s
->procs
[i
]);
1744 if (s
->procs
[i
].pid
<= 0) {
1745 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1746 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1747 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1750 sleepSeconds
= min(sleepSeconds
,
1751 max((int) s
->restartDelay
, FCGI_MIN_EXEC_RETRY_DELAY
));
1753 ap_assert(s
->procs
[i
].pid
< 0);
1757 s
->procs
[i
].start_time
= now
;
1758 s
->restartTime
= now
;
1760 if (s
->startTime
== 0) {
1764 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1766 fcgi_dynamic_total_proc_count
++;
1767 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1770 s
->procs
[i
].state
= FCGI_RUNNING_STATE
;
1773 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1774 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1775 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1776 s
->fs_path
, (long) s
->uid
, (long) s
->gid
,
1777 restart
? "re" : "", (long) s
->procs
[i
].pid
);
1780 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1781 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1782 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1783 s
->fs_path
, restart
? "re" : "", (long) s
->procs
[i
].pid
);
1785 ap_assert(s
->procs
[i
].pid
> 0);
1787 sleepSeconds
= min(sleepSeconds
, restartTime
- now
);
1796 goto ProcessSigTerm
;
1798 if((!caughtSigChld
) && (!caughtSigAlarm
)) {
1801 alarm(sleepSeconds
);
1804 FD_SET(fcgi_pm_pipe
[0], &rfds
);
1805 read_ready
= ap_select(fcgi_pm_pipe
[0] + 1, &rfds
, NULL
, NULL
, NULL
);
1807 alarmLeft
= alarm(0);
1809 callWaitPid
= caughtSigChld
;
1810 caughtSigChld
= FALSE
;
1811 callDynamicProcs
= caughtSigAlarm
;
1812 caughtSigAlarm
= FALSE
;
1817 * Dynamic fcgi process management
1819 if((callDynamicProcs
) || (!callWaitPid
)) {
1820 dynamic_read_msgs(read_ready
);
1821 if(fcgi_dynamic_epoch
== 0) {
1822 fcgi_dynamic_epoch
= now
;
1824 if(((long)(now
-fcgi_dynamic_epoch
)>=dynamicKillInterval
) ||
1825 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
)>=dynamicMaxProcs
)) {
1826 dynamic_kill_idle_fs_procs();
1827 fcgi_dynamic_epoch
= now
;
1835 /* We've caught SIGCHLD, so find out who it was using waitpid,
1836 * write a log message and update its data structure. */
1840 goto ProcessSigTerm
;
1842 childPid
= waitpid(-1, &waitStatus
, WNOHANG
);
1844 if (childPid
== -1 || childPid
== 0)
1847 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1848 if (s
->directive
== APP_CLASS_EXTERNAL
)
1851 if (s
->directive
== APP_CLASS_DYNAMIC
)
1852 numChildren
= dynamicMaxClassProcs
;
1854 numChildren
= s
->numProcesses
;
1856 for (i
= 0; i
< numChildren
; i
++) {
1857 if (s
->procs
[i
].pid
== childPid
)
1862 /* TODO: print something about this unknown child */
1866 s
->procs
[i
].pid
= -1;
1868 if (s
->directive
== APP_CLASS_STANDARD
) {
1869 /* Always restart static apps */
1870 s
->procs
[i
].state
= FCGI_START_STATE
;
1875 fcgi_dynamic_total_proc_count
--;
1877 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1878 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1881 /* A dynamic app died or exited without provocation from the PM */
1884 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0))
1885 s
->procs
[i
].state
= FCGI_START_STATE
;
1887 s
->procs
[i
].state
= FCGI_READY_STATE
;
1891 if (WIFEXITED(waitStatus
)) {
1892 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1893 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1894 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1895 s
->fs_path
, (long) childPid
, WEXITSTATUS(waitStatus
));
1897 else if (WIFSIGNALED(waitStatus
)) {
1898 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1899 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1900 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1901 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
),
1903 WCOREDUMP(waitStatus
) ? ", a core file may have been generated" : "");
1908 else if (WIFSTOPPED(waitStatus
)) {
1909 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1910 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1911 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1912 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
));
1914 } /* for (;;), waitpid() */
1918 /* wait for an event to occur or timer expires */
1919 expire
= time(NULL
) + sleepSeconds
;
1920 dwRet
= WaitForMultipleObjects(3, (HANDLE
*) fcgi_event_handles
, FALSE
, sleepSeconds
* 1000);
1922 if (dwRet
== WAIT_FAILED
) {
1923 /* There is something seriously wrong here */
1924 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1925 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1929 if (dwRet
!= WAIT_TIMEOUT
) {
1933 alarmLeft
= expire
- now
;
1937 * Dynamic fcgi process management
1939 if ((dwRet
== MBOX_EVENT
) || (dwRet
== WAIT_TIMEOUT
)) {
1940 if (dwRet
== MBOX_EVENT
) {
1946 dynamic_read_msgs(read_ready
);
1948 if(fcgi_dynamic_epoch
== 0) {
1949 fcgi_dynamic_epoch
= now
;
1952 if ((now
-fcgi_dynamic_epoch
>= (int) dynamicKillInterval
) ||
1953 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
) >= dynamicMaxProcs
)) {
1954 dynamic_kill_idle_fs_procs();
1955 fcgi_dynamic_epoch
= now
;
1959 else if (dwRet
== WAKE_EVENT
) {
1962 else if (dwRet
== TERM_EVENT
) {
1963 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
1964 "FastCGI: Termination event received process manager shutting down");
1967 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
1969 goto ProcessSigTerm
;
1972 // Have an received an unknown event - should not happen
1973 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1974 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1977 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
1979 goto ProcessSigTerm
;
1984 } /* for (;;), the whole shoot'n match */
1988 * Kill off the children, then exit.
2000 int fcgi_pm_add_job(fcgi_pm_job
*new_job
)
2002 int rv
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
2004 if (rv
!= WAIT_OBJECT_0
&& rv
!= WAIT_ABANDONED
)
2006 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2007 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2011 new_job
->next
= fcgi_dynamic_mbox
;
2012 fcgi_dynamic_mbox
= new_job
;
2014 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
2016 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2017 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");