2 * $Id: fcgi_pm.c,v 1.91 2004/04/15 00:32:56 robs Exp $
8 #if defined(APACHE2) && !defined(WIN32)
12 #include "apr_signal.h"
21 #define seteuid(arg) setresuid(-1, (arg), -1)
24 int fcgi_dynamic_total_proc_count
= 0; /* number of running apps */
25 time_t fcgi_dynamic_epoch
= 0; /* last time kill_procs was
26 * invoked by process mgr */
27 time_t fcgi_dynamic_last_analyzed
= 0; /* last time calculation was
28 * made for the dynamic procs */
30 static time_t now
= 0;
36 #pragma warning ( disable : 4100 4102 )
37 static BOOL bTimeToDie
= FALSE
; /* process termination flag */
38 HANDLE fcgi_event_handles
[3];
46 static int seteuid_root(void)
48 int rc
= seteuid(getuid());
50 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
51 "FastCGI: seteuid(0) failed");
56 static int seteuid_user(void)
58 int rc
= seteuid(ap_user_id
);
60 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
61 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id
);
68 * Signal the process to exit. How (or if) the process responds
69 * depends on the FastCGI application library (esp. on Win32) and
70 * possibly application code (signal handlers and whether or not
71 * SA_RESTART is on). At any rate, we send the signal with the
72 * hopes that the process will exit on its own. Later, as we
73 * review the state of application processes, if we see one marked
74 * for death, but that hasn't died within a specified period of
75 * time, fcgi_kill() is called again with a KILL)
77 static void fcgi_kill(ServerProcess
*process
, int sig
)
79 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process
->pid
, sig
);
81 process
->state
= FCGI_VICTIM_STATE
;
87 SetEvent(process
->terminationEvent
);
89 else if (sig
== SIGKILL
)
91 TerminateProcess(process
->handle
, 1);
105 kill(process
->pid
, sig
);
115 /*******************************************************************************
116 * Send SIGTERM to each process in the server class, remove socket
117 * file if appropriate. Currently this is only called when the PM is shutting
118 * down and thus memory isn't freed and sockets and files aren't closed.
120 static void shutdown_all()
122 fcgi_server
*s
= fcgi_servers
;
126 ServerProcess
*proc
= s
->procs
;
128 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
129 ? dynamicMaxClassProcs
133 if (s
->socket_path
!= NULL
&& s
->directive
!= APP_CLASS_EXTERNAL
)
135 /* Remove the socket file */
136 if (unlink(s
->socket_path
) != 0 && errno
!= ENOENT
) {
137 ap_log_error(FCGI_LOG_ERR
, fcgi_apache_main_server
,
138 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
140 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "", s
->fs_path
);
145 /* Send TERM to all processes */
146 for (i
= 0; i
< numChildren
; i
++, proc
++)
148 if (proc
->state
== FCGI_RUNNING_STATE
)
150 fcgi_kill(proc
, SIGTERM
);
157 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
160 * WIN32 applications may not have support for the shutdown event
161 * depending on their application library version
164 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT
);
169 ServerProcess
*proc
= s
->procs
;
171 int numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
172 ? dynamicMaxClassProcs
175 /* Send KILL to all processes */
176 for (i
= 0; i
< numChildren
; i
++, proc
++)
178 if (proc
->state
== FCGI_RUNNING_STATE
)
180 fcgi_kill(proc
, SIGKILL
);
190 static int init_listen_sock(fcgi_server
* fs
)
192 ap_assert(fs
->directive
!= APP_CLASS_EXTERNAL
);
194 /* Create the socket */
195 if ((fs
->listenFd
= socket(fs
->socket_addr
->sa_family
, SOCK_STREAM
, 0)) < 0)
198 errno
= WSAGetLastError(); /* Not sure if this will work as expected */
200 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
201 "FastCGI: can't create %sserver \"%s\": socket() failed",
202 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
208 if (fs
->socket_addr
->sa_family
== AF_UNIX
)
210 /* Remove any existing socket file.. just in case */
211 unlink(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
);
217 setsockopt(fs
->listenFd
, SOL_SOCKET
, SO_REUSEADDR
, (char *)&flag
, sizeof(flag
));
220 /* Bind it to the socket_addr */
221 if (bind(fs
->listenFd
, fs
->socket_addr
, fs
->socket_addr_len
))
226 errno
= WSAGetLastError();
228 ap_snprintf(port
, sizeof(port
), "port=%d",
229 ((struct sockaddr_in
*)fs
->socket_addr
)->sin_port
);
231 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
232 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
233 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
236 (fs
->socket_addr
->sa_family
== AF_UNIX
) ?
237 ((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
:
243 /* Twiddle Unix socket permissions */
244 else if (fs
->socket_addr
->sa_family
== AF_UNIX
245 && chmod(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
, S_IRUSR
| S_IWUSR
))
247 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
248 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
249 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
255 else if (listen(fs
->listenFd
, fs
->listenQueueDepth
))
258 errno
= WSAGetLastError();
260 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
261 "FastCGI: can't create %sserver \"%s\": listen() failed",
262 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
271 closesocket(fs
->listenFd
);
282 *----------------------------------------------------------------------
286 * The FastCGI process manager, which runs as a separate
287 * process responsible for:
288 * - Starting all the FastCGI proceses.
289 * - Restarting any of these processes that die (indicated
291 * - Catching SIGTERM and relaying it to all the FastCGI
292 * processes before exiting.
295 * Uses global variable fcgi_servers.
303 *----------------------------------------------------------------------
306 static int caughtSigTerm
= FALSE
;
307 static int caughtSigChld
= FALSE
;
308 static int caughtSigAlarm
= FALSE
;
310 static void signal_handler(int signo
)
312 if ((signo
== SIGTERM
) || (signo
== SIGUSR1
) || (signo
== SIGHUP
)) {
313 /* SIGUSR1 & SIGHUP are sent by apache to its process group
314 * when apache get 'em. Apache follows up (1.2.x) with attacks
315 * on each of its child processes, but we've got the KillMgr
316 * sitting between us so we never see the KILL. The main loop
317 * in ProcMgr also checks to see if the KillMgr has terminated,
318 * and if it has, we handl it as if we should shutdown too. */
319 caughtSigTerm
= TRUE
;
320 } else if(signo
== SIGCHLD
) {
321 caughtSigChld
= TRUE
;
322 } else if(signo
== SIGALRM
) {
323 caughtSigAlarm
= TRUE
;
329 *----------------------------------------------------------------------
331 * spawn_fs_process --
333 * Fork and exec the specified fcgi process.
336 * 0 for successful fork, -1 for failed fork.
338 * In case the child fails before or in the exec, the child
339 * obtains the error log by calling getErrLog, logs
340 * the error, and exits with exit status = errno of
341 * the failed system call.
344 * Child process created.
346 *----------------------------------------------------------------------
348 static pid_t
spawn_fs_process(fcgi_server
*fs
, ServerProcess
*process
)
355 char *dnEnd
, *failedSysCall
;
362 /* We're the child. We're gonna exec() so pools don't matter. */
364 dnEnd
= strrchr(fs
->fs_path
, '/');
368 dirName
= ap_pcalloc(fcgi_config_pool
, dnEnd
- fs
->fs_path
+ 1);
369 dirName
= memcpy(dirName
, fs
->fs_path
, dnEnd
- fs
->fs_path
);
371 if (chdir(dirName
) < 0) {
372 failedSysCall
= "chdir()";
373 goto FailedSystemCallExit
;
377 /* OS/2 dosen't support nice() */
378 if (fs
->processPriority
!= 0) {
379 if (nice(fs
->processPriority
) == -1) {
380 failedSysCall
= "nice()";
381 goto FailedSystemCallExit
;
386 /* Open the listenFd on spec'd fd */
387 if (fs
->listenFd
!= FCGI_LISTENSOCK_FILENO
)
388 dup2(fs
->listenFd
, FCGI_LISTENSOCK_FILENO
);
390 /* Close all other open fds, except stdout/stderr. Leave these two open so
391 * FastCGI applications don't have to find and fix ALL 3rd party libs that
392 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
393 * main server error_log - @@@ provide a directive control where this goes.
395 ap_error_log2stderr(fcgi_apache_main_server
);
397 for (i
= 0; i
< FCGI_MAX_FD
; i
++) {
398 if (i
!= FCGI_LISTENSOCK_FILENO
&& i
!= 2 && i
!= 1) {
403 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
404 * install its own handler. */
405 signal(SIGPIPE
, SIG_IGN
);
407 /* Apache (2 anyway) doesn't use suexec if there is no user/group in
408 * effect - this translates to a uid/gid of 0/0 (which should never
409 * be a valid uid/gid for a suexec invocation so it should be safe */
410 if (fcgi_wrapper
&& fs
->uid
&& fs
->gid
)
414 /* Relinquish our root real uid powers */
418 /* AP13 does not use suexec if the target uid/gid is the same as the
419 * server's - AP20 does. I (now) consider the AP2 approach better
420 * (fcgi_pm.c v1.42 incorporated the 1.3 behaviour, v1.84 reverted it,
421 * v1.85 added the compile time option to use the old behaviour). */
423 #ifdef NO_SUEXEC_FOR_AP_USER_N_GROUP
425 if (fcgi_user_id
== fs
->uid
&& fcgi_group_id
== fs
->gid
)
431 shortName
= strrchr(fs
->fs_path
, '/') + 1;
434 execle(fcgi_wrapper
, fcgi_wrapper
, fs
->username
, fs
->group
,
435 shortName
, NULL
, fs
->envp
);
436 } while (errno
== EINTR
);
443 execle(fs
->fs_path
, fs
->fs_path
, NULL
, fs
->envp
);
444 } while (errno
== EINTR
);
447 failedSysCall
= "execle()";
449 FailedSystemCallExit
:
450 fprintf(stderr
, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
451 fs
->fs_path
, (long) getpid(), failedSysCall
, strerror(errno
));
454 /* avoid an irrelevant compiler warning */
461 /* based on mod_cgi.c:run_cgi_child() */
464 char * termination_env_string
;
465 HANDLE listen_handle
= INVALID_HANDLE_VALUE
;
466 apr_procattr_t
* procattr
;
467 apr_proc_t proc
= { 0 };
470 cgi_exec_info_t e_info
= { 0 };
471 request_rec r
= { 0 };
475 APR_OPTIONAL_FN_TYPE(ap_cgi_build_command
) *cgi_build_command
;
477 cgi_build_command
= APR_RETRIEVE_OPTIONAL_FN(ap_cgi_build_command
);
478 if (cgi_build_command
== NULL
)
480 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
481 "FastCGI: can't exec server \"%s\", mod_cgi isn't loaded",
486 if (apr_pool_create(&tp
, fcgi_config_pool
))
489 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
490 if (process
->terminationEvent
== NULL
)
493 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
495 termination_env_string
= ap_psprintf(tp
,
496 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
498 while (fs
->envp
[i
]) i
++;
499 fs
->envp
[i
++] = termination_env_string
;
500 fs
->envp
[i
] = (char *) fs
->mutex_env_string
;
502 ap_assert(fs
->envp
[i
+ 1] == NULL
);
506 SECURITY_ATTRIBUTES sa
= { 0 };
508 sa
.bInheritHandle
= TRUE
;
509 sa
.nLength
= sizeof(sa
);
511 listen_handle
= CreateNamedPipe(fs
->socket_path
,
513 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
514 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
516 if (listen_handle
== INVALID_HANDLE_VALUE
)
518 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
519 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
526 listen_handle
= (HANDLE
) fs
->listenFd
;
529 r
.per_dir_config
= fcgi_apache_main_server
->lookup_defaults
;
530 r
.server
= fcgi_apache_main_server
;
531 r
.filename
= (char *) fs
->fs_path
;
533 r
.subprocess_env
= apr_table_make(tp
, 0);
535 e_info
.cmd_type
= APR_PROGRAM
;
537 rv
= cgi_build_command(&command
, &argv
, &r
, tp
, &e_info
);
538 if (rv
!= APR_SUCCESS
)
540 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
541 "FastCGI: don't know how to spawn cmd child process: %s",
546 if (apr_procattr_create(&procattr
, tp
))
549 if (apr_procattr_dir_set(procattr
, ap_make_dirstr_parent(tp
, fs
->fs_path
)))
552 if (apr_procattr_cmdtype_set(procattr
, e_info
.cmd_type
))
555 if (apr_procattr_detach_set(procattr
, 1))
558 if (apr_os_file_put(&file
, &listen_handle
, 0, tp
))
561 /* procattr is opaque so we have to use this - unfortuantely it dups */
562 if (apr_procattr_child_in_set(procattr
, file
, NULL
))
565 if (apr_proc_create(&proc
, command
, argv
, fs
->envp
, procattr
, tp
))
568 process
->handle
= proc
.hproc
;
572 if (fs
->socket_path
&& listen_handle
!= INVALID_HANDLE_VALUE
)
574 CloseHandle(listen_handle
);
579 fs
->envp
[i
- 1] = NULL
;
586 #else /* WIN32 && !APACHE2 */
588 /* Adapted from Apache's util_script.c ap_call_exec() */
589 char *interpreter
= NULL
;
590 char *quoted_filename
;
592 char *pEnvBlock
, *pNext
;
595 int iEnvBlockLen
= 1;
597 file_type_e fileType
;
600 PROCESS_INFORMATION pi
;
605 pool
* tp
= ap_make_sub_pool(fcgi_config_pool
);
607 HANDLE listen_handle
= INVALID_HANDLE_VALUE
;
608 char * termination_env_string
= NULL
;
610 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
611 if (process
->terminationEvent
== NULL
)
613 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
614 "FastCGI: can't create termination event for server \"%s\", "
615 "CreateEvent() failed", fs
->fs_path
);
618 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
620 termination_env_string
= ap_psprintf(tp
,
621 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
625 SECURITY_ATTRIBUTES sa
;
627 sa
.lpSecurityDescriptor
= NULL
;
628 sa
.bInheritHandle
= TRUE
;
629 sa
.nLength
= sizeof(sa
);
631 listen_handle
= CreateNamedPipe(fs
->socket_path
,
633 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
634 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
636 if (listen_handle
== INVALID_HANDLE_VALUE
)
638 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
639 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs
->fs_path
);
645 listen_handle
= (HANDLE
) fs
->listenFd
;
648 memset(&si
, 0, sizeof(si
));
649 memset(&pi
, 0, sizeof(pi
));
650 memset(&r
, 0, sizeof(r
));
652 /* Can up a fake request to pass to ap_get_win32_interpreter() */
653 r
.per_dir_config
= fcgi_apache_main_server
->lookup_defaults
;
654 r
.server
= fcgi_apache_main_server
;
655 r
.filename
= (char *) fs
->fs_path
;
658 fileType
= ap_get_win32_interpreter(&r
, &interpreter
);
660 if (fileType
== eFileTypeUNKNOWN
) {
661 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
662 "FastCGI: %s is not executable; ensure interpreted scripts have "
663 "\"#!\" as their first line",
670 * We have the interpreter (if there is one) and we have
671 * the arguments (if there are any).
672 * Build the command string to pass to CreateProcess.
674 quoted_filename
= ap_pstrcat(tp
, "\"", fs
->fs_path
, "\"", NULL
);
675 if (interpreter
&& *interpreter
) {
676 pCommand
= ap_pstrcat(tp
, interpreter
, " ", quoted_filename
, NULL
);
679 pCommand
= quoted_filename
;
683 * Make child process use hPipeOutputWrite as standard out,
684 * and make sure it does not show on screen.
687 si
.dwFlags
= STARTF_USESHOWWINDOW
| STARTF_USESTDHANDLES
;
688 si
.wShowWindow
= SW_HIDE
;
689 si
.hStdInput
= listen_handle
;
691 /* XXX These should be open to the error_log */
692 si
.hStdOutput
= INVALID_HANDLE_VALUE
;
693 si
.hStdError
= INVALID_HANDLE_VALUE
;
696 * Win32's CreateProcess call requires that the environment
697 * be passed in an environment block, a null terminated block of
698 * null terminated strings.
699 * @todo we should store the env in this format for win32.
703 iEnvBlockLen
+= strlen(fs
->envp
[i
]) + 1;
707 iEnvBlockLen
+= strlen(termination_env_string
) + 1;
708 iEnvBlockLen
+= strlen(fs
->mutex_env_string
) + 1;
710 pEnvBlock
= (char *) ap_pcalloc(tp
, iEnvBlockLen
);
716 strcpy(pNext
, fs
->envp
[i
]);
717 pNext
+= strlen(pNext
) + 1;
721 strcpy(pNext
, termination_env_string
);
722 pNext
+= strlen(pNext
) + 1;
723 strcpy(pNext
, fs
->mutex_env_string
);
725 if (CreateProcess(NULL
, pCommand
, NULL
, NULL
, TRUE
,
728 ap_make_dirstr_parent(tp
, fs
->fs_path
),
731 /* Hack to get 16-bit CGI's working. It works for all the
732 * standard modules shipped with Apache. pi.dwProcessId is 0
733 * for 16-bit CGIs and all the Unix specific code that calls
734 * ap_call_exec interprets this as a failure case. And we can't
735 * use -1 either because it is mapped to 0 by the caller.
737 pid
= (fileType
== eFileTypeEXE16
) ? -2 : pi
.dwProcessId
;
739 process
->handle
= pi
.hProcess
;
740 CloseHandle(pi
.hThread
);
745 if (fs
->socket_path
&& listen_handle
!= INVALID_HANDLE_VALUE
)
747 CloseHandle(listen_handle
);
754 #endif /* !APACHE2 */
759 static void reduce_privileges(void)
767 /* Get username if passed as a uid */
768 if (ap_user_name
[0] == '#') {
769 uid_t uid
= atoi(&ap_user_name
[1]);
770 struct passwd
*ent
= getpwuid(uid
);
773 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
774 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
775 "you probably need to modify the User directive", (unsigned)uid
);
784 if (setgid(ap_group_id
) == -1) {
785 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
786 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id
);
790 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
792 /* Initialize supplementary groups */
793 if (initgroups(name
, ap_group_id
) == -1) {
794 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
795 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
796 name
, (unsigned)ap_group_id
);
803 if (seteuid_user() == -1) {
804 ap_log_error(FCGI_LOG_ALERT_NOERRNO
, fcgi_apache_main_server
,
805 "FastCGI: process manager exiting, failed to reduce privileges");
810 if (setuid(ap_user_id
) == -1) {
811 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
812 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id
);
819 * Change the name of this process - best we can easily.
821 static void change_process_name(const char * const name
)
823 /* under Apache2, ap_server_argv0 is const */
824 strncpy((char *) ap_server_argv0
, name
, strlen(ap_server_argv0
));
828 static void schedule_start(fcgi_server
*s
, int proc
)
830 /* If we've started one recently, don't register another */
831 time_t time_passed
= now
- s
->restartTime
;
833 if ((s
->procs
[proc
].pid
&& (time_passed
< (int) s
->restartDelay
))
834 || ((s
->procs
[proc
].pid
== 0) && (time_passed
< s
->initStartDelay
)))
836 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
);
840 FCGIDBG3("scheduling_start: %s (%d)", s
->fs_path
, proc
);
841 s
->procs
[proc
].state
= FCGI_START_STATE
;
842 if (proc
== dynamicMaxClassProcs
- 1) {
843 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
844 "FastCGI: scheduled the %sstart of the last (dynamic) server "
845 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
846 s
->procs
[proc
].pid
? "re" : "", s
->fs_path
, dynamicMaxClassProcs
);
851 *----------------------------------------------------------------------
855 * Removes the records written by request handlers and decodes them.
856 * We also update the data structures to reflect the changes.
858 *----------------------------------------------------------------------
861 static void dynamic_read_msgs(int read_ready
)
867 static int buflen
= 0;
868 static char buf
[FCGI_MSGS_BUFSIZE
+ 1];
869 char *ptr1
, *ptr2
, opcode
;
870 char execName
[FCGI_MAXPATH
+ 1];
871 char user
[MAX_USER_NAME_LEN
+ 2];
872 char group
[MAX_GID_CHAR_LEN
+ 1];
873 unsigned long q_usec
= 0UL, req_usec
= 0UL;
875 fcgi_pm_job
*joblist
= NULL
;
876 fcgi_pm_job
*cjob
= NULL
;
879 pool
*sp
= NULL
, *tp
;
882 user
[MAX_USER_NAME_LEN
+ 1] = group
[MAX_GID_CHAR_LEN
] = '\0';
886 * To prevent the idle application from running indefinitely, we
887 * check the timer and if it is expired, we recompute the values
888 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
889 * message is received, only updates are made to the data structures.
891 if (fcgi_dynamic_last_analyzed
== 0) {
892 fcgi_dynamic_last_analyzed
= now
;
894 if ((now
- fcgi_dynamic_last_analyzed
) >= (int)dynamicUpdateInterval
) {
895 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
896 if (s
->directive
!= APP_CLASS_DYNAMIC
)
899 /* Advance the last analyzed timestamp by the elapsed time since
900 * it was last set. Round the increase down to the nearest
901 * multiple of dynamicUpdateInterval */
903 fcgi_dynamic_last_analyzed
+= (((long)(now
-fcgi_dynamic_last_analyzed
)/dynamicUpdateInterval
)*dynamicUpdateInterval
);
904 s
->smoothConnTime
= (unsigned long) ((1.0-dynamicGain
)*s
->smoothConnTime
+ dynamicGain
*s
->totalConnTime
);
905 s
->totalConnTime
= 0UL;
906 s
->totalQueueTime
= 0UL;
910 if (read_ready
<= 0) {
915 rc
= read(fcgi_pm_pipe
[0], (void *)(buf
+ buflen
), FCGI_MSGS_BUFSIZE
- buflen
);
917 if (!caughtSigTerm
) {
918 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
919 "FastCGI: read() from pipe failed (%d)", rc
);
921 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
922 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
923 caughtSigTerm
= TRUE
;
933 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
934 * request to do something) and/or when a timeout expires.
935 * There really should be no reason why this wait would get stuck
936 * but there's no point in waiting forever. */
938 rc
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
940 if (rc
!= WAIT_OBJECT_0
&& rc
!= WAIT_ABANDONED
)
942 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
943 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
947 joblist
= fcgi_dynamic_mbox
;
948 fcgi_dynamic_mbox
= NULL
;
950 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
952 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
953 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
960 apr_pool_create(&tp
, fcgi_config_pool
);
962 tp
= ap_make_sub_pool(fcgi_config_pool
);
966 for (ptr1
= buf
; ptr1
; ptr1
= ptr2
) {
969 ptr2
= strchr(ptr1
, '*');
981 case FCGI_SERVER_START_JOB
:
982 case FCGI_SERVER_RESTART_JOB
:
984 if (sscanf(ptr1
, "%c %s %16s %15s",
985 &opcode
, execName
, user
, group
) != 4)
991 case FCGI_REQUEST_TIMEOUT_JOB
:
993 if (sscanf(ptr1
, "%c %s %16s %15s",
994 &opcode
, execName
, user
, group
) != 4)
1000 case FCGI_REQUEST_COMPLETE_JOB
:
1002 if (sscanf(ptr1
, "%c %s %16s %15s %lu %lu",
1003 &opcode
, execName
, user
, group
, &q_usec
, &req_usec
) != 6)
1015 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode
, execName
, user
, group
, q_usec
, req_usec
);
1018 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1019 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1
);
1023 /* Update data structures for processing */
1024 while (cjob
!= NULL
) {
1025 joblist
= cjob
->next
;
1026 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob
->id
, cjob
->fs_path
, cjob
->user
, cjob
->group
, cjob
->qsec
, cjob
->start_time
);
1030 s
= fcgi_util_fs_get(execName
, user
, group
);
1032 s
= fcgi_util_fs_get(cjob
->fs_path
, cjob
->user
, cjob
->group
);
1036 if (s
==NULL
&& opcode
!= FCGI_REQUEST_COMPLETE_JOB
)
1038 if (s
==NULL
&& cjob
->id
!= FCGI_REQUEST_COMPLETE_JOB
)
1043 HANDLE mutex
= CreateMutex(NULL
, FALSE
, cjob
->fs_path
);
1047 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1048 "FastCGI: can't create accept mutex "
1049 "for (dynamic) server \"%s\"", cjob
->fs_path
);
1053 SetHandleInformation(mutex
, HANDLE_FLAG_INHERIT
, TRUE
);
1058 /* Create a perm subpool to hold the new server data,
1059 * we can destroy it if something doesn't pan out */
1061 apr_pool_create(&sp
, fcgi_config_pool
);
1063 sp
= ap_make_sub_pool(fcgi_config_pool
);
1066 /* Create a new "dynamic" server */
1067 s
= fcgi_util_fs_new(sp
);
1069 s
->directive
= APP_CLASS_DYNAMIC
;
1070 s
->restartDelay
= dynamicRestartDelay
;
1071 s
->listenQueueDepth
= dynamicListenQueueDepth
;
1072 s
->initStartDelay
= dynamicInitStartDelay
;
1073 s
->envp
= dynamicEnvp
;
1074 s
->flush
= dynamicFlush
;
1077 s
->mutex_env_string
= ap_psprintf(sp
, "_FCGI_MUTEX_=%ld", mutex
);
1078 s
->fs_path
= ap_pstrdup(sp
, cjob
->fs_path
);
1080 s
->fs_path
= ap_pstrdup(sp
, execName
);
1082 ap_getparents(s
->fs_path
);
1083 ap_no2slash(s
->fs_path
);
1084 s
->procs
= fcgi_util_fs_create_procs(sp
, dynamicMaxClassProcs
);
1086 /* XXX the socket_path (both Unix and Win) *is* deducible and
1087 * thus can and will be used by other apache instances without
1088 * the use of shared data regarding the processes serving the
1089 * requests. This can result in slightly unintuitive process
1090 * counts and security implications. This is prevented
1091 * if suexec (Unix) is in use. This is both a feature and a flaw.
1092 * Changing it now would break existing installations. */
1095 /* Create socket file's path */
1096 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, execName
, user
, group
);
1097 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1099 /* Create sockaddr, prealloc it so it won't get created in tp */
1100 s
->socket_addr
= ap_pcalloc(sp
, sizeof(struct sockaddr_un
));
1101 err
= fcgi_util_socket_make_domain_addr(tp
, (struct sockaddr_un
**)&s
->socket_addr
,
1102 &s
->socket_addr_len
, s
->socket_path
);
1104 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1105 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
1109 if (init_listen_sock(s
)) {
1113 /* If a wrapper is being used, config user/group info */
1115 if (user
[0] == '~') {
1116 /* its a user dir uri, the rest is a username, not a uid */
1117 struct passwd
*pw
= getpwnam(&user
[1]);
1120 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1121 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1122 execName
, &user
[1]);
1125 s
->uid
= pw
->pw_uid
;
1126 s
->user
= ap_pstrdup(sp
, user
);
1127 s
->username
= s
->user
;
1129 s
->gid
= pw
->pw_gid
;
1130 s
->group
= ap_psprintf(sp
, "%ld", (long)s
->gid
);
1135 s
->uid
= (uid_t
)atol(user
);
1136 pw
= getpwuid(s
->uid
);
1138 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1139 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1140 execName
, (long)s
->uid
);
1143 s
->user
= ap_pstrdup(sp
, user
);
1144 s
->username
= ap_pstrdup(sp
, pw
->pw_name
);
1146 s
->gid
= (gid_t
)atol(group
);
1147 s
->group
= ap_pstrdup(sp
, group
);
1151 /* Create socket file's path */
1152 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, cjob
->fs_path
, cjob
->user
, cjob
->group
);
1153 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1157 fcgi_util_fs_add(s
);
1161 if (opcode
== FCGI_SERVER_RESTART_JOB
) {
1163 if (cjob
->id
==FCGI_SERVER_RESTART_JOB
) {
1165 /* Check to see if the binary has changed. If so,
1166 * kill the FCGI application processes, and
1172 char * app_path
= cjob
->fs_path
;
1174 char * app_path
= execName
;
1177 if (stat(app_path
, &stbuf
) == 0 && stbuf
.st_mtime
> s
->startTime
)
1181 /* prevent addition restart requests */
1184 utime(s
->socket_path
, NULL
);
1187 /* kill old server(s) */
1188 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1190 if (s
->procs
[i
].pid
> 0
1191 && stbuf
.st_mtime
> s
->procs
[i
].start_time
)
1193 fcgi_kill(&s
->procs
[i
], SIGTERM
);
1200 ap_log_error(FCGI_LOG_WARN_NOERRNO
,
1201 fcgi_apache_main_server
, "FastCGI: restarting "
1202 "old server \"%s\" processes, newer version "
1207 /* If dynamicAutoRestart, don't mark any new processes
1208 * for starting because we probably got the
1209 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1210 * will be restarting all of those we just killed.
1212 if (dynamicAutoRestart
)
1216 else if (opcode
== FCGI_SERVER_START_JOB
) {
1218 else if (cjob
->id
==FCGI_SERVER_START_JOB
) {
1220 /* we've been asked to start a process--only start
1221 * it if we're not already running at least one
1226 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1227 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1230 /* if already running, don't start another one */
1231 if (i
< dynamicMaxClassProcs
) {
1245 case FCGI_SERVER_RESTART_JOB
:
1249 /* We just waxed 'em all. Try to find an idle slot. */
1251 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1253 if (s
->procs
[i
].state
== FCGI_START_STATE
1254 || s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1258 else if (s
->procs
[i
].state
== FCGI_KILLED_STATE
1259 || s
->procs
[i
].state
== FCGI_READY_STATE
)
1266 /* Nope, just use the first slot */
1267 if (i
== dynamicMaxClassProcs
)
1275 schedule_start(s
, i
);
1280 case FCGI_SERVER_START_JOB
:
1281 case FCGI_REQUEST_TIMEOUT_JOB
:
1283 if ((fcgi_dynamic_total_proc_count
+ 1) > (int) dynamicMaxProcs
) {
1285 * Extra instances should have been
1286 * terminated beforehand, probably need
1287 * to increase ProcessSlack parameter
1289 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1290 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1291 "exceeded dynamicMaxProcs (%d)", s
->fs_path
, dynamicMaxProcs
);
1295 /* find next free slot */
1296 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1298 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1300 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i
);
1303 else if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1308 schedule_start(s
, i
);
1313 if (i
>= dynamicMaxClassProcs
) {
1314 FCGIDBG1("ignore_job: slots are max'd");
1318 case FCGI_REQUEST_COMPLETE_JOB
:
1319 /* only record stats if we have a structure */
1322 s
->totalConnTime
+= req_usec
;
1323 s
->totalQueueTime
+= q_usec
;
1325 s
->totalConnTime
+= cjob
->start_time
;
1326 s
->totalQueueTime
+= cjob
->qsec
;
1335 /* Cleanup job data */
1336 free(cjob
->fs_path
);
1346 if (sp
) ap_destroy_pool(sp
);
1349 free(cjob
->fs_path
);
1357 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1358 "FastCGI: really bogus message: \"%s\"", ptr1
);
1359 ptr1
+= strlen(buf
);
1362 buflen
-= ptr1
- buf
;
1364 memmove(buf
, ptr1
, buflen
);
1368 ap_destroy_pool(tp
);
1372 *----------------------------------------------------------------------
1374 * dynamic_kill_idle_fs_procs
1376 * Implement a kill policy for the dynamic FastCGI applications.
1377 * We also update the data structures to reflect the changes.
1380 * Processes are marked for deletion possibly killed.
1382 *----------------------------------------------------------------------
1384 static void dynamic_kill_idle_fs_procs(void)
1389 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1392 * server's smoothed running time, or if that's 0, the current total
1394 unsigned long connTime
;
1397 * maximum number of microseconds that all of a server's running
1398 * processes together could have spent running since the last check
1400 unsigned long totalTime
;
1403 * percentage, 0-100, of totalTime that the processes actually used
1408 int really_running
= 0;
1410 if (s
->directive
!= APP_CLASS_DYNAMIC
|| s
->numProcesses
== 0)
1415 /* s->numProcesses includes pending kills so get the "active" count */
1416 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1418 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
) ++really_running
;
1421 connTime
= s
->smoothConnTime
? s
->smoothConnTime
: s
->totalConnTime
;
1422 totalTime
= really_running
* (now
- fcgi_dynamic_epoch
) * 1000000 + 1;
1424 loadFactor
= 100 * connTime
/ totalTime
;
1426 if (really_running
== 1)
1428 if (loadFactor
>= dynamicThreshold1
)
1435 int load
= really_running
/ ( really_running
- 1) * loadFactor
;
1437 if (load
>= dynamicThresholdN
)
1444 * Run through the procs to see if we can get away w/o waxing one.
1446 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1448 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1450 s
->procs
[i
].state
= FCGI_READY_STATE
;
1453 else if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
)
1459 if (i
>= dynamicMaxClassProcs
)
1461 ServerProcess
* procs
= s
->procs
;
1464 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1466 if (procs
[i
].state
== FCGI_RUNNING_STATE
)
1468 if (youngest
== -1 || procs
[i
].start_time
>= procs
[youngest
].start_time
)
1477 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1478 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1479 s
->fs_path
, (long) s
->procs
[youngest
].pid
);
1481 fcgi_kill(&s
->procs
[youngest
], SIGTERM
);
1487 * If the number of non-victims is less than or equal to
1488 * the minimum that may be running without being killed off,
1489 * don't select any more victims.
1491 if (fcgi_dynamic_total_proc_count
- victims
<= dynamicMinProcs
)
1501 /* This is a little bogus, there's gotta be a better way to do this
1502 * Can we use WaitForMultipleObjects() */
1503 #define FCGI_PROC_WAIT_TIME 100
1505 void child_wait_thread_main(void *dummy
) {
1507 DWORD dwRet
= WAIT_TIMEOUT
;
1512 while (!bTimeToDie
) {
1515 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1516 if (s
->directive
== APP_CLASS_EXTERNAL
|| s
->listenFd
< 0) {
1519 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1520 numChildren
= dynamicMaxClassProcs
;
1523 numChildren
= s
->numProcesses
;
1526 for (i
=0; i
< numChildren
; i
++) {
1527 if (s
->procs
[i
].handle
!= INVALID_HANDLE_VALUE
)
1529 DWORD exitStatus
= 0;
1531 /* timeout is currently set for 100 miliecond */
1532 /* it may need to be longer or user customizable */
1533 dwRet
= WaitForSingleObject(s
->procs
[i
].handle
, FCGI_PROC_WAIT_TIME
);
1537 if (dwRet
!= WAIT_TIMEOUT
&& dwRet
!= WAIT_FAILED
) {
1538 /* a child fs has died */
1539 /* mark the child as dead */
1541 if (s
->directive
== APP_CLASS_STANDARD
) {
1542 /* restart static app */
1543 s
->procs
[i
].state
= FCGI_START_STATE
;
1548 fcgi_dynamic_total_proc_count
--;
1549 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1551 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1552 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1555 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1558 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0)) {
1559 s
->procs
[i
].state
= FCGI_START_STATE
;
1562 s
->procs
[i
].state
= FCGI_READY_STATE
;
1567 GetExitCodeProcess(s
->procs
[i
].handle
, &exitStatus
);
1569 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1570 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1571 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1572 s
->fs_path
, (long) s
->procs
[i
].pid
, exitStatus
);
1574 CloseHandle(s
->procs
[i
].handle
);
1575 CloseHandle(s
->procs
[i
].terminationEvent
);
1576 s
->procs
[i
].handle
= INVALID_HANDLE_VALUE
;
1577 s
->procs
[i
].terminationEvent
= INVALID_HANDLE_VALUE
;
1578 s
->procs
[i
].pid
= -1;
1580 /* wake up the main thread */
1581 SetEvent(fcgi_event_handles
[WAKE_EVENT
]);
1586 Sleep(waited
? 0 : FCGI_PROC_WAIT_TIME
);
1592 static void setup_signals(void)
1594 struct sigaction sa
;
1596 /* Setup handlers */
1598 sa
.sa_handler
= signal_handler
;
1599 sigemptyset(&sa
.sa_mask
);
1602 if (sigaction(SIGTERM
, &sa
, NULL
) < 0) {
1603 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1604 "sigaction(SIGTERM) failed");
1607 if (sigaction(SIGHUP
, &sa
, NULL
) < 0) {
1608 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1609 "sigaction(SIGHUP) failed");
1611 /* httpd graceful restart */
1612 if (sigaction(SIGUSR1
, &sa
, NULL
) < 0) {
1613 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1614 "sigaction(SIGUSR1) failed");
1616 /* read messages from request handlers - kill interval expired */
1617 if (sigaction(SIGALRM
, &sa
, NULL
) < 0) {
1618 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1619 "sigaction(SIGALRM) failed");
1621 if (sigaction(SIGCHLD
, &sa
, NULL
) < 0) {
1622 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1623 "sigaction(SIGCHLD) failed");
1628 #if !defined(WIN32) && !defined(APACHE2)
1629 int fcgi_pm_main(void *dummy
, child_info
*info
)
1631 void fcgi_pm_main(void *dummy
)
1641 HANDLE child_wait_thread
= INVALID_HANDLE_VALUE
;
1643 int callWaitPid
, callDynamicProcs
;
1647 /* Add SystemRoot to the dynamic environment */
1648 char ** envp
= dynamicEnvp
;
1649 for (i
= 0; *envp
; ++i
) {
1652 fcgi_config_set_env_var(fcgi_config_pool
, dynamicEnvp
, &i
, "SystemRoot");
1656 reduce_privileges();
1657 change_process_name("fcgi-pm");
1659 close(fcgi_pm_pipe
[1]);
1663 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1664 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper
);
1668 /* Initialize AppClass */
1669 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1671 if (s
->directive
!= APP_CLASS_STANDARD
)
1679 for (i
= 0; i
< s
->numProcesses
; ++i
)
1680 s
->procs
[i
].state
= FCGI_START_STATE
;
1684 child_wait_thread
= (HANDLE
) _beginthread(child_wait_thread_main
, 0, NULL
);
1686 if (child_wait_thread
== (HANDLE
) -1)
1688 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1689 "FastCGI: failed to create process manager's wait thread!");
1692 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1693 "FastCGI: process manager initialized");
1695 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1696 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1702 * Loop until SIGTERM
1705 int sleepSeconds
= min(dynamicKillInterval
, dynamicUpdateInterval
);
1712 unsigned int numChildren
;
1713 unsigned int minServerLife
;
1716 * If we came out of sigsuspend() for any reason other than
1717 * SIGALRM, pick up where we left off.
1720 sleepSeconds
= alarmLeft
;
1723 * Examine each configured AppClass for a process that needs
1724 * starting. Compute the earliest time when the start should
1725 * be attempted, starting it now if the time has passed. Also,
1726 * remember that we do NOT need to restart externally managed
1727 * FastCGI applications.
1729 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1731 if (s
->directive
== APP_CLASS_EXTERNAL
)
1734 numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
1735 ? dynamicMaxClassProcs
1738 minServerLife
= (s
->directive
== APP_CLASS_DYNAMIC
)
1739 ? dynamicMinServerLife
1742 for (i
= 0; i
< numChildren
; ++i
)
1744 if (s
->procs
[i
].pid
<= 0 && s
->procs
[i
].state
== FCGI_START_STATE
)
1746 int restart
= (s
->procs
[i
].pid
< 0);
1747 time_t restartTime
= s
->restartTime
;
1751 /* we've gone to using the badDelay, the only thing that
1752 resets bad is when badDelay has expired. but numFailures
1753 is only just set below its threshold. the proc's
1754 start_times are all reset when the bad is. the numFailures
1755 is reset when we see an app run for a period */
1757 s
->procs
[i
].start_time
= 0;
1760 if (s
->numFailures
> MAX_FAILED_STARTS
)
1762 time_t last_start_time
= s
->procs
[i
].start_time
;
1764 if (last_start_time
&& now
- last_start_time
> minServerLife
)
1768 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1769 "FastCGI:%s server \"%s\" has remained"
1770 " running for more than %d seconds, its restart"
1771 " interval has been restored to %d seconds",
1772 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1773 s
->fs_path
, minServerLife
, s
->restartDelay
);
1779 for (j
= 0; j
< numChildren
; ++j
)
1781 if (s
->procs
[j
].pid
<= 0) continue;
1782 if (s
->procs
[j
].state
!= FCGI_RUNNING_STATE
) continue;
1783 if (s
->procs
[j
].start_time
== 0) continue;
1784 if (now
- s
->procs
[j
].start_time
> minServerLife
) break;
1787 if (j
>= numChildren
)
1790 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1791 "FastCGI:%s server \"%s\" has failed to remain"
1792 " running for %d seconds given %d attempts, its restart"
1793 " interval has been backed off to %d seconds",
1794 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1795 s
->fs_path
, minServerLife
, MAX_FAILED_STARTS
,
1796 FAILED_STARTS_DELAY
);
1802 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1803 "FastCGI:%s server \"%s\" has remained"
1804 " running for more than %d seconds, its restart"
1805 " interval has been restored to %d seconds",
1806 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1807 s
->fs_path
, minServerLife
, s
->restartDelay
);
1814 restartTime
+= FAILED_STARTS_DELAY
;
1818 restartTime
+= (restart
) ? s
->restartDelay
: s
->initStartDelay
;
1821 if (restartTime
<= now
)
1826 s
->numFailures
= MAX_FAILED_STARTS
;
1829 if (s
->listenFd
< 0 && init_listen_sock(s
))
1831 if (sleepSeconds
> s
->initStartDelay
)
1832 sleepSeconds
= s
->initStartDelay
;
1836 if (caughtSigTerm
) {
1837 goto ProcessSigTerm
;
1840 s
->procs
[i
].pid
= spawn_fs_process(s
, &s
->procs
[i
]);
1841 if (s
->procs
[i
].pid
<= 0) {
1842 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1843 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1844 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1847 sleepSeconds
= min(sleepSeconds
,
1848 max((int) s
->restartDelay
, FCGI_MIN_EXEC_RETRY_DELAY
));
1850 s
->procs
[i
].pid
= -1;
1854 s
->procs
[i
].start_time
= now
;
1855 s
->restartTime
= now
;
1857 if (s
->startTime
== 0) {
1861 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1863 fcgi_dynamic_total_proc_count
++;
1864 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1867 s
->procs
[i
].state
= FCGI_RUNNING_STATE
;
1870 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1871 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1872 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1873 s
->fs_path
, (long) s
->uid
, (long) s
->gid
,
1874 restart
? "re" : "", (long) s
->procs
[i
].pid
);
1877 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1878 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1879 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1880 s
->fs_path
, restart
? "re" : "", (long) s
->procs
[i
].pid
);
1882 ap_assert(s
->procs
[i
].pid
> 0);
1884 sleepSeconds
= min(sleepSeconds
, restartTime
- now
);
1893 goto ProcessSigTerm
;
1895 if((!caughtSigChld
) && (!caughtSigAlarm
)) {
1898 alarm(sleepSeconds
);
1901 FD_SET(fcgi_pm_pipe
[0], &rfds
);
1902 read_ready
= ap_select(fcgi_pm_pipe
[0] + 1, &rfds
, NULL
, NULL
, NULL
);
1904 alarmLeft
= alarm(0);
1906 callWaitPid
= caughtSigChld
;
1907 caughtSigChld
= FALSE
;
1908 callDynamicProcs
= caughtSigAlarm
;
1909 caughtSigAlarm
= FALSE
;
1914 * Dynamic fcgi process management
1916 if((callDynamicProcs
) || (!callWaitPid
)) {
1917 dynamic_read_msgs(read_ready
);
1918 if(fcgi_dynamic_epoch
== 0) {
1919 fcgi_dynamic_epoch
= now
;
1921 if(((long)(now
-fcgi_dynamic_epoch
)>=dynamicKillInterval
) ||
1922 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
)>=dynamicMaxProcs
)) {
1923 dynamic_kill_idle_fs_procs();
1924 fcgi_dynamic_epoch
= now
;
1932 /* We've caught SIGCHLD, so find out who it was using waitpid,
1933 * write a log message and update its data structure. */
1937 goto ProcessSigTerm
;
1939 childPid
= waitpid(-1, &waitStatus
, WNOHANG
);
1941 if (childPid
== -1 || childPid
== 0)
1944 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1945 if (s
->directive
== APP_CLASS_EXTERNAL
)
1948 if (s
->directive
== APP_CLASS_DYNAMIC
)
1949 numChildren
= dynamicMaxClassProcs
;
1951 numChildren
= s
->numProcesses
;
1953 for (i
= 0; i
< numChildren
; i
++) {
1954 if (s
->procs
[i
].pid
== childPid
)
1959 /* TODO: print something about this unknown child */
1963 s
->procs
[i
].pid
= -1;
1965 if (s
->directive
== APP_CLASS_STANDARD
) {
1966 /* Always restart static apps */
1967 s
->procs
[i
].state
= FCGI_START_STATE
;
1972 fcgi_dynamic_total_proc_count
--;
1974 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1975 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1978 /* A dynamic app died or exited without provocation from the PM */
1981 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0))
1982 s
->procs
[i
].state
= FCGI_START_STATE
;
1984 s
->procs
[i
].state
= FCGI_READY_STATE
;
1988 if (WIFEXITED(waitStatus
)) {
1989 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1990 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1991 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1992 s
->fs_path
, (long) childPid
, WEXITSTATUS(waitStatus
));
1994 else if (WIFSIGNALED(waitStatus
)) {
1995 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1996 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1997 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1998 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
),
2000 WCOREDUMP(waitStatus
) ? ", a core file may have been generated" : "");
2005 else if (WIFSTOPPED(waitStatus
)) {
2006 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
2007 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
2008 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
2009 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
));
2011 } /* for (;;), waitpid() */
2015 /* wait for an event to occur or timer expires */
2016 expire
= time(NULL
) + sleepSeconds
;
2017 dwRet
= WaitForMultipleObjects(3, (HANDLE
*) fcgi_event_handles
, FALSE
, sleepSeconds
* 1000);
2019 if (dwRet
== WAIT_FAILED
) {
2020 /* There is something seriously wrong here */
2021 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
2022 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
2026 if (dwRet
!= WAIT_TIMEOUT
) {
2030 alarmLeft
= expire
- now
;
2034 * Dynamic fcgi process management
2036 if ((dwRet
== MBOX_EVENT
) || (dwRet
== WAIT_TIMEOUT
)) {
2037 if (dwRet
== MBOX_EVENT
) {
2043 dynamic_read_msgs(read_ready
);
2045 if(fcgi_dynamic_epoch
== 0) {
2046 fcgi_dynamic_epoch
= now
;
2049 if ((now
-fcgi_dynamic_epoch
>= (int) dynamicKillInterval
) ||
2050 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
) >= dynamicMaxProcs
)) {
2051 dynamic_kill_idle_fs_procs();
2052 fcgi_dynamic_epoch
= now
;
2056 else if (dwRet
== WAKE_EVENT
) {
2059 else if (dwRet
== TERM_EVENT
) {
2060 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
2061 "FastCGI: Termination event received process manager shutting down");
2064 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
2066 goto ProcessSigTerm
;
2069 /* Have an received an unknown event - should not happen */
2070 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
2071 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
2074 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
2076 goto ProcessSigTerm
;
2081 } /* for (;;), the whole shoot'n match */
2085 * Kill off the children, then exit.
2097 int fcgi_pm_add_job(fcgi_pm_job
*new_job
)
2099 int rv
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
2101 if (rv
!= WAIT_OBJECT_0
&& rv
!= WAIT_ABANDONED
)
2103 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2104 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2108 new_job
->next
= fcgi_dynamic_mbox
;
2109 fcgi_dynamic_mbox
= new_job
;
2111 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
2113 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2114 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");