2 * $Id: fcgi_pm.c,v 1.89 2003/10/30 01:08:34 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 /* AP13 does not use suexec if the target uid/gid is the same as the
416 * server's - AP20 does. I (now) consider the AP2 approach better
417 * (fcgi_pm.c v1.42 incorporated the 1.3 behaviour, v1.84 reverted it,
418 * v1.85 added the compile time option to use the old behaviour). */
420 #ifdef NO_SUEXEC_FOR_AP_USER_N_GROUP
422 if (fcgi_user_id
== fs
->uid
&& fcgi_group_id
== fs
->gid
)
428 shortName
= strrchr(fs
->fs_path
, '/') + 1;
431 execle(fcgi_wrapper
, fcgi_wrapper
, fs
->username
, fs
->group
,
432 shortName
, NULL
, fs
->envp
);
433 } while (errno
== EINTR
);
440 execle(fs
->fs_path
, fs
->fs_path
, NULL
, fs
->envp
);
441 } while (errno
== EINTR
);
444 failedSysCall
= "execle()";
446 FailedSystemCallExit
:
447 fprintf(stderr
, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
448 fs
->fs_path
, (long) getpid(), failedSysCall
, strerror(errno
));
451 /* avoid an irrelevant compiler warning */
458 /* based on mod_cgi.c:run_cgi_child() */
461 char * termination_env_string
;
462 HANDLE listen_handle
= INVALID_HANDLE_VALUE
;
463 apr_procattr_t
* procattr
;
464 apr_proc_t proc
= { 0 };
467 cgi_exec_info_t e_info
= { 0 };
468 request_rec r
= { 0 };
472 APR_OPTIONAL_FN_TYPE(ap_cgi_build_command
) *cgi_build_command
;
474 cgi_build_command
= APR_RETRIEVE_OPTIONAL_FN(ap_cgi_build_command
);
475 if (cgi_build_command
== NULL
)
477 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
478 "FastCGI: can't exec server \"%s\", mod_cgi isn't loaded",
483 if (apr_pool_create(&tp
, fcgi_config_pool
))
486 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
487 if (process
->terminationEvent
== NULL
)
490 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
492 termination_env_string
= ap_psprintf(tp
,
493 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
495 while (fs
->envp
[i
]) i
++;
496 fs
->envp
[i
++] = termination_env_string
;
497 fs
->envp
[i
] = (char *) fs
->mutex_env_string
;
499 ap_assert(fs
->envp
[i
+ 1] == NULL
);
503 SECURITY_ATTRIBUTES sa
= { 0 };
505 sa
.bInheritHandle
= TRUE
;
506 sa
.nLength
= sizeof(sa
);
508 listen_handle
= CreateNamedPipe(fs
->socket_path
,
510 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
511 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
513 if (listen_handle
== INVALID_HANDLE_VALUE
)
515 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
516 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
523 listen_handle
= (HANDLE
) fs
->listenFd
;
526 r
.per_dir_config
= fcgi_apache_main_server
->lookup_defaults
;
527 r
.server
= fcgi_apache_main_server
;
528 r
.filename
= (char *) fs
->fs_path
;
530 r
.subprocess_env
= apr_table_make(tp
, 0);
532 e_info
.cmd_type
= APR_PROGRAM
;
534 rv
= cgi_build_command(&command
, &argv
, &r
, tp
, &e_info
);
535 if (rv
!= APR_SUCCESS
)
537 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
538 "FastCGI: don't know how to spawn cmd child process: %s",
543 if (apr_procattr_create(&procattr
, tp
))
546 if (apr_procattr_dir_set(procattr
, ap_make_dirstr_parent(tp
, fs
->fs_path
)))
549 if (apr_procattr_cmdtype_set(procattr
, e_info
.cmd_type
))
552 if (apr_procattr_detach_set(procattr
, 1))
555 if (apr_os_file_put(&file
, &listen_handle
, 0, tp
))
558 /* procattr is opaque so we have to use this - unfortuantely it dups */
559 if (apr_procattr_child_in_set(procattr
, file
, NULL
))
562 if (apr_proc_create(&proc
, command
, argv
, fs
->envp
, procattr
, tp
))
565 process
->handle
= proc
.hproc
;
569 if (fs
->socket_path
&& listen_handle
!= INVALID_HANDLE_VALUE
)
571 CloseHandle(listen_handle
);
576 fs
->envp
[i
- 1] = NULL
;
583 #else /* WIN32 && !APACHE2 */
585 /* Adapted from Apache's util_script.c ap_call_exec() */
586 char *interpreter
= NULL
;
587 char *quoted_filename
;
589 char *pEnvBlock
, *pNext
;
592 int iEnvBlockLen
= 1;
594 file_type_e fileType
;
597 PROCESS_INFORMATION pi
;
602 pool
* tp
= ap_make_sub_pool(fcgi_config_pool
);
604 HANDLE listen_handle
= INVALID_HANDLE_VALUE
;
605 char * termination_env_string
= NULL
;
607 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
608 if (process
->terminationEvent
== NULL
)
610 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
611 "FastCGI: can't create termination event for server \"%s\", "
612 "CreateEvent() failed", fs
->fs_path
);
615 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
617 termination_env_string
= ap_psprintf(tp
,
618 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
622 SECURITY_ATTRIBUTES sa
;
624 sa
.lpSecurityDescriptor
= NULL
;
625 sa
.bInheritHandle
= TRUE
;
626 sa
.nLength
= sizeof(sa
);
628 listen_handle
= CreateNamedPipe(fs
->socket_path
,
630 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
631 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
633 if (listen_handle
== INVALID_HANDLE_VALUE
)
635 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
636 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs
->fs_path
);
642 listen_handle
= (HANDLE
) fs
->listenFd
;
645 memset(&si
, 0, sizeof(si
));
646 memset(&pi
, 0, sizeof(pi
));
647 memset(&r
, 0, sizeof(r
));
649 /* Can up a fake request to pass to ap_get_win32_interpreter() */
650 r
.per_dir_config
= fcgi_apache_main_server
->lookup_defaults
;
651 r
.server
= fcgi_apache_main_server
;
652 r
.filename
= (char *) fs
->fs_path
;
655 fileType
= ap_get_win32_interpreter(&r
, &interpreter
);
657 if (fileType
== eFileTypeUNKNOWN
) {
658 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
659 "FastCGI: %s is not executable; ensure interpreted scripts have "
660 "\"#!\" as their first line",
667 * We have the interpreter (if there is one) and we have
668 * the arguments (if there are any).
669 * Build the command string to pass to CreateProcess.
671 quoted_filename
= ap_pstrcat(tp
, "\"", fs
->fs_path
, "\"", NULL
);
672 if (interpreter
&& *interpreter
) {
673 pCommand
= ap_pstrcat(tp
, interpreter
, " ", quoted_filename
, NULL
);
676 pCommand
= quoted_filename
;
680 * Make child process use hPipeOutputWrite as standard out,
681 * and make sure it does not show on screen.
684 si
.dwFlags
= STARTF_USESHOWWINDOW
| STARTF_USESTDHANDLES
;
685 si
.wShowWindow
= SW_HIDE
;
686 si
.hStdInput
= listen_handle
;
688 /* XXX These should be open to the error_log */
689 si
.hStdOutput
= INVALID_HANDLE_VALUE
;
690 si
.hStdError
= INVALID_HANDLE_VALUE
;
693 * Win32's CreateProcess call requires that the environment
694 * be passed in an environment block, a null terminated block of
695 * null terminated strings.
696 * @todo we should store the env in this format for win32.
700 iEnvBlockLen
+= strlen(fs
->envp
[i
]) + 1;
704 iEnvBlockLen
+= strlen(termination_env_string
) + 1;
705 iEnvBlockLen
+= strlen(fs
->mutex_env_string
) + 1;
707 pEnvBlock
= (char *) ap_pcalloc(tp
, iEnvBlockLen
);
713 strcpy(pNext
, fs
->envp
[i
]);
714 pNext
+= strlen(pNext
) + 1;
718 strcpy(pNext
, termination_env_string
);
719 pNext
+= strlen(pNext
) + 1;
720 strcpy(pNext
, fs
->mutex_env_string
);
722 if (CreateProcess(NULL
, pCommand
, NULL
, NULL
, TRUE
,
725 ap_make_dirstr_parent(tp
, fs
->fs_path
),
728 /* Hack to get 16-bit CGI's working. It works for all the
729 * standard modules shipped with Apache. pi.dwProcessId is 0
730 * for 16-bit CGIs and all the Unix specific code that calls
731 * ap_call_exec interprets this as a failure case. And we can't
732 * use -1 either because it is mapped to 0 by the caller.
734 pid
= (fileType
== eFileTypeEXE16
) ? -2 : pi
.dwProcessId
;
736 process
->handle
= pi
.hProcess
;
737 CloseHandle(pi
.hThread
);
742 if (fs
->socket_path
&& listen_handle
!= INVALID_HANDLE_VALUE
)
744 CloseHandle(listen_handle
);
751 #endif /* !APACHE2 */
756 static void reduce_privileges(void)
764 /* Get username if passed as a uid */
765 if (ap_user_name
[0] == '#') {
766 uid_t uid
= atoi(&ap_user_name
[1]);
767 struct passwd
*ent
= getpwuid(uid
);
770 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
771 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
772 "you probably need to modify the User directive", (unsigned)uid
);
781 if (setgid(ap_group_id
) == -1) {
782 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
783 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id
);
787 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
789 /* Initialize supplementary groups */
790 if (initgroups(name
, ap_group_id
) == -1) {
791 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
792 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
793 name
, (unsigned)ap_group_id
);
800 if (seteuid_user() == -1) {
801 ap_log_error(FCGI_LOG_ALERT_NOERRNO
, fcgi_apache_main_server
,
802 "FastCGI: process manager exiting, failed to reduce privileges");
807 if (setuid(ap_user_id
) == -1) {
808 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
809 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id
);
816 * Change the name of this process - best we can easily.
818 static void change_process_name(const char * const name
)
820 /* under Apache2, ap_server_argv0 is const */
821 strncpy((char *) ap_server_argv0
, name
, strlen(ap_server_argv0
));
825 static void schedule_start(fcgi_server
*s
, int proc
)
827 /* If we've started one recently, don't register another */
828 time_t time_passed
= now
- s
->restartTime
;
830 if ((s
->procs
[proc
].pid
&& (time_passed
< (int) s
->restartDelay
))
831 || ((s
->procs
[proc
].pid
== 0) && (time_passed
< s
->initStartDelay
)))
833 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
);
837 FCGIDBG3("scheduling_start: %s (%d)", s
->fs_path
, proc
);
838 s
->procs
[proc
].state
= FCGI_START_STATE
;
839 if (proc
== dynamicMaxClassProcs
- 1) {
840 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
841 "FastCGI: scheduled the %sstart of the last (dynamic) server "
842 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
843 s
->procs
[proc
].pid
? "re" : "", s
->fs_path
, dynamicMaxClassProcs
);
848 *----------------------------------------------------------------------
852 * Removes the records written by request handlers and decodes them.
853 * We also update the data structures to reflect the changes.
855 *----------------------------------------------------------------------
858 static void dynamic_read_msgs(int read_ready
)
864 static int buflen
= 0;
865 static char buf
[FCGI_MSGS_BUFSIZE
+ 1];
866 char *ptr1
, *ptr2
, opcode
;
867 char execName
[FCGI_MAXPATH
+ 1];
868 char user
[MAX_USER_NAME_LEN
+ 2];
869 char group
[MAX_GID_CHAR_LEN
+ 1];
870 unsigned long q_usec
= 0UL, req_usec
= 0UL;
872 fcgi_pm_job
*joblist
= NULL
;
873 fcgi_pm_job
*cjob
= NULL
;
876 pool
*sp
= NULL
, *tp
;
879 user
[MAX_USER_NAME_LEN
+ 1] = group
[MAX_GID_CHAR_LEN
] = '\0';
883 * To prevent the idle application from running indefinitely, we
884 * check the timer and if it is expired, we recompute the values
885 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
886 * message is received, only updates are made to the data structures.
888 if (fcgi_dynamic_last_analyzed
== 0) {
889 fcgi_dynamic_last_analyzed
= now
;
891 if ((now
- fcgi_dynamic_last_analyzed
) >= (int)dynamicUpdateInterval
) {
892 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
893 if (s
->directive
!= APP_CLASS_DYNAMIC
)
896 /* Advance the last analyzed timestamp by the elapsed time since
897 * it was last set. Round the increase down to the nearest
898 * multiple of dynamicUpdateInterval */
900 fcgi_dynamic_last_analyzed
+= (((long)(now
-fcgi_dynamic_last_analyzed
)/dynamicUpdateInterval
)*dynamicUpdateInterval
);
901 s
->smoothConnTime
= (unsigned long) ((1.0-dynamicGain
)*s
->smoothConnTime
+ dynamicGain
*s
->totalConnTime
);
902 s
->totalConnTime
= 0UL;
903 s
->totalQueueTime
= 0UL;
907 if (read_ready
<= 0) {
912 rc
= read(fcgi_pm_pipe
[0], (void *)(buf
+ buflen
), FCGI_MSGS_BUFSIZE
- buflen
);
914 if (!caughtSigTerm
) {
915 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
916 "FastCGI: read() from pipe failed (%d)", rc
);
918 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
919 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
920 caughtSigTerm
= TRUE
;
930 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
931 * request to do something) and/or when a timeout expires.
932 * There really should be no reason why this wait would get stuck
933 * but there's no point in waiting forever. */
935 rc
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
937 if (rc
!= WAIT_OBJECT_0
&& rc
!= WAIT_ABANDONED
)
939 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
940 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
944 joblist
= fcgi_dynamic_mbox
;
945 fcgi_dynamic_mbox
= NULL
;
947 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
949 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
950 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
957 apr_pool_create(&tp
, fcgi_config_pool
);
959 tp
= ap_make_sub_pool(fcgi_config_pool
);
963 for (ptr1
= buf
; ptr1
; ptr1
= ptr2
) {
966 ptr2
= strchr(ptr1
, '*');
978 case FCGI_SERVER_START_JOB
:
979 case FCGI_SERVER_RESTART_JOB
:
981 if (sscanf(ptr1
, "%c %s %16s %15s",
982 &opcode
, execName
, user
, group
) != 4)
988 case FCGI_REQUEST_TIMEOUT_JOB
:
990 if (sscanf(ptr1
, "%c %s %16s %15s",
991 &opcode
, execName
, user
, group
) != 4)
997 case FCGI_REQUEST_COMPLETE_JOB
:
999 if (sscanf(ptr1
, "%c %s %16s %15s %lu %lu",
1000 &opcode
, execName
, user
, group
, &q_usec
, &req_usec
) != 6)
1012 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode
, execName
, user
, group
, q_usec
, req_usec
);
1015 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1016 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1
);
1020 /* Update data structures for processing */
1021 while (cjob
!= NULL
) {
1022 joblist
= cjob
->next
;
1023 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob
->id
, cjob
->fs_path
, cjob
->user
, cjob
->group
, cjob
->qsec
, cjob
->start_time
);
1027 s
= fcgi_util_fs_get(execName
, user
, group
);
1029 s
= fcgi_util_fs_get(cjob
->fs_path
, cjob
->user
, cjob
->group
);
1033 if (s
==NULL
&& opcode
!= FCGI_REQUEST_COMPLETE_JOB
)
1035 if (s
==NULL
&& cjob
->id
!= FCGI_REQUEST_COMPLETE_JOB
)
1040 HANDLE mutex
= CreateMutex(NULL
, FALSE
, cjob
->fs_path
);
1044 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1045 "FastCGI: can't create accept mutex "
1046 "for (dynamic) server \"%s\"", cjob
->fs_path
);
1050 SetHandleInformation(mutex
, HANDLE_FLAG_INHERIT
, TRUE
);
1055 /* Create a perm subpool to hold the new server data,
1056 * we can destroy it if something doesn't pan out */
1058 apr_pool_create(&sp
, fcgi_config_pool
);
1060 sp
= ap_make_sub_pool(fcgi_config_pool
);
1063 /* Create a new "dynamic" server */
1064 s
= fcgi_util_fs_new(sp
);
1066 s
->directive
= APP_CLASS_DYNAMIC
;
1067 s
->restartDelay
= dynamicRestartDelay
;
1068 s
->listenQueueDepth
= dynamicListenQueueDepth
;
1069 s
->initStartDelay
= dynamicInitStartDelay
;
1070 s
->envp
= dynamicEnvp
;
1071 s
->flush
= dynamicFlush
;
1074 s
->mutex_env_string
= ap_psprintf(sp
, "_FCGI_MUTEX_=%ld", mutex
);
1075 s
->fs_path
= ap_pstrdup(sp
, cjob
->fs_path
);
1077 s
->fs_path
= ap_pstrdup(sp
, execName
);
1079 ap_getparents(s
->fs_path
);
1080 ap_no2slash(s
->fs_path
);
1081 s
->procs
= fcgi_util_fs_create_procs(sp
, dynamicMaxClassProcs
);
1083 /* XXX the socket_path (both Unix and Win) *is* deducible and
1084 * thus can and will be used by other apache instances without
1085 * the use of shared data regarding the processes serving the
1086 * requests. This can result in slightly unintuitive process
1087 * counts and security implications. This is prevented
1088 * if suexec (Unix) is in use. This is both a feature and a flaw.
1089 * Changing it now would break existing installations. */
1092 /* Create socket file's path */
1093 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, execName
, user
, group
);
1094 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1096 /* Create sockaddr, prealloc it so it won't get created in tp */
1097 s
->socket_addr
= ap_pcalloc(sp
, sizeof(struct sockaddr_un
));
1098 err
= fcgi_util_socket_make_domain_addr(tp
, (struct sockaddr_un
**)&s
->socket_addr
,
1099 &s
->socket_addr_len
, s
->socket_path
);
1101 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1102 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
1106 if (init_listen_sock(s
)) {
1110 /* If a wrapper is being used, config user/group info */
1112 if (user
[0] == '~') {
1113 /* its a user dir uri, the rest is a username, not a uid */
1114 struct passwd
*pw
= getpwnam(&user
[1]);
1117 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1118 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1119 execName
, &user
[1]);
1122 s
->uid
= pw
->pw_uid
;
1123 s
->user
= ap_pstrdup(sp
, user
);
1124 s
->username
= s
->user
;
1126 s
->gid
= pw
->pw_gid
;
1127 s
->group
= ap_psprintf(sp
, "%ld", (long)s
->gid
);
1132 s
->uid
= (uid_t
)atol(user
);
1133 pw
= getpwuid(s
->uid
);
1135 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1136 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1137 execName
, (long)s
->uid
);
1140 s
->user
= ap_pstrdup(sp
, user
);
1141 s
->username
= ap_pstrdup(sp
, pw
->pw_name
);
1143 s
->gid
= (gid_t
)atol(group
);
1144 s
->group
= ap_pstrdup(sp
, group
);
1148 /* Create socket file's path */
1149 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, cjob
->fs_path
, cjob
->user
, cjob
->group
);
1150 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1154 fcgi_util_fs_add(s
);
1158 if (opcode
== FCGI_SERVER_RESTART_JOB
) {
1160 if (cjob
->id
==FCGI_SERVER_RESTART_JOB
) {
1162 /* Check to see if the binary has changed. If so,
1163 * kill the FCGI application processes, and
1169 char * app_path
= cjob
->fs_path
;
1171 char * app_path
= execName
;
1174 if (stat(app_path
, &stbuf
) == 0 && stbuf
.st_mtime
> s
->startTime
)
1178 /* prevent addition restart requests */
1181 utime(s
->socket_path
, NULL
);
1184 /* kill old server(s) */
1185 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1187 if (s
->procs
[i
].pid
> 0
1188 && stbuf
.st_mtime
> s
->procs
[i
].start_time
)
1190 fcgi_kill(&s
->procs
[i
], SIGTERM
);
1197 ap_log_error(FCGI_LOG_WARN_NOERRNO
,
1198 fcgi_apache_main_server
, "FastCGI: restarting "
1199 "old server \"%s\" processes, newer version "
1204 /* If dynamicAutoRestart, don't mark any new processes
1205 * for starting because we probably got the
1206 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1207 * will be restarting all of those we just killed.
1209 if (dynamicAutoRestart
)
1213 else if (opcode
== FCGI_SERVER_START_JOB
) {
1215 else if (cjob
->id
==FCGI_SERVER_START_JOB
) {
1217 /* we've been asked to start a process--only start
1218 * it if we're not already running at least one
1223 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1224 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1227 /* if already running, don't start another one */
1228 if (i
< dynamicMaxClassProcs
) {
1242 case FCGI_SERVER_RESTART_JOB
:
1246 /* We just waxed 'em all. Try to find an idle slot. */
1248 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1250 if (s
->procs
[i
].state
== FCGI_START_STATE
1251 || s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1255 else if (s
->procs
[i
].state
== FCGI_KILLED_STATE
1256 || s
->procs
[i
].state
== FCGI_READY_STATE
)
1263 /* Nope, just use the first slot */
1264 if (i
== dynamicMaxClassProcs
)
1272 schedule_start(s
, i
);
1277 case FCGI_SERVER_START_JOB
:
1278 case FCGI_REQUEST_TIMEOUT_JOB
:
1280 if ((fcgi_dynamic_total_proc_count
+ 1) > (int) dynamicMaxProcs
) {
1282 * Extra instances should have been
1283 * terminated beforehand, probably need
1284 * to increase ProcessSlack parameter
1286 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1287 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1288 "exceeded dynamicMaxProcs (%d)", s
->fs_path
, dynamicMaxProcs
);
1292 /* find next free slot */
1293 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1295 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1297 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i
);
1300 else if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1305 schedule_start(s
, i
);
1310 if (i
>= dynamicMaxClassProcs
) {
1311 FCGIDBG1("ignore_job: slots are max'd");
1315 case FCGI_REQUEST_COMPLETE_JOB
:
1316 /* only record stats if we have a structure */
1319 s
->totalConnTime
+= req_usec
;
1320 s
->totalQueueTime
+= q_usec
;
1322 s
->totalConnTime
+= cjob
->start_time
;
1323 s
->totalQueueTime
+= cjob
->qsec
;
1332 /* Cleanup job data */
1333 free(cjob
->fs_path
);
1343 if (sp
) ap_destroy_pool(sp
);
1346 free(cjob
->fs_path
);
1354 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1355 "FastCGI: really bogus message: \"%s\"", ptr1
);
1356 ptr1
+= strlen(buf
);
1359 buflen
-= ptr1
- buf
;
1361 memmove(buf
, ptr1
, buflen
);
1365 ap_destroy_pool(tp
);
1369 *----------------------------------------------------------------------
1371 * dynamic_kill_idle_fs_procs
1373 * Implement a kill policy for the dynamic FastCGI applications.
1374 * We also update the data structures to reflect the changes.
1377 * Processes are marked for deletion possibly killed.
1379 *----------------------------------------------------------------------
1381 static void dynamic_kill_idle_fs_procs(void)
1386 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1389 * server's smoothed running time, or if that's 0, the current total
1391 unsigned long connTime
;
1394 * maximum number of microseconds that all of a server's running
1395 * processes together could have spent running since the last check
1397 unsigned long totalTime
;
1400 * percentage, 0-100, of totalTime that the processes actually used
1405 int really_running
= 0;
1407 if (s
->directive
!= APP_CLASS_DYNAMIC
|| s
->numProcesses
== 0)
1412 /* s->numProcesses includes pending kills so get the "active" count */
1413 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1415 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
) ++really_running
;
1418 connTime
= s
->smoothConnTime
? s
->smoothConnTime
: s
->totalConnTime
;
1419 totalTime
= really_running
* (now
- fcgi_dynamic_epoch
) * 1000000 + 1;
1421 loadFactor
= 100 * connTime
/ totalTime
;
1423 if (really_running
== 1)
1425 if (loadFactor
>= dynamicThreshold1
)
1432 int load
= really_running
/ ( really_running
- 1) * loadFactor
;
1434 if (load
>= dynamicThresholdN
)
1441 * Run through the procs to see if we can get away w/o waxing one.
1443 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1445 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1447 s
->procs
[i
].state
= FCGI_READY_STATE
;
1450 else if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
)
1456 if (i
>= dynamicMaxClassProcs
)
1458 ServerProcess
* procs
= s
->procs
;
1461 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1463 if (procs
[i
].state
== FCGI_RUNNING_STATE
)
1465 if (youngest
== -1 || procs
[i
].start_time
>= procs
[youngest
].start_time
)
1474 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1475 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1476 s
->fs_path
, (long) s
->procs
[youngest
].pid
);
1478 fcgi_kill(&s
->procs
[youngest
], SIGTERM
);
1484 * If the number of non-victims is less than or equal to
1485 * the minimum that may be running without being killed off,
1486 * don't select any more victims.
1488 if (fcgi_dynamic_total_proc_count
- victims
<= dynamicMinProcs
)
1498 /* This is a little bogus, there's gotta be a better way to do this
1499 * Can we use WaitForMultipleObjects() */
1500 #define FCGI_PROC_WAIT_TIME 100
1502 void child_wait_thread_main(void *dummy
) {
1504 DWORD dwRet
= WAIT_TIMEOUT
;
1509 while (!bTimeToDie
) {
1512 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1513 if (s
->directive
== APP_CLASS_EXTERNAL
|| s
->listenFd
< 0) {
1516 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1517 numChildren
= dynamicMaxClassProcs
;
1520 numChildren
= s
->numProcesses
;
1523 for (i
=0; i
< numChildren
; i
++) {
1524 if (s
->procs
[i
].handle
!= INVALID_HANDLE_VALUE
)
1526 DWORD exitStatus
= 0;
1528 /* timeout is currently set for 100 miliecond */
1529 /* it may need to be longer or user customizable */
1530 dwRet
= WaitForSingleObject(s
->procs
[i
].handle
, FCGI_PROC_WAIT_TIME
);
1534 if (dwRet
!= WAIT_TIMEOUT
&& dwRet
!= WAIT_FAILED
) {
1535 /* a child fs has died */
1536 /* mark the child as dead */
1538 if (s
->directive
== APP_CLASS_STANDARD
) {
1539 /* restart static app */
1540 s
->procs
[i
].state
= FCGI_START_STATE
;
1545 fcgi_dynamic_total_proc_count
--;
1546 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1548 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1549 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1552 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1555 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0)) {
1556 s
->procs
[i
].state
= FCGI_START_STATE
;
1559 s
->procs
[i
].state
= FCGI_READY_STATE
;
1564 GetExitCodeProcess(s
->procs
[i
].handle
, &exitStatus
);
1566 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1567 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1568 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1569 s
->fs_path
, (long) s
->procs
[i
].pid
, exitStatus
);
1571 CloseHandle(s
->procs
[i
].handle
);
1572 CloseHandle(s
->procs
[i
].terminationEvent
);
1573 s
->procs
[i
].handle
= INVALID_HANDLE_VALUE
;
1574 s
->procs
[i
].terminationEvent
= INVALID_HANDLE_VALUE
;
1575 s
->procs
[i
].pid
= -1;
1577 /* wake up the main thread */
1578 SetEvent(fcgi_event_handles
[WAKE_EVENT
]);
1583 Sleep(waited
? 0 : FCGI_PROC_WAIT_TIME
);
1589 static void setup_signals(void)
1591 struct sigaction sa
;
1593 /* Setup handlers */
1595 sa
.sa_handler
= signal_handler
;
1596 sigemptyset(&sa
.sa_mask
);
1599 if (sigaction(SIGTERM
, &sa
, NULL
) < 0) {
1600 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1601 "sigaction(SIGTERM) failed");
1604 if (sigaction(SIGHUP
, &sa
, NULL
) < 0) {
1605 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1606 "sigaction(SIGHUP) failed");
1608 /* httpd graceful restart */
1609 if (sigaction(SIGUSR1
, &sa
, NULL
) < 0) {
1610 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1611 "sigaction(SIGUSR1) failed");
1613 /* read messages from request handlers - kill interval expired */
1614 if (sigaction(SIGALRM
, &sa
, NULL
) < 0) {
1615 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1616 "sigaction(SIGALRM) failed");
1618 if (sigaction(SIGCHLD
, &sa
, NULL
) < 0) {
1619 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1620 "sigaction(SIGCHLD) failed");
1625 #if !defined(WIN32) && !defined(APACHE2)
1626 int fcgi_pm_main(void *dummy
, child_info
*info
)
1628 void fcgi_pm_main(void *dummy
)
1638 HANDLE child_wait_thread
= INVALID_HANDLE_VALUE
;
1640 int callWaitPid
, callDynamicProcs
;
1644 /* Add SystemRoot to the dynamic environment */
1645 char ** envp
= dynamicEnvp
;
1646 for (i
= 0; *envp
; ++i
) {
1649 fcgi_config_set_env_var(fcgi_config_pool
, dynamicEnvp
, &i
, "SystemRoot");
1653 reduce_privileges();
1654 change_process_name("fcgi-pm");
1656 close(fcgi_pm_pipe
[1]);
1660 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1661 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper
);
1665 /* Initialize AppClass */
1666 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1668 if (s
->directive
!= APP_CLASS_STANDARD
)
1676 for (i
= 0; i
< s
->numProcesses
; ++i
)
1677 s
->procs
[i
].state
= FCGI_START_STATE
;
1681 child_wait_thread
= (HANDLE
) _beginthread(child_wait_thread_main
, 0, NULL
);
1683 if (child_wait_thread
== (HANDLE
) -1)
1685 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1686 "FastCGI: failed to create process manager's wait thread!");
1689 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1690 "FastCGI: process manager initialized");
1692 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1693 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1699 * Loop until SIGTERM
1702 int sleepSeconds
= min(dynamicKillInterval
, dynamicUpdateInterval
);
1709 unsigned int numChildren
;
1712 * If we came out of sigsuspend() for any reason other than
1713 * SIGALRM, pick up where we left off.
1716 sleepSeconds
= alarmLeft
;
1719 * Examine each configured AppClass for a process that needs
1720 * starting. Compute the earliest time when the start should
1721 * be attempted, starting it now if the time has passed. Also,
1722 * remember that we do NOT need to restart externally managed
1723 * FastCGI applications.
1725 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1727 if (s
->directive
== APP_CLASS_EXTERNAL
)
1730 numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
1731 ? dynamicMaxClassProcs
1734 for (i
= 0; i
< numChildren
; ++i
)
1736 if (s
->procs
[i
].pid
<= 0 && s
->procs
[i
].state
== FCGI_START_STATE
)
1738 int restart
= (s
->procs
[i
].pid
< 0);
1739 time_t restartTime
= s
->restartTime
;
1743 /* we've gone to using the badDelay, the only thing that
1744 resets bad is when badDelay has expired. but numFailures
1745 is only just set below its threshold. the proc's
1746 start_times are all reset when the bad is. the numFailures
1747 is reset when we see an app run for a period */
1749 s
->procs
[i
].start_time
= 0;
1752 if (s
->numFailures
> MAX_FAILED_STARTS
)
1754 time_t last_start_time
= s
->procs
[i
].start_time
;
1756 if (last_start_time
&& now
- last_start_time
> RUNTIME_SUCCESS_INTERVAL
)
1760 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1761 "FastCGI:%s server \"%s\" has remained"
1762 " running for more than %d seconds, its restart"
1763 " interval has been restored to %d seconds",
1764 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1765 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, s
->restartDelay
);
1771 for (j
= 0; j
< numChildren
; ++j
)
1773 if (s
->procs
[j
].pid
<= 0) continue;
1774 if (s
->procs
[j
].state
!= FCGI_RUNNING_STATE
) continue;
1775 if (s
->procs
[j
].start_time
== 0) continue;
1776 if (now
- s
->procs
[j
].start_time
> RUNTIME_SUCCESS_INTERVAL
) break;
1779 if (j
>= numChildren
)
1782 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1783 "FastCGI:%s server \"%s\" has failed to remain"
1784 " running for %d seconds given %d attempts, its restart"
1785 " interval has been backed off to %d seconds",
1786 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1787 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, MAX_FAILED_STARTS
,
1788 FAILED_STARTS_DELAY
);
1794 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1795 "FastCGI:%s server \"%s\" has remained"
1796 " running for more than %d seconds, its restart"
1797 " interval has been restored to %d seconds",
1798 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1799 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, s
->restartDelay
);
1806 restartTime
+= FAILED_STARTS_DELAY
;
1810 restartTime
+= (restart
) ? s
->restartDelay
: s
->initStartDelay
;
1813 if (restartTime
<= now
)
1818 s
->numFailures
= MAX_FAILED_STARTS
;
1821 if (s
->listenFd
< 0 && init_listen_sock(s
))
1823 if (sleepSeconds
> s
->initStartDelay
)
1824 sleepSeconds
= s
->initStartDelay
;
1828 if (caughtSigTerm
) {
1829 goto ProcessSigTerm
;
1832 s
->procs
[i
].pid
= spawn_fs_process(s
, &s
->procs
[i
]);
1833 if (s
->procs
[i
].pid
<= 0) {
1834 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1835 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1836 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1839 sleepSeconds
= min(sleepSeconds
,
1840 max((int) s
->restartDelay
, FCGI_MIN_EXEC_RETRY_DELAY
));
1842 s
->procs
[i
].pid
= -1;
1846 s
->procs
[i
].start_time
= now
;
1847 s
->restartTime
= now
;
1849 if (s
->startTime
== 0) {
1853 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1855 fcgi_dynamic_total_proc_count
++;
1856 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1859 s
->procs
[i
].state
= FCGI_RUNNING_STATE
;
1862 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1863 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1864 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1865 s
->fs_path
, (long) s
->uid
, (long) s
->gid
,
1866 restart
? "re" : "", (long) s
->procs
[i
].pid
);
1869 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1870 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1871 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1872 s
->fs_path
, restart
? "re" : "", (long) s
->procs
[i
].pid
);
1874 ap_assert(s
->procs
[i
].pid
> 0);
1876 sleepSeconds
= min(sleepSeconds
, restartTime
- now
);
1885 goto ProcessSigTerm
;
1887 if((!caughtSigChld
) && (!caughtSigAlarm
)) {
1890 alarm(sleepSeconds
);
1893 FD_SET(fcgi_pm_pipe
[0], &rfds
);
1894 read_ready
= ap_select(fcgi_pm_pipe
[0] + 1, &rfds
, NULL
, NULL
, NULL
);
1896 alarmLeft
= alarm(0);
1898 callWaitPid
= caughtSigChld
;
1899 caughtSigChld
= FALSE
;
1900 callDynamicProcs
= caughtSigAlarm
;
1901 caughtSigAlarm
= FALSE
;
1906 * Dynamic fcgi process management
1908 if((callDynamicProcs
) || (!callWaitPid
)) {
1909 dynamic_read_msgs(read_ready
);
1910 if(fcgi_dynamic_epoch
== 0) {
1911 fcgi_dynamic_epoch
= now
;
1913 if(((long)(now
-fcgi_dynamic_epoch
)>=dynamicKillInterval
) ||
1914 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
)>=dynamicMaxProcs
)) {
1915 dynamic_kill_idle_fs_procs();
1916 fcgi_dynamic_epoch
= now
;
1924 /* We've caught SIGCHLD, so find out who it was using waitpid,
1925 * write a log message and update its data structure. */
1929 goto ProcessSigTerm
;
1931 childPid
= waitpid(-1, &waitStatus
, WNOHANG
);
1933 if (childPid
== -1 || childPid
== 0)
1936 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1937 if (s
->directive
== APP_CLASS_EXTERNAL
)
1940 if (s
->directive
== APP_CLASS_DYNAMIC
)
1941 numChildren
= dynamicMaxClassProcs
;
1943 numChildren
= s
->numProcesses
;
1945 for (i
= 0; i
< numChildren
; i
++) {
1946 if (s
->procs
[i
].pid
== childPid
)
1951 /* TODO: print something about this unknown child */
1955 s
->procs
[i
].pid
= -1;
1957 if (s
->directive
== APP_CLASS_STANDARD
) {
1958 /* Always restart static apps */
1959 s
->procs
[i
].state
= FCGI_START_STATE
;
1964 fcgi_dynamic_total_proc_count
--;
1966 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1967 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1970 /* A dynamic app died or exited without provocation from the PM */
1973 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0))
1974 s
->procs
[i
].state
= FCGI_START_STATE
;
1976 s
->procs
[i
].state
= FCGI_READY_STATE
;
1980 if (WIFEXITED(waitStatus
)) {
1981 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1982 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1983 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1984 s
->fs_path
, (long) childPid
, WEXITSTATUS(waitStatus
));
1986 else if (WIFSIGNALED(waitStatus
)) {
1987 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1988 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1989 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1990 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
),
1992 WCOREDUMP(waitStatus
) ? ", a core file may have been generated" : "");
1997 else if (WIFSTOPPED(waitStatus
)) {
1998 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1999 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
2000 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
2001 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
));
2003 } /* for (;;), waitpid() */
2007 /* wait for an event to occur or timer expires */
2008 expire
= time(NULL
) + sleepSeconds
;
2009 dwRet
= WaitForMultipleObjects(3, (HANDLE
*) fcgi_event_handles
, FALSE
, sleepSeconds
* 1000);
2011 if (dwRet
== WAIT_FAILED
) {
2012 /* There is something seriously wrong here */
2013 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
2014 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
2018 if (dwRet
!= WAIT_TIMEOUT
) {
2022 alarmLeft
= expire
- now
;
2026 * Dynamic fcgi process management
2028 if ((dwRet
== MBOX_EVENT
) || (dwRet
== WAIT_TIMEOUT
)) {
2029 if (dwRet
== MBOX_EVENT
) {
2035 dynamic_read_msgs(read_ready
);
2037 if(fcgi_dynamic_epoch
== 0) {
2038 fcgi_dynamic_epoch
= now
;
2041 if ((now
-fcgi_dynamic_epoch
>= (int) dynamicKillInterval
) ||
2042 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
) >= dynamicMaxProcs
)) {
2043 dynamic_kill_idle_fs_procs();
2044 fcgi_dynamic_epoch
= now
;
2048 else if (dwRet
== WAKE_EVENT
) {
2051 else if (dwRet
== TERM_EVENT
) {
2052 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
2053 "FastCGI: Termination event received process manager shutting down");
2056 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
2058 goto ProcessSigTerm
;
2061 /* Have an received an unknown event - should not happen */
2062 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
2063 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
2066 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
2068 goto ProcessSigTerm
;
2073 } /* for (;;), the whole shoot'n match */
2077 * Kill off the children, then exit.
2089 int fcgi_pm_add_job(fcgi_pm_job
*new_job
)
2091 int rv
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
2093 if (rv
!= WAIT_OBJECT_0
&& rv
!= WAIT_ABANDONED
)
2095 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2096 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2100 new_job
->next
= fcgi_dynamic_mbox
;
2101 fcgi_dynamic_mbox
= new_job
;
2103 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
2105 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2106 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");