2 * $Id: fcgi_pm.c,v 1.74 2002/07/26 03:10:54 robs Exp $
8 #if defined(APACHE2) && !defined(WIN32)
14 #define seteuid(arg) setresuid(-1, (arg), -1)
17 int fcgi_dynamic_total_proc_count
= 0; /* number of running apps */
18 time_t fcgi_dynamic_epoch
= 0; /* last time kill_procs was
19 * invoked by process mgr */
20 time_t fcgi_dynamic_last_analyzed
= 0; /* last time calculation was
21 * made for the dynamic procs */
23 static time_t now
= 0;
26 #pragma warning ( disable : 4100 4102 )
27 static BOOL bTimeToDie
= FALSE
; /* process termination flag */
28 HANDLE fcgi_event_handles
[3];
35 #if !defined(WIN32) && !defined(APACHE2)
36 static int seteuid_root(void)
38 int rc
= seteuid((uid_t
)0);
40 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
41 "FastCGI: seteuid(0) failed");
46 static int seteuid_user(void)
48 int rc
= seteuid(ap_user_id
);
50 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
51 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id
);
58 * Signal the process to exit. How (or if) the process responds
59 * depends on the FastCGI application library (esp. on Win32) and
60 * possibly application code (signal handlers and whether or not
61 * SA_RESTART is on). At any rate, we send the signal with the
62 * hopes that the process will exit on its own. Later, as we
63 * review the state of application processes, if we see one marked
64 * for death, but that hasn't died within a specified period of
65 * time, fcgi_kill() is called again with a KILL)
67 static void fcgi_kill(ServerProcess
*process
, int sig
)
69 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process
->pid
, sig
);
71 process
->state
= FCGI_VICTIM_STATE
;
76 SetEvent(process
->terminationEvent
);
78 else if (sig
== SIGKILL
)
80 TerminateProcess(process
->handle
, 1);
92 kill(process
->pid
, sig
);
101 /*******************************************************************************
102 * Send SIGTERM to each process in the server class, remove socket
103 * file if appropriate. Currently this is only called when the PM is shutting
104 * down and thus memory isn't freed and sockets and files aren't closed.
106 static void shutdown_all()
108 fcgi_server
*s
= fcgi_servers
;
112 ServerProcess
*proc
= s
->procs
;
114 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
115 ? dynamicMaxClassProcs
119 if (s
->socket_path
!= NULL
&& s
->directive
!= APP_CLASS_EXTERNAL
)
121 /* Remove the socket file */
122 if (unlink(s
->socket_path
) != 0 && errno
!= ENOENT
) {
123 ap_log_error(FCGI_LOG_ERR
, fcgi_apache_main_server
,
124 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
126 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "", s
->fs_path
);
131 /* Send TERM to all processes */
132 for (i
= 0; i
< numChildren
; i
++, proc
++)
134 if (proc
->state
== FCGI_RUNNING_STATE
)
136 fcgi_kill(proc
, SIGTERM
);
143 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
146 * WIN32 applications may not have support for the shutdown event
147 * depending on their application library version
150 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT
);
155 ServerProcess
*proc
= s
->procs
;
157 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
158 ? dynamicMaxClassProcs
161 /* Send KILL to all processes */
162 for (i
= 0; i
< numChildren
; i
++, proc
++)
164 if (proc
->state
== FCGI_RUNNING_STATE
)
166 fcgi_kill(proc
, SIGKILL
);
176 static int init_listen_sock(fcgi_server
* fs
)
178 ap_assert(fs
->directive
!= APP_CLASS_EXTERNAL
);
180 /* Create the socket */
181 if ((fs
->listenFd
= socket(fs
->socket_addr
->sa_family
, SOCK_STREAM
, 0)) < 0)
184 errno
= WSAGetLastError(); // Not sure if this will work as expected
186 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
187 "FastCGI: can't create %sserver \"%s\": socket() failed",
188 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
194 if (fs
->socket_addr
->sa_family
== AF_UNIX
)
196 /* Remove any existing socket file.. just in case */
197 unlink(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
);
203 setsockopt(fs
->listenFd
, SOL_SOCKET
, SO_REUSEADDR
, (char *)&flag
, sizeof(flag
));
206 /* Bind it to the socket_addr */
207 if (bind(fs
->listenFd
, fs
->socket_addr
, fs
->socket_addr_len
))
212 errno
= WSAGetLastError();
214 ap_snprintf(port
, sizeof(port
), "port=%d",
215 ((struct sockaddr_in
*)fs
->socket_addr
)->sin_port
);
217 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
218 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
219 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
222 (fs
->socket_addr
->sa_family
== AF_UNIX
) ?
223 ((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
:
229 /* Twiddle Unix socket permissions */
230 else if (fs
->socket_addr
->sa_family
== AF_UNIX
231 && chmod(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
, S_IRUSR
| S_IWUSR
))
233 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
234 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
235 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
241 else if (listen(fs
->listenFd
, fs
->listenQueueDepth
))
244 errno
= WSAGetLastError();
246 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
247 "FastCGI: can't create %sserver \"%s\": listen() failed",
248 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
262 *----------------------------------------------------------------------
266 * The FastCGI process manager, which runs as a separate
267 * process responsible for:
268 * - Starting all the FastCGI proceses.
269 * - Restarting any of these processes that die (indicated
271 * - Catching SIGTERM and relaying it to all the FastCGI
272 * processes before exiting.
275 * Uses global variable fcgi_servers.
283 *----------------------------------------------------------------------
286 static int caughtSigTerm
= FALSE
;
287 static int caughtSigChld
= FALSE
;
288 static int caughtSigAlarm
= FALSE
;
290 static void signal_handler(int signo
)
292 if ((signo
== SIGTERM
) || (signo
== SIGUSR1
) || (signo
== SIGHUP
)) {
293 /* SIGUSR1 & SIGHUP are sent by apache to its process group
294 * when apache get 'em. Apache follows up (1.2.x) with attacks
295 * on each of its child processes, but we've got the KillMgr
296 * sitting between us so we never see the KILL. The main loop
297 * in ProcMgr also checks to see if the KillMgr has terminated,
298 * and if it has, we handl it as if we should shutdown too. */
299 caughtSigTerm
= TRUE
;
300 } else if(signo
== SIGCHLD
) {
301 caughtSigChld
= TRUE
;
302 } else if(signo
== SIGALRM
) {
303 caughtSigAlarm
= TRUE
;
309 *----------------------------------------------------------------------
311 * spawn_fs_process --
313 * Fork and exec the specified fcgi process.
316 * 0 for successful fork, -1 for failed fork.
318 * In case the child fails before or in the exec, the child
319 * obtains the error log by calling getErrLog, logs
320 * the error, and exits with exit status = errno of
321 * the failed system call.
324 * Child process created.
326 *----------------------------------------------------------------------
328 static pid_t
spawn_fs_process(fcgi_server
*fs
, ServerProcess
*process
)
335 char *dnEnd
, *failedSysCall
;
342 /* We're the child. We're gonna exec() so pools don't matter. */
344 dnEnd
= strrchr(fs
->fs_path
, '/');
348 dirName
= ap_pcalloc(fcgi_config_pool
, dnEnd
- fs
->fs_path
+ 1);
349 dirName
= memcpy(dirName
, fs
->fs_path
, dnEnd
- fs
->fs_path
);
351 if (chdir(dirName
) < 0) {
352 failedSysCall
= "chdir()";
353 goto FailedSystemCallExit
;
357 /* OS/2 dosen't support nice() */
358 if (fs
->processPriority
!= 0) {
359 if (nice(fs
->processPriority
) == -1) {
360 failedSysCall
= "nice()";
361 goto FailedSystemCallExit
;
366 /* Open the listenFd on spec'd fd */
367 if (fs
->listenFd
!= FCGI_LISTENSOCK_FILENO
)
368 dup2(fs
->listenFd
, FCGI_LISTENSOCK_FILENO
);
370 /* Close all other open fds, except stdout/stderr. Leave these two open so
371 * FastCGI applications don't have to find and fix ALL 3rd party libs that
372 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
373 * main server error_log - @@@ provide a directive control where this goes.
375 ap_error_log2stderr(fcgi_apache_main_server
);
377 for (i
= 0; i
< FCGI_MAX_FD
; i
++) {
378 if (i
!= FCGI_LISTENSOCK_FILENO
&& i
!= 2 && i
!= 1) {
383 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
384 * install its own handler. */
385 signal(SIGPIPE
, SIG_IGN
);
387 if (fcgi_wrapper
&& (fcgi_user_id
!= fs
->uid
|| fcgi_group_id
!= fs
->gid
)) {
388 char *shortName
= strrchr(fs
->fs_path
, '/') + 1;
391 /* Relinquish our root real uid powers */
397 execle(fcgi_wrapper
, fcgi_wrapper
, fs
->username
, fs
->group
, shortName
, NULL
, fs
->envp
);
398 } while (errno
== EINTR
);
402 execle(fs
->fs_path
, fs
->fs_path
, NULL
, fs
->envp
);
403 } while (errno
== EINTR
);
406 failedSysCall
= "execle()";
408 FailedSystemCallExit
:
409 fprintf(stderr
, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
410 fs
->fs_path
, (long) getpid(), failedSysCall
, strerror(errno
));
413 /* avoid an irrelevant compiler warning */
420 /* based on mod_cgi.c:run_cgi_child() */
423 char * termination_env_string
;
424 HANDLE listen_handle
= INVALID_HANDLE_VALUE
;
425 apr_procattr_t
* procattr
;
426 apr_proc_t proc
= { 0 };
430 if (apr_pool_create(&tp
, fcgi_config_pool
))
433 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
434 if (process
->terminationEvent
== NULL
)
437 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
439 termination_env_string
= ap_psprintf(tp
,
440 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
442 while (fs
->envp
[i
]) i
++;
443 fs
->envp
[i
++] = termination_env_string
;
444 fs
->envp
[i
] = (char *) fs
->mutex_env_string
;
446 ap_assert(fs
->envp
[i
+ 1] == NULL
);
450 SECURITY_ATTRIBUTES sa
= { 0 };
452 sa
.bInheritHandle
= TRUE
;
453 sa
.nLength
= sizeof(sa
);
455 listen_handle
= CreateNamedPipe(fs
->socket_path
,
457 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
458 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
460 if (listen_handle
== INVALID_HANDLE_VALUE
)
462 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
463 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
470 listen_handle
= (HANDLE
) fs
->listenFd
;
473 if (apr_procattr_create(&procattr
, tp
))
476 if (apr_procattr_dir_set(procattr
, ap_make_dirstr_parent(tp
, fs
->fs_path
)))
479 if (apr_procattr_detach_set(procattr
, 1))
482 if (apr_os_file_put(&file
, &listen_handle
, 0, tp
))
485 /* procattr is opaque so we have to use this - unfortuantely it dups */
486 if (apr_procattr_child_in_set(procattr
, file
, NULL
))
489 if (apr_proc_create(&proc
, fs
->fs_path
, NULL
, fs
->envp
, procattr
, tp
))
492 process
->handle
= proc
.hproc
;
499 fs
->envp
[i
- 1] = NULL
;
506 #else /* WIN32 && !APACHE2 */
508 /* Adapted from Apache's util_script.c ap_call_exec() */
509 char *interpreter
= NULL
;
510 char *quoted_filename
;
512 char *pEnvBlock
, *pNext
;
515 int iEnvBlockLen
= 1;
517 file_type_e fileType
;
520 PROCESS_INFORMATION pi
;
525 pool
* tp
= ap_make_sub_pool(fcgi_config_pool
);
527 HANDLE listen_handle
;
528 char * termination_env_string
= NULL
;
530 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
531 if (process
->terminationEvent
== NULL
)
533 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
534 "FastCGI: can't create termination event for server \"%s\", "
535 "CreateEvent() failed", fs
->fs_path
);
538 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
540 termination_env_string
= ap_psprintf(tp
,
541 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
545 SECURITY_ATTRIBUTES sa
;
547 sa
.lpSecurityDescriptor
= NULL
;
548 sa
.bInheritHandle
= TRUE
;
549 sa
.nLength
= sizeof(sa
);
551 listen_handle
= CreateNamedPipe(fs
->socket_path
,
553 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
554 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
556 if (listen_handle
== INVALID_HANDLE_VALUE
)
558 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
559 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs
->fs_path
);
565 listen_handle
= (HANDLE
) fs
->listenFd
;
568 memset(&si
, 0, sizeof(si
));
569 memset(&pi
, 0, sizeof(pi
));
570 memset(&r
, 0, sizeof(r
));
572 // Can up a fake request to pass to ap_get_win32_interpreter()
573 r
.per_dir_config
= fcgi_apache_main_server
->lookup_defaults
;
574 r
.server
= fcgi_apache_main_server
;
575 r
.filename
= (char *) fs
->fs_path
;
578 fileType
= ap_get_win32_interpreter(&r
, &interpreter
);
580 if (fileType
== eFileTypeUNKNOWN
) {
581 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
582 "FastCGI: %s is not executable; ensure interpreted scripts have "
583 "\"#!\" as their first line",
590 * We have the interpreter (if there is one) and we have
591 * the arguments (if there are any).
592 * Build the command string to pass to CreateProcess.
594 quoted_filename
= ap_pstrcat(tp
, "\"", fs
->fs_path
, "\"", NULL
);
595 if (interpreter
&& *interpreter
) {
596 pCommand
= ap_pstrcat(tp
, interpreter
, " ", quoted_filename
, NULL
);
599 pCommand
= quoted_filename
;
603 * Make child process use hPipeOutputWrite as standard out,
604 * and make sure it does not show on screen.
607 si
.dwFlags
= STARTF_USESHOWWINDOW
| STARTF_USESTDHANDLES
;
608 si
.wShowWindow
= SW_HIDE
;
609 si
.hStdInput
= listen_handle
;
611 // XXX These should be open to the error_log
612 si
.hStdOutput
= INVALID_HANDLE_VALUE
;
613 si
.hStdError
= INVALID_HANDLE_VALUE
;
616 * Win32's CreateProcess call requires that the environment
617 * be passed in an environment block, a null terminated block of
618 * null terminated strings.
619 * @todo we should store the env in this format for win32.
623 iEnvBlockLen
+= strlen(fs
->envp
[i
]) + 1;
627 iEnvBlockLen
+= strlen(termination_env_string
) + 1;
628 iEnvBlockLen
+= strlen(fs
->mutex_env_string
) + 1;
630 pEnvBlock
= (char *) ap_pcalloc(tp
, iEnvBlockLen
);
636 strcpy(pNext
, fs
->envp
[i
]);
637 pNext
+= strlen(pNext
) + 1;
641 strcpy(pNext
, termination_env_string
);
642 pNext
+= strlen(pNext
) + 1;
643 strcpy(pNext
, fs
->mutex_env_string
);
645 if (CreateProcess(NULL
, pCommand
, NULL
, NULL
, TRUE
,
648 ap_make_dirstr_parent(tp
, fs
->fs_path
),
651 /* Hack to get 16-bit CGI's working. It works for all the
652 * standard modules shipped with Apache. pi.dwProcessId is 0
653 * for 16-bit CGIs and all the Unix specific code that calls
654 * ap_call_exec interprets this as a failure case. And we can't
655 * use -1 either because it is mapped to 0 by the caller.
657 pid
= (fileType
== eFileTypeEXE16
) ? -2 : pi
.dwProcessId
;
659 process
->handle
= pi
.hProcess
;
660 CloseHandle(pi
.hThread
);
665 CloseHandle(listen_handle
);
674 #endif /* !APACHE2 */
678 #if !defined(WIN32) && !defined(APACHE2)
679 static void reduce_privileges(void)
687 /* Get username if passed as a uid */
688 if (ap_user_name
[0] == '#') {
689 uid_t uid
= atoi(&ap_user_name
[1]);
690 struct passwd
*ent
= getpwuid(uid
);
693 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
694 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
695 "you probably need to modify the User directive", (unsigned)uid
);
704 if (setgid(ap_group_id
) == -1) {
705 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
706 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id
);
710 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
712 /* Initialize supplementary groups */
713 if (initgroups(name
, ap_group_id
) == -1) {
714 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
715 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
716 name
, (unsigned)ap_group_id
);
723 if (seteuid_user() == -1) {
724 ap_log_error(FCGI_LOG_ALERT_NOERRNO
, fcgi_apache_main_server
,
725 "FastCGI: process manager exiting, failed to reduce privileges");
730 if (setuid(ap_user_id
) == -1) {
731 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
732 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id
);
739 * Change the name of this process - best we can easily.
741 static void change_process_name(const char * const name
)
743 strncpy(ap_server_argv0
, name
, strlen(ap_server_argv0
));
747 static void schedule_start(fcgi_server
*s
, int proc
)
749 /* If we've started one recently, don't register another */
750 time_t time_passed
= now
- s
->restartTime
;
752 if ((s
->procs
[proc
].pid
&& (time_passed
< (int) s
->restartDelay
))
753 || ((s
->procs
[proc
].pid
== 0) && (time_passed
< s
->initStartDelay
)))
755 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
);
759 FCGIDBG3("scheduling_start: %s (%d)", s
->fs_path
, proc
);
760 s
->procs
[proc
].state
= FCGI_START_STATE
;
761 if (proc
== dynamicMaxClassProcs
- 1) {
762 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
763 "FastCGI: scheduled the %sstart of the last (dynamic) server "
764 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
765 s
->procs
[proc
].pid
? "re" : "", s
->fs_path
, dynamicMaxClassProcs
);
770 *----------------------------------------------------------------------
774 * Removes the records written by request handlers and decodes them.
775 * We also update the data structures to reflect the changes.
777 *----------------------------------------------------------------------
780 static void dynamic_read_msgs(int read_ready
)
786 static int buflen
= 0;
787 static char buf
[FCGI_MSGS_BUFSIZE
+ 1];
788 char *ptr1
, *ptr2
, opcode
;
789 char execName
[FCGI_MAXPATH
+ 1];
790 char user
[MAX_USER_NAME_LEN
+ 2];
791 char group
[MAX_GID_CHAR_LEN
+ 1];
792 unsigned long q_usec
= 0UL, req_usec
= 0UL;
794 fcgi_pm_job
*joblist
= NULL
;
795 fcgi_pm_job
*cjob
= NULL
;
798 pool
*sp
= NULL
, *tp
;
801 user
[MAX_USER_NAME_LEN
+ 1] = group
[MAX_GID_CHAR_LEN
] = '\0';
805 * To prevent the idle application from running indefinitely, we
806 * check the timer and if it is expired, we recompute the values
807 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
808 * message is received, only updates are made to the data structures.
810 if (fcgi_dynamic_last_analyzed
== 0) {
811 fcgi_dynamic_last_analyzed
= now
;
813 if ((now
- fcgi_dynamic_last_analyzed
) >= (int)dynamicUpdateInterval
) {
814 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
815 if (s
->directive
!= APP_CLASS_DYNAMIC
)
818 /* Advance the last analyzed timestamp by the elapsed time since
819 * it was last set. Round the increase down to the nearest
820 * multiple of dynamicUpdateInterval */
822 fcgi_dynamic_last_analyzed
+= (((long)(now
-fcgi_dynamic_last_analyzed
)/dynamicUpdateInterval
)*dynamicUpdateInterval
);
823 s
->smoothConnTime
= (unsigned long) ((1.0-dynamicGain
)*s
->smoothConnTime
+ dynamicGain
*s
->totalConnTime
);
824 s
->totalConnTime
= 0UL;
825 s
->totalQueueTime
= 0UL;
829 if (read_ready
<= 0) {
834 rc
= read(fcgi_pm_pipe
[0], (void *)(buf
+ buflen
), FCGI_MSGS_BUFSIZE
- buflen
);
836 if (!caughtSigTerm
) {
837 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
838 "FastCGI: read() from pipe failed (%d)", rc
);
840 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
841 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
842 caughtSigTerm
= TRUE
;
852 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
853 * request to do something) and/or when a timeout expires.
854 * There really should be no reason why this wait would get stuck
855 * but there's no point in waiting forever. */
857 rc
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
859 if (rc
!= WAIT_OBJECT_0
&& rc
!= WAIT_ABANDONED
)
861 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
862 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
866 joblist
= fcgi_dynamic_mbox
;
867 fcgi_dynamic_mbox
= NULL
;
869 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
871 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
872 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
879 apr_pool_create(&tp
, fcgi_config_pool
);
881 tp
= ap_make_sub_pool(fcgi_config_pool
);
885 for (ptr1
= buf
; ptr1
; ptr1
= ptr2
) {
888 ptr2
= strchr(ptr1
, '*');
900 case FCGI_SERVER_START_JOB
:
901 case FCGI_SERVER_RESTART_JOB
:
903 if (sscanf(ptr1
, "%c %s %16s %15s",
904 &opcode
, execName
, user
, group
) != 4)
910 case FCGI_REQUEST_TIMEOUT_JOB
:
912 if (sscanf(ptr1
, "%c %s %16s %15s",
913 &opcode
, execName
, user
, group
) != 4)
919 case FCGI_REQUEST_COMPLETE_JOB
:
921 if (sscanf(ptr1
, "%c %s %16s %15s %lu %lu",
922 &opcode
, execName
, user
, group
, &q_usec
, &req_usec
) != 6)
934 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode
, execName
, user
, group
, q_usec
, req_usec
);
937 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
938 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1
);
942 /* Update data structures for processing */
943 while (cjob
!= NULL
) {
944 joblist
= cjob
->next
;
945 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob
->id
, cjob
->fs_path
, cjob
->user
, cjob
->group
, cjob
->qsec
, cjob
->start_time
);
949 s
= fcgi_util_fs_get(execName
, user
, group
);
951 s
= fcgi_util_fs_get(cjob
->fs_path
, cjob
->user
, cjob
->group
);
955 if (s
==NULL
&& opcode
!= FCGI_REQUEST_COMPLETE_JOB
)
957 if (s
==NULL
&& cjob
->id
!= FCGI_REQUEST_COMPLETE_JOB
)
962 HANDLE mutex
= CreateMutex(NULL
, FALSE
, cjob
->fs_path
);
966 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
967 "FastCGI: can't create accept mutex "
968 "for (dynamic) server \"%s\"", cjob
->fs_path
);
972 SetHandleInformation(mutex
, HANDLE_FLAG_INHERIT
, TRUE
);
977 /* Create a perm subpool to hold the new server data,
978 * we can destroy it if something doesn't pan out */
980 apr_pool_create(&sp
, fcgi_config_pool
);
982 sp
= ap_make_sub_pool(fcgi_config_pool
);
985 /* Create a new "dynamic" server */
986 s
= fcgi_util_fs_new(sp
);
988 s
->directive
= APP_CLASS_DYNAMIC
;
989 s
->restartDelay
= dynamicRestartDelay
;
990 s
->listenQueueDepth
= dynamicListenQueueDepth
;
991 s
->initStartDelay
= dynamicInitStartDelay
;
992 s
->envp
= dynamicEnvp
;
993 s
->flush
= dynamicFlush
;
996 s
->mutex_env_string
= ap_psprintf(sp
, "_FCGI_MUTEX_=%ld", mutex
);
997 s
->fs_path
= ap_pstrdup(sp
, cjob
->fs_path
);
999 s
->fs_path
= ap_pstrdup(sp
, execName
);
1001 ap_getparents(s
->fs_path
);
1002 ap_no2slash(s
->fs_path
);
1003 s
->procs
= fcgi_util_fs_create_procs(sp
, dynamicMaxClassProcs
);
1005 /* XXX the socket_path (both Unix and Win) *is* deducible and
1006 * thus can and will be used by other apache instances without
1007 * the use of shared data regarding the processes serving the
1008 * requests. This can result in slightly unintuitive process
1009 * counts and security implications. This is prevented
1010 * if suexec (Unix) is in use. This is both a feature and a flaw.
1011 * Changing it now would break existing installations. */
1014 /* Create socket file's path */
1015 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, execName
, user
, group
);
1016 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1018 /* Create sockaddr, prealloc it so it won't get created in tp */
1019 s
->socket_addr
= ap_pcalloc(sp
, sizeof(struct sockaddr_un
));
1020 err
= fcgi_util_socket_make_domain_addr(tp
, (struct sockaddr_un
**)&s
->socket_addr
,
1021 &s
->socket_addr_len
, s
->socket_path
);
1023 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1024 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
1028 if (init_listen_sock(s
)) {
1032 /* If a wrapper is being used, config user/group info */
1034 if (user
[0] == '~') {
1035 /* its a user dir uri, the rest is a username, not a uid */
1036 struct passwd
*pw
= getpwnam(&user
[1]);
1039 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1040 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1041 execName
, &user
[1]);
1044 s
->uid
= pw
->pw_uid
;
1045 s
->user
= ap_pstrdup(sp
, user
);
1046 s
->username
= s
->user
;
1048 s
->gid
= pw
->pw_gid
;
1049 s
->group
= ap_psprintf(sp
, "%ld", (long)s
->gid
);
1054 s
->uid
= (uid_t
)atol(user
);
1055 pw
= getpwuid(s
->uid
);
1057 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1058 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1059 execName
, (long)s
->uid
);
1062 s
->user
= ap_pstrdup(sp
, user
);
1063 s
->username
= ap_pstrdup(sp
, pw
->pw_name
);
1065 s
->gid
= (gid_t
)atol(group
);
1066 s
->group
= ap_pstrdup(sp
, group
);
1070 /* Create socket file's path */
1071 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, cjob
->fs_path
, cjob
->user
, cjob
->group
);
1072 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1076 fcgi_util_fs_add(s
);
1080 if (opcode
== FCGI_SERVER_RESTART_JOB
) {
1082 if (cjob
->id
==FCGI_SERVER_RESTART_JOB
) {
1084 /* Check to see if the binary has changed. If so,
1085 * kill the FCGI application processes, and
1091 char * app_path
= cjob
->fs_path
;
1093 char * app_path
= execName
;
1096 if (stat(app_path
, &stbuf
) == 0 && stbuf
.st_mtime
> s
->startTime
)
1100 /* prevent addition restart requests */
1103 utime(s
->socket_path
, NULL
);
1106 /* kill old server(s) */
1107 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1109 if (s
->procs
[i
].pid
> 0
1110 && stbuf
.st_mtime
> s
->procs
[i
].start_time
)
1112 fcgi_kill(&s
->procs
[i
], SIGTERM
);
1119 ap_log_error(FCGI_LOG_WARN_NOERRNO
,
1120 fcgi_apache_main_server
, "FastCGI: restarting "
1121 "old server \"%s\" processes, newer version "
1126 /* If dynamicAutoRestart, don't mark any new processes
1127 * for starting because we probably got the
1128 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1129 * will be restarting all of those we just killed.
1131 if (dynamicAutoRestart
)
1135 else if (opcode
== FCGI_SERVER_START_JOB
) {
1137 else if (cjob
->id
==FCGI_SERVER_START_JOB
) {
1139 /* we've been asked to start a process--only start
1140 * it if we're not already running at least one
1145 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1146 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1149 /* if already running, don't start another one */
1150 if (i
< dynamicMaxClassProcs
) {
1164 case FCGI_SERVER_RESTART_JOB
:
1168 /* We just waxed 'em all. Try to find an idle slot. */
1170 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1172 if (s
->procs
[i
].state
== FCGI_START_STATE
1173 || s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1177 else if (s
->procs
[i
].state
== FCGI_KILLED_STATE
1178 || s
->procs
[i
].state
== FCGI_READY_STATE
)
1185 /* Nope, just use the first slot */
1186 if (i
== dynamicMaxClassProcs
)
1194 schedule_start(s
, i
);
1199 case FCGI_SERVER_START_JOB
:
1200 case FCGI_REQUEST_TIMEOUT_JOB
:
1202 if ((fcgi_dynamic_total_proc_count
+ 1) > (int) dynamicMaxProcs
) {
1204 * Extra instances should have been
1205 * terminated beforehand, probably need
1206 * to increase ProcessSlack parameter
1208 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1209 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1210 "exceeded dynamicMaxProcs (%d)", s
->fs_path
, dynamicMaxProcs
);
1214 /* find next free slot */
1215 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1217 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1219 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i
);
1222 else if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1227 schedule_start(s
, i
);
1232 if (i
>= dynamicMaxClassProcs
) {
1233 FCGIDBG1("ignore_job: slots are max'd");
1237 case FCGI_REQUEST_COMPLETE_JOB
:
1238 /* only record stats if we have a structure */
1241 s
->totalConnTime
+= req_usec
;
1242 s
->totalQueueTime
+= q_usec
;
1244 s
->totalConnTime
+= cjob
->start_time
;
1245 s
->totalQueueTime
+= cjob
->qsec
;
1254 /* Cleanup job data */
1255 free(cjob
->fs_path
);
1265 if (sp
) ap_destroy_pool(sp
);
1268 free(cjob
->fs_path
);
1276 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1277 "FastCGI: really bogus message: \"%s\"", ptr1
);
1278 ptr1
+= strlen(buf
);
1281 buflen
-= ptr1
- buf
;
1283 memmove(buf
, ptr1
, buflen
);
1287 ap_destroy_pool(tp
);
1291 *----------------------------------------------------------------------
1293 * dynamic_kill_idle_fs_procs
1295 * Implement a kill policy for the dynamic FastCGI applications.
1296 * We also update the data structures to reflect the changes.
1299 * Processes are marked for deletion possibly killed.
1301 *----------------------------------------------------------------------
1303 static void dynamic_kill_idle_fs_procs(void)
1308 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1311 * server's smoothed running time, or if that's 0, the current total
1313 unsigned long connTime
;
1316 * maximum number of microseconds that all of a server's running
1317 * processes together could have spent running since the last check
1319 unsigned long totalTime
;
1322 * percentage, 0-100, of totalTime that the processes actually used
1327 int really_running
= 0;
1329 if (s
->directive
!= APP_CLASS_DYNAMIC
|| s
->numProcesses
== 0)
1334 /* s->numProcesses includes pending kills so get the "active" count */
1335 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1337 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
) ++really_running
;
1340 connTime
= s
->smoothConnTime
? s
->smoothConnTime
: s
->totalConnTime
;
1341 totalTime
= really_running
* (now
- fcgi_dynamic_epoch
) * 1000000 + 1;
1343 loadFactor
= 100 * connTime
/ totalTime
;
1345 if (really_running
== 1)
1347 if (loadFactor
>= dynamicThreshold1
)
1354 int load
= really_running
/ ( really_running
- 1) * loadFactor
;
1356 if (load
>= dynamicThresholdN
)
1363 * Run through the procs to see if we can get away w/o waxing one.
1365 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1367 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1369 s
->procs
[i
].state
= FCGI_READY_STATE
;
1372 else if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
)
1378 if (i
>= dynamicMaxClassProcs
)
1380 ServerProcess
* procs
= s
->procs
;
1383 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1385 if (procs
[i
].state
== FCGI_RUNNING_STATE
)
1387 if (youngest
== -1 || procs
[i
].start_time
>= procs
[youngest
].start_time
)
1396 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1397 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1398 s
->fs_path
, (long) s
->procs
[youngest
].pid
);
1400 fcgi_kill(&s
->procs
[youngest
], SIGTERM
);
1406 * If the number of non-victims is less than or equal to
1407 * the minimum that may be running without being killed off,
1408 * don't select any more victims.
1410 if (fcgi_dynamic_total_proc_count
- victims
<= dynamicMinProcs
)
1420 // This is a little bogus, there's gotta be a better way to do this
1421 // Can we use WaitForMultipleObjects()
1422 #define FCGI_PROC_WAIT_TIME 100
1424 void child_wait_thread_main(void *dummy
) {
1426 DWORD dwRet
= WAIT_TIMEOUT
;
1431 while (!bTimeToDie
) {
1434 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1435 if (s
->directive
== APP_CLASS_EXTERNAL
|| s
->listenFd
< 0) {
1438 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1439 numChildren
= dynamicMaxClassProcs
;
1442 numChildren
= s
->numProcesses
;
1445 for (i
=0; i
< numChildren
; i
++) {
1446 if (s
->procs
[i
].handle
!= INVALID_HANDLE_VALUE
)
1448 DWORD exitStatus
= 0;
1450 /* timeout is currently set for 100 miliecond */
1451 /* it may need to be longer or user customizable */
1452 dwRet
= WaitForSingleObject(s
->procs
[i
].handle
, FCGI_PROC_WAIT_TIME
);
1456 if (dwRet
!= WAIT_TIMEOUT
&& dwRet
!= WAIT_FAILED
) {
1457 /* a child fs has died */
1458 /* mark the child as dead */
1460 if (s
->directive
== APP_CLASS_STANDARD
) {
1461 /* restart static app */
1462 s
->procs
[i
].state
= FCGI_START_STATE
;
1467 fcgi_dynamic_total_proc_count
--;
1468 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1470 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1471 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1474 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1477 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0)) {
1478 s
->procs
[i
].state
= FCGI_START_STATE
;
1481 s
->procs
[i
].state
= FCGI_READY_STATE
;
1486 GetExitCodeProcess(s
->procs
[i
].handle
, &exitStatus
);
1488 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1489 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1490 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1491 s
->fs_path
, (long) s
->procs
[i
].pid
, exitStatus
);
1493 CloseHandle(s
->procs
[i
].handle
);
1494 s
->procs
[i
].handle
= INVALID_HANDLE_VALUE
;
1495 s
->procs
[i
].pid
= -1;
1497 /* wake up the main thread */
1498 SetEvent(fcgi_event_handles
[WAKE_EVENT
]);
1503 Sleep(waited
? 0 : FCGI_PROC_WAIT_TIME
);
1509 static void setup_signals(void)
1511 struct sigaction sa
;
1513 /* Setup handlers */
1515 sa
.sa_handler
= signal_handler
;
1516 sigemptyset(&sa
.sa_mask
);
1519 if (sigaction(SIGTERM
, &sa
, NULL
) < 0) {
1520 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1521 "sigaction(SIGTERM) failed");
1524 if (sigaction(SIGHUP
, &sa
, NULL
) < 0) {
1525 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1526 "sigaction(SIGHUP) failed");
1528 /* httpd graceful restart */
1529 if (sigaction(SIGUSR1
, &sa
, NULL
) < 0) {
1530 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1531 "sigaction(SIGUSR1) failed");
1533 /* read messages from request handlers - kill interval expired */
1534 if (sigaction(SIGALRM
, &sa
, NULL
) < 0) {
1535 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1536 "sigaction(SIGALRM) failed");
1538 if (sigaction(SIGCHLD
, &sa
, NULL
) < 0) {
1539 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1540 "sigaction(SIGCHLD) failed");
1545 #if !defined(WIN32) && !defined(APACHE2)
1546 int fcgi_pm_main(void *dummy
, child_info
*info
)
1548 void fcgi_pm_main(void *dummy
)
1558 HANDLE child_wait_thread
= INVALID_HANDLE_VALUE
;
1560 int callWaitPid
, callDynamicProcs
;
1564 // Add SystemRoot to the dynamic environment
1565 char ** envp
= dynamicEnvp
;
1566 for (i
= 0; *envp
; ++i
) {
1569 fcgi_config_set_env_var(fcgi_config_pool
, dynamicEnvp
, &i
, "SystemRoot");
1572 reduce_privileges();
1574 close(fcgi_pm_pipe
[1]);
1575 change_process_name("fcgi-pm");
1579 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1580 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper
);
1584 /* Initialize AppClass */
1585 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1587 if (s
->directive
!= APP_CLASS_STANDARD
)
1595 for (i
= 0; i
< s
->numProcesses
; ++i
)
1596 s
->procs
[i
].state
= FCGI_START_STATE
;
1600 child_wait_thread
= (HANDLE
) _beginthread(child_wait_thread_main
, 0, NULL
);
1602 if (child_wait_thread
== (HANDLE
) -1)
1604 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1605 "FastCGI: failed to create process manager's wait thread!");
1608 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1609 "FastCGI: process manager initialized");
1611 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1612 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1618 * Loop until SIGTERM
1621 int sleepSeconds
= min(dynamicKillInterval
, dynamicUpdateInterval
);
1628 unsigned int numChildren
;
1631 * If we came out of sigsuspend() for any reason other than
1632 * SIGALRM, pick up where we left off.
1635 sleepSeconds
= alarmLeft
;
1638 * Examine each configured AppClass for a process that needs
1639 * starting. Compute the earliest time when the start should
1640 * be attempted, starting it now if the time has passed. Also,
1641 * remember that we do NOT need to restart externally managed
1642 * FastCGI applications.
1644 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1646 if (s
->directive
== APP_CLASS_EXTERNAL
)
1649 numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
1650 ? dynamicMaxClassProcs
1653 for (i
= 0; i
< numChildren
; ++i
)
1655 if (s
->procs
[i
].pid
<= 0 && s
->procs
[i
].state
== FCGI_START_STATE
)
1657 int restart
= (s
->procs
[i
].pid
< 0);
1658 time_t restartTime
= s
->restartTime
;
1662 /* we've gone to using the badDelay, the only thing that
1663 resets bad is when badDelay has expired. but numFailures
1664 is only just set below its threshold. the proc's
1665 start_times are all reset when the bad is. the numFailures
1666 is reset when we see an app run for a period */
1668 s
->procs
[i
].start_time
= 0;
1671 if (s
->numFailures
> MAX_FAILED_STARTS
)
1673 time_t last_start_time
= s
->procs
[i
].start_time
;
1675 if (last_start_time
&& now
- last_start_time
> RUNTIME_SUCCESS_INTERVAL
)
1684 for (j
= 0; j
< numChildren
; ++j
)
1686 if (s
->procs
[j
].pid
<= 0) continue;
1687 if (s
->procs
[j
].state
!= FCGI_RUNNING_STATE
) continue;
1688 if (s
->procs
[j
].start_time
== 0) continue;
1689 if (now
- s
->procs
[j
].start_time
> RUNTIME_SUCCESS_INTERVAL
) break;
1692 if (j
>= numChildren
)
1706 restartTime
+= FAILED_STARTS_DELAY
;
1710 restartTime
+= (restart
) ? s
->restartDelay
: s
->initStartDelay
;
1713 if (restartTime
<= now
)
1718 s
->numFailures
= MAX_FAILED_STARTS
;
1721 if (s
->listenFd
< 0 && init_listen_sock(s
))
1723 if (sleepSeconds
> s
->initStartDelay
)
1724 sleepSeconds
= s
->initStartDelay
;
1728 if (caughtSigTerm
) {
1729 goto ProcessSigTerm
;
1732 s
->procs
[i
].pid
= spawn_fs_process(s
, &s
->procs
[i
]);
1733 if (s
->procs
[i
].pid
<= 0) {
1734 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1735 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1736 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1739 sleepSeconds
= min(sleepSeconds
,
1740 max((int) s
->restartDelay
, FCGI_MIN_EXEC_RETRY_DELAY
));
1742 ap_assert(s
->procs
[i
].pid
< 0);
1746 s
->procs
[i
].start_time
= now
;
1747 s
->restartTime
= now
;
1749 if (s
->startTime
== 0) {
1753 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1755 fcgi_dynamic_total_proc_count
++;
1756 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1759 s
->procs
[i
].state
= FCGI_RUNNING_STATE
;
1762 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1763 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1764 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1765 s
->fs_path
, (long) s
->uid
, (long) s
->gid
,
1766 restart
? "re" : "", (long) s
->procs
[i
].pid
);
1769 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1770 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1771 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1772 s
->fs_path
, restart
? "re" : "", (long) s
->procs
[i
].pid
);
1774 ap_assert(s
->procs
[i
].pid
> 0);
1776 sleepSeconds
= min(sleepSeconds
, restartTime
- now
);
1785 goto ProcessSigTerm
;
1787 if((!caughtSigChld
) && (!caughtSigAlarm
)) {
1790 alarm(sleepSeconds
);
1793 FD_SET(fcgi_pm_pipe
[0], &rfds
);
1794 read_ready
= ap_select(fcgi_pm_pipe
[0] + 1, &rfds
, NULL
, NULL
, NULL
);
1796 alarmLeft
= alarm(0);
1798 callWaitPid
= caughtSigChld
;
1799 caughtSigChld
= FALSE
;
1800 callDynamicProcs
= caughtSigAlarm
;
1801 caughtSigAlarm
= FALSE
;
1806 * Dynamic fcgi process management
1808 if((callDynamicProcs
) || (!callWaitPid
)) {
1809 dynamic_read_msgs(read_ready
);
1810 if(fcgi_dynamic_epoch
== 0) {
1811 fcgi_dynamic_epoch
= now
;
1813 if(((long)(now
-fcgi_dynamic_epoch
)>=dynamicKillInterval
) ||
1814 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
)>=dynamicMaxProcs
)) {
1815 dynamic_kill_idle_fs_procs();
1816 fcgi_dynamic_epoch
= now
;
1824 /* We've caught SIGCHLD, so find out who it was using waitpid,
1825 * write a log message and update its data structure. */
1829 goto ProcessSigTerm
;
1831 childPid
= waitpid(-1, &waitStatus
, WNOHANG
);
1833 if (childPid
== -1 || childPid
== 0)
1836 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1837 if (s
->directive
== APP_CLASS_EXTERNAL
)
1840 if (s
->directive
== APP_CLASS_DYNAMIC
)
1841 numChildren
= dynamicMaxClassProcs
;
1843 numChildren
= s
->numProcesses
;
1845 for (i
= 0; i
< numChildren
; i
++) {
1846 if (s
->procs
[i
].pid
== childPid
)
1851 /* TODO: print something about this unknown child */
1855 s
->procs
[i
].pid
= -1;
1857 if (s
->directive
== APP_CLASS_STANDARD
) {
1858 /* Always restart static apps */
1859 s
->procs
[i
].state
= FCGI_START_STATE
;
1864 fcgi_dynamic_total_proc_count
--;
1866 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1867 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1870 /* A dynamic app died or exited without provocation from the PM */
1873 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0))
1874 s
->procs
[i
].state
= FCGI_START_STATE
;
1876 s
->procs
[i
].state
= FCGI_READY_STATE
;
1880 if (WIFEXITED(waitStatus
)) {
1881 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1882 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1883 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1884 s
->fs_path
, (long) childPid
, WEXITSTATUS(waitStatus
));
1886 else if (WIFSIGNALED(waitStatus
)) {
1887 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1888 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1889 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1890 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), SYS_SIGLIST
[WTERMSIG(waitStatus
)],
1892 WCOREDUMP(waitStatus
) ? ", a core file may have been generated" : "");
1897 else if (WIFSTOPPED(waitStatus
)) {
1898 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1899 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1900 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1901 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), SYS_SIGLIST
[WTERMSIG(waitStatus
)]);
1903 } /* for (;;), waitpid() */
1907 /* wait for an event to occur or timer expires */
1908 expire
= time(NULL
) + sleepSeconds
;
1909 dwRet
= WaitForMultipleObjects(3, (HANDLE
*) fcgi_event_handles
, FALSE
, sleepSeconds
* 1000);
1911 if (dwRet
== WAIT_FAILED
) {
1912 /* There is something seriously wrong here */
1913 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1914 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1918 if (dwRet
!= WAIT_TIMEOUT
) {
1922 alarmLeft
= expire
- now
;
1926 * Dynamic fcgi process management
1928 if ((dwRet
== MBOX_EVENT
) || (dwRet
== WAIT_TIMEOUT
)) {
1929 if (dwRet
== MBOX_EVENT
) {
1935 dynamic_read_msgs(read_ready
);
1937 if(fcgi_dynamic_epoch
== 0) {
1938 fcgi_dynamic_epoch
= now
;
1941 if ((now
-fcgi_dynamic_epoch
>= (int) dynamicKillInterval
) ||
1942 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
) >= dynamicMaxProcs
)) {
1943 dynamic_kill_idle_fs_procs();
1944 fcgi_dynamic_epoch
= now
;
1948 else if (dwRet
== WAKE_EVENT
) {
1951 else if (dwRet
== TERM_EVENT
) {
1952 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
1953 "FastCGI: Termination event received process manager shutting down");
1956 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
1958 goto ProcessSigTerm
;
1961 // Have an received an unknown event - should not happen
1962 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1963 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1966 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
1968 goto ProcessSigTerm
;
1973 } /* for (;;), the whole shoot'n match */
1977 * Kill off the children, then exit.
1989 int fcgi_pm_add_job(fcgi_pm_job
*new_job
)
1991 int rv
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
1993 if (rv
!= WAIT_OBJECT_0
&& rv
!= WAIT_ABANDONED
)
1995 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1996 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2000 new_job
->next
= fcgi_dynamic_mbox
;
2001 fcgi_dynamic_mbox
= new_job
;
2003 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
2005 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2006 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");