2 * $Id: fcgi_pm.c,v 1.82 2002/10/22 02:37:32 robs Exp $
8 #if defined(APACHE2) && !defined(WIN32)
13 #include "apr_signal.h"
18 #define seteuid(arg) setresuid(-1, (arg), -1)
21 int fcgi_dynamic_total_proc_count
= 0; /* number of running apps */
22 time_t fcgi_dynamic_epoch
= 0; /* last time kill_procs was
23 * invoked by process mgr */
24 time_t fcgi_dynamic_last_analyzed
= 0; /* last time calculation was
25 * made for the dynamic procs */
27 static time_t now
= 0;
30 #pragma warning ( disable : 4100 4102 )
31 static BOOL bTimeToDie
= FALSE
; /* process termination flag */
32 HANDLE fcgi_event_handles
[3];
40 static int seteuid_root(void)
42 int rc
= seteuid(getuid());
44 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
45 "FastCGI: seteuid(0) failed");
50 static int seteuid_user(void)
52 int rc
= seteuid(ap_user_id
);
54 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
55 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id
);
62 * Signal the process to exit. How (or if) the process responds
63 * depends on the FastCGI application library (esp. on Win32) and
64 * possibly application code (signal handlers and whether or not
65 * SA_RESTART is on). At any rate, we send the signal with the
66 * hopes that the process will exit on its own. Later, as we
67 * review the state of application processes, if we see one marked
68 * for death, but that hasn't died within a specified period of
69 * time, fcgi_kill() is called again with a KILL)
71 static void fcgi_kill(ServerProcess
*process
, int sig
)
73 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process
->pid
, sig
);
75 process
->state
= FCGI_VICTIM_STATE
;
81 SetEvent(process
->terminationEvent
);
83 else if (sig
== SIGKILL
)
85 TerminateProcess(process
->handle
, 1);
99 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) " : "",
265 closesocket(fs
->listenFd
);
276 *----------------------------------------------------------------------
280 * The FastCGI process manager, which runs as a separate
281 * process responsible for:
282 * - Starting all the FastCGI proceses.
283 * - Restarting any of these processes that die (indicated
285 * - Catching SIGTERM and relaying it to all the FastCGI
286 * processes before exiting.
289 * Uses global variable fcgi_servers.
297 *----------------------------------------------------------------------
300 static int caughtSigTerm
= FALSE
;
301 static int caughtSigChld
= FALSE
;
302 static int caughtSigAlarm
= FALSE
;
304 static void signal_handler(int signo
)
306 if ((signo
== SIGTERM
) || (signo
== SIGUSR1
) || (signo
== SIGHUP
)) {
307 /* SIGUSR1 & SIGHUP are sent by apache to its process group
308 * when apache get 'em. Apache follows up (1.2.x) with attacks
309 * on each of its child processes, but we've got the KillMgr
310 * sitting between us so we never see the KILL. The main loop
311 * in ProcMgr also checks to see if the KillMgr has terminated,
312 * and if it has, we handl it as if we should shutdown too. */
313 caughtSigTerm
= TRUE
;
314 } else if(signo
== SIGCHLD
) {
315 caughtSigChld
= TRUE
;
316 } else if(signo
== SIGALRM
) {
317 caughtSigAlarm
= TRUE
;
323 *----------------------------------------------------------------------
325 * spawn_fs_process --
327 * Fork and exec the specified fcgi process.
330 * 0 for successful fork, -1 for failed fork.
332 * In case the child fails before or in the exec, the child
333 * obtains the error log by calling getErrLog, logs
334 * the error, and exits with exit status = errno of
335 * the failed system call.
338 * Child process created.
340 *----------------------------------------------------------------------
342 static pid_t
spawn_fs_process(fcgi_server
*fs
, ServerProcess
*process
)
349 char *dnEnd
, *failedSysCall
;
356 /* We're the child. We're gonna exec() so pools don't matter. */
358 dnEnd
= strrchr(fs
->fs_path
, '/');
362 dirName
= ap_pcalloc(fcgi_config_pool
, dnEnd
- fs
->fs_path
+ 1);
363 dirName
= memcpy(dirName
, fs
->fs_path
, dnEnd
- fs
->fs_path
);
365 if (chdir(dirName
) < 0) {
366 failedSysCall
= "chdir()";
367 goto FailedSystemCallExit
;
371 /* OS/2 dosen't support nice() */
372 if (fs
->processPriority
!= 0) {
373 if (nice(fs
->processPriority
) == -1) {
374 failedSysCall
= "nice()";
375 goto FailedSystemCallExit
;
380 /* Open the listenFd on spec'd fd */
381 if (fs
->listenFd
!= FCGI_LISTENSOCK_FILENO
)
382 dup2(fs
->listenFd
, FCGI_LISTENSOCK_FILENO
);
384 /* Close all other open fds, except stdout/stderr. Leave these two open so
385 * FastCGI applications don't have to find and fix ALL 3rd party libs that
386 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
387 * main server error_log - @@@ provide a directive control where this goes.
389 ap_error_log2stderr(fcgi_apache_main_server
);
391 for (i
= 0; i
< FCGI_MAX_FD
; i
++) {
392 if (i
!= FCGI_LISTENSOCK_FILENO
&& i
!= 2 && i
!= 1) {
397 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
398 * install its own handler. */
399 signal(SIGPIPE
, SIG_IGN
);
401 if (fcgi_wrapper
&& (fcgi_user_id
!= fs
->uid
|| fcgi_group_id
!= fs
->gid
)) {
402 char *shortName
= strrchr(fs
->fs_path
, '/') + 1;
404 /* Relinquish our root real uid powers */
409 execle(fcgi_wrapper
, fcgi_wrapper
, fs
->username
, fs
->group
, shortName
, NULL
, fs
->envp
);
410 } while (errno
== EINTR
);
414 execle(fs
->fs_path
, fs
->fs_path
, NULL
, fs
->envp
);
415 } while (errno
== EINTR
);
418 failedSysCall
= "execle()";
420 FailedSystemCallExit
:
421 fprintf(stderr
, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
422 fs
->fs_path
, (long) getpid(), failedSysCall
, strerror(errno
));
425 /* avoid an irrelevant compiler warning */
432 /* based on mod_cgi.c:run_cgi_child() */
435 char * termination_env_string
;
436 HANDLE listen_handle
= INVALID_HANDLE_VALUE
;
437 apr_procattr_t
* procattr
;
438 apr_proc_t proc
= { 0 };
442 if (apr_pool_create(&tp
, fcgi_config_pool
))
445 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
446 if (process
->terminationEvent
== NULL
)
449 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
451 termination_env_string
= ap_psprintf(tp
,
452 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
454 while (fs
->envp
[i
]) i
++;
455 fs
->envp
[i
++] = termination_env_string
;
456 fs
->envp
[i
] = (char *) fs
->mutex_env_string
;
458 ap_assert(fs
->envp
[i
+ 1] == NULL
);
462 SECURITY_ATTRIBUTES sa
= { 0 };
464 sa
.bInheritHandle
= TRUE
;
465 sa
.nLength
= sizeof(sa
);
467 listen_handle
= CreateNamedPipe(fs
->socket_path
,
469 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
470 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
472 if (listen_handle
== INVALID_HANDLE_VALUE
)
474 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
475 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
482 listen_handle
= (HANDLE
) fs
->listenFd
;
485 if (apr_procattr_create(&procattr
, tp
))
488 if (apr_procattr_dir_set(procattr
, ap_make_dirstr_parent(tp
, fs
->fs_path
)))
491 if (apr_procattr_detach_set(procattr
, 1))
494 if (apr_os_file_put(&file
, &listen_handle
, 0, tp
))
497 /* procattr is opaque so we have to use this - unfortuantely it dups */
498 if (apr_procattr_child_in_set(procattr
, file
, NULL
))
501 if (apr_proc_create(&proc
, fs
->fs_path
, NULL
, fs
->envp
, procattr
, tp
))
504 process
->handle
= proc
.hproc
;
511 fs
->envp
[i
- 1] = NULL
;
518 #else /* WIN32 && !APACHE2 */
520 /* Adapted from Apache's util_script.c ap_call_exec() */
521 char *interpreter
= NULL
;
522 char *quoted_filename
;
524 char *pEnvBlock
, *pNext
;
527 int iEnvBlockLen
= 1;
529 file_type_e fileType
;
532 PROCESS_INFORMATION pi
;
537 pool
* tp
= ap_make_sub_pool(fcgi_config_pool
);
539 HANDLE listen_handle
;
540 char * termination_env_string
= NULL
;
542 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
543 if (process
->terminationEvent
== NULL
)
545 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
546 "FastCGI: can't create termination event for server \"%s\", "
547 "CreateEvent() failed", fs
->fs_path
);
550 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
552 termination_env_string
= ap_psprintf(tp
,
553 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
557 SECURITY_ATTRIBUTES sa
;
559 sa
.lpSecurityDescriptor
= NULL
;
560 sa
.bInheritHandle
= TRUE
;
561 sa
.nLength
= sizeof(sa
);
563 listen_handle
= CreateNamedPipe(fs
->socket_path
,
565 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
566 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
568 if (listen_handle
== INVALID_HANDLE_VALUE
)
570 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
571 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs
->fs_path
);
577 listen_handle
= (HANDLE
) fs
->listenFd
;
580 memset(&si
, 0, sizeof(si
));
581 memset(&pi
, 0, sizeof(pi
));
582 memset(&r
, 0, sizeof(r
));
584 // Can up a fake request to pass to ap_get_win32_interpreter()
585 r
.per_dir_config
= fcgi_apache_main_server
->lookup_defaults
;
586 r
.server
= fcgi_apache_main_server
;
587 r
.filename
= (char *) fs
->fs_path
;
590 fileType
= ap_get_win32_interpreter(&r
, &interpreter
);
592 if (fileType
== eFileTypeUNKNOWN
) {
593 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
594 "FastCGI: %s is not executable; ensure interpreted scripts have "
595 "\"#!\" as their first line",
602 * We have the interpreter (if there is one) and we have
603 * the arguments (if there are any).
604 * Build the command string to pass to CreateProcess.
606 quoted_filename
= ap_pstrcat(tp
, "\"", fs
->fs_path
, "\"", NULL
);
607 if (interpreter
&& *interpreter
) {
608 pCommand
= ap_pstrcat(tp
, interpreter
, " ", quoted_filename
, NULL
);
611 pCommand
= quoted_filename
;
615 * Make child process use hPipeOutputWrite as standard out,
616 * and make sure it does not show on screen.
619 si
.dwFlags
= STARTF_USESHOWWINDOW
| STARTF_USESTDHANDLES
;
620 si
.wShowWindow
= SW_HIDE
;
621 si
.hStdInput
= listen_handle
;
623 // XXX These should be open to the error_log
624 si
.hStdOutput
= INVALID_HANDLE_VALUE
;
625 si
.hStdError
= INVALID_HANDLE_VALUE
;
628 * Win32's CreateProcess call requires that the environment
629 * be passed in an environment block, a null terminated block of
630 * null terminated strings.
631 * @todo we should store the env in this format for win32.
635 iEnvBlockLen
+= strlen(fs
->envp
[i
]) + 1;
639 iEnvBlockLen
+= strlen(termination_env_string
) + 1;
640 iEnvBlockLen
+= strlen(fs
->mutex_env_string
) + 1;
642 pEnvBlock
= (char *) ap_pcalloc(tp
, iEnvBlockLen
);
648 strcpy(pNext
, fs
->envp
[i
]);
649 pNext
+= strlen(pNext
) + 1;
653 strcpy(pNext
, termination_env_string
);
654 pNext
+= strlen(pNext
) + 1;
655 strcpy(pNext
, fs
->mutex_env_string
);
657 if (CreateProcess(NULL
, pCommand
, NULL
, NULL
, TRUE
,
660 ap_make_dirstr_parent(tp
, fs
->fs_path
),
663 /* Hack to get 16-bit CGI's working. It works for all the
664 * standard modules shipped with Apache. pi.dwProcessId is 0
665 * for 16-bit CGIs and all the Unix specific code that calls
666 * ap_call_exec interprets this as a failure case. And we can't
667 * use -1 either because it is mapped to 0 by the caller.
669 pid
= (fileType
== eFileTypeEXE16
) ? -2 : pi
.dwProcessId
;
671 process
->handle
= pi
.hProcess
;
672 CloseHandle(pi
.hThread
);
677 CloseHandle(listen_handle
);
686 #endif /* !APACHE2 */
691 static void reduce_privileges(void)
699 /* Get username if passed as a uid */
700 if (ap_user_name
[0] == '#') {
701 uid_t uid
= atoi(&ap_user_name
[1]);
702 struct passwd
*ent
= getpwuid(uid
);
705 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
706 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
707 "you probably need to modify the User directive", (unsigned)uid
);
716 if (setgid(ap_group_id
) == -1) {
717 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
718 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id
);
722 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
724 /* Initialize supplementary groups */
725 if (initgroups(name
, ap_group_id
) == -1) {
726 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
727 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
728 name
, (unsigned)ap_group_id
);
735 if (seteuid_user() == -1) {
736 ap_log_error(FCGI_LOG_ALERT_NOERRNO
, fcgi_apache_main_server
,
737 "FastCGI: process manager exiting, failed to reduce privileges");
742 if (setuid(ap_user_id
) == -1) {
743 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
744 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id
);
751 * Change the name of this process - best we can easily.
753 static void change_process_name(const char * const name
)
755 /* under Apache2, ap_server_argv0 is const */
756 strncpy((char *) ap_server_argv0
, name
, strlen(ap_server_argv0
));
760 static void schedule_start(fcgi_server
*s
, int proc
)
762 /* If we've started one recently, don't register another */
763 time_t time_passed
= now
- s
->restartTime
;
765 if ((s
->procs
[proc
].pid
&& (time_passed
< (int) s
->restartDelay
))
766 || ((s
->procs
[proc
].pid
== 0) && (time_passed
< s
->initStartDelay
)))
768 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
);
772 FCGIDBG3("scheduling_start: %s (%d)", s
->fs_path
, proc
);
773 s
->procs
[proc
].state
= FCGI_START_STATE
;
774 if (proc
== dynamicMaxClassProcs
- 1) {
775 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
776 "FastCGI: scheduled the %sstart of the last (dynamic) server "
777 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
778 s
->procs
[proc
].pid
? "re" : "", s
->fs_path
, dynamicMaxClassProcs
);
783 *----------------------------------------------------------------------
787 * Removes the records written by request handlers and decodes them.
788 * We also update the data structures to reflect the changes.
790 *----------------------------------------------------------------------
793 static void dynamic_read_msgs(int read_ready
)
799 static int buflen
= 0;
800 static char buf
[FCGI_MSGS_BUFSIZE
+ 1];
801 char *ptr1
, *ptr2
, opcode
;
802 char execName
[FCGI_MAXPATH
+ 1];
803 char user
[MAX_USER_NAME_LEN
+ 2];
804 char group
[MAX_GID_CHAR_LEN
+ 1];
805 unsigned long q_usec
= 0UL, req_usec
= 0UL;
807 fcgi_pm_job
*joblist
= NULL
;
808 fcgi_pm_job
*cjob
= NULL
;
811 pool
*sp
= NULL
, *tp
;
814 user
[MAX_USER_NAME_LEN
+ 1] = group
[MAX_GID_CHAR_LEN
] = '\0';
818 * To prevent the idle application from running indefinitely, we
819 * check the timer and if it is expired, we recompute the values
820 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
821 * message is received, only updates are made to the data structures.
823 if (fcgi_dynamic_last_analyzed
== 0) {
824 fcgi_dynamic_last_analyzed
= now
;
826 if ((now
- fcgi_dynamic_last_analyzed
) >= (int)dynamicUpdateInterval
) {
827 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
828 if (s
->directive
!= APP_CLASS_DYNAMIC
)
831 /* Advance the last analyzed timestamp by the elapsed time since
832 * it was last set. Round the increase down to the nearest
833 * multiple of dynamicUpdateInterval */
835 fcgi_dynamic_last_analyzed
+= (((long)(now
-fcgi_dynamic_last_analyzed
)/dynamicUpdateInterval
)*dynamicUpdateInterval
);
836 s
->smoothConnTime
= (unsigned long) ((1.0-dynamicGain
)*s
->smoothConnTime
+ dynamicGain
*s
->totalConnTime
);
837 s
->totalConnTime
= 0UL;
838 s
->totalQueueTime
= 0UL;
842 if (read_ready
<= 0) {
847 rc
= read(fcgi_pm_pipe
[0], (void *)(buf
+ buflen
), FCGI_MSGS_BUFSIZE
- buflen
);
849 if (!caughtSigTerm
) {
850 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
851 "FastCGI: read() from pipe failed (%d)", rc
);
853 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
854 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
855 caughtSigTerm
= TRUE
;
865 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
866 * request to do something) and/or when a timeout expires.
867 * There really should be no reason why this wait would get stuck
868 * but there's no point in waiting forever. */
870 rc
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
872 if (rc
!= WAIT_OBJECT_0
&& rc
!= WAIT_ABANDONED
)
874 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
875 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
879 joblist
= fcgi_dynamic_mbox
;
880 fcgi_dynamic_mbox
= NULL
;
882 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
884 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
885 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
892 apr_pool_create(&tp
, fcgi_config_pool
);
894 tp
= ap_make_sub_pool(fcgi_config_pool
);
898 for (ptr1
= buf
; ptr1
; ptr1
= ptr2
) {
901 ptr2
= strchr(ptr1
, '*');
913 case FCGI_SERVER_START_JOB
:
914 case FCGI_SERVER_RESTART_JOB
:
916 if (sscanf(ptr1
, "%c %s %16s %15s",
917 &opcode
, execName
, user
, group
) != 4)
923 case FCGI_REQUEST_TIMEOUT_JOB
:
925 if (sscanf(ptr1
, "%c %s %16s %15s",
926 &opcode
, execName
, user
, group
) != 4)
932 case FCGI_REQUEST_COMPLETE_JOB
:
934 if (sscanf(ptr1
, "%c %s %16s %15s %lu %lu",
935 &opcode
, execName
, user
, group
, &q_usec
, &req_usec
) != 6)
947 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode
, execName
, user
, group
, q_usec
, req_usec
);
950 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
951 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1
);
955 /* Update data structures for processing */
956 while (cjob
!= NULL
) {
957 joblist
= cjob
->next
;
958 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob
->id
, cjob
->fs_path
, cjob
->user
, cjob
->group
, cjob
->qsec
, cjob
->start_time
);
962 s
= fcgi_util_fs_get(execName
, user
, group
);
964 s
= fcgi_util_fs_get(cjob
->fs_path
, cjob
->user
, cjob
->group
);
968 if (s
==NULL
&& opcode
!= FCGI_REQUEST_COMPLETE_JOB
)
970 if (s
==NULL
&& cjob
->id
!= FCGI_REQUEST_COMPLETE_JOB
)
975 HANDLE mutex
= CreateMutex(NULL
, FALSE
, cjob
->fs_path
);
979 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
980 "FastCGI: can't create accept mutex "
981 "for (dynamic) server \"%s\"", cjob
->fs_path
);
985 SetHandleInformation(mutex
, HANDLE_FLAG_INHERIT
, TRUE
);
990 /* Create a perm subpool to hold the new server data,
991 * we can destroy it if something doesn't pan out */
993 apr_pool_create(&sp
, fcgi_config_pool
);
995 sp
= ap_make_sub_pool(fcgi_config_pool
);
998 /* Create a new "dynamic" server */
999 s
= fcgi_util_fs_new(sp
);
1001 s
->directive
= APP_CLASS_DYNAMIC
;
1002 s
->restartDelay
= dynamicRestartDelay
;
1003 s
->listenQueueDepth
= dynamicListenQueueDepth
;
1004 s
->initStartDelay
= dynamicInitStartDelay
;
1005 s
->envp
= dynamicEnvp
;
1006 s
->flush
= dynamicFlush
;
1009 s
->mutex_env_string
= ap_psprintf(sp
, "_FCGI_MUTEX_=%ld", mutex
);
1010 s
->fs_path
= ap_pstrdup(sp
, cjob
->fs_path
);
1012 s
->fs_path
= ap_pstrdup(sp
, execName
);
1014 ap_getparents(s
->fs_path
);
1015 ap_no2slash(s
->fs_path
);
1016 s
->procs
= fcgi_util_fs_create_procs(sp
, dynamicMaxClassProcs
);
1018 /* XXX the socket_path (both Unix and Win) *is* deducible and
1019 * thus can and will be used by other apache instances without
1020 * the use of shared data regarding the processes serving the
1021 * requests. This can result in slightly unintuitive process
1022 * counts and security implications. This is prevented
1023 * if suexec (Unix) is in use. This is both a feature and a flaw.
1024 * Changing it now would break existing installations. */
1027 /* Create socket file's path */
1028 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, execName
, user
, group
);
1029 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1031 /* Create sockaddr, prealloc it so it won't get created in tp */
1032 s
->socket_addr
= ap_pcalloc(sp
, sizeof(struct sockaddr_un
));
1033 err
= fcgi_util_socket_make_domain_addr(tp
, (struct sockaddr_un
**)&s
->socket_addr
,
1034 &s
->socket_addr_len
, s
->socket_path
);
1036 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1037 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
1041 if (init_listen_sock(s
)) {
1045 /* If a wrapper is being used, config user/group info */
1047 if (user
[0] == '~') {
1048 /* its a user dir uri, the rest is a username, not a uid */
1049 struct passwd
*pw
= getpwnam(&user
[1]);
1052 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1053 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1054 execName
, &user
[1]);
1057 s
->uid
= pw
->pw_uid
;
1058 s
->user
= ap_pstrdup(sp
, user
);
1059 s
->username
= s
->user
;
1061 s
->gid
= pw
->pw_gid
;
1062 s
->group
= ap_psprintf(sp
, "%ld", (long)s
->gid
);
1067 s
->uid
= (uid_t
)atol(user
);
1068 pw
= getpwuid(s
->uid
);
1070 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1071 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1072 execName
, (long)s
->uid
);
1075 s
->user
= ap_pstrdup(sp
, user
);
1076 s
->username
= ap_pstrdup(sp
, pw
->pw_name
);
1078 s
->gid
= (gid_t
)atol(group
);
1079 s
->group
= ap_pstrdup(sp
, group
);
1083 /* Create socket file's path */
1084 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, cjob
->fs_path
, cjob
->user
, cjob
->group
);
1085 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1089 fcgi_util_fs_add(s
);
1093 if (opcode
== FCGI_SERVER_RESTART_JOB
) {
1095 if (cjob
->id
==FCGI_SERVER_RESTART_JOB
) {
1097 /* Check to see if the binary has changed. If so,
1098 * kill the FCGI application processes, and
1104 char * app_path
= cjob
->fs_path
;
1106 char * app_path
= execName
;
1109 if (stat(app_path
, &stbuf
) == 0 && stbuf
.st_mtime
> s
->startTime
)
1113 /* prevent addition restart requests */
1116 utime(s
->socket_path
, NULL
);
1119 /* kill old server(s) */
1120 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1122 if (s
->procs
[i
].pid
> 0
1123 && stbuf
.st_mtime
> s
->procs
[i
].start_time
)
1125 fcgi_kill(&s
->procs
[i
], SIGTERM
);
1132 ap_log_error(FCGI_LOG_WARN_NOERRNO
,
1133 fcgi_apache_main_server
, "FastCGI: restarting "
1134 "old server \"%s\" processes, newer version "
1139 /* If dynamicAutoRestart, don't mark any new processes
1140 * for starting because we probably got the
1141 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1142 * will be restarting all of those we just killed.
1144 if (dynamicAutoRestart
)
1148 else if (opcode
== FCGI_SERVER_START_JOB
) {
1150 else if (cjob
->id
==FCGI_SERVER_START_JOB
) {
1152 /* we've been asked to start a process--only start
1153 * it if we're not already running at least one
1158 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1159 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1162 /* if already running, don't start another one */
1163 if (i
< dynamicMaxClassProcs
) {
1177 case FCGI_SERVER_RESTART_JOB
:
1181 /* We just waxed 'em all. Try to find an idle slot. */
1183 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1185 if (s
->procs
[i
].state
== FCGI_START_STATE
1186 || s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1190 else if (s
->procs
[i
].state
== FCGI_KILLED_STATE
1191 || s
->procs
[i
].state
== FCGI_READY_STATE
)
1198 /* Nope, just use the first slot */
1199 if (i
== dynamicMaxClassProcs
)
1207 schedule_start(s
, i
);
1212 case FCGI_SERVER_START_JOB
:
1213 case FCGI_REQUEST_TIMEOUT_JOB
:
1215 if ((fcgi_dynamic_total_proc_count
+ 1) > (int) dynamicMaxProcs
) {
1217 * Extra instances should have been
1218 * terminated beforehand, probably need
1219 * to increase ProcessSlack parameter
1221 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1222 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1223 "exceeded dynamicMaxProcs (%d)", s
->fs_path
, dynamicMaxProcs
);
1227 /* find next free slot */
1228 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1230 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1232 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i
);
1235 else if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1240 schedule_start(s
, i
);
1245 if (i
>= dynamicMaxClassProcs
) {
1246 FCGIDBG1("ignore_job: slots are max'd");
1250 case FCGI_REQUEST_COMPLETE_JOB
:
1251 /* only record stats if we have a structure */
1254 s
->totalConnTime
+= req_usec
;
1255 s
->totalQueueTime
+= q_usec
;
1257 s
->totalConnTime
+= cjob
->start_time
;
1258 s
->totalQueueTime
+= cjob
->qsec
;
1267 /* Cleanup job data */
1268 free(cjob
->fs_path
);
1278 if (sp
) ap_destroy_pool(sp
);
1281 free(cjob
->fs_path
);
1289 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1290 "FastCGI: really bogus message: \"%s\"", ptr1
);
1291 ptr1
+= strlen(buf
);
1294 buflen
-= ptr1
- buf
;
1296 memmove(buf
, ptr1
, buflen
);
1300 ap_destroy_pool(tp
);
1304 *----------------------------------------------------------------------
1306 * dynamic_kill_idle_fs_procs
1308 * Implement a kill policy for the dynamic FastCGI applications.
1309 * We also update the data structures to reflect the changes.
1312 * Processes are marked for deletion possibly killed.
1314 *----------------------------------------------------------------------
1316 static void dynamic_kill_idle_fs_procs(void)
1321 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1324 * server's smoothed running time, or if that's 0, the current total
1326 unsigned long connTime
;
1329 * maximum number of microseconds that all of a server's running
1330 * processes together could have spent running since the last check
1332 unsigned long totalTime
;
1335 * percentage, 0-100, of totalTime that the processes actually used
1340 int really_running
= 0;
1342 if (s
->directive
!= APP_CLASS_DYNAMIC
|| s
->numProcesses
== 0)
1347 /* s->numProcesses includes pending kills so get the "active" count */
1348 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1350 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
) ++really_running
;
1353 connTime
= s
->smoothConnTime
? s
->smoothConnTime
: s
->totalConnTime
;
1354 totalTime
= really_running
* (now
- fcgi_dynamic_epoch
) * 1000000 + 1;
1356 loadFactor
= 100 * connTime
/ totalTime
;
1358 if (really_running
== 1)
1360 if (loadFactor
>= dynamicThreshold1
)
1367 int load
= really_running
/ ( really_running
- 1) * loadFactor
;
1369 if (load
>= dynamicThresholdN
)
1376 * Run through the procs to see if we can get away w/o waxing one.
1378 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1380 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1382 s
->procs
[i
].state
= FCGI_READY_STATE
;
1385 else if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
)
1391 if (i
>= dynamicMaxClassProcs
)
1393 ServerProcess
* procs
= s
->procs
;
1396 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1398 if (procs
[i
].state
== FCGI_RUNNING_STATE
)
1400 if (youngest
== -1 || procs
[i
].start_time
>= procs
[youngest
].start_time
)
1409 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1410 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1411 s
->fs_path
, (long) s
->procs
[youngest
].pid
);
1413 fcgi_kill(&s
->procs
[youngest
], SIGTERM
);
1419 * If the number of non-victims is less than or equal to
1420 * the minimum that may be running without being killed off,
1421 * don't select any more victims.
1423 if (fcgi_dynamic_total_proc_count
- victims
<= dynamicMinProcs
)
1433 // This is a little bogus, there's gotta be a better way to do this
1434 // Can we use WaitForMultipleObjects()
1435 #define FCGI_PROC_WAIT_TIME 100
1437 void child_wait_thread_main(void *dummy
) {
1439 DWORD dwRet
= WAIT_TIMEOUT
;
1444 while (!bTimeToDie
) {
1447 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1448 if (s
->directive
== APP_CLASS_EXTERNAL
|| s
->listenFd
< 0) {
1451 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1452 numChildren
= dynamicMaxClassProcs
;
1455 numChildren
= s
->numProcesses
;
1458 for (i
=0; i
< numChildren
; i
++) {
1459 if (s
->procs
[i
].handle
!= INVALID_HANDLE_VALUE
)
1461 DWORD exitStatus
= 0;
1463 /* timeout is currently set for 100 miliecond */
1464 /* it may need to be longer or user customizable */
1465 dwRet
= WaitForSingleObject(s
->procs
[i
].handle
, FCGI_PROC_WAIT_TIME
);
1469 if (dwRet
!= WAIT_TIMEOUT
&& dwRet
!= WAIT_FAILED
) {
1470 /* a child fs has died */
1471 /* mark the child as dead */
1473 if (s
->directive
== APP_CLASS_STANDARD
) {
1474 /* restart static app */
1475 s
->procs
[i
].state
= FCGI_START_STATE
;
1480 fcgi_dynamic_total_proc_count
--;
1481 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1483 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1484 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1487 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1490 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0)) {
1491 s
->procs
[i
].state
= FCGI_START_STATE
;
1494 s
->procs
[i
].state
= FCGI_READY_STATE
;
1499 GetExitCodeProcess(s
->procs
[i
].handle
, &exitStatus
);
1501 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1502 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1503 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1504 s
->fs_path
, (long) s
->procs
[i
].pid
, exitStatus
);
1506 CloseHandle(s
->procs
[i
].handle
);
1507 s
->procs
[i
].handle
= INVALID_HANDLE_VALUE
;
1508 s
->procs
[i
].pid
= -1;
1510 /* wake up the main thread */
1511 SetEvent(fcgi_event_handles
[WAKE_EVENT
]);
1516 Sleep(waited
? 0 : FCGI_PROC_WAIT_TIME
);
1522 static void setup_signals(void)
1524 struct sigaction sa
;
1526 /* Setup handlers */
1528 sa
.sa_handler
= signal_handler
;
1529 sigemptyset(&sa
.sa_mask
);
1532 if (sigaction(SIGTERM
, &sa
, NULL
) < 0) {
1533 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1534 "sigaction(SIGTERM) failed");
1537 if (sigaction(SIGHUP
, &sa
, NULL
) < 0) {
1538 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1539 "sigaction(SIGHUP) failed");
1541 /* httpd graceful restart */
1542 if (sigaction(SIGUSR1
, &sa
, NULL
) < 0) {
1543 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1544 "sigaction(SIGUSR1) failed");
1546 /* read messages from request handlers - kill interval expired */
1547 if (sigaction(SIGALRM
, &sa
, NULL
) < 0) {
1548 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1549 "sigaction(SIGALRM) failed");
1551 if (sigaction(SIGCHLD
, &sa
, NULL
) < 0) {
1552 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1553 "sigaction(SIGCHLD) failed");
1558 #if !defined(WIN32) && !defined(APACHE2)
1559 int fcgi_pm_main(void *dummy
, child_info
*info
)
1561 void fcgi_pm_main(void *dummy
)
1571 HANDLE child_wait_thread
= INVALID_HANDLE_VALUE
;
1573 int callWaitPid
, callDynamicProcs
;
1577 // Add SystemRoot to the dynamic environment
1578 char ** envp
= dynamicEnvp
;
1579 for (i
= 0; *envp
; ++i
) {
1582 fcgi_config_set_env_var(fcgi_config_pool
, dynamicEnvp
, &i
, "SystemRoot");
1586 reduce_privileges();
1587 change_process_name("fcgi-pm");
1589 close(fcgi_pm_pipe
[1]);
1593 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1594 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper
);
1598 /* Initialize AppClass */
1599 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1601 if (s
->directive
!= APP_CLASS_STANDARD
)
1609 for (i
= 0; i
< s
->numProcesses
; ++i
)
1610 s
->procs
[i
].state
= FCGI_START_STATE
;
1614 child_wait_thread
= (HANDLE
) _beginthread(child_wait_thread_main
, 0, NULL
);
1616 if (child_wait_thread
== (HANDLE
) -1)
1618 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1619 "FastCGI: failed to create process manager's wait thread!");
1622 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1623 "FastCGI: process manager initialized");
1625 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1626 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1632 * Loop until SIGTERM
1635 int sleepSeconds
= min(dynamicKillInterval
, dynamicUpdateInterval
);
1642 unsigned int numChildren
;
1645 * If we came out of sigsuspend() for any reason other than
1646 * SIGALRM, pick up where we left off.
1649 sleepSeconds
= alarmLeft
;
1652 * Examine each configured AppClass for a process that needs
1653 * starting. Compute the earliest time when the start should
1654 * be attempted, starting it now if the time has passed. Also,
1655 * remember that we do NOT need to restart externally managed
1656 * FastCGI applications.
1658 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1660 if (s
->directive
== APP_CLASS_EXTERNAL
)
1663 numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
1664 ? dynamicMaxClassProcs
1667 for (i
= 0; i
< numChildren
; ++i
)
1669 if (s
->procs
[i
].pid
<= 0 && s
->procs
[i
].state
== FCGI_START_STATE
)
1671 int restart
= (s
->procs
[i
].pid
< 0);
1672 time_t restartTime
= s
->restartTime
;
1676 /* we've gone to using the badDelay, the only thing that
1677 resets bad is when badDelay has expired. but numFailures
1678 is only just set below its threshold. the proc's
1679 start_times are all reset when the bad is. the numFailures
1680 is reset when we see an app run for a period */
1682 s
->procs
[i
].start_time
= 0;
1685 if (s
->numFailures
> MAX_FAILED_STARTS
)
1687 time_t last_start_time
= s
->procs
[i
].start_time
;
1689 if (last_start_time
&& now
- last_start_time
> RUNTIME_SUCCESS_INTERVAL
)
1693 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1694 "FastCGI:%s server \"%s\" has remained"
1695 " running for more than %d seconds, its restart"
1696 " interval has been restored to %d seconds",
1697 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1698 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, s
->restartDelay
);
1704 for (j
= 0; j
< numChildren
; ++j
)
1706 if (s
->procs
[j
].pid
<= 0) continue;
1707 if (s
->procs
[j
].state
!= FCGI_RUNNING_STATE
) continue;
1708 if (s
->procs
[j
].start_time
== 0) continue;
1709 if (now
- s
->procs
[j
].start_time
> RUNTIME_SUCCESS_INTERVAL
) break;
1712 if (j
>= numChildren
)
1715 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1716 "FastCGI:%s server \"%s\" has failed to remain"
1717 " running for %d seconds given %d attempts, its restart"
1718 " interval has been backed off to %d seconds",
1719 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1720 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, MAX_FAILED_STARTS
,
1721 FAILED_STARTS_DELAY
);
1727 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1728 "FastCGI:%s server \"%s\" has remained"
1729 " running for more than %d seconds, its restart"
1730 " interval has been restored to %d seconds",
1731 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1732 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, s
->restartDelay
);
1739 restartTime
+= FAILED_STARTS_DELAY
;
1743 restartTime
+= (restart
) ? s
->restartDelay
: s
->initStartDelay
;
1746 if (restartTime
<= now
)
1751 s
->numFailures
= MAX_FAILED_STARTS
;
1754 if (s
->listenFd
< 0 && init_listen_sock(s
))
1756 if (sleepSeconds
> s
->initStartDelay
)
1757 sleepSeconds
= s
->initStartDelay
;
1761 if (caughtSigTerm
) {
1762 goto ProcessSigTerm
;
1765 s
->procs
[i
].pid
= spawn_fs_process(s
, &s
->procs
[i
]);
1766 if (s
->procs
[i
].pid
<= 0) {
1767 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1768 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1769 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1772 sleepSeconds
= min(sleepSeconds
,
1773 max((int) s
->restartDelay
, FCGI_MIN_EXEC_RETRY_DELAY
));
1775 ap_assert(s
->procs
[i
].pid
< 0);
1779 s
->procs
[i
].start_time
= now
;
1780 s
->restartTime
= now
;
1782 if (s
->startTime
== 0) {
1786 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1788 fcgi_dynamic_total_proc_count
++;
1789 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1792 s
->procs
[i
].state
= FCGI_RUNNING_STATE
;
1795 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1796 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1797 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1798 s
->fs_path
, (long) s
->uid
, (long) s
->gid
,
1799 restart
? "re" : "", (long) s
->procs
[i
].pid
);
1802 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1803 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1804 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1805 s
->fs_path
, restart
? "re" : "", (long) s
->procs
[i
].pid
);
1807 ap_assert(s
->procs
[i
].pid
> 0);
1809 sleepSeconds
= min(sleepSeconds
, restartTime
- now
);
1818 goto ProcessSigTerm
;
1820 if((!caughtSigChld
) && (!caughtSigAlarm
)) {
1823 alarm(sleepSeconds
);
1826 FD_SET(fcgi_pm_pipe
[0], &rfds
);
1827 read_ready
= ap_select(fcgi_pm_pipe
[0] + 1, &rfds
, NULL
, NULL
, NULL
);
1829 alarmLeft
= alarm(0);
1831 callWaitPid
= caughtSigChld
;
1832 caughtSigChld
= FALSE
;
1833 callDynamicProcs
= caughtSigAlarm
;
1834 caughtSigAlarm
= FALSE
;
1839 * Dynamic fcgi process management
1841 if((callDynamicProcs
) || (!callWaitPid
)) {
1842 dynamic_read_msgs(read_ready
);
1843 if(fcgi_dynamic_epoch
== 0) {
1844 fcgi_dynamic_epoch
= now
;
1846 if(((long)(now
-fcgi_dynamic_epoch
)>=dynamicKillInterval
) ||
1847 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
)>=dynamicMaxProcs
)) {
1848 dynamic_kill_idle_fs_procs();
1849 fcgi_dynamic_epoch
= now
;
1857 /* We've caught SIGCHLD, so find out who it was using waitpid,
1858 * write a log message and update its data structure. */
1862 goto ProcessSigTerm
;
1864 childPid
= waitpid(-1, &waitStatus
, WNOHANG
);
1866 if (childPid
== -1 || childPid
== 0)
1869 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1870 if (s
->directive
== APP_CLASS_EXTERNAL
)
1873 if (s
->directive
== APP_CLASS_DYNAMIC
)
1874 numChildren
= dynamicMaxClassProcs
;
1876 numChildren
= s
->numProcesses
;
1878 for (i
= 0; i
< numChildren
; i
++) {
1879 if (s
->procs
[i
].pid
== childPid
)
1884 /* TODO: print something about this unknown child */
1888 s
->procs
[i
].pid
= -1;
1890 if (s
->directive
== APP_CLASS_STANDARD
) {
1891 /* Always restart static apps */
1892 s
->procs
[i
].state
= FCGI_START_STATE
;
1897 fcgi_dynamic_total_proc_count
--;
1899 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1900 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1903 /* A dynamic app died or exited without provocation from the PM */
1906 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0))
1907 s
->procs
[i
].state
= FCGI_START_STATE
;
1909 s
->procs
[i
].state
= FCGI_READY_STATE
;
1913 if (WIFEXITED(waitStatus
)) {
1914 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1915 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1916 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1917 s
->fs_path
, (long) childPid
, WEXITSTATUS(waitStatus
));
1919 else if (WIFSIGNALED(waitStatus
)) {
1920 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1921 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1922 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1923 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
),
1925 WCOREDUMP(waitStatus
) ? ", a core file may have been generated" : "");
1930 else if (WIFSTOPPED(waitStatus
)) {
1931 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1932 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1933 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1934 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
));
1936 } /* for (;;), waitpid() */
1940 /* wait for an event to occur or timer expires */
1941 expire
= time(NULL
) + sleepSeconds
;
1942 dwRet
= WaitForMultipleObjects(3, (HANDLE
*) fcgi_event_handles
, FALSE
, sleepSeconds
* 1000);
1944 if (dwRet
== WAIT_FAILED
) {
1945 /* There is something seriously wrong here */
1946 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1947 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1951 if (dwRet
!= WAIT_TIMEOUT
) {
1955 alarmLeft
= expire
- now
;
1959 * Dynamic fcgi process management
1961 if ((dwRet
== MBOX_EVENT
) || (dwRet
== WAIT_TIMEOUT
)) {
1962 if (dwRet
== MBOX_EVENT
) {
1968 dynamic_read_msgs(read_ready
);
1970 if(fcgi_dynamic_epoch
== 0) {
1971 fcgi_dynamic_epoch
= now
;
1974 if ((now
-fcgi_dynamic_epoch
>= (int) dynamicKillInterval
) ||
1975 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
) >= dynamicMaxProcs
)) {
1976 dynamic_kill_idle_fs_procs();
1977 fcgi_dynamic_epoch
= now
;
1981 else if (dwRet
== WAKE_EVENT
) {
1984 else if (dwRet
== TERM_EVENT
) {
1985 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
1986 "FastCGI: Termination event received process manager shutting down");
1989 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
1991 goto ProcessSigTerm
;
1994 // Have an received an unknown event - should not happen
1995 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1996 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1999 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
2001 goto ProcessSigTerm
;
2006 } /* for (;;), the whole shoot'n match */
2010 * Kill off the children, then exit.
2022 int fcgi_pm_add_job(fcgi_pm_job
*new_job
)
2024 int rv
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
2026 if (rv
!= WAIT_OBJECT_0
&& rv
!= WAIT_ABANDONED
)
2028 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2029 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2033 new_job
->next
= fcgi_dynamic_mbox
;
2034 fcgi_dynamic_mbox
= new_job
;
2036 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
2038 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2039 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");