2 * $Id: fcgi_pm.c,v 1.93 2004/04/15 02:01:26 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;
36 #pragma warning ( disable : 4100 4102 )
37 static BOOL bTimeToDie
= FALSE
; /* process termination flag */
38 HANDLE fcgi_event_handles
[3];
46 static int seteuid_root(void)
48 int rc
= seteuid(getuid());
50 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
51 "FastCGI: seteuid(0) failed");
56 static int seteuid_user(void)
58 int rc
= seteuid(ap_user_id
);
60 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
61 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id
);
68 * Signal the process to exit. How (or if) the process responds
69 * depends on the FastCGI application library (esp. on Win32) and
70 * possibly application code (signal handlers and whether or not
71 * SA_RESTART is on). At any rate, we send the signal with the
72 * hopes that the process will exit on its own. Later, as we
73 * review the state of application processes, if we see one marked
74 * for death, but that hasn't died within a specified period of
75 * time, fcgi_kill() is called again with a KILL)
77 static void fcgi_kill(ServerProcess
*process
, int sig
)
79 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process
->pid
, sig
);
81 process
->state
= FCGI_VICTIM_STATE
;
87 SetEvent(process
->terminationEvent
);
89 else if (sig
== SIGKILL
)
91 TerminateProcess(process
->handle
, 1);
105 kill(process
->pid
, sig
);
115 /*******************************************************************************
116 * Send SIGTERM to each process in the server class, remove socket
117 * file if appropriate. Currently this is only called when the PM is shutting
118 * down and thus memory isn't freed and sockets and files aren't closed.
120 static void shutdown_all()
122 fcgi_server
*s
= fcgi_servers
;
126 ServerProcess
*proc
= s
->procs
;
128 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
129 ? dynamicMaxClassProcs
133 if (s
->socket_path
!= NULL
&& s
->directive
!= APP_CLASS_EXTERNAL
)
135 /* Remove the socket file */
136 if (unlink(s
->socket_path
) != 0 && errno
!= ENOENT
) {
137 ap_log_error(FCGI_LOG_ERR
, fcgi_apache_main_server
,
138 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
140 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "", s
->fs_path
);
145 /* Send TERM to all processes */
146 for (i
= 0; i
< numChildren
; i
++, proc
++)
148 if (proc
->state
== FCGI_RUNNING_STATE
)
150 fcgi_kill(proc
, SIGTERM
);
157 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
160 * WIN32 applications may not have support for the shutdown event
161 * depending on their application library version
164 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT
);
169 ServerProcess
*proc
= s
->procs
;
171 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
172 ? dynamicMaxClassProcs
175 /* Send KILL to all processes */
176 for (i
= 0; i
< numChildren
; i
++, proc
++)
178 if (proc
->state
== FCGI_RUNNING_STATE
)
180 fcgi_kill(proc
, SIGKILL
);
190 static int init_listen_sock(fcgi_server
* fs
)
192 ap_assert(fs
->directive
!= APP_CLASS_EXTERNAL
);
194 /* Create the socket */
195 if ((fs
->listenFd
= socket(fs
->socket_addr
->sa_family
, SOCK_STREAM
, 0)) < 0)
198 errno
= WSAGetLastError(); /* Not sure if this will work as expected */
200 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
201 "FastCGI: can't create %sserver \"%s\": socket() failed",
202 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
208 if (fs
->socket_addr
->sa_family
== AF_UNIX
)
210 /* Remove any existing socket file.. just in case */
211 unlink(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
);
217 setsockopt(fs
->listenFd
, SOL_SOCKET
, SO_REUSEADDR
, (char *)&flag
, sizeof(flag
));
220 /* Bind it to the socket_addr */
221 if (bind(fs
->listenFd
, fs
->socket_addr
, fs
->socket_addr_len
))
226 errno
= WSAGetLastError();
228 ap_snprintf(port
, sizeof(port
), "port=%d",
229 ((struct sockaddr_in
*)fs
->socket_addr
)->sin_port
);
231 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
232 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
233 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
236 (fs
->socket_addr
->sa_family
== AF_UNIX
) ?
237 ((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
:
243 /* Twiddle Unix socket permissions */
244 else if (fs
->socket_addr
->sa_family
== AF_UNIX
245 && chmod(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
, S_IRUSR
| S_IWUSR
))
247 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
248 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
249 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
255 else if (listen(fs
->listenFd
, fs
->listenQueueDepth
))
258 errno
= WSAGetLastError();
260 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
261 "FastCGI: can't create %sserver \"%s\": listen() failed",
262 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
271 closesocket(fs
->listenFd
);
282 *----------------------------------------------------------------------
286 * The FastCGI process manager, which runs as a separate
287 * process responsible for:
288 * - Starting all the FastCGI proceses.
289 * - Restarting any of these processes that die (indicated
291 * - Catching SIGTERM and relaying it to all the FastCGI
292 * processes before exiting.
295 * Uses global variable fcgi_servers.
303 *----------------------------------------------------------------------
306 static int caughtSigTerm
= FALSE
;
307 static int caughtSigChld
= FALSE
;
308 static int caughtSigAlarm
= FALSE
;
310 static void signal_handler(int signo
)
312 if ((signo
== SIGTERM
) || (signo
== SIGUSR1
) || (signo
== SIGHUP
)) {
313 /* SIGUSR1 & SIGHUP are sent by apache to its process group
314 * when apache get 'em. Apache follows up (1.2.x) with attacks
315 * on each of its child processes, but we've got the KillMgr
316 * sitting between us so we never see the KILL. The main loop
317 * in ProcMgr also checks to see if the KillMgr has terminated,
318 * and if it has, we handl it as if we should shutdown too. */
319 caughtSigTerm
= TRUE
;
320 } else if(signo
== SIGCHLD
) {
321 caughtSigChld
= TRUE
;
322 } else if(signo
== SIGALRM
) {
323 caughtSigAlarm
= TRUE
;
329 *----------------------------------------------------------------------
331 * spawn_fs_process --
333 * Fork and exec the specified fcgi process.
336 * 0 for successful fork, -1 for failed fork.
338 * In case the child fails before or in the exec, the child
339 * obtains the error log by calling getErrLog, logs
340 * the error, and exits with exit status = errno of
341 * the failed system call.
344 * Child process created.
346 *----------------------------------------------------------------------
348 static pid_t
spawn_fs_process(fcgi_server
*fs
, ServerProcess
*process
)
355 char *dnEnd
, *failedSysCall
;
362 /* We're the child. We're gonna exec() so pools don't matter. */
364 dnEnd
= strrchr(fs
->fs_path
, '/');
368 dirName
= ap_pcalloc(fcgi_config_pool
, dnEnd
- fs
->fs_path
+ 1);
369 dirName
= memcpy(dirName
, fs
->fs_path
, dnEnd
- fs
->fs_path
);
371 if (chdir(dirName
) < 0) {
372 failedSysCall
= "chdir()";
373 goto FailedSystemCallExit
;
377 /* OS/2 dosen't support nice() */
378 if (fs
->processPriority
!= 0) {
379 if (nice(fs
->processPriority
) == -1) {
380 failedSysCall
= "nice()";
381 goto FailedSystemCallExit
;
386 /* Open the listenFd on spec'd fd */
387 if (fs
->listenFd
!= FCGI_LISTENSOCK_FILENO
)
388 dup2(fs
->listenFd
, FCGI_LISTENSOCK_FILENO
);
390 /* Close all other open fds, except stdout/stderr. Leave these two open so
391 * FastCGI applications don't have to find and fix ALL 3rd party libs that
392 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
393 * main server error_log - @@@ provide a directive control where this goes.
395 ap_error_log2stderr(fcgi_apache_main_server
);
397 for (i
= 0; i
< FCGI_MAX_FD
; i
++) {
398 if (i
!= FCGI_LISTENSOCK_FILENO
&& i
!= 2 && i
!= 1) {
403 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
404 * install its own handler. */
405 signal(SIGPIPE
, SIG_IGN
);
411 /* Relinquish our root real uid powers */
415 /* Apache (2 anyway) doesn't use suexec if there is no user/group in
416 * effect - this translates to a uid/gid of 0/0 (which should never
417 * be a valid uid/gid for an suexec invocation so it should be safe */
418 if (fs
->uid
== 0 && fs
->gid
== 0) {
422 #ifdef NO_SUEXEC_FOR_AP_USER_N_GROUP
424 /* AP13 does not use suexec if the target uid/gid is the same as the
425 * server's - AP20 does. I (now) consider the AP2 approach better
426 * (fcgi_pm.c v1.42 incorporated the 1.3 behaviour, v1.84 reverted it,
427 * v1.85 added the compile time option to use the old behaviour). */
428 if (fcgi_user_id
== fs
->uid
&& fcgi_group_id
== fs
->gid
) {
433 shortName
= strrchr(fs
->fs_path
, '/') + 1;
436 execle(fcgi_wrapper
, fcgi_wrapper
, fs
->username
, fs
->group
,
437 shortName
, NULL
, fs
->envp
);
438 } while (errno
== EINTR
);
444 execle(fs
->fs_path
, fs
->fs_path
, NULL
, fs
->envp
);
445 } while (errno
== EINTR
);
448 failedSysCall
= "execle()";
450 FailedSystemCallExit
:
451 fprintf(stderr
, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
452 fs
->fs_path
, (long) getpid(), failedSysCall
, strerror(errno
));
455 /* avoid an irrelevant compiler warning */
462 /* based on mod_cgi.c:run_cgi_child() */
465 char * termination_env_string
;
466 HANDLE listen_handle
= INVALID_HANDLE_VALUE
;
467 apr_procattr_t
* procattr
;
468 apr_proc_t proc
= { 0 };
471 cgi_exec_info_t e_info
= { 0 };
472 request_rec r
= { 0 };
476 APR_OPTIONAL_FN_TYPE(ap_cgi_build_command
) *cgi_build_command
;
478 cgi_build_command
= APR_RETRIEVE_OPTIONAL_FN(ap_cgi_build_command
);
479 if (cgi_build_command
== NULL
)
481 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
482 "FastCGI: can't exec server \"%s\", mod_cgi isn't loaded",
487 if (apr_pool_create(&tp
, fcgi_config_pool
))
490 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
491 if (process
->terminationEvent
== NULL
)
494 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
496 termination_env_string
= ap_psprintf(tp
,
497 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
499 while (fs
->envp
[i
]) i
++;
500 fs
->envp
[i
++] = termination_env_string
;
501 fs
->envp
[i
] = (char *) fs
->mutex_env_string
;
503 ap_assert(fs
->envp
[i
+ 1] == NULL
);
507 SECURITY_ATTRIBUTES sa
= { 0 };
509 sa
.bInheritHandle
= TRUE
;
510 sa
.nLength
= sizeof(sa
);
512 listen_handle
= CreateNamedPipe(fs
->socket_path
,
514 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
515 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
517 if (listen_handle
== INVALID_HANDLE_VALUE
)
519 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
520 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
527 listen_handle
= (HANDLE
) fs
->listenFd
;
530 r
.per_dir_config
= fcgi_apache_main_server
->lookup_defaults
;
531 r
.server
= fcgi_apache_main_server
;
532 r
.filename
= (char *) fs
->fs_path
;
534 r
.subprocess_env
= apr_table_make(tp
, 0);
536 e_info
.cmd_type
= APR_PROGRAM
;
538 rv
= cgi_build_command(&command
, &argv
, &r
, tp
, &e_info
);
539 if (rv
!= APR_SUCCESS
)
541 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
542 "FastCGI: don't know how to spawn cmd child process: %s",
547 if (apr_procattr_create(&procattr
, tp
))
550 if (apr_procattr_dir_set(procattr
, ap_make_dirstr_parent(tp
, fs
->fs_path
)))
553 if (apr_procattr_cmdtype_set(procattr
, e_info
.cmd_type
))
556 if (apr_procattr_detach_set(procattr
, 1))
559 if (apr_os_file_put(&file
, &listen_handle
, 0, tp
))
562 /* procattr is opaque so we have to use this - unfortuantely it dups */
563 if (apr_procattr_child_in_set(procattr
, file
, NULL
))
566 if (apr_proc_create(&proc
, command
, argv
, fs
->envp
, procattr
, tp
))
569 process
->handle
= proc
.hproc
;
573 if (fs
->socket_path
&& listen_handle
!= INVALID_HANDLE_VALUE
)
575 CloseHandle(listen_handle
);
580 fs
->envp
[i
- 1] = NULL
;
587 #else /* WIN32 && !APACHE2 */
589 /* Adapted from Apache's util_script.c ap_call_exec() */
590 char *interpreter
= NULL
;
591 char *quoted_filename
;
593 char *pEnvBlock
, *pNext
;
596 int iEnvBlockLen
= 1;
598 file_type_e fileType
;
601 PROCESS_INFORMATION pi
;
606 pool
* tp
= ap_make_sub_pool(fcgi_config_pool
);
608 HANDLE listen_handle
= INVALID_HANDLE_VALUE
;
609 char * termination_env_string
= NULL
;
611 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
612 if (process
->terminationEvent
== NULL
)
614 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
615 "FastCGI: can't create termination event for server \"%s\", "
616 "CreateEvent() failed", fs
->fs_path
);
619 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
621 termination_env_string
= ap_psprintf(tp
,
622 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
626 SECURITY_ATTRIBUTES sa
;
628 sa
.lpSecurityDescriptor
= NULL
;
629 sa
.bInheritHandle
= TRUE
;
630 sa
.nLength
= sizeof(sa
);
632 listen_handle
= CreateNamedPipe(fs
->socket_path
,
634 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
635 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
637 if (listen_handle
== INVALID_HANDLE_VALUE
)
639 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
640 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs
->fs_path
);
646 listen_handle
= (HANDLE
) fs
->listenFd
;
649 memset(&si
, 0, sizeof(si
));
650 memset(&pi
, 0, sizeof(pi
));
651 memset(&r
, 0, sizeof(r
));
653 /* Can up a fake request to pass to ap_get_win32_interpreter() */
654 r
.per_dir_config
= fcgi_apache_main_server
->lookup_defaults
;
655 r
.server
= fcgi_apache_main_server
;
656 r
.filename
= (char *) fs
->fs_path
;
659 fileType
= ap_get_win32_interpreter(&r
, &interpreter
);
661 if (fileType
== eFileTypeUNKNOWN
) {
662 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
663 "FastCGI: %s is not executable; ensure interpreted scripts have "
664 "\"#!\" as their first line",
671 * We have the interpreter (if there is one) and we have
672 * the arguments (if there are any).
673 * Build the command string to pass to CreateProcess.
675 quoted_filename
= ap_pstrcat(tp
, "\"", fs
->fs_path
, "\"", NULL
);
676 if (interpreter
&& *interpreter
) {
677 pCommand
= ap_pstrcat(tp
, interpreter
, " ", quoted_filename
, NULL
);
680 pCommand
= quoted_filename
;
684 * Make child process use hPipeOutputWrite as standard out,
685 * and make sure it does not show on screen.
688 si
.dwFlags
= STARTF_USESHOWWINDOW
| STARTF_USESTDHANDLES
;
689 si
.wShowWindow
= SW_HIDE
;
690 si
.hStdInput
= listen_handle
;
692 /* XXX These should be open to the error_log */
693 si
.hStdOutput
= INVALID_HANDLE_VALUE
;
694 si
.hStdError
= INVALID_HANDLE_VALUE
;
697 * Win32's CreateProcess call requires that the environment
698 * be passed in an environment block, a null terminated block of
699 * null terminated strings.
700 * @todo we should store the env in this format for win32.
704 iEnvBlockLen
+= strlen(fs
->envp
[i
]) + 1;
708 iEnvBlockLen
+= strlen(termination_env_string
) + 1;
709 iEnvBlockLen
+= strlen(fs
->mutex_env_string
) + 1;
711 pEnvBlock
= (char *) ap_pcalloc(tp
, iEnvBlockLen
);
717 strcpy(pNext
, fs
->envp
[i
]);
718 pNext
+= strlen(pNext
) + 1;
722 strcpy(pNext
, termination_env_string
);
723 pNext
+= strlen(pNext
) + 1;
724 strcpy(pNext
, fs
->mutex_env_string
);
726 if (CreateProcess(NULL
, pCommand
, NULL
, NULL
, TRUE
,
729 ap_make_dirstr_parent(tp
, fs
->fs_path
),
732 /* Hack to get 16-bit CGI's working. It works for all the
733 * standard modules shipped with Apache. pi.dwProcessId is 0
734 * for 16-bit CGIs and all the Unix specific code that calls
735 * ap_call_exec interprets this as a failure case. And we can't
736 * use -1 either because it is mapped to 0 by the caller.
738 pid
= (fileType
== eFileTypeEXE16
) ? -2 : pi
.dwProcessId
;
740 process
->handle
= pi
.hProcess
;
741 CloseHandle(pi
.hThread
);
746 if (fs
->socket_path
&& listen_handle
!= INVALID_HANDLE_VALUE
)
748 CloseHandle(listen_handle
);
755 #endif /* !APACHE2 */
760 static void reduce_privileges(void)
768 /* Get username if passed as a uid */
769 if (ap_user_name
[0] == '#') {
770 uid_t uid
= atoi(&ap_user_name
[1]);
771 struct passwd
*ent
= getpwuid(uid
);
774 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
775 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
776 "you probably need to modify the User directive", (unsigned)uid
);
785 if (setgid(ap_group_id
) == -1) {
786 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
787 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id
);
791 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
793 /* Initialize supplementary groups */
794 if (initgroups(name
, ap_group_id
) == -1) {
795 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
796 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
797 name
, (unsigned)ap_group_id
);
804 if (seteuid_user() == -1) {
805 ap_log_error(FCGI_LOG_ALERT_NOERRNO
, fcgi_apache_main_server
,
806 "FastCGI: process manager exiting, failed to reduce privileges");
811 if (setuid(ap_user_id
) == -1) {
812 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
813 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id
);
820 * Change the name of this process - best we can easily.
822 static void change_process_name(const char * const name
)
824 /* under Apache2, ap_server_argv0 is const */
825 strncpy((char *) ap_server_argv0
, name
, strlen(ap_server_argv0
));
829 static void schedule_start(fcgi_server
*s
, int proc
)
831 /* If we've started one recently, don't register another */
832 time_t time_passed
= now
- s
->restartTime
;
834 if ((s
->procs
[proc
].pid
&& (time_passed
< (int) s
->restartDelay
))
835 || ((s
->procs
[proc
].pid
== 0) && (time_passed
< s
->initStartDelay
)))
837 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
);
841 FCGIDBG3("scheduling_start: %s (%d)", s
->fs_path
, proc
);
842 s
->procs
[proc
].state
= FCGI_START_STATE
;
843 if (proc
== dynamicMaxClassProcs
- 1) {
844 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
845 "FastCGI: scheduled the %sstart of the last (dynamic) server "
846 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
847 s
->procs
[proc
].pid
? "re" : "", s
->fs_path
, dynamicMaxClassProcs
);
852 *----------------------------------------------------------------------
856 * Removes the records written by request handlers and decodes them.
857 * We also update the data structures to reflect the changes.
859 *----------------------------------------------------------------------
862 static void dynamic_read_msgs(int read_ready
)
868 static int buflen
= 0;
869 static char buf
[FCGI_MSGS_BUFSIZE
+ 1];
870 char *ptr1
, *ptr2
, opcode
;
871 char execName
[FCGI_MAXPATH
+ 1];
872 char user
[MAX_USER_NAME_LEN
+ 2];
873 char group
[MAX_GID_CHAR_LEN
+ 1];
874 unsigned long q_usec
= 0UL, req_usec
= 0UL;
876 fcgi_pm_job
*joblist
= NULL
;
877 fcgi_pm_job
*cjob
= NULL
;
880 pool
*sp
= NULL
, *tp
;
883 user
[MAX_USER_NAME_LEN
+ 1] = group
[MAX_GID_CHAR_LEN
] = '\0';
887 * To prevent the idle application from running indefinitely, we
888 * check the timer and if it is expired, we recompute the values
889 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
890 * message is received, only updates are made to the data structures.
892 if (fcgi_dynamic_last_analyzed
== 0) {
893 fcgi_dynamic_last_analyzed
= now
;
895 if ((now
- fcgi_dynamic_last_analyzed
) >= (int)dynamicUpdateInterval
) {
896 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
897 if (s
->directive
!= APP_CLASS_DYNAMIC
)
900 /* Advance the last analyzed timestamp by the elapsed time since
901 * it was last set. Round the increase down to the nearest
902 * multiple of dynamicUpdateInterval */
904 fcgi_dynamic_last_analyzed
+= (((long)(now
-fcgi_dynamic_last_analyzed
)/dynamicUpdateInterval
)*dynamicUpdateInterval
);
905 s
->smoothConnTime
= (unsigned long) ((1.0-dynamicGain
)*s
->smoothConnTime
+ dynamicGain
*s
->totalConnTime
);
906 s
->totalConnTime
= 0UL;
907 s
->totalQueueTime
= 0UL;
911 if (read_ready
<= 0) {
916 rc
= read(fcgi_pm_pipe
[0], (void *)(buf
+ buflen
), FCGI_MSGS_BUFSIZE
- buflen
);
918 if (!caughtSigTerm
) {
919 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
920 "FastCGI: read() from pipe failed (%d)", rc
);
922 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
923 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
924 caughtSigTerm
= TRUE
;
934 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
935 * request to do something) and/or when a timeout expires.
936 * There really should be no reason why this wait would get stuck
937 * but there's no point in waiting forever. */
939 rc
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
941 if (rc
!= WAIT_OBJECT_0
&& rc
!= WAIT_ABANDONED
)
943 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
944 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
948 joblist
= fcgi_dynamic_mbox
;
949 fcgi_dynamic_mbox
= NULL
;
951 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
953 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
954 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
961 apr_pool_create(&tp
, fcgi_config_pool
);
963 tp
= ap_make_sub_pool(fcgi_config_pool
);
967 for (ptr1
= buf
; ptr1
; ptr1
= ptr2
) {
970 ptr2
= strchr(ptr1
, '*');
982 case FCGI_SERVER_START_JOB
:
983 case FCGI_SERVER_RESTART_JOB
:
985 if (sscanf(ptr1
, "%c %s %16s %15s",
986 &opcode
, execName
, user
, group
) != 4)
992 case FCGI_REQUEST_TIMEOUT_JOB
:
994 if (sscanf(ptr1
, "%c %s %16s %15s",
995 &opcode
, execName
, user
, group
) != 4)
1001 case FCGI_REQUEST_COMPLETE_JOB
:
1003 if (sscanf(ptr1
, "%c %s %16s %15s %lu %lu",
1004 &opcode
, execName
, user
, group
, &q_usec
, &req_usec
) != 6)
1016 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode
, execName
, user
, group
, q_usec
, req_usec
);
1019 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1020 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1
);
1024 /* Update data structures for processing */
1025 while (cjob
!= NULL
) {
1026 joblist
= cjob
->next
;
1027 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob
->id
, cjob
->fs_path
, cjob
->user
, cjob
->group
, cjob
->qsec
, cjob
->start_time
);
1031 s
= fcgi_util_fs_get(execName
, user
, group
);
1033 s
= fcgi_util_fs_get(cjob
->fs_path
, cjob
->user
, cjob
->group
);
1037 if (s
==NULL
&& opcode
!= FCGI_REQUEST_COMPLETE_JOB
)
1039 if (s
==NULL
&& cjob
->id
!= FCGI_REQUEST_COMPLETE_JOB
)
1044 HANDLE mutex
= CreateMutex(NULL
, FALSE
, cjob
->fs_path
);
1048 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1049 "FastCGI: can't create accept mutex "
1050 "for (dynamic) server \"%s\"", cjob
->fs_path
);
1054 SetHandleInformation(mutex
, HANDLE_FLAG_INHERIT
, TRUE
);
1059 /* Create a perm subpool to hold the new server data,
1060 * we can destroy it if something doesn't pan out */
1062 apr_pool_create(&sp
, fcgi_config_pool
);
1064 sp
= ap_make_sub_pool(fcgi_config_pool
);
1067 /* Create a new "dynamic" server */
1068 s
= fcgi_util_fs_new(sp
);
1070 s
->directive
= APP_CLASS_DYNAMIC
;
1071 s
->restartDelay
= dynamicRestartDelay
;
1072 s
->listenQueueDepth
= dynamicListenQueueDepth
;
1073 s
->initStartDelay
= dynamicInitStartDelay
;
1074 s
->envp
= dynamicEnvp
;
1075 s
->flush
= dynamicFlush
;
1078 s
->mutex_env_string
= ap_psprintf(sp
, "_FCGI_MUTEX_=%ld", mutex
);
1079 s
->fs_path
= ap_pstrdup(sp
, cjob
->fs_path
);
1081 s
->fs_path
= ap_pstrdup(sp
, execName
);
1083 ap_getparents(s
->fs_path
);
1084 ap_no2slash(s
->fs_path
);
1085 s
->procs
= fcgi_util_fs_create_procs(sp
, dynamicMaxClassProcs
);
1087 /* XXX the socket_path (both Unix and Win) *is* deducible and
1088 * thus can and will be used by other apache instances without
1089 * the use of shared data regarding the processes serving the
1090 * requests. This can result in slightly unintuitive process
1091 * counts and security implications. This is prevented
1092 * if suexec (Unix) is in use. This is both a feature and a flaw.
1093 * Changing it now would break existing installations. */
1096 /* Create socket file's path */
1097 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, execName
, user
, group
);
1098 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1100 /* Create sockaddr, prealloc it so it won't get created in tp */
1101 s
->socket_addr
= ap_pcalloc(sp
, sizeof(struct sockaddr_un
));
1102 err
= fcgi_util_socket_make_domain_addr(tp
, (struct sockaddr_un
**)&s
->socket_addr
,
1103 &s
->socket_addr_len
, s
->socket_path
);
1105 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1106 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
1110 if (init_listen_sock(s
)) {
1114 /* If a wrapper is being used, config user/group info */
1116 if (user
[0] == '~') {
1117 /* its a user dir uri, the rest is a username, not a uid */
1118 struct passwd
*pw
= getpwnam(&user
[1]);
1121 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1122 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1123 execName
, &user
[1]);
1126 s
->uid
= pw
->pw_uid
;
1127 s
->user
= ap_pstrdup(sp
, user
);
1128 s
->username
= s
->user
;
1130 s
->gid
= pw
->pw_gid
;
1131 s
->group
= ap_psprintf(sp
, "%ld", (long)s
->gid
);
1136 s
->uid
= (uid_t
)atol(user
);
1137 pw
= getpwuid(s
->uid
);
1139 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1140 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1141 execName
, (long)s
->uid
);
1144 s
->user
= ap_pstrdup(sp
, user
);
1145 s
->username
= ap_pstrdup(sp
, pw
->pw_name
);
1147 s
->gid
= (gid_t
)atol(group
);
1148 s
->group
= ap_pstrdup(sp
, group
);
1152 /* Create socket file's path */
1153 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, cjob
->fs_path
, cjob
->user
, cjob
->group
);
1154 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1158 fcgi_util_fs_add(s
);
1162 if (opcode
== FCGI_SERVER_RESTART_JOB
) {
1164 if (cjob
->id
==FCGI_SERVER_RESTART_JOB
) {
1166 /* Check to see if the binary has changed. If so,
1167 * kill the FCGI application processes, and
1173 char * app_path
= cjob
->fs_path
;
1175 char * app_path
= execName
;
1178 if (stat(app_path
, &stbuf
) == 0 && stbuf
.st_mtime
> s
->startTime
)
1182 /* prevent addition restart requests */
1185 utime(s
->socket_path
, NULL
);
1188 /* kill old server(s) */
1189 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1191 if (s
->procs
[i
].pid
> 0
1192 && stbuf
.st_mtime
> s
->procs
[i
].start_time
)
1194 fcgi_kill(&s
->procs
[i
], SIGTERM
);
1201 ap_log_error(FCGI_LOG_WARN_NOERRNO
,
1202 fcgi_apache_main_server
, "FastCGI: restarting "
1203 "old server \"%s\" processes, newer version "
1208 /* If dynamicAutoRestart, don't mark any new processes
1209 * for starting because we probably got the
1210 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1211 * will be restarting all of those we just killed.
1213 if (dynamicAutoRestart
)
1217 else if (opcode
== FCGI_SERVER_START_JOB
) {
1219 else if (cjob
->id
==FCGI_SERVER_START_JOB
) {
1221 /* we've been asked to start a process--only start
1222 * it if we're not already running at least one
1227 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1228 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1231 /* if already running, don't start another one */
1232 if (i
< dynamicMaxClassProcs
) {
1246 case FCGI_SERVER_RESTART_JOB
:
1250 /* We just waxed 'em all. Try to find an idle slot. */
1252 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1254 if (s
->procs
[i
].state
== FCGI_START_STATE
1255 || s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1259 else if (s
->procs
[i
].state
== FCGI_KILLED_STATE
1260 || s
->procs
[i
].state
== FCGI_READY_STATE
)
1267 /* Nope, just use the first slot */
1268 if (i
== dynamicMaxClassProcs
)
1276 schedule_start(s
, i
);
1281 case FCGI_SERVER_START_JOB
:
1282 case FCGI_REQUEST_TIMEOUT_JOB
:
1284 if ((fcgi_dynamic_total_proc_count
+ 1) > (int) dynamicMaxProcs
) {
1286 * Extra instances should have been
1287 * terminated beforehand, probably need
1288 * to increase ProcessSlack parameter
1290 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1291 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1292 "exceeded dynamicMaxProcs (%d)", s
->fs_path
, dynamicMaxProcs
);
1296 /* find next free slot */
1297 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1299 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1301 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i
);
1304 else if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1309 schedule_start(s
, i
);
1314 if (i
>= dynamicMaxClassProcs
) {
1315 FCGIDBG1("ignore_job: slots are max'd");
1319 case FCGI_REQUEST_COMPLETE_JOB
:
1320 /* only record stats if we have a structure */
1323 s
->totalConnTime
+= req_usec
;
1324 s
->totalQueueTime
+= q_usec
;
1326 s
->totalConnTime
+= cjob
->start_time
;
1327 s
->totalQueueTime
+= cjob
->qsec
;
1336 /* Cleanup job data */
1337 free(cjob
->fs_path
);
1347 if (sp
) ap_destroy_pool(sp
);
1350 free(cjob
->fs_path
);
1358 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1359 "FastCGI: really bogus message: \"%s\"", ptr1
);
1360 ptr1
+= strlen(buf
);
1363 buflen
-= ptr1
- buf
;
1365 memmove(buf
, ptr1
, buflen
);
1369 ap_destroy_pool(tp
);
1373 *----------------------------------------------------------------------
1375 * dynamic_kill_idle_fs_procs
1377 * Implement a kill policy for the dynamic FastCGI applications.
1378 * We also update the data structures to reflect the changes.
1381 * Processes are marked for deletion possibly killed.
1383 *----------------------------------------------------------------------
1385 static void dynamic_kill_idle_fs_procs(void)
1390 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1393 * server's smoothed running time, or if that's 0, the current total
1395 unsigned long connTime
;
1398 * maximum number of microseconds that all of a server's running
1399 * processes together could have spent running since the last check
1401 unsigned long totalTime
;
1404 * percentage, 0-100, of totalTime that the processes actually used
1409 int really_running
= 0;
1411 if (s
->directive
!= APP_CLASS_DYNAMIC
|| s
->numProcesses
== 0)
1416 /* s->numProcesses includes pending kills so get the "active" count */
1417 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1419 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
) ++really_running
;
1422 connTime
= s
->smoothConnTime
? s
->smoothConnTime
: s
->totalConnTime
;
1423 totalTime
= really_running
* (now
- fcgi_dynamic_epoch
) * 1000000 + 1;
1425 loadFactor
= 100 * connTime
/ totalTime
;
1427 if (really_running
== 1)
1429 if (loadFactor
>= dynamicThreshold1
)
1436 int load
= really_running
/ ( really_running
- 1) * loadFactor
;
1438 if (load
>= dynamicThresholdN
)
1445 * Run through the procs to see if we can get away w/o waxing one.
1447 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1449 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1451 s
->procs
[i
].state
= FCGI_READY_STATE
;
1454 else if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
)
1460 if (i
>= dynamicMaxClassProcs
)
1462 ServerProcess
* procs
= s
->procs
;
1465 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1467 if (procs
[i
].state
== FCGI_RUNNING_STATE
)
1469 if (youngest
== -1 || procs
[i
].start_time
>= procs
[youngest
].start_time
)
1478 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1479 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1480 s
->fs_path
, (long) s
->procs
[youngest
].pid
);
1482 fcgi_kill(&s
->procs
[youngest
], SIGTERM
);
1488 * If the number of non-victims is less than or equal to
1489 * the minimum that may be running without being killed off,
1490 * don't select any more victims.
1492 if (fcgi_dynamic_total_proc_count
- victims
<= dynamicMinProcs
)
1502 /* This is a little bogus, there's gotta be a better way to do this
1503 * Can we use WaitForMultipleObjects() */
1504 #define FCGI_PROC_WAIT_TIME 100
1506 void child_wait_thread_main(void *dummy
) {
1508 DWORD dwRet
= WAIT_TIMEOUT
;
1513 while (!bTimeToDie
) {
1516 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1517 if (s
->directive
== APP_CLASS_EXTERNAL
|| s
->listenFd
< 0) {
1520 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1521 numChildren
= dynamicMaxClassProcs
;
1524 numChildren
= s
->numProcesses
;
1527 for (i
=0; i
< numChildren
; i
++) {
1528 if (s
->procs
[i
].handle
!= INVALID_HANDLE_VALUE
)
1530 DWORD exitStatus
= 0;
1532 /* timeout is currently set for 100 miliecond */
1533 /* it may need to be longer or user customizable */
1534 dwRet
= WaitForSingleObject(s
->procs
[i
].handle
, FCGI_PROC_WAIT_TIME
);
1538 if (dwRet
!= WAIT_TIMEOUT
&& dwRet
!= WAIT_FAILED
) {
1539 /* a child fs has died */
1540 /* mark the child as dead */
1542 GetExitCodeProcess(s
->procs
[i
].handle
, &exitStatus
);
1544 if (s
->directive
== APP_CLASS_STANDARD
) {
1545 /* restart static app */
1546 s
->procs
[i
].state
= FCGI_START_STATE
;
1547 if (exitStatus
!= 0) {
1548 /* don't bump failure count on exit 0 */
1554 fcgi_dynamic_total_proc_count
--;
1555 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1557 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1558 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1561 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1562 if (exitStatus
!= 0) {
1563 /* don't bump failure count on exit 0 */
1567 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0)) {
1568 s
->procs
[i
].state
= FCGI_START_STATE
;
1571 s
->procs
[i
].state
= FCGI_READY_STATE
;
1576 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1577 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1578 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1579 s
->fs_path
, (long) s
->procs
[i
].pid
, exitStatus
);
1581 CloseHandle(s
->procs
[i
].handle
);
1582 CloseHandle(s
->procs
[i
].terminationEvent
);
1583 s
->procs
[i
].handle
= INVALID_HANDLE_VALUE
;
1584 s
->procs
[i
].terminationEvent
= INVALID_HANDLE_VALUE
;
1585 s
->procs
[i
].pid
= -1;
1587 /* wake up the main thread */
1588 SetEvent(fcgi_event_handles
[WAKE_EVENT
]);
1593 Sleep(waited
? 0 : FCGI_PROC_WAIT_TIME
);
1599 static void setup_signals(void)
1601 struct sigaction sa
;
1603 /* Setup handlers */
1605 sa
.sa_handler
= signal_handler
;
1606 sigemptyset(&sa
.sa_mask
);
1609 if (sigaction(SIGTERM
, &sa
, NULL
) < 0) {
1610 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1611 "sigaction(SIGTERM) failed");
1614 if (sigaction(SIGHUP
, &sa
, NULL
) < 0) {
1615 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1616 "sigaction(SIGHUP) failed");
1618 /* httpd graceful restart */
1619 if (sigaction(SIGUSR1
, &sa
, NULL
) < 0) {
1620 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1621 "sigaction(SIGUSR1) failed");
1623 /* read messages from request handlers - kill interval expired */
1624 if (sigaction(SIGALRM
, &sa
, NULL
) < 0) {
1625 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1626 "sigaction(SIGALRM) failed");
1628 if (sigaction(SIGCHLD
, &sa
, NULL
) < 0) {
1629 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1630 "sigaction(SIGCHLD) failed");
1635 #if !defined(WIN32) && !defined(APACHE2)
1636 int fcgi_pm_main(void *dummy
, child_info
*info
)
1638 void fcgi_pm_main(void *dummy
)
1648 HANDLE child_wait_thread
= INVALID_HANDLE_VALUE
;
1650 int callWaitPid
, callDynamicProcs
;
1654 /* Add SystemRoot to the dynamic environment */
1655 char ** envp
= dynamicEnvp
;
1656 for (i
= 0; *envp
; ++i
) {
1659 fcgi_config_set_env_var(fcgi_config_pool
, dynamicEnvp
, &i
, "SystemRoot");
1663 reduce_privileges();
1664 change_process_name("fcgi-pm");
1666 close(fcgi_pm_pipe
[1]);
1670 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1671 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper
);
1675 /* Initialize AppClass */
1676 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1678 if (s
->directive
!= APP_CLASS_STANDARD
)
1686 for (i
= 0; i
< s
->numProcesses
; ++i
)
1687 s
->procs
[i
].state
= FCGI_START_STATE
;
1691 child_wait_thread
= (HANDLE
) _beginthread(child_wait_thread_main
, 0, NULL
);
1693 if (child_wait_thread
== (HANDLE
) -1)
1695 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1696 "FastCGI: failed to create process manager's wait thread!");
1699 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1700 "FastCGI: process manager initialized");
1702 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1703 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1709 * Loop until SIGTERM
1712 int sleepSeconds
= min(dynamicKillInterval
, dynamicUpdateInterval
);
1719 unsigned int numChildren
;
1720 unsigned int minServerLife
;
1723 * If we came out of sigsuspend() for any reason other than
1724 * SIGALRM, pick up where we left off.
1727 sleepSeconds
= alarmLeft
;
1730 * Examine each configured AppClass for a process that needs
1731 * starting. Compute the earliest time when the start should
1732 * be attempted, starting it now if the time has passed. Also,
1733 * remember that we do NOT need to restart externally managed
1734 * FastCGI applications.
1736 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1738 if (s
->directive
== APP_CLASS_EXTERNAL
)
1741 numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
1742 ? dynamicMaxClassProcs
1745 minServerLife
= (s
->directive
== APP_CLASS_DYNAMIC
)
1746 ? dynamicMinServerLife
1749 for (i
= 0; i
< numChildren
; ++i
)
1751 if (s
->procs
[i
].pid
<= 0 && s
->procs
[i
].state
== FCGI_START_STATE
)
1753 int restart
= (s
->procs
[i
].pid
< 0);
1754 time_t restartTime
= s
->restartTime
;
1758 /* we've gone to using the badDelay, the only thing that
1759 resets bad is when badDelay has expired. but numFailures
1760 is only just set below its threshold. the proc's
1761 start_times are all reset when the bad is. the numFailures
1762 is reset when we see an app run for a period */
1764 s
->procs
[i
].start_time
= 0;
1767 if (s
->numFailures
> MAX_FAILED_STARTS
)
1769 time_t last_start_time
= s
->procs
[i
].start_time
;
1771 if (last_start_time
&& now
- last_start_time
> minServerLife
)
1775 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1776 "FastCGI:%s server \"%s\" has remained"
1777 " running for more than %d seconds, its restart"
1778 " interval has been restored to %d seconds",
1779 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1780 s
->fs_path
, minServerLife
, s
->restartDelay
);
1786 for (j
= 0; j
< numChildren
; ++j
)
1788 if (s
->procs
[j
].pid
<= 0) continue;
1789 if (s
->procs
[j
].state
!= FCGI_RUNNING_STATE
) continue;
1790 if (s
->procs
[j
].start_time
== 0) continue;
1791 if (now
- s
->procs
[j
].start_time
> minServerLife
) break;
1794 if (j
>= numChildren
)
1797 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1798 "FastCGI:%s server \"%s\" has failed to remain"
1799 " running for %d seconds given %d attempts, its restart"
1800 " interval has been backed off to %d seconds",
1801 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1802 s
->fs_path
, minServerLife
, MAX_FAILED_STARTS
,
1803 FAILED_STARTS_DELAY
);
1809 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1810 "FastCGI:%s server \"%s\" has remained"
1811 " running for more than %d seconds, its restart"
1812 " interval has been restored to %d seconds",
1813 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1814 s
->fs_path
, minServerLife
, s
->restartDelay
);
1821 restartTime
+= FAILED_STARTS_DELAY
;
1825 restartTime
+= (restart
) ? s
->restartDelay
: s
->initStartDelay
;
1828 if (restartTime
<= now
)
1833 s
->numFailures
= MAX_FAILED_STARTS
;
1836 if (s
->listenFd
< 0 && init_listen_sock(s
))
1838 if (sleepSeconds
> s
->initStartDelay
)
1839 sleepSeconds
= s
->initStartDelay
;
1843 if (caughtSigTerm
) {
1844 goto ProcessSigTerm
;
1847 s
->procs
[i
].pid
= spawn_fs_process(s
, &s
->procs
[i
]);
1848 if (s
->procs
[i
].pid
<= 0) {
1849 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1850 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1851 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1854 sleepSeconds
= min(sleepSeconds
,
1855 max((int) s
->restartDelay
, FCGI_MIN_EXEC_RETRY_DELAY
));
1857 s
->procs
[i
].pid
= -1;
1861 s
->procs
[i
].start_time
= now
;
1862 s
->restartTime
= now
;
1864 if (s
->startTime
== 0) {
1868 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1870 fcgi_dynamic_total_proc_count
++;
1871 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1874 s
->procs
[i
].state
= FCGI_RUNNING_STATE
;
1877 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1878 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1879 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1880 s
->fs_path
, (long) s
->uid
, (long) s
->gid
,
1881 restart
? "re" : "", (long) s
->procs
[i
].pid
);
1884 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1885 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1886 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1887 s
->fs_path
, restart
? "re" : "", (long) s
->procs
[i
].pid
);
1889 ap_assert(s
->procs
[i
].pid
> 0);
1891 sleepSeconds
= min(sleepSeconds
, restartTime
- now
);
1900 goto ProcessSigTerm
;
1902 if((!caughtSigChld
) && (!caughtSigAlarm
)) {
1905 alarm(sleepSeconds
);
1908 FD_SET(fcgi_pm_pipe
[0], &rfds
);
1909 read_ready
= ap_select(fcgi_pm_pipe
[0] + 1, &rfds
, NULL
, NULL
, NULL
);
1911 alarmLeft
= alarm(0);
1913 callWaitPid
= caughtSigChld
;
1914 caughtSigChld
= FALSE
;
1915 callDynamicProcs
= caughtSigAlarm
;
1916 caughtSigAlarm
= FALSE
;
1921 * Dynamic fcgi process management
1923 if((callDynamicProcs
) || (!callWaitPid
)) {
1924 dynamic_read_msgs(read_ready
);
1925 if(fcgi_dynamic_epoch
== 0) {
1926 fcgi_dynamic_epoch
= now
;
1928 if(((long)(now
-fcgi_dynamic_epoch
)>=dynamicKillInterval
) ||
1929 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
)>=dynamicMaxProcs
)) {
1930 dynamic_kill_idle_fs_procs();
1931 fcgi_dynamic_epoch
= now
;
1939 /* We've caught SIGCHLD, so find out who it was using waitpid,
1940 * write a log message and update its data structure. */
1944 goto ProcessSigTerm
;
1946 childPid
= waitpid(-1, &waitStatus
, WNOHANG
);
1948 if (childPid
== -1 || childPid
== 0)
1951 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1952 if (s
->directive
== APP_CLASS_EXTERNAL
)
1955 if (s
->directive
== APP_CLASS_DYNAMIC
)
1956 numChildren
= dynamicMaxClassProcs
;
1958 numChildren
= s
->numProcesses
;
1960 for (i
= 0; i
< numChildren
; i
++) {
1961 if (s
->procs
[i
].pid
== childPid
)
1966 /* TODO: print something about this unknown child */
1970 s
->procs
[i
].pid
= -1;
1972 if (s
->directive
== APP_CLASS_STANDARD
) {
1973 /* Always restart static apps */
1974 s
->procs
[i
].state
= FCGI_START_STATE
;
1975 if (! (WIFEXITED(waitStatus
) && (WEXITSTATUS(waitStatus
) == 0))) {
1976 /* don't bump the failure count if the app exited with 0 */
1982 fcgi_dynamic_total_proc_count
--;
1984 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1985 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1988 /* A dynamic app died or exited without provocation from the PM */
1990 if (! (WIFEXITED(waitStatus
) && (WEXITSTATUS(waitStatus
) == 0))) {
1991 /* don't bump the failure count if the app exited with 0 */
1995 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0))
1996 s
->procs
[i
].state
= FCGI_START_STATE
;
1998 s
->procs
[i
].state
= FCGI_READY_STATE
;
2002 if (WIFEXITED(waitStatus
)) {
2003 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
2004 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
2005 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
2006 s
->fs_path
, (long) childPid
, WEXITSTATUS(waitStatus
));
2008 else if (WIFSIGNALED(waitStatus
)) {
2009 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
2010 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
2011 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
2012 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
),
2014 WCOREDUMP(waitStatus
) ? ", a core file may have been generated" : "");
2019 else if (WIFSTOPPED(waitStatus
)) {
2020 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
2021 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
2022 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
2023 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
));
2025 } /* for (;;), waitpid() */
2029 /* wait for an event to occur or timer expires */
2030 expire
= time(NULL
) + sleepSeconds
;
2031 dwRet
= WaitForMultipleObjects(3, (HANDLE
*) fcgi_event_handles
, FALSE
, sleepSeconds
* 1000);
2033 if (dwRet
== WAIT_FAILED
) {
2034 /* There is something seriously wrong here */
2035 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
2036 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
2040 if (dwRet
!= WAIT_TIMEOUT
) {
2044 alarmLeft
= expire
- now
;
2048 * Dynamic fcgi process management
2050 if ((dwRet
== MBOX_EVENT
) || (dwRet
== WAIT_TIMEOUT
)) {
2051 if (dwRet
== MBOX_EVENT
) {
2057 dynamic_read_msgs(read_ready
);
2059 if(fcgi_dynamic_epoch
== 0) {
2060 fcgi_dynamic_epoch
= now
;
2063 if ((now
-fcgi_dynamic_epoch
>= (int) dynamicKillInterval
) ||
2064 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
) >= dynamicMaxProcs
)) {
2065 dynamic_kill_idle_fs_procs();
2066 fcgi_dynamic_epoch
= now
;
2070 else if (dwRet
== WAKE_EVENT
) {
2073 else if (dwRet
== TERM_EVENT
) {
2074 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
2075 "FastCGI: Termination event received process manager shutting down");
2078 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
2080 goto ProcessSigTerm
;
2083 /* Have an received an unknown event - should not happen */
2084 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
2085 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
2088 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
2090 goto ProcessSigTerm
;
2095 } /* for (;;), the whole shoot'n match */
2099 * Kill off the children, then exit.
2111 int fcgi_pm_add_job(fcgi_pm_job
*new_job
)
2113 int rv
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
2115 if (rv
!= WAIT_OBJECT_0
&& rv
!= WAIT_ABANDONED
)
2117 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2118 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2122 new_job
->next
= fcgi_dynamic_mbox
;
2123 fcgi_dynamic_mbox
= new_job
;
2125 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
2127 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2128 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");