Increase the number of open FDs we look to close when spawning apps.
[mod_fastcgi.git] / fcgi_pm.c
blobaec0a78a87b9bf9aa74c0d470a732e6ee5fea9a5
1 /*
2 * $Id: fcgi_pm.c,v 1.31 2000/06/19 19:24:28 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 #else
26 FcgiRWLock *lock; /* reader/writer lock for dynamic app */
27 #endif
28 ServerProcess *process; /* process to issue SIGTERM to */
31 #ifdef WIN32
32 static BOOL bTimeToDie = FALSE; /* process termination flag */
33 HANDLE fcgi_event_handles[3];
35 static void append_mutex_to_env(char ** envp, HANDLE pipe_mutex) {
36 int i;
37 char * mutex;
38 char ** env = envp;
40 for (i = 0; *envp; ++i) {
41 ++envp;
44 mutex = ap_psprintf(fcgi_config_pool, "_FCGI_MUTEX_=%d", (int) pipe_mutex);
45 fcgi_config_set_env_var(fcgi_config_pool, env, &i, mutex);
48 #endif
51 #ifndef WIN32
52 static int seteuid_root(void)
54 int rc = seteuid((uid_t)0);
55 if (rc == -1) {
56 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
57 "FastCGI: seteuid(0) failed");
59 return rc;
62 static int seteuid_user(void)
64 int rc = seteuid(ap_user_id);
65 if (rc == -1) {
66 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
67 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id);
69 return rc;
71 #endif
73 static int fcgi_kill(ServerProcess *process, int sig)
75 int rc;
77 #ifndef WIN32
78 if (fcgi_suexec) {
79 seteuid_root();
82 rc = kill(process->pid, sig);
84 if (fcgi_suexec) {
85 seteuid_user();
88 #else
89 rc = TerminateProcess(process->handle, sig);
90 #endif
92 return rc;
95 /*******************************************************************************
96 * Send SIGTERM to each process in the server class, remove socket and lock
97 * file if appropriate. Currently this is only called when the PM is shutting
98 * down and thus memory isn't freed and sockets and files aren't closed.
100 static void kill_fs_procs(pool *p, fcgi_server *s)
102 ServerProcess *proc = s->procs;
103 int i, numChildren;
105 if (s->directive == APP_CLASS_DYNAMIC)
106 numChildren = dynamicMaxClassProcs;
107 else
108 numChildren = s->numProcesses;
110 for (i = 0; i < numChildren; i++, proc++) {
111 #ifndef WIN32
112 if (proc->pid > 0) {
113 fcgi_kill(proc, SIGTERM);
114 proc->pid = -1;
116 #else
117 if (proc->handle != INVALID_HANDLE_VALUE) {
118 fcgi_kill(proc, 1);
119 CloseHandle(proc->handle);
120 proc->handle = INVALID_HANDLE_VALUE;
121 proc->pid = -1;
123 #endif
126 /* Remove the dead lock file */
127 if (s->directive == APP_CLASS_DYNAMIC) {
128 #ifndef WIN32
129 const char *lockFileName = fcgi_util_socket_get_lock_filename(p, s->socket_path);
131 if (unlink(lockFileName) != 0) {
132 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
133 "FastCGI: unlink() failed to remove lock file \"%s\" for (dynamic) server \"%s\"",
134 lockFileName, s->fs_path);
136 #else
137 fcgi_rdwr_destroy(s->dynamic_lock);
138 #endif
141 /* Remove the socket file */
142 if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL) {
143 #ifndef WIN32
144 if (unlink(s->socket_path) != 0) {
145 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
146 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
147 s->socket_path,
148 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path);
150 #else
151 CloseHandle((HANDLE)s->listenFd);
152 #endif
154 fcgi_servers = s->next;
157 /*******************************************************************************
158 * Bind an address to a socket and set it to listen for incoming connects.
159 * The error messages are allocated from the pool p, use temp storage.
160 * Don't forget to close the socket, if an error occurs.
162 static const char *bind_n_listen(pool *p, struct sockaddr *socket_addr,
163 int socket_addr_len, int backlog, int sock)
165 #ifndef WIN32
166 if (socket_addr->sa_family == AF_UNIX) {
167 /* Remove any existing socket file.. just in case */
168 unlink(((struct sockaddr_un *)socket_addr)->sun_path);
170 else
171 #endif
173 int flag = 1;
174 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
177 /* Bind it to the socket_addr */
178 if (bind(sock, socket_addr, socket_addr_len) != 0)
179 return "bind() failed";
181 #ifndef WIN32
182 /* Twiddle permissions */
183 if (socket_addr->sa_family == AF_UNIX) {
184 if (chmod(((struct sockaddr_un *)socket_addr)->sun_path, S_IRUSR | S_IWUSR))
185 return "chmod() of socket failed";
187 #endif
189 /* Set to listen */
190 if (listen(sock, backlog) != 0)
191 return "listen() failed";
193 return NULL;
197 *----------------------------------------------------------------------
199 * dynamic_blocking_kill
201 * Block on the lock file until it is available, and then
202 * issue a kill signal to the corresponding application.
203 * Since this function is executed in the child process,
204 * _exit() is called upon completion.
206 * Inputs
207 * Pointer to the data structure containing a process id to
208 * issue a signal to and the full pathname to the lockfile
209 * that needs to be locked before the issue of the signal.
211 * Notes
212 * Memory is allocated by the caller, but is freed by this
213 * function.
215 *----------------------------------------------------------------------
217 static void dynamic_blocking_kill(void *data)
219 struct FuncData *funcData = (struct FuncData *)data;
220 #ifndef WIN32
221 int lockFd;
223 ap_assert(funcData->lockFileName);
224 if ((lockFd = open(funcData->lockFileName, O_RDWR)) < 0) {
225 /* There is something terribly wrong here */
226 } else {
227 if (fcgi_wait_for_shared_write_lock(lockFd) < 0) {
228 /* This is a major problem */
229 } else {
230 fcgi_kill(funcData->process, SIGTERM);
233 /* exit() may flush stdio buffers inherited from the parent. */
234 _exit(0);
235 #else
236 ap_assert(funcData->lock);
237 if (fcgi_wait_for_shared_write_lock(funcData->lock) < 0) {
238 // This is a major problem
240 else {
241 fcgi_kill(funcData->process, 1);
242 fcgi_rdwr_unlock(funcData->lock, WRITER);
244 return;
245 #endif
249 *----------------------------------------------------------------------
251 * pm_main
253 * The FastCGI process manager, which runs as a separate
254 * process responsible for:
255 * - Starting all the FastCGI proceses.
256 * - Restarting any of these processes that die (indicated
257 * by SIGCHLD).
258 * - Catching SIGTERM and relaying it to all the FastCGI
259 * processes before exiting.
261 * Inputs:
262 * Uses global variable fcgi_servers.
264 * Results:
265 * Does not return.
267 * Side effects:
268 * Described above.
270 *----------------------------------------------------------------------
272 #ifndef WIN32
273 static int caughtSigTerm = FALSE;
274 static int caughtSigChld = FALSE;
275 static int caughtSigUsr2 = FALSE;
277 static void signal_handler(int signo)
279 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
280 /* SIGUSR1 & SIGHUP are sent by apache to its process group
281 * when apache get 'em. Apache follows up (1.2.x) with attacks
282 * on each of its child processes, but we've got the KillMgr
283 * sitting between us so we never see the KILL. The main loop
284 * in ProcMgr also checks to see if the KillMgr has terminated,
285 * and if it has, we handl it as if we should shutdown too. */
286 caughtSigTerm = TRUE;
287 } else if(signo == SIGCHLD) {
288 caughtSigChld = TRUE;
289 } else if(signo == SIGALRM) {
290 caughtSigUsr2 = TRUE;
293 #endif
296 *----------------------------------------------------------------------
298 * spawn_fs_process --
300 * Fork and exec the specified fcgi process.
302 * Results:
303 * 0 for successful fork, -1 for failed fork.
305 * In case the child fails before or in the exec, the child
306 * obtains the error log by calling getErrLog, logs
307 * the error, and exits with exit status = errno of
308 * the failed system call.
310 * Side effects:
311 * Child process created.
313 *----------------------------------------------------------------------
316 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
318 #ifndef WIN32
319 pid_t child_pid;
320 int i;
321 char *dirName;
322 char *dnEnd, *failedSysCall;
324 child_pid = fork();
325 if (child_pid) {
326 return child_pid;
329 /* We're the child. We're gonna exec() so pools don't matter. */
331 dnEnd = strrchr(fs->fs_path, '/');
332 if (dnEnd == NULL) {
333 dirName = "./";
334 } else {
335 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
336 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
338 if (chdir(dirName) < 0) {
339 failedSysCall = "chdir()";
340 goto FailedSystemCallExit;
343 #ifndef __EMX__
344 /* OS/2 dosen't support nice() */
345 if (fs->processPriority != 0) {
346 if (nice(fs->processPriority) == -1) {
347 failedSysCall = "nice()";
348 goto FailedSystemCallExit;
351 #endif
353 /* Open the listenFd on spec'd fd */
354 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
355 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
357 /* Close all other open fds, except stdout/stderr. Leave these two open so
358 * FastCGI applications don't have to find and fix ALL 3rd party libs that
359 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
360 * main server error_log - @@@ provide a directive control where this goes.
362 ap_error_log2stderr(fcgi_apache_main_server);
363 dup2(STDERR_FILENO, STDOUT_FILENO);
364 for (i = 0; i < FCGI_MAX_FD; i++) {
365 if (i != FCGI_LISTENSOCK_FILENO && i != STDERR_FILENO && i != STDOUT_FILENO) {
366 close(i);
370 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
371 * install its own handler. */
372 signal(SIGPIPE, SIG_IGN);
374 if (fcgi_suexec != NULL) {
375 char *shortName = strrchr(fs->fs_path, '/') + 1;
377 /* Relinquish our root real uid powers */
378 seteuid_root();
379 setuid(ap_user_id);
381 do {
382 execle(fcgi_suexec, fcgi_suexec, fs->username, fs->group, shortName, NULL, fs->envp);
383 } while (errno == EINTR);
385 else {
386 do {
387 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
388 } while (errno == EINTR);
391 failedSysCall = "execle()";
393 FailedSystemCallExit:
394 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
395 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
396 exit(-1);
398 /* avoid an irrelevant compiler warning */
399 return(0);
401 #else
403 /* Adapted from Apache's util_script.c ap_call_exec() */
404 char *interpreter = NULL;
405 char *ext = NULL;
406 char *exename = NULL;
407 char *s = NULL;
408 char *quoted_filename;
409 char *pCommand;
410 char *pEnvBlock, *pNext;
412 int i;
413 int iEnvBlockLen;
415 file_type_e fileType;
417 STARTUPINFO si;
418 PROCESS_INFORMATION pi;
420 request_rec r;
421 pid_t pid = -1;
423 pool * tp = ap_make_sub_pool(fcgi_config_pool);
425 memset(&si, 0, sizeof(si));
426 memset(&pi, 0, sizeof(pi));
427 memset(&r, 0, sizeof(r));
429 // Can up a fake request to pass to ap_get_win32_interpreter()
430 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
431 r.server = fcgi_apache_main_server;
432 r.filename = (char *) fs->fs_path;
433 r.pool = tp;
435 fileType = ap_get_win32_interpreter(&r, &interpreter);
437 if (fileType == eFileTypeUNKNOWN) {
438 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, fcgi_apache_main_server,
439 "FastCGI: %s is not executable; ensure interpreted scripts have "
440 "\"#!\" as their first line",
441 fs->fs_path);
442 ap_destroy_pool(tp);
443 return (pid);
447 * We have the interpreter (if there is one) and we have
448 * the arguments (if there are any).
449 * Build the command string to pass to CreateProcess.
451 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
452 if (interpreter && *interpreter) {
453 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
455 else {
456 pCommand = quoted_filename;
460 * Make child process use hPipeOutputWrite as standard out,
461 * and make sure it does not show on screen.
463 si.cb = sizeof(si);
464 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
465 si.wShowWindow = SW_HIDE;
466 si.hStdInput = (HANDLE) fs->listenFd;
468 // XXX These should be open to the error_log
469 si.hStdOutput = INVALID_HANDLE_VALUE;
470 si.hStdError = INVALID_HANDLE_VALUE;
473 * Win32's CreateProcess call requires that the environment
474 * be passed in an environment block, a null terminated block of
475 * null terminated strings.
477 i = 0;
478 iEnvBlockLen = 1;
479 while (fs->envp[i]) {
480 iEnvBlockLen += strlen(fs->envp[i]) + 1;
481 i++;
484 pEnvBlock = (char *)ap_pcalloc(tp,iEnvBlockLen);
486 i = 0;
487 pNext = pEnvBlock;
488 while (fs->envp[i]) {
489 strcpy(pNext, fs->envp[i]);
490 pNext = pNext + strlen(pNext) + 1;
491 i++;
494 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
496 pEnvBlock,
497 ap_make_dirstr_parent(tp, fs->fs_path),
498 &si, &pi)) {
499 if (fileType == eFileTypeEXE16) {
500 /* Hack to get 16-bit CGI's working. It works for all the
501 * standard modules shipped with Apache. pi.dwProcessId is 0
502 * for 16-bit CGIs and all the Unix specific code that calls
503 * ap_call_exec interprets this as a failure case. And we can't
504 * use -1 either because it is mapped to 0 by the caller.
506 pid = -2;
508 else {
509 pid = pi.dwProcessId;
510 process->handle = pi.hProcess;
511 CloseHandle(pi.hThread);
515 ap_destroy_pool(tp);
517 return pid;
519 #endif
522 #ifndef WIN32
523 static void reduce_priveleges(void)
525 char *name;
527 if (geteuid() != 0)
528 return;
530 #ifndef __EMX__
531 /* Get username if passed as a uid */
532 if (ap_user_name[0] == '#') {
533 uid_t uid = atoi(&ap_user_name[1]);
534 struct passwd *ent = getpwuid(uid);
536 if (ent == NULL) {
537 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
538 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
539 "you probably need to modify the User directive", (unsigned)uid);
540 exit(1);
542 name = ent->pw_name;
544 else
545 name = ap_user_name;
547 /* Change Group */
548 if (setgid(ap_group_id) == -1) {
549 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
550 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
551 exit(1);
554 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
556 /* Initialize supplementary groups */
557 if (initgroups(name, ap_group_id) == -1) {
558 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
559 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
560 name, (unsigned)ap_group_id);
561 exit(1);
563 #endif /* __EMX__ */
565 /* Change User */
566 if (fcgi_suexec) {
567 if (seteuid_user() == -1) {
568 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
569 "FastCGI: process manager exiting, failed to reduce priveleges");
570 exit(1);
573 else {
574 if (setuid(ap_user_id) == -1) {
575 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
576 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
577 exit(1);
582 /*************
583 * Change the name of this process - best we can easily.
585 static void change_process_name(const char * const name)
587 strncpy(ap_server_argv0, name, strlen(ap_server_argv0));
589 #endif
591 static void schedule_start(fcgi_server *s, int proc)
593 s->procs[proc].state = STATE_NEEDS_STARTING;
594 if (proc == (int)dynamicMaxClassProcs - 1) {
595 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
596 "FastCGI: scheduled the %sstart of the last (dynamic) server "
597 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
598 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
603 *----------------------------------------------------------------------
605 * dynamic_read_msgs
607 * Removes the records written by request handlers and decodes them.
608 * We also update the data structures to reflect the changes.
610 *----------------------------------------------------------------------
613 static void dynamic_read_msgs(int read_ready)
615 fcgi_server *s;
617 #ifndef WIN32
618 int rc;
619 static int buflen = 0;
620 static char buf[FCGI_MSGS_BUFSIZE + 1];
621 char *ptr1, *ptr2, opcode;
622 char execName[FCGI_MAXPATH + 1];
623 char user[MAX_USER_NAME_LEN + 2];
624 char group[MAX_GID_CHAR_LEN + 1];
625 unsigned long q_usec = 0UL, req_usec = 0UL;
626 #else
627 fcgi_pm_job *joblist = NULL;
628 fcgi_pm_job *cjob = NULL;
629 SECURITY_ATTRIBUTES sa;
630 #endif
632 pool *sp, *tp;
634 #ifndef WIN32
635 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
636 #endif
639 * To prevent the idle application from running indefinitely, we
640 * check the timer and if it is expired, we recompute the values
641 * for each running application class. Then, when REQ_COMPLETE
642 * message is recieved, only updates are made to the data structures.
644 if (fcgi_dynamic_last_analyzed == 0) {
645 fcgi_dynamic_last_analyzed = now;
647 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
648 for (s = fcgi_servers; s != NULL; s = s->next) {
649 if (s->directive != APP_CLASS_DYNAMIC)
650 break;
651 /* XXX what does this adjustment do? */
652 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
653 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
654 s->totalConnTime = 0UL;
655 s->totalQueueTime = 0UL;
659 if (read_ready <= 0) {
660 return;
663 #ifndef WIN32
664 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
665 if (rc <= 0) {
666 if (!caughtSigTerm) {
667 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
668 "FastCGI: read() from pipe failed (%d)", rc);
670 return;
672 buflen += rc;
673 buf[buflen] = '\0';
674 #else
675 /* Obtain the data from the fcgi_dynamic_mbox file */
676 ap_acquire_mutex(fcgi_dynamic_mbox_mutex);
678 if (fcgi_dynamic_mbox == NULL) {
679 return;
681 else {
682 joblist = fcgi_dynamic_mbox;
683 fcgi_dynamic_mbox = NULL;
686 ap_release_mutex(fcgi_dynamic_mbox_mutex);
688 cjob = joblist;
689 #endif
691 tp = ap_make_sub_pool(fcgi_config_pool);
693 #ifndef WIN32
694 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
695 int scan_failed = 0;
697 ptr2 = strchr(ptr1, '*');
698 if (ptr2) {
699 *ptr2++ = '\0';
701 else {
702 break;
705 opcode = *ptr1;
707 switch (opcode) {
708 case PLEASE_START:
709 if (sscanf(ptr1, "%c %s %16s %15s",
710 &opcode, execName, user, group) != 4)
712 scan_failed = 1;
714 break;
715 case CONN_TIMEOUT:
716 if (sscanf(ptr1, "%c %s %16s %15s",
717 &opcode, execName, user, group) != 4)
719 scan_failed = 1;
721 break;
722 case REQ_COMPLETE:
723 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
724 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
726 scan_failed = 1;
728 break;
729 default:
730 scan_failed = 1;
731 break;
734 if (scan_failed) {
735 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
736 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
737 goto NextJob;
739 #else
740 /* Update data structures for processing */
741 while (cjob != NULL) {
742 joblist = cjob->next;
743 #endif
745 #ifndef WIN32
746 s = fcgi_util_fs_get(execName, user, group);
747 #else
748 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
749 #endif
751 #ifndef WIN32
752 if (s==NULL && opcode != REQ_COMPLETE)
753 #else
754 if (s==NULL && cjob->id != REQ_COMPLETE)
755 #endif
757 #ifdef WIN32
758 char ** envp;
759 int i;
760 #else
761 int fd;
762 const char *err, *lockPath;
763 #endif
765 /* Create a perm subpool to hold the new server data,
766 * we can destroy it if something doesn't pan out */
767 sp = ap_make_sub_pool(fcgi_config_pool);
769 /* Create a new "dynamic" server */
770 s = fcgi_util_fs_new(sp);
771 s->directive = APP_CLASS_DYNAMIC;
772 s->restartDelay = dynamicRestartDelay;
773 s->listenQueueDepth = dynamicListenQueueDepth;
774 s->initStartDelay = dynamicInitStartDelay;
775 #ifndef WIN32
776 s->envp = dynamicEnvp;
777 ap_getparents(execName);
778 ap_no2slash(execName);
779 s->fs_path = ap_pstrdup(sp, execName);
780 #else
781 // Create our own copy of the env so we can _FCGI_MUTEX_
782 envp = dynamicEnvp;
783 for (i = 0; *envp; ++i) {
784 ++envp;
786 s->envp = ap_pcalloc(sp, sizeof(char *) * (i + 2));
787 memcpy(s->envp, dynamicEnvp, sizeof(char *) * i);
789 ap_getparents(cjob->fs_path);
790 ap_no2slash(cjob->fs_path);
791 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
792 #endif
793 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
795 #ifndef WIN32
796 /* Create socket file's path */
797 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
798 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
800 /* Create sockaddr, prealloc it so it won't get created in tp */
801 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
802 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
803 &s->socket_addr_len, s->socket_path);
804 if (err) {
805 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
806 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
807 goto BagNewServer;
810 /* Create the socket */
811 if ((s->listenFd = ap_psocket(sp, s->socket_addr->sa_family, SOCK_STREAM, 0)) < 0) {
812 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
813 "FastCGI: can't create (dynamic) server \"%s\": socket() failed", execName);
814 goto BagNewServer;
817 /* bind() and listen() */
818 err = bind_n_listen(tp, s->socket_addr, s->socket_addr_len,
819 s->listenQueueDepth, s->listenFd);
820 if (err) {
821 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
822 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
823 goto BagNewServer;
826 /* Create the lock file */
827 lockPath = fcgi_util_socket_get_lock_filename(tp, s->socket_path);
828 fd = ap_popenf(tp, lockPath,
829 O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
830 if (fd < 0) {
831 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
832 "FastCGI: can't create (dynamic) server \"%s\": can't open lock file \"%s\": popenf() failed",
833 execName, lockPath);
834 goto BagNewServer;
836 ap_pclosef(tp, fd);
838 /* If suexec is being used, config user/group info */
839 if (fcgi_suexec) {
840 if (user[0] == '~') {
841 /* its a user dir uri, the rest is a username, not a uid */
842 struct passwd *pw = getpwnam(&user[1]);
844 if (!pw) {
845 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
846 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for suexec: getpwnam(%s) failed",
847 execName, &user[1]);
848 goto BagNewServer;
850 s->uid = pw->pw_uid;
851 s->user = ap_pstrdup(sp, user);
852 s->username = s->user;
854 s->gid = pw->pw_gid;
855 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
857 else {
858 struct passwd *pw;
860 s->uid = (uid_t)atol(user);
861 pw = getpwuid(s->uid);
862 if (!pw) {
863 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
864 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for suexec: getwpuid(%ld) failed",
865 execName, (long)s->uid);
866 goto BagNewServer;
868 s->user = ap_pstrdup(sp, user);
869 s->username = ap_pstrdup(sp, pw->pw_name);
871 s->gid = (gid_t)atol(group);
872 s->group = ap_pstrdup(sp, group);
875 #else
876 /* Create socket file's path */
877 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
878 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
880 /* Create Named Pipe and I/O mutex*/
881 sa.nLength = sizeof(sa);
882 sa.lpSecurityDescriptor = NULL;
883 sa.bInheritHandle = TRUE;
885 s->listenFd = (int)CreateNamedPipe(s->socket_path,
886 PIPE_ACCESS_DUPLEX,
887 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
888 PIPE_UNLIMITED_INSTANCES, 4096,4096,0, &sa);
890 if ((HANDLE)s->listenFd == INVALID_HANDLE_VALUE) {
891 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
892 "FASTCGI: can't create (dynamic) server \"%s\": CreateNamePipe() failed",
893 cjob->fs_path);
894 goto BagNewServer;
897 s->hPipeMutex = ap_create_mutex(NULL);
899 if (s->hPipeMutex == NULL) {
900 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
901 "FastCGI: can't create (dynamic) server \"%s\": ap_create_mutex() failed", cjob->fs_path);
902 CloseHandle((HANDLE)s->listenFd);
903 goto BagNewServer;
906 SetHandleInformation(s->hPipeMutex, HANDLE_FLAG_INHERIT, TRUE);
908 append_mutex_to_env(s->envp, s->hPipeMutex);
910 /* Create the application lock */
911 if ((s->dynamic_lock = fcgi_rdwr_create()) == NULL) {
912 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
913 "FastCGI: can;t create (dynamic) server \"%s\": fcgi_rdwr_create() failed",
914 cjob->fs_path);
915 CloseHandle((HANDLE)s->listenFd);
916 CloseHandle(s->hPipeMutex);
917 goto BagNewServer;
919 #endif
921 fcgi_util_fs_add(s);
923 else {
924 #ifndef WIN32
925 if (opcode == PLEASE_START) {
926 #else
927 if(cjob->id==PLEASE_START) {
928 #endif
929 if (dynamicAutoUpdate) {
930 /* Check to see if the binary has changed. If so,
931 * kill the FCGI application processes, and
932 * restart them.
934 struct stat stbuf;
935 unsigned int i;
937 #ifndef WIN32
938 if ((stat(execName, &stbuf)==0) &&
939 #else
940 if ((stat(cjob->fs_path, &stbuf)==0) &&
941 #endif
942 (stbuf.st_mtime > s->restartTime)) {
943 /* kill old server(s) */
944 for (i = 0; i < dynamicMaxClassProcs; i++) {
945 if (s->procs[i].pid > 0) {
946 fcgi_kill(&s->procs[i], SIGTERM);
950 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
951 "FastCGI: restarting server \"%s\" processes, newer version found",
952 #ifndef WIN32
953 execName);
954 #else
955 cjob->fs_path);
956 #endif
959 /* If dynamicAutoRestart, don't mark any new processes
960 * for starting because we probably got the
961 * PLEASE_START due to dynamicAutoUpdate and the ProcMgr
962 * will be restarting all of those we just killed.
964 if (dynamicAutoRestart)
965 goto NextJob;
966 } else {
967 /* we've been asked to start a process--only start
968 * it if we're not already running at least one
969 * instance.
971 unsigned int i;
973 for (i = 0; i < dynamicMaxClassProcs; i++) {
974 if (s->procs[i].state == STATE_STARTED)
975 break;
977 /* if already running, don't start another one */
978 if (i < dynamicMaxClassProcs) {
979 goto NextJob;
984 #ifndef WIN32
985 switch (opcode)
986 #else
987 switch (cjob->id)
988 #endif
990 unsigned int i;
991 time_t time_passed;
993 case PLEASE_START:
994 case CONN_TIMEOUT:
995 /* If we've started one recently, don't register another */
996 time_passed = now - s->restartTime;
998 if (time_passed < (int) s->initStartDelay
999 && time_passed < (int) s->restartDelay)
1001 goto NextJob;
1004 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1006 * Extra instances should have been
1007 * terminated beforehand, probably need
1008 * to increase ProcessSlack parameter
1010 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1011 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1012 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1013 goto NextJob;
1015 /* find next free slot */
1016 for (i = 0; i < dynamicMaxClassProcs; i++) {
1017 if (s->procs[i].state != STATE_READY
1018 && s->procs[i].state != STATE_NEEDS_STARTING
1019 && s->procs[i].state != STATE_KILLED)
1021 goto NextJob;
1024 if (s->procs[i].pid < 0)
1026 if (time_passed > (int)s->restartDelay) {
1027 schedule_start(s, i);
1029 break;
1031 else if (s->procs[i].pid == 0)
1033 if (time_passed > (int)s->initStartDelay) {
1034 schedule_start(s, i);
1036 break;
1040 break;
1041 case REQ_COMPLETE:
1042 /* only record stats if we have a structure */
1043 if (s) {
1044 #ifndef WIN32
1045 s->totalConnTime += req_usec;
1046 s->totalQueueTime += q_usec;
1047 #else
1048 s->totalConnTime += cjob->start_time;
1049 s->totalQueueTime += cjob->qsec;
1050 #endif
1052 break;
1055 NextJob:
1057 #ifdef WIN32
1058 /* Cleanup job data */
1059 free(cjob->fs_path);
1060 free(cjob->user);
1061 free(cjob->group);
1062 free(cjob);
1063 cjob = joblist;
1064 #endif
1066 continue;
1068 BagNewServer:
1069 ap_destroy_pool(sp);
1071 #ifdef WIN32
1072 free(cjob->fs_path);
1073 free(cjob);
1074 cjob = joblist;
1075 #endif
1078 #ifndef WIN32
1079 if (ptr1 == buf) {
1080 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1081 "FastCGI: really bogus message: \"%s\"", ptr1);
1082 ptr1 += strlen(buf);
1085 buflen -= ptr1 - buf;
1086 if (buflen) {
1087 memmove(buf, ptr1, buflen);
1089 #endif
1091 ap_destroy_pool(tp);
1095 *----------------------------------------------------------------------
1097 * dynamic_kill_idle_fs_procs
1099 * Implement a kill policy for the dynamic FastCGI applications.
1100 * We also update the data structures to reflect the changes.
1102 * Side effects:
1103 * Processes are marked for deletion possibly killed.
1105 *----------------------------------------------------------------------
1107 static void dynamic_kill_idle_fs_procs(void)
1109 fcgi_server *s;
1110 struct FuncData *funcData = NULL;
1111 unsigned long connTime; /* server's smoothed running time, or
1112 * if that's 0, the current total */
1113 unsigned long totalTime; /* maximum number of microseconds that all
1114 * of a server's running processes together
1115 * could have spent running since the
1116 * last check */
1117 double loadFactor; /* percentage, 0-100, of totalTime that
1118 * the processes actually used */
1119 unsigned int i, victims = 0;
1120 #ifndef WIN32
1121 const char *lockFileName;
1122 int lockFd;
1123 pid_t pid;
1124 #endif
1125 pool *tp = ap_make_sub_pool(fcgi_config_pool);
1127 /* pass 1 - locate and mark all victims */
1128 for(s=fcgi_servers; s!=NULL; s=s->next) {
1129 /* Only kill dynamic apps */
1130 if (s->directive != APP_CLASS_DYNAMIC)
1131 continue;
1133 /* If the number of non-victims is less than or equal to
1134 the minimum that may be running without being killed off,
1135 don't select any more victims. */
1136 if ((fcgi_dynamic_total_proc_count - victims) <= (int) dynamicMinProcs) {
1137 break;
1140 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1141 totalTime = (s->numProcesses)*(now - fcgi_dynamic_epoch)*1000000 + 1;
1143 /* XXX producing a heavy load with one client, I haven't been
1144 able to achieve a loadFactor greater than 0.5. Perhaps this
1145 should be scaled up by another order of magnitude or two. */
1146 loadFactor = connTime/totalTime*100.0;
1148 if ((s->numProcesses > 1
1149 && s->numProcesses/(s->numProcesses - 1)*loadFactor < dynamicThreshholdN)
1150 || (s->numProcesses == 1 && loadFactor < dynamicThreshhold1))
1152 int got_one = 0;
1154 for (i = 0; !got_one && i < dynamicMaxClassProcs; ++i) {
1155 if (s->procs[i].state == STATE_NEEDS_STARTING) {
1156 s->procs[i].state = STATE_READY;
1157 got_one = 1;
1159 else if (s->procs[i].state == STATE_VICTIM || s->procs[i].state == STATE_KILL) {
1160 got_one = 1;
1164 for (i = 0; !got_one && i < dynamicMaxClassProcs; ++i) {
1165 if (s->procs[i].state == STATE_STARTED) {
1166 s->procs[i].state = STATE_KILL;
1167 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1168 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination scheduled",
1169 s->fs_path, s->procs[i].pid);
1170 victims++;
1171 got_one = 1;
1177 /* pass 2 - kill procs off */
1178 for(s=fcgi_servers; s!=NULL; s=s->next) {
1179 /* Only kill dynamic apps */
1180 if (s->directive != APP_CLASS_DYNAMIC)
1181 continue;
1183 for(i = 0; i < dynamicMaxClassProcs; i++) {
1184 if (s->procs[i].state == STATE_KILL) {
1185 #ifndef WIN32
1186 lockFileName = fcgi_util_socket_get_lock_filename(tp, s->socket_path);
1187 if ((lockFd = ap_popenf(tp, lockFileName, O_RDWR, 0))<0) {
1189 * If we need to kill an application and the
1190 * corresponding lock file does not exist, then
1191 * that means we are in big trouble here
1193 /*@@@ this should be logged, but since all the lock
1194 * file stuff will be tossed, I'll leave it now */
1195 ap_pclosef(tp, lockFd);
1196 continue;
1199 if (fcgi_get_exclusive_write_lock_no_wait(lockFd) < 0) {
1200 #else
1201 if (fcgi_get_exclusive_write_lock_no_wait(s->dynamic_lock) < 0) {
1202 DWORD tid;
1203 #endif
1205 * Unable to lock the lockfile, indicative
1206 * of WS performing operation with the given
1207 * application class. The simplest solution
1208 * is to spawn off another process and block
1209 * on lock to kill it. This is under assumptions
1210 * that fork() is not very costly and this
1211 * situation occurs very rarely, which it should
1213 funcData = ap_pcalloc(tp, sizeof(struct FuncData));
1214 #ifndef WIN32
1215 funcData->lockFileName = lockFileName;
1216 #else
1217 funcData->lock = s->dynamic_lock;
1218 #endif
1219 funcData->process = &s->procs[i];
1221 #ifndef WIN32
1222 if((pid=fork())<0) {
1223 /*@@@ this should be logged, but since all the lock
1224 * file stuff will be tossed, I'll leave it now */
1225 ap_pclosef(tp, lockFd);
1226 continue;
1227 } else if(pid==0) {
1228 /* child */
1230 /* rename the process for ps - best we can easily */
1231 change_process_name("fcgiBlkKill");
1233 dynamic_blocking_kill(funcData);
1234 } else {
1235 /* parent */
1236 s->procs[i].state = STATE_VICTIM;
1237 ap_pclosef(tp, lockFd);
1239 #else
1240 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)dynamic_blocking_kill, (void *)funcData, 0, &tid);
1241 s->procs[i].state = STATE_VICTIM;
1242 #endif
1244 else {
1245 s->procs[i].state = STATE_VICTIM;
1246 #ifndef WIN32
1247 fcgi_kill(&s->procs[i], SIGTERM);
1248 ap_pclosef(tp, lockFd);
1249 #else
1250 fcgi_kill(&s->procs[i], 1);
1251 #endif
1256 ap_destroy_pool(tp);
1259 #ifdef WIN32
1261 // This is a little bogus, there's gotta be a better way to do this
1262 // Can we use WaitForMultipleObjects()
1263 #define FCGI_PROC_WAIT_TIME 100
1265 void child_wait_thread(void *dummy) {
1266 fcgi_server *s;
1267 DWORD dwRet = WAIT_TIMEOUT;
1268 int numChildren;
1269 int i;
1270 int waited;
1272 while (!bTimeToDie) {
1273 waited = 0;
1275 for (s = fcgi_servers; s != NULL; s = s->next) {
1276 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1277 continue;
1279 if (s->directive == APP_CLASS_DYNAMIC) {
1280 numChildren = dynamicMaxClassProcs;
1282 else {
1283 numChildren = s->numProcesses;
1286 for (i=0; i < numChildren; i++) {
1287 if (s->procs[i].handle != INVALID_HANDLE_VALUE) {
1288 /* timeout is currently set for 100 miliecond */
1289 /* it may need t longer or user customizable */
1290 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1292 waited = 1;
1294 if (dwRet != WAIT_TIMEOUT) {
1295 /* a child fs has died */
1296 /* mark the child as dead */
1298 if (s->directive == APP_CLASS_STANDARD) {
1299 /* restart static app */
1300 s->procs[i].state = STATE_NEEDS_STARTING;
1301 s->numFailures++;
1303 else {
1304 s->numProcesses--;
1305 fcgi_dynamic_total_proc_count--;
1307 if (s->procs[i].state == STATE_VICTIM) {
1308 s->procs[i].state = STATE_KILLED;
1309 continue;
1311 else {
1312 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1313 s->numFailures++;
1315 if (dynamicAutoRestart) {
1316 s->procs[i].state = STATE_NEEDS_STARTING;
1318 else {
1319 s->procs[i].state = STATE_READY;
1324 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1325 "FastCGI:%s server \"%s\" (pid %d) terminated",
1326 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1327 s->fs_path, s->procs[i].pid);
1329 CloseHandle(s->procs[i].handle);
1330 s->procs[i].handle = INVALID_HANDLE_VALUE;
1331 s->procs[i].pid = -1;
1333 /* wake up the main thread */
1334 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1339 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1342 #endif
1344 #ifndef WIN32
1345 static void setup_signals(void)
1347 sigset_t mask;
1348 struct sigaction sa;
1350 /* Ignore USR2 */
1351 sigemptyset(&mask);
1352 sigaddset(&mask, SIGUSR2);
1353 sigprocmask(SIG_BLOCK, &mask, NULL);
1355 /* Setup handlers */
1357 sa.sa_handler = signal_handler;
1358 sigemptyset(&sa.sa_mask);
1359 sa.sa_flags = 0;
1361 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1362 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1363 "sigaction(SIGTERM) failed");
1365 /* httpd restart */
1366 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1367 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1368 "sigaction(SIGHUP) failed");
1370 /* httpd graceful restart */
1371 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1372 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1373 "sigaction(SIGUSR1) failed");
1375 /* read messages from request handlers - kill interval expired */
1376 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1377 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1378 "sigaction(SIGALRM) failed");
1380 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1381 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1382 "sigaction(SIGCHLD) failed");
1385 #endif
1387 #ifndef WIN32
1388 int fcgi_pm_main(void *dummy, child_info *info)
1389 #else
1390 void fcgi_pm_main(void *dummy)
1391 #endif
1393 fcgi_server *s;
1394 unsigned int i;
1395 int read_ready = 0;
1396 int alarmLeft = 0;
1397 pool *tp;
1398 const char *err;
1400 #ifdef WIN32
1401 DWORD dwRet;
1402 int first_time = 1;
1403 HANDLE wait_thread = INVALID_HANDLE_VALUE;
1404 #else
1405 int callWaitPid, callDynamicProcs;
1406 #endif
1408 #ifdef WIN32
1409 // Add SystemRoot to the dyanmic environment
1410 char ** envp = dynamicEnvp;
1411 for (i = 0; *envp; ++i) {
1412 ++envp;
1414 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1416 #else
1417 reduce_priveleges();
1419 close(fcgi_pm_pipe[1]);
1420 change_process_name("fcgi-pm");
1421 setup_signals();
1423 if (fcgi_suexec) {
1424 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1425 "FastCGI: suEXEC mechanism enabled (wrapper: %s)", fcgi_suexec);
1427 #endif
1429 /* Initialize AppClass */
1430 tp = ap_make_sub_pool(fcgi_config_pool);
1431 for(s = fcgi_servers; s != NULL; s = s->next) {
1432 if (s->directive == APP_CLASS_EXTERNAL)
1433 continue;
1435 #ifdef WIN32
1436 if (s->socket_path) {
1437 SECURITY_ATTRIBUTES sa;
1439 sa.nLength = sizeof(sa);
1440 sa.lpSecurityDescriptor = NULL;
1441 sa.bInheritHandle = TRUE;
1443 s->listenFd = (int)CreateNamedPipe(s->socket_path, PIPE_ACCESS_DUPLEX,
1444 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
1445 PIPE_UNLIMITED_INSTANCES, 4096,4096,0, &sa);
1446 if ((HANDLE)s->listenFd == INVALID_HANDLE_VALUE) {
1447 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1448 "FastCGI: server \"%s\" disabled, CreateNamedPipe() failed", s->fs_path);
1449 continue;
1452 s->hPipeMutex = ap_create_mutex(NULL);
1454 if (s->hPipeMutex == NULL) {
1455 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1456 "FastCGI: server \"%s\" disabled, ap_create_mutex() failed", s->fs_path);
1457 CloseHandle((HANDLE)s->listenFd);
1458 s->listenFd = (int) INVALID_HANDLE_VALUE;
1459 continue;
1462 SetHandleInformation(s->hPipeMutex, HANDLE_FLAG_INHERIT, TRUE);
1464 append_mutex_to_env(s->envp, s->hPipeMutex);
1466 else
1467 #endif
1469 /* Create the socket */
1470 s->listenFd = ap_psocket(fcgi_config_pool, s->socket_addr->sa_family, SOCK_STREAM, 0);
1471 if (s->listenFd < 0) {
1472 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1473 "FastCGI: server \"%s\" disabled, socket() failed", s->fs_path);
1474 continue;
1477 /* bind() and listen() */
1478 err = bind_n_listen(tp, s->socket_addr, s->socket_addr_len,
1479 s->listenQueueDepth, s->listenFd);
1480 if (err) {
1481 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1482 "FastCGI: server \"%s\" disabled: %s",
1483 s->fs_path, err);
1484 ap_pclosesocket(fcgi_config_pool, s->listenFd);
1485 s->listenFd = -1;
1486 continue;
1490 for (i = 0; i < s->numProcesses; i++)
1491 s->procs[i].state = STATE_NEEDS_STARTING;
1494 ap_destroy_pool(tp);
1496 #ifdef WIN32
1497 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1498 "FastCGI: process manager initialized");
1499 #else
1500 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1501 "FastCGI: process manager initialized (pid %ld)", (long)getpid());
1502 #endif
1504 now = time(NULL);
1507 * Loop until SIGTERM
1509 for (;;) {
1510 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1511 #ifdef WIN32
1512 time_t expire;
1513 #else
1514 pid_t childPid;
1515 int waitStatus;
1516 #endif
1517 unsigned int numChildren;
1520 * If we came out of sigsuspend() for any reason other than
1521 * SIGALRM, pick up where we left off.
1523 if (alarmLeft)
1524 sleepSeconds = alarmLeft;
1527 * Examine each configured AppClass for a process that needs
1528 * starting. Compute the earliest time when the start should
1529 * be attempted, starting it now if the time has passed. Also,
1530 * remember that we do NOT need to restart externally managed
1531 * FastCGI applications.
1533 for (s = fcgi_servers; s != NULL; s = s->next) {
1534 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1535 continue;
1537 if (s->directive == APP_CLASS_DYNAMIC) {
1538 numChildren = dynamicMaxClassProcs;
1539 } else {
1540 numChildren = s->numProcesses;
1543 for (i = 0; i < numChildren; i++) {
1544 if ((s->procs[i].pid <= 0) &&
1545 (s->procs[i].state == STATE_NEEDS_STARTING))
1547 time_t restartTime;
1549 if (s->procs[i].pid == 0) {
1550 restartTime = s->restartTime + s->initStartDelay;
1551 } else {
1552 restartTime = s->restartTime + s->restartDelay;
1555 if (restartTime <= now) {
1556 int restart = (s->procs[i].pid < 0);
1558 s->restartTime = now;
1560 #ifndef WIN32
1561 if (caughtSigTerm) {
1562 goto ProcessSigTerm;
1564 #endif
1565 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1566 if (s->procs[i].pid <= 0) {
1567 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1568 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1569 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1570 s->fs_path);
1572 sleepSeconds = min(sleepSeconds,
1573 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1575 ap_assert(s->procs[i].pid < 0);
1576 break;
1579 if (s->directive == APP_CLASS_DYNAMIC) {
1580 s->numProcesses++;
1581 fcgi_dynamic_total_proc_count++;
1584 s->procs[i].state = STATE_STARTED;
1586 if (restart)
1587 s->numRestarts++;
1589 if (fcgi_suexec != NULL) {
1590 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1591 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1592 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1593 s->fs_path, (long)s->uid, (long)s->gid,
1594 restart ? "re" : "", (long)s->procs[i].pid);
1596 else {
1597 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1598 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1599 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1600 s->fs_path, restart ? "re" : "", (long)s->procs[i].pid);
1602 ap_assert(s->procs[i].pid > 0);
1603 } else {
1604 sleepSeconds = min(sleepSeconds, restartTime - now);
1610 #ifndef WIN32
1611 if(caughtSigTerm) {
1612 goto ProcessSigTerm;
1614 if((!caughtSigChld) && (!caughtSigUsr2)) {
1615 fd_set rfds;
1617 alarm(sleepSeconds);
1619 FD_ZERO(&rfds);
1620 FD_SET(fcgi_pm_pipe[0], &rfds);
1621 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1623 alarmLeft = alarm(0);
1625 callWaitPid = caughtSigChld;
1626 caughtSigChld = FALSE;
1627 callDynamicProcs = caughtSigUsr2;
1628 caughtSigUsr2 = FALSE;
1630 now = time(NULL);
1633 * Dynamic fcgi process management
1635 if((callDynamicProcs) || (!callWaitPid)) {
1636 dynamic_read_msgs(read_ready);
1637 if(fcgi_dynamic_epoch == 0) {
1638 fcgi_dynamic_epoch = now;
1640 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1641 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1642 dynamic_kill_idle_fs_procs();
1643 fcgi_dynamic_epoch = now;
1647 if(!callWaitPid) {
1648 continue;
1651 /* We've caught SIGCHLD, so find out who it was using waitpid,
1652 * write a log message and update its data structure. */
1654 for (;;) {
1655 if (caughtSigTerm)
1656 goto ProcessSigTerm;
1658 childPid = waitpid(-1, &waitStatus, WNOHANG);
1660 if (childPid == -1 || childPid == 0)
1661 break;
1663 for (s = fcgi_servers; s != NULL; s = s->next) {
1664 if (s->directive == APP_CLASS_EXTERNAL)
1665 continue;
1667 if (s->directive == APP_CLASS_DYNAMIC)
1668 numChildren = dynamicMaxClassProcs;
1669 else
1670 numChildren = s->numProcesses;
1672 for (i = 0; i < numChildren; i++) {
1673 if (s->procs[i].pid == childPid)
1674 goto ChildFound;
1678 /* @@@ This (comment) needs to go away when dynamic gets cleaned up.
1679 * If we get to this point, we have detected the
1680 * termination of the process that was spawned off by
1681 * the process manager to do a blocking kill above. */
1682 continue;
1684 ChildFound:
1685 s->procs[i].pid = -1;
1687 if (s->directive == APP_CLASS_STANDARD) {
1688 /* Always restart static apps */
1689 s->procs[i].state = STATE_NEEDS_STARTING;
1690 s->numFailures++;
1692 else {
1693 s->numProcesses--;
1694 fcgi_dynamic_total_proc_count--;
1696 if (s->procs[i].state == STATE_VICTIM) {
1697 s->procs[i].state = STATE_KILLED;
1699 else {
1700 /* A dynamic app died or exited without provacation from the PM */
1701 s->numFailures++;
1703 if (dynamicAutoRestart || s->numProcesses <= 0)
1704 s->procs[i].state = STATE_NEEDS_STARTING;
1705 else
1706 s->procs[i].state = STATE_READY;
1710 if (WIFEXITED(waitStatus)) {
1711 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1712 "FastCGI:%s server \"%s\" (pid %d) terminated by calling exit with status '%d'",
1713 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1714 s->fs_path, (int)childPid, WEXITSTATUS(waitStatus));
1716 else if (WIFSIGNALED(waitStatus)) {
1717 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1718 "FastCGI:%s server \"%s\" (pid %d) terminated due to uncaught signal '%d' (%s)%s",
1719 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1720 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)],
1721 #ifdef WCOREDUMP
1722 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1723 #else
1724 "");
1725 #endif
1727 else if (WIFSTOPPED(waitStatus)) {
1728 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1729 "FastCGI:%s server \"%s\" (pid %d) stopped due to uncaught signal '%d' (%s)",
1730 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1731 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)]);
1733 } /* for (;;), waitpid() */
1734 #else
1735 if (first_time) {
1736 DWORD tid; /* Thread id */
1738 /* Start the child wait thread */
1739 wait_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)child_wait_thread, NULL, 0, &tid);
1740 first_time = 0;
1743 /* wait for an event to occur or timer expires */
1744 expire = time(NULL) + sleepSeconds;
1745 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1747 if (dwRet == WAIT_FAILED) {
1748 /* There is something seriously wrong here */
1749 ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, fcgi_apache_main_server,
1750 "FastDGI: WaitForMultipleObjects on event handles -- pm is shuting down");
1751 bTimeToDie = TRUE;
1754 if (dwRet != WAIT_TIMEOUT) {
1755 now = time(NULL);
1757 if (now < expire)
1758 alarmLeft = expire - now;
1762 * Dynamic fcgi process management
1764 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1765 if (dwRet == MBOX_EVENT) {
1766 read_ready = 1;
1769 now = time(NULL);
1771 dynamic_read_msgs(read_ready);
1773 if(fcgi_dynamic_epoch == 0) {
1774 fcgi_dynamic_epoch = now;
1777 if (((long)(now-fcgi_dynamic_epoch) >= (int)dynamicKillInterval) ||
1778 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1779 dynamic_kill_idle_fs_procs();
1780 fcgi_dynamic_epoch = now;
1782 read_ready = 0;
1784 else if (dwRet == WAKE_EVENT) {
1785 continue;
1787 else if (dwRet == TERM_EVENT) {
1788 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1789 "FastCGI: Termination event received process manager shutting down");
1790 bTimeToDie = TRUE;
1792 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1793 goto ProcessSigTerm;
1795 else {
1796 // Have an received an unknown event - should not happen
1797 ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, fcgi_apache_main_server,
1798 "FastCGI: WaitForMultipleobjects return an unrecognized event");
1799 bTimeToDie = TRUE;
1800 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1801 goto ProcessSigTerm;
1803 #endif
1804 } /* for (;;), the whole shoot'n match */
1806 ProcessSigTerm:
1808 * Kill off the children, then exit.
1810 while (fcgi_servers != NULL) {
1811 kill_fs_procs(fcgi_config_pool, fcgi_servers);
1814 #ifdef WIN32
1815 return;
1816 #else
1817 exit(0);
1818 #endif
1821 #ifdef WIN32
1822 int fcgi_pm_add_job(fcgi_pm_job *new_job) {
1824 if (new_job == NULL)
1825 return 0;
1827 ap_acquire_mutex(fcgi_dynamic_mbox_mutex);
1828 new_job->next = fcgi_dynamic_mbox;
1829 fcgi_dynamic_mbox = new_job;
1830 ap_release_mutex(fcgi_dynamic_mbox_mutex);
1832 return 1;
1834 #endif