Fix stderr handling again
[mod_fastcgi.git] / fcgi_pm.c
blob5e2444695a6ae3e8988d3e2d250becabfdd7f8a7
1 /*
2 * $Id: fcgi_pm.c,v 1.20 1999/10/06 11:44:23 roberts Exp $
3 */
5 #include "fcgi.h"
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. */
16 struct FuncData {
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);
24 if (rc == -1) {
25 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
26 "FastCGI: seteuid(0) failed");
28 return rc;
31 static int seteuid_user(void)
33 int rc = seteuid(ap_user_id);
34 if (rc == -1) {
35 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
36 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id);
38 return rc;
41 static int fcgi_kill(pid_t pid, int sig)
43 int rc;
44 if (fcgi_suexec) {
45 seteuid_root();
47 rc = kill(pid, sig);
48 if (fcgi_suexec) {
49 seteuid_user();
51 return rc;
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;
62 int i, numChildren;
64 if (s->directive == APP_CLASS_DYNAMIC)
65 numChildren = dynamicMaxClassProcs;
66 else
67 numChildren = s->numProcesses;
69 for (i = 0; i < numChildren; i++, proc++) {
70 if (proc->pid > 0) {
71 fcgi_kill(proc->pid, SIGTERM);
72 proc->pid = -1;
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\"",
92 s->socket_path,
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);
110 } else {
111 int flag = 1;
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";
125 /* Set to listen */
126 if (listen(sock, backlog) != 0)
127 return "listen() failed";
129 return NULL;
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.
142 * Inputs
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.
147 * Notes
148 * Memory is allocated by the caller, but is freed by this
149 * function.
151 *----------------------------------------------------------------------
153 static void dynamic_blocking_kill(void *data)
155 struct FuncData *funcData = (struct FuncData *)data;
156 int lockFd;
158 ap_assert(funcData->lockFileName);
159 if ((lockFd = open(funcData->lockFileName, O_RDWR)) < 0) {
160 /* There is something terribly wrong here */
161 } else {
162 if (fcgi_wait_for_shared_write_lock(lockFd) < 0) {
163 /* This is a major problem */
164 } else {
165 fcgi_kill(funcData->pid, SIGTERM);
168 /* exit() may flush stdio buffers inherited from the parent. */
169 _exit(0);
173 *----------------------------------------------------------------------
175 * pm_main
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
181 * by SIGCHLD).
182 * - Catching SIGTERM and relaying it to all the FastCGI
183 * processes before exiting.
185 * Inputs:
186 * Uses global variable fcgi_servers.
188 * Results:
189 * Does not return.
191 * Side effects:
192 * Described above.
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.
224 * Results:
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.
232 * Side effects:
233 * Child process created.
235 *----------------------------------------------------------------------
238 static pid_t spawn_fs_process(const fcgi_server *fs)
240 pid_t child_pid;
241 int i;
242 char *dirName;
243 char *dnEnd, *failedSysCall;
245 child_pid = fork();
246 if (child_pid) {
247 return child_pid;
250 /* We're the child. We're gonna exec() so pools don't matter. */
252 dnEnd = strrchr(fs->fs_path, '/');
253 if (dnEnd == NULL) {
254 dirName = "./";
255 } else {
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;
264 #ifndef __EMX__
265 /* OS/2 dosen't support nice() */
266 if (fs->processPriority != 0) {
267 if (nice(fs->processPriority) == -1) {
268 failedSysCall = "nice()";
269 goto FailedSystemCallExit;
272 #endif
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) {
287 close(i);
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 */
299 seteuid_root();
300 setuid(ap_user_id);
302 do {
303 execle(fcgi_suexec, fcgi_suexec, fs->username, fs->group, shortName, NULL, fs->envp);
304 } while (errno == EINTR);
306 else {
307 do {
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));
317 exit(-1);
319 /* avoid an irrelevant compiler warning */
320 return(0);
323 static void reduce_priveleges(void)
325 char *name;
327 if (geteuid() != 0)
328 return;
330 #ifndef __EMX__
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);
336 if (ent == NULL) {
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);
340 exit(1);
342 name = ent->pw_name;
344 else
345 name = ap_user_name;
347 /* Change Group */
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);
351 exit(1);
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);
361 exit(1);
363 #endif /* __EMX__ */
365 /* Change User */
366 if (fcgi_suexec) {
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");
370 exit(1);
373 else {
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);
377 exit(1);
382 /*************
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 *----------------------------------------------------------------------
404 * dynamic_read_msgs
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)
414 fcgi_server *s;
415 int rc;
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;
423 pool *sp, *tp;
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)
439 break;
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) {
449 return;
452 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
453 if (rc <= 0) {
454 if (!caughtSigTerm) {
455 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
456 "FastCGI: read() from pipe failed (%d)", rc);
458 return;
460 buflen += rc;
461 buf[buflen] = '\0';
463 tp = ap_make_sub_pool(fcgi_config_pool);
465 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
466 int scan_failed = 0;
468 ptr2 = strchr(ptr1, '*');
469 if (ptr2) {
470 *ptr2++ = '\0';
472 else {
473 break;
476 opcode = *ptr1;
478 switch (opcode) {
479 case PLEASE_START:
480 if (sscanf(ptr1, "%c %s %16s %15s",
481 &opcode, execName, user, group) != 4)
483 scan_failed = 1;
485 break;
486 case CONN_TIMEOUT:
487 if (sscanf(ptr1, "%c %s %16s %15s",
488 &opcode, execName, user, group) != 4)
490 scan_failed = 1;
492 break;
493 case REQ_COMPLETE:
494 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
495 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
497 scan_failed = 1;
499 break;
500 default:
501 scan_failed = 1;
502 break;
505 if (scan_failed) {
506 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
507 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
508 continue;
511 s = fcgi_util_fs_get(execName, user, group);
513 if (s==NULL && opcode != REQ_COMPLETE) {
514 int fd;
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);
541 if (err) {
542 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
543 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
544 goto BagNewServer;
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);
551 goto BagNewServer;
554 /* bind() and listen() */
555 err = bind_n_listen(tp, s->socket_addr, s->socket_addr_len,
556 s->listenQueueDepth, s->listenFd);
557 if (err) {
558 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
559 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
560 goto BagNewServer;
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);
567 if (fd < 0) {
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",
570 execName, lockPath);
571 goto BagNewServer;
573 ap_pclosef(tp, fd);
575 /* If suexec is being used, config user/group info */
576 if (fcgi_suexec) {
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]);
581 if (!pw) {
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",
584 execName, &user[1]);
585 goto BagNewServer;
587 s->uid = pw->pw_uid;
588 s->user = ap_pstrdup(sp, user);
589 s->username = s->user;
591 s->gid = pw->pw_gid;
592 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
594 else {
595 struct passwd *pw;
597 s->uid = (uid_t)atol(user);
598 pw = getpwuid(s->uid);
599 if (!pw) {
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);
603 goto BagNewServer;
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);
612 fcgi_util_fs_add(s);
613 } else {
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
618 * restart them.
620 struct stat stbuf;
621 int i;
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)
641 continue;
642 } else {
643 /* we've been asked to start a process--only start
644 * it if we're not already running at least one
645 * instance.
647 int i;
649 for (i = 0; i < dynamicMaxClassProcs; i++) {
650 if (s->procs[i].state == STATE_STARTED)
651 break;
653 /* if already running, don't start another one */
654 if (i < dynamicMaxClassProcs) {
655 continue;
660 switch (opcode) {
661 int i;
662 time_t time_passed;
664 case PLEASE_START:
665 case CONN_TIMEOUT:
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)
672 continue;
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);
684 continue;
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)
692 continue;
694 if (s->procs[i].pid < 0) {
695 if (time_passed > s->restartDelay) {
696 schedule_start(s, i);
698 break;
700 else if (s->procs[i].pid == 0) {
701 if (time_passed > s->initStartDelay) {
702 schedule_start(s, i);
704 break;
708 break;
709 case REQ_COMPLETE:
710 /* only record stats if we have a structure */
711 if (s) {
712 s->totalConnTime += req_usec;
713 s->totalQueueTime += q_usec;
715 break;
718 continue;
720 BagNewServer:
721 ap_destroy_pool(sp);
724 if (ptr1 == buf) {
725 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
726 "FastCGI: really bogus message: \"%s\"", ptr1);
727 ptr1 += strlen(buf);
730 buflen -= ptr1 - buf;
731 if (buflen) {
732 memmove(buf, ptr1, buflen);
735 ap_destroy_pool(tp);
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.
746 * Side effects:
747 * Processes are marked for deletion possibly killed.
749 *----------------------------------------------------------------------
751 static void dynamic_kill_idle_fs_procs(void)
753 fcgi_server *s;
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
760 * last check */
761 float loadFactor; /* percentage, 0-100, of totalTime that
762 * the processes actually used */
763 int i, victims = 0;
764 const char *lockFileName;
765 int lockFd;
766 pid_t pid;
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)
773 continue;
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) {
779 break;
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))
791 int got_one = 0;
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;
796 got_one = 1;
798 else if (s->procs[i].state == STATE_VICTIM || s->procs[i].state == STATE_KILL) {
799 got_one = 1;
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);
809 victims++;
810 got_one = 1;
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)
819 continue;
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);
833 continue;
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;
849 if((pid=fork())<0) {
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);
853 continue;
854 } else if(pid==0) {
855 /* child */
857 /* rename the process for ps - best we can easily */
858 change_process_name("fcgiBlkKill");
860 dynamic_blocking_kill(funcData);
861 } else {
862 /* parent */
863 s->procs[i].state = STATE_VICTIM;
864 ap_pclosef(tp, lockFd);
866 } else {
867 s->procs[i].state = STATE_VICTIM;
868 fcgi_kill(s->procs[i].pid, SIGTERM);
869 ap_pclosef(tp, lockFd);
874 ap_destroy_pool(tp);
877 static void setup_signals(void)
879 sigset_t mask;
880 struct sigaction sa;
882 /* Ignore USR2 */
883 sigemptyset(&mask);
884 sigaddset(&mask, SIGUSR2);
885 sigprocmask(SIG_BLOCK, &mask, NULL);
887 /* Setup handlers */
889 sa.sa_handler = signal_handler;
890 sigemptyset(&sa.sa_mask);
891 sa.sa_flags = 0;
893 if (sigaction(SIGTERM, &sa, NULL) < 0) {
894 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
895 "sigaction(SIGTERM) failed");
897 /* httpd restart */
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)
920 fcgi_server *s;
921 int i, read_ready;
922 int callWaitPid, callDynamicProcs;
923 int alarmLeft = 0;
924 pool *tp;
925 const char *err;
927 reduce_priveleges();
929 close(fcgi_pm_pipe[1]);
930 change_process_name("fcgi-pm");
931 setup_signals();
933 if (fcgi_suexec) {
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)
942 continue;
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);
949 continue;
952 /* bind() and listen() */
953 err = bind_n_listen(tp, s->socket_addr, s->socket_addr_len,
954 s->listenQueueDepth, s->listenFd);
955 if (err) {
956 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
957 "FastCGI: server \"%s\" disabled: %s",
958 s->fs_path, err);
959 ap_pclosesocket(fcgi_config_pool, s->listenFd);
960 s->listenFd = -1;
961 continue;
964 for (i = 0; i < s->numProcesses; i++)
965 s->procs[i].state = STATE_NEEDS_STARTING;
967 ap_destroy_pool(tp);
969 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
970 "FastCGI: process manager initialized (pid %ld)", (long)getpid());
972 now = time(NULL);
975 * Loop until SIGTERM
977 for (;;) {
978 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
979 pid_t childPid;
980 int waitStatus;
981 int numChildren;
984 * If we came out of sigsuspend() for any reason other than
985 * SIGALRM, pick up where we left off.
987 if (alarmLeft)
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) {
999 continue;
1001 if (s->directive == APP_CLASS_DYNAMIC) {
1002 numChildren = dynamicMaxClassProcs;
1003 } else {
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))
1010 time_t restartTime;
1012 if (s->procs[i].pid == 0) {
1013 restartTime = s->restartTime + s->initStartDelay;
1014 } else {
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)" : "",
1031 s->fs_path);
1033 sleepSeconds = min(sleepSeconds,
1034 max(s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1036 ap_assert(s->procs[i].pid < 0);
1037 break;
1039 if (s->directive == APP_CLASS_DYNAMIC) {
1040 s->numProcesses++;
1041 fcgi_dynamic_total_proc_count++;
1043 s->procs[i].state = STATE_STARTED;
1045 if (restart)
1046 s->numRestarts++;
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);
1055 else {
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);
1062 } else {
1063 sleepSeconds = min(sleepSeconds, restartTime - now);
1069 if(caughtSigTerm) {
1070 goto ProcessSigTerm;
1072 if((!caughtSigChld) && (!caughtSigUsr2)) {
1073 fd_set rfds;
1075 alarm(sleepSeconds);
1077 FD_ZERO(&rfds);
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;
1088 now = time(NULL);
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;
1105 if(!callWaitPid) {
1106 continue;
1109 /* We've caught SIGCHLD, so find out who it was using waitpid,
1110 * write a log message and update its data structure. */
1112 for (;;) {
1113 if (caughtSigTerm)
1114 goto ProcessSigTerm;
1116 childPid = waitpid(-1, &waitStatus, WNOHANG);
1118 if (childPid == -1 || childPid == 0)
1119 break;
1121 for (s = fcgi_servers; s != NULL; s = s->next) {
1122 if (s->directive == APP_CLASS_EXTERNAL)
1123 continue;
1125 if (s->directive == APP_CLASS_DYNAMIC)
1126 numChildren = dynamicMaxClassProcs;
1127 else
1128 numChildren = s->numProcesses;
1130 for (i = 0; i < numChildren; i++) {
1131 if (s->procs[i].pid == childPid)
1132 goto ChildFound;
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. */
1140 continue;
1142 ChildFound:
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;
1148 s->numFailures++;
1150 else {
1151 s->numProcesses--;
1152 fcgi_dynamic_total_proc_count--;
1154 if (s->procs[i].state == STATE_VICTIM) {
1155 s->procs[i].state = STATE_KILLED;
1157 else {
1158 /* A dynamic app died or exited without provacation from the PM */
1159 s->numFailures++;
1161 if (dynamicAutoRestart || s->numProcesses <= 0)
1162 s->procs[i].state = STATE_NEEDS_STARTING;
1163 else
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)],
1179 #ifdef WCOREDUMP
1180 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1181 #else
1182 "");
1183 #endif
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 */
1194 ProcessSigTerm:
1196 * Kill off the children, then exit.
1198 while (fcgi_servers != NULL) {
1199 kill_fs_procs(fcgi_config_pool, fcgi_servers);
1201 exit(0);