2 * $Id: fcgi_pm.c,v 1.29 2000/05/10 05:15:48 robs Exp $
9 #include "multithread.h"
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 */
25 pid_t pid
; /* process to issue SIGTERM to */
27 FcgiRWLock
*lock
; /* reader/writer lock for dynamic app */
28 HANDLE pid
; /* process to issue SIGTERM to */
33 static BOOL bTimeToDie
= FALSE
; /* process termination flag */
34 HANDLE fcgi_event_handles
[3];
39 static int seteuid_root(void)
41 int rc
= seteuid((uid_t
)0);
43 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
44 "FastCGI: seteuid(0) failed");
49 static int seteuid_user(void)
51 int rc
= seteuid(ap_user_id
);
53 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
54 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id
);
60 static int fcgi_kill(pid_t pid
, int sig
)
72 rc
= TerminateProcess((HANDLE
) pid
, sig
);
77 /*******************************************************************************
78 * Send SIGTERM to each process in the server class, remove socket and lock
79 * file if appropriate. Currently this is only called when the PM is shutting
80 * down and thus memory isn't freed and sockets and files aren't closed.
82 static void kill_fs_procs(pool
*p
, fcgi_server
*s
)
84 ServerProcess
*proc
= s
->procs
;
87 if (s
->directive
== APP_CLASS_DYNAMIC
)
88 numChildren
= dynamicMaxClassProcs
;
90 numChildren
= s
->numProcesses
;
92 for (i
= 0; i
< numChildren
; i
++, proc
++) {
95 fcgi_kill(proc
->pid
, SIGTERM
);
99 if (proc
->pid
!= INVALID_HANDLE_VALUE
) {
100 fcgi_kill((int) proc
->pid
, 1);
101 CloseHandle((HANDLE
) proc
->pid
);
102 proc
->pid
= INVALID_HANDLE_VALUE
;
107 /* Remove the dead lock file */
108 if (s
->directive
== APP_CLASS_DYNAMIC
) {
110 const char *lockFileName
= fcgi_util_socket_get_lock_filename(p
, s
->socket_path
);
112 if (unlink(lockFileName
) != 0) {
113 ap_log_error(FCGI_LOG_ERR
, fcgi_apache_main_server
,
114 "FastCGI: unlink() failed to remove lock file \"%s\" for (dynamic) server \"%s\"",
115 lockFileName
, s
->fs_path
);
118 fcgi_rdwr_destroy(s
->dynamic_lock
);
122 /* Remove the socket file */
123 if (s
->socket_path
!= NULL
&& s
->directive
!= APP_CLASS_EXTERNAL
) {
125 if (unlink(s
->socket_path
) != 0) {
126 ap_log_error(FCGI_LOG_ERR
, fcgi_apache_main_server
,
127 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
129 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "", s
->fs_path
);
132 CloseHandle((HANDLE
)s
->listenFd
);
135 fcgi_servers
= s
->next
;
138 /*******************************************************************************
139 * Bind an address to a socket and set it to listen for incoming connects.
140 * The error messages are allocated from the pool p, use temp storage.
141 * Don't forget to close the socket, if an error occurs.
143 static const char *bind_n_listen(pool
*p
, struct sockaddr
*socket_addr
,
144 int socket_addr_len
, int backlog
, int sock
)
147 if (socket_addr
->sa_family
== AF_UNIX
) {
148 /* Remove any existing socket file.. just in case */
149 unlink(((struct sockaddr_un
*)socket_addr
)->sun_path
);
155 setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, (char *)&flag
, sizeof(flag
));
158 /* Bind it to the socket_addr */
159 if (bind(sock
, socket_addr
, socket_addr_len
) != 0)
160 return "bind() failed";
163 /* Twiddle permissions */
164 if (socket_addr
->sa_family
== AF_UNIX
) {
165 if (chmod(((struct sockaddr_un
*)socket_addr
)->sun_path
, S_IRUSR
| S_IWUSR
))
166 return "chmod() of socket failed";
171 if (listen(sock
, backlog
) != 0)
172 return "listen() failed";
178 *----------------------------------------------------------------------
180 * dynamic_blocking_kill
182 * Block on the lock file until it is available, and then
183 * issue a kill signal to the corresponding application.
184 * Since this function is executed in the child process,
185 * _exit() is called upon completion.
188 * Pointer to the data structure containing a process id to
189 * issue a signal to and the full pathname to the lockfile
190 * that needs to be locked before the issue of the signal.
193 * Memory is allocated by the caller, but is freed by this
196 *----------------------------------------------------------------------
198 static void dynamic_blocking_kill(void *data
)
200 struct FuncData
*funcData
= (struct FuncData
*)data
;
204 ap_assert(funcData
->lockFileName
);
205 if ((lockFd
= open(funcData
->lockFileName
, O_RDWR
)) < 0) {
206 /* There is something terribly wrong here */
208 if (fcgi_wait_for_shared_write_lock(lockFd
) < 0) {
209 /* This is a major problem */
211 fcgi_kill(funcData
->pid
, SIGTERM
);
214 /* exit() may flush stdio buffers inherited from the parent. */
217 ap_assert(funcData
->lock
);
218 if (fcgi_wait_for_shared_write_lock(funcData
->lock
) < 0) {
219 // This is a major problem
222 TerminateProcess(funcData
->pid
, 1);
223 fcgi_rdwr_unlock(funcData
->lock
, WRITER
);
230 *----------------------------------------------------------------------
234 * The FastCGI process manager, which runs as a separate
235 * process responsible for:
236 * - Starting all the FastCGI proceses.
237 * - Restarting any of these processes that die (indicated
239 * - Catching SIGTERM and relaying it to all the FastCGI
240 * processes before exiting.
243 * Uses global variable fcgi_servers.
251 *----------------------------------------------------------------------
254 static int caughtSigTerm
= FALSE
;
255 static int caughtSigChld
= FALSE
;
256 static int caughtSigUsr2
= FALSE
;
258 static void signal_handler(int signo
)
260 if ((signo
== SIGTERM
) || (signo
== SIGUSR1
) || (signo
== SIGHUP
)) {
261 /* SIGUSR1 & SIGHUP are sent by apache to its process group
262 * when apache get 'em. Apache follows up (1.2.x) with attacks
263 * on each of its child processes, but we've got the KillMgr
264 * sitting between us so we never see the KILL. The main loop
265 * in ProcMgr also checks to see if the KillMgr has terminated,
266 * and if it has, we handl it as if we should shutdown too. */
267 caughtSigTerm
= TRUE
;
268 } else if(signo
== SIGCHLD
) {
269 caughtSigChld
= TRUE
;
270 } else if(signo
== SIGALRM
) {
271 caughtSigUsr2
= TRUE
;
277 *----------------------------------------------------------------------
279 * spawn_fs_process --
281 * Fork and exec the specified fcgi process.
284 * 0 for successful fork, -1 for failed fork.
286 * In case the child fails before or in the exec, the child
287 * obtains the error log by calling getErrLog, logs
288 * the error, and exits with exit status = errno of
289 * the failed system call.
292 * Child process created.
294 *----------------------------------------------------------------------
298 static pid_t
spawn_fs_process(const fcgi_server
*fs
)
300 static HANDLE
spawn_fs_process(fcgi_server
*fs
)
307 char *dnEnd
, *failedSysCall
;
314 /* We're the child. We're gonna exec() so pools don't matter. */
316 dnEnd
= strrchr(fs
->fs_path
, '/');
320 dirName
= ap_pcalloc(fcgi_config_pool
, dnEnd
- fs
->fs_path
+ 1);
321 dirName
= memcpy(dirName
, fs
->fs_path
, dnEnd
- fs
->fs_path
);
323 if (chdir(dirName
) < 0) {
324 failedSysCall
= "chdir()";
325 goto FailedSystemCallExit
;
329 /* OS/2 dosen't support nice() */
330 if (fs
->processPriority
!= 0) {
331 if (nice(fs
->processPriority
) == -1) {
332 failedSysCall
= "nice()";
333 goto FailedSystemCallExit
;
338 /* Open the listenFd on spec'd fd */
339 if (fs
->listenFd
!= FCGI_LISTENSOCK_FILENO
)
340 dup2(fs
->listenFd
, FCGI_LISTENSOCK_FILENO
);
342 /* Close all other open fds, except stdout/stderr. Leave these two open so
343 * FastCGI applications don't have to find and fix ALL 3rd party libs that
344 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
345 * main server error_log - @@@ provide a directive control where this goes.
347 ap_error_log2stderr(fcgi_apache_main_server
);
348 dup2(STDERR_FILENO
, STDOUT_FILENO
);
349 for (i
= 0; i
< MAX_OPEN_FDS
; i
++) {
350 if (i
!= FCGI_LISTENSOCK_FILENO
&& i
!= STDERR_FILENO
&& i
!= STDOUT_FILENO
) {
355 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
356 * install its own handler. */
357 signal(SIGPIPE
, SIG_IGN
);
359 if (fcgi_suexec
!= NULL
) {
360 char *shortName
= strrchr(fs
->fs_path
, '/') + 1;
362 /* Relinquish our root real uid powers */
367 execle(fcgi_suexec
, fcgi_suexec
, fs
->username
, fs
->group
, shortName
, NULL
, fs
->envp
);
368 } while (errno
== EINTR
);
372 execle(fs
->fs_path
, fs
->fs_path
, NULL
, fs
->envp
);
373 } while (errno
== EINTR
);
376 failedSysCall
= "execle()";
378 FailedSystemCallExit
:
379 fprintf(stderr
, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
380 fs
->fs_path
, (long) getpid(), failedSysCall
, strerror(errno
));
383 /* avoid an irrelevant compiler warning */
389 char * EnvBlock
= NULL
;
392 HANDLE child_process
;
395 PROCESS_INFORMATION pi
;
396 pool
* tp
=ap_make_sub_pool(fcgi_config_pool
);
398 memset(&si
, 0, sizeof(si
));
399 memset(&pi
, 0, sizeof(pi
));
402 si
.dwFlags
= STARTF_USESHOWWINDOW
| STARTF_USESTDHANDLES
;
403 si
.wShowWindow
= SW_HIDE
;
404 si
.hStdInput
= (HANDLE
) fs
->listenFd
;
405 si
.hStdOutput
= INVALID_HANDLE_VALUE
;
406 si
.hStdError
= INVALID_HANDLE_VALUE
;
408 // There is always at least SystemRoot under Win32
409 while (fs
->envp
[i
]) {
410 EnvBlockLen
+= strlen(fs
->envp
[i
]) + 1;
414 if (fs
->socket_path
) {
415 mutex
= ap_psprintf(tp
, "_FCGI_MUTEX_=%d", (int) fs
->hPipeMutex
);
416 EnvBlockLen
+= strlen(mutex
) + 1;
419 EnvBlock
= (char *) ap_pcalloc(tp
, EnvBlockLen
);
421 // These are supposed to be sorted, oh well
424 while (fs
->envp
[i
]) {
425 strcpy(Next
, fs
->envp
[i
]);
426 Next
= Next
+ strlen(Next
) + 1;
430 if (fs
->socket_path
) {
434 success
= SetHandleInformation(si
.hStdInput
, HANDLE_FLAG_INHERIT
, TRUE
);
436 success
= CreateProcess(NULL
, (char *)fs
->fs_path
, NULL
, NULL
, TRUE
, 0, EnvBlock
,
437 ap_make_dirstr_parent(tp
, fs
->fs_path
), &si
, &pi
);
442 child_process
= pi
.hProcess
;
443 CloseHandle(pi
.hThread
);
445 return(child_process
);
448 return INVALID_HANDLE_VALUE
;
454 static void reduce_priveleges(void)
462 /* Get username if passed as a uid */
463 if (ap_user_name
[0] == '#') {
464 uid_t uid
= atoi(&ap_user_name
[1]);
465 struct passwd
*ent
= getpwuid(uid
);
468 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
469 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
470 "you probably need to modify the User directive", (unsigned)uid
);
479 if (setgid(ap_group_id
) == -1) {
480 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
481 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id
);
485 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
487 /* Initialize supplementary groups */
488 if (initgroups(name
, ap_group_id
) == -1) {
489 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
490 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
491 name
, (unsigned)ap_group_id
);
498 if (seteuid_user() == -1) {
499 ap_log_error(FCGI_LOG_ALERT_NOERRNO
, fcgi_apache_main_server
,
500 "FastCGI: process manager exiting, failed to reduce priveleges");
505 if (setuid(ap_user_id
) == -1) {
506 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
507 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id
);
514 * Change the name of this process - best we can easily.
516 static void change_process_name(const char * const name
)
518 strncpy(ap_server_argv0
, name
, strlen(ap_server_argv0
));
522 static void schedule_start(fcgi_server
*s
, int proc
)
524 s
->procs
[proc
].state
= STATE_NEEDS_STARTING
;
525 if (proc
== (int)dynamicMaxClassProcs
- 1) {
526 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
527 "FastCGI: scheduled the %sstart of the last (dynamic) server "
528 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
529 s
->procs
[proc
].pid
? "re" : "", s
->fs_path
, dynamicMaxClassProcs
);
534 *----------------------------------------------------------------------
538 * Removes the records written by request handlers and decodes them.
539 * We also update the data structures to reflect the changes.
541 *----------------------------------------------------------------------
544 static void dynamic_read_msgs(int read_ready
)
550 static int buflen
= 0;
551 static char buf
[FCGI_MSGS_BUFSIZE
+ 1];
552 char *ptr1
, *ptr2
, opcode
;
553 char execName
[FCGI_MAXPATH
+ 1];
554 char user
[MAX_USER_NAME_LEN
+ 2];
555 char group
[MAX_GID_CHAR_LEN
+ 1];
556 unsigned long q_usec
= 0UL, req_usec
= 0UL;
558 fcgi_pm_job
*joblist
= NULL
;
559 fcgi_pm_job
*cjob
= NULL
;
560 SECURITY_ATTRIBUTES sa
;
566 user
[MAX_USER_NAME_LEN
+ 1] = group
[MAX_GID_CHAR_LEN
] = '\0';
570 * To prevent the idle application from running indefinitely, we
571 * check the timer and if it is expired, we recompute the values
572 * for each running application class. Then, when REQ_COMPLETE
573 * message is recieved, only updates are made to the data structures.
575 if (fcgi_dynamic_last_analyzed
== 0) {
576 fcgi_dynamic_last_analyzed
= now
;
578 if ((now
- fcgi_dynamic_last_analyzed
) >= (int)dynamicUpdateInterval
) {
579 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
580 if (s
->directive
!= APP_CLASS_DYNAMIC
)
582 /* XXX what does this adjustment do? */
583 fcgi_dynamic_last_analyzed
+= (((long)(now
-fcgi_dynamic_last_analyzed
)/dynamicUpdateInterval
)*dynamicUpdateInterval
);
584 s
->smoothConnTime
= (unsigned long) ((1.0-dynamicGain
)*s
->smoothConnTime
+ dynamicGain
*s
->totalConnTime
);
585 s
->totalConnTime
= 0UL;
586 s
->totalQueueTime
= 0UL;
590 if (read_ready
<= 0) {
595 rc
= read(fcgi_pm_pipe
[0], (void *)(buf
+ buflen
), FCGI_MSGS_BUFSIZE
- buflen
);
597 if (!caughtSigTerm
) {
598 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
599 "FastCGI: read() from pipe failed (%d)", rc
);
606 /* Obtain the data from the fcgi_dynamic_mbox file */
607 ap_acquire_mutex(fcgi_dynamic_mbox_mutex
);
609 if (fcgi_dynamic_mbox
== NULL
) {
613 joblist
= fcgi_dynamic_mbox
;
614 fcgi_dynamic_mbox
= NULL
;
617 ap_release_mutex(fcgi_dynamic_mbox_mutex
);
622 tp
= ap_make_sub_pool(fcgi_config_pool
);
625 for (ptr1
= buf
; ptr1
; ptr1
= ptr2
) {
628 ptr2
= strchr(ptr1
, '*');
640 if (sscanf(ptr1
, "%c %s %16s %15s",
641 &opcode
, execName
, user
, group
) != 4)
647 if (sscanf(ptr1
, "%c %s %16s %15s",
648 &opcode
, execName
, user
, group
) != 4)
654 if (sscanf(ptr1
, "%c %s %16s %15s %lu %lu",
655 &opcode
, execName
, user
, group
, &q_usec
, &req_usec
) != 6)
666 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
667 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1
);
671 /* Update data structures for processing */
672 while (cjob
!= NULL
) {
673 joblist
= cjob
->next
;
677 s
= fcgi_util_fs_get(execName
, user
, group
);
679 s
= fcgi_util_fs_get(cjob
->fs_path
, cjob
->user
, cjob
->group
);
683 if (s
==NULL
&& opcode
!= REQ_COMPLETE
)
685 if (s
==NULL
&& cjob
->id
!= REQ_COMPLETE
)
690 const char *err
, *lockPath
;
693 /* Create a perm subpool to hold the new server data,
694 * we can destroy it if something doesn't pan out */
695 sp
= ap_make_sub_pool(fcgi_config_pool
);
697 /* Create a new "dynamic" server */
698 s
= fcgi_util_fs_new(sp
);
699 s
->directive
= APP_CLASS_DYNAMIC
;
700 s
->restartDelay
= dynamicRestartDelay
;
701 s
->listenQueueDepth
= dynamicListenQueueDepth
;
702 s
->initStartDelay
= dynamicInitStartDelay
;
703 s
->envp
= dynamicEnvp
;
705 ap_getparents(execName
);
706 ap_no2slash(execName
);
707 s
->fs_path
= ap_pstrdup(sp
, execName
);
709 ap_getparents(cjob
->fs_path
);
710 ap_no2slash(cjob
->fs_path
);
711 s
->fs_path
= ap_pstrdup(sp
, cjob
->fs_path
);
713 s
->procs
= fcgi_util_fs_create_procs(sp
, dynamicMaxClassProcs
);
716 /* Create socket file's path */
717 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, execName
, user
, group
);
718 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
720 /* Create sockaddr, prealloc it so it won't get created in tp */
721 s
->socket_addr
= ap_pcalloc(sp
, sizeof(struct sockaddr_un
));
722 err
= fcgi_util_socket_make_domain_addr(tp
, (struct sockaddr_un
**)&s
->socket_addr
,
723 &s
->socket_addr_len
, s
->socket_path
);
725 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
726 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
730 /* Create the socket */
731 if ((s
->listenFd
= ap_psocket(sp
, s
->socket_addr
->sa_family
, SOCK_STREAM
, 0)) < 0) {
732 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
733 "FastCGI: can't create (dynamic) server \"%s\": socket() failed", execName
);
737 /* bind() and listen() */
738 err
= bind_n_listen(tp
, s
->socket_addr
, s
->socket_addr_len
,
739 s
->listenQueueDepth
, s
->listenFd
);
741 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
742 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
746 /* Create the lock file */
747 lockPath
= fcgi_util_socket_get_lock_filename(tp
, s
->socket_path
);
748 fd
= ap_popenf(tp
, lockPath
,
749 O_WRONLY
| O_CREAT
| O_TRUNC
, S_IRUSR
| S_IWUSR
);
751 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
752 "FastCGI: can't create (dynamic) server \"%s\": can't open lock file \"%s\": popenf() failed",
758 /* If suexec is being used, config user/group info */
760 if (user
[0] == '~') {
761 /* its a user dir uri, the rest is a username, not a uid */
762 struct passwd
*pw
= getpwnam(&user
[1]);
765 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
766 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for suexec: getpwnam(%s) failed",
771 s
->user
= ap_pstrdup(sp
, user
);
772 s
->username
= s
->user
;
775 s
->group
= ap_psprintf(sp
, "%ld", (long)s
->gid
);
780 s
->uid
= (uid_t
)atol(user
);
781 pw
= getpwuid(s
->uid
);
783 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
784 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for suexec: getwpuid(%ld) failed",
785 execName
, (long)s
->uid
);
788 s
->user
= ap_pstrdup(sp
, user
);
789 s
->username
= ap_pstrdup(sp
, pw
->pw_name
);
791 s
->gid
= (gid_t
)atol(group
);
792 s
->group
= ap_pstrdup(sp
, group
);
796 /* Create socket file's path */
797 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, cjob
->fs_path
, cjob
->user
, cjob
->group
);
798 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
800 /* Create Named Pipe and I/O mutex*/
801 sa
.nLength
= sizeof(sa
);
802 sa
.lpSecurityDescriptor
= NULL
;
803 sa
.bInheritHandle
= TRUE
;
805 s
->listenFd
= (int)CreateNamedPipe(s
->socket_path
,
807 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
808 PIPE_UNLIMITED_INSTANCES
, 4096,4096,0, &sa
);
810 if ((HANDLE
)s
->listenFd
== INVALID_HANDLE_VALUE
) {
811 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
812 "FASTCGI: can't create (dynamic) server \"%s\": CreateNamePipe() failed",
817 s
->hPipeMutex
= ap_create_mutex(NULL
);
819 if (s
->hPipeMutex
== NULL
) {
820 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
821 "FastCGI: can't create (dynamic) server \"%s\": ap_create_mutex() failed", cjob
->fs_path
);
822 CloseHandle((HANDLE
)s
->listenFd
);
826 SetHandleInformation(s
->hPipeMutex
, HANDLE_FLAG_INHERIT
, TRUE
);
828 /* Create the application lock */
829 if ((s
->dynamic_lock
= fcgi_rdwr_create()) == NULL
) {
830 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
831 "FastCGI: can;t create (dynamic) server \"%s\": fcgi_rdwr_create() failed",
833 CloseHandle((HANDLE
)s
->listenFd
);
834 CloseHandle(s
->hPipeMutex
);
843 if (opcode
== PLEASE_START
) {
845 if(cjob
->id
==PLEASE_START
) {
847 if (dynamicAutoUpdate
) {
848 /* Check to see if the binary has changed. If so,
849 * kill the FCGI application processes, and
856 if ((stat(execName
, &stbuf
)==0) &&
858 if ((stat(cjob
->fs_path
, &stbuf
)==0) &&
860 (stbuf
.st_mtime
> s
->restartTime
)) {
861 /* kill old server(s) */
862 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
864 if (s
->procs
[i
].pid
> 0) {
865 fcgi_kill(s
->procs
[i
].pid
, SIGTERM
);
868 if (s
->procs
[i
].pid
!= INVALID_HANDLE_VALUE
) {
869 fcgi_kill((int) s
->procs
[i
].pid
, 1);
874 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
875 "FastCGI: restarting server \"%s\" processes, newer version found",
883 /* If dynamicAutoRestart, don't mark any new processes
884 * for starting because we probably got the
885 * PLEASE_START due to dynamicAutoUpdate and the ProcMgr
886 * will be restarting all of those we just killed.
888 if (dynamicAutoRestart
)
891 /* we've been asked to start a process--only start
892 * it if we're not already running at least one
897 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
898 if (s
->procs
[i
].state
== STATE_STARTED
)
901 /* if already running, don't start another one */
902 if (i
< dynamicMaxClassProcs
) {
919 /* If we've started one recently, don't register another */
920 time_passed
= now
- s
->restartTime
;
922 if (time_passed
< (int) s
->initStartDelay
923 && time_passed
< (int) s
->restartDelay
)
928 if ((fcgi_dynamic_total_proc_count
+ 1) > (int) dynamicMaxProcs
) {
930 * Extra instances should have been
931 * terminated beforehand, probably need
932 * to increase ProcessSlack parameter
934 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
935 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
936 "exceeded dynamicMaxProcs (%d)", s
->fs_path
, dynamicMaxProcs
);
939 /* find next free slot */
940 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
941 if (s
->procs
[i
].state
!= STATE_READY
942 && s
->procs
[i
].state
!= STATE_NEEDS_STARTING
943 && s
->procs
[i
].state
!= STATE_KILLED
)
948 if (s
->procs
[i
].pid
< 0)
950 if (s
->procs
[i
].pid
== INVALID_HANDLE_VALUE
)
953 if (time_passed
> (int)s
->restartDelay
) {
954 schedule_start(s
, i
);
959 else if (s
->procs
[i
].pid
== 0)
961 else if (s
->procs
[i
].pid
== (HANDLE
) 0)
964 if (time_passed
> (int)s
->initStartDelay
) {
965 schedule_start(s
, i
);
973 /* only record stats if we have a structure */
976 s
->totalConnTime
+= req_usec
;
977 s
->totalQueueTime
+= q_usec
;
979 s
->totalConnTime
+= cjob
->start_time
;
980 s
->totalQueueTime
+= cjob
->qsec
;
987 /* Cleanup job data */
1001 free(cjob
->fs_path
);
1009 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1010 "FastCGI: really bogus message: \"%s\"", ptr1
);
1011 ptr1
+= strlen(buf
);
1014 buflen
-= ptr1
- buf
;
1016 memmove(buf
, ptr1
, buflen
);
1020 ap_destroy_pool(tp
);
1024 *----------------------------------------------------------------------
1026 * dynamic_kill_idle_fs_procs
1028 * Implement a kill policy for the dynamic FastCGI applications.
1029 * We also update the data structures to reflect the changes.
1032 * Processes are marked for deletion possibly killed.
1034 *----------------------------------------------------------------------
1036 static void dynamic_kill_idle_fs_procs(void)
1039 struct FuncData
*funcData
= NULL
;
1040 unsigned long connTime
; /* server's smoothed running time, or
1041 * if that's 0, the current total */
1042 unsigned long totalTime
; /* maximum number of microseconds that all
1043 * of a server's running processes together
1044 * could have spent running since the
1046 double loadFactor
; /* percentage, 0-100, of totalTime that
1047 * the processes actually used */
1048 unsigned int i
, victims
= 0;
1050 const char *lockFileName
;
1054 pool
*tp
= ap_make_sub_pool(fcgi_config_pool
);
1056 /* pass 1 - locate and mark all victims */
1057 for(s
=fcgi_servers
; s
!=NULL
; s
=s
->next
) {
1058 /* Only kill dynamic apps */
1059 if (s
->directive
!= APP_CLASS_DYNAMIC
)
1062 /* If the number of non-victims is less than or equal to
1063 the minimum that may be running without being killed off,
1064 don't select any more victims. */
1065 if ((fcgi_dynamic_total_proc_count
- victims
) <= (int) dynamicMinProcs
) {
1069 connTime
= s
->smoothConnTime
? s
->smoothConnTime
: s
->totalConnTime
;
1070 totalTime
= (s
->numProcesses
)*(now
- fcgi_dynamic_epoch
)*1000000 + 1;
1072 /* XXX producing a heavy load with one client, I haven't been
1073 able to achieve a loadFactor greater than 0.5. Perhaps this
1074 should be scaled up by another order of magnitude or two. */
1075 loadFactor
= connTime
/totalTime
*100.0;
1077 if ((s
->numProcesses
> 1
1078 && s
->numProcesses
/(s
->numProcesses
- 1)*loadFactor
< dynamicThreshholdN
)
1079 || (s
->numProcesses
== 1 && loadFactor
< dynamicThreshhold1
))
1083 for (i
= 0; !got_one
&& i
< dynamicMaxClassProcs
; ++i
) {
1084 if (s
->procs
[i
].state
== STATE_NEEDS_STARTING
) {
1085 s
->procs
[i
].state
= STATE_READY
;
1088 else if (s
->procs
[i
].state
== STATE_VICTIM
|| s
->procs
[i
].state
== STATE_KILL
) {
1093 for (i
= 0; !got_one
&& i
< dynamicMaxClassProcs
; ++i
) {
1094 if (s
->procs
[i
].state
== STATE_STARTED
) {
1095 s
->procs
[i
].state
= STATE_KILL
;
1096 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1097 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination scheduled",
1098 s
->fs_path
, s
->procs
[i
].pid
);
1106 /* pass 2 - kill procs off */
1107 for(s
=fcgi_servers
; s
!=NULL
; s
=s
->next
) {
1108 /* Only kill dynamic apps */
1109 if (s
->directive
!= APP_CLASS_DYNAMIC
)
1112 for(i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1113 if (s
->procs
[i
].state
== STATE_KILL
) {
1115 lockFileName
= fcgi_util_socket_get_lock_filename(tp
, s
->socket_path
);
1116 if ((lockFd
= ap_popenf(tp
, lockFileName
, O_RDWR
, 0))<0) {
1118 * If we need to kill an application and the
1119 * corresponding lock file does not exist, then
1120 * that means we are in big trouble here
1122 /*@@@ this should be logged, but since all the lock
1123 * file stuff will be tossed, I'll leave it now */
1124 ap_pclosef(tp
, lockFd
);
1128 if (fcgi_get_exclusive_write_lock_no_wait(lockFd
) < 0) {
1130 if (fcgi_get_exclusive_write_lock_no_wait(s
->dynamic_lock
) < 0) {
1134 * Unable to lock the lockfile, indicative
1135 * of WS performing operation with the given
1136 * application class. The simplest solution
1137 * is to spawn off another process and block
1138 * on lock to kill it. This is under assumptions
1139 * that fork() is not very costly and this
1140 * situation occurs very rarely, which it should
1142 funcData
= ap_pcalloc(tp
, sizeof(struct FuncData
));
1144 funcData
->lockFileName
= lockFileName
;
1146 funcData
->lock
= s
->dynamic_lock
;
1148 funcData
->pid
= s
->procs
[i
].pid
;
1151 if((pid
=fork())<0) {
1152 /*@@@ this should be logged, but since all the lock
1153 * file stuff will be tossed, I'll leave it now */
1154 ap_pclosef(tp
, lockFd
);
1159 /* rename the process for ps - best we can easily */
1160 change_process_name("fcgiBlkKill");
1162 dynamic_blocking_kill(funcData
);
1165 s
->procs
[i
].state
= STATE_VICTIM
;
1166 ap_pclosef(tp
, lockFd
);
1169 CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)dynamic_blocking_kill
, (void *)funcData
, 0, &tid
);
1170 s
->procs
[i
].state
= STATE_VICTIM
;
1174 s
->procs
[i
].state
= STATE_VICTIM
;
1176 fcgi_kill(s
->procs
[i
].pid
, SIGTERM
);
1177 ap_pclosef(tp
, lockFd
);
1179 fcgi_kill((int)s
->procs
[i
].pid
, 1);
1185 ap_destroy_pool(tp
);
1190 // This is a little bogus, there's gotta be a better way to do this
1191 // Can we use WaitForMultipleObjects()
1192 #define FCGI_PROC_WAIT_TIME 100
1194 void child_wait_thread(void *dummy
) {
1196 DWORD dwRet
= WAIT_TIMEOUT
;
1201 while (!bTimeToDie
) {
1204 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1205 if (s
->directive
== APP_CLASS_EXTERNAL
|| s
->listenFd
< 0) {
1208 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1209 numChildren
= dynamicMaxClassProcs
;
1212 numChildren
= s
->numProcesses
;
1215 for (i
=0; i
< numChildren
; i
++) {
1216 if (s
->procs
[i
].pid
!= INVALID_HANDLE_VALUE
&& s
->procs
[i
].pid
!= (HANDLE
) 0) {
1217 /* timeout is currently set for 100 miliecond */
1218 /* it may need t longer or user customizable */
1219 dwRet
= WaitForSingleObject(s
->procs
[i
].pid
, FCGI_PROC_WAIT_TIME
);
1223 if (dwRet
!= WAIT_TIMEOUT
) {
1224 /* a child fs has died */
1225 /* mark the child as dead */
1227 if (s
->directive
== APP_CLASS_STANDARD
) {
1228 /* restart static app */
1229 s
->procs
[i
].state
= STATE_NEEDS_STARTING
;
1234 fcgi_dynamic_total_proc_count
--;
1236 if (s
->procs
[i
].state
== STATE_VICTIM
) {
1237 s
->procs
[i
].state
= STATE_KILLED
;
1241 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1244 if (dynamicAutoRestart
) {
1245 s
->procs
[i
].state
= STATE_NEEDS_STARTING
;
1248 s
->procs
[i
].state
= STATE_READY
;
1253 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1254 "FastCGI:%s server \"%s\" (pid %d) terminated",
1255 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1256 s
->fs_path
, s
->procs
[i
].pid
);
1258 CloseHandle(s
->procs
[i
].pid
);
1259 s
->procs
[i
].pid
= INVALID_HANDLE_VALUE
;
1261 /* wake up the main thread */
1262 SetEvent(fcgi_event_handles
[WAKE_EVENT
]);
1267 Sleep(waited
? 0 : FCGI_PROC_WAIT_TIME
);
1273 static void setup_signals(void)
1276 struct sigaction sa
;
1280 sigaddset(&mask
, SIGUSR2
);
1281 sigprocmask(SIG_BLOCK
, &mask
, NULL
);
1283 /* Setup handlers */
1285 sa
.sa_handler
= signal_handler
;
1286 sigemptyset(&sa
.sa_mask
);
1289 if (sigaction(SIGTERM
, &sa
, NULL
) < 0) {
1290 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1291 "sigaction(SIGTERM) failed");
1294 if (sigaction(SIGHUP
, &sa
, NULL
) < 0) {
1295 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1296 "sigaction(SIGHUP) failed");
1298 /* httpd graceful restart */
1299 if (sigaction(SIGUSR1
, &sa
, NULL
) < 0) {
1300 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1301 "sigaction(SIGUSR1) failed");
1303 /* read messages from request handlers - kill interval expired */
1304 if (sigaction(SIGALRM
, &sa
, NULL
) < 0) {
1305 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1306 "sigaction(SIGALRM) failed");
1308 if (sigaction(SIGCHLD
, &sa
, NULL
) < 0) {
1309 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1310 "sigaction(SIGCHLD) failed");
1316 int fcgi_pm_main(void *dummy
, child_info
*info
)
1318 void fcgi_pm_main(void *dummy
)
1331 HANDLE wait_thread
= INVALID_HANDLE_VALUE
;
1333 int callWaitPid
, callDynamicProcs
;
1337 // Add SystemRoot to the dyanmic environment
1338 char ** envp
= dynamicEnvp
;
1339 for (i
= 0; *envp
; ++i
) {
1342 fcgi_config_set_env_var(fcgi_config_pool
, dynamicEnvp
, &i
, "SystemRoot");
1345 reduce_priveleges();
1347 close(fcgi_pm_pipe
[1]);
1348 change_process_name("fcgi-pm");
1352 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
1353 "FastCGI: suEXEC mechanism enabled (wrapper: %s)", fcgi_suexec
);
1357 /* Initialize AppClass */
1358 tp
= ap_make_sub_pool(fcgi_config_pool
);
1359 for(s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1360 if (s
->directive
== APP_CLASS_EXTERNAL
)
1364 if (s
->socket_path
) {
1365 SECURITY_ATTRIBUTES sa
;
1367 sa
.nLength
= sizeof(sa
);
1368 sa
.lpSecurityDescriptor
= NULL
;
1369 sa
.bInheritHandle
= TRUE
;
1371 s
->listenFd
= (int)CreateNamedPipe(s
->socket_path
, PIPE_ACCESS_DUPLEX
,
1372 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
1373 PIPE_UNLIMITED_INSTANCES
, 4096,4096,0, &sa
);
1374 if ((HANDLE
)s
->listenFd
== INVALID_HANDLE_VALUE
) {
1375 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1376 "FastCGI: server \"%s\" disabled, CreateNamedPipe() failed", s
->fs_path
);
1380 s
->hPipeMutex
= ap_create_mutex(NULL
);
1382 if (s
->hPipeMutex
== NULL
) {
1383 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1384 "FastCGI: server \"%s\" disabled, ap_create_mutex() failed", s
->fs_path
);
1385 CloseHandle((HANDLE
)s
->listenFd
);
1386 s
->listenFd
= (int) INVALID_HANDLE_VALUE
;
1390 SetHandleInformation(s
->hPipeMutex
, HANDLE_FLAG_INHERIT
, TRUE
);
1395 /* Create the socket */
1396 s
->listenFd
= ap_psocket(fcgi_config_pool
, s
->socket_addr
->sa_family
, SOCK_STREAM
, 0);
1397 if (s
->listenFd
< 0) {
1398 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1399 "FastCGI: server \"%s\" disabled, socket() failed", s
->fs_path
);
1403 /* bind() and listen() */
1404 err
= bind_n_listen(tp
, s
->socket_addr
, s
->socket_addr_len
,
1405 s
->listenQueueDepth
, s
->listenFd
);
1407 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1408 "FastCGI: server \"%s\" disabled: %s",
1410 ap_pclosesocket(fcgi_config_pool
, s
->listenFd
);
1416 for (i
= 0; i
< s
->numProcesses
; i
++)
1417 s
->procs
[i
].state
= STATE_NEEDS_STARTING
;
1420 ap_destroy_pool(tp
);
1422 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1423 "FastCGI: process manager initialized (pid %ld)", (long)getpid());
1428 * Loop until SIGTERM
1431 int sleepSeconds
= min(dynamicKillInterval
, dynamicUpdateInterval
);
1438 unsigned int numChildren
;
1441 * If we came out of sigsuspend() for any reason other than
1442 * SIGALRM, pick up where we left off.
1445 sleepSeconds
= alarmLeft
;
1448 * Examine each configured AppClass for a process that needs
1449 * starting. Compute the earliest time when the start should
1450 * be attempted, starting it now if the time has passed. Also,
1451 * remember that we do NOT need to restart externally managed
1452 * FastCGI applications.
1454 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1455 if (s
->directive
== APP_CLASS_EXTERNAL
|| s
->listenFd
< 0) {
1458 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1459 numChildren
= dynamicMaxClassProcs
;
1461 numChildren
= s
->numProcesses
;
1464 for (i
= 0; i
< numChildren
; i
++) {
1466 if ((s
->procs
[i
].pid
<= 0) &&
1468 if (((s
->procs
[i
].pid
<= (HANDLE
) 0) || (s
->procs
[i
].pid
== INVALID_HANDLE_VALUE
)) &&
1470 (s
->procs
[i
].state
== STATE_NEEDS_STARTING
))
1474 if (s
->procs
[i
].pid
== 0) {
1475 restartTime
= s
->restartTime
+ s
->initStartDelay
;
1477 restartTime
= s
->restartTime
+ s
->restartDelay
;
1480 if(restartTime
<= now
) {
1482 int restart
= (s
->procs
[i
].pid
== INVALID_HANDLE_VALUE
);
1484 int restart
= (s
->procs
[i
].pid
< 0);
1487 s
->restartTime
= now
;
1490 if (caughtSigTerm
) {
1491 goto ProcessSigTerm
;
1494 s
->procs
[i
].pid
= spawn_fs_process(s
);
1495 if (s
->procs
[i
].pid
<= 0) {
1496 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1497 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1498 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1501 sleepSeconds
= min(sleepSeconds
,
1502 max((int) s
->restartDelay
, FCGI_MIN_EXEC_RETRY_DELAY
));
1504 ap_assert(s
->procs
[i
].pid
< 0);
1508 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1510 fcgi_dynamic_total_proc_count
++;
1513 s
->procs
[i
].state
= STATE_STARTED
;
1518 if (fcgi_suexec
!= NULL
) {
1519 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1520 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1521 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1522 s
->fs_path
, (long)s
->uid
, (long)s
->gid
,
1523 restart
? "re" : "", (long)s
->procs
[i
].pid
);
1526 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1527 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1528 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1529 s
->fs_path
, restart
? "re" : "", (long)s
->procs
[i
].pid
);
1531 ap_assert(s
->procs
[i
].pid
> 0);
1533 sleepSeconds
= min(sleepSeconds
, restartTime
- now
);
1541 goto ProcessSigTerm
;
1543 if((!caughtSigChld
) && (!caughtSigUsr2
)) {
1546 alarm(sleepSeconds
);
1549 FD_SET(fcgi_pm_pipe
[0], &rfds
);
1550 read_ready
= ap_select(fcgi_pm_pipe
[0] + 1, &rfds
, NULL
, NULL
, NULL
);
1552 alarmLeft
= alarm(0);
1554 callWaitPid
= caughtSigChld
;
1555 caughtSigChld
= FALSE
;
1556 callDynamicProcs
= caughtSigUsr2
;
1557 caughtSigUsr2
= FALSE
;
1562 * Dynamic fcgi process management
1564 if((callDynamicProcs
) || (!callWaitPid
)) {
1565 dynamic_read_msgs(read_ready
);
1566 if(fcgi_dynamic_epoch
== 0) {
1567 fcgi_dynamic_epoch
= now
;
1569 if(((long)(now
-fcgi_dynamic_epoch
)>=dynamicKillInterval
) ||
1570 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
)>=dynamicMaxProcs
)) {
1571 dynamic_kill_idle_fs_procs();
1572 fcgi_dynamic_epoch
= now
;
1580 /* We've caught SIGCHLD, so find out who it was using waitpid,
1581 * write a log message and update its data structure. */
1585 goto ProcessSigTerm
;
1587 childPid
= waitpid(-1, &waitStatus
, WNOHANG
);
1589 if (childPid
== -1 || childPid
== 0)
1592 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1593 if (s
->directive
== APP_CLASS_EXTERNAL
)
1596 if (s
->directive
== APP_CLASS_DYNAMIC
)
1597 numChildren
= dynamicMaxClassProcs
;
1599 numChildren
= s
->numProcesses
;
1601 for (i
= 0; i
< numChildren
; i
++) {
1602 if (s
->procs
[i
].pid
== childPid
)
1607 /* @@@ This (comment) needs to go away when dynamic gets cleaned up.
1608 * If we get to this point, we have detected the
1609 * termination of the process that was spawned off by
1610 * the process manager to do a blocking kill above. */
1614 s
->procs
[i
].pid
= -1;
1616 if (s
->directive
== APP_CLASS_STANDARD
) {
1617 /* Always restart static apps */
1618 s
->procs
[i
].state
= STATE_NEEDS_STARTING
;
1623 fcgi_dynamic_total_proc_count
--;
1625 if (s
->procs
[i
].state
== STATE_VICTIM
) {
1626 s
->procs
[i
].state
= STATE_KILLED
;
1629 /* A dynamic app died or exited without provacation from the PM */
1632 if (dynamicAutoRestart
|| s
->numProcesses
<= 0)
1633 s
->procs
[i
].state
= STATE_NEEDS_STARTING
;
1635 s
->procs
[i
].state
= STATE_READY
;
1639 if (WIFEXITED(waitStatus
)) {
1640 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1641 "FastCGI:%s server \"%s\" (pid %d) terminated by calling exit with status '%d'",
1642 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1643 s
->fs_path
, (int)childPid
, WEXITSTATUS(waitStatus
));
1645 else if (WIFSIGNALED(waitStatus
)) {
1646 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1647 "FastCGI:%s server \"%s\" (pid %d) terminated due to uncaught signal '%d' (%s)%s",
1648 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1649 s
->fs_path
, (int)childPid
, WTERMSIG(waitStatus
), SYS_SIGLIST
[WTERMSIG(waitStatus
)],
1651 WCOREDUMP(waitStatus
) ? ", a core file may have been generated" : "");
1656 else if (WIFSTOPPED(waitStatus
)) {
1657 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1658 "FastCGI:%s server \"%s\" (pid %d) stopped due to uncaught signal '%d' (%s)",
1659 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1660 s
->fs_path
, (int)childPid
, WTERMSIG(waitStatus
), SYS_SIGLIST
[WTERMSIG(waitStatus
)]);
1662 } /* for (;;), waitpid() */
1665 DWORD tid
; /* Thread id */
1667 /* Start the child wait thread */
1668 wait_thread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)child_wait_thread
, NULL
, 0, &tid
);
1672 /* wait for an event to occur or timer expires */
1673 expire
= time(NULL
) + sleepSeconds
;
1674 dwRet
= WaitForMultipleObjects(3, (HANDLE
*) fcgi_event_handles
, FALSE
, sleepSeconds
* 1000);
1676 if (dwRet
== WAIT_FAILED
) {
1677 /* There is something seriously wrong here */
1678 ap_log_error(APLOG_MARK
,APLOG_CRIT
|APLOG_WIN32ERROR
, fcgi_apache_main_server
,
1679 "FastDGI: WaitForMultipleObjects on event handles -- pm is shuting down");
1683 if (dwRet
!= WAIT_TIMEOUT
) {
1687 alarmLeft
= expire
- now
;
1691 * Dynamic fcgi process management
1693 if ((dwRet
== MBOX_EVENT
) || (dwRet
== WAIT_TIMEOUT
)) {
1694 if (dwRet
== MBOX_EVENT
) {
1700 dynamic_read_msgs(read_ready
);
1702 if(fcgi_dynamic_epoch
== 0) {
1703 fcgi_dynamic_epoch
= now
;
1706 if (((long)(now
-fcgi_dynamic_epoch
) >= (int)dynamicKillInterval
) ||
1707 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
) >= dynamicMaxProcs
)) {
1708 dynamic_kill_idle_fs_procs();
1709 fcgi_dynamic_epoch
= now
;
1713 else if (dwRet
== WAKE_EVENT
) {
1716 else if (dwRet
== TERM_EVENT
) {
1717 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
1718 "FastCGI: Termination event received process manager shutting down");
1721 dwRet
= WaitForSingleObject(wait_thread
, INFINITE
);
1722 goto ProcessSigTerm
;
1725 // Have an received an unknown event - should not happen
1726 ap_log_error(APLOG_MARK
,APLOG_CRIT
|APLOG_WIN32ERROR
, fcgi_apache_main_server
,
1727 "FastCGI: WaitForMultipleobjects return an unrecognized event");
1729 dwRet
= WaitForSingleObject(wait_thread
, INFINITE
);
1730 goto ProcessSigTerm
;
1733 } /* for (;;), the whole shoot'n match */
1737 * Kill off the children, then exit.
1739 while (fcgi_servers
!= NULL
) {
1740 kill_fs_procs(fcgi_config_pool
, fcgi_servers
);
1751 int fcgi_pm_add_job(fcgi_pm_job
*new_job
) {
1753 if (new_job
== NULL
)
1756 ap_acquire_mutex(fcgi_dynamic_mbox_mutex
);
1757 new_job
->next
= fcgi_dynamic_mbox
;
1758 fcgi_dynamic_mbox
= new_job
;
1759 ap_release_mutex(fcgi_dynamic_mbox_mutex
);