2 * $Id: fcgi_pm.c,v 1.20 1999/10/06 11:44:23 roberts Exp $
7 int fcgi_dynamic_total_proc_count
= 0; /* number of running apps */
8 time_t fcgi_dynamic_epoch
= 0; /* last time kill_procs was
9 * invoked by process mgr */
10 time_t fcgi_dynamic_last_analyzed
= 0; /* last time calculation was
11 * made for the dynamic procs*/
13 static time_t now
= 0;
15 /* Information about a process we are doing a blocking kill of. */
17 const char *lockFileName
; /* name of the lock file to lock */
18 pid_t pid
; /* process to issue SIGTERM to */
21 static int seteuid_root(void)
23 int rc
= seteuid((uid_t
)0);
25 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
26 "FastCGI: seteuid(0) failed");
31 static int seteuid_user(void)
33 int rc
= seteuid(ap_user_id
);
35 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
36 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id
);
41 static int fcgi_kill(pid_t pid
, int sig
)
54 /*******************************************************************************
55 * Send SIGTERM to each process in the server class, remove socket and lock
56 * file if appropriate. Currently this is only called when the PM is shutting
57 * down and thus memory isn't freed and sockets and files aren't closed.
59 static void kill_fs_procs(pool
*p
, fcgi_server
*s
)
61 ServerProcess
*proc
= s
->procs
;
64 if (s
->directive
== APP_CLASS_DYNAMIC
)
65 numChildren
= dynamicMaxClassProcs
;
67 numChildren
= s
->numProcesses
;
69 for (i
= 0; i
< numChildren
; i
++, proc
++) {
71 fcgi_kill(proc
->pid
, SIGTERM
);
76 /* Remove the dead lock file */
77 if (s
->directive
== APP_CLASS_DYNAMIC
) {
78 const char *lockFileName
= fcgi_util_socket_get_lock_filename(p
, s
->socket_path
);
80 if (unlink(lockFileName
) != 0) {
81 ap_log_error(FCGI_LOG_ERR
, fcgi_apache_main_server
,
82 "FastCGI: unlink() failed to remove lock file \"%s\" for (dynamic) server \"%s\"",
83 lockFileName
, s
->fs_path
);
87 /* Remove the socket file */
88 if (s
->socket_path
!= NULL
&& s
->directive
!= APP_CLASS_EXTERNAL
) {
89 if (unlink(s
->socket_path
) != 0) {
90 ap_log_error(FCGI_LOG_ERR
, fcgi_apache_main_server
,
91 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
93 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "", s
->fs_path
);
96 fcgi_servers
= s
->next
;
99 /*******************************************************************************
100 * Bind an address to a socket and set it to listen for incoming connects.
101 * The error messages are allocated from the pool p, use temp storage.
102 * Don't forget to close the socket, if an error occurs.
104 static const char *bind_n_listen(pool
*p
, struct sockaddr
*socket_addr
,
105 int socket_addr_len
, int backlog
, int sock
)
107 if (socket_addr
->sa_family
== AF_UNIX
) {
108 /* Remove any existing socket file.. just in case */
109 unlink(((struct sockaddr_un
*)socket_addr
)->sun_path
);
112 setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, (char *)&flag
, sizeof(flag
));
115 /* Bind it to the socket_addr */
116 if (bind(sock
, socket_addr
, socket_addr_len
) != 0)
117 return "bind() failed";
119 /* Twiddle permissions */
120 if (socket_addr
->sa_family
== AF_UNIX
) {
121 if (chmod(((struct sockaddr_un
*)socket_addr
)->sun_path
, S_IRUSR
| S_IWUSR
))
122 return "chmod() of socket failed";
126 if (listen(sock
, backlog
) != 0)
127 return "listen() failed";
133 *----------------------------------------------------------------------
135 * dynamic_blocking_kill
137 * Block on the lock file until it is available, and then
138 * issue a kill signal to the corresponding application.
139 * Since this function is executed in the child process,
140 * _exit() is called upon completion.
143 * Pointer to the data structure containing a process id to
144 * issue a signal to and the full pathname to the lockfile
145 * that needs to be locked before the issue of the signal.
148 * Memory is allocated by the caller, but is freed by this
151 *----------------------------------------------------------------------
153 static void dynamic_blocking_kill(void *data
)
155 struct FuncData
*funcData
= (struct FuncData
*)data
;
158 ap_assert(funcData
->lockFileName
);
159 if ((lockFd
= open(funcData
->lockFileName
, O_RDWR
)) < 0) {
160 /* There is something terribly wrong here */
162 if (fcgi_wait_for_shared_write_lock(lockFd
) < 0) {
163 /* This is a major problem */
165 fcgi_kill(funcData
->pid
, SIGTERM
);
168 /* exit() may flush stdio buffers inherited from the parent. */
173 *----------------------------------------------------------------------
177 * The FastCGI process manager, which runs as a separate
178 * process responsible for:
179 * - Starting all the FastCGI proceses.
180 * - Restarting any of these processes that die (indicated
182 * - Catching SIGTERM and relaying it to all the FastCGI
183 * processes before exiting.
186 * Uses global variable fcgi_servers.
194 *----------------------------------------------------------------------
196 static int caughtSigTerm
= FALSE
;
197 static int caughtSigChld
= FALSE
;
198 static int caughtSigUsr2
= FALSE
;
200 static void signal_handler(int signo
)
202 if ((signo
== SIGTERM
) || (signo
== SIGUSR1
) || (signo
== SIGHUP
)) {
203 /* SIGUSR1 & SIGHUP are sent by apache to its process group
204 * when apache get 'em. Apache follows up (1.2.x) with attacks
205 * on each of its child processes, but we've got the KillMgr
206 * sitting between us so we never see the KILL. The main loop
207 * in ProcMgr also checks to see if the KillMgr has terminated,
208 * and if it has, we handl it as if we should shutdown too. */
209 caughtSigTerm
= TRUE
;
210 } else if(signo
== SIGCHLD
) {
211 caughtSigChld
= TRUE
;
212 } else if(signo
== SIGALRM
) {
213 caughtSigUsr2
= TRUE
;
218 *----------------------------------------------------------------------
220 * spawn_fs_process --
222 * Fork and exec the specified fcgi process.
225 * 0 for successful fork, -1 for failed fork.
227 * In case the child fails before or in the exec, the child
228 * obtains the error log by calling getErrLog, logs
229 * the error, and exits with exit status = errno of
230 * the failed system call.
233 * Child process created.
235 *----------------------------------------------------------------------
238 static pid_t
spawn_fs_process(const fcgi_server
*fs
)
243 char *dnEnd
, *failedSysCall
;
250 /* We're the child. We're gonna exec() so pools don't matter. */
252 dnEnd
= strrchr(fs
->fs_path
, '/');
256 dirName
= ap_pcalloc(fcgi_config_pool
, dnEnd
- fs
->fs_path
+ 1);
257 dirName
= memcpy(dirName
, fs
->fs_path
, dnEnd
- fs
->fs_path
);
259 if (chdir(dirName
) < 0) {
260 failedSysCall
= "chdir()";
261 goto FailedSystemCallExit
;
265 /* OS/2 dosen't support nice() */
266 if (fs
->processPriority
!= 0) {
267 if (nice(fs
->processPriority
) == -1) {
268 failedSysCall
= "nice()";
269 goto FailedSystemCallExit
;
274 /* Open the listenFd on spec'd fd */
275 if (fs
->listenFd
!= FCGI_LISTENSOCK_FILENO
)
276 dup2(fs
->listenFd
, FCGI_LISTENSOCK_FILENO
);
278 /* Close all other open fds, except stdout/stderr. Leave these two open so
279 * FastCGI applications don't have to find and fix ALL 3rd party libs that
280 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
281 * main server error_log - @@@ provide a directive control where this goes.
283 ap_error_log2stderr(fcgi_apache_main_server
);
284 dup2(STDERR_FILENO
, STDOUT_FILENO
);
285 for (i
= 0; i
< MAX_OPEN_FDS
; i
++) {
286 if (i
!= FCGI_LISTENSOCK_FILENO
&& i
!= STDERR_FILENO
&& i
!= STDOUT_FILENO
) {
291 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
292 * install its own handler. */
293 signal(SIGPIPE
, SIG_IGN
);
295 if (fcgi_suexec
!= NULL
) {
296 char *shortName
= strrchr(fs
->fs_path
, '/') + 1;
298 /* Relinquish our root real uid powers */
303 execle(fcgi_suexec
, fcgi_suexec
, fs
->username
, fs
->group
, shortName
, NULL
, fs
->envp
);
304 } while (errno
== EINTR
);
308 execle(fs
->fs_path
, fs
->fs_path
, NULL
, fs
->envp
);
309 } while (errno
== EINTR
);
312 failedSysCall
= "execle()";
314 FailedSystemCallExit
:
315 fprintf(stderr
, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
316 fs
->fs_path
, (long) getpid(), failedSysCall
, strerror(errno
));
319 /* avoid an irrelevant compiler warning */
323 static void reduce_priveleges(void)
331 /* Get username if passed as a uid */
332 if (ap_user_name
[0] == '#') {
333 uid_t uid
= atoi(&ap_user_name
[1]);
334 struct passwd
*ent
= getpwuid(uid
);
337 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
338 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
339 "you probably need to modify the User directive", (unsigned)uid
);
348 if (setgid(ap_group_id
) == -1) {
349 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
350 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id
);
354 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
356 /* Initialize supplementary groups */
357 if (initgroups(name
, ap_group_id
) == -1) {
358 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
359 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
360 name
, (unsigned)ap_group_id
);
367 if (seteuid_user() == -1) {
368 ap_log_error(FCGI_LOG_ALERT_NOERRNO
, fcgi_apache_main_server
,
369 "FastCGI: process manager exiting, failed to reduce priveleges");
374 if (setuid(ap_user_id
) == -1) {
375 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
376 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id
);
383 * Change the name of this process - best we can easily.
385 static void change_process_name(const char * const name
)
387 strncpy(ap_server_argv0
, name
, strlen(ap_server_argv0
));
390 static void schedule_start(fcgi_server
*s
, int proc
)
392 s
->procs
[proc
].state
= STATE_NEEDS_STARTING
;
393 if (proc
== dynamicMaxClassProcs
- 1) {
394 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
395 "FastCGI: scheduled the %sstart of the last (dynamic) server "
396 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
397 s
->procs
[proc
].pid
? "re" : "", s
->fs_path
, dynamicMaxClassProcs
);
402 *----------------------------------------------------------------------
406 * Removes the records written by request handlers and decodes them.
407 * We also update the data structures to reflect the changes.
409 *----------------------------------------------------------------------
412 static void dynamic_read_msgs(int read_ready
)
416 static int buflen
= 0;
417 static char buf
[FCGI_MSGS_BUFSIZE
+ 1];
418 char *ptr1
, *ptr2
, opcode
;
419 char execName
[FCGI_MAXPATH
+ 1];
420 char user
[MAX_USER_NAME_LEN
+ 2];
421 char group
[MAX_GID_CHAR_LEN
+ 1];
422 unsigned long q_usec
= 0UL, req_usec
= 0UL;
425 user
[MAX_USER_NAME_LEN
+ 1] = group
[MAX_GID_CHAR_LEN
] = '\0';
428 * To prevent the idle application from running indefinitely, we
429 * check the timer and if it is expired, we recompute the values
430 * for each running application class. Then, when REQ_COMPLETE
431 * message is recieved, only updates are made to the data structures.
433 if (fcgi_dynamic_last_analyzed
== 0) {
434 fcgi_dynamic_last_analyzed
= now
;
436 if ((long)(now
- fcgi_dynamic_last_analyzed
) >= dynamicUpdateInterval
) {
437 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
438 if (s
->directive
!= APP_CLASS_DYNAMIC
)
440 /* XXX what does this adjustment do? */
441 fcgi_dynamic_last_analyzed
+= (((long)(now
-fcgi_dynamic_last_analyzed
)/dynamicUpdateInterval
)*dynamicUpdateInterval
);
442 s
->smoothConnTime
= (1.0-dynamicGain
)*s
->smoothConnTime
+ dynamicGain
*s
->totalConnTime
;
443 s
->totalConnTime
= 0UL;
444 s
->totalQueueTime
= 0UL;
448 if (read_ready
<= 0) {
452 rc
= read(fcgi_pm_pipe
[0], (void *)(buf
+ buflen
), FCGI_MSGS_BUFSIZE
- buflen
);
454 if (!caughtSigTerm
) {
455 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
456 "FastCGI: read() from pipe failed (%d)", rc
);
463 tp
= ap_make_sub_pool(fcgi_config_pool
);
465 for (ptr1
= buf
; ptr1
; ptr1
= ptr2
) {
468 ptr2
= strchr(ptr1
, '*');
480 if (sscanf(ptr1
, "%c %s %16s %15s",
481 &opcode
, execName
, user
, group
) != 4)
487 if (sscanf(ptr1
, "%c %s %16s %15s",
488 &opcode
, execName
, user
, group
) != 4)
494 if (sscanf(ptr1
, "%c %s %16s %15s %lu %lu",
495 &opcode
, execName
, user
, group
, &q_usec
, &req_usec
) != 6)
506 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
507 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1
);
511 s
= fcgi_util_fs_get(execName
, user
, group
);
513 if (s
==NULL
&& opcode
!= REQ_COMPLETE
) {
515 const char *err
, *lockPath
;
517 /* Create a perm subpool to hold the new server data,
518 * we can destroy it if something doesn't pan out */
519 sp
= ap_make_sub_pool(fcgi_config_pool
);
521 /* Create a new "dynamic" server */
522 s
= fcgi_util_fs_new(sp
);
523 s
->directive
= APP_CLASS_DYNAMIC
;
524 s
->restartDelay
= dynamicRestartDelay
;
525 s
->listenQueueDepth
= dynamicListenQueueDepth
;
526 s
->initStartDelay
= dynamicInitStartDelay
;
527 s
->envp
= dynamicEnvp
;
528 ap_getparents(execName
);
529 ap_no2slash(execName
);
530 s
->fs_path
= ap_pstrdup(sp
, execName
);
531 s
->procs
= fcgi_util_fs_create_procs(sp
, dynamicMaxClassProcs
);
533 /* Create socket file's path */
534 s
->socket_path
= fcgi_util_socket_hash_filename(tp
, execName
, user
, group
);
535 s
->socket_path
= fcgi_util_socket_make_path_absolute(sp
, s
->socket_path
, 1);
537 /* Create sockaddr, prealloc it so it won't get created in tp */
538 s
->socket_addr
= ap_pcalloc(sp
, sizeof(struct sockaddr_un
));
539 err
= fcgi_util_socket_make_domain_addr(tp
, (struct sockaddr_un
**)&s
->socket_addr
,
540 &s
->socket_addr_len
, s
->socket_path
);
542 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
543 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
547 /* Create the socket */
548 if ((s
->listenFd
= ap_psocket(sp
, s
->socket_addr
->sa_family
, SOCK_STREAM
, 0)) < 0) {
549 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
550 "FastCGI: can't create (dynamic) server \"%s\": socket() failed", execName
);
554 /* bind() and listen() */
555 err
= bind_n_listen(tp
, s
->socket_addr
, s
->socket_addr_len
,
556 s
->listenQueueDepth
, s
->listenFd
);
558 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
559 "FastCGI: can't create (dynamic) server \"%s\": %s", execName
, err
);
563 /* Create the lock file */
564 lockPath
= fcgi_util_socket_get_lock_filename(tp
, s
->socket_path
);
565 fd
= ap_popenf(tp
, lockPath
,
566 O_WRONLY
| O_CREAT
| O_TRUNC
, S_IRUSR
| S_IWUSR
);
568 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
569 "FastCGI: can't create (dynamic) server \"%s\": can't open lock file \"%s\": popenf() failed",
575 /* If suexec is being used, config user/group info */
577 if (user
[0] == '~') {
578 /* its a user dir uri, the rest is a username, not a uid */
579 struct passwd
*pw
= getpwnam(&user
[1]);
582 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
583 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for suexec: getpwnam(%s) failed",
588 s
->user
= ap_pstrdup(sp
, user
);
589 s
->username
= s
->user
;
592 s
->group
= ap_psprintf(sp
, "%ld", (long)s
->gid
);
597 s
->uid
= (uid_t
)atol(user
);
598 pw
= getpwuid(s
->uid
);
600 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
601 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for suexec: getwpuid(%ld) failed",
602 execName
, (long)s
->uid
);
605 s
->user
= ap_pstrdup(sp
, user
);
606 s
->username
= ap_pstrdup(sp
, pw
->pw_name
);
608 s
->gid
= (gid_t
)atol(group
);
609 s
->group
= ap_pstrdup(sp
, group
);
614 if(opcode
==PLEASE_START
) {
615 if (dynamicAutoUpdate
) {
616 /* Check to see if the binary has changed. If so,
617 * kill the FCGI application processes, and
623 if ((stat(execName
, &stbuf
)==0) &&
624 (stbuf
.st_mtime
> s
->restartTime
)) {
625 /* kill old server(s) */
626 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
627 if (s
->procs
[i
].pid
> 0) {
628 fcgi_kill(s
->procs
[i
].pid
, SIGTERM
);
631 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
632 "FastCGI: restarting server \"%s\" processes, newer version found", execName
);
635 /* If dynamicAutoRestart, don't mark any new processes
636 * for starting because we probably got the
637 * PLEASE_START due to dynamicAutoUpdate and the ProcMgr
638 * will be restarting all of those we just killed.
640 if (dynamicAutoRestart
)
643 /* we've been asked to start a process--only start
644 * it if we're not already running at least one
649 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
650 if (s
->procs
[i
].state
== STATE_STARTED
)
653 /* if already running, don't start another one */
654 if (i
< dynamicMaxClassProcs
) {
666 /* If we've started one recently, don't register another */
667 time_passed
= now
- s
->restartTime
;
669 if (time_passed
< s
->initStartDelay
670 && time_passed
< s
->restartDelay
)
675 if ((fcgi_dynamic_total_proc_count
+ 1) > dynamicMaxProcs
) {
677 * Extra instances should have been
678 * terminated beforehand, probably need
679 * to increase ProcessSlack parameter
681 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
682 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
683 "exceeded dynamicMaxProcs (%d)", s
->fs_path
, dynamicMaxProcs
);
686 /* find next free slot */
687 for (i
= 0; i
< dynamicMaxClassProcs
; i
++) {
688 if (s
->procs
[i
].state
!= STATE_READY
689 && s
->procs
[i
].state
!= STATE_NEEDS_STARTING
690 && s
->procs
[i
].state
!= STATE_KILLED
)
694 if (s
->procs
[i
].pid
< 0) {
695 if (time_passed
> s
->restartDelay
) {
696 schedule_start(s
, i
);
700 else if (s
->procs
[i
].pid
== 0) {
701 if (time_passed
> s
->initStartDelay
) {
702 schedule_start(s
, i
);
710 /* only record stats if we have a structure */
712 s
->totalConnTime
+= req_usec
;
713 s
->totalQueueTime
+= q_usec
;
725 ap_log_error(FCGI_LOG_ERR_NOERRNO
, fcgi_apache_main_server
,
726 "FastCGI: really bogus message: \"%s\"", ptr1
);
730 buflen
-= ptr1
- buf
;
732 memmove(buf
, ptr1
, buflen
);
739 *----------------------------------------------------------------------
741 * dynamic_kill_idle_fs_procs
743 * Implement a kill policy for the dynamic FastCGI applications.
744 * We also update the data structures to reflect the changes.
747 * Processes are marked for deletion possibly killed.
749 *----------------------------------------------------------------------
751 static void dynamic_kill_idle_fs_procs(void)
754 struct FuncData
*funcData
= NULL
;
755 float connTime
; /* server's smoothed running time, or
756 * if that's 0, the current total */
757 float totalTime
; /* maximum number of microseconds that all
758 * of a server's running processes together
759 * could have spent running since the
761 float loadFactor
; /* percentage, 0-100, of totalTime that
762 * the processes actually used */
764 const char *lockFileName
;
767 pool
*tp
= ap_make_sub_pool(fcgi_config_pool
);
769 /* pass 1 - locate and mark all victims */
770 for(s
=fcgi_servers
; s
!=NULL
; s
=s
->next
) {
771 /* Only kill dynamic apps */
772 if (s
->directive
!= APP_CLASS_DYNAMIC
)
775 /* If the number of non-victims is less than or equal to
776 the minimum that may be running without being killed off,
777 don't select any more victims. */
778 if((fcgi_dynamic_total_proc_count
-victims
)<=dynamicMinProcs
) {
781 connTime
= s
->smoothConnTime
? s
->smoothConnTime
: s
->totalConnTime
;
782 totalTime
= (s
->numProcesses
)*(now
- fcgi_dynamic_epoch
)*1000000 + 1;
783 /* XXX producing a heavy load with one client, I haven't been
784 able to achieve a loadFactor greater than 0.5. Perhaps this
785 should be scaled up by another order of magnitude or two. */
786 loadFactor
= connTime
/totalTime
*100.0;
787 if ((s
->numProcesses
> 1
788 && s
->numProcesses
/(s
->numProcesses
- 1)*loadFactor
< dynamicThreshholdN
)
789 || (s
->numProcesses
== 1 && loadFactor
< dynamicThreshhold1
))
793 for (i
= 0; !got_one
&& i
< dynamicMaxClassProcs
; ++i
) {
794 if (s
->procs
[i
].state
== STATE_NEEDS_STARTING
) {
795 s
->procs
[i
].state
= STATE_READY
;
798 else if (s
->procs
[i
].state
== STATE_VICTIM
|| s
->procs
[i
].state
== STATE_KILL
) {
803 for (i
= 0; !got_one
&& i
< dynamicMaxClassProcs
; ++i
) {
804 if (s
->procs
[i
].state
== STATE_STARTED
) {
805 s
->procs
[i
].state
= STATE_KILL
;
806 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
807 "FastCGI: (dynamic) server \"%s\" (pid %d) termination scheduled",
808 s
->fs_path
, s
->procs
[i
].pid
);
815 /* pass 2 - kill procs off */
816 for(s
=fcgi_servers
; s
!=NULL
; s
=s
->next
) {
817 /* Only kill dynamic apps */
818 if (s
->directive
!= APP_CLASS_DYNAMIC
)
821 for(i
= 0; i
< dynamicMaxClassProcs
; i
++) {
822 if (s
->procs
[i
].state
== STATE_KILL
) {
823 lockFileName
= fcgi_util_socket_get_lock_filename(tp
, s
->socket_path
);
824 if ((lockFd
= ap_popenf(tp
, lockFileName
, O_RDWR
, 0))<0) {
826 * If we need to kill an application and the
827 * corresponding lock file does not exist, then
828 * that means we are in big trouble here
830 /*@@@ this should be logged, but since all the lock
831 * file stuff will be tossed, I'll leave it now */
832 ap_pclosef(tp
, lockFd
);
835 if (fcgi_get_exclusive_write_lock_no_wait(lockFd
) < 0) {
837 * Unable to lock the lockfile, indicative
838 * of WS performing operation with the given
839 * application class. The simplest solution
840 * is to spawn off another process and block
841 * on lock to kill it. This is under assumptions
842 * that fork() is not very costly and this
843 * situation occurs very rarely, which it should
845 funcData
= ap_pcalloc(tp
, sizeof(struct FuncData
));
846 funcData
->lockFileName
= lockFileName
;
847 funcData
->pid
= s
->procs
[i
].pid
;
850 /*@@@ this should be logged, but since all the lock
851 * file stuff will be tossed, I'll leave it now */
852 ap_pclosef(tp
, lockFd
);
857 /* rename the process for ps - best we can easily */
858 change_process_name("fcgiBlkKill");
860 dynamic_blocking_kill(funcData
);
863 s
->procs
[i
].state
= STATE_VICTIM
;
864 ap_pclosef(tp
, lockFd
);
867 s
->procs
[i
].state
= STATE_VICTIM
;
868 fcgi_kill(s
->procs
[i
].pid
, SIGTERM
);
869 ap_pclosef(tp
, lockFd
);
877 static void setup_signals(void)
884 sigaddset(&mask
, SIGUSR2
);
885 sigprocmask(SIG_BLOCK
, &mask
, NULL
);
889 sa
.sa_handler
= signal_handler
;
890 sigemptyset(&sa
.sa_mask
);
893 if (sigaction(SIGTERM
, &sa
, NULL
) < 0) {
894 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
895 "sigaction(SIGTERM) failed");
898 if (sigaction(SIGHUP
, &sa
, NULL
) < 0) {
899 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
900 "sigaction(SIGHUP) failed");
902 /* httpd graceful restart */
903 if (sigaction(SIGUSR1
, &sa
, NULL
) < 0) {
904 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
905 "sigaction(SIGUSR1) failed");
907 /* read messages from request handlers - kill interval expired */
908 if (sigaction(SIGALRM
, &sa
, NULL
) < 0) {
909 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
910 "sigaction(SIGALRM) failed");
912 if (sigaction(SIGCHLD
, &sa
, NULL
) < 0) {
913 ap_log_error(FCGI_LOG_ALERT
, fcgi_apache_main_server
,
914 "sigaction(SIGCHLD) failed");
918 int fcgi_pm_main(void *dummy
, child_info
*info
)
922 int callWaitPid
, callDynamicProcs
;
929 close(fcgi_pm_pipe
[1]);
930 change_process_name("fcgi-pm");
934 ap_log_error(FCGI_LOG_INFO_NOERRNO
, fcgi_apache_main_server
,
935 "FastCGI: suEXEC mechanism enabled (wrapper: %s)", fcgi_suexec
);
938 /* Initialize AppClass */
939 tp
= ap_make_sub_pool(fcgi_config_pool
);
940 for(s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
941 if (s
->directive
== APP_CLASS_EXTERNAL
)
944 /* Create the socket */
945 s
->listenFd
= ap_psocket(fcgi_config_pool
, s
->socket_addr
->sa_family
, SOCK_STREAM
, 0);
946 if (s
->listenFd
< 0) {
947 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
948 "FastCGI: server \"%s\" disabled, socket() failed", s
->fs_path
);
952 /* bind() and listen() */
953 err
= bind_n_listen(tp
, s
->socket_addr
, s
->socket_addr_len
,
954 s
->listenQueueDepth
, s
->listenFd
);
956 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
957 "FastCGI: server \"%s\" disabled: %s",
959 ap_pclosesocket(fcgi_config_pool
, s
->listenFd
);
964 for (i
= 0; i
< s
->numProcesses
; i
++)
965 s
->procs
[i
].state
= STATE_NEEDS_STARTING
;
969 ap_log_error(FCGI_LOG_NOTICE_NOERRNO
, fcgi_apache_main_server
,
970 "FastCGI: process manager initialized (pid %ld)", (long)getpid());
978 int sleepSeconds
= min(dynamicKillInterval
, dynamicUpdateInterval
);
984 * If we came out of sigsuspend() for any reason other than
985 * SIGALRM, pick up where we left off.
988 sleepSeconds
= alarmLeft
;
991 * Examine each configured AppClass for a process that needs
992 * starting. Compute the earliest time when the start should
993 * be attempted, starting it now if the time has passed. Also,
994 * remember that we do NOT need to restart externally managed
995 * FastCGI applications.
997 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
998 if (s
->directive
== APP_CLASS_EXTERNAL
|| s
->listenFd
< 0) {
1001 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1002 numChildren
= dynamicMaxClassProcs
;
1004 numChildren
= s
->numProcesses
;
1006 for (i
= 0; i
< numChildren
; i
++) {
1007 if ((s
->procs
[i
].pid
<= 0) &&
1008 (s
->procs
[i
].state
== STATE_NEEDS_STARTING
))
1012 if (s
->procs
[i
].pid
== 0) {
1013 restartTime
= s
->restartTime
+ s
->initStartDelay
;
1015 restartTime
= s
->restartTime
+ s
->restartDelay
;
1018 if(restartTime
<= now
) {
1019 int restart
= (s
->procs
[i
].pid
< 0);
1021 s
->restartTime
= now
;
1022 if (caughtSigTerm
) {
1023 goto ProcessSigTerm
;
1026 s
->procs
[i
].pid
= spawn_fs_process(s
);
1027 if (s
->procs
[i
].pid
<= 0) {
1028 ap_log_error(FCGI_LOG_CRIT
, fcgi_apache_main_server
,
1029 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1030 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1033 sleepSeconds
= min(sleepSeconds
,
1034 max(s
->restartDelay
, FCGI_MIN_EXEC_RETRY_DELAY
));
1036 ap_assert(s
->procs
[i
].pid
< 0);
1039 if (s
->directive
== APP_CLASS_DYNAMIC
) {
1041 fcgi_dynamic_total_proc_count
++;
1043 s
->procs
[i
].state
= STATE_STARTED
;
1048 if (fcgi_suexec
!= NULL
) {
1049 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1050 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1051 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1052 s
->fs_path
, (long)s
->uid
, (long)s
->gid
,
1053 restart
? "re" : "", (long)s
->procs
[i
].pid
);
1056 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1057 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1058 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1059 s
->fs_path
, restart
? "re" : "", (long)s
->procs
[i
].pid
);
1061 ap_assert(s
->procs
[i
].pid
> 0);
1063 sleepSeconds
= min(sleepSeconds
, restartTime
- now
);
1070 goto ProcessSigTerm
;
1072 if((!caughtSigChld
) && (!caughtSigUsr2
)) {
1075 alarm(sleepSeconds
);
1078 FD_SET(fcgi_pm_pipe
[0], &rfds
);
1079 read_ready
= ap_select(fcgi_pm_pipe
[0] + 1, &rfds
, NULL
, NULL
, NULL
);
1081 alarmLeft
= alarm(0);
1083 callWaitPid
= caughtSigChld
;
1084 caughtSigChld
= FALSE
;
1085 callDynamicProcs
= caughtSigUsr2
;
1086 caughtSigUsr2
= FALSE
;
1091 * Dynamic fcgi process management
1093 if((callDynamicProcs
) || (!callWaitPid
)) {
1094 dynamic_read_msgs(read_ready
);
1095 if(fcgi_dynamic_epoch
== 0) {
1096 fcgi_dynamic_epoch
= now
;
1098 if(((long)(now
-fcgi_dynamic_epoch
)>=dynamicKillInterval
) ||
1099 ((fcgi_dynamic_total_proc_count
+dynamicProcessSlack
)>=dynamicMaxProcs
)) {
1100 dynamic_kill_idle_fs_procs();
1101 fcgi_dynamic_epoch
= now
;
1109 /* We've caught SIGCHLD, so find out who it was using waitpid,
1110 * write a log message and update its data structure. */
1114 goto ProcessSigTerm
;
1116 childPid
= waitpid(-1, &waitStatus
, WNOHANG
);
1118 if (childPid
== -1 || childPid
== 0)
1121 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
1122 if (s
->directive
== APP_CLASS_EXTERNAL
)
1125 if (s
->directive
== APP_CLASS_DYNAMIC
)
1126 numChildren
= dynamicMaxClassProcs
;
1128 numChildren
= s
->numProcesses
;
1130 for (i
= 0; i
< numChildren
; i
++) {
1131 if (s
->procs
[i
].pid
== childPid
)
1136 /* @@@ This (comment) needs to go away when dynamic gets cleaned up.
1137 * If we get to this point, we have detected the
1138 * termination of the process that was spawned off by
1139 * the process manager to do a blocking kill above. */
1143 s
->procs
[i
].pid
= -1;
1145 if (s
->directive
== APP_CLASS_STANDARD
) {
1146 /* Always restart static apps */
1147 s
->procs
[i
].state
= STATE_NEEDS_STARTING
;
1152 fcgi_dynamic_total_proc_count
--;
1154 if (s
->procs
[i
].state
== STATE_VICTIM
) {
1155 s
->procs
[i
].state
= STATE_KILLED
;
1158 /* A dynamic app died or exited without provacation from the PM */
1161 if (dynamicAutoRestart
|| s
->numProcesses
<= 0)
1162 s
->procs
[i
].state
= STATE_NEEDS_STARTING
;
1164 s
->procs
[i
].state
= STATE_READY
;
1168 if (WIFEXITED(waitStatus
)) {
1169 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1170 "FastCGI:%s server \"%s\" (pid %d) terminated by calling exit with status '%d'",
1171 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1172 s
->fs_path
, (int)childPid
, WEXITSTATUS(waitStatus
));
1174 else if (WIFSIGNALED(waitStatus
)) {
1175 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1176 "FastCGI:%s server \"%s\" (pid %d) terminated due to uncaught signal '%d' (%s)%s",
1177 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1178 s
->fs_path
, (int)childPid
, WTERMSIG(waitStatus
), SYS_SIGLIST
[WTERMSIG(waitStatus
)],
1180 WCOREDUMP(waitStatus
) ? ", a core file may have been generated" : "");
1185 else if (WIFSTOPPED(waitStatus
)) {
1186 ap_log_error(FCGI_LOG_WARN_NOERRNO
, fcgi_apache_main_server
,
1187 "FastCGI:%s server \"%s\" (pid %d) stopped due to uncaught signal '%d' (%s)",
1188 (s
->directive
== APP_CLASS_DYNAMIC
) ? " (dynamic)" : "",
1189 s
->fs_path
, (int)childPid
, WTERMSIG(waitStatus
), SYS_SIGLIST
[WTERMSIG(waitStatus
)]);
1191 } /* for (;;), waitpid() */
1192 } /* for (;;), the whole shoot'n match */
1196 * Kill off the children, then exit.
1198 while (fcgi_servers
!= NULL
) {
1199 kill_fs_procs(fcgi_config_pool
, fcgi_servers
);