2 * $Id: fcgi_pm.c,v 1.87 2003/06/19 02:18:00 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
);
411 /* Relinquish our root real uid powers */
415 /* AP13 does not use suexec if the target uid/gid is the same as the
416 * server's - AP20 does. I (now) consider the AP2 approach better
417 * (fcgi_pm.c v1.42 incorporated the 1.3 behaviour, v1.84 reverted it,
418 * v1.85 added the compile time option to use the old behaviour). */
420 #ifdef NO_SUEXEC_FOR_AP_USER_N_GROUP
422 if (fcgi_user_id
== fs
->uid
&& fcgi_group_id
== fs
->gid
)
428 shortName
= strrchr(fs
->fs_path
, '/') + 1;
431 execle(fcgi_wrapper
, fcgi_wrapper
, fs
->username
, fs
->group
,
432 shortName
, NULL
, fs
->envp
);
433 } while (errno
== EINTR
);
440 execle(fs
->fs_path
, fs
->fs_path
, NULL
, fs
->envp
);
441 } while (errno
== EINTR
);
444 failedSysCall
= "execle()";
446 FailedSystemCallExit
:
447 fprintf(stderr
, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
448 fs
->fs_path
, (long) getpid(), failedSysCall
, strerror(errno
));
451 /* avoid an irrelevant compiler warning */
458 /* based on mod_cgi.c:run_cgi_child() */
461 char * termination_env_string
;
462 HANDLE listen_handle
= INVALID_HANDLE_VALUE
;
463 apr_procattr_t
* procattr
;
464 apr_proc_t proc
= { 0 };
467 cgi_exec_info_t e_info
= { 0 };
468 request_rec r
= { 0 };
472 APR_OPTIONAL_FN_TYPE(ap_cgi_build_command
) *cgi_build_command
;
474 cgi_build_command
= APR_RETRIEVE_OPTIONAL_FN(ap_cgi_build_command
);
475 if (cgi_build_command
== NULL
)
477 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
478 "FastCGI: can't exec server \"%s\", mod_cgi isn't loaded",
483 if (apr_pool_create(&tp
, fcgi_config_pool
))
486 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
487 if (process
->terminationEvent
== NULL
)
490 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
492 termination_env_string
= ap_psprintf(tp
,
493 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
495 while (fs
->envp
[i
]) i
++;
496 fs
->envp
[i
++] = termination_env_string
;
497 fs
->envp
[i
] = (char *) fs
->mutex_env_string
;
499 ap_assert(fs
->envp
[i
+ 1] == NULL
);
503 SECURITY_ATTRIBUTES sa
= { 0 };
505 sa
.bInheritHandle
= TRUE
;
506 sa
.nLength
= sizeof(sa
);
508 listen_handle
= CreateNamedPipe(fs
->socket_path
,
510 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
511 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
513 if (listen_handle
== INVALID_HANDLE_VALUE
)
515 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
516 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
523 listen_handle
= (HANDLE
) fs
->listenFd
;
526 r
.per_dir_config
= fcgi_apache_main_server
->lookup_defaults
;
527 r
.server
= fcgi_apache_main_server
;
528 r
.filename
= (char *) fs
->fs_path
;
530 r
.subprocess_env
= apr_table_make(tp
, 0);
532 e_info
.cmd_type
= APR_PROGRAM
;
534 rv
= cgi_build_command(&command
, &argv
, &r
, tp
, &e_info
);
535 if (rv
!= APR_SUCCESS
)
537 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
538 "FastCGI: don't know how to spawn cmd child process: %s",
543 if (apr_procattr_create(&procattr
, tp
))
546 if (apr_procattr_dir_set(procattr
, ap_make_dirstr_parent(tp
, fs
->fs_path
)))
549 if (apr_procattr_cmdtype_set(procattr
, e_info
.cmd_type
))
552 if (apr_procattr_detach_set(procattr
, 1))
555 if (apr_os_file_put(&file
, &listen_handle
, 0, tp
))
558 /* procattr is opaque so we have to use this - unfortuantely it dups */
559 if (apr_procattr_child_in_set(procattr
, file
, NULL
))
562 if (apr_proc_create(&proc
, command
, argv
, fs
->envp
, procattr
, tp
))
565 process
->handle
= proc
.hproc
;
572 fs
->envp
[i
- 1] = NULL
;
579 #else /* WIN32 && !APACHE2 */
581 /* Adapted from Apache's util_script.c ap_call_exec() */
582 char *interpreter
= NULL
;
583 char *quoted_filename
;
585 char *pEnvBlock
, *pNext
;
588 int iEnvBlockLen
= 1;
590 file_type_e fileType
;
593 PROCESS_INFORMATION pi
;
598 pool
* tp
= ap_make_sub_pool(fcgi_config_pool
);
600 HANDLE listen_handle
;
601 char * termination_env_string
= NULL
;
603 process
->terminationEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
604 if (process
->terminationEvent
== NULL
)
606 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
607 "FastCGI: can't create termination event for server \"%s\", "
608 "CreateEvent() failed", fs
->fs_path
);
611 SetHandleInformation(process
->terminationEvent
, HANDLE_FLAG_INHERIT
, TRUE
);
613 termination_env_string
= ap_psprintf(tp
,
614 "_FCGI_SHUTDOWN_EVENT_=%ld", process
->terminationEvent
);
618 SECURITY_ATTRIBUTES sa
;
620 sa
.lpSecurityDescriptor
= NULL
;
621 sa
.bInheritHandle
= TRUE
;
622 sa
.nLength
= sizeof(sa
);
624 listen_handle
= CreateNamedPipe(fs
->socket_path
,
626 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
627 PIPE_UNLIMITED_INSTANCES
, 4096, 4096, 0, &sa
);
629 if (listen_handle
== INVALID_HANDLE_VALUE
)
631 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
632 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs
->fs_path
);
638 listen_handle
= (HANDLE
) fs
->listenFd
;
641 memset(&si
, 0, sizeof(si
));
642 memset(&pi
, 0, sizeof(pi
));
643 memset(&r
, 0, sizeof(r
));
645 // Can up a fake request to pass to ap_get_win32_interpreter()
646 r
.per_dir_config
= fcgi_apache_main_server
->lookup_defaults
;
647 r
.server
= fcgi_apache_main_server
;
648 r
.filename
= (char *) fs
->fs_path
;
651 fileType
= ap_get_win32_interpreter(&r
, &interpreter
);
653 if (fileType
== eFileTypeUNKNOWN
) {
654 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
655 "FastCGI: %s is not executable; ensure interpreted scripts have "
656 "\"#!\" as their first line",
663 * We have the interpreter (if there is one) and we have
664 * the arguments (if there are any).
665 * Build the command string to pass to CreateProcess.
667 quoted_filename
= ap_pstrcat(tp
, "\"", fs
->fs_path
, "\"", NULL
);
668 if (interpreter
&& *interpreter
) {
669 pCommand
= ap_pstrcat(tp
, interpreter
, " ", quoted_filename
, NULL
);
672 pCommand
= quoted_filename
;
676 * Make child process use hPipeOutputWrite as standard out,
677 * and make sure it does not show on screen.
680 si
.dwFlags
= STARTF_USESHOWWINDOW
| STARTF_USESTDHANDLES
;
681 si
.wShowWindow
= SW_HIDE
;
682 si
.hStdInput
= listen_handle
;
684 // XXX These should be open to the error_log
685 si
.hStdOutput
= INVALID_HANDLE_VALUE
;
686 si
.hStdError
= INVALID_HANDLE_VALUE
;
689 * Win32's CreateProcess call requires that the environment
690 * be passed in an environment block, a null terminated block of
691 * null terminated strings.
692 * @todo we should store the env in this format for win32.
696 iEnvBlockLen
+= strlen(fs
->envp
[i
]) + 1;
700 iEnvBlockLen
+= strlen(termination_env_string
) + 1;
701 iEnvBlockLen
+= strlen(fs
->mutex_env_string
) + 1;
703 pEnvBlock
= (char *) ap_pcalloc(tp
, iEnvBlockLen
);
709 strcpy(pNext
, fs
->envp
[i
]);
710 pNext
+= strlen(pNext
) + 1;
714 strcpy(pNext
, termination_env_string
);
715 pNext
+= strlen(pNext
) + 1;
716 strcpy(pNext
, fs
->mutex_env_string
);
718 if (CreateProcess(NULL
, pCommand
, NULL
, NULL
, TRUE
,
721 ap_make_dirstr_parent(tp
, fs
->fs_path
),
724 /* Hack to get 16-bit CGI's working. It works for all the
725 * standard modules shipped with Apache. pi.dwProcessId is 0
726 * for 16-bit CGIs and all the Unix specific code that calls
727 * ap_call_exec interprets this as a failure case. And we can't
728 * use -1 either because it is mapped to 0 by the caller.
730 pid
= (fileType
== eFileTypeEXE16
) ? -2 : pi
.dwProcessId
;
732 process
->handle
= pi
.hProcess
;
733 CloseHandle(pi
.hThread
);
738 CloseHandle(listen_handle
);
747 #endif /* !APACHE2 */
752 static void reduce_privileges(void)
760 /* Get username if passed as a uid */
761 if (ap_user_name
[0] == '#') {
762 uid_t uid
= atoi(&ap_user_name
[1]);
763 struct passwd
*ent
= getpwuid(uid
);
766 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
767 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
768 "you probably need to modify the User directive", (unsigned)uid
);
777 if (setgid(ap_group_id
) == -1) {
778 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
779 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id
);
783 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
785 /* Initialize supplementary groups */
786 if (initgroups(name
, ap_group_id
) == -1) {
787 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
788 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
789 name
, (unsigned)ap_group_id
);
796 if (seteuid_user() == -1) {
797 ap_log_error(FCGI_LOG_ALERT_NOERRNO
, fcgi_apache_main_server
,
798 "FastCGI: process manager exiting, failed to reduce privileges");
803 if (setuid(ap_user_id
) == -1) {
804 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
805 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id
);
812 * Change the name of this process - best we can easily.
814 static void change_process_name(const char * const name
)
816 /* under Apache2, ap_server_argv0 is const */
817 strncpy((char *) ap_server_argv0
, name
, strlen(ap_server_argv0
));
821 static void schedule_start(fcgi_server
*s
, int proc
)
823 /* If we've started one recently, don't register another */
824 time_t time_passed
= now
- s
->restartTime
;
826 if ((s
->procs
[proc
].pid
&& (time_passed
< (int) s
->restartDelay
))
827 || ((s
->procs
[proc
].pid
== 0) && (time_passed
< s
->initStartDelay
)))
829 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
);
833 FCGIDBG3("scheduling_start: %s (%d)", s
->fs_path
, proc
);
834 s
->procs
[proc
].state
= FCGI_START_STATE
;
835 if (proc
== dynamicMaxClassProcs
- 1) {
836 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
837 "FastCGI: scheduled the %sstart of the last (dynamic) server "
838 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
839 s
->procs
[proc
].pid
? "re" : "", s
->fs_path
, dynamicMaxClassProcs
);
844 *----------------------------------------------------------------------
848 * Removes the records written by request handlers and decodes them.
849 * We also update the data structures to reflect the changes.
851 *----------------------------------------------------------------------
854 static void dynamic_read_msgs(int read_ready
)
860 static int buflen
= 0;
861 static char buf
[FCGI_MSGS_BUFSIZE
+ 1];
862 char *ptr1
, *ptr2
, opcode
;
863 char execName
[FCGI_MAXPATH
+ 1];
864 char user
[MAX_USER_NAME_LEN
+ 2];
865 char group
[MAX_GID_CHAR_LEN
+ 1];
866 unsigned long q_usec
= 0UL, req_usec
= 0UL;
868 fcgi_pm_job
*joblist
= NULL
;
869 fcgi_pm_job
*cjob
= NULL
;
872 pool
*sp
= NULL
, *tp
;
875 user
[MAX_USER_NAME_LEN
+ 1] = group
[MAX_GID_CHAR_LEN
] = '\0';
879 * To prevent the idle application from running indefinitely, we
880 * check the timer and if it is expired, we recompute the values
881 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
882 * message is received, only updates are made to the data structures.
884 if (fcgi_dynamic_last_analyzed
== 0) {
885 fcgi_dynamic_last_analyzed
= now
;
887 if ((now
- fcgi_dynamic_last_analyzed
) >= (int)dynamicUpdateInterval
) {
888 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
889 if (s
->directive
!= APP_CLASS_DYNAMIC
)
892 /* Advance the last analyzed timestamp by the elapsed time since
893 * it was last set. Round the increase down to the nearest
894 * multiple of dynamicUpdateInterval */
896 fcgi_dynamic_last_analyzed
+= (((long)(now
-fcgi_dynamic_last_analyzed
)/dynamicUpdateInterval
)*dynamicUpdateInterval
);
897 s
->smoothConnTime
= (unsigned long) ((1.0-dynamicGain
)*s
->smoothConnTime
+ dynamicGain
*s
->totalConnTime
);
898 s
->totalConnTime
= 0UL;
899 s
->totalQueueTime
= 0UL;
903 if (read_ready
<= 0) {
908 rc
= read(fcgi_pm_pipe
[0], (void *)(buf
+ buflen
), FCGI_MSGS_BUFSIZE
- buflen
);
910 if (!caughtSigTerm
) {
911 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
912 "FastCGI: read() from pipe failed (%d)", rc
);
914 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
915 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
916 caughtSigTerm
= TRUE
;
926 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
927 * request to do something) and/or when a timeout expires.
928 * There really should be no reason why this wait would get stuck
929 * but there's no point in waiting forever. */
931 rc
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
933 if (rc
!= WAIT_OBJECT_0
&& rc
!= WAIT_ABANDONED
)
935 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
936 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
940 joblist
= fcgi_dynamic_mbox
;
941 fcgi_dynamic_mbox
= NULL
;
943 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
945 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
946 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
953 apr_pool_create(&tp
, fcgi_config_pool
);
955 tp
= ap_make_sub_pool(fcgi_config_pool
);
959 for (ptr1
= buf
; ptr1
; ptr1
= ptr2
) {
962 ptr2
= strchr(ptr1
, '*');
974 case FCGI_SERVER_START_JOB
:
975 case FCGI_SERVER_RESTART_JOB
:
977 if (sscanf(ptr1
, "%c %s %16s %15s",
978 &opcode
, execName
, user
, group
) != 4)
984 case FCGI_REQUEST_TIMEOUT_JOB
:
986 if (sscanf(ptr1
, "%c %s %16s %15s",
987 &opcode
, execName
, user
, group
) != 4)
993 case FCGI_REQUEST_COMPLETE_JOB
:
995 if (sscanf(ptr1
, "%c %s %16s %15s %lu %lu",
996 &opcode
, execName
, user
, group
, &q_usec
, &req_usec
) != 6)
1008 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode
, execName
, user
, group
, q_usec
, req_usec
);
1011 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1012 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1
);
1016 /* Update data structures for processing */
1017 while (cjob
!= NULL
) {
1018 joblist
= cjob
->next
;
1019 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob
->id
, cjob
->fs_path
, cjob
->user
, cjob
->group
, cjob
->qsec
, cjob
->start_time
);
1023 s
= fcgi_util_fs_get(execName
, user
, group
);
1025 s
= fcgi_util_fs_get(cjob
->fs_path
, cjob
->user
, cjob
->group
);
1029 if (s
==NULL
&& opcode
!= FCGI_REQUEST_COMPLETE_JOB
)
1031 if (s
==NULL
&& cjob
->id
!= FCGI_REQUEST_COMPLETE_JOB
)
1036 HANDLE mutex
= CreateMutex(NULL
, FALSE
, cjob
->fs_path
);
1040 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1041 "FastCGI: can't create accept mutex "
1042 "for (dynamic) server \"%s\"", cjob
->fs_path
);
1046 SetHandleInformation(mutex
, HANDLE_FLAG_INHERIT
, TRUE
);
1051 /* Create a perm subpool to hold the new server data,
1052 * we can destroy it if something doesn't pan out */
1054 apr_pool_create(&sp
, fcgi_config_pool
);
1056 sp
= ap_make_sub_pool(fcgi_config_pool
);
1059 /* Create a new "dynamic" server */
1060 s
= fcgi_util_fs_new(sp
);
1062 s
->directive
= APP_CLASS_DYNAMIC
;
1063 s
->restartDelay
= dynamicRestartDelay
;
1064 s
->listenQueueDepth
= dynamicListenQueueDepth
;
1065 s
->initStartDelay
= dynamicInitStartDelay
;
1066 s
->envp
= dynamicEnvp
;
1067 s
->flush
= dynamicFlush
;
1070 s
->mutex_env_string
= ap_psprintf(sp
, "_FCGI_MUTEX_=%ld", mutex
);
1071 s
->fs_path
= ap_pstrdup(sp
, cjob
->fs_path
);
1073 s
->fs_path
= ap_pstrdup(sp
, execName
);
1075 ap_getparents(s
->fs_path
);
1076 ap_no2slash(s
->fs_path
);
1077 s
->procs
= fcgi_util_fs_create_procs(sp
, dynamicMaxClassProcs
);
1079 /* XXX the socket_path (both Unix and Win) *is* deducible and
1080 * thus can and will be used by other apache instances without
1081 * the use of shared data regarding the processes serving the
1082 * requests. This can result in slightly unintuitive process
1083 * counts and security implications. This is prevented
1084 * if suexec (Unix) is in use. This is both a feature and a flaw.
1085 * Changing it now would break existing installations. */
1088 /* Create socket file's path */
1089 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, execName
, user
, group
);
1090 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1092 /* Create sockaddr, prealloc it so it won't get created in tp */
1093 s
->socket_addr
= ap_pcalloc(sp
, sizeof(struct sockaddr_un
));
1094 err
= fcgi_util_socket_make_domain_addr(tp
, (struct sockaddr_un
**)&s
->socket_addr
,
1095 &s
->socket_addr_len
, s
->socket_path
);
1097 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1098 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
1102 if (init_listen_sock(s
)) {
1106 /* If a wrapper is being used, config user/group info */
1108 if (user
[0] == '~') {
1109 /* its a user dir uri, the rest is a username, not a uid */
1110 struct passwd
*pw
= getpwnam(&user
[1]);
1113 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1114 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1115 execName
, &user
[1]);
1118 s
->uid
= pw
->pw_uid
;
1119 s
->user
= ap_pstrdup(sp
, user
);
1120 s
->username
= s
->user
;
1122 s
->gid
= pw
->pw_gid
;
1123 s
->group
= ap_psprintf(sp
, "%ld", (long)s
->gid
);
1128 s
->uid
= (uid_t
)atol(user
);
1129 pw
= getpwuid(s
->uid
);
1131 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1132 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1133 execName
, (long)s
->uid
);
1136 s
->user
= ap_pstrdup(sp
, user
);
1137 s
->username
= ap_pstrdup(sp
, pw
->pw_name
);
1139 s
->gid
= (gid_t
)atol(group
);
1140 s
->group
= ap_pstrdup(sp
, group
);
1144 /* Create socket file's path */
1145 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, cjob
->fs_path
, cjob
->user
, cjob
->group
);
1146 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
1150 fcgi_util_fs_add(s
);
1154 if (opcode
== FCGI_SERVER_RESTART_JOB
) {
1156 if (cjob
->id
==FCGI_SERVER_RESTART_JOB
) {
1158 /* Check to see if the binary has changed. If so,
1159 * kill the FCGI application processes, and
1165 char * app_path
= cjob
->fs_path
;
1167 char * app_path
= execName
;
1170 if (stat(app_path
, &stbuf
) == 0 && stbuf
.st_mtime
> s
->startTime
)
1174 /* prevent addition restart requests */
1177 utime(s
->socket_path
, NULL
);
1180 /* kill old server(s) */
1181 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1183 if (s
->procs
[i
].pid
> 0
1184 && stbuf
.st_mtime
> s
->procs
[i
].start_time
)
1186 fcgi_kill(&s
->procs
[i
], SIGTERM
);
1193 ap_log_error(FCGI_LOG_WARN_NOERRNO
,
1194 fcgi_apache_main_server
, "FastCGI: restarting "
1195 "old server \"%s\" processes, newer version "
1200 /* If dynamicAutoRestart, don't mark any new processes
1201 * for starting because we probably got the
1202 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1203 * will be restarting all of those we just killed.
1205 if (dynamicAutoRestart
)
1209 else if (opcode
== FCGI_SERVER_START_JOB
) {
1211 else if (cjob
->id
==FCGI_SERVER_START_JOB
) {
1213 /* we've been asked to start a process--only start
1214 * it if we're not already running at least one
1219 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1220 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1223 /* if already running, don't start another one */
1224 if (i
< dynamicMaxClassProcs
) {
1238 case FCGI_SERVER_RESTART_JOB
:
1242 /* We just waxed 'em all. Try to find an idle slot. */
1244 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1246 if (s
->procs
[i
].state
== FCGI_START_STATE
1247 || s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1251 else if (s
->procs
[i
].state
== FCGI_KILLED_STATE
1252 || s
->procs
[i
].state
== FCGI_READY_STATE
)
1259 /* Nope, just use the first slot */
1260 if (i
== dynamicMaxClassProcs
)
1268 schedule_start(s
, i
);
1273 case FCGI_SERVER_START_JOB
:
1274 case FCGI_REQUEST_TIMEOUT_JOB
:
1276 if ((fcgi_dynamic_total_proc_count
+ 1) > (int) dynamicMaxProcs
) {
1278 * Extra instances should have been
1279 * terminated beforehand, probably need
1280 * to increase ProcessSlack parameter
1282 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1283 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1284 "exceeded dynamicMaxProcs (%d)", s
->fs_path
, dynamicMaxProcs
);
1288 /* find next free slot */
1289 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1291 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1293 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i
);
1296 else if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
)
1301 schedule_start(s
, i
);
1306 if (i
>= dynamicMaxClassProcs
) {
1307 FCGIDBG1("ignore_job: slots are max'd");
1311 case FCGI_REQUEST_COMPLETE_JOB
:
1312 /* only record stats if we have a structure */
1315 s
->totalConnTime
+= req_usec
;
1316 s
->totalQueueTime
+= q_usec
;
1318 s
->totalConnTime
+= cjob
->start_time
;
1319 s
->totalQueueTime
+= cjob
->qsec
;
1328 /* Cleanup job data */
1329 free(cjob
->fs_path
);
1339 if (sp
) ap_destroy_pool(sp
);
1342 free(cjob
->fs_path
);
1350 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1351 "FastCGI: really bogus message: \"%s\"", ptr1
);
1352 ptr1
+= strlen(buf
);
1355 buflen
-= ptr1
- buf
;
1357 memmove(buf
, ptr1
, buflen
);
1361 ap_destroy_pool(tp
);
1365 *----------------------------------------------------------------------
1367 * dynamic_kill_idle_fs_procs
1369 * Implement a kill policy for the dynamic FastCGI applications.
1370 * We also update the data structures to reflect the changes.
1373 * Processes are marked for deletion possibly killed.
1375 *----------------------------------------------------------------------
1377 static void dynamic_kill_idle_fs_procs(void)
1382 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1385 * server's smoothed running time, or if that's 0, the current total
1387 unsigned long connTime
;
1390 * maximum number of microseconds that all of a server's running
1391 * processes together could have spent running since the last check
1393 unsigned long totalTime
;
1396 * percentage, 0-100, of totalTime that the processes actually used
1401 int really_running
= 0;
1403 if (s
->directive
!= APP_CLASS_DYNAMIC
|| s
->numProcesses
== 0)
1408 /* s->numProcesses includes pending kills so get the "active" count */
1409 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1411 if (s
->procs
[i
].state
== FCGI_RUNNING_STATE
) ++really_running
;
1414 connTime
= s
->smoothConnTime
? s
->smoothConnTime
: s
->totalConnTime
;
1415 totalTime
= really_running
* (now
- fcgi_dynamic_epoch
) * 1000000 + 1;
1417 loadFactor
= 100 * connTime
/ totalTime
;
1419 if (really_running
== 1)
1421 if (loadFactor
>= dynamicThreshold1
)
1428 int load
= really_running
/ ( really_running
- 1) * loadFactor
;
1430 if (load
>= dynamicThresholdN
)
1437 * Run through the procs to see if we can get away w/o waxing one.
1439 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1441 if (s
->procs
[i
].state
== FCGI_START_STATE
)
1443 s
->procs
[i
].state
= FCGI_READY_STATE
;
1446 else if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
)
1452 if (i
>= dynamicMaxClassProcs
)
1454 ServerProcess
* procs
= s
->procs
;
1457 for (i
= 0; i
< dynamicMaxClassProcs
; ++i
)
1459 if (procs
[i
].state
== FCGI_RUNNING_STATE
)
1461 if (youngest
== -1 || procs
[i
].start_time
>= procs
[youngest
].start_time
)
1470 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1471 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1472 s
->fs_path
, (long) s
->procs
[youngest
].pid
);
1474 fcgi_kill(&s
->procs
[youngest
], SIGTERM
);
1480 * If the number of non-victims is less than or equal to
1481 * the minimum that may be running without being killed off,
1482 * don't select any more victims.
1484 if (fcgi_dynamic_total_proc_count
- victims
<= dynamicMinProcs
)
1494 // This is a little bogus, there's gotta be a better way to do this
1495 // Can we use WaitForMultipleObjects()
1496 #define FCGI_PROC_WAIT_TIME 100
1498 void child_wait_thread_main(void *dummy
) {
1500 DWORD dwRet
= WAIT_TIMEOUT
;
1505 while (!bTimeToDie
) {
1508 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1509 if (s
->directive
== APP_CLASS_EXTERNAL
|| s
->listenFd
< 0) {
1512 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1513 numChildren
= dynamicMaxClassProcs
;
1516 numChildren
= s
->numProcesses
;
1519 for (i
=0; i
< numChildren
; i
++) {
1520 if (s
->procs
[i
].handle
!= INVALID_HANDLE_VALUE
)
1522 DWORD exitStatus
= 0;
1524 /* timeout is currently set for 100 miliecond */
1525 /* it may need to be longer or user customizable */
1526 dwRet
= WaitForSingleObject(s
->procs
[i
].handle
, FCGI_PROC_WAIT_TIME
);
1530 if (dwRet
!= WAIT_TIMEOUT
&& dwRet
!= WAIT_FAILED
) {
1531 /* a child fs has died */
1532 /* mark the child as dead */
1534 if (s
->directive
== APP_CLASS_STANDARD
) {
1535 /* restart static app */
1536 s
->procs
[i
].state
= FCGI_START_STATE
;
1541 fcgi_dynamic_total_proc_count
--;
1542 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1544 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1545 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1548 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1551 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0)) {
1552 s
->procs
[i
].state
= FCGI_START_STATE
;
1555 s
->procs
[i
].state
= FCGI_READY_STATE
;
1560 GetExitCodeProcess(s
->procs
[i
].handle
, &exitStatus
);
1562 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1563 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1564 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1565 s
->fs_path
, (long) s
->procs
[i
].pid
, exitStatus
);
1567 CloseHandle(s
->procs
[i
].handle
);
1568 s
->procs
[i
].handle
= INVALID_HANDLE_VALUE
;
1569 s
->procs
[i
].pid
= -1;
1571 /* wake up the main thread */
1572 SetEvent(fcgi_event_handles
[WAKE_EVENT
]);
1577 Sleep(waited
? 0 : FCGI_PROC_WAIT_TIME
);
1583 static void setup_signals(void)
1585 struct sigaction sa
;
1587 /* Setup handlers */
1589 sa
.sa_handler
= signal_handler
;
1590 sigemptyset(&sa
.sa_mask
);
1593 if (sigaction(SIGTERM
, &sa
, NULL
) < 0) {
1594 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1595 "sigaction(SIGTERM) failed");
1598 if (sigaction(SIGHUP
, &sa
, NULL
) < 0) {
1599 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1600 "sigaction(SIGHUP) failed");
1602 /* httpd graceful restart */
1603 if (sigaction(SIGUSR1
, &sa
, NULL
) < 0) {
1604 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1605 "sigaction(SIGUSR1) failed");
1607 /* read messages from request handlers - kill interval expired */
1608 if (sigaction(SIGALRM
, &sa
, NULL
) < 0) {
1609 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1610 "sigaction(SIGALRM) failed");
1612 if (sigaction(SIGCHLD
, &sa
, NULL
) < 0) {
1613 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1614 "sigaction(SIGCHLD) failed");
1619 #if !defined(WIN32) && !defined(APACHE2)
1620 int fcgi_pm_main(void *dummy
, child_info
*info
)
1622 void fcgi_pm_main(void *dummy
)
1632 HANDLE child_wait_thread
= INVALID_HANDLE_VALUE
;
1634 int callWaitPid
, callDynamicProcs
;
1638 // Add SystemRoot to the dynamic environment
1639 char ** envp
= dynamicEnvp
;
1640 for (i
= 0; *envp
; ++i
) {
1643 fcgi_config_set_env_var(fcgi_config_pool
, dynamicEnvp
, &i
, "SystemRoot");
1647 reduce_privileges();
1648 change_process_name("fcgi-pm");
1650 close(fcgi_pm_pipe
[1]);
1654 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1655 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper
);
1659 /* Initialize AppClass */
1660 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1662 if (s
->directive
!= APP_CLASS_STANDARD
)
1670 for (i
= 0; i
< s
->numProcesses
; ++i
)
1671 s
->procs
[i
].state
= FCGI_START_STATE
;
1675 child_wait_thread
= (HANDLE
) _beginthread(child_wait_thread_main
, 0, NULL
);
1677 if (child_wait_thread
== (HANDLE
) -1)
1679 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1680 "FastCGI: failed to create process manager's wait thread!");
1683 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1684 "FastCGI: process manager initialized");
1686 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1687 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1693 * Loop until SIGTERM
1696 int sleepSeconds
= min(dynamicKillInterval
, dynamicUpdateInterval
);
1703 unsigned int numChildren
;
1706 * If we came out of sigsuspend() for any reason other than
1707 * SIGALRM, pick up where we left off.
1710 sleepSeconds
= alarmLeft
;
1713 * Examine each configured AppClass for a process that needs
1714 * starting. Compute the earliest time when the start should
1715 * be attempted, starting it now if the time has passed. Also,
1716 * remember that we do NOT need to restart externally managed
1717 * FastCGI applications.
1719 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1721 if (s
->directive
== APP_CLASS_EXTERNAL
)
1724 numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
1725 ? dynamicMaxClassProcs
1728 for (i
= 0; i
< numChildren
; ++i
)
1730 if (s
->procs
[i
].pid
<= 0 && s
->procs
[i
].state
== FCGI_START_STATE
)
1732 int restart
= (s
->procs
[i
].pid
< 0);
1733 time_t restartTime
= s
->restartTime
;
1737 /* we've gone to using the badDelay, the only thing that
1738 resets bad is when badDelay has expired. but numFailures
1739 is only just set below its threshold. the proc's
1740 start_times are all reset when the bad is. the numFailures
1741 is reset when we see an app run for a period */
1743 s
->procs
[i
].start_time
= 0;
1746 if (s
->numFailures
> MAX_FAILED_STARTS
)
1748 time_t last_start_time
= s
->procs
[i
].start_time
;
1750 if (last_start_time
&& now
- last_start_time
> RUNTIME_SUCCESS_INTERVAL
)
1754 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1755 "FastCGI:%s server \"%s\" has remained"
1756 " running for more than %d seconds, its restart"
1757 " interval has been restored to %d seconds",
1758 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1759 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, s
->restartDelay
);
1765 for (j
= 0; j
< numChildren
; ++j
)
1767 if (s
->procs
[j
].pid
<= 0) continue;
1768 if (s
->procs
[j
].state
!= FCGI_RUNNING_STATE
) continue;
1769 if (s
->procs
[j
].start_time
== 0) continue;
1770 if (now
- s
->procs
[j
].start_time
> RUNTIME_SUCCESS_INTERVAL
) break;
1773 if (j
>= numChildren
)
1776 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1777 "FastCGI:%s server \"%s\" has failed to remain"
1778 " running for %d seconds given %d attempts, its restart"
1779 " interval has been backed off to %d seconds",
1780 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1781 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, MAX_FAILED_STARTS
,
1782 FAILED_STARTS_DELAY
);
1788 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1789 "FastCGI:%s server \"%s\" has remained"
1790 " running for more than %d seconds, its restart"
1791 " interval has been restored to %d seconds",
1792 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1793 s
->fs_path
, RUNTIME_SUCCESS_INTERVAL
, s
->restartDelay
);
1800 restartTime
+= FAILED_STARTS_DELAY
;
1804 restartTime
+= (restart
) ? s
->restartDelay
: s
->initStartDelay
;
1807 if (restartTime
<= now
)
1812 s
->numFailures
= MAX_FAILED_STARTS
;
1815 if (s
->listenFd
< 0 && init_listen_sock(s
))
1817 if (sleepSeconds
> s
->initStartDelay
)
1818 sleepSeconds
= s
->initStartDelay
;
1822 if (caughtSigTerm
) {
1823 goto ProcessSigTerm
;
1826 s
->procs
[i
].pid
= spawn_fs_process(s
, &s
->procs
[i
]);
1827 if (s
->procs
[i
].pid
<= 0) {
1828 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1829 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1830 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1833 sleepSeconds
= min(sleepSeconds
,
1834 max((int) s
->restartDelay
, FCGI_MIN_EXEC_RETRY_DELAY
));
1836 s
->procs
[i
].pid
= -1;
1840 s
->procs
[i
].start_time
= now
;
1841 s
->restartTime
= now
;
1843 if (s
->startTime
== 0) {
1847 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1849 fcgi_dynamic_total_proc_count
++;
1850 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1853 s
->procs
[i
].state
= FCGI_RUNNING_STATE
;
1856 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1857 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1858 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1859 s
->fs_path
, (long) s
->uid
, (long) s
->gid
,
1860 restart
? "re" : "", (long) s
->procs
[i
].pid
);
1863 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1864 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1865 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1866 s
->fs_path
, restart
? "re" : "", (long) s
->procs
[i
].pid
);
1868 ap_assert(s
->procs
[i
].pid
> 0);
1870 sleepSeconds
= min(sleepSeconds
, restartTime
- now
);
1879 goto ProcessSigTerm
;
1881 if((!caughtSigChld
) && (!caughtSigAlarm
)) {
1884 alarm(sleepSeconds
);
1887 FD_SET(fcgi_pm_pipe
[0], &rfds
);
1888 read_ready
= ap_select(fcgi_pm_pipe
[0] + 1, &rfds
, NULL
, NULL
, NULL
);
1890 alarmLeft
= alarm(0);
1892 callWaitPid
= caughtSigChld
;
1893 caughtSigChld
= FALSE
;
1894 callDynamicProcs
= caughtSigAlarm
;
1895 caughtSigAlarm
= FALSE
;
1900 * Dynamic fcgi process management
1902 if((callDynamicProcs
) || (!callWaitPid
)) {
1903 dynamic_read_msgs(read_ready
);
1904 if(fcgi_dynamic_epoch
== 0) {
1905 fcgi_dynamic_epoch
= now
;
1907 if(((long)(now
-fcgi_dynamic_epoch
)>=dynamicKillInterval
) ||
1908 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
)>=dynamicMaxProcs
)) {
1909 dynamic_kill_idle_fs_procs();
1910 fcgi_dynamic_epoch
= now
;
1918 /* We've caught SIGCHLD, so find out who it was using waitpid,
1919 * write a log message and update its data structure. */
1923 goto ProcessSigTerm
;
1925 childPid
= waitpid(-1, &waitStatus
, WNOHANG
);
1927 if (childPid
== -1 || childPid
== 0)
1930 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1931 if (s
->directive
== APP_CLASS_EXTERNAL
)
1934 if (s
->directive
== APP_CLASS_DYNAMIC
)
1935 numChildren
= dynamicMaxClassProcs
;
1937 numChildren
= s
->numProcesses
;
1939 for (i
= 0; i
< numChildren
; i
++) {
1940 if (s
->procs
[i
].pid
== childPid
)
1945 /* TODO: print something about this unknown child */
1949 s
->procs
[i
].pid
= -1;
1951 if (s
->directive
== APP_CLASS_STANDARD
) {
1952 /* Always restart static apps */
1953 s
->procs
[i
].state
= FCGI_START_STATE
;
1958 fcgi_dynamic_total_proc_count
--;
1960 if (s
->procs
[i
].state
== FCGI_VICTIM_STATE
) {
1961 s
->procs
[i
].state
= FCGI_KILLED_STATE
;
1964 /* A dynamic app died or exited without provocation from the PM */
1967 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0))
1968 s
->procs
[i
].state
= FCGI_START_STATE
;
1970 s
->procs
[i
].state
= FCGI_READY_STATE
;
1974 if (WIFEXITED(waitStatus
)) {
1975 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1976 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1977 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1978 s
->fs_path
, (long) childPid
, WEXITSTATUS(waitStatus
));
1980 else if (WIFSIGNALED(waitStatus
)) {
1981 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1982 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1983 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1984 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
),
1986 WCOREDUMP(waitStatus
) ? ", a core file may have been generated" : "");
1991 else if (WIFSTOPPED(waitStatus
)) {
1992 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1993 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1994 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1995 s
->fs_path
, (long) childPid
, WTERMSIG(waitStatus
), get_signal_text(waitStatus
));
1997 } /* for (;;), waitpid() */
2001 /* wait for an event to occur or timer expires */
2002 expire
= time(NULL
) + sleepSeconds
;
2003 dwRet
= WaitForMultipleObjects(3, (HANDLE
*) fcgi_event_handles
, FALSE
, sleepSeconds
* 1000);
2005 if (dwRet
== WAIT_FAILED
) {
2006 /* There is something seriously wrong here */
2007 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
2008 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
2012 if (dwRet
!= WAIT_TIMEOUT
) {
2016 alarmLeft
= expire
- now
;
2020 * Dynamic fcgi process management
2022 if ((dwRet
== MBOX_EVENT
) || (dwRet
== WAIT_TIMEOUT
)) {
2023 if (dwRet
== MBOX_EVENT
) {
2029 dynamic_read_msgs(read_ready
);
2031 if(fcgi_dynamic_epoch
== 0) {
2032 fcgi_dynamic_epoch
= now
;
2035 if ((now
-fcgi_dynamic_epoch
>= (int) dynamicKillInterval
) ||
2036 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
) >= dynamicMaxProcs
)) {
2037 dynamic_kill_idle_fs_procs();
2038 fcgi_dynamic_epoch
= now
;
2042 else if (dwRet
== WAKE_EVENT
) {
2045 else if (dwRet
== TERM_EVENT
) {
2046 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
2047 "FastCGI: Termination event received process manager shutting down");
2050 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
2052 goto ProcessSigTerm
;
2055 // Have an received an unknown event - should not happen
2056 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
2057 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
2060 dwRet
= WaitForSingleObject(child_wait_thread
, INFINITE
);
2062 goto ProcessSigTerm
;
2067 } /* for (;;), the whole shoot'n match */
2071 * Kill off the children, then exit.
2083 int fcgi_pm_add_job(fcgi_pm_job
*new_job
)
2085 int rv
= WaitForSingleObject(fcgi_dynamic_mbox_mutex
, FCGI_MBOX_MUTEX_TIMEOUT
);
2087 if (rv
!= WAIT_OBJECT_0
&& rv
!= WAIT_ABANDONED
)
2089 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2090 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2094 new_job
->next
= fcgi_dynamic_mbox
;
2095 fcgi_dynamic_mbox
= new_job
;
2097 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex
))
2099 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
2100 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");