2 * $Id: fcgi_pm.c,v 1.30 2000/05/24 01:51:52 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 */
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];
35 static void append_mutex_to_env(char ** envp
, HANDLE pipe_mutex
) {
40 for (i
= 0; *envp
; ++i
) {
44 mutex
= ap_psprintf(fcgi_config_pool
, "_FCGI_MUTEX_=%d", (int) pipe_mutex
);
45 fcgi_config_set_env_var(fcgi_config_pool
, env
, &i
, mutex
);
52 static int seteuid_root(void)
54 int rc
= seteuid((uid_t
)0);
56 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
57 "FastCGI: seteuid(0) failed");
62 static int seteuid_user(void)
64 int rc
= seteuid(ap_user_id
);
66 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
67 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id
);
73 static int fcgi_kill(ServerProcess
*process
, int sig
)
82 rc
= kill(process
->pid
, sig
);
89 rc
= TerminateProcess(process
->handle
, sig
);
95 /*******************************************************************************
96 * Send SIGTERM to each process in the server class, remove socket and lock
97 * file if appropriate. Currently this is only called when the PM is shutting
98 * down and thus memory isn't freed and sockets and files aren't closed.
100 static void kill_fs_procs(pool
*p
, fcgi_server
*s
)
102 ServerProcess
*proc
= s
->procs
;
105 if (s
->directive
== APP_CLASS_DYNAMIC
)
106 numChildren
= dynamicMaxClassProcs
;
108 numChildren
= s
->numProcesses
;
110 for (i
= 0; i
< numChildren
; i
++, proc
++) {
113 fcgi_kill(proc
, SIGTERM
);
117 if (proc
->handle
!= INVALID_HANDLE_VALUE
) {
119 CloseHandle(proc
->handle
);
120 proc
->handle
= INVALID_HANDLE_VALUE
;
126 /* Remove the dead lock file */
127 if (s
->directive
== APP_CLASS_DYNAMIC
) {
129 const char *lockFileName
= fcgi_util_socket_get_lock_filename(p
, s
->socket_path
);
131 if (unlink(lockFileName
) != 0) {
132 ap_log_error(FCGI_LOG_ERR
, fcgi_apache_main_server
,
133 "FastCGI: unlink() failed to remove lock file \"%s\" for (dynamic) server \"%s\"",
134 lockFileName
, s
->fs_path
);
137 fcgi_rdwr_destroy(s
->dynamic_lock
);
141 /* Remove the socket file */
142 if (s
->socket_path
!= NULL
&& s
->directive
!= APP_CLASS_EXTERNAL
) {
144 if (unlink(s
->socket_path
) != 0) {
145 ap_log_error(FCGI_LOG_ERR
, fcgi_apache_main_server
,
146 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
148 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "", s
->fs_path
);
151 CloseHandle((HANDLE
)s
->listenFd
);
154 fcgi_servers
= s
->next
;
157 /*******************************************************************************
158 * Bind an address to a socket and set it to listen for incoming connects.
159 * The error messages are allocated from the pool p, use temp storage.
160 * Don't forget to close the socket, if an error occurs.
162 static const char *bind_n_listen(pool
*p
, struct sockaddr
*socket_addr
,
163 int socket_addr_len
, int backlog
, int sock
)
166 if (socket_addr
->sa_family
== AF_UNIX
) {
167 /* Remove any existing socket file.. just in case */
168 unlink(((struct sockaddr_un
*)socket_addr
)->sun_path
);
174 setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, (char *)&flag
, sizeof(flag
));
177 /* Bind it to the socket_addr */
178 if (bind(sock
, socket_addr
, socket_addr_len
) != 0)
179 return "bind() failed";
182 /* Twiddle permissions */
183 if (socket_addr
->sa_family
== AF_UNIX
) {
184 if (chmod(((struct sockaddr_un
*)socket_addr
)->sun_path
, S_IRUSR
| S_IWUSR
))
185 return "chmod() of socket failed";
190 if (listen(sock
, backlog
) != 0)
191 return "listen() failed";
197 *----------------------------------------------------------------------
199 * dynamic_blocking_kill
201 * Block on the lock file until it is available, and then
202 * issue a kill signal to the corresponding application.
203 * Since this function is executed in the child process,
204 * _exit() is called upon completion.
207 * Pointer to the data structure containing a process id to
208 * issue a signal to and the full pathname to the lockfile
209 * that needs to be locked before the issue of the signal.
212 * Memory is allocated by the caller, but is freed by this
215 *----------------------------------------------------------------------
217 static void dynamic_blocking_kill(void *data
)
219 struct FuncData
*funcData
= (struct FuncData
*)data
;
223 ap_assert(funcData
->lockFileName
);
224 if ((lockFd
= open(funcData
->lockFileName
, O_RDWR
)) < 0) {
225 /* There is something terribly wrong here */
227 if (fcgi_wait_for_shared_write_lock(lockFd
) < 0) {
228 /* This is a major problem */
230 fcgi_kill(funcData
->process
, SIGTERM
);
233 /* exit() may flush stdio buffers inherited from the parent. */
236 ap_assert(funcData
->lock
);
237 if (fcgi_wait_for_shared_write_lock(funcData
->lock
) < 0) {
238 // This is a major problem
241 fcgi_kill(funcData
->process
, 1);
242 fcgi_rdwr_unlock(funcData
->lock
, WRITER
);
249 *----------------------------------------------------------------------
253 * The FastCGI process manager, which runs as a separate
254 * process responsible for:
255 * - Starting all the FastCGI proceses.
256 * - Restarting any of these processes that die (indicated
258 * - Catching SIGTERM and relaying it to all the FastCGI
259 * processes before exiting.
262 * Uses global variable fcgi_servers.
270 *----------------------------------------------------------------------
273 static int caughtSigTerm
= FALSE
;
274 static int caughtSigChld
= FALSE
;
275 static int caughtSigUsr2
= FALSE
;
277 static void signal_handler(int signo
)
279 if ((signo
== SIGTERM
) || (signo
== SIGUSR1
) || (signo
== SIGHUP
)) {
280 /* SIGUSR1 & SIGHUP are sent by apache to its process group
281 * when apache get 'em. Apache follows up (1.2.x) with attacks
282 * on each of its child processes, but we've got the KillMgr
283 * sitting between us so we never see the KILL. The main loop
284 * in ProcMgr also checks to see if the KillMgr has terminated,
285 * and if it has, we handl it as if we should shutdown too. */
286 caughtSigTerm
= TRUE
;
287 } else if(signo
== SIGCHLD
) {
288 caughtSigChld
= TRUE
;
289 } else if(signo
== SIGALRM
) {
290 caughtSigUsr2
= TRUE
;
296 *----------------------------------------------------------------------
298 * spawn_fs_process --
300 * Fork and exec the specified fcgi process.
303 * 0 for successful fork, -1 for failed fork.
305 * In case the child fails before or in the exec, the child
306 * obtains the error log by calling getErrLog, logs
307 * the error, and exits with exit status = errno of
308 * the failed system call.
311 * Child process created.
313 *----------------------------------------------------------------------
316 static pid_t
spawn_fs_process(fcgi_server
*fs
, ServerProcess
*process
)
322 char *dnEnd
, *failedSysCall
;
329 /* We're the child. We're gonna exec() so pools don't matter. */
331 dnEnd
= strrchr(fs
->fs_path
, '/');
335 dirName
= ap_pcalloc(fcgi_config_pool
, dnEnd
- fs
->fs_path
+ 1);
336 dirName
= memcpy(dirName
, fs
->fs_path
, dnEnd
- fs
->fs_path
);
338 if (chdir(dirName
) < 0) {
339 failedSysCall
= "chdir()";
340 goto FailedSystemCallExit
;
344 /* OS/2 dosen't support nice() */
345 if (fs
->processPriority
!= 0) {
346 if (nice(fs
->processPriority
) == -1) {
347 failedSysCall
= "nice()";
348 goto FailedSystemCallExit
;
353 /* Open the listenFd on spec'd fd */
354 if (fs
->listenFd
!= FCGI_LISTENSOCK_FILENO
)
355 dup2(fs
->listenFd
, FCGI_LISTENSOCK_FILENO
);
357 /* Close all other open fds, except stdout/stderr. Leave these two open so
358 * FastCGI applications don't have to find and fix ALL 3rd party libs that
359 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
360 * main server error_log - @@@ provide a directive control where this goes.
362 ap_error_log2stderr(fcgi_apache_main_server
);
363 dup2(STDERR_FILENO
, STDOUT_FILENO
);
364 for (i
= 0; i
< MAX_OPEN_FDS
; i
++) {
365 if (i
!= FCGI_LISTENSOCK_FILENO
&& i
!= STDERR_FILENO
&& i
!= STDOUT_FILENO
) {
370 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
371 * install its own handler. */
372 signal(SIGPIPE
, SIG_IGN
);
374 if (fcgi_suexec
!= NULL
) {
375 char *shortName
= strrchr(fs
->fs_path
, '/') + 1;
377 /* Relinquish our root real uid powers */
382 execle(fcgi_suexec
, fcgi_suexec
, fs
->username
, fs
->group
, shortName
, NULL
, fs
->envp
);
383 } while (errno
== EINTR
);
387 execle(fs
->fs_path
, fs
->fs_path
, NULL
, fs
->envp
);
388 } while (errno
== EINTR
);
391 failedSysCall
= "execle()";
393 FailedSystemCallExit
:
394 fprintf(stderr
, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
395 fs
->fs_path
, (long) getpid(), failedSysCall
, strerror(errno
));
398 /* avoid an irrelevant compiler warning */
403 /* Adapted from Apache's util_script.c ap_call_exec() */
404 char *interpreter
= NULL
;
406 char *exename
= NULL
;
408 char *quoted_filename
;
410 char *pEnvBlock
, *pNext
;
415 file_type_e fileType
;
418 PROCESS_INFORMATION pi
;
423 pool
* tp
= ap_make_sub_pool(fcgi_config_pool
);
425 memset(&si
, 0, sizeof(si
));
426 memset(&pi
, 0, sizeof(pi
));
427 memset(&r
, 0, sizeof(r
));
429 // Can up a fake request to pass to ap_get_win32_interpreter()
430 r
.per_dir_config
= fcgi_apache_main_server
->lookup_defaults
;
431 r
.server
= fcgi_apache_main_server
;
432 r
.filename
= (char *) fs
->fs_path
;
435 fileType
= ap_get_win32_interpreter(&r
, &interpreter
);
437 if (fileType
== eFileTypeUNKNOWN
) {
438 ap_log_error(APLOG_MARK
, APLOG_ERR
|APLOG_NOERRNO
, fcgi_apache_main_server
,
439 "FastCGI: %s is not executable; ensure interpreted scripts have "
440 "\"#!\" as their first line",
447 * We have the interpreter (if there is one) and we have
448 * the arguments (if there are any).
449 * Build the command string to pass to CreateProcess.
451 quoted_filename
= ap_pstrcat(tp
, "\"", fs
->fs_path
, "\"", NULL
);
452 if (interpreter
&& *interpreter
) {
453 pCommand
= ap_pstrcat(tp
, interpreter
, " ", quoted_filename
, NULL
);
456 pCommand
= quoted_filename
;
460 * Make child process use hPipeOutputWrite as standard out,
461 * and make sure it does not show on screen.
464 si
.dwFlags
= STARTF_USESHOWWINDOW
| STARTF_USESTDHANDLES
;
465 si
.wShowWindow
= SW_HIDE
;
466 si
.hStdInput
= (HANDLE
) fs
->listenFd
;
468 // XXX These should be open to the error_log
469 si
.hStdOutput
= INVALID_HANDLE_VALUE
;
470 si
.hStdError
= INVALID_HANDLE_VALUE
;
473 * Win32's CreateProcess call requires that the environment
474 * be passed in an environment block, a null terminated block of
475 * null terminated strings.
479 while (fs
->envp
[i
]) {
480 iEnvBlockLen
+= strlen(fs
->envp
[i
]) + 1;
484 pEnvBlock
= (char *)ap_pcalloc(tp
,iEnvBlockLen
);
488 while (fs
->envp
[i
]) {
489 strcpy(pNext
, fs
->envp
[i
]);
490 pNext
= pNext
+ strlen(pNext
) + 1;
494 if (CreateProcess(NULL
, pCommand
, NULL
, NULL
, TRUE
,
497 ap_make_dirstr_parent(tp
, fs
->fs_path
),
499 if (fileType
== eFileTypeEXE16
) {
500 /* Hack to get 16-bit CGI's working. It works for all the
501 * standard modules shipped with Apache. pi.dwProcessId is 0
502 * for 16-bit CGIs and all the Unix specific code that calls
503 * ap_call_exec interprets this as a failure case. And we can't
504 * use -1 either because it is mapped to 0 by the caller.
509 pid
= pi
.dwProcessId
;
510 process
->handle
= pi
.hProcess
;
511 CloseHandle(pi
.hThread
);
523 static void reduce_priveleges(void)
531 /* Get username if passed as a uid */
532 if (ap_user_name
[0] == '#') {
533 uid_t uid
= atoi(&ap_user_name
[1]);
534 struct passwd
*ent
= getpwuid(uid
);
537 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
538 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
539 "you probably need to modify the User directive", (unsigned)uid
);
548 if (setgid(ap_group_id
) == -1) {
549 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
550 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id
);
554 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
556 /* Initialize supplementary groups */
557 if (initgroups(name
, ap_group_id
) == -1) {
558 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
559 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
560 name
, (unsigned)ap_group_id
);
567 if (seteuid_user() == -1) {
568 ap_log_error(FCGI_LOG_ALERT_NOERRNO
, fcgi_apache_main_server
,
569 "FastCGI: process manager exiting, failed to reduce priveleges");
574 if (setuid(ap_user_id
) == -1) {
575 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
576 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id
);
583 * Change the name of this process - best we can easily.
585 static void change_process_name(const char * const name
)
587 strncpy(ap_server_argv0
, name
, strlen(ap_server_argv0
));
591 static void schedule_start(fcgi_server
*s
, int proc
)
593 s
->procs
[proc
].state
= STATE_NEEDS_STARTING
;
594 if (proc
== (int)dynamicMaxClassProcs
- 1) {
595 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
596 "FastCGI: scheduled the %sstart of the last (dynamic) server "
597 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
598 s
->procs
[proc
].pid
? "re" : "", s
->fs_path
, dynamicMaxClassProcs
);
603 *----------------------------------------------------------------------
607 * Removes the records written by request handlers and decodes them.
608 * We also update the data structures to reflect the changes.
610 *----------------------------------------------------------------------
613 static void dynamic_read_msgs(int read_ready
)
619 static int buflen
= 0;
620 static char buf
[FCGI_MSGS_BUFSIZE
+ 1];
621 char *ptr1
, *ptr2
, opcode
;
622 char execName
[FCGI_MAXPATH
+ 1];
623 char user
[MAX_USER_NAME_LEN
+ 2];
624 char group
[MAX_GID_CHAR_LEN
+ 1];
625 unsigned long q_usec
= 0UL, req_usec
= 0UL;
627 fcgi_pm_job
*joblist
= NULL
;
628 fcgi_pm_job
*cjob
= NULL
;
629 SECURITY_ATTRIBUTES sa
;
635 user
[MAX_USER_NAME_LEN
+ 1] = group
[MAX_GID_CHAR_LEN
] = '\0';
639 * To prevent the idle application from running indefinitely, we
640 * check the timer and if it is expired, we recompute the values
641 * for each running application class. Then, when REQ_COMPLETE
642 * message is recieved, only updates are made to the data structures.
644 if (fcgi_dynamic_last_analyzed
== 0) {
645 fcgi_dynamic_last_analyzed
= now
;
647 if ((now
- fcgi_dynamic_last_analyzed
) >= (int)dynamicUpdateInterval
) {
648 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
649 if (s
->directive
!= APP_CLASS_DYNAMIC
)
651 /* XXX what does this adjustment do? */
652 fcgi_dynamic_last_analyzed
+= (((long)(now
-fcgi_dynamic_last_analyzed
)/dynamicUpdateInterval
)*dynamicUpdateInterval
);
653 s
->smoothConnTime
= (unsigned long) ((1.0-dynamicGain
)*s
->smoothConnTime
+ dynamicGain
*s
->totalConnTime
);
654 s
->totalConnTime
= 0UL;
655 s
->totalQueueTime
= 0UL;
659 if (read_ready
<= 0) {
664 rc
= read(fcgi_pm_pipe
[0], (void *)(buf
+ buflen
), FCGI_MSGS_BUFSIZE
- buflen
);
666 if (!caughtSigTerm
) {
667 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
668 "FastCGI: read() from pipe failed (%d)", rc
);
675 /* Obtain the data from the fcgi_dynamic_mbox file */
676 ap_acquire_mutex(fcgi_dynamic_mbox_mutex
);
678 if (fcgi_dynamic_mbox
== NULL
) {
682 joblist
= fcgi_dynamic_mbox
;
683 fcgi_dynamic_mbox
= NULL
;
686 ap_release_mutex(fcgi_dynamic_mbox_mutex
);
691 tp
= ap_make_sub_pool(fcgi_config_pool
);
694 for (ptr1
= buf
; ptr1
; ptr1
= ptr2
) {
697 ptr2
= strchr(ptr1
, '*');
709 if (sscanf(ptr1
, "%c %s %16s %15s",
710 &opcode
, execName
, user
, group
) != 4)
716 if (sscanf(ptr1
, "%c %s %16s %15s",
717 &opcode
, execName
, user
, group
) != 4)
723 if (sscanf(ptr1
, "%c %s %16s %15s %lu %lu",
724 &opcode
, execName
, user
, group
, &q_usec
, &req_usec
) != 6)
735 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
736 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1
);
740 /* Update data structures for processing */
741 while (cjob
!= NULL
) {
742 joblist
= cjob
->next
;
746 s
= fcgi_util_fs_get(execName
, user
, group
);
748 s
= fcgi_util_fs_get(cjob
->fs_path
, cjob
->user
, cjob
->group
);
752 if (s
==NULL
&& opcode
!= REQ_COMPLETE
)
754 if (s
==NULL
&& cjob
->id
!= REQ_COMPLETE
)
762 const char *err
, *lockPath
;
765 /* Create a perm subpool to hold the new server data,
766 * we can destroy it if something doesn't pan out */
767 sp
= ap_make_sub_pool(fcgi_config_pool
);
769 /* Create a new "dynamic" server */
770 s
= fcgi_util_fs_new(sp
);
771 s
->directive
= APP_CLASS_DYNAMIC
;
772 s
->restartDelay
= dynamicRestartDelay
;
773 s
->listenQueueDepth
= dynamicListenQueueDepth
;
774 s
->initStartDelay
= dynamicInitStartDelay
;
776 s
->envp
= dynamicEnvp
;
777 ap_getparents(execName
);
778 ap_no2slash(execName
);
779 s
->fs_path
= ap_pstrdup(sp
, execName
);
781 // Create our own copy of the env so we can _FCGI_MUTEX_
783 for (i
= 0; *envp
; ++i
) {
786 s
->envp
= ap_pcalloc(sp
, sizeof(char *) * (i
+ 2));
787 memcpy(s
->envp
, dynamicEnvp
, sizeof(char *) * i
);
789 ap_getparents(cjob
->fs_path
);
790 ap_no2slash(cjob
->fs_path
);
791 s
->fs_path
= ap_pstrdup(sp
, cjob
->fs_path
);
793 s
->procs
= fcgi_util_fs_create_procs(sp
, dynamicMaxClassProcs
);
796 /* Create socket file's path */
797 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, execName
, user
, group
);
798 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
800 /* Create sockaddr, prealloc it so it won't get created in tp */
801 s
->socket_addr
= ap_pcalloc(sp
, sizeof(struct sockaddr_un
));
802 err
= fcgi_util_socket_make_domain_addr(tp
, (struct sockaddr_un
**)&s
->socket_addr
,
803 &s
->socket_addr_len
, s
->socket_path
);
805 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
806 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
810 /* Create the socket */
811 if ((s
->listenFd
= ap_psocket(sp
, s
->socket_addr
->sa_family
, SOCK_STREAM
, 0)) < 0) {
812 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
813 "FastCGI: can't create (dynamic) server \"%s\": socket() failed", execName
);
817 /* bind() and listen() */
818 err
= bind_n_listen(tp
, s
->socket_addr
, s
->socket_addr_len
,
819 s
->listenQueueDepth
, s
->listenFd
);
821 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
822 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
826 /* Create the lock file */
827 lockPath
= fcgi_util_socket_get_lock_filename(tp
, s
->socket_path
);
828 fd
= ap_popenf(tp
, lockPath
,
829 O_WRONLY
| O_CREAT
| O_TRUNC
, S_IRUSR
| S_IWUSR
);
831 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
832 "FastCGI: can't create (dynamic) server \"%s\": can't open lock file \"%s\": popenf() failed",
838 /* If suexec is being used, config user/group info */
840 if (user
[0] == '~') {
841 /* its a user dir uri, the rest is a username, not a uid */
842 struct passwd
*pw
= getpwnam(&user
[1]);
845 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
846 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for suexec: getpwnam(%s) failed",
851 s
->user
= ap_pstrdup(sp
, user
);
852 s
->username
= s
->user
;
855 s
->group
= ap_psprintf(sp
, "%ld", (long)s
->gid
);
860 s
->uid
= (uid_t
)atol(user
);
861 pw
= getpwuid(s
->uid
);
863 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
864 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for suexec: getwpuid(%ld) failed",
865 execName
, (long)s
->uid
);
868 s
->user
= ap_pstrdup(sp
, user
);
869 s
->username
= ap_pstrdup(sp
, pw
->pw_name
);
871 s
->gid
= (gid_t
)atol(group
);
872 s
->group
= ap_pstrdup(sp
, group
);
876 /* Create socket file's path */
877 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, cjob
->fs_path
, cjob
->user
, cjob
->group
);
878 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
880 /* Create Named Pipe and I/O mutex*/
881 sa
.nLength
= sizeof(sa
);
882 sa
.lpSecurityDescriptor
= NULL
;
883 sa
.bInheritHandle
= TRUE
;
885 s
->listenFd
= (int)CreateNamedPipe(s
->socket_path
,
887 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
888 PIPE_UNLIMITED_INSTANCES
, 4096,4096,0, &sa
);
890 if ((HANDLE
)s
->listenFd
== INVALID_HANDLE_VALUE
) {
891 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
892 "FASTCGI: can't create (dynamic) server \"%s\": CreateNamePipe() failed",
897 s
->hPipeMutex
= ap_create_mutex(NULL
);
899 if (s
->hPipeMutex
== NULL
) {
900 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
901 "FastCGI: can't create (dynamic) server \"%s\": ap_create_mutex() failed", cjob
->fs_path
);
902 CloseHandle((HANDLE
)s
->listenFd
);
906 SetHandleInformation(s
->hPipeMutex
, HANDLE_FLAG_INHERIT
, TRUE
);
908 append_mutex_to_env(s
->envp
, s
->hPipeMutex
);
910 /* Create the application lock */
911 if ((s
->dynamic_lock
= fcgi_rdwr_create()) == NULL
) {
912 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
913 "FastCGI: can;t create (dynamic) server \"%s\": fcgi_rdwr_create() failed",
915 CloseHandle((HANDLE
)s
->listenFd
);
916 CloseHandle(s
->hPipeMutex
);
925 if (opcode
== PLEASE_START
) {
927 if(cjob
->id
==PLEASE_START
) {
929 if (dynamicAutoUpdate
) {
930 /* Check to see if the binary has changed. If so,
931 * kill the FCGI application processes, and
938 if ((stat(execName
, &stbuf
)==0) &&
940 if ((stat(cjob
->fs_path
, &stbuf
)==0) &&
942 (stbuf
.st_mtime
> s
->restartTime
)) {
943 /* kill old server(s) */
944 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
945 if (s
->procs
[i
].pid
> 0) {
946 fcgi_kill(&s
->procs
[i
], SIGTERM
);
950 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
951 "FastCGI: restarting server \"%s\" processes, newer version found",
959 /* If dynamicAutoRestart, don't mark any new processes
960 * for starting because we probably got the
961 * PLEASE_START due to dynamicAutoUpdate and the ProcMgr
962 * will be restarting all of those we just killed.
964 if (dynamicAutoRestart
)
967 /* we've been asked to start a process--only start
968 * it if we're not already running at least one
973 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
974 if (s
->procs
[i
].state
== STATE_STARTED
)
977 /* if already running, don't start another one */
978 if (i
< dynamicMaxClassProcs
) {
995 /* If we've started one recently, don't register another */
996 time_passed
= now
- s
->restartTime
;
998 if (time_passed
< (int) s
->initStartDelay
999 && time_passed
< (int) s
->restartDelay
)
1004 if ((fcgi_dynamic_total_proc_count
+ 1) > (int) dynamicMaxProcs
) {
1006 * Extra instances should have been
1007 * terminated beforehand, probably need
1008 * to increase ProcessSlack parameter
1010 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1011 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1012 "exceeded dynamicMaxProcs (%d)", s
->fs_path
, dynamicMaxProcs
);
1015 /* find next free slot */
1016 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1017 if (s
->procs
[i
].state
!= STATE_READY
1018 && s
->procs
[i
].state
!= STATE_NEEDS_STARTING
1019 && s
->procs
[i
].state
!= STATE_KILLED
)
1024 if (s
->procs
[i
].pid
< 0)
1026 if (time_passed
> (int)s
->restartDelay
) {
1027 schedule_start(s
, i
);
1031 else if (s
->procs
[i
].pid
== 0)
1033 if (time_passed
> (int)s
->initStartDelay
) {
1034 schedule_start(s
, i
);
1042 /* only record stats if we have a structure */
1045 s
->totalConnTime
+= req_usec
;
1046 s
->totalQueueTime
+= q_usec
;
1048 s
->totalConnTime
+= cjob
->start_time
;
1049 s
->totalQueueTime
+= cjob
->qsec
;
1058 /* Cleanup job data */
1059 free(cjob
->fs_path
);
1069 ap_destroy_pool(sp
);
1072 free(cjob
->fs_path
);
1080 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
1081 "FastCGI: really bogus message: \"%s\"", ptr1
);
1082 ptr1
+= strlen(buf
);
1085 buflen
-= ptr1
- buf
;
1087 memmove(buf
, ptr1
, buflen
);
1091 ap_destroy_pool(tp
);
1095 *----------------------------------------------------------------------
1097 * dynamic_kill_idle_fs_procs
1099 * Implement a kill policy for the dynamic FastCGI applications.
1100 * We also update the data structures to reflect the changes.
1103 * Processes are marked for deletion possibly killed.
1105 *----------------------------------------------------------------------
1107 static void dynamic_kill_idle_fs_procs(void)
1110 struct FuncData
*funcData
= NULL
;
1111 unsigned long connTime
; /* server's smoothed running time, or
1112 * if that's 0, the current total */
1113 unsigned long totalTime
; /* maximum number of microseconds that all
1114 * of a server's running processes together
1115 * could have spent running since the
1117 double loadFactor
; /* percentage, 0-100, of totalTime that
1118 * the processes actually used */
1119 unsigned int i
, victims
= 0;
1121 const char *lockFileName
;
1125 pool
*tp
= ap_make_sub_pool(fcgi_config_pool
);
1127 /* pass 1 - locate and mark all victims */
1128 for(s
=fcgi_servers
; s
!=NULL
; s
=s
->next
) {
1129 /* Only kill dynamic apps */
1130 if (s
->directive
!= APP_CLASS_DYNAMIC
)
1133 /* If the number of non-victims is less than or equal to
1134 the minimum that may be running without being killed off,
1135 don't select any more victims. */
1136 if ((fcgi_dynamic_total_proc_count
- victims
) <= (int) dynamicMinProcs
) {
1140 connTime
= s
->smoothConnTime
? s
->smoothConnTime
: s
->totalConnTime
;
1141 totalTime
= (s
->numProcesses
)*(now
- fcgi_dynamic_epoch
)*1000000 + 1;
1143 /* XXX producing a heavy load with one client, I haven't been
1144 able to achieve a loadFactor greater than 0.5. Perhaps this
1145 should be scaled up by another order of magnitude or two. */
1146 loadFactor
= connTime
/totalTime
*100.0;
1148 if ((s
->numProcesses
> 1
1149 && s
->numProcesses
/(s
->numProcesses
- 1)*loadFactor
< dynamicThreshholdN
)
1150 || (s
->numProcesses
== 1 && loadFactor
< dynamicThreshhold1
))
1154 for (i
= 0; !got_one
&& i
< dynamicMaxClassProcs
; ++i
) {
1155 if (s
->procs
[i
].state
== STATE_NEEDS_STARTING
) {
1156 s
->procs
[i
].state
= STATE_READY
;
1159 else if (s
->procs
[i
].state
== STATE_VICTIM
|| s
->procs
[i
].state
== STATE_KILL
) {
1164 for (i
= 0; !got_one
&& i
< dynamicMaxClassProcs
; ++i
) {
1165 if (s
->procs
[i
].state
== STATE_STARTED
) {
1166 s
->procs
[i
].state
= STATE_KILL
;
1167 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1168 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination scheduled",
1169 s
->fs_path
, s
->procs
[i
].pid
);
1177 /* pass 2 - kill procs off */
1178 for(s
=fcgi_servers
; s
!=NULL
; s
=s
->next
) {
1179 /* Only kill dynamic apps */
1180 if (s
->directive
!= APP_CLASS_DYNAMIC
)
1183 for(i
= 0; i
< dynamicMaxClassProcs
; i
++) {
1184 if (s
->procs
[i
].state
== STATE_KILL
) {
1186 lockFileName
= fcgi_util_socket_get_lock_filename(tp
, s
->socket_path
);
1187 if ((lockFd
= ap_popenf(tp
, lockFileName
, O_RDWR
, 0))<0) {
1189 * If we need to kill an application and the
1190 * corresponding lock file does not exist, then
1191 * that means we are in big trouble here
1193 /*@@@ this should be logged, but since all the lock
1194 * file stuff will be tossed, I'll leave it now */
1195 ap_pclosef(tp
, lockFd
);
1199 if (fcgi_get_exclusive_write_lock_no_wait(lockFd
) < 0) {
1201 if (fcgi_get_exclusive_write_lock_no_wait(s
->dynamic_lock
) < 0) {
1205 * Unable to lock the lockfile, indicative
1206 * of WS performing operation with the given
1207 * application class. The simplest solution
1208 * is to spawn off another process and block
1209 * on lock to kill it. This is under assumptions
1210 * that fork() is not very costly and this
1211 * situation occurs very rarely, which it should
1213 funcData
= ap_pcalloc(tp
, sizeof(struct FuncData
));
1215 funcData
->lockFileName
= lockFileName
;
1217 funcData
->lock
= s
->dynamic_lock
;
1219 funcData
->process
= &s
->procs
[i
];
1222 if((pid
=fork())<0) {
1223 /*@@@ this should be logged, but since all the lock
1224 * file stuff will be tossed, I'll leave it now */
1225 ap_pclosef(tp
, lockFd
);
1230 /* rename the process for ps - best we can easily */
1231 change_process_name("fcgiBlkKill");
1233 dynamic_blocking_kill(funcData
);
1236 s
->procs
[i
].state
= STATE_VICTIM
;
1237 ap_pclosef(tp
, lockFd
);
1240 CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)dynamic_blocking_kill
, (void *)funcData
, 0, &tid
);
1241 s
->procs
[i
].state
= STATE_VICTIM
;
1245 s
->procs
[i
].state
= STATE_VICTIM
;
1247 fcgi_kill(&s
->procs
[i
], SIGTERM
);
1248 ap_pclosef(tp
, lockFd
);
1250 fcgi_kill(&s
->procs
[i
], 1);
1256 ap_destroy_pool(tp
);
1261 // This is a little bogus, there's gotta be a better way to do this
1262 // Can we use WaitForMultipleObjects()
1263 #define FCGI_PROC_WAIT_TIME 100
1265 void child_wait_thread(void *dummy
) {
1267 DWORD dwRet
= WAIT_TIMEOUT
;
1272 while (!bTimeToDie
) {
1275 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1276 if (s
->directive
== APP_CLASS_EXTERNAL
|| s
->listenFd
< 0) {
1279 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1280 numChildren
= dynamicMaxClassProcs
;
1283 numChildren
= s
->numProcesses
;
1286 for (i
=0; i
< numChildren
; i
++) {
1287 if (s
->procs
[i
].handle
!= INVALID_HANDLE_VALUE
) {
1288 /* timeout is currently set for 100 miliecond */
1289 /* it may need t longer or user customizable */
1290 dwRet
= WaitForSingleObject(s
->procs
[i
].handle
, FCGI_PROC_WAIT_TIME
);
1294 if (dwRet
!= WAIT_TIMEOUT
) {
1295 /* a child fs has died */
1296 /* mark the child as dead */
1298 if (s
->directive
== APP_CLASS_STANDARD
) {
1299 /* restart static app */
1300 s
->procs
[i
].state
= STATE_NEEDS_STARTING
;
1305 fcgi_dynamic_total_proc_count
--;
1307 if (s
->procs
[i
].state
== STATE_VICTIM
) {
1308 s
->procs
[i
].state
= STATE_KILLED
;
1312 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1315 if (dynamicAutoRestart
) {
1316 s
->procs
[i
].state
= STATE_NEEDS_STARTING
;
1319 s
->procs
[i
].state
= STATE_READY
;
1324 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1325 "FastCGI:%s server \"%s\" (pid %d) terminated",
1326 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1327 s
->fs_path
, s
->procs
[i
].pid
);
1329 CloseHandle(s
->procs
[i
].handle
);
1330 s
->procs
[i
].handle
= INVALID_HANDLE_VALUE
;
1331 s
->procs
[i
].pid
= -1;
1333 /* wake up the main thread */
1334 SetEvent(fcgi_event_handles
[WAKE_EVENT
]);
1339 Sleep(waited
? 0 : FCGI_PROC_WAIT_TIME
);
1345 static void setup_signals(void)
1348 struct sigaction sa
;
1352 sigaddset(&mask
, SIGUSR2
);
1353 sigprocmask(SIG_BLOCK
, &mask
, NULL
);
1355 /* Setup handlers */
1357 sa
.sa_handler
= signal_handler
;
1358 sigemptyset(&sa
.sa_mask
);
1361 if (sigaction(SIGTERM
, &sa
, NULL
) < 0) {
1362 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1363 "sigaction(SIGTERM) failed");
1366 if (sigaction(SIGHUP
, &sa
, NULL
) < 0) {
1367 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1368 "sigaction(SIGHUP) failed");
1370 /* httpd graceful restart */
1371 if (sigaction(SIGUSR1
, &sa
, NULL
) < 0) {
1372 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1373 "sigaction(SIGUSR1) failed");
1375 /* read messages from request handlers - kill interval expired */
1376 if (sigaction(SIGALRM
, &sa
, NULL
) < 0) {
1377 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1378 "sigaction(SIGALRM) failed");
1380 if (sigaction(SIGCHLD
, &sa
, NULL
) < 0) {
1381 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
1382 "sigaction(SIGCHLD) failed");
1388 int fcgi_pm_main(void *dummy
, child_info
*info
)
1390 void fcgi_pm_main(void *dummy
)
1403 HANDLE wait_thread
= INVALID_HANDLE_VALUE
;
1405 int callWaitPid
, callDynamicProcs
;
1409 // Add SystemRoot to the dyanmic environment
1410 char ** envp
= dynamicEnvp
;
1411 for (i
= 0; *envp
; ++i
) {
1414 fcgi_config_set_env_var(fcgi_config_pool
, dynamicEnvp
, &i
, "SystemRoot");
1417 reduce_priveleges();
1419 close(fcgi_pm_pipe
[1]);
1420 change_process_name("fcgi-pm");
1424 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
1425 "FastCGI: suEXEC mechanism enabled (wrapper: %s)", fcgi_suexec
);
1429 /* Initialize AppClass */
1430 tp
= ap_make_sub_pool(fcgi_config_pool
);
1431 for(s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1432 if (s
->directive
== APP_CLASS_EXTERNAL
)
1436 if (s
->socket_path
) {
1437 SECURITY_ATTRIBUTES sa
;
1439 sa
.nLength
= sizeof(sa
);
1440 sa
.lpSecurityDescriptor
= NULL
;
1441 sa
.bInheritHandle
= TRUE
;
1443 s
->listenFd
= (int)CreateNamedPipe(s
->socket_path
, PIPE_ACCESS_DUPLEX
,
1444 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
1445 PIPE_UNLIMITED_INSTANCES
, 4096,4096,0, &sa
);
1446 if ((HANDLE
)s
->listenFd
== INVALID_HANDLE_VALUE
) {
1447 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1448 "FastCGI: server \"%s\" disabled, CreateNamedPipe() failed", s
->fs_path
);
1452 s
->hPipeMutex
= ap_create_mutex(NULL
);
1454 if (s
->hPipeMutex
== NULL
) {
1455 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1456 "FastCGI: server \"%s\" disabled, ap_create_mutex() failed", s
->fs_path
);
1457 CloseHandle((HANDLE
)s
->listenFd
);
1458 s
->listenFd
= (int) INVALID_HANDLE_VALUE
;
1462 SetHandleInformation(s
->hPipeMutex
, HANDLE_FLAG_INHERIT
, TRUE
);
1464 append_mutex_to_env(s
->envp
, s
->hPipeMutex
);
1469 /* Create the socket */
1470 s
->listenFd
= ap_psocket(fcgi_config_pool
, s
->socket_addr
->sa_family
, SOCK_STREAM
, 0);
1471 if (s
->listenFd
< 0) {
1472 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1473 "FastCGI: server \"%s\" disabled, socket() failed", s
->fs_path
);
1477 /* bind() and listen() */
1478 err
= bind_n_listen(tp
, s
->socket_addr
, s
->socket_addr_len
,
1479 s
->listenQueueDepth
, s
->listenFd
);
1481 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1482 "FastCGI: server \"%s\" disabled: %s",
1484 ap_pclosesocket(fcgi_config_pool
, s
->listenFd
);
1490 for (i
= 0; i
< s
->numProcesses
; i
++)
1491 s
->procs
[i
].state
= STATE_NEEDS_STARTING
;
1494 ap_destroy_pool(tp
);
1497 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1498 "FastCGI: process manager initialized");
1500 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
1501 "FastCGI: process manager initialized (pid %ld)", (long)getpid());
1507 * Loop until SIGTERM
1510 int sleepSeconds
= min(dynamicKillInterval
, dynamicUpdateInterval
);
1517 unsigned int numChildren
;
1520 * If we came out of sigsuspend() for any reason other than
1521 * SIGALRM, pick up where we left off.
1524 sleepSeconds
= alarmLeft
;
1527 * Examine each configured AppClass for a process that needs
1528 * starting. Compute the earliest time when the start should
1529 * be attempted, starting it now if the time has passed. Also,
1530 * remember that we do NOT need to restart externally managed
1531 * FastCGI applications.
1533 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1534 if (s
->directive
== APP_CLASS_EXTERNAL
|| s
->listenFd
< 0) {
1537 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1538 numChildren
= dynamicMaxClassProcs
;
1540 numChildren
= s
->numProcesses
;
1543 for (i
= 0; i
< numChildren
; i
++) {
1544 if ((s
->procs
[i
].pid
<= 0) &&
1545 (s
->procs
[i
].state
== STATE_NEEDS_STARTING
))
1549 if (s
->procs
[i
].pid
== 0) {
1550 restartTime
= s
->restartTime
+ s
->initStartDelay
;
1552 restartTime
= s
->restartTime
+ s
->restartDelay
;
1555 if (restartTime
<= now
) {
1556 int restart
= (s
->procs
[i
].pid
< 0);
1558 s
->restartTime
= now
;
1561 if (caughtSigTerm
) {
1562 goto ProcessSigTerm
;
1565 s
->procs
[i
].pid
= spawn_fs_process(s
, &s
->procs
[i
]);
1566 if (s
->procs
[i
].pid
<= 0) {
1567 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1568 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1569 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1572 sleepSeconds
= min(sleepSeconds
,
1573 max((int) s
->restartDelay
, FCGI_MIN_EXEC_RETRY_DELAY
));
1575 ap_assert(s
->procs
[i
].pid
< 0);
1579 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1581 fcgi_dynamic_total_proc_count
++;
1584 s
->procs
[i
].state
= STATE_STARTED
;
1589 if (fcgi_suexec
!= NULL
) {
1590 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1591 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1592 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1593 s
->fs_path
, (long)s
->uid
, (long)s
->gid
,
1594 restart
? "re" : "", (long)s
->procs
[i
].pid
);
1597 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1598 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1599 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1600 s
->fs_path
, restart
? "re" : "", (long)s
->procs
[i
].pid
);
1602 ap_assert(s
->procs
[i
].pid
> 0);
1604 sleepSeconds
= min(sleepSeconds
, restartTime
- now
);
1612 goto ProcessSigTerm
;
1614 if((!caughtSigChld
) && (!caughtSigUsr2
)) {
1617 alarm(sleepSeconds
);
1620 FD_SET(fcgi_pm_pipe
[0], &rfds
);
1621 read_ready
= ap_select(fcgi_pm_pipe
[0] + 1, &rfds
, NULL
, NULL
, NULL
);
1623 alarmLeft
= alarm(0);
1625 callWaitPid
= caughtSigChld
;
1626 caughtSigChld
= FALSE
;
1627 callDynamicProcs
= caughtSigUsr2
;
1628 caughtSigUsr2
= FALSE
;
1633 * Dynamic fcgi process management
1635 if((callDynamicProcs
) || (!callWaitPid
)) {
1636 dynamic_read_msgs(read_ready
);
1637 if(fcgi_dynamic_epoch
== 0) {
1638 fcgi_dynamic_epoch
= now
;
1640 if(((long)(now
-fcgi_dynamic_epoch
)>=dynamicKillInterval
) ||
1641 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
)>=dynamicMaxProcs
)) {
1642 dynamic_kill_idle_fs_procs();
1643 fcgi_dynamic_epoch
= now
;
1651 /* We've caught SIGCHLD, so find out who it was using waitpid,
1652 * write a log message and update its data structure. */
1656 goto ProcessSigTerm
;
1658 childPid
= waitpid(-1, &waitStatus
, WNOHANG
);
1660 if (childPid
== -1 || childPid
== 0)
1663 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1664 if (s
->directive
== APP_CLASS_EXTERNAL
)
1667 if (s
->directive
== APP_CLASS_DYNAMIC
)
1668 numChildren
= dynamicMaxClassProcs
;
1670 numChildren
= s
->numProcesses
;
1672 for (i
= 0; i
< numChildren
; i
++) {
1673 if (s
->procs
[i
].pid
== childPid
)
1678 /* @@@ This (comment) needs to go away when dynamic gets cleaned up.
1679 * If we get to this point, we have detected the
1680 * termination of the process that was spawned off by
1681 * the process manager to do a blocking kill above. */
1685 s
->procs
[i
].pid
= -1;
1687 if (s
->directive
== APP_CLASS_STANDARD
) {
1688 /* Always restart static apps */
1689 s
->procs
[i
].state
= STATE_NEEDS_STARTING
;
1694 fcgi_dynamic_total_proc_count
--;
1696 if (s
->procs
[i
].state
== STATE_VICTIM
) {
1697 s
->procs
[i
].state
= STATE_KILLED
;
1700 /* A dynamic app died or exited without provacation from the PM */
1703 if (dynamicAutoRestart
|| s
->numProcesses
<= 0)
1704 s
->procs
[i
].state
= STATE_NEEDS_STARTING
;
1706 s
->procs
[i
].state
= STATE_READY
;
1710 if (WIFEXITED(waitStatus
)) {
1711 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1712 "FastCGI:%s server \"%s\" (pid %d) terminated by calling exit with status '%d'",
1713 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1714 s
->fs_path
, (int)childPid
, WEXITSTATUS(waitStatus
));
1716 else if (WIFSIGNALED(waitStatus
)) {
1717 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1718 "FastCGI:%s server \"%s\" (pid %d) terminated due to uncaught signal '%d' (%s)%s",
1719 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1720 s
->fs_path
, (int)childPid
, WTERMSIG(waitStatus
), SYS_SIGLIST
[WTERMSIG(waitStatus
)],
1722 WCOREDUMP(waitStatus
) ? ", a core file may have been generated" : "");
1727 else if (WIFSTOPPED(waitStatus
)) {
1728 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1729 "FastCGI:%s server \"%s\" (pid %d) stopped due to uncaught signal '%d' (%s)",
1730 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1731 s
->fs_path
, (int)childPid
, WTERMSIG(waitStatus
), SYS_SIGLIST
[WTERMSIG(waitStatus
)]);
1733 } /* for (;;), waitpid() */
1736 DWORD tid
; /* Thread id */
1738 /* Start the child wait thread */
1739 wait_thread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)child_wait_thread
, NULL
, 0, &tid
);
1743 /* wait for an event to occur or timer expires */
1744 expire
= time(NULL
) + sleepSeconds
;
1745 dwRet
= WaitForMultipleObjects(3, (HANDLE
*) fcgi_event_handles
, FALSE
, sleepSeconds
* 1000);
1747 if (dwRet
== WAIT_FAILED
) {
1748 /* There is something seriously wrong here */
1749 ap_log_error(APLOG_MARK
,APLOG_CRIT
|APLOG_WIN32ERROR
, fcgi_apache_main_server
,
1750 "FastDGI: WaitForMultipleObjects on event handles -- pm is shuting down");
1754 if (dwRet
!= WAIT_TIMEOUT
) {
1758 alarmLeft
= expire
- now
;
1762 * Dynamic fcgi process management
1764 if ((dwRet
== MBOX_EVENT
) || (dwRet
== WAIT_TIMEOUT
)) {
1765 if (dwRet
== MBOX_EVENT
) {
1771 dynamic_read_msgs(read_ready
);
1773 if(fcgi_dynamic_epoch
== 0) {
1774 fcgi_dynamic_epoch
= now
;
1777 if (((long)(now
-fcgi_dynamic_epoch
) >= (int)dynamicKillInterval
) ||
1778 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
) >= dynamicMaxProcs
)) {
1779 dynamic_kill_idle_fs_procs();
1780 fcgi_dynamic_epoch
= now
;
1784 else if (dwRet
== WAKE_EVENT
) {
1787 else if (dwRet
== TERM_EVENT
) {
1788 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
1789 "FastCGI: Termination event received process manager shutting down");
1792 dwRet
= WaitForSingleObject(wait_thread
, INFINITE
);
1793 goto ProcessSigTerm
;
1796 // Have an received an unknown event - should not happen
1797 ap_log_error(APLOG_MARK
,APLOG_CRIT
|APLOG_WIN32ERROR
, fcgi_apache_main_server
,
1798 "FastCGI: WaitForMultipleobjects return an unrecognized event");
1800 dwRet
= WaitForSingleObject(wait_thread
, INFINITE
);
1801 goto ProcessSigTerm
;
1804 } /* for (;;), the whole shoot'n match */
1808 * Kill off the children, then exit.
1810 while (fcgi_servers
!= NULL
) {
1811 kill_fs_procs(fcgi_config_pool
, fcgi_servers
);
1822 int fcgi_pm_add_job(fcgi_pm_job
*new_job
) {
1824 if (new_job
== NULL
)
1827 ap_acquire_mutex(fcgi_dynamic_mbox_mutex
);
1828 new_job
->next
= fcgi_dynamic_mbox
;
1829 fcgi_dynamic_mbox
= new_job
;
1830 ap_release_mutex(fcgi_dynamic_mbox_mutex
);