2 * $Id: fcgi_pm.c,v 1.78 2002/09/21 14:22:46 robs Exp $
8 #if defined(APACHE2) && !defined(WIN32)
15 #define seteuid(arg) setresuid(-1, (arg), -1)
18 int fcgi_dynamic_total_proc_count
= 0; /* number of running apps */
19 time_t fcgi_dynamic_epoch
= 0; /* last time kill_procs was
20 * invoked by process mgr */
21 time_t fcgi_dynamic_last_analyzed
= 0; /* last time calculation was
22 * made for the dynamic procs */
24 static time_t now
= 0;
27 #pragma warning ( disable : 4100 4102 )
28 static BOOL bTimeToDie
= FALSE
; /* process termination flag */
29 HANDLE fcgi_event_handles
[3];
37 static int seteuid_root(void)
39 int rc
= seteuid((uid_t
)0);
41 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
42 "FastCGI: seteuid(0) failed");
47 static int seteuid_user(void)
49 int rc
= seteuid(ap_user_id
);
51 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
52 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id
);
59 * Signal the process to exit. How (or if) the process responds
60 * depends on the FastCGI application library (esp. on Win32) and
61 * possibly application code (signal handlers and whether or not
62 * SA_RESTART is on). At any rate, we send the signal with the
63 * hopes that the process will exit on its own. Later, as we
64 * review the state of application processes, if we see one marked
65 * for death, but that hasn't died within a specified period of
66 * time, fcgi_kill() is called again with a KILL)
68 static void fcgi_kill(ServerProcess
*process
, int sig
)
70 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process
->pid
, sig
);
72 process
->state
= FCGI_VICTIM_STATE
;
78 SetEvent(process
->terminationEvent
);
80 else if (sig
== SIGKILL
)
82 TerminateProcess(process
->handle
, 1);
96 kill(process
->pid
, sig
);
106 /*******************************************************************************
107 * Send SIGTERM to each process in the server class, remove socket
108 * file if appropriate. Currently this is only called when the PM is shutting
109 * down and thus memory isn't freed and sockets and files aren't closed.
111 static void shutdown_all()
113 fcgi_server
*s
= fcgi_servers
;
117 ServerProcess
*proc
= s
->procs
;
119 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
120 ? dynamicMaxClassProcs
124 if (s
->socket_path
!= NULL
&& s
->directive
!= APP_CLASS_EXTERNAL
)
126 /* Remove the socket file */
127 if (unlink(s
->socket_path
) != 0 && errno
!= ENOENT
) {
128 ap_log_error(FCGI_LOG_ERR
, fcgi_apache_main_server
,
129 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
131 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "", s
->fs_path
);
136 /* Send TERM to all processes */
137 for (i
= 0; i
< numChildren
; i
++, proc
++)
139 if (proc
->state
== FCGI_RUNNING_STATE
)
141 fcgi_kill(proc
, SIGTERM
);
148 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
151 * WIN32 applications may not have support for the shutdown event
152 * depending on their application library version
155 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT
);
160 ServerProcess
*proc
= s
->procs
;
162 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
163 ? dynamicMaxClassProcs
166 /* Send KILL to all processes */
167 for (i
= 0; i
< numChildren
; i
++, proc
++)
169 if (proc
->state
== FCGI_RUNNING_STATE
)
171 fcgi_kill(proc
, SIGKILL
);
181 static int init_listen_sock(fcgi_server
* fs
)
183 ap_assert(fs
->directive
!= APP_CLASS_EXTERNAL
);
185 /* Create the socket */
186 if ((fs
->listenFd
= socket(fs
->socket_addr
->sa_family
, SOCK_STREAM
, 0)) < 0)
189 errno
= WSAGetLastError(); // Not sure if this will work as expected
191 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
192 "FastCGI: can't create %sserver \"%s\": socket() failed",
193 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
199 if (fs
->socket_addr
->sa_family
== AF_UNIX
)
201 /* Remove any existing socket file.. just in case */
202 unlink(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
);
208 setsockopt(fs
->listenFd
, SOL_SOCKET
, SO_REUSEADDR
, (char *)&flag
, sizeof(flag
));
211 /* Bind it to the socket_addr */
212 if (bind(fs
->listenFd
, fs
->socket_addr
, fs
->socket_addr_len
))
217 errno
= WSAGetLastError();
219 ap_snprintf(port
, sizeof(port
), "port=%d",
220 ((struct sockaddr_in
*)fs
->socket_addr
)->sin_port
);
222 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
223 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
224 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
227 (fs
->socket_addr
->sa_family
== AF_UNIX
) ?
228 ((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
:
234 /* Twiddle Unix socket permissions */
235 else if (fs
->socket_addr
->sa_family
== AF_UNIX
236 && chmod(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
, S_IRUSR
| S_IWUSR
))
238 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
239 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
240 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
246 else if (listen(fs
->listenFd
, fs
->listenQueueDepth
))
249 errno
= WSAGetLastError();
251 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
252 "FastCGI: can't create %sserver \"%s\": listen() failed",
253 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
267 *----------------------------------------------------------------------
271 * The FastCGI process manager, which runs as a separate
272 * process responsible for:
273 * - Starting all the FastCGI proceses.
274 * - Restarting any of these processes that die (indicated
276 * - Catching SIGTERM and relaying it to all the FastCGI
277 * processes before exiting.
280 * Uses global variable fcgi_servers.
288 *----------------------------------------------------------------------
291 static int caughtSigTerm
= FALSE
;
292 static int caughtSigChld
= FALSE
;
293 static int caughtSigAlarm
= FALSE
;
295 static void signal_handler(int signo
)
297 if ((signo
== SIGTERM
) || (signo
== SIGUSR1
) || (signo
== SIGHUP
)) {
298 /* SIGUSR1 & SIGHUP are sent by apache to its process group
299 * when apache get 'em. Apache follows up (1.2.x) with attacks
300 * on each of its child processes, but we've got the KillMgr
301 * sitting between us so we never see the KILL. The main loop
302 * in ProcMgr also checks to see if the KillMgr has terminated,
303 * and if it has, we handl it as if we should shutdown too. */
304 caughtSigTerm
= TRUE
;
305 } else if(signo
== SIGCHLD
) {
306 caughtSigChld
= TRUE
;
307 } else if(signo
== SIGALRM
) {
308 caughtSigAlarm
= TRUE
;
314 *----------------------------------------------------------------------
316 * spawn_fs_process --
318 * Fork and exec the specified fcgi process.
321 * 0 for successful fork, -1 for failed fork.
323 * In case the child fails before or in the exec, the child
324 * obtains the error log by calling getErrLog, logs
325 * the error, and exits with exit status = errno of
326 * the failed system call.
329 * Child process created.
331 *----------------------------------------------------------------------
333 static pid_t
spawn_fs_process(fcgi_server
*fs
, ServerProcess
*process
)
340 char *dnEnd
, *failedSysCall
;
347 /* We're the child. We're gonna exec() so pools don't matter. */
349 dnEnd
= strrchr(fs
->fs_path
, '/');
353 dirName
= ap_pcalloc(fcgi_config_pool
, dnEnd
- fs
->fs_path
+ 1);
354 dirName
= memcpy(dirName
, fs
->fs_path
, dnEnd
- fs
->fs_path
);
356 if (chdir(dirName
) < 0) {
357 failedSysCall
= "chdir()";
358 goto FailedSystemCallExit
;
362 /* OS/2 dosen't support nice() */
363 if (fs
->processPriority
!= 0) {
364 if (nice(fs
->processPriority
) == -1) {
365 failedSysCall
= "nice()";
366 goto FailedSystemCallExit
;
371 /* Open the listenFd on spec'd fd */
372 if (fs
->listenFd
!= FCGI_LISTENSOCK_FILENO
)
373 dup2(fs
->listenFd
, FCGI_LISTENSOCK_FILENO
);
375 /* Close all other open fds, except stdout/stderr. Leave these two open so
376 * FastCGI applications don't have to find and fix ALL 3rd party libs that
377 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
378 * main server error_log - @@@ provide a directive control where this goes.
380 ap_error_log2stderr(fcgi_apache_main_server
);
382 for (i
= 0; i
< FCGI_MAX_FD
; i
++) {
383 if (i
!= FCGI_LISTENSOCK_FILENO
&& i
!= 2 && i
!= 1) {
388 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
389 * install its own handler. */
390 signal(SIGPIPE
, SIG_IGN
);
392 if (fcgi_wrapper
&& (fcgi_user_id
!= fs
->uid
|| fcgi_group_id
!= fs
->gid
)) {
393 char *shortName
= strrchr(fs
->fs_path
, '/') + 1;
395 /* Relinquish our root real uid powers */
400 execle(fcgi_wrapper
, fcgi_wrapper
, fs
->username
, fs
->group
, shortName
, NULL
, fs
->envp
);
401 } while (errno
== EINTR
);
405 execle(fs
->fs_path
, fs
->fs_path
, NULL
, fs
->envp
);
406 } while (errno
== EINTR
);
409 failedSysCall
= "execle()";
411 FailedSystemCallExit
:
412 fprintf(stderr
, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
413 fs
->fs_path
, (long) getpid(), failedSysCall
, strerror(errno
));
416 /* avoid an irrelevant compiler warning */
423 /* based on mod_cgi.c:run_cgi_child() */
426 char * termination_env_string
;
427 HANDLE listen_handle
= INVALID_HANDLE_VALUE
;
428 apr_procattr_t
* procattr
;
429 apr_proc_t proc
= { 0 };
433 if (apr_pool_create(&tp
, fcgi_config_pool
))
436 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
437 if (process
->terminationEvent
== NULL
)
440 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
442 termination_env_string
= ap_psprintf(tp
,
443 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
445 while (fs
->envp
[i
]) i
++;
446 fs
->envp
[i
++] = termination_env_string
;
447 fs
->envp
[i
] = (char *) fs
->mutex_env_string
;
449 ap_assert(fs
->envp
[i
+ 1] == NULL
);
453 SECURITY_ATTRIBUTES sa
= { 0 };
455 sa
.bInheritHandle
= TRUE
;
456 sa
.nLength
= sizeof(sa
);
458 listen_handle
= CreateNamedPipe(fs
->socket_path
,
460 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
461 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
463 if (listen_handle
== INVALID_HANDLE_VALUE
)
465 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
466 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
473 listen_handle
= (HANDLE
) fs
->listenFd
;
476 if (apr_procattr_create(&procattr
, tp
))
479 if (apr_procattr_dir_set(procattr
, ap_make_dirstr_parent(tp
, fs
->fs_path
)))
482 if (apr_procattr_detach_set(procattr
, 1))
485 if (apr_os_file_put(&file
, &listen_handle
, 0, tp
))
488 /* procattr is opaque so we have to use this - unfortuantely it dups */
489 if (apr_procattr_child_in_set(procattr
, file
, NULL
))
492 if (apr_proc_create(&proc
, fs
->fs_path
, NULL
, fs
->envp
, procattr
, tp
))
495 process
->handle
= proc
.hproc
;
502 fs
->envp
[i
- 1] = NULL
;
509 #else /* WIN32 && !APACHE2 */
511 /* Adapted from Apache's util_script.c ap_call_exec() */
512 char *interpreter
= NULL
;
513 char *quoted_filename
;
515 char *pEnvBlock
, *pNext
;
518 int iEnvBlockLen
= 1;
520 file_type_e fileType
;
523 PROCESS_INFORMATION pi
;
528 pool
* tp
= ap_make_sub_pool(fcgi_config_pool
);
530 HANDLE listen_handle
;
531 char * termination_env_string
= NULL
;
533 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
534 if (process
->terminationEvent
== NULL
)
536 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
537 "FastCGI: can't create termination event for server \"%s\", "
538 "CreateEvent() failed", fs
->fs_path
);
541 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
543 termination_env_string
= ap_psprintf(tp
,
544 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
548 SECURITY_ATTRIBUTES sa
;
550 sa
.lpSecurityDescriptor
= NULL
;
551 sa
.bInheritHandle
= TRUE
;
552 sa
.nLength
= sizeof(sa
);
554 listen_handle
= CreateNamedPipe(fs
->socket_path
,
556 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
557 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
559 if (listen_handle
== INVALID_HANDLE_VALUE
)
561 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
562 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs
->fs_path
);
568 listen_handle
= (HANDLE
) fs
->listenFd
;
571 memset(&si
, 0, sizeof(si
));
572 memset(&pi
, 0, sizeof(pi
));
573 memset(&r
, 0, sizeof(r
));
575 // Can up a fake request to pass to ap_get_win32_interpreter()
576 r
.per_dir_config
= fcgi_apache_main_server
->lookup_defaults
;
577 r
.server
= fcgi_apache_main_server
;
578 r
.filename
= (char *) fs
->fs_path
;
581 fileType
= ap_get_win32_interpreter(&r
, &interpreter
);
583 if (fileType
== eFileTypeUNKNOWN
) {
584 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
585 "FastCGI: %s is not executable; ensure interpreted scripts have "
586 "\"#!\" as their first line",
593 * We have the interpreter (if there is one) and we have
594 * the arguments (if there are any).
595 * Build the command string to pass to CreateProcess.
597 quoted_filename
= ap_pstrcat(tp
, "\"", fs
->fs_path
, "\"", NULL
);
598 if (interpreter
&& *interpreter
) {
599 pCommand
= ap_pstrcat(tp
, interpreter
, " ", quoted_filename
, NULL
);
602 pCommand
= quoted_filename
;
606 * Make child process use hPipeOutputWrite as standard out,
607 * and make sure it does not show on screen.
610 si
.dwFlags
= STARTF_USESHOWWINDOW
| STARTF_USESTDHANDLES
;
611 si
.wShowWindow
= SW_HIDE
;
612 si
.hStdInput
= listen_handle
;
614 // XXX These should be open to the error_log
615 si
.hStdOutput
= INVALID_HANDLE_VALUE
;
616 si
.hStdError
= INVALID_HANDLE_VALUE
;
619 * Win32's CreateProcess call requires that the environment
620 * be passed in an environment block, a null terminated block of
621 * null terminated strings.
622 * @todo we should store the env in this format for win32.
626 iEnvBlockLen
+= strlen(fs
->envp
[i
]) + 1;
630 iEnvBlockLen
+= strlen(termination_env_string
) + 1;
631 iEnvBlockLen
+= strlen(fs
->mutex_env_string
) + 1;
633 pEnvBlock
= (char *) ap_pcalloc(tp
, iEnvBlockLen
);
639 strcpy(pNext
, fs
->envp
[i
]);
640 pNext
+= strlen(pNext
) + 1;
644 strcpy(pNext
, termination_env_string
);
645 pNext
+= strlen(pNext
) + 1;
646 strcpy(pNext
, fs
->mutex_env_string
);
648 if (CreateProcess(NULL
, pCommand
, NULL
, NULL
, TRUE
,
651 ap_make_dirstr_parent(tp
, fs
->fs_path
),
654 /* Hack to get 16-bit CGI's working. It works for all the
655 * standard modules shipped with Apache. pi.dwProcessId is 0
656 * for 16-bit CGIs and all the Unix specific code that calls
657 * ap_call_exec interprets this as a failure case. And we can't
658 * use -1 either because it is mapped to 0 by the caller.
660 pid
= (fileType
== eFileTypeEXE16
) ? -2 : pi
.dwProcessId
;
662 process
->handle
= pi
.hProcess
;
663 CloseHandle(pi
.hThread
);
668 CloseHandle(listen_handle
);
677 #endif /* !APACHE2 */
682 static void reduce_privileges(void)
690 /* Get username if passed as a uid */
691 if (ap_user_name
[0] == '#') {
692 uid_t uid
= atoi(&ap_user_name
[1]);
693 struct passwd
*ent
= getpwuid(uid
);
696 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
697 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
698 "you probably need to modify the User directive", (unsigned)uid
);
707 if (setgid(ap_group_id
) == -1) {
708 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
709 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id
);
713 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
715 /* Initialize supplementary groups */
716 if (initgroups(name
, ap_group_id
) == -1) {
717 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
718 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
719 name
, (unsigned)ap_group_id
);
726 if (seteuid_user() == -1) {
727 ap_log_error(FCGI_LOG_ALERT_NOERRNO
, fcgi_apache_main_server
,
728 "FastCGI: process manager exiting, failed to reduce privileges");
733 if (setuid(ap_user_id
) == -1) {
734 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
735 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id
);
742 * Change the name of this process - best we can easily.
744 static void change_process_name(const char * const name
)
746 /* under Apache2, ap_server_argv0 is const */
747 strncpy((char *) ap_server_argv0
, name
, strlen(ap_server_argv0
));
751 static void schedule_start(fcgi_server
*s
, int proc
)
753 /* If we've started one recently, don't register another */
754 time_t time_passed
= now
- s
->restartTime
;
756 if ((s
->procs
[proc
].pid
&& (time_passed
< (int) s
->restartDelay
))
757 || ((s
->procs
[proc
].pid
== 0) && (time_passed
< s
->initStartDelay
)))
759 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
);
763 FCGIDBG3("scheduling_start: %s (%d)", s
->fs_path
, proc
);
764 s
->procs
[proc
].state
= FCGI_START_STATE
;
765 if (proc
== dynamicMaxClassProcs
- 1) {
766 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
767 "FastCGI: scheduled the %sstart of the last (dynamic) server "
768 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
769 s
->procs
[proc
].pid
? "re" : "", s
->fs_path
, dynamicMaxClassProcs
);
774 *----------------------------------------------------------------------
778 * Removes the records written by request handlers and decodes them.
779 * We also update the data structures to reflect the changes.
781 *----------------------------------------------------------------------
784 static void dynamic_read_msgs(int read_ready
)
790 static int buflen
= 0;
791 static char buf
[FCGI_MSGS_BUFSIZE
+ 1];
792 char *ptr1
, *ptr2
, opcode
;
793 char execName
[FCGI_MAXPATH
+ 1];
794 char user
[MAX_USER_NAME_LEN
+ 2];
795 char group
[MAX_GID_CHAR_LEN
+ 1];
796 unsigned long q_usec
= 0UL, req_usec
= 0UL;
798 fcgi_pm_job
*joblist
= NULL
;
799 fcgi_pm_job
*cjob
= NULL
;
802 pool
*sp
= NULL
, *tp
;
805 user
[MAX_USER_NAME_LEN
+ 1] = group
[MAX_GID_CHAR_LEN
] = '\0';
809 * To prevent the idle application from running indefinitely, we
810 * check the timer and if it is expired, we recompute the values
811 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
812 * message is received, only updates are made to the data structures.
814 if (fcgi_dynamic_last_analyzed
== 0) {
815 fcgi_dynamic_last_analyzed
= now
;
817 if ((now
- fcgi_dynamic_last_analyzed
) >= (int)dynamicUpdateInterval
) {
818 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
819 if (s
->directive
!= APP_CLASS_DYNAMIC
)
822 /* Advance the last analyzed timestamp by the elapsed time since
823 * it was last set. Round the increase down to the nearest
824 * multiple of dynamicUpdateInterval */
826 fcgi_dynamic_last_analyzed
+= (((long)(now
-fcgi_dynamic_last_analyzed
)/dynamicUpdateInterval
)*dynamicUpdateInterval
);
827 s
->smoothConnTime
= (unsigned long) ((1.0-dynamicGain
)*s
->smoothConnTime
+ dynamicGain
*s
->totalConnTime
);
828 s
->totalConnTime
= 0UL;
829 s
->totalQueueTime
= 0UL;
833 if (read_ready
<= 0) {
838 rc
= read(fcgi_pm_pipe
[0], (void *)(buf
+ buflen
), FCGI_MSGS_BUFSIZE
- buflen
);
840 if (!caughtSigTerm
) {
841 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
842 "FastCGI: read() from pipe failed (%d)", rc
);
844 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
845 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
846 caughtSigTerm
= TRUE
;
856 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
857 * request to do something) and/or when a timeout expires.
858 * There really should be no reason why this wait would get stuck
859 * but there's no point in waiting forever. */
861 rc
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
863 if (rc
!= WAIT_OBJECT_0
&& rc
!= WAIT_ABANDONED
)
865 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
866 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
870 joblist
= fcgi_dynamic_mbox
;
871 fcgi_dynamic_mbox
= NULL
;
873 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
875 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
876 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
883 apr_pool_create(&tp
, fcgi_config_pool
);
885 tp
= ap_make_sub_pool(fcgi_config_pool
);
889 for (ptr1
= buf
; ptr1
; ptr1
= ptr2
) {
892 ptr2
= strchr(ptr1
, '*');
904 case FCGI_SERVER_START_JOB
:
905 case FCGI_SERVER_RESTART_JOB
:
907 if (sscanf(ptr1
, "%c %s %16s %15s",
908 &opcode
, execName
, user
, group
) != 4)
914 case FCGI_REQUEST_TIMEOUT_JOB
:
916 if (sscanf(ptr1
, "%c %s %16s %15s",
917 &opcode
, execName
, user
, group
) != 4)
923 case FCGI_REQUEST_COMPLETE_JOB
:
925 if (sscanf(ptr1
, "%c %s %16s %15s %lu %lu",
926 &opcode
, execName
, user
, group
, &q_usec
, &req_usec
) != 6)
938 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode
, execName
, user
, group
, q_usec
, req_usec
);
941 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
942 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1
);
946 /* Update data structures for processing */
947 while (cjob
!= NULL
) {
948 joblist
= cjob
->next
;
949 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob
->id
, cjob
->fs_path
, cjob
->user
, cjob
->group
, cjob
->qsec
, cjob
->start_time
);
953 s
= fcgi_util_fs_get(execName
, user
, group
);
955 s
= fcgi_util_fs_get(cjob
->fs_path
, cjob
->user
, cjob
->group
);
959 if (s
==NULL
&& opcode
!= FCGI_REQUEST_COMPLETE_JOB
)
961 if (s
==NULL
&& cjob
->id
!= FCGI_REQUEST_COMPLETE_JOB
)
966 HANDLE mutex
= CreateMutex(NULL
, FALSE
, cjob
->fs_path
);
970 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
971 "FastCGI: can't create accept mutex "
972 "for (dynamic) server \"%s\"", cjob
->fs_path
);
976 SetHandleInformation(mutex
, HANDLE_FLAG_INHERIT
, TRUE
);
981 /* Create a perm subpool to hold the new server data,
982 * we can destroy it if something doesn't pan out */
984 apr_pool_create(&sp
, fcgi_config_pool
);
986 sp
= ap_make_sub_pool(fcgi_config_pool
);
989 /* Create a new "dynamic" server */
990 s
= fcgi_util_fs_new(sp
);
992 s
->directive
= APP_CLASS_DYNAMIC
;
993 s
->restartDelay
= dynamicRestartDelay
;
994 s
->listenQueueDepth
= dynamicListenQueueDepth
;
995 s
->initStartDelay
= dynamicInitStartDelay
;
996 s
->envp
= dynamicEnvp
;
997 s
->flush
= dynamicFlush
;
1000 s
->mutex_env_string
= ap_psprintf(sp
, "_FCGI_MUTEX_=%ld", mutex
);
1001 s
->fs_path
= ap_pstrdup(sp
, cjob
->fs_path
);
1003 s
->fs_path
= ap_pstrdup(sp
, execName
);
1005 ap_getparents(s
->fs_path
);
1006 ap_no2slash(s
->fs_path
);
1007 s
->procs
= fcgi_util_fs_create_procs(sp
, dynamicMaxClassProcs
);
1009 /* XXX the socket_path (both Unix and Win) *is* deducible and
1010 * thus can and will be used by other apache instances without
1011 * the use of shared data regarding the processes serving the
1012 * requests. This can result in slightly unintuitive process
1013 * counts and security implications. This is prevented
1014 * if suexec (Unix) is in use. This is both a feature and a flaw.
1015 * Changing it now would break existing installations. */
1018 /* Create socket file's path */
1019 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, execName
, user
, group
);
1020 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1022 /* Create sockaddr, prealloc it so it won't get created in tp */
1023 s
->socket_addr
= ap_pcalloc(sp
, sizeof(struct sockaddr_un
));
1024 err
= fcgi_util_socket_make_domain_addr(tp
, (struct sockaddr_un
**)&s
->socket_addr
,
1025 &s
->socket_addr_len
, s
->socket_path
);
1027 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1028 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
1032 if (init_listen_sock(s
)) {
1036 /* If a wrapper is being used, config user/group info */
1038 if (user
[0] == '~') {
1039 /* its a user dir uri, the rest is a username, not a uid */
1040 struct passwd
*pw
= getpwnam(&user
[1]);
1043 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1044 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1045 execName
, &user
[1]);
1048 s
->uid
= pw
->pw_uid
;
1049 s
->user
= ap_pstrdup(sp
, user
);
1050 s
->username
= s
->user
;
1052 s
->gid
= pw
->pw_gid
;
1053 s
->group
= ap_psprintf(sp
, "%ld", (long)s
->gid
);
1058 s
->uid
= (uid_t
)atol(user
);
1059 pw
= getpwuid(s
->uid
);
1061 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1062 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1063 execName
, (long)s
->uid
);
1066 s
->user
= ap_pstrdup(sp
, user
);
1067 s
->username
= ap_pstrdup(sp
, pw
->pw_name
);
1069 s
->gid
= (gid_t
)atol(group
);
1070 s
->group
= ap_pstrdup(sp
, group
);
1074 /* Create socket file's path */
1075 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, cjob
->fs_path
, cjob
->user
, cjob
->group
);
1076 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1080 fcgi_util_fs_add(s
);
1084 if (opcode
== FCGI_SERVER_RESTART_JOB
) {
1086 if (cjob
->id
==FCGI_SERVER_RESTART_JOB
) {
1088 /* Check to see if the binary has changed. If so,
1089 * kill the FCGI application processes, and
1095 char * app_path
= cjob
->fs_path
;
1097 char * app_path
= execName
;
1100 if (stat(app_path
, &stbuf
) == 0 && stbuf
.st_mtime
> s
->startTime
)
1104 /* prevent addition restart requests */
1107 utime(s
->socket_path
, NULL
);
1110 /* kill old server(s) */
1111 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1113 if (s
->procs
[i
].pid
> 0
1114 && stbuf
.st_mtime
> s
->procs
[i
].start_time
)
1116 fcgi_kill(&s
->procs
[i
], SIGTERM
);
1123 ap_log_error(FCGI_LOG_WARN_NOERRNO
,
1124 fcgi_apache_main_server
, "FastCGI: restarting "
1125 "old server \"%s\" processes, newer version "
1130 /* If dynamicAutoRestart, don't mark any new processes
1131 * for starting because we probably got the
1132 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1133 * will be restarting all of those we just killed.
1135 if (dynamicAutoRestart
)
1139 else if (opcode
== FCGI_SERVER_START_JOB
) {
1141 else if (cjob
->id
==FCGI_SERVER_START_JOB
) {
1143 /* we've been asked to start a process--only start
1144 * it if we're not already running at least one
1149 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1150 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1153 /* if already running, don't start another one */
1154 if (i
< dynamicMaxClassProcs
) {
1168 case FCGI_SERVER_RESTART_JOB
:
1172 /* We just waxed 'em all. Try to find an idle slot. */
1174 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1176 if (s
->procs
[i
].state
== FCGI_START_STATE
1177 || s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1181 else if (s
->procs
[i
].state
== FCGI_KILLED_STATE
1182 || s
->procs
[i
].state
== FCGI_READY_STATE
)
1189 /* Nope, just use the first slot */
1190 if (i
== dynamicMaxClassProcs
)
1198 schedule_start(s
, i
);
1203 case FCGI_SERVER_START_JOB
:
1204 case FCGI_REQUEST_TIMEOUT_JOB
:
1206 if ((fcgi_dynamic_total_proc_count
+ 1) > (int) dynamicMaxProcs
) {
1208 * Extra instances should have been
1209 * terminated beforehand, probably need
1210 * to increase ProcessSlack parameter
1212 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1213 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1214 "exceeded dynamicMaxProcs (%d)", s
->fs_path
, dynamicMaxProcs
);
1218 /* find next free slot */
1219 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1221 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1223 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i
);
1226 else if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1231 schedule_start(s
, i
);
1236 if (i
>= dynamicMaxClassProcs
) {
1237 FCGIDBG1("ignore_job: slots are max'd");
1241 case FCGI_REQUEST_COMPLETE_JOB
:
1242 /* only record stats if we have a structure */
1245 s
->totalConnTime
+= req_usec
;
1246 s
->totalQueueTime
+= q_usec
;
1248 s
->totalConnTime
+= cjob
->start_time
;
1249 s
->totalQueueTime
+= cjob
->qsec
;
1258 /* Cleanup job data */
1259 free(cjob
->fs_path
);
1269 if (sp
) ap_destroy_pool(sp
);
1272 free(cjob
->fs_path
);
1280 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1281 "FastCGI: really bogus message: \"%s\"", ptr1
);
1282 ptr1
+= strlen(buf
);
1285 buflen
-= ptr1
- buf
;
1287 memmove(buf
, ptr1
, buflen
);
1291 ap_destroy_pool(tp
);
1295 *----------------------------------------------------------------------
1297 * dynamic_kill_idle_fs_procs
1299 * Implement a kill policy for the dynamic FastCGI applications.
1300 * We also update the data structures to reflect the changes.
1303 * Processes are marked for deletion possibly killed.
1305 *----------------------------------------------------------------------
1307 static void dynamic_kill_idle_fs_procs(void)
1312 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1315 * server's smoothed running time, or if that's 0, the current total
1317 unsigned long connTime
;
1320 * maximum number of microseconds that all of a server's running
1321 * processes together could have spent running since the last check
1323 unsigned long totalTime
;
1326 * percentage, 0-100, of totalTime that the processes actually used
1331 int really_running
= 0;
1333 if (s
->directive
!= APP_CLASS_DYNAMIC
|| s
->numProcesses
== 0)
1338 /* s->numProcesses includes pending kills so get the "active" count */
1339 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1341 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
) ++really_running
;
1344 connTime
= s
->smoothConnTime
? s
->smoothConnTime
: s
->totalConnTime
;
1345 totalTime
= really_running
* (now
- fcgi_dynamic_epoch
) * 1000000 + 1;
1347 loadFactor
= 100 * connTime
/ totalTime
;
1349 if (really_running
== 1)
1351 if (loadFactor
>= dynamicThreshold1
)
1358 int load
= really_running
/ ( really_running
- 1) * loadFactor
;
1360 if (load
>= dynamicThresholdN
)
1367 * Run through the procs to see if we can get away w/o waxing one.
1369 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1371 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1373 s
->procs
[i
].state
= FCGI_READY_STATE
;
1376 else if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
)
1382 if (i
>= dynamicMaxClassProcs
)
1384 ServerProcess
* procs
= s
->procs
;
1387 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1389 if (procs
[i
].state
== FCGI_RUNNING_STATE
)
1391 if (youngest
== -1 || procs
[i
].start_time
>= procs
[youngest
].start_time
)
1400 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1401 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1402 s
->fs_path
, (long) s
->procs
[youngest
].pid
);
1404 fcgi_kill(&s
->procs
[youngest
], SIGTERM
);
1410 * If the number of non-victims is less than or equal to
1411 * the minimum that may be running without being killed off,
1412 * don't select any more victims.
1414 if (fcgi_dynamic_total_proc_count
- victims
<= dynamicMinProcs
)
1424 // This is a little bogus, there's gotta be a better way to do this
1425 // Can we use WaitForMultipleObjects()
1426 #define FCGI_PROC_WAIT_TIME 100
1428 void child_wait_thread_main(void *dummy
) {
1430 DWORD dwRet
= WAIT_TIMEOUT
;
1435 while (!bTimeToDie
) {
1438 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1439 if (s
->directive
== APP_CLASS_EXTERNAL
|| s
->listenFd
< 0) {
1442 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1443 numChildren
= dynamicMaxClassProcs
;
1446 numChildren
= s
->numProcesses
;
1449 for (i
=0; i
< numChildren
; i
++) {
1450 if (s
->procs
[i
].handle
!= INVALID_HANDLE_VALUE
)
1452 DWORD exitStatus
= 0;
1454 /* timeout is currently set for 100 miliecond */
1455 /* it may need to be longer or user customizable */
1456 dwRet
= WaitForSingleObject(s
->procs
[i
].handle
, FCGI_PROC_WAIT_TIME
);
1460 if (dwRet
!= WAIT_TIMEOUT
&& dwRet
!= WAIT_FAILED
) {
1461 /* a child fs has died */
1462 /* mark the child as dead */
1464 if (s
->directive
== APP_CLASS_STANDARD
) {
1465 /* restart static app */
1466 s
->procs
[i
].state
= FCGI_START_STATE
;
1471 fcgi_dynamic_total_proc_count
--;
1472 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1474 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1475 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1478 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1481 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0)) {
1482 s
->procs
[i
].state
= FCGI_START_STATE
;
1485 s
->procs
[i
].state
= FCGI_READY_STATE
;
1490 GetExitCodeProcess(s
->procs
[i
].handle
, &exitStatus
);
1492 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1493 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1494 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1495 s
->fs_path
, (long) s
->procs
[i
].pid
, exitStatus
);
1497 CloseHandle(s
->procs
[i
].handle
);
1498 s
->procs
[i
].handle
= INVALID_HANDLE_VALUE
;
1499 s
->procs
[i
].pid
= -1;
1501 /* wake up the main thread */
1502 SetEvent(fcgi_event_handles
[WAKE_EVENT
]);
1507 Sleep(waited
? 0 : FCGI_PROC_WAIT_TIME
);
1513 static void setup_signals(void)
1515 struct sigaction sa
;
1517 /* Setup handlers */
1519 sa
.sa_handler
= signal_handler
;
1520 sigemptyset(&sa
.sa_mask
);
1523 if (sigaction(SIGTERM
, &sa
, NULL
) < 0) {
1524 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1525 "sigaction(SIGTERM) failed");
1528 if (sigaction(SIGHUP
, &sa
, NULL
) < 0) {
1529 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1530 "sigaction(SIGHUP) failed");
1532 /* httpd graceful restart */
1533 if (sigaction(SIGUSR1
, &sa
, NULL
) < 0) {
1534 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1535 "sigaction(SIGUSR1) failed");
1537 /* read messages from request handlers - kill interval expired */
1538 if (sigaction(SIGALRM
, &sa
, NULL
) < 0) {
1539 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1540 "sigaction(SIGALRM) failed");
1542 if (sigaction(SIGCHLD
, &sa
, NULL
) < 0) {
1543 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1544 "sigaction(SIGCHLD) failed");
1549 #if !defined(WIN32) && !defined(APACHE2)
1550 int fcgi_pm_main(void *dummy
, child_info
*info
)
1552 void fcgi_pm_main(void *dummy
)
1562 HANDLE child_wait_thread
= INVALID_HANDLE_VALUE
;
1564 int callWaitPid
, callDynamicProcs
;
1568 // Add SystemRoot to the dynamic environment
1569 char ** envp
= dynamicEnvp
;
1570 for (i
= 0; *envp
; ++i
) {
1573 fcgi_config_set_env_var(fcgi_config_pool
, dynamicEnvp
, &i
, "SystemRoot");
1577 reduce_privileges();
1578 change_process_name("fcgi-pm");
1580 close(fcgi_pm_pipe
[1]);
1584 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1585 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper
);
1589 /* Initialize AppClass */
1590 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1592 if (s
->directive
!= APP_CLASS_STANDARD
)
1600 for (i
= 0; i
< s
->numProcesses
; ++i
)
1601 s
->procs
[i
].state
= FCGI_START_STATE
;
1605 child_wait_thread
= (HANDLE
) _beginthread(child_wait_thread_main
, 0, NULL
);
1607 if (child_wait_thread
== (HANDLE
) -1)
1609 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1610 "FastCGI: failed to create process manager's wait thread!");
1613 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1614 "FastCGI: process manager initialized");
1616 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1617 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1623 * Loop until SIGTERM
1626 int sleepSeconds
= min(dynamicKillInterval
, dynamicUpdateInterval
);
1633 unsigned int numChildren
;
1636 * If we came out of sigsuspend() for any reason other than
1637 * SIGALRM, pick up where we left off.
1640 sleepSeconds
= alarmLeft
;
1643 * Examine each configured AppClass for a process that needs
1644 * starting. Compute the earliest time when the start should
1645 * be attempted, starting it now if the time has passed. Also,
1646 * remember that we do NOT need to restart externally managed
1647 * FastCGI applications.
1649 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1651 if (s
->directive
== APP_CLASS_EXTERNAL
)
1654 numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
1655 ? dynamicMaxClassProcs
1658 for (i
= 0; i
< numChildren
; ++i
)
1660 if (s
->procs
[i
].pid
<= 0 && s
->procs
[i
].state
== FCGI_START_STATE
)
1662 int restart
= (s
->procs
[i
].pid
< 0);
1663 time_t restartTime
= s
->restartTime
;
1667 /* we've gone to using the badDelay, the only thing that
1668 resets bad is when badDelay has expired. but numFailures
1669 is only just set below its threshold. the proc's
1670 start_times are all reset when the bad is. the numFailures
1671 is reset when we see an app run for a period */
1673 s
->procs
[i
].start_time
= 0;
1676 if (s
->numFailures
> MAX_FAILED_STARTS
)
1678 time_t last_start_time
= s
->procs
[i
].start_time
;
1680 if (last_start_time
&& now
- last_start_time
> RUNTIME_SUCCESS_INTERVAL
)
1684 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1685 "FastCGI:%s server \"%s\" has remained"
1686 " running for more than %d seconds, its restart"
1687 " interval has been restored to %d seconds",
1688 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1689 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, s
->restartDelay
);
1695 for (j
= 0; j
< numChildren
; ++j
)
1697 if (s
->procs
[j
].pid
<= 0) continue;
1698 if (s
->procs
[j
].state
!= FCGI_RUNNING_STATE
) continue;
1699 if (s
->procs
[j
].start_time
== 0) continue;
1700 if (now
- s
->procs
[j
].start_time
> RUNTIME_SUCCESS_INTERVAL
) break;
1703 if (j
>= numChildren
)
1706 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1707 "FastCGI:%s server \"%s\" has failed to remain"
1708 " running for %d seconds given %d attempts, its restart"
1709 " interval has been backed off to %d seconds",
1710 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1711 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, MAX_FAILED_STARTS
,
1712 FAILED_STARTS_DELAY
);
1718 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1719 "FastCGI:%s server \"%s\" has remained"
1720 " running for more than %d seconds, its restart"
1721 " interval has been restored to %d seconds",
1722 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1723 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, s
->restartDelay
);
1730 restartTime
+= FAILED_STARTS_DELAY
;
1734 restartTime
+= (restart
) ? s
->restartDelay
: s
->initStartDelay
;
1737 if (restartTime
<= now
)
1742 s
->numFailures
= MAX_FAILED_STARTS
;
1745 if (s
->listenFd
< 0 && init_listen_sock(s
))
1747 if (sleepSeconds
> s
->initStartDelay
)
1748 sleepSeconds
= s
->initStartDelay
;
1752 if (caughtSigTerm
) {
1753 goto ProcessSigTerm
;
1756 s
->procs
[i
].pid
= spawn_fs_process(s
, &s
->procs
[i
]);
1757 if (s
->procs
[i
].pid
<= 0) {
1758 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1759 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1760 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1763 sleepSeconds
= min(sleepSeconds
,
1764 max((int) s
->restartDelay
, FCGI_MIN_EXEC_RETRY_DELAY
));
1766 ap_assert(s
->procs
[i
].pid
< 0);
1770 s
->procs
[i
].start_time
= now
;
1771 s
->restartTime
= now
;
1773 if (s
->startTime
== 0) {
1777 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1779 fcgi_dynamic_total_proc_count
++;
1780 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1783 s
->procs
[i
].state
= FCGI_RUNNING_STATE
;
1786 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1787 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1788 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1789 s
->fs_path
, (long) s
->uid
, (long) s
->gid
,
1790 restart
? "re" : "", (long) s
->procs
[i
].pid
);
1793 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1794 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1795 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1796 s
->fs_path
, restart
? "re" : "", (long) s
->procs
[i
].pid
);
1798 ap_assert(s
->procs
[i
].pid
> 0);
1800 sleepSeconds
= min(sleepSeconds
, restartTime
- now
);
1809 goto ProcessSigTerm
;
1811 if((!caughtSigChld
) && (!caughtSigAlarm
)) {
1814 alarm(sleepSeconds
);
1817 FD_SET(fcgi_pm_pipe
[0], &rfds
);
1818 read_ready
= ap_select(fcgi_pm_pipe
[0] + 1, &rfds
, NULL
, NULL
, NULL
);
1820 alarmLeft
= alarm(0);
1822 callWaitPid
= caughtSigChld
;
1823 caughtSigChld
= FALSE
;
1824 callDynamicProcs
= caughtSigAlarm
;
1825 caughtSigAlarm
= FALSE
;
1830 * Dynamic fcgi process management
1832 if((callDynamicProcs
) || (!callWaitPid
)) {
1833 dynamic_read_msgs(read_ready
);
1834 if(fcgi_dynamic_epoch
== 0) {
1835 fcgi_dynamic_epoch
= now
;
1837 if(((long)(now
-fcgi_dynamic_epoch
)>=dynamicKillInterval
) ||
1838 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
)>=dynamicMaxProcs
)) {
1839 dynamic_kill_idle_fs_procs();
1840 fcgi_dynamic_epoch
= now
;
1848 /* We've caught SIGCHLD, so find out who it was using waitpid,
1849 * write a log message and update its data structure. */
1853 goto ProcessSigTerm
;
1855 childPid
= waitpid(-1, &waitStatus
, WNOHANG
);
1857 if (childPid
== -1 || childPid
== 0)
1860 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1861 if (s
->directive
== APP_CLASS_EXTERNAL
)
1864 if (s
->directive
== APP_CLASS_DYNAMIC
)
1865 numChildren
= dynamicMaxClassProcs
;
1867 numChildren
= s
->numProcesses
;
1869 for (i
= 0; i
< numChildren
; i
++) {
1870 if (s
->procs
[i
].pid
== childPid
)
1875 /* TODO: print something about this unknown child */
1879 s
->procs
[i
].pid
= -1;
1881 if (s
->directive
== APP_CLASS_STANDARD
) {
1882 /* Always restart static apps */
1883 s
->procs
[i
].state
= FCGI_START_STATE
;
1888 fcgi_dynamic_total_proc_count
--;
1890 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1891 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1894 /* A dynamic app died or exited without provocation from the PM */
1897 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0))
1898 s
->procs
[i
].state
= FCGI_START_STATE
;
1900 s
->procs
[i
].state
= FCGI_READY_STATE
;
1904 if (WIFEXITED(waitStatus
)) {
1905 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1906 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1907 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1908 s
->fs_path
, (long) childPid
, WEXITSTATUS(waitStatus
));
1910 else if (WIFSIGNALED(waitStatus
)) {
1911 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1912 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1913 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1914 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
),
1916 WCOREDUMP(waitStatus
) ? ", a core file may have been generated" : "");
1921 else if (WIFSTOPPED(waitStatus
)) {
1922 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1923 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1924 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1925 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
));
1927 } /* for (;;), waitpid() */
1931 /* wait for an event to occur or timer expires */
1932 expire
= time(NULL
) + sleepSeconds
;
1933 dwRet
= WaitForMultipleObjects(3, (HANDLE
*) fcgi_event_handles
, FALSE
, sleepSeconds
* 1000);
1935 if (dwRet
== WAIT_FAILED
) {
1936 /* There is something seriously wrong here */
1937 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1938 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1942 if (dwRet
!= WAIT_TIMEOUT
) {
1946 alarmLeft
= expire
- now
;
1950 * Dynamic fcgi process management
1952 if ((dwRet
== MBOX_EVENT
) || (dwRet
== WAIT_TIMEOUT
)) {
1953 if (dwRet
== MBOX_EVENT
) {
1959 dynamic_read_msgs(read_ready
);
1961 if(fcgi_dynamic_epoch
== 0) {
1962 fcgi_dynamic_epoch
= now
;
1965 if ((now
-fcgi_dynamic_epoch
>= (int) dynamicKillInterval
) ||
1966 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
) >= dynamicMaxProcs
)) {
1967 dynamic_kill_idle_fs_procs();
1968 fcgi_dynamic_epoch
= now
;
1972 else if (dwRet
== WAKE_EVENT
) {
1975 else if (dwRet
== TERM_EVENT
) {
1976 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
1977 "FastCGI: Termination event received process manager shutting down");
1980 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
1982 goto ProcessSigTerm
;
1985 // Have an received an unknown event - should not happen
1986 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1987 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1990 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
1992 goto ProcessSigTerm
;
1997 } /* for (;;), the whole shoot'n match */
2001 * Kill off the children, then exit.
2013 int fcgi_pm_add_job(fcgi_pm_job
*new_job
)
2015 int rv
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
2017 if (rv
!= WAIT_OBJECT_0
&& rv
!= WAIT_ABANDONED
)
2019 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2020 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2024 new_job
->next
= fcgi_dynamic_mbox
;
2025 fcgi_dynamic_mbox
= new_job
;
2027 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
2029 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2030 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");