2 * $Id: fcgi_pm.c,v 1.84 2003/01/19 16:33:51 robs Exp $
8 #if defined(APACHE2) && !defined(WIN32)
12 #include "apr_signal.h"
21 #define seteuid(arg) setresuid(-1, (arg), -1)
24 int fcgi_dynamic_total_proc_count
= 0; /* number of running apps */
25 time_t fcgi_dynamic_epoch
= 0; /* last time kill_procs was
26 * invoked by process mgr */
27 time_t fcgi_dynamic_last_analyzed
= 0; /* last time calculation was
28 * made for the dynamic procs */
30 static time_t now
= 0;
33 #pragma warning ( disable : 4100 4102 )
34 static BOOL bTimeToDie
= FALSE
; /* process termination flag */
35 HANDLE fcgi_event_handles
[3];
43 static int seteuid_root(void)
45 int rc
= seteuid(getuid());
47 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
48 "FastCGI: seteuid(0) failed");
53 static int seteuid_user(void)
55 int rc
= seteuid(ap_user_id
);
57 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
58 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id
);
65 * Signal the process to exit. How (or if) the process responds
66 * depends on the FastCGI application library (esp. on Win32) and
67 * possibly application code (signal handlers and whether or not
68 * SA_RESTART is on). At any rate, we send the signal with the
69 * hopes that the process will exit on its own. Later, as we
70 * review the state of application processes, if we see one marked
71 * for death, but that hasn't died within a specified period of
72 * time, fcgi_kill() is called again with a KILL)
74 static void fcgi_kill(ServerProcess
*process
, int sig
)
76 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process
->pid
, sig
);
78 process
->state
= FCGI_VICTIM_STATE
;
84 SetEvent(process
->terminationEvent
);
86 else if (sig
== SIGKILL
)
88 TerminateProcess(process
->handle
, 1);
102 kill(process
->pid
, sig
);
112 /*******************************************************************************
113 * Send SIGTERM to each process in the server class, remove socket
114 * file if appropriate. Currently this is only called when the PM is shutting
115 * down and thus memory isn't freed and sockets and files aren't closed.
117 static void shutdown_all()
119 fcgi_server
*s
= fcgi_servers
;
123 ServerProcess
*proc
= s
->procs
;
125 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
126 ? dynamicMaxClassProcs
130 if (s
->socket_path
!= NULL
&& s
->directive
!= APP_CLASS_EXTERNAL
)
132 /* Remove the socket file */
133 if (unlink(s
->socket_path
) != 0 && errno
!= ENOENT
) {
134 ap_log_error(FCGI_LOG_ERR
, fcgi_apache_main_server
,
135 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
137 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "", s
->fs_path
);
142 /* Send TERM to all processes */
143 for (i
= 0; i
< numChildren
; i
++, proc
++)
145 if (proc
->state
== FCGI_RUNNING_STATE
)
147 fcgi_kill(proc
, SIGTERM
);
154 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
157 * WIN32 applications may not have support for the shutdown event
158 * depending on their application library version
161 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT
);
166 ServerProcess
*proc
= s
->procs
;
168 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
169 ? dynamicMaxClassProcs
172 /* Send KILL to all processes */
173 for (i
= 0; i
< numChildren
; i
++, proc
++)
175 if (proc
->state
== FCGI_RUNNING_STATE
)
177 fcgi_kill(proc
, SIGKILL
);
187 static int init_listen_sock(fcgi_server
* fs
)
189 ap_assert(fs
->directive
!= APP_CLASS_EXTERNAL
);
191 /* Create the socket */
192 if ((fs
->listenFd
= socket(fs
->socket_addr
->sa_family
, SOCK_STREAM
, 0)) < 0)
195 errno
= WSAGetLastError(); // Not sure if this will work as expected
197 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
198 "FastCGI: can't create %sserver \"%s\": socket() failed",
199 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
205 if (fs
->socket_addr
->sa_family
== AF_UNIX
)
207 /* Remove any existing socket file.. just in case */
208 unlink(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
);
214 setsockopt(fs
->listenFd
, SOL_SOCKET
, SO_REUSEADDR
, (char *)&flag
, sizeof(flag
));
217 /* Bind it to the socket_addr */
218 if (bind(fs
->listenFd
, fs
->socket_addr
, fs
->socket_addr_len
))
223 errno
= WSAGetLastError();
225 ap_snprintf(port
, sizeof(port
), "port=%d",
226 ((struct sockaddr_in
*)fs
->socket_addr
)->sin_port
);
228 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
229 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
230 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
233 (fs
->socket_addr
->sa_family
== AF_UNIX
) ?
234 ((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
:
240 /* Twiddle Unix socket permissions */
241 else if (fs
->socket_addr
->sa_family
== AF_UNIX
242 && chmod(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
, S_IRUSR
| S_IWUSR
))
244 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
245 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
246 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
252 else if (listen(fs
->listenFd
, fs
->listenQueueDepth
))
255 errno
= WSAGetLastError();
257 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
258 "FastCGI: can't create %sserver \"%s\": listen() failed",
259 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
268 closesocket(fs
->listenFd
);
279 *----------------------------------------------------------------------
283 * The FastCGI process manager, which runs as a separate
284 * process responsible for:
285 * - Starting all the FastCGI proceses.
286 * - Restarting any of these processes that die (indicated
288 * - Catching SIGTERM and relaying it to all the FastCGI
289 * processes before exiting.
292 * Uses global variable fcgi_servers.
300 *----------------------------------------------------------------------
303 static int caughtSigTerm
= FALSE
;
304 static int caughtSigChld
= FALSE
;
305 static int caughtSigAlarm
= FALSE
;
307 static void signal_handler(int signo
)
309 if ((signo
== SIGTERM
) || (signo
== SIGUSR1
) || (signo
== SIGHUP
)) {
310 /* SIGUSR1 & SIGHUP are sent by apache to its process group
311 * when apache get 'em. Apache follows up (1.2.x) with attacks
312 * on each of its child processes, but we've got the KillMgr
313 * sitting between us so we never see the KILL. The main loop
314 * in ProcMgr also checks to see if the KillMgr has terminated,
315 * and if it has, we handl it as if we should shutdown too. */
316 caughtSigTerm
= TRUE
;
317 } else if(signo
== SIGCHLD
) {
318 caughtSigChld
= TRUE
;
319 } else if(signo
== SIGALRM
) {
320 caughtSigAlarm
= TRUE
;
326 *----------------------------------------------------------------------
328 * spawn_fs_process --
330 * Fork and exec the specified fcgi process.
333 * 0 for successful fork, -1 for failed fork.
335 * In case the child fails before or in the exec, the child
336 * obtains the error log by calling getErrLog, logs
337 * the error, and exits with exit status = errno of
338 * the failed system call.
341 * Child process created.
343 *----------------------------------------------------------------------
345 static pid_t
spawn_fs_process(fcgi_server
*fs
, ServerProcess
*process
)
352 char *dnEnd
, *failedSysCall
;
359 /* We're the child. We're gonna exec() so pools don't matter. */
361 dnEnd
= strrchr(fs
->fs_path
, '/');
365 dirName
= ap_pcalloc(fcgi_config_pool
, dnEnd
- fs
->fs_path
+ 1);
366 dirName
= memcpy(dirName
, fs
->fs_path
, dnEnd
- fs
->fs_path
);
368 if (chdir(dirName
) < 0) {
369 failedSysCall
= "chdir()";
370 goto FailedSystemCallExit
;
374 /* OS/2 dosen't support nice() */
375 if (fs
->processPriority
!= 0) {
376 if (nice(fs
->processPriority
) == -1) {
377 failedSysCall
= "nice()";
378 goto FailedSystemCallExit
;
383 /* Open the listenFd on spec'd fd */
384 if (fs
->listenFd
!= FCGI_LISTENSOCK_FILENO
)
385 dup2(fs
->listenFd
, FCGI_LISTENSOCK_FILENO
);
387 /* Close all other open fds, except stdout/stderr. Leave these two open so
388 * FastCGI applications don't have to find and fix ALL 3rd party libs that
389 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
390 * main server error_log - @@@ provide a directive control where this goes.
392 ap_error_log2stderr(fcgi_apache_main_server
);
394 for (i
= 0; i
< FCGI_MAX_FD
; i
++) {
395 if (i
!= FCGI_LISTENSOCK_FILENO
&& i
!= 2 && i
!= 1) {
400 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
401 * install its own handler. */
402 signal(SIGPIPE
, SIG_IGN
);
408 /* Relinquish our root real uid powers */
412 /* AP13 does not use suexec if the target uid/gid is the same as the
413 * server's - AP20 does. I (now) consider the latter approach better
414 * (fcgi_pm.c v1.42 incorporated the 1.3 behaviour, v1.84 reverted it). */
416 shortName
= strrchr(fs
->fs_path
, '/') + 1;
419 execle(fcgi_wrapper
, fcgi_wrapper
, fs
->username
, fs
->group
,
420 shortName
, NULL
, fs
->envp
);
421 } while (errno
== EINTR
);
425 execle(fs
->fs_path
, fs
->fs_path
, NULL
, fs
->envp
);
426 } while (errno
== EINTR
);
429 failedSysCall
= "execle()";
431 FailedSystemCallExit
:
432 fprintf(stderr
, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
433 fs
->fs_path
, (long) getpid(), failedSysCall
, strerror(errno
));
436 /* avoid an irrelevant compiler warning */
443 /* based on mod_cgi.c:run_cgi_child() */
446 char * termination_env_string
;
447 HANDLE listen_handle
= INVALID_HANDLE_VALUE
;
448 apr_procattr_t
* procattr
;
449 apr_proc_t proc
= { 0 };
453 if (apr_pool_create(&tp
, fcgi_config_pool
))
456 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
457 if (process
->terminationEvent
== NULL
)
460 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
462 termination_env_string
= ap_psprintf(tp
,
463 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
465 while (fs
->envp
[i
]) i
++;
466 fs
->envp
[i
++] = termination_env_string
;
467 fs
->envp
[i
] = (char *) fs
->mutex_env_string
;
469 ap_assert(fs
->envp
[i
+ 1] == NULL
);
473 SECURITY_ATTRIBUTES sa
= { 0 };
475 sa
.bInheritHandle
= TRUE
;
476 sa
.nLength
= sizeof(sa
);
478 listen_handle
= CreateNamedPipe(fs
->socket_path
,
480 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
481 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
483 if (listen_handle
== INVALID_HANDLE_VALUE
)
485 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
486 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
493 listen_handle
= (HANDLE
) fs
->listenFd
;
496 if (apr_procattr_create(&procattr
, tp
))
499 if (apr_procattr_dir_set(procattr
, ap_make_dirstr_parent(tp
, fs
->fs_path
)))
502 if (apr_procattr_detach_set(procattr
, 1))
505 if (apr_os_file_put(&file
, &listen_handle
, 0, tp
))
508 /* procattr is opaque so we have to use this - unfortuantely it dups */
509 if (apr_procattr_child_in_set(procattr
, file
, NULL
))
512 if (apr_proc_create(&proc
, fs
->fs_path
, NULL
, fs
->envp
, procattr
, tp
))
515 process
->handle
= proc
.hproc
;
522 fs
->envp
[i
- 1] = NULL
;
529 #else /* WIN32 && !APACHE2 */
531 /* Adapted from Apache's util_script.c ap_call_exec() */
532 char *interpreter
= NULL
;
533 char *quoted_filename
;
535 char *pEnvBlock
, *pNext
;
538 int iEnvBlockLen
= 1;
540 file_type_e fileType
;
543 PROCESS_INFORMATION pi
;
548 pool
* tp
= ap_make_sub_pool(fcgi_config_pool
);
550 HANDLE listen_handle
;
551 char * termination_env_string
= NULL
;
553 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
554 if (process
->terminationEvent
== NULL
)
556 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
557 "FastCGI: can't create termination event for server \"%s\", "
558 "CreateEvent() failed", fs
->fs_path
);
561 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
563 termination_env_string
= ap_psprintf(tp
,
564 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
568 SECURITY_ATTRIBUTES sa
;
570 sa
.lpSecurityDescriptor
= NULL
;
571 sa
.bInheritHandle
= TRUE
;
572 sa
.nLength
= sizeof(sa
);
574 listen_handle
= CreateNamedPipe(fs
->socket_path
,
576 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
577 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
579 if (listen_handle
== INVALID_HANDLE_VALUE
)
581 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
582 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs
->fs_path
);
588 listen_handle
= (HANDLE
) fs
->listenFd
;
591 memset(&si
, 0, sizeof(si
));
592 memset(&pi
, 0, sizeof(pi
));
593 memset(&r
, 0, sizeof(r
));
595 // Can up a fake request to pass to ap_get_win32_interpreter()
596 r
.per_dir_config
= fcgi_apache_main_server
->lookup_defaults
;
597 r
.server
= fcgi_apache_main_server
;
598 r
.filename
= (char *) fs
->fs_path
;
601 fileType
= ap_get_win32_interpreter(&r
, &interpreter
);
603 if (fileType
== eFileTypeUNKNOWN
) {
604 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
605 "FastCGI: %s is not executable; ensure interpreted scripts have "
606 "\"#!\" as their first line",
613 * We have the interpreter (if there is one) and we have
614 * the arguments (if there are any).
615 * Build the command string to pass to CreateProcess.
617 quoted_filename
= ap_pstrcat(tp
, "\"", fs
->fs_path
, "\"", NULL
);
618 if (interpreter
&& *interpreter
) {
619 pCommand
= ap_pstrcat(tp
, interpreter
, " ", quoted_filename
, NULL
);
622 pCommand
= quoted_filename
;
626 * Make child process use hPipeOutputWrite as standard out,
627 * and make sure it does not show on screen.
630 si
.dwFlags
= STARTF_USESHOWWINDOW
| STARTF_USESTDHANDLES
;
631 si
.wShowWindow
= SW_HIDE
;
632 si
.hStdInput
= listen_handle
;
634 // XXX These should be open to the error_log
635 si
.hStdOutput
= INVALID_HANDLE_VALUE
;
636 si
.hStdError
= INVALID_HANDLE_VALUE
;
639 * Win32's CreateProcess call requires that the environment
640 * be passed in an environment block, a null terminated block of
641 * null terminated strings.
642 * @todo we should store the env in this format for win32.
646 iEnvBlockLen
+= strlen(fs
->envp
[i
]) + 1;
650 iEnvBlockLen
+= strlen(termination_env_string
) + 1;
651 iEnvBlockLen
+= strlen(fs
->mutex_env_string
) + 1;
653 pEnvBlock
= (char *) ap_pcalloc(tp
, iEnvBlockLen
);
659 strcpy(pNext
, fs
->envp
[i
]);
660 pNext
+= strlen(pNext
) + 1;
664 strcpy(pNext
, termination_env_string
);
665 pNext
+= strlen(pNext
) + 1;
666 strcpy(pNext
, fs
->mutex_env_string
);
668 if (CreateProcess(NULL
, pCommand
, NULL
, NULL
, TRUE
,
671 ap_make_dirstr_parent(tp
, fs
->fs_path
),
674 /* Hack to get 16-bit CGI's working. It works for all the
675 * standard modules shipped with Apache. pi.dwProcessId is 0
676 * for 16-bit CGIs and all the Unix specific code that calls
677 * ap_call_exec interprets this as a failure case. And we can't
678 * use -1 either because it is mapped to 0 by the caller.
680 pid
= (fileType
== eFileTypeEXE16
) ? -2 : pi
.dwProcessId
;
682 process
->handle
= pi
.hProcess
;
683 CloseHandle(pi
.hThread
);
688 CloseHandle(listen_handle
);
697 #endif /* !APACHE2 */
702 static void reduce_privileges(void)
710 /* Get username if passed as a uid */
711 if (ap_user_name
[0] == '#') {
712 uid_t uid
= atoi(&ap_user_name
[1]);
713 struct passwd
*ent
= getpwuid(uid
);
716 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
717 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
718 "you probably need to modify the User directive", (unsigned)uid
);
727 if (setgid(ap_group_id
) == -1) {
728 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
729 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id
);
733 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
735 /* Initialize supplementary groups */
736 if (initgroups(name
, ap_group_id
) == -1) {
737 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
738 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
739 name
, (unsigned)ap_group_id
);
746 if (seteuid_user() == -1) {
747 ap_log_error(FCGI_LOG_ALERT_NOERRNO
, fcgi_apache_main_server
,
748 "FastCGI: process manager exiting, failed to reduce privileges");
753 if (setuid(ap_user_id
) == -1) {
754 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
755 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id
);
762 * Change the name of this process - best we can easily.
764 static void change_process_name(const char * const name
)
766 /* under Apache2, ap_server_argv0 is const */
767 strncpy((char *) ap_server_argv0
, name
, strlen(ap_server_argv0
));
771 static void schedule_start(fcgi_server
*s
, int proc
)
773 /* If we've started one recently, don't register another */
774 time_t time_passed
= now
- s
->restartTime
;
776 if ((s
->procs
[proc
].pid
&& (time_passed
< (int) s
->restartDelay
))
777 || ((s
->procs
[proc
].pid
== 0) && (time_passed
< s
->initStartDelay
)))
779 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
);
783 FCGIDBG3("scheduling_start: %s (%d)", s
->fs_path
, proc
);
784 s
->procs
[proc
].state
= FCGI_START_STATE
;
785 if (proc
== dynamicMaxClassProcs
- 1) {
786 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
787 "FastCGI: scheduled the %sstart of the last (dynamic) server "
788 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
789 s
->procs
[proc
].pid
? "re" : "", s
->fs_path
, dynamicMaxClassProcs
);
794 *----------------------------------------------------------------------
798 * Removes the records written by request handlers and decodes them.
799 * We also update the data structures to reflect the changes.
801 *----------------------------------------------------------------------
804 static void dynamic_read_msgs(int read_ready
)
810 static int buflen
= 0;
811 static char buf
[FCGI_MSGS_BUFSIZE
+ 1];
812 char *ptr1
, *ptr2
, opcode
;
813 char execName
[FCGI_MAXPATH
+ 1];
814 char user
[MAX_USER_NAME_LEN
+ 2];
815 char group
[MAX_GID_CHAR_LEN
+ 1];
816 unsigned long q_usec
= 0UL, req_usec
= 0UL;
818 fcgi_pm_job
*joblist
= NULL
;
819 fcgi_pm_job
*cjob
= NULL
;
822 pool
*sp
= NULL
, *tp
;
825 user
[MAX_USER_NAME_LEN
+ 1] = group
[MAX_GID_CHAR_LEN
] = '\0';
829 * To prevent the idle application from running indefinitely, we
830 * check the timer and if it is expired, we recompute the values
831 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
832 * message is received, only updates are made to the data structures.
834 if (fcgi_dynamic_last_analyzed
== 0) {
835 fcgi_dynamic_last_analyzed
= now
;
837 if ((now
- fcgi_dynamic_last_analyzed
) >= (int)dynamicUpdateInterval
) {
838 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
839 if (s
->directive
!= APP_CLASS_DYNAMIC
)
842 /* Advance the last analyzed timestamp by the elapsed time since
843 * it was last set. Round the increase down to the nearest
844 * multiple of dynamicUpdateInterval */
846 fcgi_dynamic_last_analyzed
+= (((long)(now
-fcgi_dynamic_last_analyzed
)/dynamicUpdateInterval
)*dynamicUpdateInterval
);
847 s
->smoothConnTime
= (unsigned long) ((1.0-dynamicGain
)*s
->smoothConnTime
+ dynamicGain
*s
->totalConnTime
);
848 s
->totalConnTime
= 0UL;
849 s
->totalQueueTime
= 0UL;
853 if (read_ready
<= 0) {
858 rc
= read(fcgi_pm_pipe
[0], (void *)(buf
+ buflen
), FCGI_MSGS_BUFSIZE
- buflen
);
860 if (!caughtSigTerm
) {
861 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
862 "FastCGI: read() from pipe failed (%d)", rc
);
864 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
865 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
866 caughtSigTerm
= TRUE
;
876 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
877 * request to do something) and/or when a timeout expires.
878 * There really should be no reason why this wait would get stuck
879 * but there's no point in waiting forever. */
881 rc
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
883 if (rc
!= WAIT_OBJECT_0
&& rc
!= WAIT_ABANDONED
)
885 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
886 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
890 joblist
= fcgi_dynamic_mbox
;
891 fcgi_dynamic_mbox
= NULL
;
893 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
895 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
896 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
903 apr_pool_create(&tp
, fcgi_config_pool
);
905 tp
= ap_make_sub_pool(fcgi_config_pool
);
909 for (ptr1
= buf
; ptr1
; ptr1
= ptr2
) {
912 ptr2
= strchr(ptr1
, '*');
924 case FCGI_SERVER_START_JOB
:
925 case FCGI_SERVER_RESTART_JOB
:
927 if (sscanf(ptr1
, "%c %s %16s %15s",
928 &opcode
, execName
, user
, group
) != 4)
934 case FCGI_REQUEST_TIMEOUT_JOB
:
936 if (sscanf(ptr1
, "%c %s %16s %15s",
937 &opcode
, execName
, user
, group
) != 4)
943 case FCGI_REQUEST_COMPLETE_JOB
:
945 if (sscanf(ptr1
, "%c %s %16s %15s %lu %lu",
946 &opcode
, execName
, user
, group
, &q_usec
, &req_usec
) != 6)
958 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode
, execName
, user
, group
, q_usec
, req_usec
);
961 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
962 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1
);
966 /* Update data structures for processing */
967 while (cjob
!= NULL
) {
968 joblist
= cjob
->next
;
969 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob
->id
, cjob
->fs_path
, cjob
->user
, cjob
->group
, cjob
->qsec
, cjob
->start_time
);
973 s
= fcgi_util_fs_get(execName
, user
, group
);
975 s
= fcgi_util_fs_get(cjob
->fs_path
, cjob
->user
, cjob
->group
);
979 if (s
==NULL
&& opcode
!= FCGI_REQUEST_COMPLETE_JOB
)
981 if (s
==NULL
&& cjob
->id
!= FCGI_REQUEST_COMPLETE_JOB
)
986 HANDLE mutex
= CreateMutex(NULL
, FALSE
, cjob
->fs_path
);
990 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
991 "FastCGI: can't create accept mutex "
992 "for (dynamic) server \"%s\"", cjob
->fs_path
);
996 SetHandleInformation(mutex
, HANDLE_FLAG_INHERIT
, TRUE
);
1001 /* Create a perm subpool to hold the new server data,
1002 * we can destroy it if something doesn't pan out */
1004 apr_pool_create(&sp
, fcgi_config_pool
);
1006 sp
= ap_make_sub_pool(fcgi_config_pool
);
1009 /* Create a new "dynamic" server */
1010 s
= fcgi_util_fs_new(sp
);
1012 s
->directive
= APP_CLASS_DYNAMIC
;
1013 s
->restartDelay
= dynamicRestartDelay
;
1014 s
->listenQueueDepth
= dynamicListenQueueDepth
;
1015 s
->initStartDelay
= dynamicInitStartDelay
;
1016 s
->envp
= dynamicEnvp
;
1017 s
->flush
= dynamicFlush
;
1020 s
->mutex_env_string
= ap_psprintf(sp
, "_FCGI_MUTEX_=%ld", mutex
);
1021 s
->fs_path
= ap_pstrdup(sp
, cjob
->fs_path
);
1023 s
->fs_path
= ap_pstrdup(sp
, execName
);
1025 ap_getparents(s
->fs_path
);
1026 ap_no2slash(s
->fs_path
);
1027 s
->procs
= fcgi_util_fs_create_procs(sp
, dynamicMaxClassProcs
);
1029 /* XXX the socket_path (both Unix and Win) *is* deducible and
1030 * thus can and will be used by other apache instances without
1031 * the use of shared data regarding the processes serving the
1032 * requests. This can result in slightly unintuitive process
1033 * counts and security implications. This is prevented
1034 * if suexec (Unix) is in use. This is both a feature and a flaw.
1035 * Changing it now would break existing installations. */
1038 /* Create socket file's path */
1039 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, execName
, user
, group
);
1040 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1042 /* Create sockaddr, prealloc it so it won't get created in tp */
1043 s
->socket_addr
= ap_pcalloc(sp
, sizeof(struct sockaddr_un
));
1044 err
= fcgi_util_socket_make_domain_addr(tp
, (struct sockaddr_un
**)&s
->socket_addr
,
1045 &s
->socket_addr_len
, s
->socket_path
);
1047 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1048 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
1052 if (init_listen_sock(s
)) {
1056 /* If a wrapper is being used, config user/group info */
1058 if (user
[0] == '~') {
1059 /* its a user dir uri, the rest is a username, not a uid */
1060 struct passwd
*pw
= getpwnam(&user
[1]);
1063 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1064 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1065 execName
, &user
[1]);
1068 s
->uid
= pw
->pw_uid
;
1069 s
->user
= ap_pstrdup(sp
, user
);
1070 s
->username
= s
->user
;
1072 s
->gid
= pw
->pw_gid
;
1073 s
->group
= ap_psprintf(sp
, "%ld", (long)s
->gid
);
1078 s
->uid
= (uid_t
)atol(user
);
1079 pw
= getpwuid(s
->uid
);
1081 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1082 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1083 execName
, (long)s
->uid
);
1086 s
->user
= ap_pstrdup(sp
, user
);
1087 s
->username
= ap_pstrdup(sp
, pw
->pw_name
);
1089 s
->gid
= (gid_t
)atol(group
);
1090 s
->group
= ap_pstrdup(sp
, group
);
1094 /* Create socket file's path */
1095 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, cjob
->fs_path
, cjob
->user
, cjob
->group
);
1096 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1100 fcgi_util_fs_add(s
);
1104 if (opcode
== FCGI_SERVER_RESTART_JOB
) {
1106 if (cjob
->id
==FCGI_SERVER_RESTART_JOB
) {
1108 /* Check to see if the binary has changed. If so,
1109 * kill the FCGI application processes, and
1115 char * app_path
= cjob
->fs_path
;
1117 char * app_path
= execName
;
1120 if (stat(app_path
, &stbuf
) == 0 && stbuf
.st_mtime
> s
->startTime
)
1124 /* prevent addition restart requests */
1127 utime(s
->socket_path
, NULL
);
1130 /* kill old server(s) */
1131 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1133 if (s
->procs
[i
].pid
> 0
1134 && stbuf
.st_mtime
> s
->procs
[i
].start_time
)
1136 fcgi_kill(&s
->procs
[i
], SIGTERM
);
1143 ap_log_error(FCGI_LOG_WARN_NOERRNO
,
1144 fcgi_apache_main_server
, "FastCGI: restarting "
1145 "old server \"%s\" processes, newer version "
1150 /* If dynamicAutoRestart, don't mark any new processes
1151 * for starting because we probably got the
1152 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1153 * will be restarting all of those we just killed.
1155 if (dynamicAutoRestart
)
1159 else if (opcode
== FCGI_SERVER_START_JOB
) {
1161 else if (cjob
->id
==FCGI_SERVER_START_JOB
) {
1163 /* we've been asked to start a process--only start
1164 * it if we're not already running at least one
1169 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1170 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1173 /* if already running, don't start another one */
1174 if (i
< dynamicMaxClassProcs
) {
1188 case FCGI_SERVER_RESTART_JOB
:
1192 /* We just waxed 'em all. Try to find an idle slot. */
1194 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1196 if (s
->procs
[i
].state
== FCGI_START_STATE
1197 || s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1201 else if (s
->procs
[i
].state
== FCGI_KILLED_STATE
1202 || s
->procs
[i
].state
== FCGI_READY_STATE
)
1209 /* Nope, just use the first slot */
1210 if (i
== dynamicMaxClassProcs
)
1218 schedule_start(s
, i
);
1223 case FCGI_SERVER_START_JOB
:
1224 case FCGI_REQUEST_TIMEOUT_JOB
:
1226 if ((fcgi_dynamic_total_proc_count
+ 1) > (int) dynamicMaxProcs
) {
1228 * Extra instances should have been
1229 * terminated beforehand, probably need
1230 * to increase ProcessSlack parameter
1232 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1233 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1234 "exceeded dynamicMaxProcs (%d)", s
->fs_path
, dynamicMaxProcs
);
1238 /* find next free slot */
1239 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1241 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1243 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i
);
1246 else if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1251 schedule_start(s
, i
);
1256 if (i
>= dynamicMaxClassProcs
) {
1257 FCGIDBG1("ignore_job: slots are max'd");
1261 case FCGI_REQUEST_COMPLETE_JOB
:
1262 /* only record stats if we have a structure */
1265 s
->totalConnTime
+= req_usec
;
1266 s
->totalQueueTime
+= q_usec
;
1268 s
->totalConnTime
+= cjob
->start_time
;
1269 s
->totalQueueTime
+= cjob
->qsec
;
1278 /* Cleanup job data */
1279 free(cjob
->fs_path
);
1289 if (sp
) ap_destroy_pool(sp
);
1292 free(cjob
->fs_path
);
1300 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1301 "FastCGI: really bogus message: \"%s\"", ptr1
);
1302 ptr1
+= strlen(buf
);
1305 buflen
-= ptr1
- buf
;
1307 memmove(buf
, ptr1
, buflen
);
1311 ap_destroy_pool(tp
);
1315 *----------------------------------------------------------------------
1317 * dynamic_kill_idle_fs_procs
1319 * Implement a kill policy for the dynamic FastCGI applications.
1320 * We also update the data structures to reflect the changes.
1323 * Processes are marked for deletion possibly killed.
1325 *----------------------------------------------------------------------
1327 static void dynamic_kill_idle_fs_procs(void)
1332 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1335 * server's smoothed running time, or if that's 0, the current total
1337 unsigned long connTime
;
1340 * maximum number of microseconds that all of a server's running
1341 * processes together could have spent running since the last check
1343 unsigned long totalTime
;
1346 * percentage, 0-100, of totalTime that the processes actually used
1351 int really_running
= 0;
1353 if (s
->directive
!= APP_CLASS_DYNAMIC
|| s
->numProcesses
== 0)
1358 /* s->numProcesses includes pending kills so get the "active" count */
1359 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1361 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
) ++really_running
;
1364 connTime
= s
->smoothConnTime
? s
->smoothConnTime
: s
->totalConnTime
;
1365 totalTime
= really_running
* (now
- fcgi_dynamic_epoch
) * 1000000 + 1;
1367 loadFactor
= 100 * connTime
/ totalTime
;
1369 if (really_running
== 1)
1371 if (loadFactor
>= dynamicThreshold1
)
1378 int load
= really_running
/ ( really_running
- 1) * loadFactor
;
1380 if (load
>= dynamicThresholdN
)
1387 * Run through the procs to see if we can get away w/o waxing one.
1389 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1391 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1393 s
->procs
[i
].state
= FCGI_READY_STATE
;
1396 else if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
)
1402 if (i
>= dynamicMaxClassProcs
)
1404 ServerProcess
* procs
= s
->procs
;
1407 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1409 if (procs
[i
].state
== FCGI_RUNNING_STATE
)
1411 if (youngest
== -1 || procs
[i
].start_time
>= procs
[youngest
].start_time
)
1420 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1421 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1422 s
->fs_path
, (long) s
->procs
[youngest
].pid
);
1424 fcgi_kill(&s
->procs
[youngest
], SIGTERM
);
1430 * If the number of non-victims is less than or equal to
1431 * the minimum that may be running without being killed off,
1432 * don't select any more victims.
1434 if (fcgi_dynamic_total_proc_count
- victims
<= dynamicMinProcs
)
1444 // This is a little bogus, there's gotta be a better way to do this
1445 // Can we use WaitForMultipleObjects()
1446 #define FCGI_PROC_WAIT_TIME 100
1448 void child_wait_thread_main(void *dummy
) {
1450 DWORD dwRet
= WAIT_TIMEOUT
;
1455 while (!bTimeToDie
) {
1458 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1459 if (s
->directive
== APP_CLASS_EXTERNAL
|| s
->listenFd
< 0) {
1462 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1463 numChildren
= dynamicMaxClassProcs
;
1466 numChildren
= s
->numProcesses
;
1469 for (i
=0; i
< numChildren
; i
++) {
1470 if (s
->procs
[i
].handle
!= INVALID_HANDLE_VALUE
)
1472 DWORD exitStatus
= 0;
1474 /* timeout is currently set for 100 miliecond */
1475 /* it may need to be longer or user customizable */
1476 dwRet
= WaitForSingleObject(s
->procs
[i
].handle
, FCGI_PROC_WAIT_TIME
);
1480 if (dwRet
!= WAIT_TIMEOUT
&& dwRet
!= WAIT_FAILED
) {
1481 /* a child fs has died */
1482 /* mark the child as dead */
1484 if (s
->directive
== APP_CLASS_STANDARD
) {
1485 /* restart static app */
1486 s
->procs
[i
].state
= FCGI_START_STATE
;
1491 fcgi_dynamic_total_proc_count
--;
1492 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1494 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1495 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1498 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1501 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0)) {
1502 s
->procs
[i
].state
= FCGI_START_STATE
;
1505 s
->procs
[i
].state
= FCGI_READY_STATE
;
1510 GetExitCodeProcess(s
->procs
[i
].handle
, &exitStatus
);
1512 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1513 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1514 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1515 s
->fs_path
, (long) s
->procs
[i
].pid
, exitStatus
);
1517 CloseHandle(s
->procs
[i
].handle
);
1518 s
->procs
[i
].handle
= INVALID_HANDLE_VALUE
;
1519 s
->procs
[i
].pid
= -1;
1521 /* wake up the main thread */
1522 SetEvent(fcgi_event_handles
[WAKE_EVENT
]);
1527 Sleep(waited
? 0 : FCGI_PROC_WAIT_TIME
);
1533 static void setup_signals(void)
1535 struct sigaction sa
;
1537 /* Setup handlers */
1539 sa
.sa_handler
= signal_handler
;
1540 sigemptyset(&sa
.sa_mask
);
1543 if (sigaction(SIGTERM
, &sa
, NULL
) < 0) {
1544 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1545 "sigaction(SIGTERM) failed");
1548 if (sigaction(SIGHUP
, &sa
, NULL
) < 0) {
1549 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1550 "sigaction(SIGHUP) failed");
1552 /* httpd graceful restart */
1553 if (sigaction(SIGUSR1
, &sa
, NULL
) < 0) {
1554 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1555 "sigaction(SIGUSR1) failed");
1557 /* read messages from request handlers - kill interval expired */
1558 if (sigaction(SIGALRM
, &sa
, NULL
) < 0) {
1559 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1560 "sigaction(SIGALRM) failed");
1562 if (sigaction(SIGCHLD
, &sa
, NULL
) < 0) {
1563 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1564 "sigaction(SIGCHLD) failed");
1569 #if !defined(WIN32) && !defined(APACHE2)
1570 int fcgi_pm_main(void *dummy
, child_info
*info
)
1572 void fcgi_pm_main(void *dummy
)
1582 HANDLE child_wait_thread
= INVALID_HANDLE_VALUE
;
1584 int callWaitPid
, callDynamicProcs
;
1588 // Add SystemRoot to the dynamic environment
1589 char ** envp
= dynamicEnvp
;
1590 for (i
= 0; *envp
; ++i
) {
1593 fcgi_config_set_env_var(fcgi_config_pool
, dynamicEnvp
, &i
, "SystemRoot");
1597 reduce_privileges();
1598 change_process_name("fcgi-pm");
1600 close(fcgi_pm_pipe
[1]);
1604 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1605 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper
);
1609 /* Initialize AppClass */
1610 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1612 if (s
->directive
!= APP_CLASS_STANDARD
)
1620 for (i
= 0; i
< s
->numProcesses
; ++i
)
1621 s
->procs
[i
].state
= FCGI_START_STATE
;
1625 child_wait_thread
= (HANDLE
) _beginthread(child_wait_thread_main
, 0, NULL
);
1627 if (child_wait_thread
== (HANDLE
) -1)
1629 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1630 "FastCGI: failed to create process manager's wait thread!");
1633 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1634 "FastCGI: process manager initialized");
1636 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1637 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1643 * Loop until SIGTERM
1646 int sleepSeconds
= min(dynamicKillInterval
, dynamicUpdateInterval
);
1653 unsigned int numChildren
;
1656 * If we came out of sigsuspend() for any reason other than
1657 * SIGALRM, pick up where we left off.
1660 sleepSeconds
= alarmLeft
;
1663 * Examine each configured AppClass for a process that needs
1664 * starting. Compute the earliest time when the start should
1665 * be attempted, starting it now if the time has passed. Also,
1666 * remember that we do NOT need to restart externally managed
1667 * FastCGI applications.
1669 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1671 if (s
->directive
== APP_CLASS_EXTERNAL
)
1674 numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
1675 ? dynamicMaxClassProcs
1678 for (i
= 0; i
< numChildren
; ++i
)
1680 if (s
->procs
[i
].pid
<= 0 && s
->procs
[i
].state
== FCGI_START_STATE
)
1682 int restart
= (s
->procs
[i
].pid
< 0);
1683 time_t restartTime
= s
->restartTime
;
1687 /* we've gone to using the badDelay, the only thing that
1688 resets bad is when badDelay has expired. but numFailures
1689 is only just set below its threshold. the proc's
1690 start_times are all reset when the bad is. the numFailures
1691 is reset when we see an app run for a period */
1693 s
->procs
[i
].start_time
= 0;
1696 if (s
->numFailures
> MAX_FAILED_STARTS
)
1698 time_t last_start_time
= s
->procs
[i
].start_time
;
1700 if (last_start_time
&& now
- last_start_time
> RUNTIME_SUCCESS_INTERVAL
)
1704 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1705 "FastCGI:%s server \"%s\" has remained"
1706 " running for more than %d seconds, its restart"
1707 " interval has been restored to %d seconds",
1708 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1709 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, s
->restartDelay
);
1715 for (j
= 0; j
< numChildren
; ++j
)
1717 if (s
->procs
[j
].pid
<= 0) continue;
1718 if (s
->procs
[j
].state
!= FCGI_RUNNING_STATE
) continue;
1719 if (s
->procs
[j
].start_time
== 0) continue;
1720 if (now
- s
->procs
[j
].start_time
> RUNTIME_SUCCESS_INTERVAL
) break;
1723 if (j
>= numChildren
)
1726 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1727 "FastCGI:%s server \"%s\" has failed to remain"
1728 " running for %d seconds given %d attempts, its restart"
1729 " interval has been backed off to %d seconds",
1730 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1731 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, MAX_FAILED_STARTS
,
1732 FAILED_STARTS_DELAY
);
1738 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1739 "FastCGI:%s server \"%s\" has remained"
1740 " running for more than %d seconds, its restart"
1741 " interval has been restored to %d seconds",
1742 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1743 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, s
->restartDelay
);
1750 restartTime
+= FAILED_STARTS_DELAY
;
1754 restartTime
+= (restart
) ? s
->restartDelay
: s
->initStartDelay
;
1757 if (restartTime
<= now
)
1762 s
->numFailures
= MAX_FAILED_STARTS
;
1765 if (s
->listenFd
< 0 && init_listen_sock(s
))
1767 if (sleepSeconds
> s
->initStartDelay
)
1768 sleepSeconds
= s
->initStartDelay
;
1772 if (caughtSigTerm
) {
1773 goto ProcessSigTerm
;
1776 s
->procs
[i
].pid
= spawn_fs_process(s
, &s
->procs
[i
]);
1777 if (s
->procs
[i
].pid
<= 0) {
1778 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1779 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1780 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1783 sleepSeconds
= min(sleepSeconds
,
1784 max((int) s
->restartDelay
, FCGI_MIN_EXEC_RETRY_DELAY
));
1786 ap_assert(s
->procs
[i
].pid
< 0);
1790 s
->procs
[i
].start_time
= now
;
1791 s
->restartTime
= now
;
1793 if (s
->startTime
== 0) {
1797 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1799 fcgi_dynamic_total_proc_count
++;
1800 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1803 s
->procs
[i
].state
= FCGI_RUNNING_STATE
;
1806 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1807 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1808 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1809 s
->fs_path
, (long) s
->uid
, (long) s
->gid
,
1810 restart
? "re" : "", (long) s
->procs
[i
].pid
);
1813 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1814 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1815 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1816 s
->fs_path
, restart
? "re" : "", (long) s
->procs
[i
].pid
);
1818 ap_assert(s
->procs
[i
].pid
> 0);
1820 sleepSeconds
= min(sleepSeconds
, restartTime
- now
);
1829 goto ProcessSigTerm
;
1831 if((!caughtSigChld
) && (!caughtSigAlarm
)) {
1834 alarm(sleepSeconds
);
1837 FD_SET(fcgi_pm_pipe
[0], &rfds
);
1838 read_ready
= ap_select(fcgi_pm_pipe
[0] + 1, &rfds
, NULL
, NULL
, NULL
);
1840 alarmLeft
= alarm(0);
1842 callWaitPid
= caughtSigChld
;
1843 caughtSigChld
= FALSE
;
1844 callDynamicProcs
= caughtSigAlarm
;
1845 caughtSigAlarm
= FALSE
;
1850 * Dynamic fcgi process management
1852 if((callDynamicProcs
) || (!callWaitPid
)) {
1853 dynamic_read_msgs(read_ready
);
1854 if(fcgi_dynamic_epoch
== 0) {
1855 fcgi_dynamic_epoch
= now
;
1857 if(((long)(now
-fcgi_dynamic_epoch
)>=dynamicKillInterval
) ||
1858 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
)>=dynamicMaxProcs
)) {
1859 dynamic_kill_idle_fs_procs();
1860 fcgi_dynamic_epoch
= now
;
1868 /* We've caught SIGCHLD, so find out who it was using waitpid,
1869 * write a log message and update its data structure. */
1873 goto ProcessSigTerm
;
1875 childPid
= waitpid(-1, &waitStatus
, WNOHANG
);
1877 if (childPid
== -1 || childPid
== 0)
1880 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1881 if (s
->directive
== APP_CLASS_EXTERNAL
)
1884 if (s
->directive
== APP_CLASS_DYNAMIC
)
1885 numChildren
= dynamicMaxClassProcs
;
1887 numChildren
= s
->numProcesses
;
1889 for (i
= 0; i
< numChildren
; i
++) {
1890 if (s
->procs
[i
].pid
== childPid
)
1895 /* TODO: print something about this unknown child */
1899 s
->procs
[i
].pid
= -1;
1901 if (s
->directive
== APP_CLASS_STANDARD
) {
1902 /* Always restart static apps */
1903 s
->procs
[i
].state
= FCGI_START_STATE
;
1908 fcgi_dynamic_total_proc_count
--;
1910 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1911 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1914 /* A dynamic app died or exited without provocation from the PM */
1917 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0))
1918 s
->procs
[i
].state
= FCGI_START_STATE
;
1920 s
->procs
[i
].state
= FCGI_READY_STATE
;
1924 if (WIFEXITED(waitStatus
)) {
1925 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1926 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1927 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1928 s
->fs_path
, (long) childPid
, WEXITSTATUS(waitStatus
));
1930 else if (WIFSIGNALED(waitStatus
)) {
1931 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1932 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1933 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1934 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
),
1936 WCOREDUMP(waitStatus
) ? ", a core file may have been generated" : "");
1941 else if (WIFSTOPPED(waitStatus
)) {
1942 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1943 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1944 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1945 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
));
1947 } /* for (;;), waitpid() */
1951 /* wait for an event to occur or timer expires */
1952 expire
= time(NULL
) + sleepSeconds
;
1953 dwRet
= WaitForMultipleObjects(3, (HANDLE
*) fcgi_event_handles
, FALSE
, sleepSeconds
* 1000);
1955 if (dwRet
== WAIT_FAILED
) {
1956 /* There is something seriously wrong here */
1957 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1958 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1962 if (dwRet
!= WAIT_TIMEOUT
) {
1966 alarmLeft
= expire
- now
;
1970 * Dynamic fcgi process management
1972 if ((dwRet
== MBOX_EVENT
) || (dwRet
== WAIT_TIMEOUT
)) {
1973 if (dwRet
== MBOX_EVENT
) {
1979 dynamic_read_msgs(read_ready
);
1981 if(fcgi_dynamic_epoch
== 0) {
1982 fcgi_dynamic_epoch
= now
;
1985 if ((now
-fcgi_dynamic_epoch
>= (int) dynamicKillInterval
) ||
1986 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
) >= dynamicMaxProcs
)) {
1987 dynamic_kill_idle_fs_procs();
1988 fcgi_dynamic_epoch
= now
;
1992 else if (dwRet
== WAKE_EVENT
) {
1995 else if (dwRet
== TERM_EVENT
) {
1996 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
1997 "FastCGI: Termination event received process manager shutting down");
2000 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
2002 goto ProcessSigTerm
;
2005 // Have an received an unknown event - should not happen
2006 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
2007 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
2010 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
2012 goto ProcessSigTerm
;
2017 } /* for (;;), the whole shoot'n match */
2021 * Kill off the children, then exit.
2033 int fcgi_pm_add_job(fcgi_pm_job
*new_job
)
2035 int rv
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
2037 if (rv
!= WAIT_OBJECT_0
&& rv
!= WAIT_ABANDONED
)
2039 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2040 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2044 new_job
->next
= fcgi_dynamic_mbox
;
2045 fcgi_dynamic_mbox
= new_job
;
2047 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
2049 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2050 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");