2 * $Id: fcgi_pm.c,v 1.80 2002/10/11 00:07:03 robs Exp $
8 #if defined(APACHE2) && !defined(WIN32)
15 #define seteuid(arg) setresuid(-1, (arg), -1)
18 int fcgi_dynamic_total_proc_count
= 0; /* number of running apps */
19 time_t fcgi_dynamic_epoch
= 0; /* last time kill_procs was
20 * invoked by process mgr */
21 time_t fcgi_dynamic_last_analyzed
= 0; /* last time calculation was
22 * made for the dynamic procs */
24 static time_t now
= 0;
27 #pragma warning ( disable : 4100 4102 )
28 static BOOL bTimeToDie
= FALSE
; /* process termination flag */
29 HANDLE fcgi_event_handles
[3];
37 static int seteuid_root(void)
39 int rc
= seteuid((uid_t
)0);
41 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
42 "FastCGI: seteuid(0) failed");
47 static int seteuid_user(void)
49 int rc
= seteuid(ap_user_id
);
51 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
52 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id
);
59 * Signal the process to exit. How (or if) the process responds
60 * depends on the FastCGI application library (esp. on Win32) and
61 * possibly application code (signal handlers and whether or not
62 * SA_RESTART is on). At any rate, we send the signal with the
63 * hopes that the process will exit on its own. Later, as we
64 * review the state of application processes, if we see one marked
65 * for death, but that hasn't died within a specified period of
66 * time, fcgi_kill() is called again with a KILL)
68 static void fcgi_kill(ServerProcess
*process
, int sig
)
70 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process
->pid
, sig
);
72 process
->state
= FCGI_VICTIM_STATE
;
78 SetEvent(process
->terminationEvent
);
80 else if (sig
== SIGKILL
)
82 TerminateProcess(process
->handle
, 1);
96 kill(process
->pid
, sig
);
106 /*******************************************************************************
107 * Send SIGTERM to each process in the server class, remove socket
108 * file if appropriate. Currently this is only called when the PM is shutting
109 * down and thus memory isn't freed and sockets and files aren't closed.
111 static void shutdown_all()
113 fcgi_server
*s
= fcgi_servers
;
117 ServerProcess
*proc
= s
->procs
;
119 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
120 ? dynamicMaxClassProcs
124 if (s
->socket_path
!= NULL
&& s
->directive
!= APP_CLASS_EXTERNAL
)
126 /* Remove the socket file */
127 if (unlink(s
->socket_path
) != 0 && errno
!= ENOENT
) {
128 ap_log_error(FCGI_LOG_ERR
, fcgi_apache_main_server
,
129 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
131 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "", s
->fs_path
);
136 /* Send TERM to all processes */
137 for (i
= 0; i
< numChildren
; i
++, proc
++)
139 if (proc
->state
== FCGI_RUNNING_STATE
)
141 fcgi_kill(proc
, SIGTERM
);
148 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
151 * WIN32 applications may not have support for the shutdown event
152 * depending on their application library version
155 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT
);
160 ServerProcess
*proc
= s
->procs
;
162 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
163 ? dynamicMaxClassProcs
166 /* Send KILL to all processes */
167 for (i
= 0; i
< numChildren
; i
++, proc
++)
169 if (proc
->state
== FCGI_RUNNING_STATE
)
171 fcgi_kill(proc
, SIGKILL
);
181 static int init_listen_sock(fcgi_server
* fs
)
183 ap_assert(fs
->directive
!= APP_CLASS_EXTERNAL
);
185 /* Create the socket */
186 if ((fs
->listenFd
= socket(fs
->socket_addr
->sa_family
, SOCK_STREAM
, 0)) < 0)
189 errno
= WSAGetLastError(); // Not sure if this will work as expected
191 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
192 "FastCGI: can't create %sserver \"%s\": socket() failed",
193 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
199 if (fs
->socket_addr
->sa_family
== AF_UNIX
)
201 /* Remove any existing socket file.. just in case */
202 unlink(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
);
208 setsockopt(fs
->listenFd
, SOL_SOCKET
, SO_REUSEADDR
, (char *)&flag
, sizeof(flag
));
211 /* Bind it to the socket_addr */
212 if (bind(fs
->listenFd
, fs
->socket_addr
, fs
->socket_addr_len
))
217 errno
= WSAGetLastError();
219 ap_snprintf(port
, sizeof(port
), "port=%d",
220 ((struct sockaddr_in
*)fs
->socket_addr
)->sin_port
);
222 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
223 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
224 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
227 (fs
->socket_addr
->sa_family
== AF_UNIX
) ?
228 ((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
:
234 /* Twiddle Unix socket permissions */
235 else if (fs
->socket_addr
->sa_family
== AF_UNIX
236 && chmod(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
, S_IRUSR
| S_IWUSR
))
238 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
239 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
240 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
246 else if (listen(fs
->listenFd
, fs
->listenQueueDepth
))
249 errno
= WSAGetLastError();
251 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
252 "FastCGI: can't create %sserver \"%s\": listen() failed",
253 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
262 closesocket(fs
->listenFd
);
273 *----------------------------------------------------------------------
277 * The FastCGI process manager, which runs as a separate
278 * process responsible for:
279 * - Starting all the FastCGI proceses.
280 * - Restarting any of these processes that die (indicated
282 * - Catching SIGTERM and relaying it to all the FastCGI
283 * processes before exiting.
286 * Uses global variable fcgi_servers.
294 *----------------------------------------------------------------------
297 static int caughtSigTerm
= FALSE
;
298 static int caughtSigChld
= FALSE
;
299 static int caughtSigAlarm
= FALSE
;
301 static void signal_handler(int signo
)
303 if ((signo
== SIGTERM
) || (signo
== SIGUSR1
) || (signo
== SIGHUP
)) {
304 /* SIGUSR1 & SIGHUP are sent by apache to its process group
305 * when apache get 'em. Apache follows up (1.2.x) with attacks
306 * on each of its child processes, but we've got the KillMgr
307 * sitting between us so we never see the KILL. The main loop
308 * in ProcMgr also checks to see if the KillMgr has terminated,
309 * and if it has, we handl it as if we should shutdown too. */
310 caughtSigTerm
= TRUE
;
311 } else if(signo
== SIGCHLD
) {
312 caughtSigChld
= TRUE
;
313 } else if(signo
== SIGALRM
) {
314 caughtSigAlarm
= TRUE
;
320 *----------------------------------------------------------------------
322 * spawn_fs_process --
324 * Fork and exec the specified fcgi process.
327 * 0 for successful fork, -1 for failed fork.
329 * In case the child fails before or in the exec, the child
330 * obtains the error log by calling getErrLog, logs
331 * the error, and exits with exit status = errno of
332 * the failed system call.
335 * Child process created.
337 *----------------------------------------------------------------------
339 static pid_t
spawn_fs_process(fcgi_server
*fs
, ServerProcess
*process
)
346 char *dnEnd
, *failedSysCall
;
353 /* We're the child. We're gonna exec() so pools don't matter. */
355 dnEnd
= strrchr(fs
->fs_path
, '/');
359 dirName
= ap_pcalloc(fcgi_config_pool
, dnEnd
- fs
->fs_path
+ 1);
360 dirName
= memcpy(dirName
, fs
->fs_path
, dnEnd
- fs
->fs_path
);
362 if (chdir(dirName
) < 0) {
363 failedSysCall
= "chdir()";
364 goto FailedSystemCallExit
;
368 /* OS/2 dosen't support nice() */
369 if (fs
->processPriority
!= 0) {
370 if (nice(fs
->processPriority
) == -1) {
371 failedSysCall
= "nice()";
372 goto FailedSystemCallExit
;
377 /* Open the listenFd on spec'd fd */
378 if (fs
->listenFd
!= FCGI_LISTENSOCK_FILENO
)
379 dup2(fs
->listenFd
, FCGI_LISTENSOCK_FILENO
);
381 /* Close all other open fds, except stdout/stderr. Leave these two open so
382 * FastCGI applications don't have to find and fix ALL 3rd party libs that
383 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
384 * main server error_log - @@@ provide a directive control where this goes.
386 ap_error_log2stderr(fcgi_apache_main_server
);
388 for (i
= 0; i
< FCGI_MAX_FD
; i
++) {
389 if (i
!= FCGI_LISTENSOCK_FILENO
&& i
!= 2 && i
!= 1) {
394 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
395 * install its own handler. */
396 signal(SIGPIPE
, SIG_IGN
);
398 if (fcgi_wrapper
&& (fcgi_user_id
!= fs
->uid
|| fcgi_group_id
!= fs
->gid
)) {
399 char *shortName
= strrchr(fs
->fs_path
, '/') + 1;
401 /* Relinquish our root real uid powers */
406 execle(fcgi_wrapper
, fcgi_wrapper
, fs
->username
, fs
->group
, shortName
, NULL
, fs
->envp
);
407 } while (errno
== EINTR
);
411 execle(fs
->fs_path
, fs
->fs_path
, NULL
, fs
->envp
);
412 } while (errno
== EINTR
);
415 failedSysCall
= "execle()";
417 FailedSystemCallExit
:
418 fprintf(stderr
, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
419 fs
->fs_path
, (long) getpid(), failedSysCall
, strerror(errno
));
422 /* avoid an irrelevant compiler warning */
429 /* based on mod_cgi.c:run_cgi_child() */
432 char * termination_env_string
;
433 HANDLE listen_handle
= INVALID_HANDLE_VALUE
;
434 apr_procattr_t
* procattr
;
435 apr_proc_t proc
= { 0 };
439 if (apr_pool_create(&tp
, fcgi_config_pool
))
442 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
443 if (process
->terminationEvent
== NULL
)
446 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
448 termination_env_string
= ap_psprintf(tp
,
449 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
451 while (fs
->envp
[i
]) i
++;
452 fs
->envp
[i
++] = termination_env_string
;
453 fs
->envp
[i
] = (char *) fs
->mutex_env_string
;
455 ap_assert(fs
->envp
[i
+ 1] == NULL
);
459 SECURITY_ATTRIBUTES sa
= { 0 };
461 sa
.bInheritHandle
= TRUE
;
462 sa
.nLength
= sizeof(sa
);
464 listen_handle
= CreateNamedPipe(fs
->socket_path
,
466 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
467 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
469 if (listen_handle
== INVALID_HANDLE_VALUE
)
471 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
472 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
479 listen_handle
= (HANDLE
) fs
->listenFd
;
482 if (apr_procattr_create(&procattr
, tp
))
485 if (apr_procattr_dir_set(procattr
, ap_make_dirstr_parent(tp
, fs
->fs_path
)))
488 if (apr_procattr_detach_set(procattr
, 1))
491 if (apr_os_file_put(&file
, &listen_handle
, 0, tp
))
494 /* procattr is opaque so we have to use this - unfortuantely it dups */
495 if (apr_procattr_child_in_set(procattr
, file
, NULL
))
498 if (apr_proc_create(&proc
, fs
->fs_path
, NULL
, fs
->envp
, procattr
, tp
))
501 process
->handle
= proc
.hproc
;
508 fs
->envp
[i
- 1] = NULL
;
515 #else /* WIN32 && !APACHE2 */
517 /* Adapted from Apache's util_script.c ap_call_exec() */
518 char *interpreter
= NULL
;
519 char *quoted_filename
;
521 char *pEnvBlock
, *pNext
;
524 int iEnvBlockLen
= 1;
526 file_type_e fileType
;
529 PROCESS_INFORMATION pi
;
534 pool
* tp
= ap_make_sub_pool(fcgi_config_pool
);
536 HANDLE listen_handle
;
537 char * termination_env_string
= NULL
;
539 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
540 if (process
->terminationEvent
== NULL
)
542 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
543 "FastCGI: can't create termination event for server \"%s\", "
544 "CreateEvent() failed", fs
->fs_path
);
547 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
549 termination_env_string
= ap_psprintf(tp
,
550 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
554 SECURITY_ATTRIBUTES sa
;
556 sa
.lpSecurityDescriptor
= NULL
;
557 sa
.bInheritHandle
= TRUE
;
558 sa
.nLength
= sizeof(sa
);
560 listen_handle
= CreateNamedPipe(fs
->socket_path
,
562 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
563 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
565 if (listen_handle
== INVALID_HANDLE_VALUE
)
567 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
568 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs
->fs_path
);
574 listen_handle
= (HANDLE
) fs
->listenFd
;
577 memset(&si
, 0, sizeof(si
));
578 memset(&pi
, 0, sizeof(pi
));
579 memset(&r
, 0, sizeof(r
));
581 // Can up a fake request to pass to ap_get_win32_interpreter()
582 r
.per_dir_config
= fcgi_apache_main_server
->lookup_defaults
;
583 r
.server
= fcgi_apache_main_server
;
584 r
.filename
= (char *) fs
->fs_path
;
587 fileType
= ap_get_win32_interpreter(&r
, &interpreter
);
589 if (fileType
== eFileTypeUNKNOWN
) {
590 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
591 "FastCGI: %s is not executable; ensure interpreted scripts have "
592 "\"#!\" as their first line",
599 * We have the interpreter (if there is one) and we have
600 * the arguments (if there are any).
601 * Build the command string to pass to CreateProcess.
603 quoted_filename
= ap_pstrcat(tp
, "\"", fs
->fs_path
, "\"", NULL
);
604 if (interpreter
&& *interpreter
) {
605 pCommand
= ap_pstrcat(tp
, interpreter
, " ", quoted_filename
, NULL
);
608 pCommand
= quoted_filename
;
612 * Make child process use hPipeOutputWrite as standard out,
613 * and make sure it does not show on screen.
616 si
.dwFlags
= STARTF_USESHOWWINDOW
| STARTF_USESTDHANDLES
;
617 si
.wShowWindow
= SW_HIDE
;
618 si
.hStdInput
= listen_handle
;
620 // XXX These should be open to the error_log
621 si
.hStdOutput
= INVALID_HANDLE_VALUE
;
622 si
.hStdError
= INVALID_HANDLE_VALUE
;
625 * Win32's CreateProcess call requires that the environment
626 * be passed in an environment block, a null terminated block of
627 * null terminated strings.
628 * @todo we should store the env in this format for win32.
632 iEnvBlockLen
+= strlen(fs
->envp
[i
]) + 1;
636 iEnvBlockLen
+= strlen(termination_env_string
) + 1;
637 iEnvBlockLen
+= strlen(fs
->mutex_env_string
) + 1;
639 pEnvBlock
= (char *) ap_pcalloc(tp
, iEnvBlockLen
);
645 strcpy(pNext
, fs
->envp
[i
]);
646 pNext
+= strlen(pNext
) + 1;
650 strcpy(pNext
, termination_env_string
);
651 pNext
+= strlen(pNext
) + 1;
652 strcpy(pNext
, fs
->mutex_env_string
);
654 if (CreateProcess(NULL
, pCommand
, NULL
, NULL
, TRUE
,
657 ap_make_dirstr_parent(tp
, fs
->fs_path
),
660 /* Hack to get 16-bit CGI's working. It works for all the
661 * standard modules shipped with Apache. pi.dwProcessId is 0
662 * for 16-bit CGIs and all the Unix specific code that calls
663 * ap_call_exec interprets this as a failure case. And we can't
664 * use -1 either because it is mapped to 0 by the caller.
666 pid
= (fileType
== eFileTypeEXE16
) ? -2 : pi
.dwProcessId
;
668 process
->handle
= pi
.hProcess
;
669 CloseHandle(pi
.hThread
);
674 CloseHandle(listen_handle
);
683 #endif /* !APACHE2 */
688 static void reduce_privileges(void)
696 /* Get username if passed as a uid */
697 if (ap_user_name
[0] == '#') {
698 uid_t uid
= atoi(&ap_user_name
[1]);
699 struct passwd
*ent
= getpwuid(uid
);
702 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
703 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
704 "you probably need to modify the User directive", (unsigned)uid
);
713 if (setgid(ap_group_id
) == -1) {
714 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
715 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id
);
719 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
721 /* Initialize supplementary groups */
722 if (initgroups(name
, ap_group_id
) == -1) {
723 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
724 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
725 name
, (unsigned)ap_group_id
);
732 if (seteuid_user() == -1) {
733 ap_log_error(FCGI_LOG_ALERT_NOERRNO
, fcgi_apache_main_server
,
734 "FastCGI: process manager exiting, failed to reduce privileges");
739 if (setuid(ap_user_id
) == -1) {
740 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
741 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id
);
748 * Change the name of this process - best we can easily.
750 static void change_process_name(const char * const name
)
752 /* under Apache2, ap_server_argv0 is const */
753 strncpy((char *) ap_server_argv0
, name
, strlen(ap_server_argv0
));
757 static void schedule_start(fcgi_server
*s
, int proc
)
759 /* If we've started one recently, don't register another */
760 time_t time_passed
= now
- s
->restartTime
;
762 if ((s
->procs
[proc
].pid
&& (time_passed
< (int) s
->restartDelay
))
763 || ((s
->procs
[proc
].pid
== 0) && (time_passed
< s
->initStartDelay
)))
765 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
);
769 FCGIDBG3("scheduling_start: %s (%d)", s
->fs_path
, proc
);
770 s
->procs
[proc
].state
= FCGI_START_STATE
;
771 if (proc
== dynamicMaxClassProcs
- 1) {
772 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
773 "FastCGI: scheduled the %sstart of the last (dynamic) server "
774 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
775 s
->procs
[proc
].pid
? "re" : "", s
->fs_path
, dynamicMaxClassProcs
);
780 *----------------------------------------------------------------------
784 * Removes the records written by request handlers and decodes them.
785 * We also update the data structures to reflect the changes.
787 *----------------------------------------------------------------------
790 static void dynamic_read_msgs(int read_ready
)
796 static int buflen
= 0;
797 static char buf
[FCGI_MSGS_BUFSIZE
+ 1];
798 char *ptr1
, *ptr2
, opcode
;
799 char execName
[FCGI_MAXPATH
+ 1];
800 char user
[MAX_USER_NAME_LEN
+ 2];
801 char group
[MAX_GID_CHAR_LEN
+ 1];
802 unsigned long q_usec
= 0UL, req_usec
= 0UL;
804 fcgi_pm_job
*joblist
= NULL
;
805 fcgi_pm_job
*cjob
= NULL
;
808 pool
*sp
= NULL
, *tp
;
811 user
[MAX_USER_NAME_LEN
+ 1] = group
[MAX_GID_CHAR_LEN
] = '\0';
815 * To prevent the idle application from running indefinitely, we
816 * check the timer and if it is expired, we recompute the values
817 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
818 * message is received, only updates are made to the data structures.
820 if (fcgi_dynamic_last_analyzed
== 0) {
821 fcgi_dynamic_last_analyzed
= now
;
823 if ((now
- fcgi_dynamic_last_analyzed
) >= (int)dynamicUpdateInterval
) {
824 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
825 if (s
->directive
!= APP_CLASS_DYNAMIC
)
828 /* Advance the last analyzed timestamp by the elapsed time since
829 * it was last set. Round the increase down to the nearest
830 * multiple of dynamicUpdateInterval */
832 fcgi_dynamic_last_analyzed
+= (((long)(now
-fcgi_dynamic_last_analyzed
)/dynamicUpdateInterval
)*dynamicUpdateInterval
);
833 s
->smoothConnTime
= (unsigned long) ((1.0-dynamicGain
)*s
->smoothConnTime
+ dynamicGain
*s
->totalConnTime
);
834 s
->totalConnTime
= 0UL;
835 s
->totalQueueTime
= 0UL;
839 if (read_ready
<= 0) {
844 rc
= read(fcgi_pm_pipe
[0], (void *)(buf
+ buflen
), FCGI_MSGS_BUFSIZE
- buflen
);
846 if (!caughtSigTerm
) {
847 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
848 "FastCGI: read() from pipe failed (%d)", rc
);
850 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
851 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
852 caughtSigTerm
= TRUE
;
862 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
863 * request to do something) and/or when a timeout expires.
864 * There really should be no reason why this wait would get stuck
865 * but there's no point in waiting forever. */
867 rc
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
869 if (rc
!= WAIT_OBJECT_0
&& rc
!= WAIT_ABANDONED
)
871 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
872 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
876 joblist
= fcgi_dynamic_mbox
;
877 fcgi_dynamic_mbox
= NULL
;
879 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
881 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
882 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
889 apr_pool_create(&tp
, fcgi_config_pool
);
891 tp
= ap_make_sub_pool(fcgi_config_pool
);
895 for (ptr1
= buf
; ptr1
; ptr1
= ptr2
) {
898 ptr2
= strchr(ptr1
, '*');
910 case FCGI_SERVER_START_JOB
:
911 case FCGI_SERVER_RESTART_JOB
:
913 if (sscanf(ptr1
, "%c %s %16s %15s",
914 &opcode
, execName
, user
, group
) != 4)
920 case FCGI_REQUEST_TIMEOUT_JOB
:
922 if (sscanf(ptr1
, "%c %s %16s %15s",
923 &opcode
, execName
, user
, group
) != 4)
929 case FCGI_REQUEST_COMPLETE_JOB
:
931 if (sscanf(ptr1
, "%c %s %16s %15s %lu %lu",
932 &opcode
, execName
, user
, group
, &q_usec
, &req_usec
) != 6)
944 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode
, execName
, user
, group
, q_usec
, req_usec
);
947 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
948 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1
);
952 /* Update data structures for processing */
953 while (cjob
!= NULL
) {
954 joblist
= cjob
->next
;
955 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob
->id
, cjob
->fs_path
, cjob
->user
, cjob
->group
, cjob
->qsec
, cjob
->start_time
);
959 s
= fcgi_util_fs_get(execName
, user
, group
);
961 s
= fcgi_util_fs_get(cjob
->fs_path
, cjob
->user
, cjob
->group
);
965 if (s
==NULL
&& opcode
!= FCGI_REQUEST_COMPLETE_JOB
)
967 if (s
==NULL
&& cjob
->id
!= FCGI_REQUEST_COMPLETE_JOB
)
972 HANDLE mutex
= CreateMutex(NULL
, FALSE
, cjob
->fs_path
);
976 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
977 "FastCGI: can't create accept mutex "
978 "for (dynamic) server \"%s\"", cjob
->fs_path
);
982 SetHandleInformation(mutex
, HANDLE_FLAG_INHERIT
, TRUE
);
987 /* Create a perm subpool to hold the new server data,
988 * we can destroy it if something doesn't pan out */
990 apr_pool_create(&sp
, fcgi_config_pool
);
992 sp
= ap_make_sub_pool(fcgi_config_pool
);
995 /* Create a new "dynamic" server */
996 s
= fcgi_util_fs_new(sp
);
998 s
->directive
= APP_CLASS_DYNAMIC
;
999 s
->restartDelay
= dynamicRestartDelay
;
1000 s
->listenQueueDepth
= dynamicListenQueueDepth
;
1001 s
->initStartDelay
= dynamicInitStartDelay
;
1002 s
->envp
= dynamicEnvp
;
1003 s
->flush
= dynamicFlush
;
1006 s
->mutex_env_string
= ap_psprintf(sp
, "_FCGI_MUTEX_=%ld", mutex
);
1007 s
->fs_path
= ap_pstrdup(sp
, cjob
->fs_path
);
1009 s
->fs_path
= ap_pstrdup(sp
, execName
);
1011 ap_getparents(s
->fs_path
);
1012 ap_no2slash(s
->fs_path
);
1013 s
->procs
= fcgi_util_fs_create_procs(sp
, dynamicMaxClassProcs
);
1015 /* XXX the socket_path (both Unix and Win) *is* deducible and
1016 * thus can and will be used by other apache instances without
1017 * the use of shared data regarding the processes serving the
1018 * requests. This can result in slightly unintuitive process
1019 * counts and security implications. This is prevented
1020 * if suexec (Unix) is in use. This is both a feature and a flaw.
1021 * Changing it now would break existing installations. */
1024 /* Create socket file's path */
1025 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, execName
, user
, group
);
1026 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1028 /* Create sockaddr, prealloc it so it won't get created in tp */
1029 s
->socket_addr
= ap_pcalloc(sp
, sizeof(struct sockaddr_un
));
1030 err
= fcgi_util_socket_make_domain_addr(tp
, (struct sockaddr_un
**)&s
->socket_addr
,
1031 &s
->socket_addr_len
, s
->socket_path
);
1033 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1034 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
1038 if (init_listen_sock(s
)) {
1042 /* If a wrapper is being used, config user/group info */
1044 if (user
[0] == '~') {
1045 /* its a user dir uri, the rest is a username, not a uid */
1046 struct passwd
*pw
= getpwnam(&user
[1]);
1049 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1050 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1051 execName
, &user
[1]);
1054 s
->uid
= pw
->pw_uid
;
1055 s
->user
= ap_pstrdup(sp
, user
);
1056 s
->username
= s
->user
;
1058 s
->gid
= pw
->pw_gid
;
1059 s
->group
= ap_psprintf(sp
, "%ld", (long)s
->gid
);
1064 s
->uid
= (uid_t
)atol(user
);
1065 pw
= getpwuid(s
->uid
);
1067 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1068 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1069 execName
, (long)s
->uid
);
1072 s
->user
= ap_pstrdup(sp
, user
);
1073 s
->username
= ap_pstrdup(sp
, pw
->pw_name
);
1075 s
->gid
= (gid_t
)atol(group
);
1076 s
->group
= ap_pstrdup(sp
, group
);
1080 /* Create socket file's path */
1081 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, cjob
->fs_path
, cjob
->user
, cjob
->group
);
1082 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1086 fcgi_util_fs_add(s
);
1090 if (opcode
== FCGI_SERVER_RESTART_JOB
) {
1092 if (cjob
->id
==FCGI_SERVER_RESTART_JOB
) {
1094 /* Check to see if the binary has changed. If so,
1095 * kill the FCGI application processes, and
1101 char * app_path
= cjob
->fs_path
;
1103 char * app_path
= execName
;
1106 if (stat(app_path
, &stbuf
) == 0 && stbuf
.st_mtime
> s
->startTime
)
1110 /* prevent addition restart requests */
1113 utime(s
->socket_path
, NULL
);
1116 /* kill old server(s) */
1117 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1119 if (s
->procs
[i
].pid
> 0
1120 && stbuf
.st_mtime
> s
->procs
[i
].start_time
)
1122 fcgi_kill(&s
->procs
[i
], SIGTERM
);
1129 ap_log_error(FCGI_LOG_WARN_NOERRNO
,
1130 fcgi_apache_main_server
, "FastCGI: restarting "
1131 "old server \"%s\" processes, newer version "
1136 /* If dynamicAutoRestart, don't mark any new processes
1137 * for starting because we probably got the
1138 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1139 * will be restarting all of those we just killed.
1141 if (dynamicAutoRestart
)
1145 else if (opcode
== FCGI_SERVER_START_JOB
) {
1147 else if (cjob
->id
==FCGI_SERVER_START_JOB
) {
1149 /* we've been asked to start a process--only start
1150 * it if we're not already running at least one
1155 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1156 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1159 /* if already running, don't start another one */
1160 if (i
< dynamicMaxClassProcs
) {
1174 case FCGI_SERVER_RESTART_JOB
:
1178 /* We just waxed 'em all. Try to find an idle slot. */
1180 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1182 if (s
->procs
[i
].state
== FCGI_START_STATE
1183 || s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1187 else if (s
->procs
[i
].state
== FCGI_KILLED_STATE
1188 || s
->procs
[i
].state
== FCGI_READY_STATE
)
1195 /* Nope, just use the first slot */
1196 if (i
== dynamicMaxClassProcs
)
1204 schedule_start(s
, i
);
1209 case FCGI_SERVER_START_JOB
:
1210 case FCGI_REQUEST_TIMEOUT_JOB
:
1212 if ((fcgi_dynamic_total_proc_count
+ 1) > (int) dynamicMaxProcs
) {
1214 * Extra instances should have been
1215 * terminated beforehand, probably need
1216 * to increase ProcessSlack parameter
1218 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1219 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1220 "exceeded dynamicMaxProcs (%d)", s
->fs_path
, dynamicMaxProcs
);
1224 /* find next free slot */
1225 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1227 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1229 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i
);
1232 else if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1237 schedule_start(s
, i
);
1242 if (i
>= dynamicMaxClassProcs
) {
1243 FCGIDBG1("ignore_job: slots are max'd");
1247 case FCGI_REQUEST_COMPLETE_JOB
:
1248 /* only record stats if we have a structure */
1251 s
->totalConnTime
+= req_usec
;
1252 s
->totalQueueTime
+= q_usec
;
1254 s
->totalConnTime
+= cjob
->start_time
;
1255 s
->totalQueueTime
+= cjob
->qsec
;
1264 /* Cleanup job data */
1265 free(cjob
->fs_path
);
1275 if (sp
) ap_destroy_pool(sp
);
1278 free(cjob
->fs_path
);
1286 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1287 "FastCGI: really bogus message: \"%s\"", ptr1
);
1288 ptr1
+= strlen(buf
);
1291 buflen
-= ptr1
- buf
;
1293 memmove(buf
, ptr1
, buflen
);
1297 ap_destroy_pool(tp
);
1301 *----------------------------------------------------------------------
1303 * dynamic_kill_idle_fs_procs
1305 * Implement a kill policy for the dynamic FastCGI applications.
1306 * We also update the data structures to reflect the changes.
1309 * Processes are marked for deletion possibly killed.
1311 *----------------------------------------------------------------------
1313 static void dynamic_kill_idle_fs_procs(void)
1318 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1321 * server's smoothed running time, or if that's 0, the current total
1323 unsigned long connTime
;
1326 * maximum number of microseconds that all of a server's running
1327 * processes together could have spent running since the last check
1329 unsigned long totalTime
;
1332 * percentage, 0-100, of totalTime that the processes actually used
1337 int really_running
= 0;
1339 if (s
->directive
!= APP_CLASS_DYNAMIC
|| s
->numProcesses
== 0)
1344 /* s->numProcesses includes pending kills so get the "active" count */
1345 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1347 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
) ++really_running
;
1350 connTime
= s
->smoothConnTime
? s
->smoothConnTime
: s
->totalConnTime
;
1351 totalTime
= really_running
* (now
- fcgi_dynamic_epoch
) * 1000000 + 1;
1353 loadFactor
= 100 * connTime
/ totalTime
;
1355 if (really_running
== 1)
1357 if (loadFactor
>= dynamicThreshold1
)
1364 int load
= really_running
/ ( really_running
- 1) * loadFactor
;
1366 if (load
>= dynamicThresholdN
)
1373 * Run through the procs to see if we can get away w/o waxing one.
1375 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1377 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1379 s
->procs
[i
].state
= FCGI_READY_STATE
;
1382 else if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
)
1388 if (i
>= dynamicMaxClassProcs
)
1390 ServerProcess
* procs
= s
->procs
;
1393 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1395 if (procs
[i
].state
== FCGI_RUNNING_STATE
)
1397 if (youngest
== -1 || procs
[i
].start_time
>= procs
[youngest
].start_time
)
1406 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1407 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1408 s
->fs_path
, (long) s
->procs
[youngest
].pid
);
1410 fcgi_kill(&s
->procs
[youngest
], SIGTERM
);
1416 * If the number of non-victims is less than or equal to
1417 * the minimum that may be running without being killed off,
1418 * don't select any more victims.
1420 if (fcgi_dynamic_total_proc_count
- victims
<= dynamicMinProcs
)
1430 // This is a little bogus, there's gotta be a better way to do this
1431 // Can we use WaitForMultipleObjects()
1432 #define FCGI_PROC_WAIT_TIME 100
1434 void child_wait_thread_main(void *dummy
) {
1436 DWORD dwRet
= WAIT_TIMEOUT
;
1441 while (!bTimeToDie
) {
1444 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1445 if (s
->directive
== APP_CLASS_EXTERNAL
|| s
->listenFd
< 0) {
1448 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1449 numChildren
= dynamicMaxClassProcs
;
1452 numChildren
= s
->numProcesses
;
1455 for (i
=0; i
< numChildren
; i
++) {
1456 if (s
->procs
[i
].handle
!= INVALID_HANDLE_VALUE
)
1458 DWORD exitStatus
= 0;
1460 /* timeout is currently set for 100 miliecond */
1461 /* it may need to be longer or user customizable */
1462 dwRet
= WaitForSingleObject(s
->procs
[i
].handle
, FCGI_PROC_WAIT_TIME
);
1466 if (dwRet
!= WAIT_TIMEOUT
&& dwRet
!= WAIT_FAILED
) {
1467 /* a child fs has died */
1468 /* mark the child as dead */
1470 if (s
->directive
== APP_CLASS_STANDARD
) {
1471 /* restart static app */
1472 s
->procs
[i
].state
= FCGI_START_STATE
;
1477 fcgi_dynamic_total_proc_count
--;
1478 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1480 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1481 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1484 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1487 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0)) {
1488 s
->procs
[i
].state
= FCGI_START_STATE
;
1491 s
->procs
[i
].state
= FCGI_READY_STATE
;
1496 GetExitCodeProcess(s
->procs
[i
].handle
, &exitStatus
);
1498 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1499 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1500 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1501 s
->fs_path
, (long) s
->procs
[i
].pid
, exitStatus
);
1503 CloseHandle(s
->procs
[i
].handle
);
1504 s
->procs
[i
].handle
= INVALID_HANDLE_VALUE
;
1505 s
->procs
[i
].pid
= -1;
1507 /* wake up the main thread */
1508 SetEvent(fcgi_event_handles
[WAKE_EVENT
]);
1513 Sleep(waited
? 0 : FCGI_PROC_WAIT_TIME
);
1519 static void setup_signals(void)
1521 struct sigaction sa
;
1523 /* Setup handlers */
1525 sa
.sa_handler
= signal_handler
;
1526 sigemptyset(&sa
.sa_mask
);
1529 if (sigaction(SIGTERM
, &sa
, NULL
) < 0) {
1530 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1531 "sigaction(SIGTERM) failed");
1534 if (sigaction(SIGHUP
, &sa
, NULL
) < 0) {
1535 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1536 "sigaction(SIGHUP) failed");
1538 /* httpd graceful restart */
1539 if (sigaction(SIGUSR1
, &sa
, NULL
) < 0) {
1540 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1541 "sigaction(SIGUSR1) failed");
1543 /* read messages from request handlers - kill interval expired */
1544 if (sigaction(SIGALRM
, &sa
, NULL
) < 0) {
1545 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1546 "sigaction(SIGALRM) failed");
1548 if (sigaction(SIGCHLD
, &sa
, NULL
) < 0) {
1549 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1550 "sigaction(SIGCHLD) failed");
1555 #if !defined(WIN32) && !defined(APACHE2)
1556 int fcgi_pm_main(void *dummy
, child_info
*info
)
1558 void fcgi_pm_main(void *dummy
)
1568 HANDLE child_wait_thread
= INVALID_HANDLE_VALUE
;
1570 int callWaitPid
, callDynamicProcs
;
1574 // Add SystemRoot to the dynamic environment
1575 char ** envp
= dynamicEnvp
;
1576 for (i
= 0; *envp
; ++i
) {
1579 fcgi_config_set_env_var(fcgi_config_pool
, dynamicEnvp
, &i
, "SystemRoot");
1583 reduce_privileges();
1584 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
)
1690 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1691 "FastCGI:%s server \"%s\" has remained"
1692 " running for more than %d seconds, its restart"
1693 " interval has been restored to %d seconds",
1694 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1695 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, s
->restartDelay
);
1701 for (j
= 0; j
< numChildren
; ++j
)
1703 if (s
->procs
[j
].pid
<= 0) continue;
1704 if (s
->procs
[j
].state
!= FCGI_RUNNING_STATE
) continue;
1705 if (s
->procs
[j
].start_time
== 0) continue;
1706 if (now
- s
->procs
[j
].start_time
> RUNTIME_SUCCESS_INTERVAL
) break;
1709 if (j
>= numChildren
)
1712 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1713 "FastCGI:%s server \"%s\" has failed to remain"
1714 " running for %d seconds given %d attempts, its restart"
1715 " interval has been backed off to %d seconds",
1716 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1717 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, MAX_FAILED_STARTS
,
1718 FAILED_STARTS_DELAY
);
1724 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1725 "FastCGI:%s server \"%s\" has remained"
1726 " running for more than %d seconds, its restart"
1727 " interval has been restored to %d seconds",
1728 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1729 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, s
->restartDelay
);
1736 restartTime
+= FAILED_STARTS_DELAY
;
1740 restartTime
+= (restart
) ? s
->restartDelay
: s
->initStartDelay
;
1743 if (restartTime
<= now
)
1748 s
->numFailures
= MAX_FAILED_STARTS
;
1751 if (s
->listenFd
< 0 && init_listen_sock(s
))
1753 if (sleepSeconds
> s
->initStartDelay
)
1754 sleepSeconds
= s
->initStartDelay
;
1758 if (caughtSigTerm
) {
1759 goto ProcessSigTerm
;
1762 s
->procs
[i
].pid
= spawn_fs_process(s
, &s
->procs
[i
]);
1763 if (s
->procs
[i
].pid
<= 0) {
1764 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1765 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1766 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1769 sleepSeconds
= min(sleepSeconds
,
1770 max((int) s
->restartDelay
, FCGI_MIN_EXEC_RETRY_DELAY
));
1772 ap_assert(s
->procs
[i
].pid
< 0);
1776 s
->procs
[i
].start_time
= now
;
1777 s
->restartTime
= now
;
1779 if (s
->startTime
== 0) {
1783 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1785 fcgi_dynamic_total_proc_count
++;
1786 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1789 s
->procs
[i
].state
= FCGI_RUNNING_STATE
;
1792 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1793 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1794 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1795 s
->fs_path
, (long) s
->uid
, (long) s
->gid
,
1796 restart
? "re" : "", (long) s
->procs
[i
].pid
);
1799 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1800 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1801 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1802 s
->fs_path
, restart
? "re" : "", (long) s
->procs
[i
].pid
);
1804 ap_assert(s
->procs
[i
].pid
> 0);
1806 sleepSeconds
= min(sleepSeconds
, restartTime
- now
);
1815 goto ProcessSigTerm
;
1817 if((!caughtSigChld
) && (!caughtSigAlarm
)) {
1820 alarm(sleepSeconds
);
1823 FD_SET(fcgi_pm_pipe
[0], &rfds
);
1824 read_ready
= ap_select(fcgi_pm_pipe
[0] + 1, &rfds
, NULL
, NULL
, NULL
);
1826 alarmLeft
= alarm(0);
1828 callWaitPid
= caughtSigChld
;
1829 caughtSigChld
= FALSE
;
1830 callDynamicProcs
= caughtSigAlarm
;
1831 caughtSigAlarm
= FALSE
;
1836 * Dynamic fcgi process management
1838 if((callDynamicProcs
) || (!callWaitPid
)) {
1839 dynamic_read_msgs(read_ready
);
1840 if(fcgi_dynamic_epoch
== 0) {
1841 fcgi_dynamic_epoch
= now
;
1843 if(((long)(now
-fcgi_dynamic_epoch
)>=dynamicKillInterval
) ||
1844 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
)>=dynamicMaxProcs
)) {
1845 dynamic_kill_idle_fs_procs();
1846 fcgi_dynamic_epoch
= now
;
1854 /* We've caught SIGCHLD, so find out who it was using waitpid,
1855 * write a log message and update its data structure. */
1859 goto ProcessSigTerm
;
1861 childPid
= waitpid(-1, &waitStatus
, WNOHANG
);
1863 if (childPid
== -1 || childPid
== 0)
1866 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1867 if (s
->directive
== APP_CLASS_EXTERNAL
)
1870 if (s
->directive
== APP_CLASS_DYNAMIC
)
1871 numChildren
= dynamicMaxClassProcs
;
1873 numChildren
= s
->numProcesses
;
1875 for (i
= 0; i
< numChildren
; i
++) {
1876 if (s
->procs
[i
].pid
== childPid
)
1881 /* TODO: print something about this unknown child */
1885 s
->procs
[i
].pid
= -1;
1887 if (s
->directive
== APP_CLASS_STANDARD
) {
1888 /* Always restart static apps */
1889 s
->procs
[i
].state
= FCGI_START_STATE
;
1894 fcgi_dynamic_total_proc_count
--;
1896 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1897 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1900 /* A dynamic app died or exited without provocation from the PM */
1903 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0))
1904 s
->procs
[i
].state
= FCGI_START_STATE
;
1906 s
->procs
[i
].state
= FCGI_READY_STATE
;
1910 if (WIFEXITED(waitStatus
)) {
1911 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1912 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1913 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1914 s
->fs_path
, (long) childPid
, WEXITSTATUS(waitStatus
));
1916 else if (WIFSIGNALED(waitStatus
)) {
1917 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1918 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1919 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1920 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
),
1922 WCOREDUMP(waitStatus
) ? ", a core file may have been generated" : "");
1927 else if (WIFSTOPPED(waitStatus
)) {
1928 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1929 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1930 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1931 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
));
1933 } /* for (;;), waitpid() */
1937 /* wait for an event to occur or timer expires */
1938 expire
= time(NULL
) + sleepSeconds
;
1939 dwRet
= WaitForMultipleObjects(3, (HANDLE
*) fcgi_event_handles
, FALSE
, sleepSeconds
* 1000);
1941 if (dwRet
== WAIT_FAILED
) {
1942 /* There is something seriously wrong here */
1943 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1944 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1948 if (dwRet
!= WAIT_TIMEOUT
) {
1952 alarmLeft
= expire
- now
;
1956 * Dynamic fcgi process management
1958 if ((dwRet
== MBOX_EVENT
) || (dwRet
== WAIT_TIMEOUT
)) {
1959 if (dwRet
== MBOX_EVENT
) {
1965 dynamic_read_msgs(read_ready
);
1967 if(fcgi_dynamic_epoch
== 0) {
1968 fcgi_dynamic_epoch
= now
;
1971 if ((now
-fcgi_dynamic_epoch
>= (int) dynamicKillInterval
) ||
1972 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
) >= dynamicMaxProcs
)) {
1973 dynamic_kill_idle_fs_procs();
1974 fcgi_dynamic_epoch
= now
;
1978 else if (dwRet
== WAKE_EVENT
) {
1981 else if (dwRet
== TERM_EVENT
) {
1982 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
1983 "FastCGI: Termination event received process manager shutting down");
1986 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
1988 goto ProcessSigTerm
;
1991 // Have an received an unknown event - should not happen
1992 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1993 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1996 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
1998 goto ProcessSigTerm
;
2003 } /* for (;;), the whole shoot'n match */
2007 * Kill off the children, then exit.
2019 int fcgi_pm_add_job(fcgi_pm_job
*new_job
)
2021 int rv
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
2023 if (rv
!= WAIT_OBJECT_0
&& rv
!= WAIT_ABANDONED
)
2025 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2026 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2030 new_job
->next
= fcgi_dynamic_mbox
;
2031 fcgi_dynamic_mbox
= new_job
;
2033 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
2035 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2036 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");