Reccomend against using appConnTimeout
[mod_fastcgi.git] / fcgi_pm.c
blob71ebd62075262ca04cdafca720d3ec7fa78545a5
1 /*
2 * $Id: fcgi_pm.c,v 1.29 2000/05/10 05:15:48 robs Exp $
3 */
6 #include "fcgi.h"
8 #ifdef WIN32
9 #include "multithread.h"
10 #endif
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. */
22 struct FuncData {
23 #ifndef WIN32
24 const char *lockFileName; /* name of the lock file to lock */
25 pid_t pid; /* process to issue SIGTERM to */
26 #else
27 FcgiRWLock *lock; /* reader/writer lock for dynamic app */
28 HANDLE pid; /* process to issue SIGTERM to */
29 #endif
32 #ifdef WIN32
33 static BOOL bTimeToDie = FALSE; /* process termination flag */
34 HANDLE fcgi_event_handles[3];
35 #endif
38 #ifndef WIN32
39 static int seteuid_root(void)
41 int rc = seteuid((uid_t)0);
42 if (rc == -1) {
43 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
44 "FastCGI: seteuid(0) failed");
46 return rc;
49 static int seteuid_user(void)
51 int rc = seteuid(ap_user_id);
52 if (rc == -1) {
53 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
54 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id);
56 return rc;
58 #endif
60 static int fcgi_kill(pid_t pid, int sig)
62 int rc;
63 #ifndef WIN32
64 if (fcgi_suexec) {
65 seteuid_root();
67 rc = kill(pid, sig);
68 if (fcgi_suexec) {
69 seteuid_user();
71 #else
72 rc = TerminateProcess((HANDLE) pid, sig);
73 #endif
74 return rc;
77 /*******************************************************************************
78 * Send SIGTERM to each process in the server class, remove socket and lock
79 * file if appropriate. Currently this is only called when the PM is shutting
80 * down and thus memory isn't freed and sockets and files aren't closed.
82 static void kill_fs_procs(pool *p, fcgi_server *s)
84 ServerProcess *proc = s->procs;
85 int i, numChildren;
87 if (s->directive == APP_CLASS_DYNAMIC)
88 numChildren = dynamicMaxClassProcs;
89 else
90 numChildren = s->numProcesses;
92 for (i = 0; i < numChildren; i++, proc++) {
93 #ifndef WIN32
94 if (proc->pid > 0) {
95 fcgi_kill(proc->pid, SIGTERM);
96 proc->pid = -1;
98 #else
99 if (proc->pid != INVALID_HANDLE_VALUE) {
100 fcgi_kill((int) proc->pid, 1);
101 CloseHandle((HANDLE) proc->pid);
102 proc->pid = INVALID_HANDLE_VALUE;
104 #endif
107 /* Remove the dead lock file */
108 if (s->directive == APP_CLASS_DYNAMIC) {
109 #ifndef WIN32
110 const char *lockFileName = fcgi_util_socket_get_lock_filename(p, s->socket_path);
112 if (unlink(lockFileName) != 0) {
113 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
114 "FastCGI: unlink() failed to remove lock file \"%s\" for (dynamic) server \"%s\"",
115 lockFileName, s->fs_path);
117 #else
118 fcgi_rdwr_destroy(s->dynamic_lock);
119 #endif
122 /* Remove the socket file */
123 if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL) {
124 #ifndef WIN32
125 if (unlink(s->socket_path) != 0) {
126 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
127 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
128 s->socket_path,
129 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path);
131 #else
132 CloseHandle((HANDLE)s->listenFd);
133 #endif
135 fcgi_servers = s->next;
138 /*******************************************************************************
139 * Bind an address to a socket and set it to listen for incoming connects.
140 * The error messages are allocated from the pool p, use temp storage.
141 * Don't forget to close the socket, if an error occurs.
143 static const char *bind_n_listen(pool *p, struct sockaddr *socket_addr,
144 int socket_addr_len, int backlog, int sock)
146 #ifndef WIN32
147 if (socket_addr->sa_family == AF_UNIX) {
148 /* Remove any existing socket file.. just in case */
149 unlink(((struct sockaddr_un *)socket_addr)->sun_path);
151 else
152 #endif
154 int flag = 1;
155 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
158 /* Bind it to the socket_addr */
159 if (bind(sock, socket_addr, socket_addr_len) != 0)
160 return "bind() failed";
162 #ifndef WIN32
163 /* Twiddle permissions */
164 if (socket_addr->sa_family == AF_UNIX) {
165 if (chmod(((struct sockaddr_un *)socket_addr)->sun_path, S_IRUSR | S_IWUSR))
166 return "chmod() of socket failed";
168 #endif
170 /* Set to listen */
171 if (listen(sock, backlog) != 0)
172 return "listen() failed";
174 return NULL;
178 *----------------------------------------------------------------------
180 * dynamic_blocking_kill
182 * Block on the lock file until it is available, and then
183 * issue a kill signal to the corresponding application.
184 * Since this function is executed in the child process,
185 * _exit() is called upon completion.
187 * Inputs
188 * Pointer to the data structure containing a process id to
189 * issue a signal to and the full pathname to the lockfile
190 * that needs to be locked before the issue of the signal.
192 * Notes
193 * Memory is allocated by the caller, but is freed by this
194 * function.
196 *----------------------------------------------------------------------
198 static void dynamic_blocking_kill(void *data)
200 struct FuncData *funcData = (struct FuncData *)data;
201 #ifndef WIN32
202 int lockFd;
204 ap_assert(funcData->lockFileName);
205 if ((lockFd = open(funcData->lockFileName, O_RDWR)) < 0) {
206 /* There is something terribly wrong here */
207 } else {
208 if (fcgi_wait_for_shared_write_lock(lockFd) < 0) {
209 /* This is a major problem */
210 } else {
211 fcgi_kill(funcData->pid, SIGTERM);
214 /* exit() may flush stdio buffers inherited from the parent. */
215 _exit(0);
216 #else
217 ap_assert(funcData->lock);
218 if (fcgi_wait_for_shared_write_lock(funcData->lock) < 0) {
219 // This is a major problem
221 else {
222 TerminateProcess(funcData->pid, 1);
223 fcgi_rdwr_unlock(funcData->lock, WRITER);
225 return;
226 #endif
230 *----------------------------------------------------------------------
232 * pm_main
234 * The FastCGI process manager, which runs as a separate
235 * process responsible for:
236 * - Starting all the FastCGI proceses.
237 * - Restarting any of these processes that die (indicated
238 * by SIGCHLD).
239 * - Catching SIGTERM and relaying it to all the FastCGI
240 * processes before exiting.
242 * Inputs:
243 * Uses global variable fcgi_servers.
245 * Results:
246 * Does not return.
248 * Side effects:
249 * Described above.
251 *----------------------------------------------------------------------
253 #ifndef WIN32
254 static int caughtSigTerm = FALSE;
255 static int caughtSigChld = FALSE;
256 static int caughtSigUsr2 = FALSE;
258 static void signal_handler(int signo)
260 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
261 /* SIGUSR1 & SIGHUP are sent by apache to its process group
262 * when apache get 'em. Apache follows up (1.2.x) with attacks
263 * on each of its child processes, but we've got the KillMgr
264 * sitting between us so we never see the KILL. The main loop
265 * in ProcMgr also checks to see if the KillMgr has terminated,
266 * and if it has, we handl it as if we should shutdown too. */
267 caughtSigTerm = TRUE;
268 } else if(signo == SIGCHLD) {
269 caughtSigChld = TRUE;
270 } else if(signo == SIGALRM) {
271 caughtSigUsr2 = TRUE;
274 #endif
277 *----------------------------------------------------------------------
279 * spawn_fs_process --
281 * Fork and exec the specified fcgi process.
283 * Results:
284 * 0 for successful fork, -1 for failed fork.
286 * In case the child fails before or in the exec, the child
287 * obtains the error log by calling getErrLog, logs
288 * the error, and exits with exit status = errno of
289 * the failed system call.
291 * Side effects:
292 * Child process created.
294 *----------------------------------------------------------------------
297 #ifndef WIN32
298 static pid_t spawn_fs_process(const fcgi_server *fs)
299 #else
300 static HANDLE spawn_fs_process(fcgi_server *fs)
301 #endif
303 #ifndef WIN32
304 pid_t child_pid;
305 int i;
306 char *dirName;
307 char *dnEnd, *failedSysCall;
309 child_pid = fork();
310 if (child_pid) {
311 return child_pid;
314 /* We're the child. We're gonna exec() so pools don't matter. */
316 dnEnd = strrchr(fs->fs_path, '/');
317 if (dnEnd == NULL) {
318 dirName = "./";
319 } else {
320 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
321 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
323 if (chdir(dirName) < 0) {
324 failedSysCall = "chdir()";
325 goto FailedSystemCallExit;
328 #ifndef __EMX__
329 /* OS/2 dosen't support nice() */
330 if (fs->processPriority != 0) {
331 if (nice(fs->processPriority) == -1) {
332 failedSysCall = "nice()";
333 goto FailedSystemCallExit;
336 #endif
338 /* Open the listenFd on spec'd fd */
339 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
340 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
342 /* Close all other open fds, except stdout/stderr. Leave these two open so
343 * FastCGI applications don't have to find and fix ALL 3rd party libs that
344 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
345 * main server error_log - @@@ provide a directive control where this goes.
347 ap_error_log2stderr(fcgi_apache_main_server);
348 dup2(STDERR_FILENO, STDOUT_FILENO);
349 for (i = 0; i < MAX_OPEN_FDS; i++) {
350 if (i != FCGI_LISTENSOCK_FILENO && i != STDERR_FILENO && i != STDOUT_FILENO) {
351 close(i);
355 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
356 * install its own handler. */
357 signal(SIGPIPE, SIG_IGN);
359 if (fcgi_suexec != NULL) {
360 char *shortName = strrchr(fs->fs_path, '/') + 1;
362 /* Relinquish our root real uid powers */
363 seteuid_root();
364 setuid(ap_user_id);
366 do {
367 execle(fcgi_suexec, fcgi_suexec, fs->username, fs->group, shortName, NULL, fs->envp);
368 } while (errno == EINTR);
370 else {
371 do {
372 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
373 } while (errno == EINTR);
376 failedSysCall = "execle()";
378 FailedSystemCallExit:
379 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
380 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
381 exit(-1);
383 /* avoid an irrelevant compiler warning */
384 return(0);
385 #else
386 int i = 0;
387 int EnvBlockLen = 1;
388 int success = 0;
389 char * EnvBlock = NULL;
390 char * Next;
391 char * mutex = NULL;
392 HANDLE child_process;
394 STARTUPINFO si;
395 PROCESS_INFORMATION pi;
396 pool * tp =ap_make_sub_pool(fcgi_config_pool);
398 memset(&si, 0, sizeof(si));
399 memset(&pi, 0, sizeof(pi));
401 si.cb = sizeof(si);
402 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
403 si.wShowWindow = SW_HIDE;
404 si.hStdInput = (HANDLE) fs->listenFd;
405 si.hStdOutput = INVALID_HANDLE_VALUE;
406 si.hStdError = INVALID_HANDLE_VALUE;
408 // There is always at least SystemRoot under Win32
409 while (fs->envp[i]) {
410 EnvBlockLen += strlen(fs->envp[i]) + 1;
411 i++;
414 if (fs->socket_path) {
415 mutex = ap_psprintf(tp, "_FCGI_MUTEX_=%d", (int) fs->hPipeMutex);
416 EnvBlockLen += strlen(mutex) + 1;
419 EnvBlock = (char *) ap_pcalloc(tp, EnvBlockLen);
421 // These are supposed to be sorted, oh well
422 i = 0;
423 Next = EnvBlock;
424 while (fs->envp[i]) {
425 strcpy(Next, fs->envp[i]);
426 Next = Next + strlen(Next) + 1;
427 i++;
430 if (fs->socket_path) {
431 strcpy(Next, mutex);
434 success = SetHandleInformation(si.hStdInput, HANDLE_FLAG_INHERIT, TRUE);
436 success = CreateProcess(NULL, (char *)fs->fs_path, NULL, NULL, TRUE, 0, EnvBlock,
437 ap_make_dirstr_parent(tp, fs->fs_path), &si, &pi);
439 ap_destroy_pool(tp);
441 if (success) {
442 child_process = pi.hProcess;
443 CloseHandle(pi.hThread);
445 return(child_process);
447 else {
448 return INVALID_HANDLE_VALUE;
450 #endif
453 #ifndef WIN32
454 static void reduce_priveleges(void)
456 char *name;
458 if (geteuid() != 0)
459 return;
461 #ifndef __EMX__
462 /* Get username if passed as a uid */
463 if (ap_user_name[0] == '#') {
464 uid_t uid = atoi(&ap_user_name[1]);
465 struct passwd *ent = getpwuid(uid);
467 if (ent == NULL) {
468 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
469 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
470 "you probably need to modify the User directive", (unsigned)uid);
471 exit(1);
473 name = ent->pw_name;
475 else
476 name = ap_user_name;
478 /* Change Group */
479 if (setgid(ap_group_id) == -1) {
480 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
481 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
482 exit(1);
485 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
487 /* Initialize supplementary groups */
488 if (initgroups(name, ap_group_id) == -1) {
489 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
490 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
491 name, (unsigned)ap_group_id);
492 exit(1);
494 #endif /* __EMX__ */
496 /* Change User */
497 if (fcgi_suexec) {
498 if (seteuid_user() == -1) {
499 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
500 "FastCGI: process manager exiting, failed to reduce priveleges");
501 exit(1);
504 else {
505 if (setuid(ap_user_id) == -1) {
506 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
507 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
508 exit(1);
513 /*************
514 * Change the name of this process - best we can easily.
516 static void change_process_name(const char * const name)
518 strncpy(ap_server_argv0, name, strlen(ap_server_argv0));
520 #endif
522 static void schedule_start(fcgi_server *s, int proc)
524 s->procs[proc].state = STATE_NEEDS_STARTING;
525 if (proc == (int)dynamicMaxClassProcs - 1) {
526 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
527 "FastCGI: scheduled the %sstart of the last (dynamic) server "
528 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
529 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
534 *----------------------------------------------------------------------
536 * dynamic_read_msgs
538 * Removes the records written by request handlers and decodes them.
539 * We also update the data structures to reflect the changes.
541 *----------------------------------------------------------------------
544 static void dynamic_read_msgs(int read_ready)
546 fcgi_server *s;
548 #ifndef WIN32
549 int rc;
550 static int buflen = 0;
551 static char buf[FCGI_MSGS_BUFSIZE + 1];
552 char *ptr1, *ptr2, opcode;
553 char execName[FCGI_MAXPATH + 1];
554 char user[MAX_USER_NAME_LEN + 2];
555 char group[MAX_GID_CHAR_LEN + 1];
556 unsigned long q_usec = 0UL, req_usec = 0UL;
557 #else
558 fcgi_pm_job *joblist = NULL;
559 fcgi_pm_job *cjob = NULL;
560 SECURITY_ATTRIBUTES sa;
561 #endif
563 pool *sp, *tp;
565 #ifndef WIN32
566 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
567 #endif
570 * To prevent the idle application from running indefinitely, we
571 * check the timer and if it is expired, we recompute the values
572 * for each running application class. Then, when REQ_COMPLETE
573 * message is recieved, only updates are made to the data structures.
575 if (fcgi_dynamic_last_analyzed == 0) {
576 fcgi_dynamic_last_analyzed = now;
578 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
579 for (s = fcgi_servers; s != NULL; s = s->next) {
580 if (s->directive != APP_CLASS_DYNAMIC)
581 break;
582 /* XXX what does this adjustment do? */
583 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
584 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
585 s->totalConnTime = 0UL;
586 s->totalQueueTime = 0UL;
590 if (read_ready <= 0) {
591 return;
594 #ifndef WIN32
595 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
596 if (rc <= 0) {
597 if (!caughtSigTerm) {
598 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
599 "FastCGI: read() from pipe failed (%d)", rc);
601 return;
603 buflen += rc;
604 buf[buflen] = '\0';
605 #else
606 /* Obtain the data from the fcgi_dynamic_mbox file */
607 ap_acquire_mutex(fcgi_dynamic_mbox_mutex);
609 if (fcgi_dynamic_mbox == NULL) {
610 return;
612 else {
613 joblist = fcgi_dynamic_mbox;
614 fcgi_dynamic_mbox = NULL;
617 ap_release_mutex(fcgi_dynamic_mbox_mutex);
619 cjob = joblist;
620 #endif
622 tp = ap_make_sub_pool(fcgi_config_pool);
624 #ifndef WIN32
625 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
626 int scan_failed = 0;
628 ptr2 = strchr(ptr1, '*');
629 if (ptr2) {
630 *ptr2++ = '\0';
632 else {
633 break;
636 opcode = *ptr1;
638 switch (opcode) {
639 case PLEASE_START:
640 if (sscanf(ptr1, "%c %s %16s %15s",
641 &opcode, execName, user, group) != 4)
643 scan_failed = 1;
645 break;
646 case CONN_TIMEOUT:
647 if (sscanf(ptr1, "%c %s %16s %15s",
648 &opcode, execName, user, group) != 4)
650 scan_failed = 1;
652 break;
653 case REQ_COMPLETE:
654 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
655 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
657 scan_failed = 1;
659 break;
660 default:
661 scan_failed = 1;
662 break;
665 if (scan_failed) {
666 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
667 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
668 continue;
670 #else
671 /* Update data structures for processing */
672 while (cjob != NULL) {
673 joblist = cjob->next;
674 #endif
676 #ifndef WIN32
677 s = fcgi_util_fs_get(execName, user, group);
678 #else
679 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
680 #endif
682 #ifndef WIN32
683 if (s==NULL && opcode != REQ_COMPLETE)
684 #else
685 if (s==NULL && cjob->id != REQ_COMPLETE)
686 #endif
688 #ifndef WIN32
689 int fd;
690 const char *err, *lockPath;
691 #endif
693 /* Create a perm subpool to hold the new server data,
694 * we can destroy it if something doesn't pan out */
695 sp = ap_make_sub_pool(fcgi_config_pool);
697 /* Create a new "dynamic" server */
698 s = fcgi_util_fs_new(sp);
699 s->directive = APP_CLASS_DYNAMIC;
700 s->restartDelay = dynamicRestartDelay;
701 s->listenQueueDepth = dynamicListenQueueDepth;
702 s->initStartDelay = dynamicInitStartDelay;
703 s->envp = dynamicEnvp;
704 #ifndef WIN32
705 ap_getparents(execName);
706 ap_no2slash(execName);
707 s->fs_path = ap_pstrdup(sp, execName);
708 #else
709 ap_getparents(cjob->fs_path);
710 ap_no2slash(cjob->fs_path);
711 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
712 #endif
713 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
715 #ifndef WIN32
716 /* Create socket file's path */
717 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
718 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
720 /* Create sockaddr, prealloc it so it won't get created in tp */
721 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
722 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
723 &s->socket_addr_len, s->socket_path);
724 if (err) {
725 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
726 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
727 goto BagNewServer;
730 /* Create the socket */
731 if ((s->listenFd = ap_psocket(sp, s->socket_addr->sa_family, SOCK_STREAM, 0)) < 0) {
732 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
733 "FastCGI: can't create (dynamic) server \"%s\": socket() failed", execName);
734 goto BagNewServer;
737 /* bind() and listen() */
738 err = bind_n_listen(tp, s->socket_addr, s->socket_addr_len,
739 s->listenQueueDepth, s->listenFd);
740 if (err) {
741 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
742 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
743 goto BagNewServer;
746 /* Create the lock file */
747 lockPath = fcgi_util_socket_get_lock_filename(tp, s->socket_path);
748 fd = ap_popenf(tp, lockPath,
749 O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
750 if (fd < 0) {
751 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
752 "FastCGI: can't create (dynamic) server \"%s\": can't open lock file \"%s\": popenf() failed",
753 execName, lockPath);
754 goto BagNewServer;
756 ap_pclosef(tp, fd);
758 /* If suexec is being used, config user/group info */
759 if (fcgi_suexec) {
760 if (user[0] == '~') {
761 /* its a user dir uri, the rest is a username, not a uid */
762 struct passwd *pw = getpwnam(&user[1]);
764 if (!pw) {
765 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
766 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for suexec: getpwnam(%s) failed",
767 execName, &user[1]);
768 goto BagNewServer;
770 s->uid = pw->pw_uid;
771 s->user = ap_pstrdup(sp, user);
772 s->username = s->user;
774 s->gid = pw->pw_gid;
775 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
777 else {
778 struct passwd *pw;
780 s->uid = (uid_t)atol(user);
781 pw = getpwuid(s->uid);
782 if (!pw) {
783 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
784 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for suexec: getwpuid(%ld) failed",
785 execName, (long)s->uid);
786 goto BagNewServer;
788 s->user = ap_pstrdup(sp, user);
789 s->username = ap_pstrdup(sp, pw->pw_name);
791 s->gid = (gid_t)atol(group);
792 s->group = ap_pstrdup(sp, group);
795 #else
796 /* Create socket file's path */
797 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
798 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
800 /* Create Named Pipe and I/O mutex*/
801 sa.nLength = sizeof(sa);
802 sa.lpSecurityDescriptor = NULL;
803 sa.bInheritHandle = TRUE;
805 s->listenFd = (int)CreateNamedPipe(s->socket_path,
806 PIPE_ACCESS_DUPLEX,
807 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
808 PIPE_UNLIMITED_INSTANCES, 4096,4096,0, &sa);
810 if ((HANDLE)s->listenFd == INVALID_HANDLE_VALUE) {
811 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
812 "FASTCGI: can't create (dynamic) server \"%s\": CreateNamePipe() failed",
813 cjob->fs_path);
814 goto BagNewServer;
817 s->hPipeMutex = ap_create_mutex(NULL);
819 if (s->hPipeMutex == NULL) {
820 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
821 "FastCGI: can't create (dynamic) server \"%s\": ap_create_mutex() failed", cjob->fs_path);
822 CloseHandle((HANDLE)s->listenFd);
823 goto BagNewServer;
826 SetHandleInformation(s->hPipeMutex, HANDLE_FLAG_INHERIT, TRUE);
828 /* Create the application lock */
829 if ((s->dynamic_lock = fcgi_rdwr_create()) == NULL) {
830 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
831 "FastCGI: can;t create (dynamic) server \"%s\": fcgi_rdwr_create() failed",
832 cjob->fs_path);
833 CloseHandle((HANDLE)s->listenFd);
834 CloseHandle(s->hPipeMutex);
835 goto BagNewServer;
837 #endif
839 fcgi_util_fs_add(s);
841 else {
842 #ifndef WIN32
843 if (opcode == PLEASE_START) {
844 #else
845 if(cjob->id==PLEASE_START) {
846 #endif
847 if (dynamicAutoUpdate) {
848 /* Check to see if the binary has changed. If so,
849 * kill the FCGI application processes, and
850 * restart them.
852 struct stat stbuf;
853 unsigned int i;
855 #ifndef WIN32
856 if ((stat(execName, &stbuf)==0) &&
857 #else
858 if ((stat(cjob->fs_path, &stbuf)==0) &&
859 #endif
860 (stbuf.st_mtime > s->restartTime)) {
861 /* kill old server(s) */
862 for (i = 0; i < dynamicMaxClassProcs; i++) {
863 #ifndef WIN32
864 if (s->procs[i].pid > 0) {
865 fcgi_kill(s->procs[i].pid, SIGTERM);
867 #else
868 if (s->procs[i].pid != INVALID_HANDLE_VALUE) {
869 fcgi_kill((int) s->procs[i].pid, 1);
871 #endif
874 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
875 "FastCGI: restarting server \"%s\" processes, newer version found",
876 #ifndef WIN32
877 execName);
878 #else
879 cjob->fs_path);
880 #endif
883 /* If dynamicAutoRestart, don't mark any new processes
884 * for starting because we probably got the
885 * PLEASE_START due to dynamicAutoUpdate and the ProcMgr
886 * will be restarting all of those we just killed.
888 if (dynamicAutoRestart)
889 continue;
890 } else {
891 /* we've been asked to start a process--only start
892 * it if we're not already running at least one
893 * instance.
895 unsigned int i;
897 for (i = 0; i < dynamicMaxClassProcs; i++) {
898 if (s->procs[i].state == STATE_STARTED)
899 break;
901 /* if already running, don't start another one */
902 if (i < dynamicMaxClassProcs) {
903 continue;
908 #ifndef WIN32
909 switch (opcode)
910 #else
911 switch (cjob->id)
912 #endif
914 unsigned int i;
915 time_t time_passed;
917 case PLEASE_START:
918 case CONN_TIMEOUT:
919 /* If we've started one recently, don't register another */
920 time_passed = now - s->restartTime;
922 if (time_passed < (int) s->initStartDelay
923 && time_passed < (int) s->restartDelay)
925 continue;
928 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
930 * Extra instances should have been
931 * terminated beforehand, probably need
932 * to increase ProcessSlack parameter
934 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
935 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
936 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
937 continue;
939 /* find next free slot */
940 for (i = 0; i < dynamicMaxClassProcs; i++) {
941 if (s->procs[i].state != STATE_READY
942 && s->procs[i].state != STATE_NEEDS_STARTING
943 && s->procs[i].state != STATE_KILLED)
945 continue;
947 #ifndef WIN32
948 if (s->procs[i].pid < 0)
949 #else
950 if (s->procs[i].pid == INVALID_HANDLE_VALUE)
951 #endif
953 if (time_passed > (int)s->restartDelay) {
954 schedule_start(s, i);
956 break;
958 #ifndef WIN32
959 else if (s->procs[i].pid == 0)
960 #else
961 else if (s->procs[i].pid == (HANDLE) 0)
962 #endif
964 if (time_passed > (int)s->initStartDelay) {
965 schedule_start(s, i);
967 break;
971 break;
972 case REQ_COMPLETE:
973 /* only record stats if we have a structure */
974 if (s) {
975 #ifndef WIN32
976 s->totalConnTime += req_usec;
977 s->totalQueueTime += q_usec;
978 #else
979 s->totalConnTime += cjob->start_time;
980 s->totalQueueTime += cjob->qsec;
981 #endif
983 break;
986 #ifdef WIN32
987 /* Cleanup job data */
988 free(cjob->fs_path);
989 free(cjob->user);
990 free(cjob->group);
991 free(cjob);
992 cjob = joblist;
993 #endif
995 continue;
997 BagNewServer:
998 ap_destroy_pool(sp);
1000 #ifdef WIN32
1001 free(cjob->fs_path);
1002 free(cjob);
1003 cjob = joblist;
1004 #endif
1007 #ifndef WIN32
1008 if (ptr1 == buf) {
1009 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1010 "FastCGI: really bogus message: \"%s\"", ptr1);
1011 ptr1 += strlen(buf);
1014 buflen -= ptr1 - buf;
1015 if (buflen) {
1016 memmove(buf, ptr1, buflen);
1018 #endif
1020 ap_destroy_pool(tp);
1024 *----------------------------------------------------------------------
1026 * dynamic_kill_idle_fs_procs
1028 * Implement a kill policy for the dynamic FastCGI applications.
1029 * We also update the data structures to reflect the changes.
1031 * Side effects:
1032 * Processes are marked for deletion possibly killed.
1034 *----------------------------------------------------------------------
1036 static void dynamic_kill_idle_fs_procs(void)
1038 fcgi_server *s;
1039 struct FuncData *funcData = NULL;
1040 unsigned long connTime; /* server's smoothed running time, or
1041 * if that's 0, the current total */
1042 unsigned long totalTime; /* maximum number of microseconds that all
1043 * of a server's running processes together
1044 * could have spent running since the
1045 * last check */
1046 double loadFactor; /* percentage, 0-100, of totalTime that
1047 * the processes actually used */
1048 unsigned int i, victims = 0;
1049 #ifndef WIN32
1050 const char *lockFileName;
1051 int lockFd;
1052 pid_t pid;
1053 #endif
1054 pool *tp = ap_make_sub_pool(fcgi_config_pool);
1056 /* pass 1 - locate and mark all victims */
1057 for(s=fcgi_servers; s!=NULL; s=s->next) {
1058 /* Only kill dynamic apps */
1059 if (s->directive != APP_CLASS_DYNAMIC)
1060 continue;
1062 /* If the number of non-victims is less than or equal to
1063 the minimum that may be running without being killed off,
1064 don't select any more victims. */
1065 if ((fcgi_dynamic_total_proc_count - victims) <= (int) dynamicMinProcs) {
1066 break;
1069 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1070 totalTime = (s->numProcesses)*(now - fcgi_dynamic_epoch)*1000000 + 1;
1072 /* XXX producing a heavy load with one client, I haven't been
1073 able to achieve a loadFactor greater than 0.5. Perhaps this
1074 should be scaled up by another order of magnitude or two. */
1075 loadFactor = connTime/totalTime*100.0;
1077 if ((s->numProcesses > 1
1078 && s->numProcesses/(s->numProcesses - 1)*loadFactor < dynamicThreshholdN)
1079 || (s->numProcesses == 1 && loadFactor < dynamicThreshhold1))
1081 int got_one = 0;
1083 for (i = 0; !got_one && i < dynamicMaxClassProcs; ++i) {
1084 if (s->procs[i].state == STATE_NEEDS_STARTING) {
1085 s->procs[i].state = STATE_READY;
1086 got_one = 1;
1088 else if (s->procs[i].state == STATE_VICTIM || s->procs[i].state == STATE_KILL) {
1089 got_one = 1;
1093 for (i = 0; !got_one && i < dynamicMaxClassProcs; ++i) {
1094 if (s->procs[i].state == STATE_STARTED) {
1095 s->procs[i].state = STATE_KILL;
1096 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1097 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination scheduled",
1098 s->fs_path, s->procs[i].pid);
1099 victims++;
1100 got_one = 1;
1106 /* pass 2 - kill procs off */
1107 for(s=fcgi_servers; s!=NULL; s=s->next) {
1108 /* Only kill dynamic apps */
1109 if (s->directive != APP_CLASS_DYNAMIC)
1110 continue;
1112 for(i = 0; i < dynamicMaxClassProcs; i++) {
1113 if (s->procs[i].state == STATE_KILL) {
1114 #ifndef WIN32
1115 lockFileName = fcgi_util_socket_get_lock_filename(tp, s->socket_path);
1116 if ((lockFd = ap_popenf(tp, lockFileName, O_RDWR, 0))<0) {
1118 * If we need to kill an application and the
1119 * corresponding lock file does not exist, then
1120 * that means we are in big trouble here
1122 /*@@@ this should be logged, but since all the lock
1123 * file stuff will be tossed, I'll leave it now */
1124 ap_pclosef(tp, lockFd);
1125 continue;
1128 if (fcgi_get_exclusive_write_lock_no_wait(lockFd) < 0) {
1129 #else
1130 if (fcgi_get_exclusive_write_lock_no_wait(s->dynamic_lock) < 0) {
1131 DWORD tid;
1132 #endif
1134 * Unable to lock the lockfile, indicative
1135 * of WS performing operation with the given
1136 * application class. The simplest solution
1137 * is to spawn off another process and block
1138 * on lock to kill it. This is under assumptions
1139 * that fork() is not very costly and this
1140 * situation occurs very rarely, which it should
1142 funcData = ap_pcalloc(tp, sizeof(struct FuncData));
1143 #ifndef WIN32
1144 funcData->lockFileName = lockFileName;
1145 #else
1146 funcData->lock = s->dynamic_lock;
1147 #endif
1148 funcData->pid = s->procs[i].pid;
1150 #ifndef WIN32
1151 if((pid=fork())<0) {
1152 /*@@@ this should be logged, but since all the lock
1153 * file stuff will be tossed, I'll leave it now */
1154 ap_pclosef(tp, lockFd);
1155 continue;
1156 } else if(pid==0) {
1157 /* child */
1159 /* rename the process for ps - best we can easily */
1160 change_process_name("fcgiBlkKill");
1162 dynamic_blocking_kill(funcData);
1163 } else {
1164 /* parent */
1165 s->procs[i].state = STATE_VICTIM;
1166 ap_pclosef(tp, lockFd);
1168 #else
1169 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)dynamic_blocking_kill, (void *)funcData, 0, &tid);
1170 s->procs[i].state = STATE_VICTIM;
1171 #endif
1173 else {
1174 s->procs[i].state = STATE_VICTIM;
1175 #ifndef WIN32
1176 fcgi_kill(s->procs[i].pid, SIGTERM);
1177 ap_pclosef(tp, lockFd);
1178 #else
1179 fcgi_kill((int)s->procs[i].pid, 1);
1180 #endif
1185 ap_destroy_pool(tp);
1188 #ifdef WIN32
1190 // This is a little bogus, there's gotta be a better way to do this
1191 // Can we use WaitForMultipleObjects()
1192 #define FCGI_PROC_WAIT_TIME 100
1194 void child_wait_thread(void *dummy) {
1195 fcgi_server *s;
1196 DWORD dwRet = WAIT_TIMEOUT;
1197 int numChildren;
1198 int i;
1199 int waited;
1201 while (!bTimeToDie) {
1202 waited = 0;
1204 for (s = fcgi_servers; s != NULL; s = s->next) {
1205 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1206 continue;
1208 if (s->directive == APP_CLASS_DYNAMIC) {
1209 numChildren = dynamicMaxClassProcs;
1211 else {
1212 numChildren = s->numProcesses;
1215 for (i=0; i < numChildren; i++) {
1216 if (s->procs[i].pid != INVALID_HANDLE_VALUE && s->procs[i].pid != (HANDLE) 0) {
1217 /* timeout is currently set for 100 miliecond */
1218 /* it may need t longer or user customizable */
1219 dwRet = WaitForSingleObject(s->procs[i].pid, FCGI_PROC_WAIT_TIME);
1221 waited = 1;
1223 if (dwRet != WAIT_TIMEOUT) {
1224 /* a child fs has died */
1225 /* mark the child as dead */
1227 if (s->directive == APP_CLASS_STANDARD) {
1228 /* restart static app */
1229 s->procs[i].state = STATE_NEEDS_STARTING;
1230 s->numFailures++;
1232 else {
1233 s->numProcesses--;
1234 fcgi_dynamic_total_proc_count--;
1236 if (s->procs[i].state == STATE_VICTIM) {
1237 s->procs[i].state = STATE_KILLED;
1238 continue;
1240 else {
1241 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1242 s->numFailures++;
1244 if (dynamicAutoRestart) {
1245 s->procs[i].state = STATE_NEEDS_STARTING;
1247 else {
1248 s->procs[i].state = STATE_READY;
1253 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1254 "FastCGI:%s server \"%s\" (pid %d) terminated",
1255 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1256 s->fs_path, s->procs[i].pid);
1258 CloseHandle(s->procs[i].pid);
1259 s->procs[i].pid = INVALID_HANDLE_VALUE;
1261 /* wake up the main thread */
1262 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1267 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1270 #endif
1272 #ifndef WIN32
1273 static void setup_signals(void)
1275 sigset_t mask;
1276 struct sigaction sa;
1278 /* Ignore USR2 */
1279 sigemptyset(&mask);
1280 sigaddset(&mask, SIGUSR2);
1281 sigprocmask(SIG_BLOCK, &mask, NULL);
1283 /* Setup handlers */
1285 sa.sa_handler = signal_handler;
1286 sigemptyset(&sa.sa_mask);
1287 sa.sa_flags = 0;
1289 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1290 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1291 "sigaction(SIGTERM) failed");
1293 /* httpd restart */
1294 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1295 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1296 "sigaction(SIGHUP) failed");
1298 /* httpd graceful restart */
1299 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1300 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1301 "sigaction(SIGUSR1) failed");
1303 /* read messages from request handlers - kill interval expired */
1304 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1305 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1306 "sigaction(SIGALRM) failed");
1308 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1309 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1310 "sigaction(SIGCHLD) failed");
1313 #endif
1315 #ifndef WIN32
1316 int fcgi_pm_main(void *dummy, child_info *info)
1317 #else
1318 void fcgi_pm_main(void *dummy)
1319 #endif
1321 fcgi_server *s;
1322 unsigned int i;
1323 int read_ready = 0;
1324 int alarmLeft = 0;
1325 pool *tp;
1326 const char *err;
1328 #ifdef WIN32
1329 DWORD dwRet;
1330 int first_time = 1;
1331 HANDLE wait_thread = INVALID_HANDLE_VALUE;
1332 #else
1333 int callWaitPid, callDynamicProcs;
1334 #endif
1336 #ifdef WIN32
1337 // Add SystemRoot to the dyanmic environment
1338 char ** envp = dynamicEnvp;
1339 for (i = 0; *envp; ++i) {
1340 ++envp;
1342 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1344 #else
1345 reduce_priveleges();
1347 close(fcgi_pm_pipe[1]);
1348 change_process_name("fcgi-pm");
1349 setup_signals();
1351 if (fcgi_suexec) {
1352 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1353 "FastCGI: suEXEC mechanism enabled (wrapper: %s)", fcgi_suexec);
1355 #endif
1357 /* Initialize AppClass */
1358 tp = ap_make_sub_pool(fcgi_config_pool);
1359 for(s = fcgi_servers; s != NULL; s = s->next) {
1360 if (s->directive == APP_CLASS_EXTERNAL)
1361 continue;
1363 #ifdef WIN32
1364 if (s->socket_path) {
1365 SECURITY_ATTRIBUTES sa;
1367 sa.nLength = sizeof(sa);
1368 sa.lpSecurityDescriptor = NULL;
1369 sa.bInheritHandle = TRUE;
1371 s->listenFd = (int)CreateNamedPipe(s->socket_path, PIPE_ACCESS_DUPLEX,
1372 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
1373 PIPE_UNLIMITED_INSTANCES, 4096,4096,0, &sa);
1374 if ((HANDLE)s->listenFd == INVALID_HANDLE_VALUE) {
1375 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1376 "FastCGI: server \"%s\" disabled, CreateNamedPipe() failed", s->fs_path);
1377 continue;
1380 s->hPipeMutex = ap_create_mutex(NULL);
1382 if (s->hPipeMutex == NULL) {
1383 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1384 "FastCGI: server \"%s\" disabled, ap_create_mutex() failed", s->fs_path);
1385 CloseHandle((HANDLE)s->listenFd);
1386 s->listenFd = (int) INVALID_HANDLE_VALUE;
1387 continue;
1390 SetHandleInformation(s->hPipeMutex, HANDLE_FLAG_INHERIT, TRUE);
1392 else
1393 #endif
1395 /* Create the socket */
1396 s->listenFd = ap_psocket(fcgi_config_pool, s->socket_addr->sa_family, SOCK_STREAM, 0);
1397 if (s->listenFd < 0) {
1398 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1399 "FastCGI: server \"%s\" disabled, socket() failed", s->fs_path);
1400 continue;
1403 /* bind() and listen() */
1404 err = bind_n_listen(tp, s->socket_addr, s->socket_addr_len,
1405 s->listenQueueDepth, s->listenFd);
1406 if (err) {
1407 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1408 "FastCGI: server \"%s\" disabled: %s",
1409 s->fs_path, err);
1410 ap_pclosesocket(fcgi_config_pool, s->listenFd);
1411 s->listenFd = -1;
1412 continue;
1416 for (i = 0; i < s->numProcesses; i++)
1417 s->procs[i].state = STATE_NEEDS_STARTING;
1420 ap_destroy_pool(tp);
1422 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1423 "FastCGI: process manager initialized (pid %ld)", (long)getpid());
1425 now = time(NULL);
1428 * Loop until SIGTERM
1430 for (;;) {
1431 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1432 #ifdef WIN32
1433 time_t expire;
1434 #else
1435 pid_t childPid;
1436 int waitStatus;
1437 #endif
1438 unsigned int numChildren;
1441 * If we came out of sigsuspend() for any reason other than
1442 * SIGALRM, pick up where we left off.
1444 if (alarmLeft)
1445 sleepSeconds = alarmLeft;
1448 * Examine each configured AppClass for a process that needs
1449 * starting. Compute the earliest time when the start should
1450 * be attempted, starting it now if the time has passed. Also,
1451 * remember that we do NOT need to restart externally managed
1452 * FastCGI applications.
1454 for (s = fcgi_servers; s != NULL; s = s->next) {
1455 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1456 continue;
1458 if (s->directive == APP_CLASS_DYNAMIC) {
1459 numChildren = dynamicMaxClassProcs;
1460 } else {
1461 numChildren = s->numProcesses;
1464 for (i = 0; i < numChildren; i++) {
1465 #ifndef WIN32
1466 if ((s->procs[i].pid <= 0) &&
1467 #else
1468 if (((s->procs[i].pid <= (HANDLE) 0) || (s->procs[i].pid == INVALID_HANDLE_VALUE)) &&
1469 #endif
1470 (s->procs[i].state == STATE_NEEDS_STARTING))
1472 time_t restartTime;
1474 if (s->procs[i].pid == 0) {
1475 restartTime = s->restartTime + s->initStartDelay;
1476 } else {
1477 restartTime = s->restartTime + s->restartDelay;
1480 if(restartTime <= now) {
1481 #ifdef WIN32
1482 int restart = (s->procs[i].pid == INVALID_HANDLE_VALUE);
1483 #else
1484 int restart = (s->procs[i].pid < 0);
1485 #endif
1487 s->restartTime = now;
1489 #ifndef WIN32
1490 if (caughtSigTerm) {
1491 goto ProcessSigTerm;
1493 #endif
1494 s->procs[i].pid = spawn_fs_process(s);
1495 if (s->procs[i].pid <= 0) {
1496 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1497 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1498 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1499 s->fs_path);
1501 sleepSeconds = min(sleepSeconds,
1502 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1504 ap_assert(s->procs[i].pid < 0);
1505 break;
1508 if (s->directive == APP_CLASS_DYNAMIC) {
1509 s->numProcesses++;
1510 fcgi_dynamic_total_proc_count++;
1513 s->procs[i].state = STATE_STARTED;
1515 if (restart)
1516 s->numRestarts++;
1518 if (fcgi_suexec != NULL) {
1519 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1520 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1521 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1522 s->fs_path, (long)s->uid, (long)s->gid,
1523 restart ? "re" : "", (long)s->procs[i].pid);
1525 else {
1526 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1527 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1528 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1529 s->fs_path, restart ? "re" : "", (long)s->procs[i].pid);
1531 ap_assert(s->procs[i].pid > 0);
1532 } else {
1533 sleepSeconds = min(sleepSeconds, restartTime - now);
1539 #ifndef WIN32
1540 if(caughtSigTerm) {
1541 goto ProcessSigTerm;
1543 if((!caughtSigChld) && (!caughtSigUsr2)) {
1544 fd_set rfds;
1546 alarm(sleepSeconds);
1548 FD_ZERO(&rfds);
1549 FD_SET(fcgi_pm_pipe[0], &rfds);
1550 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1552 alarmLeft = alarm(0);
1554 callWaitPid = caughtSigChld;
1555 caughtSigChld = FALSE;
1556 callDynamicProcs = caughtSigUsr2;
1557 caughtSigUsr2 = FALSE;
1559 now = time(NULL);
1562 * Dynamic fcgi process management
1564 if((callDynamicProcs) || (!callWaitPid)) {
1565 dynamic_read_msgs(read_ready);
1566 if(fcgi_dynamic_epoch == 0) {
1567 fcgi_dynamic_epoch = now;
1569 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1570 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1571 dynamic_kill_idle_fs_procs();
1572 fcgi_dynamic_epoch = now;
1576 if(!callWaitPid) {
1577 continue;
1580 /* We've caught SIGCHLD, so find out who it was using waitpid,
1581 * write a log message and update its data structure. */
1583 for (;;) {
1584 if (caughtSigTerm)
1585 goto ProcessSigTerm;
1587 childPid = waitpid(-1, &waitStatus, WNOHANG);
1589 if (childPid == -1 || childPid == 0)
1590 break;
1592 for (s = fcgi_servers; s != NULL; s = s->next) {
1593 if (s->directive == APP_CLASS_EXTERNAL)
1594 continue;
1596 if (s->directive == APP_CLASS_DYNAMIC)
1597 numChildren = dynamicMaxClassProcs;
1598 else
1599 numChildren = s->numProcesses;
1601 for (i = 0; i < numChildren; i++) {
1602 if (s->procs[i].pid == childPid)
1603 goto ChildFound;
1607 /* @@@ This (comment) needs to go away when dynamic gets cleaned up.
1608 * If we get to this point, we have detected the
1609 * termination of the process that was spawned off by
1610 * the process manager to do a blocking kill above. */
1611 continue;
1613 ChildFound:
1614 s->procs[i].pid = -1;
1616 if (s->directive == APP_CLASS_STANDARD) {
1617 /* Always restart static apps */
1618 s->procs[i].state = STATE_NEEDS_STARTING;
1619 s->numFailures++;
1621 else {
1622 s->numProcesses--;
1623 fcgi_dynamic_total_proc_count--;
1625 if (s->procs[i].state == STATE_VICTIM) {
1626 s->procs[i].state = STATE_KILLED;
1628 else {
1629 /* A dynamic app died or exited without provacation from the PM */
1630 s->numFailures++;
1632 if (dynamicAutoRestart || s->numProcesses <= 0)
1633 s->procs[i].state = STATE_NEEDS_STARTING;
1634 else
1635 s->procs[i].state = STATE_READY;
1639 if (WIFEXITED(waitStatus)) {
1640 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1641 "FastCGI:%s server \"%s\" (pid %d) terminated by calling exit with status '%d'",
1642 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1643 s->fs_path, (int)childPid, WEXITSTATUS(waitStatus));
1645 else if (WIFSIGNALED(waitStatus)) {
1646 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1647 "FastCGI:%s server \"%s\" (pid %d) terminated due to uncaught signal '%d' (%s)%s",
1648 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1649 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)],
1650 #ifdef WCOREDUMP
1651 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1652 #else
1653 "");
1654 #endif
1656 else if (WIFSTOPPED(waitStatus)) {
1657 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1658 "FastCGI:%s server \"%s\" (pid %d) stopped due to uncaught signal '%d' (%s)",
1659 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1660 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)]);
1662 } /* for (;;), waitpid() */
1663 #else
1664 if (first_time) {
1665 DWORD tid; /* Thread id */
1667 /* Start the child wait thread */
1668 wait_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)child_wait_thread, NULL, 0, &tid);
1669 first_time = 0;
1672 /* wait for an event to occur or timer expires */
1673 expire = time(NULL) + sleepSeconds;
1674 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1676 if (dwRet == WAIT_FAILED) {
1677 /* There is something seriously wrong here */
1678 ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, fcgi_apache_main_server,
1679 "FastDGI: WaitForMultipleObjects on event handles -- pm is shuting down");
1680 bTimeToDie = TRUE;
1683 if (dwRet != WAIT_TIMEOUT) {
1684 now = time(NULL);
1686 if (now < expire)
1687 alarmLeft = expire - now;
1691 * Dynamic fcgi process management
1693 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1694 if (dwRet == MBOX_EVENT) {
1695 read_ready = 1;
1698 now = time(NULL);
1700 dynamic_read_msgs(read_ready);
1702 if(fcgi_dynamic_epoch == 0) {
1703 fcgi_dynamic_epoch = now;
1706 if (((long)(now-fcgi_dynamic_epoch) >= (int)dynamicKillInterval) ||
1707 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1708 dynamic_kill_idle_fs_procs();
1709 fcgi_dynamic_epoch = now;
1711 read_ready = 0;
1713 else if (dwRet == WAKE_EVENT) {
1714 continue;
1716 else if (dwRet == TERM_EVENT) {
1717 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1718 "FastCGI: Termination event received process manager shutting down");
1719 bTimeToDie = TRUE;
1721 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1722 goto ProcessSigTerm;
1724 else {
1725 // Have an received an unknown event - should not happen
1726 ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, fcgi_apache_main_server,
1727 "FastCGI: WaitForMultipleobjects return an unrecognized event");
1728 bTimeToDie = TRUE;
1729 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1730 goto ProcessSigTerm;
1732 #endif
1733 } /* for (;;), the whole shoot'n match */
1735 ProcessSigTerm:
1737 * Kill off the children, then exit.
1739 while (fcgi_servers != NULL) {
1740 kill_fs_procs(fcgi_config_pool, fcgi_servers);
1743 #ifdef WIN32
1744 return;
1745 #else
1746 exit(0);
1747 #endif
1750 #ifdef WIN32
1751 int fcgi_pm_add_job(fcgi_pm_job *new_job) {
1753 if (new_job == NULL)
1754 return 0;
1756 ap_acquire_mutex(fcgi_dynamic_mbox_mutex);
1757 new_job->next = fcgi_dynamic_mbox;
1758 fcgi_dynamic_mbox = new_job;
1759 ap_release_mutex(fcgi_dynamic_mbox_mutex);
1761 return 1;
1763 #endif