2 * $Id: fcgi_pm.c,v 1.85 2003/02/04 01:14:10 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;
33 #pragma warning ( disable : 4100 4102 )
34 static BOOL bTimeToDie
= FALSE
; /* process termination flag */
35 HANDLE fcgi_event_handles
[3];
43 static int seteuid_root(void)
45 int rc
= seteuid(getuid());
47 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
48 "FastCGI: seteuid(0) failed");
53 static int seteuid_user(void)
55 int rc
= seteuid(ap_user_id
);
57 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
58 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id
);
65 * Signal the process to exit. How (or if) the process responds
66 * depends on the FastCGI application library (esp. on Win32) and
67 * possibly application code (signal handlers and whether or not
68 * SA_RESTART is on). At any rate, we send the signal with the
69 * hopes that the process will exit on its own. Later, as we
70 * review the state of application processes, if we see one marked
71 * for death, but that hasn't died within a specified period of
72 * time, fcgi_kill() is called again with a KILL)
74 static void fcgi_kill(ServerProcess
*process
, int sig
)
76 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process
->pid
, sig
);
78 process
->state
= FCGI_VICTIM_STATE
;
84 SetEvent(process
->terminationEvent
);
86 else if (sig
== SIGKILL
)
88 TerminateProcess(process
->handle
, 1);
102 kill(process
->pid
, sig
);
112 /*******************************************************************************
113 * Send SIGTERM to each process in the server class, remove socket
114 * file if appropriate. Currently this is only called when the PM is shutting
115 * down and thus memory isn't freed and sockets and files aren't closed.
117 static void shutdown_all()
119 fcgi_server
*s
= fcgi_servers
;
123 ServerProcess
*proc
= s
->procs
;
125 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
126 ? dynamicMaxClassProcs
130 if (s
->socket_path
!= NULL
&& s
->directive
!= APP_CLASS_EXTERNAL
)
132 /* Remove the socket file */
133 if (unlink(s
->socket_path
) != 0 && errno
!= ENOENT
) {
134 ap_log_error(FCGI_LOG_ERR
, fcgi_apache_main_server
,
135 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
137 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "", s
->fs_path
);
142 /* Send TERM to all processes */
143 for (i
= 0; i
< numChildren
; i
++, proc
++)
145 if (proc
->state
== FCGI_RUNNING_STATE
)
147 fcgi_kill(proc
, SIGTERM
);
154 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
157 * WIN32 applications may not have support for the shutdown event
158 * depending on their application library version
161 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT
);
166 ServerProcess
*proc
= s
->procs
;
168 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
169 ? dynamicMaxClassProcs
172 /* Send KILL to all processes */
173 for (i
= 0; i
< numChildren
; i
++, proc
++)
175 if (proc
->state
== FCGI_RUNNING_STATE
)
177 fcgi_kill(proc
, SIGKILL
);
187 static int init_listen_sock(fcgi_server
* fs
)
189 ap_assert(fs
->directive
!= APP_CLASS_EXTERNAL
);
191 /* Create the socket */
192 if ((fs
->listenFd
= socket(fs
->socket_addr
->sa_family
, SOCK_STREAM
, 0)) < 0)
195 errno
= WSAGetLastError(); // Not sure if this will work as expected
197 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
198 "FastCGI: can't create %sserver \"%s\": socket() failed",
199 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
205 if (fs
->socket_addr
->sa_family
== AF_UNIX
)
207 /* Remove any existing socket file.. just in case */
208 unlink(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
);
214 setsockopt(fs
->listenFd
, SOL_SOCKET
, SO_REUSEADDR
, (char *)&flag
, sizeof(flag
));
217 /* Bind it to the socket_addr */
218 if (bind(fs
->listenFd
, fs
->socket_addr
, fs
->socket_addr_len
))
223 errno
= WSAGetLastError();
225 ap_snprintf(port
, sizeof(port
), "port=%d",
226 ((struct sockaddr_in
*)fs
->socket_addr
)->sin_port
);
228 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
229 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
230 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
233 (fs
->socket_addr
->sa_family
== AF_UNIX
) ?
234 ((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
:
240 /* Twiddle Unix socket permissions */
241 else if (fs
->socket_addr
->sa_family
== AF_UNIX
242 && chmod(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
, S_IRUSR
| S_IWUSR
))
244 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
245 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
246 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
252 else if (listen(fs
->listenFd
, fs
->listenQueueDepth
))
255 errno
= WSAGetLastError();
257 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
258 "FastCGI: can't create %sserver \"%s\": listen() failed",
259 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
268 closesocket(fs
->listenFd
);
279 *----------------------------------------------------------------------
283 * The FastCGI process manager, which runs as a separate
284 * process responsible for:
285 * - Starting all the FastCGI proceses.
286 * - Restarting any of these processes that die (indicated
288 * - Catching SIGTERM and relaying it to all the FastCGI
289 * processes before exiting.
292 * Uses global variable fcgi_servers.
300 *----------------------------------------------------------------------
303 static int caughtSigTerm
= FALSE
;
304 static int caughtSigChld
= FALSE
;
305 static int caughtSigAlarm
= FALSE
;
307 static void signal_handler(int signo
)
309 if ((signo
== SIGTERM
) || (signo
== SIGUSR1
) || (signo
== SIGHUP
)) {
310 /* SIGUSR1 & SIGHUP are sent by apache to its process group
311 * when apache get 'em. Apache follows up (1.2.x) with attacks
312 * on each of its child processes, but we've got the KillMgr
313 * sitting between us so we never see the KILL. The main loop
314 * in ProcMgr also checks to see if the KillMgr has terminated,
315 * and if it has, we handl it as if we should shutdown too. */
316 caughtSigTerm
= TRUE
;
317 } else if(signo
== SIGCHLD
) {
318 caughtSigChld
= TRUE
;
319 } else if(signo
== SIGALRM
) {
320 caughtSigAlarm
= TRUE
;
326 *----------------------------------------------------------------------
328 * spawn_fs_process --
330 * Fork and exec the specified fcgi process.
333 * 0 for successful fork, -1 for failed fork.
335 * In case the child fails before or in the exec, the child
336 * obtains the error log by calling getErrLog, logs
337 * the error, and exits with exit status = errno of
338 * the failed system call.
341 * Child process created.
343 *----------------------------------------------------------------------
345 static pid_t
spawn_fs_process(fcgi_server
*fs
, ServerProcess
*process
)
352 char *dnEnd
, *failedSysCall
;
359 /* We're the child. We're gonna exec() so pools don't matter. */
361 dnEnd
= strrchr(fs
->fs_path
, '/');
365 dirName
= ap_pcalloc(fcgi_config_pool
, dnEnd
- fs
->fs_path
+ 1);
366 dirName
= memcpy(dirName
, fs
->fs_path
, dnEnd
- fs
->fs_path
);
368 if (chdir(dirName
) < 0) {
369 failedSysCall
= "chdir()";
370 goto FailedSystemCallExit
;
374 /* OS/2 dosen't support nice() */
375 if (fs
->processPriority
!= 0) {
376 if (nice(fs
->processPriority
) == -1) {
377 failedSysCall
= "nice()";
378 goto FailedSystemCallExit
;
383 /* Open the listenFd on spec'd fd */
384 if (fs
->listenFd
!= FCGI_LISTENSOCK_FILENO
)
385 dup2(fs
->listenFd
, FCGI_LISTENSOCK_FILENO
);
387 /* Close all other open fds, except stdout/stderr. Leave these two open so
388 * FastCGI applications don't have to find and fix ALL 3rd party libs that
389 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
390 * main server error_log - @@@ provide a directive control where this goes.
392 ap_error_log2stderr(fcgi_apache_main_server
);
394 for (i
= 0; i
< FCGI_MAX_FD
; i
++) {
395 if (i
!= FCGI_LISTENSOCK_FILENO
&& i
!= 2 && i
!= 1) {
400 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
401 * install its own handler. */
402 signal(SIGPIPE
, SIG_IGN
);
408 /* Relinquish our root real uid powers */
412 /* AP13 does not use suexec if the target uid/gid is the same as the
413 * server's - AP20 does. I (now) consider the AP2 approach better
414 * (fcgi_pm.c v1.42 incorporated the 1.3 behaviour, v1.84 reverted it,
415 * v1.85 added the compile time option to use the old behaviour). */
417 #ifdef NO_SUEXEC_FOR_AP_USER_N_GROUP
419 if (fcgi_user_id
== fs
->uid
&& fcgi_group_id
== fs
->gid
)
425 shortName
= strrchr(fs
->fs_path
, '/') + 1;
428 execle(fcgi_wrapper
, fcgi_wrapper
, fs
->username
, fs
->group
,
429 shortName
, NULL
, fs
->envp
);
430 } while (errno
== EINTR
);
437 execle(fs
->fs_path
, fs
->fs_path
, NULL
, fs
->envp
);
438 } while (errno
== EINTR
);
441 failedSysCall
= "execle()";
443 FailedSystemCallExit
:
444 fprintf(stderr
, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
445 fs
->fs_path
, (long) getpid(), failedSysCall
, strerror(errno
));
448 /* avoid an irrelevant compiler warning */
455 /* based on mod_cgi.c:run_cgi_child() */
458 char * termination_env_string
;
459 HANDLE listen_handle
= INVALID_HANDLE_VALUE
;
460 apr_procattr_t
* procattr
;
461 apr_proc_t proc
= { 0 };
465 if (apr_pool_create(&tp
, fcgi_config_pool
))
468 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
469 if (process
->terminationEvent
== NULL
)
472 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
474 termination_env_string
= ap_psprintf(tp
,
475 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
477 while (fs
->envp
[i
]) i
++;
478 fs
->envp
[i
++] = termination_env_string
;
479 fs
->envp
[i
] = (char *) fs
->mutex_env_string
;
481 ap_assert(fs
->envp
[i
+ 1] == NULL
);
485 SECURITY_ATTRIBUTES sa
= { 0 };
487 sa
.bInheritHandle
= TRUE
;
488 sa
.nLength
= sizeof(sa
);
490 listen_handle
= CreateNamedPipe(fs
->socket_path
,
492 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
493 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
495 if (listen_handle
== INVALID_HANDLE_VALUE
)
497 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
498 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
505 listen_handle
= (HANDLE
) fs
->listenFd
;
508 if (apr_procattr_create(&procattr
, tp
))
511 if (apr_procattr_dir_set(procattr
, ap_make_dirstr_parent(tp
, fs
->fs_path
)))
514 if (apr_procattr_detach_set(procattr
, 1))
517 if (apr_os_file_put(&file
, &listen_handle
, 0, tp
))
520 /* procattr is opaque so we have to use this - unfortuantely it dups */
521 if (apr_procattr_child_in_set(procattr
, file
, NULL
))
524 if (apr_proc_create(&proc
, fs
->fs_path
, NULL
, fs
->envp
, procattr
, tp
))
527 process
->handle
= proc
.hproc
;
534 fs
->envp
[i
- 1] = NULL
;
541 #else /* WIN32 && !APACHE2 */
543 /* Adapted from Apache's util_script.c ap_call_exec() */
544 char *interpreter
= NULL
;
545 char *quoted_filename
;
547 char *pEnvBlock
, *pNext
;
550 int iEnvBlockLen
= 1;
552 file_type_e fileType
;
555 PROCESS_INFORMATION pi
;
560 pool
* tp
= ap_make_sub_pool(fcgi_config_pool
);
562 HANDLE listen_handle
;
563 char * termination_env_string
= NULL
;
565 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
566 if (process
->terminationEvent
== NULL
)
568 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
569 "FastCGI: can't create termination event for server \"%s\", "
570 "CreateEvent() failed", fs
->fs_path
);
573 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
575 termination_env_string
= ap_psprintf(tp
,
576 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
580 SECURITY_ATTRIBUTES sa
;
582 sa
.lpSecurityDescriptor
= NULL
;
583 sa
.bInheritHandle
= TRUE
;
584 sa
.nLength
= sizeof(sa
);
586 listen_handle
= CreateNamedPipe(fs
->socket_path
,
588 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
589 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
591 if (listen_handle
== INVALID_HANDLE_VALUE
)
593 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
594 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs
->fs_path
);
600 listen_handle
= (HANDLE
) fs
->listenFd
;
603 memset(&si
, 0, sizeof(si
));
604 memset(&pi
, 0, sizeof(pi
));
605 memset(&r
, 0, sizeof(r
));
607 // Can up a fake request to pass to ap_get_win32_interpreter()
608 r
.per_dir_config
= fcgi_apache_main_server
->lookup_defaults
;
609 r
.server
= fcgi_apache_main_server
;
610 r
.filename
= (char *) fs
->fs_path
;
613 fileType
= ap_get_win32_interpreter(&r
, &interpreter
);
615 if (fileType
== eFileTypeUNKNOWN
) {
616 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
617 "FastCGI: %s is not executable; ensure interpreted scripts have "
618 "\"#!\" as their first line",
625 * We have the interpreter (if there is one) and we have
626 * the arguments (if there are any).
627 * Build the command string to pass to CreateProcess.
629 quoted_filename
= ap_pstrcat(tp
, "\"", fs
->fs_path
, "\"", NULL
);
630 if (interpreter
&& *interpreter
) {
631 pCommand
= ap_pstrcat(tp
, interpreter
, " ", quoted_filename
, NULL
);
634 pCommand
= quoted_filename
;
638 * Make child process use hPipeOutputWrite as standard out,
639 * and make sure it does not show on screen.
642 si
.dwFlags
= STARTF_USESHOWWINDOW
| STARTF_USESTDHANDLES
;
643 si
.wShowWindow
= SW_HIDE
;
644 si
.hStdInput
= listen_handle
;
646 // XXX These should be open to the error_log
647 si
.hStdOutput
= INVALID_HANDLE_VALUE
;
648 si
.hStdError
= INVALID_HANDLE_VALUE
;
651 * Win32's CreateProcess call requires that the environment
652 * be passed in an environment block, a null terminated block of
653 * null terminated strings.
654 * @todo we should store the env in this format for win32.
658 iEnvBlockLen
+= strlen(fs
->envp
[i
]) + 1;
662 iEnvBlockLen
+= strlen(termination_env_string
) + 1;
663 iEnvBlockLen
+= strlen(fs
->mutex_env_string
) + 1;
665 pEnvBlock
= (char *) ap_pcalloc(tp
, iEnvBlockLen
);
671 strcpy(pNext
, fs
->envp
[i
]);
672 pNext
+= strlen(pNext
) + 1;
676 strcpy(pNext
, termination_env_string
);
677 pNext
+= strlen(pNext
) + 1;
678 strcpy(pNext
, fs
->mutex_env_string
);
680 if (CreateProcess(NULL
, pCommand
, NULL
, NULL
, TRUE
,
683 ap_make_dirstr_parent(tp
, fs
->fs_path
),
686 /* Hack to get 16-bit CGI's working. It works for all the
687 * standard modules shipped with Apache. pi.dwProcessId is 0
688 * for 16-bit CGIs and all the Unix specific code that calls
689 * ap_call_exec interprets this as a failure case. And we can't
690 * use -1 either because it is mapped to 0 by the caller.
692 pid
= (fileType
== eFileTypeEXE16
) ? -2 : pi
.dwProcessId
;
694 process
->handle
= pi
.hProcess
;
695 CloseHandle(pi
.hThread
);
700 CloseHandle(listen_handle
);
709 #endif /* !APACHE2 */
714 static void reduce_privileges(void)
722 /* Get username if passed as a uid */
723 if (ap_user_name
[0] == '#') {
724 uid_t uid
= atoi(&ap_user_name
[1]);
725 struct passwd
*ent
= getpwuid(uid
);
728 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
729 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
730 "you probably need to modify the User directive", (unsigned)uid
);
739 if (setgid(ap_group_id
) == -1) {
740 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
741 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id
);
745 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
747 /* Initialize supplementary groups */
748 if (initgroups(name
, ap_group_id
) == -1) {
749 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
750 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
751 name
, (unsigned)ap_group_id
);
758 if (seteuid_user() == -1) {
759 ap_log_error(FCGI_LOG_ALERT_NOERRNO
, fcgi_apache_main_server
,
760 "FastCGI: process manager exiting, failed to reduce privileges");
765 if (setuid(ap_user_id
) == -1) {
766 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
767 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id
);
774 * Change the name of this process - best we can easily.
776 static void change_process_name(const char * const name
)
778 /* under Apache2, ap_server_argv0 is const */
779 strncpy((char *) ap_server_argv0
, name
, strlen(ap_server_argv0
));
783 static void schedule_start(fcgi_server
*s
, int proc
)
785 /* If we've started one recently, don't register another */
786 time_t time_passed
= now
- s
->restartTime
;
788 if ((s
->procs
[proc
].pid
&& (time_passed
< (int) s
->restartDelay
))
789 || ((s
->procs
[proc
].pid
== 0) && (time_passed
< s
->initStartDelay
)))
791 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
);
795 FCGIDBG3("scheduling_start: %s (%d)", s
->fs_path
, proc
);
796 s
->procs
[proc
].state
= FCGI_START_STATE
;
797 if (proc
== dynamicMaxClassProcs
- 1) {
798 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
799 "FastCGI: scheduled the %sstart of the last (dynamic) server "
800 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
801 s
->procs
[proc
].pid
? "re" : "", s
->fs_path
, dynamicMaxClassProcs
);
806 *----------------------------------------------------------------------
810 * Removes the records written by request handlers and decodes them.
811 * We also update the data structures to reflect the changes.
813 *----------------------------------------------------------------------
816 static void dynamic_read_msgs(int read_ready
)
822 static int buflen
= 0;
823 static char buf
[FCGI_MSGS_BUFSIZE
+ 1];
824 char *ptr1
, *ptr2
, opcode
;
825 char execName
[FCGI_MAXPATH
+ 1];
826 char user
[MAX_USER_NAME_LEN
+ 2];
827 char group
[MAX_GID_CHAR_LEN
+ 1];
828 unsigned long q_usec
= 0UL, req_usec
= 0UL;
830 fcgi_pm_job
*joblist
= NULL
;
831 fcgi_pm_job
*cjob
= NULL
;
834 pool
*sp
= NULL
, *tp
;
837 user
[MAX_USER_NAME_LEN
+ 1] = group
[MAX_GID_CHAR_LEN
] = '\0';
841 * To prevent the idle application from running indefinitely, we
842 * check the timer and if it is expired, we recompute the values
843 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
844 * message is received, only updates are made to the data structures.
846 if (fcgi_dynamic_last_analyzed
== 0) {
847 fcgi_dynamic_last_analyzed
= now
;
849 if ((now
- fcgi_dynamic_last_analyzed
) >= (int)dynamicUpdateInterval
) {
850 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
851 if (s
->directive
!= APP_CLASS_DYNAMIC
)
854 /* Advance the last analyzed timestamp by the elapsed time since
855 * it was last set. Round the increase down to the nearest
856 * multiple of dynamicUpdateInterval */
858 fcgi_dynamic_last_analyzed
+= (((long)(now
-fcgi_dynamic_last_analyzed
)/dynamicUpdateInterval
)*dynamicUpdateInterval
);
859 s
->smoothConnTime
= (unsigned long) ((1.0-dynamicGain
)*s
->smoothConnTime
+ dynamicGain
*s
->totalConnTime
);
860 s
->totalConnTime
= 0UL;
861 s
->totalQueueTime
= 0UL;
865 if (read_ready
<= 0) {
870 rc
= read(fcgi_pm_pipe
[0], (void *)(buf
+ buflen
), FCGI_MSGS_BUFSIZE
- buflen
);
872 if (!caughtSigTerm
) {
873 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
874 "FastCGI: read() from pipe failed (%d)", rc
);
876 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
877 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
878 caughtSigTerm
= TRUE
;
888 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
889 * request to do something) and/or when a timeout expires.
890 * There really should be no reason why this wait would get stuck
891 * but there's no point in waiting forever. */
893 rc
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
895 if (rc
!= WAIT_OBJECT_0
&& rc
!= WAIT_ABANDONED
)
897 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
898 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
902 joblist
= fcgi_dynamic_mbox
;
903 fcgi_dynamic_mbox
= NULL
;
905 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
907 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
908 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
915 apr_pool_create(&tp
, fcgi_config_pool
);
917 tp
= ap_make_sub_pool(fcgi_config_pool
);
921 for (ptr1
= buf
; ptr1
; ptr1
= ptr2
) {
924 ptr2
= strchr(ptr1
, '*');
936 case FCGI_SERVER_START_JOB
:
937 case FCGI_SERVER_RESTART_JOB
:
939 if (sscanf(ptr1
, "%c %s %16s %15s",
940 &opcode
, execName
, user
, group
) != 4)
946 case FCGI_REQUEST_TIMEOUT_JOB
:
948 if (sscanf(ptr1
, "%c %s %16s %15s",
949 &opcode
, execName
, user
, group
) != 4)
955 case FCGI_REQUEST_COMPLETE_JOB
:
957 if (sscanf(ptr1
, "%c %s %16s %15s %lu %lu",
958 &opcode
, execName
, user
, group
, &q_usec
, &req_usec
) != 6)
970 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode
, execName
, user
, group
, q_usec
, req_usec
);
973 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
974 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1
);
978 /* Update data structures for processing */
979 while (cjob
!= NULL
) {
980 joblist
= cjob
->next
;
981 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob
->id
, cjob
->fs_path
, cjob
->user
, cjob
->group
, cjob
->qsec
, cjob
->start_time
);
985 s
= fcgi_util_fs_get(execName
, user
, group
);
987 s
= fcgi_util_fs_get(cjob
->fs_path
, cjob
->user
, cjob
->group
);
991 if (s
==NULL
&& opcode
!= FCGI_REQUEST_COMPLETE_JOB
)
993 if (s
==NULL
&& cjob
->id
!= FCGI_REQUEST_COMPLETE_JOB
)
998 HANDLE mutex
= CreateMutex(NULL
, FALSE
, cjob
->fs_path
);
1002 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1003 "FastCGI: can't create accept mutex "
1004 "for (dynamic) server \"%s\"", cjob
->fs_path
);
1008 SetHandleInformation(mutex
, HANDLE_FLAG_INHERIT
, TRUE
);
1013 /* Create a perm subpool to hold the new server data,
1014 * we can destroy it if something doesn't pan out */
1016 apr_pool_create(&sp
, fcgi_config_pool
);
1018 sp
= ap_make_sub_pool(fcgi_config_pool
);
1021 /* Create a new "dynamic" server */
1022 s
= fcgi_util_fs_new(sp
);
1024 s
->directive
= APP_CLASS_DYNAMIC
;
1025 s
->restartDelay
= dynamicRestartDelay
;
1026 s
->listenQueueDepth
= dynamicListenQueueDepth
;
1027 s
->initStartDelay
= dynamicInitStartDelay
;
1028 s
->envp
= dynamicEnvp
;
1029 s
->flush
= dynamicFlush
;
1032 s
->mutex_env_string
= ap_psprintf(sp
, "_FCGI_MUTEX_=%ld", mutex
);
1033 s
->fs_path
= ap_pstrdup(sp
, cjob
->fs_path
);
1035 s
->fs_path
= ap_pstrdup(sp
, execName
);
1037 ap_getparents(s
->fs_path
);
1038 ap_no2slash(s
->fs_path
);
1039 s
->procs
= fcgi_util_fs_create_procs(sp
, dynamicMaxClassProcs
);
1041 /* XXX the socket_path (both Unix and Win) *is* deducible and
1042 * thus can and will be used by other apache instances without
1043 * the use of shared data regarding the processes serving the
1044 * requests. This can result in slightly unintuitive process
1045 * counts and security implications. This is prevented
1046 * if suexec (Unix) is in use. This is both a feature and a flaw.
1047 * Changing it now would break existing installations. */
1050 /* Create socket file's path */
1051 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, execName
, user
, group
);
1052 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1054 /* Create sockaddr, prealloc it so it won't get created in tp */
1055 s
->socket_addr
= ap_pcalloc(sp
, sizeof(struct sockaddr_un
));
1056 err
= fcgi_util_socket_make_domain_addr(tp
, (struct sockaddr_un
**)&s
->socket_addr
,
1057 &s
->socket_addr_len
, s
->socket_path
);
1059 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1060 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
1064 if (init_listen_sock(s
)) {
1068 /* If a wrapper is being used, config user/group info */
1070 if (user
[0] == '~') {
1071 /* its a user dir uri, the rest is a username, not a uid */
1072 struct passwd
*pw
= getpwnam(&user
[1]);
1075 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1076 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1077 execName
, &user
[1]);
1080 s
->uid
= pw
->pw_uid
;
1081 s
->user
= ap_pstrdup(sp
, user
);
1082 s
->username
= s
->user
;
1084 s
->gid
= pw
->pw_gid
;
1085 s
->group
= ap_psprintf(sp
, "%ld", (long)s
->gid
);
1090 s
->uid
= (uid_t
)atol(user
);
1091 pw
= getpwuid(s
->uid
);
1093 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1094 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1095 execName
, (long)s
->uid
);
1098 s
->user
= ap_pstrdup(sp
, user
);
1099 s
->username
= ap_pstrdup(sp
, pw
->pw_name
);
1101 s
->gid
= (gid_t
)atol(group
);
1102 s
->group
= ap_pstrdup(sp
, group
);
1106 /* Create socket file's path */
1107 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, cjob
->fs_path
, cjob
->user
, cjob
->group
);
1108 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1112 fcgi_util_fs_add(s
);
1116 if (opcode
== FCGI_SERVER_RESTART_JOB
) {
1118 if (cjob
->id
==FCGI_SERVER_RESTART_JOB
) {
1120 /* Check to see if the binary has changed. If so,
1121 * kill the FCGI application processes, and
1127 char * app_path
= cjob
->fs_path
;
1129 char * app_path
= execName
;
1132 if (stat(app_path
, &stbuf
) == 0 && stbuf
.st_mtime
> s
->startTime
)
1136 /* prevent addition restart requests */
1139 utime(s
->socket_path
, NULL
);
1142 /* kill old server(s) */
1143 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1145 if (s
->procs
[i
].pid
> 0
1146 && stbuf
.st_mtime
> s
->procs
[i
].start_time
)
1148 fcgi_kill(&s
->procs
[i
], SIGTERM
);
1155 ap_log_error(FCGI_LOG_WARN_NOERRNO
,
1156 fcgi_apache_main_server
, "FastCGI: restarting "
1157 "old server \"%s\" processes, newer version "
1162 /* If dynamicAutoRestart, don't mark any new processes
1163 * for starting because we probably got the
1164 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1165 * will be restarting all of those we just killed.
1167 if (dynamicAutoRestart
)
1171 else if (opcode
== FCGI_SERVER_START_JOB
) {
1173 else if (cjob
->id
==FCGI_SERVER_START_JOB
) {
1175 /* we've been asked to start a process--only start
1176 * it if we're not already running at least one
1181 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1182 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1185 /* if already running, don't start another one */
1186 if (i
< dynamicMaxClassProcs
) {
1200 case FCGI_SERVER_RESTART_JOB
:
1204 /* We just waxed 'em all. Try to find an idle slot. */
1206 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1208 if (s
->procs
[i
].state
== FCGI_START_STATE
1209 || s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1213 else if (s
->procs
[i
].state
== FCGI_KILLED_STATE
1214 || s
->procs
[i
].state
== FCGI_READY_STATE
)
1221 /* Nope, just use the first slot */
1222 if (i
== dynamicMaxClassProcs
)
1230 schedule_start(s
, i
);
1235 case FCGI_SERVER_START_JOB
:
1236 case FCGI_REQUEST_TIMEOUT_JOB
:
1238 if ((fcgi_dynamic_total_proc_count
+ 1) > (int) dynamicMaxProcs
) {
1240 * Extra instances should have been
1241 * terminated beforehand, probably need
1242 * to increase ProcessSlack parameter
1244 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1245 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1246 "exceeded dynamicMaxProcs (%d)", s
->fs_path
, dynamicMaxProcs
);
1250 /* find next free slot */
1251 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1253 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1255 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i
);
1258 else if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1263 schedule_start(s
, i
);
1268 if (i
>= dynamicMaxClassProcs
) {
1269 FCGIDBG1("ignore_job: slots are max'd");
1273 case FCGI_REQUEST_COMPLETE_JOB
:
1274 /* only record stats if we have a structure */
1277 s
->totalConnTime
+= req_usec
;
1278 s
->totalQueueTime
+= q_usec
;
1280 s
->totalConnTime
+= cjob
->start_time
;
1281 s
->totalQueueTime
+= cjob
->qsec
;
1290 /* Cleanup job data */
1291 free(cjob
->fs_path
);
1301 if (sp
) ap_destroy_pool(sp
);
1304 free(cjob
->fs_path
);
1312 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1313 "FastCGI: really bogus message: \"%s\"", ptr1
);
1314 ptr1
+= strlen(buf
);
1317 buflen
-= ptr1
- buf
;
1319 memmove(buf
, ptr1
, buflen
);
1323 ap_destroy_pool(tp
);
1327 *----------------------------------------------------------------------
1329 * dynamic_kill_idle_fs_procs
1331 * Implement a kill policy for the dynamic FastCGI applications.
1332 * We also update the data structures to reflect the changes.
1335 * Processes are marked for deletion possibly killed.
1337 *----------------------------------------------------------------------
1339 static void dynamic_kill_idle_fs_procs(void)
1344 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1347 * server's smoothed running time, or if that's 0, the current total
1349 unsigned long connTime
;
1352 * maximum number of microseconds that all of a server's running
1353 * processes together could have spent running since the last check
1355 unsigned long totalTime
;
1358 * percentage, 0-100, of totalTime that the processes actually used
1363 int really_running
= 0;
1365 if (s
->directive
!= APP_CLASS_DYNAMIC
|| s
->numProcesses
== 0)
1370 /* s->numProcesses includes pending kills so get the "active" count */
1371 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1373 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
) ++really_running
;
1376 connTime
= s
->smoothConnTime
? s
->smoothConnTime
: s
->totalConnTime
;
1377 totalTime
= really_running
* (now
- fcgi_dynamic_epoch
) * 1000000 + 1;
1379 loadFactor
= 100 * connTime
/ totalTime
;
1381 if (really_running
== 1)
1383 if (loadFactor
>= dynamicThreshold1
)
1390 int load
= really_running
/ ( really_running
- 1) * loadFactor
;
1392 if (load
>= dynamicThresholdN
)
1399 * Run through the procs to see if we can get away w/o waxing one.
1401 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1403 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1405 s
->procs
[i
].state
= FCGI_READY_STATE
;
1408 else if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
)
1414 if (i
>= dynamicMaxClassProcs
)
1416 ServerProcess
* procs
= s
->procs
;
1419 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1421 if (procs
[i
].state
== FCGI_RUNNING_STATE
)
1423 if (youngest
== -1 || procs
[i
].start_time
>= procs
[youngest
].start_time
)
1432 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1433 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1434 s
->fs_path
, (long) s
->procs
[youngest
].pid
);
1436 fcgi_kill(&s
->procs
[youngest
], SIGTERM
);
1442 * If the number of non-victims is less than or equal to
1443 * the minimum that may be running without being killed off,
1444 * don't select any more victims.
1446 if (fcgi_dynamic_total_proc_count
- victims
<= dynamicMinProcs
)
1456 // This is a little bogus, there's gotta be a better way to do this
1457 // Can we use WaitForMultipleObjects()
1458 #define FCGI_PROC_WAIT_TIME 100
1460 void child_wait_thread_main(void *dummy
) {
1462 DWORD dwRet
= WAIT_TIMEOUT
;
1467 while (!bTimeToDie
) {
1470 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1471 if (s
->directive
== APP_CLASS_EXTERNAL
|| s
->listenFd
< 0) {
1474 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1475 numChildren
= dynamicMaxClassProcs
;
1478 numChildren
= s
->numProcesses
;
1481 for (i
=0; i
< numChildren
; i
++) {
1482 if (s
->procs
[i
].handle
!= INVALID_HANDLE_VALUE
)
1484 DWORD exitStatus
= 0;
1486 /* timeout is currently set for 100 miliecond */
1487 /* it may need to be longer or user customizable */
1488 dwRet
= WaitForSingleObject(s
->procs
[i
].handle
, FCGI_PROC_WAIT_TIME
);
1492 if (dwRet
!= WAIT_TIMEOUT
&& dwRet
!= WAIT_FAILED
) {
1493 /* a child fs has died */
1494 /* mark the child as dead */
1496 if (s
->directive
== APP_CLASS_STANDARD
) {
1497 /* restart static app */
1498 s
->procs
[i
].state
= FCGI_START_STATE
;
1503 fcgi_dynamic_total_proc_count
--;
1504 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1506 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1507 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1510 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1513 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0)) {
1514 s
->procs
[i
].state
= FCGI_START_STATE
;
1517 s
->procs
[i
].state
= FCGI_READY_STATE
;
1522 GetExitCodeProcess(s
->procs
[i
].handle
, &exitStatus
);
1524 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1525 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1526 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1527 s
->fs_path
, (long) s
->procs
[i
].pid
, exitStatus
);
1529 CloseHandle(s
->procs
[i
].handle
);
1530 s
->procs
[i
].handle
= INVALID_HANDLE_VALUE
;
1531 s
->procs
[i
].pid
= -1;
1533 /* wake up the main thread */
1534 SetEvent(fcgi_event_handles
[WAKE_EVENT
]);
1539 Sleep(waited
? 0 : FCGI_PROC_WAIT_TIME
);
1545 static void setup_signals(void)
1547 struct sigaction sa
;
1549 /* Setup handlers */
1551 sa
.sa_handler
= signal_handler
;
1552 sigemptyset(&sa
.sa_mask
);
1555 if (sigaction(SIGTERM
, &sa
, NULL
) < 0) {
1556 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1557 "sigaction(SIGTERM) failed");
1560 if (sigaction(SIGHUP
, &sa
, NULL
) < 0) {
1561 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1562 "sigaction(SIGHUP) failed");
1564 /* httpd graceful restart */
1565 if (sigaction(SIGUSR1
, &sa
, NULL
) < 0) {
1566 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1567 "sigaction(SIGUSR1) failed");
1569 /* read messages from request handlers - kill interval expired */
1570 if (sigaction(SIGALRM
, &sa
, NULL
) < 0) {
1571 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1572 "sigaction(SIGALRM) failed");
1574 if (sigaction(SIGCHLD
, &sa
, NULL
) < 0) {
1575 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1576 "sigaction(SIGCHLD) failed");
1581 #if !defined(WIN32) && !defined(APACHE2)
1582 int fcgi_pm_main(void *dummy
, child_info
*info
)
1584 void fcgi_pm_main(void *dummy
)
1594 HANDLE child_wait_thread
= INVALID_HANDLE_VALUE
;
1596 int callWaitPid
, callDynamicProcs
;
1600 // Add SystemRoot to the dynamic environment
1601 char ** envp
= dynamicEnvp
;
1602 for (i
= 0; *envp
; ++i
) {
1605 fcgi_config_set_env_var(fcgi_config_pool
, dynamicEnvp
, &i
, "SystemRoot");
1609 reduce_privileges();
1610 change_process_name("fcgi-pm");
1612 close(fcgi_pm_pipe
[1]);
1616 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1617 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper
);
1621 /* Initialize AppClass */
1622 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1624 if (s
->directive
!= APP_CLASS_STANDARD
)
1632 for (i
= 0; i
< s
->numProcesses
; ++i
)
1633 s
->procs
[i
].state
= FCGI_START_STATE
;
1637 child_wait_thread
= (HANDLE
) _beginthread(child_wait_thread_main
, 0, NULL
);
1639 if (child_wait_thread
== (HANDLE
) -1)
1641 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1642 "FastCGI: failed to create process manager's wait thread!");
1645 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1646 "FastCGI: process manager initialized");
1648 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1649 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1655 * Loop until SIGTERM
1658 int sleepSeconds
= min(dynamicKillInterval
, dynamicUpdateInterval
);
1665 unsigned int numChildren
;
1668 * If we came out of sigsuspend() for any reason other than
1669 * SIGALRM, pick up where we left off.
1672 sleepSeconds
= alarmLeft
;
1675 * Examine each configured AppClass for a process that needs
1676 * starting. Compute the earliest time when the start should
1677 * be attempted, starting it now if the time has passed. Also,
1678 * remember that we do NOT need to restart externally managed
1679 * FastCGI applications.
1681 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1683 if (s
->directive
== APP_CLASS_EXTERNAL
)
1686 numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
1687 ? dynamicMaxClassProcs
1690 for (i
= 0; i
< numChildren
; ++i
)
1692 if (s
->procs
[i
].pid
<= 0 && s
->procs
[i
].state
== FCGI_START_STATE
)
1694 int restart
= (s
->procs
[i
].pid
< 0);
1695 time_t restartTime
= s
->restartTime
;
1699 /* we've gone to using the badDelay, the only thing that
1700 resets bad is when badDelay has expired. but numFailures
1701 is only just set below its threshold. the proc's
1702 start_times are all reset when the bad is. the numFailures
1703 is reset when we see an app run for a period */
1705 s
->procs
[i
].start_time
= 0;
1708 if (s
->numFailures
> MAX_FAILED_STARTS
)
1710 time_t last_start_time
= s
->procs
[i
].start_time
;
1712 if (last_start_time
&& now
- last_start_time
> RUNTIME_SUCCESS_INTERVAL
)
1716 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1717 "FastCGI:%s server \"%s\" has remained"
1718 " running for more than %d seconds, its restart"
1719 " interval has been restored to %d seconds",
1720 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1721 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, s
->restartDelay
);
1727 for (j
= 0; j
< numChildren
; ++j
)
1729 if (s
->procs
[j
].pid
<= 0) continue;
1730 if (s
->procs
[j
].state
!= FCGI_RUNNING_STATE
) continue;
1731 if (s
->procs
[j
].start_time
== 0) continue;
1732 if (now
- s
->procs
[j
].start_time
> RUNTIME_SUCCESS_INTERVAL
) break;
1735 if (j
>= numChildren
)
1738 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1739 "FastCGI:%s server \"%s\" has failed to remain"
1740 " running for %d seconds given %d attempts, its restart"
1741 " interval has been backed off to %d seconds",
1742 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1743 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, MAX_FAILED_STARTS
,
1744 FAILED_STARTS_DELAY
);
1750 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1751 "FastCGI:%s server \"%s\" has remained"
1752 " running for more than %d seconds, its restart"
1753 " interval has been restored to %d seconds",
1754 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1755 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, s
->restartDelay
);
1762 restartTime
+= FAILED_STARTS_DELAY
;
1766 restartTime
+= (restart
) ? s
->restartDelay
: s
->initStartDelay
;
1769 if (restartTime
<= now
)
1774 s
->numFailures
= MAX_FAILED_STARTS
;
1777 if (s
->listenFd
< 0 && init_listen_sock(s
))
1779 if (sleepSeconds
> s
->initStartDelay
)
1780 sleepSeconds
= s
->initStartDelay
;
1784 if (caughtSigTerm
) {
1785 goto ProcessSigTerm
;
1788 s
->procs
[i
].pid
= spawn_fs_process(s
, &s
->procs
[i
]);
1789 if (s
->procs
[i
].pid
<= 0) {
1790 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1791 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1792 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1795 sleepSeconds
= min(sleepSeconds
,
1796 max((int) s
->restartDelay
, FCGI_MIN_EXEC_RETRY_DELAY
));
1798 ap_assert(s
->procs
[i
].pid
< 0);
1802 s
->procs
[i
].start_time
= now
;
1803 s
->restartTime
= now
;
1805 if (s
->startTime
== 0) {
1809 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1811 fcgi_dynamic_total_proc_count
++;
1812 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1815 s
->procs
[i
].state
= FCGI_RUNNING_STATE
;
1818 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1819 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1820 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1821 s
->fs_path
, (long) s
->uid
, (long) s
->gid
,
1822 restart
? "re" : "", (long) s
->procs
[i
].pid
);
1825 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1826 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1827 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1828 s
->fs_path
, restart
? "re" : "", (long) s
->procs
[i
].pid
);
1830 ap_assert(s
->procs
[i
].pid
> 0);
1832 sleepSeconds
= min(sleepSeconds
, restartTime
- now
);
1841 goto ProcessSigTerm
;
1843 if((!caughtSigChld
) && (!caughtSigAlarm
)) {
1846 alarm(sleepSeconds
);
1849 FD_SET(fcgi_pm_pipe
[0], &rfds
);
1850 read_ready
= ap_select(fcgi_pm_pipe
[0] + 1, &rfds
, NULL
, NULL
, NULL
);
1852 alarmLeft
= alarm(0);
1854 callWaitPid
= caughtSigChld
;
1855 caughtSigChld
= FALSE
;
1856 callDynamicProcs
= caughtSigAlarm
;
1857 caughtSigAlarm
= FALSE
;
1862 * Dynamic fcgi process management
1864 if((callDynamicProcs
) || (!callWaitPid
)) {
1865 dynamic_read_msgs(read_ready
);
1866 if(fcgi_dynamic_epoch
== 0) {
1867 fcgi_dynamic_epoch
= now
;
1869 if(((long)(now
-fcgi_dynamic_epoch
)>=dynamicKillInterval
) ||
1870 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
)>=dynamicMaxProcs
)) {
1871 dynamic_kill_idle_fs_procs();
1872 fcgi_dynamic_epoch
= now
;
1880 /* We've caught SIGCHLD, so find out who it was using waitpid,
1881 * write a log message and update its data structure. */
1885 goto ProcessSigTerm
;
1887 childPid
= waitpid(-1, &waitStatus
, WNOHANG
);
1889 if (childPid
== -1 || childPid
== 0)
1892 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1893 if (s
->directive
== APP_CLASS_EXTERNAL
)
1896 if (s
->directive
== APP_CLASS_DYNAMIC
)
1897 numChildren
= dynamicMaxClassProcs
;
1899 numChildren
= s
->numProcesses
;
1901 for (i
= 0; i
< numChildren
; i
++) {
1902 if (s
->procs
[i
].pid
== childPid
)
1907 /* TODO: print something about this unknown child */
1911 s
->procs
[i
].pid
= -1;
1913 if (s
->directive
== APP_CLASS_STANDARD
) {
1914 /* Always restart static apps */
1915 s
->procs
[i
].state
= FCGI_START_STATE
;
1920 fcgi_dynamic_total_proc_count
--;
1922 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1923 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1926 /* A dynamic app died or exited without provocation from the PM */
1929 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0))
1930 s
->procs
[i
].state
= FCGI_START_STATE
;
1932 s
->procs
[i
].state
= FCGI_READY_STATE
;
1936 if (WIFEXITED(waitStatus
)) {
1937 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1938 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1939 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1940 s
->fs_path
, (long) childPid
, WEXITSTATUS(waitStatus
));
1942 else if (WIFSIGNALED(waitStatus
)) {
1943 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1944 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1945 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1946 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
),
1948 WCOREDUMP(waitStatus
) ? ", a core file may have been generated" : "");
1953 else if (WIFSTOPPED(waitStatus
)) {
1954 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1955 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1956 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1957 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
));
1959 } /* for (;;), waitpid() */
1963 /* wait for an event to occur or timer expires */
1964 expire
= time(NULL
) + sleepSeconds
;
1965 dwRet
= WaitForMultipleObjects(3, (HANDLE
*) fcgi_event_handles
, FALSE
, sleepSeconds
* 1000);
1967 if (dwRet
== WAIT_FAILED
) {
1968 /* There is something seriously wrong here */
1969 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1970 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1974 if (dwRet
!= WAIT_TIMEOUT
) {
1978 alarmLeft
= expire
- now
;
1982 * Dynamic fcgi process management
1984 if ((dwRet
== MBOX_EVENT
) || (dwRet
== WAIT_TIMEOUT
)) {
1985 if (dwRet
== MBOX_EVENT
) {
1991 dynamic_read_msgs(read_ready
);
1993 if(fcgi_dynamic_epoch
== 0) {
1994 fcgi_dynamic_epoch
= now
;
1997 if ((now
-fcgi_dynamic_epoch
>= (int) dynamicKillInterval
) ||
1998 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
) >= dynamicMaxProcs
)) {
1999 dynamic_kill_idle_fs_procs();
2000 fcgi_dynamic_epoch
= now
;
2004 else if (dwRet
== WAKE_EVENT
) {
2007 else if (dwRet
== TERM_EVENT
) {
2008 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
2009 "FastCGI: Termination event received process manager shutting down");
2012 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
2014 goto ProcessSigTerm
;
2017 // Have an received an unknown event - should not happen
2018 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
2019 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
2022 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
2024 goto ProcessSigTerm
;
2029 } /* for (;;), the whole shoot'n match */
2033 * Kill off the children, then exit.
2045 int fcgi_pm_add_job(fcgi_pm_job
*new_job
)
2047 int rv
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
2049 if (rv
!= WAIT_OBJECT_0
&& rv
!= WAIT_ABANDONED
)
2051 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2052 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2056 new_job
->next
= fcgi_dynamic_mbox
;
2057 fcgi_dynamic_mbox
= new_job
;
2059 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
2061 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2062 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");