2 * $Id: fcgi_pm.c,v 1.72 2002/07/23 00:54:18 robs Exp $
10 #define seteuid(arg) setresuid(-1, (arg), -1)
13 int fcgi_dynamic_total_proc_count
= 0; /* number of running apps */
14 time_t fcgi_dynamic_epoch
= 0; /* last time kill_procs was
15 * invoked by process mgr */
16 time_t fcgi_dynamic_last_analyzed
= 0; /* last time calculation was
17 * made for the dynamic procs */
19 static time_t now
= 0;
22 #pragma warning ( disable : 4100 4102 )
23 static BOOL bTimeToDie
= FALSE
; /* process termination flag */
24 HANDLE fcgi_event_handles
[3];
32 static int seteuid_root(void)
34 int rc
= seteuid((uid_t
)0);
36 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
37 "FastCGI: seteuid(0) failed");
42 static int seteuid_user(void)
44 int rc
= seteuid(ap_user_id
);
46 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
47 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id
);
54 * Signal the process to exit. How (or if) the process responds
55 * depends on the FastCGI application library (esp. on Win32) and
56 * possibly application code (signal handlers and whether or not
57 * SA_RESTART is on). At any rate, we send the signal with the
58 * hopes that the process will exit on its own. Later, as we
59 * review the state of application processes, if we see one marked
60 * for death, but that hasn't died within a specified period of
61 * time, fcgi_kill() is called again with a KILL)
63 static void fcgi_kill(ServerProcess
*process
, int sig
)
65 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process
->pid
, sig
);
67 process
->state
= FCGI_VICTIM_STATE
;
72 SetEvent(process
->terminationEvent
);
74 else if (sig
== SIGKILL
)
76 TerminateProcess(process
->handle
, 1);
88 kill(process
->pid
, sig
);
97 /*******************************************************************************
98 * Send SIGTERM to each process in the server class, remove socket
99 * file if appropriate. Currently this is only called when the PM is shutting
100 * down and thus memory isn't freed and sockets and files aren't closed.
102 static void shutdown_all()
104 fcgi_server
*s
= fcgi_servers
;
108 ServerProcess
*proc
= s
->procs
;
110 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
111 ? dynamicMaxClassProcs
115 if (s
->socket_path
!= NULL
&& s
->directive
!= APP_CLASS_EXTERNAL
)
117 /* Remove the socket file */
118 if (unlink(s
->socket_path
) != 0 && errno
!= ENOENT
) {
119 ap_log_error(FCGI_LOG_ERR
, fcgi_apache_main_server
,
120 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
122 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "", s
->fs_path
);
127 /* Send TERM to all processes */
128 for (i
= 0; i
< numChildren
; i
++, proc
++)
130 if (proc
->state
== FCGI_RUNNING_STATE
)
132 fcgi_kill(proc
, SIGTERM
);
139 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
142 * WIN32 applications may not have support for the shutdown event
143 * depending on their application library version
146 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT
);
151 ServerProcess
*proc
= s
->procs
;
153 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
154 ? dynamicMaxClassProcs
157 /* Send KILL to all processes */
158 for (i
= 0; i
< numChildren
; i
++, proc
++)
160 if (proc
->state
== FCGI_RUNNING_STATE
)
162 fcgi_kill(proc
, SIGKILL
);
172 static int init_listen_sock(fcgi_server
* fs
)
174 ap_assert(fs
->directive
!= APP_CLASS_EXTERNAL
);
176 /* Create the socket */
177 if ((fs
->listenFd
= socket(fs
->socket_addr
->sa_family
, SOCK_STREAM
, 0)) < 0)
180 errno
= WSAGetLastError(); // Not sure if this will work as expected
182 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
183 "FastCGI: can't create %sserver \"%s\": socket() failed",
184 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
190 if (fs
->socket_addr
->sa_family
== AF_UNIX
)
192 /* Remove any existing socket file.. just in case */
193 unlink(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
);
199 setsockopt(fs
->listenFd
, SOL_SOCKET
, SO_REUSEADDR
, (char *)&flag
, sizeof(flag
));
202 /* Bind it to the socket_addr */
203 if (bind(fs
->listenFd
, fs
->socket_addr
, fs
->socket_addr_len
))
208 errno
= WSAGetLastError();
210 ap_snprintf(port
, sizeof(port
), "port=%d",
211 ((struct sockaddr_in
*)fs
->socket_addr
)->sin_port
);
213 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
214 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
215 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
218 (fs
->socket_addr
->sa_family
== AF_UNIX
) ?
219 ((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
:
225 /* Twiddle Unix socket permissions */
226 else if (fs
->socket_addr
->sa_family
== AF_UNIX
227 && chmod(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
, S_IRUSR
| S_IWUSR
))
229 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
230 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
231 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
237 else if (listen(fs
->listenFd
, fs
->listenQueueDepth
))
240 errno
= WSAGetLastError();
242 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
243 "FastCGI: can't create %sserver \"%s\": listen() failed",
244 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
258 *----------------------------------------------------------------------
262 * The FastCGI process manager, which runs as a separate
263 * process responsible for:
264 * - Starting all the FastCGI proceses.
265 * - Restarting any of these processes that die (indicated
267 * - Catching SIGTERM and relaying it to all the FastCGI
268 * processes before exiting.
271 * Uses global variable fcgi_servers.
279 *----------------------------------------------------------------------
282 static int caughtSigTerm
= FALSE
;
283 static int caughtSigChld
= FALSE
;
284 static int caughtSigAlarm
= FALSE
;
286 static void signal_handler(int signo
)
288 if ((signo
== SIGTERM
) || (signo
== SIGUSR1
) || (signo
== SIGHUP
)) {
289 /* SIGUSR1 & SIGHUP are sent by apache to its process group
290 * when apache get 'em. Apache follows up (1.2.x) with attacks
291 * on each of its child processes, but we've got the KillMgr
292 * sitting between us so we never see the KILL. The main loop
293 * in ProcMgr also checks to see if the KillMgr has terminated,
294 * and if it has, we handl it as if we should shutdown too. */
295 caughtSigTerm
= TRUE
;
296 } else if(signo
== SIGCHLD
) {
297 caughtSigChld
= TRUE
;
298 } else if(signo
== SIGALRM
) {
299 caughtSigAlarm
= TRUE
;
305 *----------------------------------------------------------------------
307 * spawn_fs_process --
309 * Fork and exec the specified fcgi process.
312 * 0 for successful fork, -1 for failed fork.
314 * In case the child fails before or in the exec, the child
315 * obtains the error log by calling getErrLog, logs
316 * the error, and exits with exit status = errno of
317 * the failed system call.
320 * Child process created.
322 *----------------------------------------------------------------------
324 static pid_t
spawn_fs_process(fcgi_server
*fs
, ServerProcess
*process
)
331 char *dnEnd
, *failedSysCall
;
338 /* We're the child. We're gonna exec() so pools don't matter. */
340 dnEnd
= strrchr(fs
->fs_path
, '/');
344 dirName
= ap_pcalloc(fcgi_config_pool
, dnEnd
- fs
->fs_path
+ 1);
345 dirName
= memcpy(dirName
, fs
->fs_path
, dnEnd
- fs
->fs_path
);
347 if (chdir(dirName
) < 0) {
348 failedSysCall
= "chdir()";
349 goto FailedSystemCallExit
;
353 /* OS/2 dosen't support nice() */
354 if (fs
->processPriority
!= 0) {
355 if (nice(fs
->processPriority
) == -1) {
356 failedSysCall
= "nice()";
357 goto FailedSystemCallExit
;
362 /* Open the listenFd on spec'd fd */
363 if (fs
->listenFd
!= FCGI_LISTENSOCK_FILENO
)
364 dup2(fs
->listenFd
, FCGI_LISTENSOCK_FILENO
);
366 /* Close all other open fds, except stdout/stderr. Leave these two open so
367 * FastCGI applications don't have to find and fix ALL 3rd party libs that
368 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
369 * main server error_log - @@@ provide a directive control where this goes.
371 ap_error_log2stderr(fcgi_apache_main_server
);
372 dup2(STDERR_FILENO
, STDOUT_FILENO
);
373 for (i
= 0; i
< FCGI_MAX_FD
; i
++) {
374 if (i
!= FCGI_LISTENSOCK_FILENO
&& i
!= STDERR_FILENO
&& i
!= STDOUT_FILENO
) {
379 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
380 * install its own handler. */
381 signal(SIGPIPE
, SIG_IGN
);
383 if (fcgi_wrapper
&& (fcgi_user_id
!= fs
->uid
|| fcgi_group_id
!= fs
->gid
)) {
384 char *shortName
= strrchr(fs
->fs_path
, '/') + 1;
386 /* Relinquish our root real uid powers */
391 execle(fcgi_wrapper
, fcgi_wrapper
, fs
->username
, fs
->group
, shortName
, NULL
, fs
->envp
);
392 } while (errno
== EINTR
);
396 execle(fs
->fs_path
, fs
->fs_path
, NULL
, fs
->envp
);
397 } while (errno
== EINTR
);
400 failedSysCall
= "execle()";
402 FailedSystemCallExit
:
403 fprintf(stderr
, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
404 fs
->fs_path
, (long) getpid(), failedSysCall
, strerror(errno
));
407 /* avoid an irrelevant compiler warning */
414 /* based on mod_cgi.c:run_cgi_child() */
417 char * termination_env_string
;
418 HANDLE listen_handle
= INVALID_HANDLE_VALUE
;
419 apr_procattr_t
* procattr
;
420 apr_proc_t proc
= { 0 };
424 if (apr_pool_create(&tp
, fcgi_config_pool
))
427 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
428 if (process
->terminationEvent
== NULL
)
431 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
433 termination_env_string
= ap_psprintf(tp
,
434 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
436 while (fs
->envp
[i
]) i
++;
437 fs
->envp
[i
++] = termination_env_string
;
438 fs
->envp
[i
] = (char *) fs
->mutex_env_string
;
440 ap_assert(fs
->envp
[i
+ 1] == NULL
);
444 SECURITY_ATTRIBUTES sa
= { 0 };
446 sa
.bInheritHandle
= TRUE
;
447 sa
.nLength
= sizeof(sa
);
449 listen_handle
= CreateNamedPipe(fs
->socket_path
,
451 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
452 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
454 if (listen_handle
== INVALID_HANDLE_VALUE
)
456 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
457 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
464 listen_handle
= (HANDLE
) fs
->listenFd
;
467 if (apr_procattr_create(&procattr
, tp
))
470 if (apr_procattr_dir_set(procattr
, ap_make_dirstr_parent(tp
, fs
->fs_path
)))
473 if (apr_procattr_detach_set(procattr
, 1))
476 if (apr_os_file_put(&file
, &listen_handle
, 0, tp
))
479 /* procattr is opaque so we have to use this - unfortuantely it dups */
480 if (apr_procattr_child_in_set(procattr
, file
, NULL
))
483 if (apr_proc_create(&proc
, fs
->fs_path
, NULL
, fs
->envp
, procattr
, tp
))
486 process
->handle
= proc
.hproc
;
493 fs
->envp
[i
- 1] = NULL
;
500 #else /* WIN32 && !APACHE2 */
502 /* Adapted from Apache's util_script.c ap_call_exec() */
503 char *interpreter
= NULL
;
504 char *quoted_filename
;
506 char *pEnvBlock
, *pNext
;
509 int iEnvBlockLen
= 1;
511 file_type_e fileType
;
514 PROCESS_INFORMATION pi
;
519 pool
* tp
= ap_make_sub_pool(fcgi_config_pool
);
521 HANDLE listen_handle
;
522 char * termination_env_string
= NULL
;
524 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
525 if (process
->terminationEvent
== NULL
)
527 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
528 "FastCGI: can't create termination event for server \"%s\", "
529 "CreateEvent() failed", fs
->fs_path
);
532 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
534 termination_env_string
= ap_psprintf(tp
,
535 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
539 SECURITY_ATTRIBUTES sa
;
541 sa
.lpSecurityDescriptor
= NULL
;
542 sa
.bInheritHandle
= TRUE
;
543 sa
.nLength
= sizeof(sa
);
545 listen_handle
= CreateNamedPipe(fs
->socket_path
,
547 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
548 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
550 if (listen_handle
== INVALID_HANDLE_VALUE
)
552 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
553 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs
->fs_path
);
559 listen_handle
= (HANDLE
) fs
->listenFd
;
562 memset(&si
, 0, sizeof(si
));
563 memset(&pi
, 0, sizeof(pi
));
564 memset(&r
, 0, sizeof(r
));
566 // Can up a fake request to pass to ap_get_win32_interpreter()
567 r
.per_dir_config
= fcgi_apache_main_server
->lookup_defaults
;
568 r
.server
= fcgi_apache_main_server
;
569 r
.filename
= (char *) fs
->fs_path
;
572 fileType
= ap_get_win32_interpreter(&r
, &interpreter
);
574 if (fileType
== eFileTypeUNKNOWN
) {
575 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
576 "FastCGI: %s is not executable; ensure interpreted scripts have "
577 "\"#!\" as their first line",
584 * We have the interpreter (if there is one) and we have
585 * the arguments (if there are any).
586 * Build the command string to pass to CreateProcess.
588 quoted_filename
= ap_pstrcat(tp
, "\"", fs
->fs_path
, "\"", NULL
);
589 if (interpreter
&& *interpreter
) {
590 pCommand
= ap_pstrcat(tp
, interpreter
, " ", quoted_filename
, NULL
);
593 pCommand
= quoted_filename
;
597 * Make child process use hPipeOutputWrite as standard out,
598 * and make sure it does not show on screen.
601 si
.dwFlags
= STARTF_USESHOWWINDOW
| STARTF_USESTDHANDLES
;
602 si
.wShowWindow
= SW_HIDE
;
603 si
.hStdInput
= listen_handle
;
605 // XXX These should be open to the error_log
606 si
.hStdOutput
= INVALID_HANDLE_VALUE
;
607 si
.hStdError
= INVALID_HANDLE_VALUE
;
610 * Win32's CreateProcess call requires that the environment
611 * be passed in an environment block, a null terminated block of
612 * null terminated strings.
613 * @todo we should store the env in this format for win32.
617 iEnvBlockLen
+= strlen(fs
->envp
[i
]) + 1;
621 iEnvBlockLen
+= strlen(termination_env_string
) + 1;
622 iEnvBlockLen
+= strlen(fs
->mutex_env_string
) + 1;
624 pEnvBlock
= (char *) ap_pcalloc(tp
, iEnvBlockLen
);
630 strcpy(pNext
, fs
->envp
[i
]);
631 pNext
+= strlen(pNext
) + 1;
635 strcpy(pNext
, termination_env_string
);
636 pNext
+= strlen(pNext
) + 1;
637 strcpy(pNext
, fs
->mutex_env_string
);
639 if (CreateProcess(NULL
, pCommand
, NULL
, NULL
, TRUE
,
642 ap_make_dirstr_parent(tp
, fs
->fs_path
),
645 /* Hack to get 16-bit CGI's working. It works for all the
646 * standard modules shipped with Apache. pi.dwProcessId is 0
647 * for 16-bit CGIs and all the Unix specific code that calls
648 * ap_call_exec interprets this as a failure case. And we can't
649 * use -1 either because it is mapped to 0 by the caller.
651 pid
= (fileType
== eFileTypeEXE16
) ? -2 : pi
.dwProcessId
;
653 process
->handle
= pi
.hProcess
;
654 CloseHandle(pi
.hThread
);
659 CloseHandle(listen_handle
);
668 #endif /* !APACHE2 */
673 static void reduce_privileges(void)
681 /* Get username if passed as a uid */
682 if (ap_user_name
[0] == '#') {
683 uid_t uid
= atoi(&ap_user_name
[1]);
684 struct passwd
*ent
= getpwuid(uid
);
687 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
688 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
689 "you probably need to modify the User directive", (unsigned)uid
);
698 if (setgid(ap_group_id
) == -1) {
699 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
700 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id
);
704 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
706 /* Initialize supplementary groups */
707 if (initgroups(name
, ap_group_id
) == -1) {
708 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
709 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
710 name
, (unsigned)ap_group_id
);
717 if (seteuid_user() == -1) {
718 ap_log_error(FCGI_LOG_ALERT_NOERRNO
, fcgi_apache_main_server
,
719 "FastCGI: process manager exiting, failed to reduce privileges");
724 if (setuid(ap_user_id
) == -1) {
725 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
726 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id
);
733 * Change the name of this process - best we can easily.
735 static void change_process_name(const char * const name
)
737 strncpy(ap_server_argv0
, name
, strlen(ap_server_argv0
));
741 static void schedule_start(fcgi_server
*s
, int proc
)
743 /* If we've started one recently, don't register another */
744 time_t time_passed
= now
- s
->restartTime
;
746 if ((s
->procs
[proc
].pid
&& (time_passed
< (int) s
->restartDelay
))
747 || ((s
->procs
[proc
].pid
== 0) && (time_passed
< s
->initStartDelay
)))
749 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
);
753 FCGIDBG3("scheduling_start: %s (%d)", s
->fs_path
, proc
);
754 s
->procs
[proc
].state
= FCGI_START_STATE
;
755 if (proc
== dynamicMaxClassProcs
- 1) {
756 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
757 "FastCGI: scheduled the %sstart of the last (dynamic) server "
758 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
759 s
->procs
[proc
].pid
? "re" : "", s
->fs_path
, dynamicMaxClassProcs
);
764 *----------------------------------------------------------------------
768 * Removes the records written by request handlers and decodes them.
769 * We also update the data structures to reflect the changes.
771 *----------------------------------------------------------------------
774 static void dynamic_read_msgs(int read_ready
)
780 static int buflen
= 0;
781 static char buf
[FCGI_MSGS_BUFSIZE
+ 1];
782 char *ptr1
, *ptr2
, opcode
;
783 char execName
[FCGI_MAXPATH
+ 1];
784 char user
[MAX_USER_NAME_LEN
+ 2];
785 char group
[MAX_GID_CHAR_LEN
+ 1];
786 unsigned long q_usec
= 0UL, req_usec
= 0UL;
788 fcgi_pm_job
*joblist
= NULL
;
789 fcgi_pm_job
*cjob
= NULL
;
792 pool
*sp
= NULL
, *tp
;
795 user
[MAX_USER_NAME_LEN
+ 1] = group
[MAX_GID_CHAR_LEN
] = '\0';
799 * To prevent the idle application from running indefinitely, we
800 * check the timer and if it is expired, we recompute the values
801 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
802 * message is received, only updates are made to the data structures.
804 if (fcgi_dynamic_last_analyzed
== 0) {
805 fcgi_dynamic_last_analyzed
= now
;
807 if ((now
- fcgi_dynamic_last_analyzed
) >= (int)dynamicUpdateInterval
) {
808 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
809 if (s
->directive
!= APP_CLASS_DYNAMIC
)
812 /* Advance the last analyzed timestamp by the elapsed time since
813 * it was last set. Round the increase down to the nearest
814 * multiple of dynamicUpdateInterval */
816 fcgi_dynamic_last_analyzed
+= (((long)(now
-fcgi_dynamic_last_analyzed
)/dynamicUpdateInterval
)*dynamicUpdateInterval
);
817 s
->smoothConnTime
= (unsigned long) ((1.0-dynamicGain
)*s
->smoothConnTime
+ dynamicGain
*s
->totalConnTime
);
818 s
->totalConnTime
= 0UL;
819 s
->totalQueueTime
= 0UL;
823 if (read_ready
<= 0) {
828 rc
= read(fcgi_pm_pipe
[0], (void *)(buf
+ buflen
), FCGI_MSGS_BUFSIZE
- buflen
);
830 if (!caughtSigTerm
) {
831 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
832 "FastCGI: read() from pipe failed (%d)", rc
);
834 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
835 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
836 caughtSigTerm
= TRUE
;
846 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
847 * request to do something) and/or when a timeout expires.
848 * There really should be no reason why this wait would get stuck
849 * but there's no point in waiting forever. */
851 rc
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
853 if (rc
!= WAIT_OBJECT_0
&& rc
!= WAIT_ABANDONED
)
855 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
856 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
860 joblist
= fcgi_dynamic_mbox
;
861 fcgi_dynamic_mbox
= NULL
;
863 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
865 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
866 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
873 apr_pool_create(&tp
, fcgi_config_pool
);
875 tp
= ap_make_sub_pool(fcgi_config_pool
);
879 for (ptr1
= buf
; ptr1
; ptr1
= ptr2
) {
882 ptr2
= strchr(ptr1
, '*');
894 case FCGI_SERVER_START_JOB
:
895 case FCGI_SERVER_RESTART_JOB
:
897 if (sscanf(ptr1
, "%c %s %16s %15s",
898 &opcode
, execName
, user
, group
) != 4)
904 case FCGI_REQUEST_TIMEOUT_JOB
:
906 if (sscanf(ptr1
, "%c %s %16s %15s",
907 &opcode
, execName
, user
, group
) != 4)
913 case FCGI_REQUEST_COMPLETE_JOB
:
915 if (sscanf(ptr1
, "%c %s %16s %15s %lu %lu",
916 &opcode
, execName
, user
, group
, &q_usec
, &req_usec
) != 6)
928 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode
, execName
, user
, group
, q_usec
, req_usec
);
931 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
932 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1
);
936 /* Update data structures for processing */
937 while (cjob
!= NULL
) {
938 joblist
= cjob
->next
;
939 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob
->id
, cjob
->fs_path
, cjob
->user
, cjob
->group
, cjob
->qsec
, cjob
->start_time
);
943 s
= fcgi_util_fs_get(execName
, user
, group
);
945 s
= fcgi_util_fs_get(cjob
->fs_path
, cjob
->user
, cjob
->group
);
949 if (s
==NULL
&& opcode
!= FCGI_REQUEST_COMPLETE_JOB
)
951 if (s
==NULL
&& cjob
->id
!= FCGI_REQUEST_COMPLETE_JOB
)
956 HANDLE mutex
= CreateMutex(NULL
, FALSE
, cjob
->fs_path
);
960 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
961 "FastCGI: can't create accept mutex "
962 "for (dynamic) server \"%s\"", cjob
->fs_path
);
966 SetHandleInformation(mutex
, HANDLE_FLAG_INHERIT
, TRUE
);
971 /* Create a perm subpool to hold the new server data,
972 * we can destroy it if something doesn't pan out */
974 apr_pool_create(&sp
, fcgi_config_pool
);
976 sp
= ap_make_sub_pool(fcgi_config_pool
);
979 /* Create a new "dynamic" server */
980 s
= fcgi_util_fs_new(sp
);
982 s
->directive
= APP_CLASS_DYNAMIC
;
983 s
->restartDelay
= dynamicRestartDelay
;
984 s
->listenQueueDepth
= dynamicListenQueueDepth
;
985 s
->initStartDelay
= dynamicInitStartDelay
;
986 s
->envp
= dynamicEnvp
;
987 s
->flush
= dynamicFlush
;
990 s
->mutex_env_string
= ap_psprintf(sp
, "_FCGI_MUTEX_=%ld", mutex
);
991 s
->fs_path
= ap_pstrdup(sp
, cjob
->fs_path
);
993 s
->fs_path
= ap_pstrdup(sp
, execName
);
995 ap_getparents(s
->fs_path
);
996 ap_no2slash(s
->fs_path
);
997 s
->procs
= fcgi_util_fs_create_procs(sp
, dynamicMaxClassProcs
);
999 /* XXX the socket_path (both Unix and Win) *is* deducible and
1000 * thus can and will be used by other apache instances without
1001 * the use of shared data regarding the processes serving the
1002 * requests. This can result in slightly unintuitive process
1003 * counts and security implications. This is prevented
1004 * if suexec (Unix) is in use. This is both a feature and a flaw.
1005 * Changing it now would break existing installations. */
1008 /* Create socket file's path */
1009 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, execName
, user
, group
);
1010 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1012 /* Create sockaddr, prealloc it so it won't get created in tp */
1013 s
->socket_addr
= ap_pcalloc(sp
, sizeof(struct sockaddr_un
));
1014 err
= fcgi_util_socket_make_domain_addr(tp
, (struct sockaddr_un
**)&s
->socket_addr
,
1015 &s
->socket_addr_len
, s
->socket_path
);
1017 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1018 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
1022 if (init_listen_sock(s
)) {
1026 /* If a wrapper is being used, config user/group info */
1028 if (user
[0] == '~') {
1029 /* its a user dir uri, the rest is a username, not a uid */
1030 struct passwd
*pw
= getpwnam(&user
[1]);
1033 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1034 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1035 execName
, &user
[1]);
1038 s
->uid
= pw
->pw_uid
;
1039 s
->user
= ap_pstrdup(sp
, user
);
1040 s
->username
= s
->user
;
1042 s
->gid
= pw
->pw_gid
;
1043 s
->group
= ap_psprintf(sp
, "%ld", (long)s
->gid
);
1048 s
->uid
= (uid_t
)atol(user
);
1049 pw
= getpwuid(s
->uid
);
1051 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1052 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1053 execName
, (long)s
->uid
);
1056 s
->user
= ap_pstrdup(sp
, user
);
1057 s
->username
= ap_pstrdup(sp
, pw
->pw_name
);
1059 s
->gid
= (gid_t
)atol(group
);
1060 s
->group
= ap_pstrdup(sp
, group
);
1064 /* Create socket file's path */
1065 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, cjob
->fs_path
, cjob
->user
, cjob
->group
);
1066 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1070 fcgi_util_fs_add(s
);
1074 if (opcode
== FCGI_SERVER_RESTART_JOB
) {
1076 if (cjob
->id
==FCGI_SERVER_RESTART_JOB
) {
1078 /* Check to see if the binary has changed. If so,
1079 * kill the FCGI application processes, and
1086 if ((stat(execName
, &stbuf
)==0) &&
1088 if ((stat(cjob
->fs_path
, &stbuf
)==0) &&
1090 (stbuf
.st_mtime
> s
->restartTime
)) {
1091 /* kill old server(s) */
1092 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1093 if (s
->procs
[i
].pid
> 0) {
1094 fcgi_kill(&s
->procs
[i
], SIGTERM
);
1098 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1099 "FastCGI: restarting server \"%s\" processes, newer version found",
1107 /* If dynamicAutoRestart, don't mark any new processes
1108 * for starting because we probably got the
1109 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1110 * will be restarting all of those we just killed.
1112 if (dynamicAutoRestart
)
1116 else if (opcode
== FCGI_SERVER_START_JOB
) {
1118 else if (cjob
->id
==FCGI_SERVER_START_JOB
) {
1120 /* we've been asked to start a process--only start
1121 * it if we're not already running at least one
1126 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1127 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1130 /* if already running, don't start another one */
1131 if (i
< dynamicMaxClassProcs
) {
1145 case FCGI_SERVER_RESTART_JOB
:
1149 /* We just waxed 'em all. Try to find an idle slot. */
1151 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1153 if (s
->procs
[i
].state
== FCGI_START_STATE
1154 || s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1158 else if (s
->procs
[i
].state
== FCGI_KILLED_STATE
1159 || s
->procs
[i
].state
== FCGI_READY_STATE
)
1166 /* Nope, just use the first slot */
1167 if (i
== dynamicMaxClassProcs
)
1175 schedule_start(s
, i
);
1180 case FCGI_SERVER_START_JOB
:
1181 case FCGI_REQUEST_TIMEOUT_JOB
:
1183 if ((fcgi_dynamic_total_proc_count
+ 1) > (int) dynamicMaxProcs
) {
1185 * Extra instances should have been
1186 * terminated beforehand, probably need
1187 * to increase ProcessSlack parameter
1189 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1190 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1191 "exceeded dynamicMaxProcs (%d)", s
->fs_path
, dynamicMaxProcs
);
1195 /* find next free slot */
1196 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1198 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1200 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i
);
1203 else if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1208 schedule_start(s
, i
);
1213 if (i
>= dynamicMaxClassProcs
) {
1214 FCGIDBG1("ignore_job: slots are max'd");
1218 case FCGI_REQUEST_COMPLETE_JOB
:
1219 /* only record stats if we have a structure */
1222 s
->totalConnTime
+= req_usec
;
1223 s
->totalQueueTime
+= q_usec
;
1225 s
->totalConnTime
+= cjob
->start_time
;
1226 s
->totalQueueTime
+= cjob
->qsec
;
1235 /* Cleanup job data */
1236 free(cjob
->fs_path
);
1246 if (sp
) ap_destroy_pool(sp
);
1249 free(cjob
->fs_path
);
1257 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1258 "FastCGI: really bogus message: \"%s\"", ptr1
);
1259 ptr1
+= strlen(buf
);
1262 buflen
-= ptr1
- buf
;
1264 memmove(buf
, ptr1
, buflen
);
1268 ap_destroy_pool(tp
);
1272 *----------------------------------------------------------------------
1274 * dynamic_kill_idle_fs_procs
1276 * Implement a kill policy for the dynamic FastCGI applications.
1277 * We also update the data structures to reflect the changes.
1280 * Processes are marked for deletion possibly killed.
1282 *----------------------------------------------------------------------
1284 static void dynamic_kill_idle_fs_procs(void)
1289 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1292 * server's smoothed running time, or if that's 0, the current total
1294 unsigned long connTime
;
1297 * maximum number of microseconds that all of a server's running
1298 * processes together could have spent running since the last check
1300 unsigned long totalTime
;
1303 * percentage, 0-100, of totalTime that the processes actually used
1308 int really_running
= 0;
1310 if (s
->directive
!= APP_CLASS_DYNAMIC
|| s
->numProcesses
== 0)
1315 /* s->numProcesses includes pending kills so get the "active" count */
1316 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1318 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
) ++really_running
;
1321 connTime
= s
->smoothConnTime
? s
->smoothConnTime
: s
->totalConnTime
;
1322 totalTime
= really_running
* (now
- fcgi_dynamic_epoch
) * 1000000 + 1;
1324 loadFactor
= 100 * connTime
/ totalTime
;
1326 if (really_running
== 1)
1328 if (loadFactor
>= dynamicThreshold1
)
1335 int load
= really_running
/ ( really_running
- 1) * loadFactor
;
1337 if (load
>= dynamicThresholdN
)
1344 * Run through the procs to see if we can get away w/o waxing one.
1346 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1348 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1350 s
->procs
[i
].state
= FCGI_READY_STATE
;
1353 else if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
)
1359 if (i
>= dynamicMaxClassProcs
)
1361 ServerProcess
* procs
= s
->procs
;
1364 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1366 if (procs
[i
].state
== FCGI_RUNNING_STATE
)
1368 if (youngest
== -1 || procs
[i
].start_time
>= procs
[youngest
].start_time
)
1377 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1378 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1379 s
->fs_path
, (long) s
->procs
[youngest
].pid
);
1381 fcgi_kill(&s
->procs
[youngest
], SIGTERM
);
1387 * If the number of non-victims is less than or equal to
1388 * the minimum that may be running without being killed off,
1389 * don't select any more victims.
1391 if (fcgi_dynamic_total_proc_count
- victims
<= dynamicMinProcs
)
1401 // This is a little bogus, there's gotta be a better way to do this
1402 // Can we use WaitForMultipleObjects()
1403 #define FCGI_PROC_WAIT_TIME 100
1405 void child_wait_thread_main(void *dummy
) {
1407 DWORD dwRet
= WAIT_TIMEOUT
;
1412 while (!bTimeToDie
) {
1415 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1416 if (s
->directive
== APP_CLASS_EXTERNAL
|| s
->listenFd
< 0) {
1419 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1420 numChildren
= dynamicMaxClassProcs
;
1423 numChildren
= s
->numProcesses
;
1426 for (i
=0; i
< numChildren
; i
++) {
1427 if (s
->procs
[i
].handle
!= INVALID_HANDLE_VALUE
)
1429 DWORD exitStatus
= 0;
1431 /* timeout is currently set for 100 miliecond */
1432 /* it may need to be longer or user customizable */
1433 dwRet
= WaitForSingleObject(s
->procs
[i
].handle
, FCGI_PROC_WAIT_TIME
);
1437 if (dwRet
!= WAIT_TIMEOUT
&& dwRet
!= WAIT_FAILED
) {
1438 /* a child fs has died */
1439 /* mark the child as dead */
1441 if (s
->directive
== APP_CLASS_STANDARD
) {
1442 /* restart static app */
1443 s
->procs
[i
].state
= FCGI_START_STATE
;
1448 fcgi_dynamic_total_proc_count
--;
1449 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1451 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1452 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1455 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1458 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0)) {
1459 s
->procs
[i
].state
= FCGI_START_STATE
;
1462 s
->procs
[i
].state
= FCGI_READY_STATE
;
1467 GetExitCodeProcess(s
->procs
[i
].handle
, &exitStatus
);
1469 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1470 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1471 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1472 s
->fs_path
, (long) s
->procs
[i
].pid
, exitStatus
);
1474 CloseHandle(s
->procs
[i
].handle
);
1475 s
->procs
[i
].handle
= INVALID_HANDLE_VALUE
;
1476 s
->procs
[i
].pid
= -1;
1478 /* wake up the main thread */
1479 SetEvent(fcgi_event_handles
[WAKE_EVENT
]);
1484 Sleep(waited
? 0 : FCGI_PROC_WAIT_TIME
);
1490 static void setup_signals(void)
1492 struct sigaction sa
;
1494 /* Setup handlers */
1496 sa
.sa_handler
= signal_handler
;
1497 sigemptyset(&sa
.sa_mask
);
1500 if (sigaction(SIGTERM
, &sa
, NULL
) < 0) {
1501 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1502 "sigaction(SIGTERM) failed");
1505 if (sigaction(SIGHUP
, &sa
, NULL
) < 0) {
1506 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1507 "sigaction(SIGHUP) failed");
1509 /* httpd graceful restart */
1510 if (sigaction(SIGUSR1
, &sa
, NULL
) < 0) {
1511 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1512 "sigaction(SIGUSR1) failed");
1514 /* read messages from request handlers - kill interval expired */
1515 if (sigaction(SIGALRM
, &sa
, NULL
) < 0) {
1516 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1517 "sigaction(SIGALRM) failed");
1519 if (sigaction(SIGCHLD
, &sa
, NULL
) < 0) {
1520 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1521 "sigaction(SIGCHLD) failed");
1527 int fcgi_pm_main(void *dummy
, child_info
*info
)
1529 void fcgi_pm_main(void *dummy
)
1539 HANDLE child_wait_thread
= INVALID_HANDLE_VALUE
;
1541 int callWaitPid
, callDynamicProcs
;
1545 // Add SystemRoot to the dynamic environment
1546 char ** envp
= dynamicEnvp
;
1547 for (i
= 0; *envp
; ++i
) {
1550 fcgi_config_set_env_var(fcgi_config_pool
, dynamicEnvp
, &i
, "SystemRoot");
1553 reduce_privileges();
1555 close(fcgi_pm_pipe
[1]);
1556 change_process_name("fcgi-pm");
1560 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1561 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper
);
1565 /* Initialize AppClass */
1566 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1568 if (s
->directive
!= APP_CLASS_STANDARD
)
1576 for (i
= 0; i
< s
->numProcesses
; ++i
)
1577 s
->procs
[i
].state
= FCGI_START_STATE
;
1581 child_wait_thread
= (HANDLE
) _beginthread(child_wait_thread_main
, 0, NULL
);
1583 if (child_wait_thread
== (HANDLE
) -1)
1585 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1586 "FastCGI: failed to create process manager's wait thread!");
1589 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1590 "FastCGI: process manager initialized");
1592 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1593 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1599 * Loop until SIGTERM
1602 int sleepSeconds
= min(dynamicKillInterval
, dynamicUpdateInterval
);
1609 unsigned int numChildren
;
1612 * If we came out of sigsuspend() for any reason other than
1613 * SIGALRM, pick up where we left off.
1616 sleepSeconds
= alarmLeft
;
1619 * Examine each configured AppClass for a process that needs
1620 * starting. Compute the earliest time when the start should
1621 * be attempted, starting it now if the time has passed. Also,
1622 * remember that we do NOT need to restart externally managed
1623 * FastCGI applications.
1625 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1627 if (s
->directive
== APP_CLASS_EXTERNAL
)
1630 numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
1631 ? dynamicMaxClassProcs
1634 for (i
= 0; i
< numChildren
; ++i
)
1636 if (s
->procs
[i
].pid
<= 0 && s
->procs
[i
].state
== FCGI_START_STATE
)
1638 int restart
= (s
->procs
[i
].pid
< 0);
1639 time_t restartTime
= s
->restartTime
;
1643 /* we've gone to using the badDelay, the only thing that
1644 resets bad is when badDelay has expired. but numFailures
1645 is only just set below its threshold. the proc's
1646 start_times are all reset when the bad is. the numFailures
1647 is reset when we see an app run for a period */
1649 s
->procs
[i
].start_time
= 0;
1652 if (s
->numFailures
> MAX_FAILED_STARTS
)
1654 time_t last_start_time
= s
->procs
[i
].start_time
;
1656 if (last_start_time
&& now
- last_start_time
> RUNTIME_SUCCESS_INTERVAL
)
1665 for (j
= 0; j
< numChildren
; ++j
)
1667 if (s
->procs
[j
].pid
<= 0) continue;
1668 if (s
->procs
[j
].state
!= FCGI_RUNNING_STATE
) continue;
1669 if (s
->procs
[j
].start_time
== 0) continue;
1670 if (now
- s
->procs
[j
].start_time
> RUNTIME_SUCCESS_INTERVAL
) break;
1673 if (j
>= numChildren
)
1687 restartTime
+= FAILED_STARTS_DELAY
;
1691 restartTime
+= (restart
) ? s
->restartDelay
: s
->initStartDelay
;
1694 if (restartTime
<= now
)
1699 s
->numFailures
= MAX_FAILED_STARTS
;
1702 if (s
->listenFd
< 0 && init_listen_sock(s
))
1704 if (sleepSeconds
> s
->initStartDelay
)
1705 sleepSeconds
= s
->initStartDelay
;
1709 if (caughtSigTerm
) {
1710 goto ProcessSigTerm
;
1713 s
->procs
[i
].pid
= spawn_fs_process(s
, &s
->procs
[i
]);
1714 if (s
->procs
[i
].pid
<= 0) {
1715 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1716 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1717 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1720 sleepSeconds
= min(sleepSeconds
,
1721 max((int) s
->restartDelay
, FCGI_MIN_EXEC_RETRY_DELAY
));
1723 ap_assert(s
->procs
[i
].pid
< 0);
1727 s
->procs
[i
].start_time
= now
;
1728 s
->restartTime
= now
;
1730 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1732 fcgi_dynamic_total_proc_count
++;
1733 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1736 s
->procs
[i
].state
= FCGI_RUNNING_STATE
;
1739 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1740 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1741 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1742 s
->fs_path
, (long) s
->uid
, (long) s
->gid
,
1743 restart
? "re" : "", (long) s
->procs
[i
].pid
);
1746 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1747 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1748 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1749 s
->fs_path
, restart
? "re" : "", (long) s
->procs
[i
].pid
);
1751 ap_assert(s
->procs
[i
].pid
> 0);
1753 sleepSeconds
= min(sleepSeconds
, restartTime
- now
);
1762 goto ProcessSigTerm
;
1764 if((!caughtSigChld
) && (!caughtSigAlarm
)) {
1767 alarm(sleepSeconds
);
1770 FD_SET(fcgi_pm_pipe
[0], &rfds
);
1771 read_ready
= ap_select(fcgi_pm_pipe
[0] + 1, &rfds
, NULL
, NULL
, NULL
);
1773 alarmLeft
= alarm(0);
1775 callWaitPid
= caughtSigChld
;
1776 caughtSigChld
= FALSE
;
1777 callDynamicProcs
= caughtSigAlarm
;
1778 caughtSigAlarm
= FALSE
;
1783 * Dynamic fcgi process management
1785 if((callDynamicProcs
) || (!callWaitPid
)) {
1786 dynamic_read_msgs(read_ready
);
1787 if(fcgi_dynamic_epoch
== 0) {
1788 fcgi_dynamic_epoch
= now
;
1790 if(((long)(now
-fcgi_dynamic_epoch
)>=dynamicKillInterval
) ||
1791 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
)>=dynamicMaxProcs
)) {
1792 dynamic_kill_idle_fs_procs();
1793 fcgi_dynamic_epoch
= now
;
1801 /* We've caught SIGCHLD, so find out who it was using waitpid,
1802 * write a log message and update its data structure. */
1806 goto ProcessSigTerm
;
1808 childPid
= waitpid(-1, &waitStatus
, WNOHANG
);
1810 if (childPid
== -1 || childPid
== 0)
1813 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1814 if (s
->directive
== APP_CLASS_EXTERNAL
)
1817 if (s
->directive
== APP_CLASS_DYNAMIC
)
1818 numChildren
= dynamicMaxClassProcs
;
1820 numChildren
= s
->numProcesses
;
1822 for (i
= 0; i
< numChildren
; i
++) {
1823 if (s
->procs
[i
].pid
== childPid
)
1828 /* TODO: print something about this unknown child */
1832 s
->procs
[i
].pid
= -1;
1834 if (s
->directive
== APP_CLASS_STANDARD
) {
1835 /* Always restart static apps */
1836 s
->procs
[i
].state
= FCGI_START_STATE
;
1841 fcgi_dynamic_total_proc_count
--;
1843 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1844 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1847 /* A dynamic app died or exited without provocation from the PM */
1850 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0))
1851 s
->procs
[i
].state
= FCGI_START_STATE
;
1853 s
->procs
[i
].state
= FCGI_READY_STATE
;
1857 if (WIFEXITED(waitStatus
)) {
1858 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1859 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1860 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1861 s
->fs_path
, (long) childPid
, WEXITSTATUS(waitStatus
));
1863 else if (WIFSIGNALED(waitStatus
)) {
1864 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1865 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1866 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1867 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), SYS_SIGLIST
[WTERMSIG(waitStatus
)],
1869 WCOREDUMP(waitStatus
) ? ", a core file may have been generated" : "");
1874 else if (WIFSTOPPED(waitStatus
)) {
1875 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1876 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1877 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1878 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), SYS_SIGLIST
[WTERMSIG(waitStatus
)]);
1880 } /* for (;;), waitpid() */
1884 /* wait for an event to occur or timer expires */
1885 expire
= time(NULL
) + sleepSeconds
;
1886 dwRet
= WaitForMultipleObjects(3, (HANDLE
*) fcgi_event_handles
, FALSE
, sleepSeconds
* 1000);
1888 if (dwRet
== WAIT_FAILED
) {
1889 /* There is something seriously wrong here */
1890 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1891 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1895 if (dwRet
!= WAIT_TIMEOUT
) {
1899 alarmLeft
= expire
- now
;
1903 * Dynamic fcgi process management
1905 if ((dwRet
== MBOX_EVENT
) || (dwRet
== WAIT_TIMEOUT
)) {
1906 if (dwRet
== MBOX_EVENT
) {
1912 dynamic_read_msgs(read_ready
);
1914 if(fcgi_dynamic_epoch
== 0) {
1915 fcgi_dynamic_epoch
= now
;
1918 if ((now
-fcgi_dynamic_epoch
>= (int) dynamicKillInterval
) ||
1919 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
) >= dynamicMaxProcs
)) {
1920 dynamic_kill_idle_fs_procs();
1921 fcgi_dynamic_epoch
= now
;
1925 else if (dwRet
== WAKE_EVENT
) {
1928 else if (dwRet
== TERM_EVENT
) {
1929 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
1930 "FastCGI: Termination event received process manager shutting down");
1933 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
1935 goto ProcessSigTerm
;
1938 // Have an received an unknown event - should not happen
1939 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1940 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1943 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
1945 goto ProcessSigTerm
;
1950 } /* for (;;), the whole shoot'n match */
1954 * Kill off the children, then exit.
1966 int fcgi_pm_add_job(fcgi_pm_job
*new_job
)
1968 int rv
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
1970 if (rv
!= WAIT_OBJECT_0
&& rv
!= WAIT_ABANDONED
)
1972 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1973 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
1977 new_job
->next
= fcgi_dynamic_mbox
;
1978 fcgi_dynamic_mbox
= new_job
;
1980 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
1982 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1983 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");