2 * $Id: fcgi_pm.c,v 1.51 2001/03/05 18:16:48 robs Exp $
10 #define seteuid(arg) setresuid(-1, (arg), -1)
13 int fcgi_dynamic_total_proc_count
= 0; /* number of running apps */
14 time_t fcgi_dynamic_epoch
= 0; /* last time kill_procs was
15 * invoked by process mgr */
16 time_t fcgi_dynamic_last_analyzed
= 0; /* last time calculation was
17 * made for the dynamic procs */
19 static time_t now
= 0;
21 /* Information about a process we are doing a blocking kill of. */
24 const char *lockFileName
; /* name of the lock file to lock */
26 FcgiRWLock
*lock
; /* reader/writer lock for dynamic app */
28 ServerProcess
*process
; /* process to issue SIGTERM to */
32 static BOOL bTimeToDie
= FALSE
; /* process termination flag */
33 HANDLE fcgi_event_handles
[3];
38 static int seteuid_root(void)
40 int rc
= seteuid((uid_t
)0);
42 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
43 "FastCGI: seteuid(0) failed");
48 static int seteuid_user(void)
50 int rc
= seteuid(ap_user_id
);
52 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
53 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id
);
59 static int fcgi_kill(ServerProcess
*process
, int sig
)
62 FCGIDBG2("fcgi_kill(%ld)", process
->pid
);
69 rc
= kill(process
->pid
, sig
);
76 rc
= TerminateProcess(process
->handle
, sig
);
82 /*******************************************************************************
83 * Send SIGTERM to each process in the server class, remove socket and lock
84 * file if appropriate. Currently this is only called when the PM is shutting
85 * down and thus memory isn't freed and sockets and files aren't closed.
87 static void kill_fs_procs(pool
*p
, fcgi_server
*s
)
89 ServerProcess
*proc
= s
->procs
;
92 if (s
->directive
== APP_CLASS_DYNAMIC
)
93 numChildren
= dynamicMaxClassProcs
;
95 numChildren
= s
->numProcesses
;
97 for (i
= 0; i
< numChildren
; i
++, proc
++) {
100 fcgi_kill(proc
, SIGTERM
);
104 if (proc
->handle
!= INVALID_HANDLE_VALUE
) {
106 CloseHandle(proc
->handle
);
107 proc
->handle
= INVALID_HANDLE_VALUE
;
113 /* Remove the dead lock file */
114 if (s
->directive
== APP_CLASS_DYNAMIC
) {
116 const char *lockFileName
= fcgi_util_socket_get_lock_filename(p
, s
->socket_path
);
118 if (unlink(lockFileName
) != 0) {
119 ap_log_error(FCGI_LOG_ERR
, fcgi_apache_main_server
,
120 "FastCGI: unlink() failed to remove lock file \"%s\" for (dynamic) server \"%s\"",
121 lockFileName
, s
->fs_path
);
124 fcgi_rdwr_destroy(s
->dynamic_lock
);
128 /* Remove the socket file */
129 if (s
->socket_path
!= NULL
&& s
->directive
!= APP_CLASS_EXTERNAL
) {
131 if (unlink(s
->socket_path
) != 0) {
132 ap_log_error(FCGI_LOG_ERR
, fcgi_apache_main_server
,
133 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
135 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "", s
->fs_path
);
138 CloseHandle((HANDLE
)s
->listenFd
);
141 fcgi_servers
= s
->next
;
144 static int init_listen_sock(fcgi_server
* fs
)
146 ap_assert(fs
->directive
!= APP_CLASS_EXTERNAL
);
148 /* Create the socket */
149 if ((fs
->listenFd
= ap_psocket(fcgi_config_pool
, fs
->socket_addr
->sa_family
, SOCK_STREAM
, 0)) < 0)
152 errno
= WSAGetLastError(); // Not sure if this will work as expected
154 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
155 "FastCGI: can't create %sserver \"%s\": socket() failed",
156 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
162 if (fs
->socket_addr
->sa_family
== AF_UNIX
)
164 /* Remove any existing socket file.. just in case */
165 unlink(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
);
171 setsockopt(fs
->listenFd
, SOL_SOCKET
, SO_REUSEADDR
, (char *)&flag
, sizeof(flag
));
174 /* Bind it to the socket_addr */
175 if (bind(fs
->listenFd
, fs
->socket_addr
, fs
->socket_addr_len
))
180 errno
= WSAGetLastError();
182 ap_snprintf(port
, sizeof(port
), "port=%d",
183 ((struct sockaddr_in
*)fs
->socket_addr
)->sin_port
);
185 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
186 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
187 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
190 (fs
->socket_addr
->sa_family
== AF_UNIX
) ?
191 ((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
:
197 /* Twiddle Unix socket permissions */
198 else if (fs
->socket_addr
->sa_family
== AF_UNIX
199 && chmod(((struct sockaddr_un
*)fs
->socket_addr
)->sun_path
, S_IRUSR
| S_IWUSR
))
201 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
202 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
203 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
209 else if (listen(fs
->listenFd
, fs
->listenQueueDepth
))
212 errno
= WSAGetLastError();
214 ap_log_error(FCGI_LOG_CRIT_ERRNO
, fcgi_apache_main_server
,
215 "FastCGI: can't create %sserver \"%s\": listen() failed",
216 (fs
->directive
== APP_CLASS_DYNAMIC
) ? "(dynamic) " : "",
224 ap_pclosesocket(fcgi_config_pool
, fs
->listenFd
);
230 *----------------------------------------------------------------------
232 * dynamic_blocking_kill
234 * Block on the lock file until it is available, and then
235 * issue a kill signal to the corresponding application.
236 * Since this function is executed in the child process,
237 * _exit() is called upon completion.
240 * Pointer to the data structure containing a process id to
241 * issue a signal to and the full pathname to the lockfile
242 * that needs to be locked before the issue of the signal.
245 * Memory is allocated by the caller, but is freed by this
248 *----------------------------------------------------------------------
250 static void dynamic_blocking_kill(void *data
)
252 struct FuncData
*funcData
= (struct FuncData
*)data
;
257 ap_assert(funcData
->lockFileName
);
258 if ((lockFd
= open(funcData
->lockFileName
, O_RDWR
)) < 0) {
259 /* There is something terribly wrong here */
261 if (fcgi_wait_for_shared_write_lock(lockFd
) < 0) {
262 /* This is a major problem */
264 fcgi_kill(funcData
->process
, SIGTERM
);
267 /* exit() may flush stdio buffers inherited from the parent. */
271 FCGIDBG1("dynamic_blocking_kill()");
272 if (fcgi_wait_for_shared_write_lock(funcData
->lock
) < 0) {
273 // This is a major problem
274 FCGIDBG1("fcgi_wait_for_shared_write_lock() failed >> MAJOR PROBLEM");
277 fcgi_kill(funcData
->process
, 1);
278 fcgi_rdwr_unlock(funcData
->lock
, WRITER
);
286 *----------------------------------------------------------------------
290 * The FastCGI process manager, which runs as a separate
291 * process responsible for:
292 * - Starting all the FastCGI proceses.
293 * - Restarting any of these processes that die (indicated
295 * - Catching SIGTERM and relaying it to all the FastCGI
296 * processes before exiting.
299 * Uses global variable fcgi_servers.
307 *----------------------------------------------------------------------
310 static int caughtSigTerm
= FALSE
;
311 static int caughtSigChld
= FALSE
;
312 static int caughtSigUsr2
= FALSE
;
314 static void signal_handler(int signo
)
316 if ((signo
== SIGTERM
) || (signo
== SIGUSR1
) || (signo
== SIGHUP
)) {
317 /* SIGUSR1 & SIGHUP are sent by apache to its process group
318 * when apache get 'em. Apache follows up (1.2.x) with attacks
319 * on each of its child processes, but we've got the KillMgr
320 * sitting between us so we never see the KILL. The main loop
321 * in ProcMgr also checks to see if the KillMgr has terminated,
322 * and if it has, we handl it as if we should shutdown too. */
323 caughtSigTerm
= TRUE
;
324 } else if(signo
== SIGCHLD
) {
325 caughtSigChld
= TRUE
;
326 } else if(signo
== SIGALRM
) {
327 caughtSigUsr2
= TRUE
;
333 *----------------------------------------------------------------------
335 * spawn_fs_process --
337 * Fork and exec the specified fcgi process.
340 * 0 for successful fork, -1 for failed fork.
342 * In case the child fails before or in the exec, the child
343 * obtains the error log by calling getErrLog, logs
344 * the error, and exits with exit status = errno of
345 * the failed system call.
348 * Child process created.
350 *----------------------------------------------------------------------
353 static pid_t
spawn_fs_process(fcgi_server
*fs
, ServerProcess
*process
)
359 char *dnEnd
, *failedSysCall
;
366 /* We're the child. We're gonna exec() so pools don't matter. */
368 dnEnd
= strrchr(fs
->fs_path
, '/');
372 dirName
= ap_pcalloc(fcgi_config_pool
, dnEnd
- fs
->fs_path
+ 1);
373 dirName
= memcpy(dirName
, fs
->fs_path
, dnEnd
- fs
->fs_path
);
375 if (chdir(dirName
) < 0) {
376 failedSysCall
= "chdir()";
377 goto FailedSystemCallExit
;
381 /* OS/2 dosen't support nice() */
382 if (fs
->processPriority
!= 0) {
383 if (nice(fs
->processPriority
) == -1) {
384 failedSysCall
= "nice()";
385 goto FailedSystemCallExit
;
390 /* Open the listenFd on spec'd fd */
391 if (fs
->listenFd
!= FCGI_LISTENSOCK_FILENO
)
392 dup2(fs
->listenFd
, FCGI_LISTENSOCK_FILENO
);
394 /* Close all other open fds, except stdout/stderr. Leave these two open so
395 * FastCGI applications don't have to find and fix ALL 3rd party libs that
396 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
397 * main server error_log - @@@ provide a directive control where this goes.
399 ap_error_log2stderr(fcgi_apache_main_server
);
400 dup2(STDERR_FILENO
, STDOUT_FILENO
);
401 for (i
= 0; i
< FCGI_MAX_FD
; i
++) {
402 if (i
!= FCGI_LISTENSOCK_FILENO
&& i
!= STDERR_FILENO
&& i
!= STDOUT_FILENO
) {
407 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
408 * install its own handler. */
409 signal(SIGPIPE
, SIG_IGN
);
411 if (fcgi_wrapper
&& (fcgi_user_id
!= fs
->uid
|| fcgi_group_id
!= fs
->gid
)) {
412 char *shortName
= strrchr(fs
->fs_path
, '/') + 1;
414 /* Relinquish our root real uid powers */
419 execle(fcgi_wrapper
, fcgi_wrapper
, fs
->username
, fs
->group
, shortName
, NULL
, fs
->envp
);
420 } while (errno
== EINTR
);
424 execle(fs
->fs_path
, fs
->fs_path
, NULL
, fs
->envp
);
425 } while (errno
== EINTR
);
428 failedSysCall
= "execle()";
430 FailedSystemCallExit
:
431 fprintf(stderr
, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
432 fs
->fs_path
, (long) getpid(), failedSysCall
, strerror(errno
));
435 /* avoid an irrelevant compiler warning */
440 /* Adapted from Apache's util_script.c ap_call_exec() */
441 char *interpreter
= NULL
;
443 char *exename
= NULL
;
445 char *quoted_filename
;
447 char *pEnvBlock
, *pNext
;
452 file_type_e fileType
;
455 PROCESS_INFORMATION pi
;
460 HANDLE listen_handle
, mutex
;
461 char * mutex_string
= NULL
;
463 pool
* tp
= ap_make_sub_pool(fcgi_config_pool
);
467 SECURITY_ATTRIBUTES sa
;
469 sa
.nLength
= sizeof(sa
);
470 sa
.lpSecurityDescriptor
= NULL
;
471 sa
.bInheritHandle
= TRUE
;
473 listen_handle
= CreateNamedPipe(fs
->socket_path
, PIPE_ACCESS_DUPLEX
,
474 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
475 PIPE_UNLIMITED_INSTANCES
, 4096,4096,0, &sa
);
476 if (listen_handle
== INVALID_HANDLE_VALUE
)
478 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
479 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs
->fs_path
);
483 // This mutex is not really necessary, but for compatibility with
484 // the existing library, we need it.
486 mutex
= ap_create_mutex(NULL
);
489 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
490 "FastCGI: can't exec server \"%s\", ap_create_mutex() failed", fs
->fs_path
);
491 CloseHandle(listen_handle
);
495 SetHandleInformation(mutex
, HANDLE_FLAG_INHERIT
, TRUE
);
497 mutex_string
= ap_psprintf(tp
, "_FCGI_MUTEX_=%ld", mutex
);
501 listen_handle
= (HANDLE
) fs
->listenFd
;
504 memset(&si
, 0, sizeof(si
));
505 memset(&pi
, 0, sizeof(pi
));
506 memset(&r
, 0, sizeof(r
));
508 // Can up a fake request to pass to ap_get_win32_interpreter()
509 r
.per_dir_config
= fcgi_apache_main_server
->lookup_defaults
;
510 r
.server
= fcgi_apache_main_server
;
511 r
.filename
= (char *) fs
->fs_path
;
514 fileType
= ap_get_win32_interpreter(&r
, &interpreter
);
516 if (fileType
== eFileTypeUNKNOWN
) {
517 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
518 "FastCGI: %s is not executable; ensure interpreted scripts have "
519 "\"#!\" as their first line",
526 * We have the interpreter (if there is one) and we have
527 * the arguments (if there are any).
528 * Build the command string to pass to CreateProcess.
530 quoted_filename
= ap_pstrcat(tp
, "\"", fs
->fs_path
, "\"", NULL
);
531 if (interpreter
&& *interpreter
) {
532 pCommand
= ap_pstrcat(tp
, interpreter
, " ", quoted_filename
, NULL
);
535 pCommand
= quoted_filename
;
539 * Make child process use hPipeOutputWrite as standard out,
540 * and make sure it does not show on screen.
543 si
.dwFlags
= STARTF_USESHOWWINDOW
| STARTF_USESTDHANDLES
;
544 si
.wShowWindow
= SW_HIDE
;
545 si
.hStdInput
= listen_handle
;
547 // XXX These should be open to the error_log
548 si
.hStdOutput
= INVALID_HANDLE_VALUE
;
549 si
.hStdError
= INVALID_HANDLE_VALUE
;
552 * Win32's CreateProcess call requires that the environment
553 * be passed in an environment block, a null terminated block of
554 * null terminated strings.
558 while (fs
->envp
[i
]) {
559 iEnvBlockLen
+= strlen(fs
->envp
[i
]) + 1;
565 iEnvBlockLen
+= strlen(mutex_string
) + 1;
568 pEnvBlock
= (char *) ap_pcalloc(tp
, iEnvBlockLen
);
572 while (fs
->envp
[i
]) {
573 strcpy(pNext
, fs
->envp
[i
]);
574 pNext
= pNext
+ strlen(pNext
) + 1;
580 strcpy(pNext
, mutex_string
);
583 if (CreateProcess(NULL
, pCommand
, NULL
, NULL
, TRUE
,
586 ap_make_dirstr_parent(tp
, fs
->fs_path
),
588 if (fileType
== eFileTypeEXE16
) {
589 /* Hack to get 16-bit CGI's working. It works for all the
590 * standard modules shipped with Apache. pi.dwProcessId is 0
591 * for 16-bit CGIs and all the Unix specific code that calls
592 * ap_call_exec interprets this as a failure case. And we can't
593 * use -1 either because it is mapped to 0 by the caller.
598 pid
= pi
.dwProcessId
;
599 process
->handle
= pi
.hProcess
;
600 CloseHandle(pi
.hThread
);
604 // We don't need these anymore..
607 CloseHandle(listen_handle
);
619 static void reduce_privileges(void)
627 /* Get username if passed as a uid */
628 if (ap_user_name
[0] == '#') {
629 uid_t uid
= atoi(&ap_user_name
[1]);
630 struct passwd
*ent
= getpwuid(uid
);
633 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
634 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
635 "you probably need to modify the User directive", (unsigned)uid
);
644 if (setgid(ap_group_id
) == -1) {
645 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
646 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id
);
650 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
652 /* Initialize supplementary groups */
653 if (initgroups(name
, ap_group_id
) == -1) {
654 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
655 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
656 name
, (unsigned)ap_group_id
);
663 if (seteuid_user() == -1) {
664 ap_log_error(FCGI_LOG_ALERT_NOERRNO
, fcgi_apache_main_server
,
665 "FastCGI: process manager exiting, failed to reduce privileges");
670 if (setuid(ap_user_id
) == -1) {
671 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
672 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id
);
679 * Change the name of this process - best we can easily.
681 static void change_process_name(const char * const name
)
683 strncpy(ap_server_argv0
, name
, strlen(ap_server_argv0
));
687 static void schedule_start(fcgi_server
*s
, int proc
)
689 /* If we've started one recently, don't register another */
690 time_t time_passed
= now
- s
->restartTime
;
692 if ((s
->procs
[proc
].pid
&& (time_passed
< (int) s
->restartDelay
))
693 || ((s
->procs
[proc
].pid
== 0) && (time_passed
< (int) s
->initStartDelay
)))
695 FCGIDBG6("ignore_job: slot=%d, pid=%ld, time_passed=%ld, initStartDelay=%ld, restartDelay=%ld", proc
, s
->procs
[proc
].pid
, time_passed
, s
->initStartDelay
, s
->restartDelay
);
699 FCGIDBG3("scheduling_start: %s (%d)", s
->fs_path
, proc
);
700 s
->procs
[proc
].state
= STATE_NEEDS_STARTING
;
701 if (proc
== (int)dynamicMaxClassProcs
- 1) {
702 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
703 "FastCGI: scheduled the %sstart of the last (dynamic) server "
704 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
705 s
->procs
[proc
].pid
? "re" : "", s
->fs_path
, dynamicMaxClassProcs
);
710 *----------------------------------------------------------------------
714 * Removes the records written by request handlers and decodes them.
715 * We also update the data structures to reflect the changes.
717 *----------------------------------------------------------------------
720 static void dynamic_read_msgs(int read_ready
)
726 static int buflen
= 0;
727 static char buf
[FCGI_MSGS_BUFSIZE
+ 1];
728 char *ptr1
, *ptr2
, opcode
;
729 char execName
[FCGI_MAXPATH
+ 1];
730 char user
[MAX_USER_NAME_LEN
+ 2];
731 char group
[MAX_GID_CHAR_LEN
+ 1];
732 unsigned long q_usec
= 0UL, req_usec
= 0UL;
734 fcgi_pm_job
*joblist
= NULL
;
735 fcgi_pm_job
*cjob
= NULL
;
741 user
[MAX_USER_NAME_LEN
+ 1] = group
[MAX_GID_CHAR_LEN
] = '\0';
745 * To prevent the idle application from running indefinitely, we
746 * check the timer and if it is expired, we recompute the values
747 * for each running application class. Then, when REQ_COMPLETE
748 * message is received, only updates are made to the data structures.
750 if (fcgi_dynamic_last_analyzed
== 0) {
751 fcgi_dynamic_last_analyzed
= now
;
753 if ((now
- fcgi_dynamic_last_analyzed
) >= (int)dynamicUpdateInterval
) {
754 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
755 if (s
->directive
!= APP_CLASS_DYNAMIC
)
758 /* Advance the last analyzed timestamp by the elapsed time since
759 * it was last set. Round the increase down to the nearest
760 * multiple of dynamicUpdateInterval */
762 fcgi_dynamic_last_analyzed
+= (((long)(now
-fcgi_dynamic_last_analyzed
)/dynamicUpdateInterval
)*dynamicUpdateInterval
);
763 s
->smoothConnTime
= (unsigned long) ((1.0-dynamicGain
)*s
->smoothConnTime
+ dynamicGain
*s
->totalConnTime
);
764 s
->totalConnTime
= 0UL;
765 s
->totalQueueTime
= 0UL;
769 if (read_ready
<= 0) {
774 rc
= read(fcgi_pm_pipe
[0], (void *)(buf
+ buflen
), FCGI_MSGS_BUFSIZE
- buflen
);
776 if (!caughtSigTerm
) {
777 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
778 "FastCGI: read() from pipe failed (%d)", rc
);
780 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
781 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
782 caughtSigTerm
= TRUE
;
790 if (ap_acquire_mutex(fcgi_dynamic_mbox_mutex
) != MULTI_OK
) {
791 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
792 "FastCGI: Failed to aquire the dynamic mbox mutex!");
795 joblist
= fcgi_dynamic_mbox
;
796 fcgi_dynamic_mbox
= NULL
;
798 if (! ap_release_mutex(fcgi_dynamic_mbox_mutex
)) {
799 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
800 "FastCGI: Failed to release the dynamic mbox mutex!");
806 tp
= ap_make_sub_pool(fcgi_config_pool
);
809 for (ptr1
= buf
; ptr1
; ptr1
= ptr2
) {
812 ptr2
= strchr(ptr1
, '*');
824 if (sscanf(ptr1
, "%c %s %16s %15s",
825 &opcode
, execName
, user
, group
) != 4)
831 if (sscanf(ptr1
, "%c %s %16s %15s",
832 &opcode
, execName
, user
, group
) != 4)
838 if (sscanf(ptr1
, "%c %s %16s %15s %lu %lu",
839 &opcode
, execName
, user
, group
, &q_usec
, &req_usec
) != 6)
850 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
851 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1
);
855 /* Update data structures for processing */
856 while (cjob
!= NULL
) {
857 joblist
= cjob
->next
;
858 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob
->id
, cjob
->fs_path
, cjob
->user
, cjob
->group
, cjob
->qsec
, cjob
->start_time
);
862 s
= fcgi_util_fs_get(execName
, user
, group
);
864 s
= fcgi_util_fs_get(cjob
->fs_path
, cjob
->user
, cjob
->group
);
868 if (s
==NULL
&& opcode
!= REQ_COMPLETE
)
870 if (s
==NULL
&& cjob
->id
!= REQ_COMPLETE
)
875 const char *err
, *lockPath
;
878 /* Create a perm subpool to hold the new server data,
879 * we can destroy it if something doesn't pan out */
880 sp
= ap_make_sub_pool(fcgi_config_pool
);
882 /* Create a new "dynamic" server */
883 s
= fcgi_util_fs_new(sp
);
884 s
->directive
= APP_CLASS_DYNAMIC
;
885 s
->restartDelay
= dynamicRestartDelay
;
886 s
->listenQueueDepth
= dynamicListenQueueDepth
;
887 s
->initStartDelay
= dynamicInitStartDelay
;
888 s
->envp
= dynamicEnvp
;
890 s
->fs_path
= ap_pstrdup(sp
, cjob
->fs_path
);
892 s
->fs_path
= ap_pstrdup(sp
, execName
);
894 ap_getparents(s
->fs_path
);
895 ap_no2slash(s
->fs_path
);
896 s
->procs
= fcgi_util_fs_create_procs(sp
, dynamicMaxClassProcs
);
899 /* Create socket file's path */
900 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, execName
, user
, group
);
901 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
903 /* Create sockaddr, prealloc it so it won't get created in tp */
904 s
->socket_addr
= ap_pcalloc(sp
, sizeof(struct sockaddr_un
));
905 err
= fcgi_util_socket_make_domain_addr(tp
, (struct sockaddr_un
**)&s
->socket_addr
,
906 &s
->socket_addr_len
, s
->socket_path
);
908 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
909 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
913 if (init_listen_sock(s
)) {
917 /* Create the lock file */
918 lockPath
= fcgi_util_socket_get_lock_filename(tp
, s
->socket_path
);
919 fd
= ap_popenf(tp
, lockPath
,
920 O_WRONLY
| O_CREAT
| O_TRUNC
, S_IRUSR
| S_IWUSR
);
922 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
923 "FastCGI: can't create (dynamic) server \"%s\": can't open lock file \"%s\": popenf() failed",
929 /* If a wrapper is being used, config user/group info */
931 if (user
[0] == '~') {
932 /* its a user dir uri, the rest is a username, not a uid */
933 struct passwd
*pw
= getpwnam(&user
[1]);
936 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
937 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
942 s
->user
= ap_pstrdup(sp
, user
);
943 s
->username
= s
->user
;
946 s
->group
= ap_psprintf(sp
, "%ld", (long)s
->gid
);
951 s
->uid
= (uid_t
)atol(user
);
952 pw
= getpwuid(s
->uid
);
954 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
955 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
956 execName
, (long)s
->uid
);
959 s
->user
= ap_pstrdup(sp
, user
);
960 s
->username
= ap_pstrdup(sp
, pw
->pw_name
);
962 s
->gid
= (gid_t
)atol(group
);
963 s
->group
= ap_pstrdup(sp
, group
);
967 /* Create socket file's path */
968 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, cjob
->fs_path
, cjob
->user
, cjob
->group
);
969 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
972 /* Create the application lock */
973 if ((s
->dynamic_lock
= fcgi_rdwr_create()) == NULL
) {
974 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
975 "FastCGI: can;t create (dynamic) server \"%s\": fcgi_rdwr_create() failed",
980 // Lock it until the first process is started
981 fcgi_rdwr_lock(s
->dynamic_lock
, WRITER
);
988 if (opcode
== PLEASE_START
) {
990 if(cjob
->id
==PLEASE_START
) {
992 if (dynamicAutoUpdate
) {
993 /* Check to see if the binary has changed. If so,
994 * kill the FCGI application processes, and
1001 if ((stat(execName
, &stbuf
)==0) &&
1003 if ((stat(cjob
->fs_path
, &stbuf
)==0) &&
1005 (stbuf
.st_mtime
> s
->restartTime
)) {
1006 /* kill old server(s) */
1007 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1008 if (s
->procs
[i
].pid
> 0) {
1009 fcgi_kill(&s
->procs
[i
], SIGTERM
);
1013 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1014 "FastCGI: restarting server \"%s\" processes, newer version found",
1022 /* If dynamicAutoRestart, don't mark any new processes
1023 * for starting because we probably got the
1024 * PLEASE_START due to dynamicAutoUpdate and the ProcMgr
1025 * will be restarting all of those we just killed.
1027 if (dynamicAutoRestart
)
1030 /* we've been asked to start a process--only start
1031 * it if we're not already running at least one
1036 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1037 if (s
->procs
[i
].state
== STATE_STARTED
)
1040 /* if already running, don't start another one */
1041 if (i
< dynamicMaxClassProcs
) {
1058 if ((fcgi_dynamic_total_proc_count
+ 1) > (int) dynamicMaxProcs
) {
1060 * Extra instances should have been
1061 * terminated beforehand, probably need
1062 * to increase ProcessSlack parameter
1064 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1065 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1066 "exceeded dynamicMaxProcs (%d)", s
->fs_path
, dynamicMaxProcs
);
1070 /* find next free slot */
1071 for (i
= 0; i
< dynamicMaxClassProcs
; i
++)
1073 if (s
->procs
[i
].state
== STATE_NEEDS_STARTING
)
1075 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i
);
1078 else if (s
->procs
[i
].state
== STATE_STARTED
)
1083 schedule_start(s
, i
);
1088 if (i
>= dynamicMaxClassProcs
) {
1089 FCGIDBG1("ignore_job: slots are max'd");
1094 /* only record stats if we have a structure */
1097 s
->totalConnTime
+= req_usec
;
1098 s
->totalQueueTime
+= q_usec
;
1100 s
->totalConnTime
+= cjob
->start_time
;
1101 s
->totalQueueTime
+= cjob
->qsec
;
1110 /* Cleanup job data */
1111 free(cjob
->fs_path
);
1121 ap_destroy_pool(sp
);
1124 free(cjob
->fs_path
);
1132 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1133 "FastCGI: really bogus message: \"%s\"", ptr1
);
1134 ptr1
+= strlen(buf
);
1137 buflen
-= ptr1
- buf
;
1139 memmove(buf
, ptr1
, buflen
);
1143 ap_destroy_pool(tp
);
1147 *----------------------------------------------------------------------
1149 * dynamic_kill_idle_fs_procs
1151 * Implement a kill policy for the dynamic FastCGI applications.
1152 * We also update the data structures to reflect the changes.
1155 * Processes are marked for deletion possibly killed.
1157 *----------------------------------------------------------------------
1159 static void dynamic_kill_idle_fs_procs(void)
1162 struct FuncData
*funcData
= NULL
;
1163 unsigned long connTime
; /* server's smoothed running time, or
1164 * if that's 0, the current total */
1165 unsigned long totalTime
; /* maximum number of microseconds that all
1166 * of a server's running processes together
1167 * could have spent running since the
1169 double loadFactor
; /* percentage, 0-100, of totalTime that
1170 * the processes actually used */
1171 unsigned int i
, victims
= 0;
1173 const char *lockFileName
;
1177 pool
*tp
= ap_make_sub_pool(fcgi_config_pool
);
1179 /* pass 1 - locate and mark all victims */
1180 for(s
=fcgi_servers
; s
!=NULL
; s
=s
->next
) {
1181 /* Only kill dynamic apps */
1182 if (s
->directive
!= APP_CLASS_DYNAMIC
)
1185 /* If the number of non-victims is less than or equal to
1186 the minimum that may be running without being killed off,
1187 don't select any more victims. */
1188 if ((fcgi_dynamic_total_proc_count
- victims
) <= (int) dynamicMinProcs
) {
1192 connTime
= s
->smoothConnTime
? s
->smoothConnTime
: s
->totalConnTime
;
1193 totalTime
= (s
->numProcesses
)*(now
- fcgi_dynamic_epoch
)*1000000 + 1;
1195 /* XXX producing a heavy load with one client, I haven't been
1196 able to achieve a loadFactor greater than 0.5. Perhaps this
1197 should be scaled up by another order of magnitude or two. */
1198 loadFactor
= 100.0 * connTime
/ totalTime
;
1200 if ((s
->numProcesses
> 1
1201 && s
->numProcesses
/(s
->numProcesses
- 1)*loadFactor
< dynamicThresholdN
)
1202 || (s
->numProcesses
== 1 && loadFactor
< dynamicThreshold1
))
1206 for (i
= 0; !got_one
&& i
< dynamicMaxClassProcs
; ++i
) {
1207 if (s
->procs
[i
].state
== STATE_NEEDS_STARTING
) {
1208 s
->procs
[i
].state
= STATE_READY
;
1211 else if (s
->procs
[i
].state
== STATE_VICTIM
|| s
->procs
[i
].state
== STATE_KILL
) {
1216 for (i
= 0; !got_one
&& i
< dynamicMaxClassProcs
; ++i
) {
1217 if (s
->procs
[i
].state
== STATE_STARTED
) {
1218 s
->procs
[i
].state
= STATE_KILL
;
1219 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1220 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination scheduled",
1221 s
->fs_path
, s
->procs
[i
].pid
);
1229 /* pass 2 - kill procs off */
1230 for(s
=fcgi_servers
; s
!=NULL
; s
=s
->next
) {
1231 /* Only kill dynamic apps */
1232 if (s
->directive
!= APP_CLASS_DYNAMIC
)
1235 for(i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1236 if (s
->procs
[i
].state
== STATE_KILL
) {
1238 lockFileName
= fcgi_util_socket_get_lock_filename(tp
, s
->socket_path
);
1239 if ((lockFd
= ap_popenf(tp
, lockFileName
, O_RDWR
, 0))<0) {
1241 * If we need to kill an application and the
1242 * corresponding lock file does not exist, then
1243 * that means we are in big trouble here
1245 /*@@@ this should be logged, but since all the lock
1246 * file stuff will be tossed, I'll leave it now */
1247 ap_pclosef(tp
, lockFd
);
1251 if (fcgi_get_exclusive_write_lock_no_wait(lockFd
) < 0) {
1253 if (fcgi_get_exclusive_write_lock_no_wait(s
->dynamic_lock
) < 0) {
1255 FCGIDBG2("fcgi_get_exclusive_write_lock_no_wait() failed (%ld)", GetLastError());
1257 * Unable to lock the lockfile, indicative
1258 * of WS performing operation with the given
1259 * application class. The simplest solution
1260 * is to spawn off another process and block
1261 * on lock to kill it. This is under assumptions
1262 * that fork() is not very costly and this
1263 * situation occurs very rarely, which it should
1266 funcData
= ap_pcalloc(tp
, sizeof(struct FuncData
));
1267 funcData
->lockFileName
= lockFileName
;
1269 funcData
= malloc(sizeof(struct FuncData
));
1270 funcData
->lock
= s
->dynamic_lock
;
1272 funcData
->process
= &s
->procs
[i
];
1275 if((pid
=fork())<0) {
1276 /*@@@ this should be logged, but since all the lock
1277 * file stuff will be tossed, I'll leave it now */
1278 ap_pclosef(tp
, lockFd
);
1283 /* rename the process for ps - best we can easily */
1284 change_process_name("fcgiBlkKill");
1286 dynamic_blocking_kill(funcData
);
1289 s
->procs
[i
].state
= STATE_VICTIM
;
1290 ap_pclosef(tp
, lockFd
);
1293 CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
) dynamic_blocking_kill
, (LPVOID
) funcData
, 0, NULL
);
1294 s
->procs
[i
].state
= STATE_VICTIM
;
1298 FCGIDBG1("fcgi_get_exclusive_write_lock_no_wait() succeeded");
1299 s
->procs
[i
].state
= STATE_VICTIM
;
1301 fcgi_kill(&s
->procs
[i
], SIGTERM
);
1302 ap_pclosef(tp
, lockFd
);
1304 fcgi_kill(&s
->procs
[i
], 1);
1305 fcgi_rdwr_unlock(s
->dynamic_lock
, WRITER
);
1311 ap_destroy_pool(tp
);
1316 // This is a little bogus, there's gotta be a better way to do this
1317 // Can we use WaitForMultipleObjects()
1318 #define FCGI_PROC_WAIT_TIME 100
1320 void child_wait_thread(void *dummy
) {
1322 DWORD dwRet
= WAIT_TIMEOUT
;
1327 while (!bTimeToDie
) {
1330 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1331 if (s
->directive
== APP_CLASS_EXTERNAL
|| s
->listenFd
< 0) {
1334 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1335 numChildren
= dynamicMaxClassProcs
;
1338 numChildren
= s
->numProcesses
;
1341 for (i
=0; i
< numChildren
; i
++) {
1342 if (s
->procs
[i
].handle
!= INVALID_HANDLE_VALUE
) {
1343 /* timeout is currently set for 100 miliecond */
1344 /* it may need t longer or user customizable */
1345 dwRet
= WaitForSingleObject(s
->procs
[i
].handle
, FCGI_PROC_WAIT_TIME
);
1349 if (dwRet
!= WAIT_TIMEOUT
&& dwRet
!= WAIT_FAILED
) {
1350 /* a child fs has died */
1351 /* mark the child as dead */
1353 if (s
->directive
== APP_CLASS_STANDARD
) {
1354 /* restart static app */
1355 s
->procs
[i
].state
= STATE_NEEDS_STARTING
;
1360 fcgi_dynamic_total_proc_count
--;
1361 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1363 if (s
->procs
[i
].state
== STATE_VICTIM
) {
1364 s
->procs
[i
].state
= STATE_KILLED
;
1367 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1370 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0)) {
1371 s
->procs
[i
].state
= STATE_NEEDS_STARTING
;
1374 s
->procs
[i
].state
= STATE_READY
;
1379 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1380 "FastCGI:%s server \"%s\" (pid %d) terminated",
1381 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1382 s
->fs_path
, s
->procs
[i
].pid
);
1384 CloseHandle(s
->procs
[i
].handle
);
1385 s
->procs
[i
].handle
= INVALID_HANDLE_VALUE
;
1386 s
->procs
[i
].pid
= -1;
1388 /* wake up the main thread */
1389 SetEvent(fcgi_event_handles
[WAKE_EVENT
]);
1394 Sleep(waited
? 0 : FCGI_PROC_WAIT_TIME
);
1400 static void setup_signals(void)
1403 struct sigaction sa
;
1407 sigaddset(&mask
, SIGUSR2
);
1408 sigprocmask(SIG_BLOCK
, &mask
, NULL
);
1410 /* Setup handlers */
1412 sa
.sa_handler
= signal_handler
;
1413 sigemptyset(&sa
.sa_mask
);
1416 if (sigaction(SIGTERM
, &sa
, NULL
) < 0) {
1417 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1418 "sigaction(SIGTERM) failed");
1421 if (sigaction(SIGHUP
, &sa
, NULL
) < 0) {
1422 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1423 "sigaction(SIGHUP) failed");
1425 /* httpd graceful restart */
1426 if (sigaction(SIGUSR1
, &sa
, NULL
) < 0) {
1427 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1428 "sigaction(SIGUSR1) failed");
1430 /* read messages from request handlers - kill interval expired */
1431 if (sigaction(SIGALRM
, &sa
, NULL
) < 0) {
1432 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1433 "sigaction(SIGALRM) failed");
1435 if (sigaction(SIGCHLD
, &sa
, NULL
) < 0) {
1436 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1437 "sigaction(SIGCHLD) failed");
1443 int fcgi_pm_main(void *dummy
, child_info
*info
)
1445 void fcgi_pm_main(void *dummy
)
1456 HANDLE wait_thread
= INVALID_HANDLE_VALUE
;
1458 int callWaitPid
, callDynamicProcs
;
1462 // Add SystemRoot to the dynamic environment
1463 char ** envp
= dynamicEnvp
;
1464 for (i
= 0; *envp
; ++i
) {
1467 fcgi_config_set_env_var(fcgi_config_pool
, dynamicEnvp
, &i
, "SystemRoot");
1470 reduce_privileges();
1472 close(fcgi_pm_pipe
[1]);
1473 change_process_name("fcgi-pm");
1477 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1478 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper
);
1482 /* Initialize AppClass */
1483 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1485 if (s
->directive
!= APP_CLASS_STANDARD
)
1493 for (i
= 0; i
< s
->numProcesses
; ++i
)
1494 s
->procs
[i
].state
= STATE_NEEDS_STARTING
;
1498 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1499 "FastCGI: process manager initialized");
1501 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1502 "FastCGI: process manager initialized (pid %ld)", (long)getpid());
1508 * Loop until SIGTERM
1511 int sleepSeconds
= min(dynamicKillInterval
, dynamicUpdateInterval
);
1518 unsigned int numChildren
;
1521 * If we came out of sigsuspend() for any reason other than
1522 * SIGALRM, pick up where we left off.
1525 sleepSeconds
= alarmLeft
;
1528 * Examine each configured AppClass for a process that needs
1529 * starting. Compute the earliest time when the start should
1530 * be attempted, starting it now if the time has passed. Also,
1531 * remember that we do NOT need to restart externally managed
1532 * FastCGI applications.
1534 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
)
1536 if (s
->directive
== APP_CLASS_EXTERNAL
)
1539 numChildren
= (s
->directive
== APP_CLASS_DYNAMIC
)
1540 ? dynamicMaxClassProcs
1543 for (i
= 0; i
< numChildren
; ++i
)
1545 if (s
->procs
[i
].pid
<= 0 && s
->procs
[i
].state
== STATE_NEEDS_STARTING
)
1547 int restart
= (s
->procs
[i
].pid
< 0);
1548 time_t restartTime
= s
->restartTime
;
1550 restartTime
+= (restart
) ? s
->restartDelay
: s
->initStartDelay
;
1552 if (restartTime
<= now
)
1554 s
->restartTime
= now
;
1556 if (s
->listenFd
< 0 && init_listen_sock(s
))
1558 if (sleepSeconds
> s
->initStartDelay
)
1559 sleepSeconds
= s
->initStartDelay
;
1564 if (caughtSigTerm
) {
1565 goto ProcessSigTerm
;
1568 s
->procs
[i
].pid
= spawn_fs_process(s
, &s
->procs
[i
]);
1569 if (s
->procs
[i
].pid
<= 0) {
1570 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1571 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1572 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1575 sleepSeconds
= min(sleepSeconds
,
1576 max((int) s
->restartDelay
, FCGI_MIN_EXEC_RETRY_DELAY
));
1578 ap_assert(s
->procs
[i
].pid
< 0);
1582 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1584 fcgi_dynamic_total_proc_count
++;
1585 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count
);
1587 if (i
== 0 && !restart
) {
1588 fcgi_rdwr_unlock(s
->dynamic_lock
, WRITER
);
1593 s
->procs
[i
].state
= STATE_STARTED
;
1599 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1600 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1601 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1602 s
->fs_path
, (long)s
->uid
, (long)s
->gid
,
1603 restart
? "re" : "", (long)s
->procs
[i
].pid
);
1606 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1607 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1608 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1609 s
->fs_path
, restart
? "re" : "", (long)s
->procs
[i
].pid
);
1611 ap_assert(s
->procs
[i
].pid
> 0);
1613 sleepSeconds
= min(sleepSeconds
, restartTime
- now
);
1621 goto ProcessSigTerm
;
1623 if((!caughtSigChld
) && (!caughtSigUsr2
)) {
1626 alarm(sleepSeconds
);
1629 FD_SET(fcgi_pm_pipe
[0], &rfds
);
1630 read_ready
= ap_select(fcgi_pm_pipe
[0] + 1, &rfds
, NULL
, NULL
, NULL
);
1632 alarmLeft
= alarm(0);
1634 callWaitPid
= caughtSigChld
;
1635 caughtSigChld
= FALSE
;
1636 callDynamicProcs
= caughtSigUsr2
;
1637 caughtSigUsr2
= FALSE
;
1642 * Dynamic fcgi process management
1644 if((callDynamicProcs
) || (!callWaitPid
)) {
1645 dynamic_read_msgs(read_ready
);
1646 if(fcgi_dynamic_epoch
== 0) {
1647 fcgi_dynamic_epoch
= now
;
1649 if(((long)(now
-fcgi_dynamic_epoch
)>=dynamicKillInterval
) ||
1650 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
)>=dynamicMaxProcs
)) {
1651 dynamic_kill_idle_fs_procs();
1652 fcgi_dynamic_epoch
= now
;
1660 /* We've caught SIGCHLD, so find out who it was using waitpid,
1661 * write a log message and update its data structure. */
1665 goto ProcessSigTerm
;
1667 childPid
= waitpid(-1, &waitStatus
, WNOHANG
);
1669 if (childPid
== -1 || childPid
== 0)
1672 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1673 if (s
->directive
== APP_CLASS_EXTERNAL
)
1676 if (s
->directive
== APP_CLASS_DYNAMIC
)
1677 numChildren
= dynamicMaxClassProcs
;
1679 numChildren
= s
->numProcesses
;
1681 for (i
= 0; i
< numChildren
; i
++) {
1682 if (s
->procs
[i
].pid
== childPid
)
1687 /* @@@ This (comment) needs to go away when dynamic gets cleaned up.
1688 * If we get to this point, we have detected the
1689 * termination of the process that was spawned off by
1690 * the process manager to do a blocking kill above. */
1694 s
->procs
[i
].pid
= -1;
1696 if (s
->directive
== APP_CLASS_STANDARD
) {
1697 /* Always restart static apps */
1698 s
->procs
[i
].state
= STATE_NEEDS_STARTING
;
1703 fcgi_dynamic_total_proc_count
--;
1705 if (s
->procs
[i
].state
== STATE_VICTIM
) {
1706 s
->procs
[i
].state
= STATE_KILLED
;
1709 /* A dynamic app died or exited without provocation from the PM */
1712 if (dynamicAutoRestart
|| (s
->numProcesses
<= 0 && dynamicThreshold1
== 0))
1713 s
->procs
[i
].state
= STATE_NEEDS_STARTING
;
1715 s
->procs
[i
].state
= STATE_READY
;
1719 if (WIFEXITED(waitStatus
)) {
1720 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1721 "FastCGI:%s server \"%s\" (pid %d) terminated by calling exit with status '%d'",
1722 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1723 s
->fs_path
, (int)childPid
, WEXITSTATUS(waitStatus
));
1725 else if (WIFSIGNALED(waitStatus
)) {
1726 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1727 "FastCGI:%s server \"%s\" (pid %d) terminated due to uncaught signal '%d' (%s)%s",
1728 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1729 s
->fs_path
, (int)childPid
, WTERMSIG(waitStatus
), SYS_SIGLIST
[WTERMSIG(waitStatus
)],
1731 WCOREDUMP(waitStatus
) ? ", a core file may have been generated" : "");
1736 else if (WIFSTOPPED(waitStatus
)) {
1737 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1738 "FastCGI:%s server \"%s\" (pid %d) stopped due to uncaught signal '%d' (%s)",
1739 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1740 s
->fs_path
, (int)childPid
, WTERMSIG(waitStatus
), SYS_SIGLIST
[WTERMSIG(waitStatus
)]);
1742 } /* for (;;), waitpid() */
1745 /* Start the child wait thread */
1746 wait_thread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)child_wait_thread
, NULL
, 0, NULL
);
1750 /* wait for an event to occur or timer expires */
1751 expire
= time(NULL
) + sleepSeconds
;
1752 dwRet
= WaitForMultipleObjects(3, (HANDLE
*) fcgi_event_handles
, FALSE
, sleepSeconds
* 1000);
1754 if (dwRet
== WAIT_FAILED
) {
1755 /* There is something seriously wrong here */
1756 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1757 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1761 if (dwRet
!= WAIT_TIMEOUT
) {
1765 alarmLeft
= expire
- now
;
1769 * Dynamic fcgi process management
1771 if ((dwRet
== MBOX_EVENT
) || (dwRet
== WAIT_TIMEOUT
)) {
1772 if (dwRet
== MBOX_EVENT
) {
1778 dynamic_read_msgs(read_ready
);
1780 if(fcgi_dynamic_epoch
== 0) {
1781 fcgi_dynamic_epoch
= now
;
1784 if (((long)(now
-fcgi_dynamic_epoch
) >= (int)dynamicKillInterval
) ||
1785 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
) >= dynamicMaxProcs
)) {
1786 dynamic_kill_idle_fs_procs();
1787 fcgi_dynamic_epoch
= now
;
1791 else if (dwRet
== WAKE_EVENT
) {
1794 else if (dwRet
== TERM_EVENT
) {
1795 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
1796 "FastCGI: Termination event received process manager shutting down");
1799 dwRet
= WaitForSingleObject(wait_thread
, INFINITE
);
1800 goto ProcessSigTerm
;
1803 // Have an received an unknown event - should not happen
1804 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1805 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1807 dwRet
= WaitForSingleObject(wait_thread
, INFINITE
);
1808 goto ProcessSigTerm
;
1811 } /* for (;;), the whole shoot'n match */
1815 * Kill off the children, then exit.
1817 while (fcgi_servers
!= NULL
) {
1818 kill_fs_procs(fcgi_config_pool
, fcgi_servers
);
1829 int fcgi_pm_add_job(fcgi_pm_job
*new_job
) {
1831 if (new_job
== NULL
)
1834 if (ap_acquire_mutex(fcgi_dynamic_mbox_mutex
) != MULTI_OK
) {
1835 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1836 "FastCGI: Failed to aquire the dynamic mbox mutex!");
1839 new_job
->next
= fcgi_dynamic_mbox
;
1840 fcgi_dynamic_mbox
= new_job
;
1842 if (! ap_release_mutex(fcgi_dynamic_mbox_mutex
)) {
1843 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1844 "FastCGI: Failed to release the dynamic mbox mutex!");