Tweak to Makefile.tmpl to support DSOs. Dave Hill [ddhill@zk3.dec.com]
[mod_fastcgi.git] / fcgi_pm.c
blobfcd736a1dab0d177be98dcf18d4bc15a6d0c05cb
1 /*
2 * $Id: fcgi_pm.c,v 1.37 2000/08/11 02:32:17 robs Exp $
3 */
6 #include "fcgi.h"
8 #ifdef _HPUX_SOURCE
9 #include <unistd.h>
10 #define seteuid(arg) setresuid(-1, (arg), -1)
11 #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];
34 #endif
37 #ifndef WIN32
38 static int seteuid_root(void)
40 int rc = seteuid((uid_t)0);
41 if (rc == -1) {
42 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
43 "FastCGI: seteuid(0) failed");
45 return rc;
48 static int seteuid_user(void)
50 int rc = seteuid(ap_user_id);
51 if (rc == -1) {
52 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
53 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id);
55 return rc;
57 #endif
59 static int fcgi_kill(ServerProcess *process, int sig)
61 int rc;
62 FCGIDBG2("fcgi_kill(%ld)", process->pid);
64 #ifndef WIN32
65 if (fcgi_suexec) {
66 seteuid_root();
69 rc = kill(process->pid, sig);
71 if (fcgi_suexec) {
72 seteuid_user();
75 #else
76 rc = TerminateProcess(process->handle, sig);
77 #endif
79 return rc;
82 /*******************************************************************************
83 * Send SIGTERM to each process in the server class, remove socket and lock
84 * file if appropriate. Currently this is only called when the PM is shutting
85 * down and thus memory isn't freed and sockets and files aren't closed.
87 static void kill_fs_procs(pool *p, fcgi_server *s)
89 ServerProcess *proc = s->procs;
90 int i, numChildren;
92 if (s->directive == APP_CLASS_DYNAMIC)
93 numChildren = dynamicMaxClassProcs;
94 else
95 numChildren = s->numProcesses;
97 for (i = 0; i < numChildren; i++, proc++) {
98 #ifndef WIN32
99 if (proc->pid > 0) {
100 fcgi_kill(proc, SIGTERM);
101 proc->pid = -1;
103 #else
104 if (proc->handle != INVALID_HANDLE_VALUE) {
105 fcgi_kill(proc, 1);
106 CloseHandle(proc->handle);
107 proc->handle = INVALID_HANDLE_VALUE;
108 proc->pid = -1;
110 #endif
113 /* Remove the dead lock file */
114 if (s->directive == APP_CLASS_DYNAMIC) {
115 #ifndef WIN32
116 const char *lockFileName = fcgi_util_socket_get_lock_filename(p, s->socket_path);
118 if (unlink(lockFileName) != 0) {
119 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
120 "FastCGI: unlink() failed to remove lock file \"%s\" for (dynamic) server \"%s\"",
121 lockFileName, s->fs_path);
123 #else
124 fcgi_rdwr_destroy(s->dynamic_lock);
125 #endif
128 /* Remove the socket file */
129 if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL) {
130 #ifndef WIN32
131 if (unlink(s->socket_path) != 0) {
132 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
133 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
134 s->socket_path,
135 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path);
137 #else
138 CloseHandle((HANDLE)s->listenFd);
139 #endif
141 fcgi_servers = s->next;
144 /*******************************************************************************
145 * Bind an address to a socket and set it to listen for incoming connects.
146 * The error messages are allocated from the pool p, use temp storage.
147 * Don't forget to close the socket, if an error occurs.
149 static const char *bind_n_listen(pool *p, struct sockaddr *socket_addr,
150 int socket_addr_len, int backlog, int sock)
152 #ifndef WIN32
153 if (socket_addr->sa_family == AF_UNIX) {
154 /* Remove any existing socket file.. just in case */
155 unlink(((struct sockaddr_un *)socket_addr)->sun_path);
157 else
158 #endif
160 int flag = 1;
161 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
164 /* Bind it to the socket_addr */
165 if (bind(sock, socket_addr, socket_addr_len) != 0) {
166 return ap_psprintf(p, "bind() failed [%s]",
167 #ifndef WIN32
168 socket_addr->sa_family == AF_UNIX ?
169 ((struct sockaddr_un *)socket_addr)->sun_path :
170 #endif
171 ap_psprintf(p, "port=%d", ((struct sockaddr_in *)socket_addr)->sin_port));
174 #ifndef WIN32
175 /* Twiddle permissions */
176 if (socket_addr->sa_family == AF_UNIX) {
177 if (chmod(((struct sockaddr_un *)socket_addr)->sun_path, S_IRUSR | S_IWUSR))
178 return "chmod() of socket failed";
180 #endif
182 /* Set to listen */
183 if (listen(sock, backlog) != 0)
184 return "listen() failed";
186 return NULL;
190 *----------------------------------------------------------------------
192 * dynamic_blocking_kill
194 * Block on the lock file until it is available, and then
195 * issue a kill signal to the corresponding application.
196 * Since this function is executed in the child process,
197 * _exit() is called upon completion.
199 * Inputs
200 * Pointer to the data structure containing a process id to
201 * issue a signal to and the full pathname to the lockfile
202 * that needs to be locked before the issue of the signal.
204 * Notes
205 * Memory is allocated by the caller, but is freed by this
206 * function.
208 *----------------------------------------------------------------------
210 static void dynamic_blocking_kill(void *data)
212 struct FuncData *funcData = (struct FuncData *)data;
214 #ifndef WIN32
215 int lockFd;
217 ap_assert(funcData->lockFileName);
218 if ((lockFd = open(funcData->lockFileName, O_RDWR)) < 0) {
219 /* There is something terribly wrong here */
220 } else {
221 if (fcgi_wait_for_shared_write_lock(lockFd) < 0) {
222 /* This is a major problem */
223 } else {
224 fcgi_kill(funcData->process, SIGTERM);
227 /* exit() may flush stdio buffers inherited from the parent. */
228 _exit(0);
230 #else
231 FCGIDBG1("dynamic_blocking_kill()");
232 if (fcgi_wait_for_shared_write_lock(funcData->lock) < 0) {
233 // This is a major problem
234 FCGIDBG1("fcgi_wait_for_shared_write_lock() failed >> MAJOR PROBLEM");
236 else {
237 fcgi_kill(funcData->process, 1);
238 fcgi_rdwr_unlock(funcData->lock, WRITER);
240 free(data);
241 return;
242 #endif
246 *----------------------------------------------------------------------
248 * pm_main
250 * The FastCGI process manager, which runs as a separate
251 * process responsible for:
252 * - Starting all the FastCGI proceses.
253 * - Restarting any of these processes that die (indicated
254 * by SIGCHLD).
255 * - Catching SIGTERM and relaying it to all the FastCGI
256 * processes before exiting.
258 * Inputs:
259 * Uses global variable fcgi_servers.
261 * Results:
262 * Does not return.
264 * Side effects:
265 * Described above.
267 *----------------------------------------------------------------------
269 #ifndef WIN32
270 static int caughtSigTerm = FALSE;
271 static int caughtSigChld = FALSE;
272 static int caughtSigUsr2 = FALSE;
274 static void signal_handler(int signo)
276 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
277 /* SIGUSR1 & SIGHUP are sent by apache to its process group
278 * when apache get 'em. Apache follows up (1.2.x) with attacks
279 * on each of its child processes, but we've got the KillMgr
280 * sitting between us so we never see the KILL. The main loop
281 * in ProcMgr also checks to see if the KillMgr has terminated,
282 * and if it has, we handl it as if we should shutdown too. */
283 caughtSigTerm = TRUE;
284 } else if(signo == SIGCHLD) {
285 caughtSigChld = TRUE;
286 } else if(signo == SIGALRM) {
287 caughtSigUsr2 = TRUE;
290 #endif
293 *----------------------------------------------------------------------
295 * spawn_fs_process --
297 * Fork and exec the specified fcgi process.
299 * Results:
300 * 0 for successful fork, -1 for failed fork.
302 * In case the child fails before or in the exec, the child
303 * obtains the error log by calling getErrLog, logs
304 * the error, and exits with exit status = errno of
305 * the failed system call.
307 * Side effects:
308 * Child process created.
310 *----------------------------------------------------------------------
313 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
315 #ifndef WIN32
316 pid_t child_pid;
317 int i;
318 char *dirName;
319 char *dnEnd, *failedSysCall;
321 child_pid = fork();
322 if (child_pid) {
323 return child_pid;
326 /* We're the child. We're gonna exec() so pools don't matter. */
328 dnEnd = strrchr(fs->fs_path, '/');
329 if (dnEnd == NULL) {
330 dirName = "./";
331 } else {
332 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
333 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
335 if (chdir(dirName) < 0) {
336 failedSysCall = "chdir()";
337 goto FailedSystemCallExit;
340 #ifndef __EMX__
341 /* OS/2 dosen't support nice() */
342 if (fs->processPriority != 0) {
343 if (nice(fs->processPriority) == -1) {
344 failedSysCall = "nice()";
345 goto FailedSystemCallExit;
348 #endif
350 /* Open the listenFd on spec'd fd */
351 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
352 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
354 /* Close all other open fds, except stdout/stderr. Leave these two open so
355 * FastCGI applications don't have to find and fix ALL 3rd party libs that
356 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
357 * main server error_log - @@@ provide a directive control where this goes.
359 ap_error_log2stderr(fcgi_apache_main_server);
360 dup2(STDERR_FILENO, STDOUT_FILENO);
361 for (i = 0; i < FCGI_MAX_FD; i++) {
362 if (i != FCGI_LISTENSOCK_FILENO && i != STDERR_FILENO && i != STDOUT_FILENO) {
363 close(i);
367 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
368 * install its own handler. */
369 signal(SIGPIPE, SIG_IGN);
371 if (fcgi_suexec != NULL) {
372 char *shortName = strrchr(fs->fs_path, '/') + 1;
374 /* Relinquish our root real uid powers */
375 seteuid_root();
376 setuid(ap_user_id);
378 do {
379 execle(fcgi_suexec, fcgi_suexec, fs->username, fs->group, shortName, NULL, fs->envp);
380 } while (errno == EINTR);
382 else {
383 do {
384 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
385 } while (errno == EINTR);
388 failedSysCall = "execle()";
390 FailedSystemCallExit:
391 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
392 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
393 exit(-1);
395 /* avoid an irrelevant compiler warning */
396 return(0);
398 #else
400 /* Adapted from Apache's util_script.c ap_call_exec() */
401 char *interpreter = NULL;
402 char *ext = NULL;
403 char *exename = NULL;
404 char *s = NULL;
405 char *quoted_filename;
406 char *pCommand;
407 char *pEnvBlock, *pNext;
409 int i;
410 int iEnvBlockLen;
412 file_type_e fileType;
414 STARTUPINFO si;
415 PROCESS_INFORMATION pi;
417 request_rec r;
418 pid_t pid = -1;
420 HANDLE listen_handle, mutex;
421 char * mutex_string = NULL;
423 pool * tp = ap_make_sub_pool(fcgi_config_pool);
425 if (fs->socket_path)
427 SECURITY_ATTRIBUTES sa;
429 sa.nLength = sizeof(sa);
430 sa.lpSecurityDescriptor = NULL;
431 sa.bInheritHandle = TRUE;
433 listen_handle = CreateNamedPipe(fs->socket_path, PIPE_ACCESS_DUPLEX,
434 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
435 PIPE_UNLIMITED_INSTANCES, 4096,4096,0, &sa);
436 if (listen_handle == INVALID_HANDLE_VALUE)
438 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
439 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
440 exit(0);
443 // This mutex is not really necessary, but for compatibility with
444 // the existing library, we need it.
446 mutex = ap_create_mutex(NULL);
447 if (mutex == NULL)
449 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
450 "FastCGI: can't exec server \"%s\", ap_create_mutex() failed", fs->fs_path);
451 CloseHandle(listen_handle);
452 exit(0);
455 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
457 mutex_string = ap_psprintf(tp, "_FCGI_MUTEX_=%ld", mutex);
459 else
461 listen_handle = (HANDLE) fs->listenFd;
464 memset(&si, 0, sizeof(si));
465 memset(&pi, 0, sizeof(pi));
466 memset(&r, 0, sizeof(r));
468 // Can up a fake request to pass to ap_get_win32_interpreter()
469 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
470 r.server = fcgi_apache_main_server;
471 r.filename = (char *) fs->fs_path;
472 r.pool = tp;
474 fileType = ap_get_win32_interpreter(&r, &interpreter);
476 if (fileType == eFileTypeUNKNOWN) {
477 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, fcgi_apache_main_server,
478 "FastCGI: %s is not executable; ensure interpreted scripts have "
479 "\"#!\" as their first line",
480 fs->fs_path);
481 ap_destroy_pool(tp);
482 return (pid);
486 * We have the interpreter (if there is one) and we have
487 * the arguments (if there are any).
488 * Build the command string to pass to CreateProcess.
490 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
491 if (interpreter && *interpreter) {
492 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
494 else {
495 pCommand = quoted_filename;
499 * Make child process use hPipeOutputWrite as standard out,
500 * and make sure it does not show on screen.
502 si.cb = sizeof(si);
503 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
504 si.wShowWindow = SW_HIDE;
505 si.hStdInput = listen_handle;
507 // XXX These should be open to the error_log
508 si.hStdOutput = INVALID_HANDLE_VALUE;
509 si.hStdError = INVALID_HANDLE_VALUE;
512 * Win32's CreateProcess call requires that the environment
513 * be passed in an environment block, a null terminated block of
514 * null terminated strings.
516 i = 0;
517 iEnvBlockLen = 1;
518 while (fs->envp[i]) {
519 iEnvBlockLen += strlen(fs->envp[i]) + 1;
520 i++;
523 if (fs->socket_path)
525 iEnvBlockLen += strlen(mutex_string) + 1;
528 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
530 i = 0;
531 pNext = pEnvBlock;
532 while (fs->envp[i]) {
533 strcpy(pNext, fs->envp[i]);
534 pNext = pNext + strlen(pNext) + 1;
535 i++;
538 if (fs->socket_path)
540 strcpy(pNext, mutex_string);
543 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
545 pEnvBlock,
546 ap_make_dirstr_parent(tp, fs->fs_path),
547 &si, &pi)) {
548 if (fileType == eFileTypeEXE16) {
549 /* Hack to get 16-bit CGI's working. It works for all the
550 * standard modules shipped with Apache. pi.dwProcessId is 0
551 * for 16-bit CGIs and all the Unix specific code that calls
552 * ap_call_exec interprets this as a failure case. And we can't
553 * use -1 either because it is mapped to 0 by the caller.
555 pid = -2;
557 else {
558 pid = pi.dwProcessId;
559 process->handle = pi.hProcess;
560 CloseHandle(pi.hThread);
564 // We don't need these anymore..
565 if (fs->socket_path)
567 CloseHandle(listen_handle);
568 CloseHandle(mutex);
571 ap_destroy_pool(tp);
573 return pid;
575 #endif
578 #ifndef WIN32
579 static void reduce_priveleges(void)
581 char *name;
583 if (geteuid() != 0)
584 return;
586 #ifndef __EMX__
587 /* Get username if passed as a uid */
588 if (ap_user_name[0] == '#') {
589 uid_t uid = atoi(&ap_user_name[1]);
590 struct passwd *ent = getpwuid(uid);
592 if (ent == NULL) {
593 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
594 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
595 "you probably need to modify the User directive", (unsigned)uid);
596 exit(1);
598 name = ent->pw_name;
600 else
601 name = ap_user_name;
603 /* Change Group */
604 if (setgid(ap_group_id) == -1) {
605 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
606 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
607 exit(1);
610 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
612 /* Initialize supplementary groups */
613 if (initgroups(name, ap_group_id) == -1) {
614 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
615 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
616 name, (unsigned)ap_group_id);
617 exit(1);
619 #endif /* __EMX__ */
621 /* Change User */
622 if (fcgi_suexec) {
623 if (seteuid_user() == -1) {
624 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
625 "FastCGI: process manager exiting, failed to reduce priveleges");
626 exit(1);
629 else {
630 if (setuid(ap_user_id) == -1) {
631 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
632 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
633 exit(1);
638 /*************
639 * Change the name of this process - best we can easily.
641 static void change_process_name(const char * const name)
643 strncpy(ap_server_argv0, name, strlen(ap_server_argv0));
645 #endif
647 static void schedule_start(fcgi_server *s, int proc)
649 /* If we've started one recently, don't register another */
650 time_t time_passed = now - s->restartTime;
652 if ((s->procs[proc].pid && time_passed < (int) s->restartDelay)
653 || (s->procs[proc].pid == 0 && time_passed < (int) s->initStartDelay))
655 FCGIDBG6("ignore_job: slot=%d, pid=%ld, time_passed=%ld, initStartDelay=%ld, restartDelay=%ld", proc, s->procs[proc].pid, time_passed, s->initStartDelay, s->restartDelay);
656 return;
659 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
660 s->procs[proc].state = STATE_NEEDS_STARTING;
661 if (proc == (int)dynamicMaxClassProcs - 1) {
662 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
663 "FastCGI: scheduled the %sstart of the last (dynamic) server "
664 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
665 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
670 *----------------------------------------------------------------------
672 * dynamic_read_msgs
674 * Removes the records written by request handlers and decodes them.
675 * We also update the data structures to reflect the changes.
677 *----------------------------------------------------------------------
680 static void dynamic_read_msgs(int read_ready)
682 fcgi_server *s;
684 #ifndef WIN32
685 int rc;
686 static int buflen = 0;
687 static char buf[FCGI_MSGS_BUFSIZE + 1];
688 char *ptr1, *ptr2, opcode;
689 char execName[FCGI_MAXPATH + 1];
690 char user[MAX_USER_NAME_LEN + 2];
691 char group[MAX_GID_CHAR_LEN + 1];
692 unsigned long q_usec = 0UL, req_usec = 0UL;
693 #else
694 fcgi_pm_job *joblist = NULL;
695 fcgi_pm_job *cjob = NULL;
696 #endif
698 pool *sp, *tp;
700 #ifndef WIN32
701 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
702 #endif
705 * To prevent the idle application from running indefinitely, we
706 * check the timer and if it is expired, we recompute the values
707 * for each running application class. Then, when REQ_COMPLETE
708 * message is recieved, only updates are made to the data structures.
710 if (fcgi_dynamic_last_analyzed == 0) {
711 fcgi_dynamic_last_analyzed = now;
713 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
714 for (s = fcgi_servers; s != NULL; s = s->next) {
715 if (s->directive != APP_CLASS_DYNAMIC)
716 break;
717 /* XXX what does this adjustment do? */
718 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
719 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
720 s->totalConnTime = 0UL;
721 s->totalQueueTime = 0UL;
725 if (read_ready <= 0) {
726 return;
729 #ifndef WIN32
730 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
731 if (rc <= 0) {
732 if (!caughtSigTerm) {
733 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
734 "FastCGI: read() from pipe failed (%d)", rc);
735 if (rc == 0) {
736 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
737 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
738 caughtSigTerm = TRUE;
741 return;
743 buflen += rc;
744 buf[buflen] = '\0';
745 #else
746 if (ap_acquire_mutex(fcgi_dynamic_mbox_mutex) != MULTI_OK) {
747 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
748 "FastCGI: Failed to aquire the dynamic mbox mutex!");
751 joblist = fcgi_dynamic_mbox;
752 fcgi_dynamic_mbox = NULL;
754 if (! ap_release_mutex(fcgi_dynamic_mbox_mutex)) {
755 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
756 "FastCGI: Failed to release the dynamic mbox mutex!");
759 cjob = joblist;
760 #endif
762 tp = ap_make_sub_pool(fcgi_config_pool);
764 #ifndef WIN32
765 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
766 int scan_failed = 0;
768 ptr2 = strchr(ptr1, '*');
769 if (ptr2) {
770 *ptr2++ = '\0';
772 else {
773 break;
776 opcode = *ptr1;
778 switch (opcode) {
779 case PLEASE_START:
780 if (sscanf(ptr1, "%c %s %16s %15s",
781 &opcode, execName, user, group) != 4)
783 scan_failed = 1;
785 break;
786 case CONN_TIMEOUT:
787 if (sscanf(ptr1, "%c %s %16s %15s",
788 &opcode, execName, user, group) != 4)
790 scan_failed = 1;
792 break;
793 case REQ_COMPLETE:
794 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
795 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
797 scan_failed = 1;
799 break;
800 default:
801 scan_failed = 1;
802 break;
805 if (scan_failed) {
806 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
807 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
808 goto NextJob;
810 #else
811 /* Update data structures for processing */
812 while (cjob != NULL) {
813 joblist = cjob->next;
814 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
815 #endif
817 #ifndef WIN32
818 s = fcgi_util_fs_get(execName, user, group);
819 #else
820 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
821 #endif
823 #ifndef WIN32
824 if (s==NULL && opcode != REQ_COMPLETE)
825 #else
826 if (s==NULL && cjob->id != REQ_COMPLETE)
827 #endif
829 #ifndef WIN32
830 int fd;
831 const char *err, *lockPath;
832 #endif
834 /* Create a perm subpool to hold the new server data,
835 * we can destroy it if something doesn't pan out */
836 sp = ap_make_sub_pool(fcgi_config_pool);
838 /* Create a new "dynamic" server */
839 s = fcgi_util_fs_new(sp);
840 s->directive = APP_CLASS_DYNAMIC;
841 s->restartDelay = dynamicRestartDelay;
842 s->listenQueueDepth = dynamicListenQueueDepth;
843 s->initStartDelay = dynamicInitStartDelay;
844 s->envp = dynamicEnvp;
845 #ifdef WIN32
846 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
847 #else
848 s->fs_path = ap_pstrdup(sp, execName);
849 #endif
850 ap_getparents(s->fs_path);
851 ap_no2slash(s->fs_path);
852 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
854 #ifndef WIN32
855 /* Create socket file's path */
856 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
857 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
859 /* Create sockaddr, prealloc it so it won't get created in tp */
860 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
861 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
862 &s->socket_addr_len, s->socket_path);
863 if (err) {
864 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
865 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
866 goto BagNewServer;
869 /* Create the socket */
870 if ((s->listenFd = ap_psocket(sp, s->socket_addr->sa_family, SOCK_STREAM, 0)) < 0) {
871 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
872 "FastCGI: can't create (dynamic) server \"%s\": socket() failed", execName);
873 goto BagNewServer;
876 /* bind() and listen() */
877 err = bind_n_listen(tp, s->socket_addr, s->socket_addr_len,
878 s->listenQueueDepth, s->listenFd);
879 if (err) {
880 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
881 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
882 goto BagNewServer;
885 /* Create the lock file */
886 lockPath = fcgi_util_socket_get_lock_filename(tp, s->socket_path);
887 fd = ap_popenf(tp, lockPath,
888 O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
889 if (fd < 0) {
890 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
891 "FastCGI: can't create (dynamic) server \"%s\": can't open lock file \"%s\": popenf() failed",
892 execName, lockPath);
893 goto BagNewServer;
895 ap_pclosef(tp, fd);
897 /* If suexec is being used, config user/group info */
898 if (fcgi_suexec) {
899 if (user[0] == '~') {
900 /* its a user dir uri, the rest is a username, not a uid */
901 struct passwd *pw = getpwnam(&user[1]);
903 if (!pw) {
904 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
905 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for suexec: getpwnam(%s) failed",
906 execName, &user[1]);
907 goto BagNewServer;
909 s->uid = pw->pw_uid;
910 s->user = ap_pstrdup(sp, user);
911 s->username = s->user;
913 s->gid = pw->pw_gid;
914 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
916 else {
917 struct passwd *pw;
919 s->uid = (uid_t)atol(user);
920 pw = getpwuid(s->uid);
921 if (!pw) {
922 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
923 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for suexec: getwpuid(%ld) failed",
924 execName, (long)s->uid);
925 goto BagNewServer;
927 s->user = ap_pstrdup(sp, user);
928 s->username = ap_pstrdup(sp, pw->pw_name);
930 s->gid = (gid_t)atol(group);
931 s->group = ap_pstrdup(sp, group);
934 #else
935 /* Create socket file's path */
936 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
937 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
938 s->listenFd = 0;
940 /* Create the application lock */
941 if ((s->dynamic_lock = fcgi_rdwr_create()) == NULL) {
942 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
943 "FastCGI: can;t create (dynamic) server \"%s\": fcgi_rdwr_create() failed",
944 cjob->fs_path);
945 goto BagNewServer;
948 // Lock it until the first process is started
949 fcgi_rdwr_lock(s->dynamic_lock, WRITER);
950 #endif
952 fcgi_util_fs_add(s);
954 else {
955 #ifndef WIN32
956 if (opcode == PLEASE_START) {
957 #else
958 if(cjob->id==PLEASE_START) {
959 #endif
960 if (dynamicAutoUpdate) {
961 /* Check to see if the binary has changed. If so,
962 * kill the FCGI application processes, and
963 * restart them.
965 struct stat stbuf;
966 unsigned int i;
968 #ifndef WIN32
969 if ((stat(execName, &stbuf)==0) &&
970 #else
971 if ((stat(cjob->fs_path, &stbuf)==0) &&
972 #endif
973 (stbuf.st_mtime > s->restartTime)) {
974 /* kill old server(s) */
975 for (i = 0; i < dynamicMaxClassProcs; i++) {
976 if (s->procs[i].pid > 0) {
977 fcgi_kill(&s->procs[i], SIGTERM);
981 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
982 "FastCGI: restarting server \"%s\" processes, newer version found",
983 #ifndef WIN32
984 execName);
985 #else
986 cjob->fs_path);
987 #endif
990 /* If dynamicAutoRestart, don't mark any new processes
991 * for starting because we probably got the
992 * PLEASE_START due to dynamicAutoUpdate and the ProcMgr
993 * will be restarting all of those we just killed.
995 if (dynamicAutoRestart)
996 goto NextJob;
997 } else {
998 /* we've been asked to start a process--only start
999 * it if we're not already running at least one
1000 * instance.
1002 unsigned int i;
1004 for (i = 0; i < dynamicMaxClassProcs; i++) {
1005 if (s->procs[i].state == STATE_STARTED)
1006 break;
1008 /* if already running, don't start another one */
1009 if (i < dynamicMaxClassProcs) {
1010 goto NextJob;
1015 #ifndef WIN32
1016 switch (opcode)
1017 #else
1018 switch (cjob->id)
1019 #endif
1021 unsigned int i;
1023 case PLEASE_START:
1024 case CONN_TIMEOUT:
1026 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1028 * Extra instances should have been
1029 * terminated beforehand, probably need
1030 * to increase ProcessSlack parameter
1032 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1033 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1034 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1035 goto NextJob;
1038 /* find next free slot */
1039 for (i = 0; i < dynamicMaxClassProcs; i++)
1041 if (s->procs[i].state == STATE_NEEDS_STARTING)
1043 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1044 break;
1046 else if (s->procs[i].state == STATE_STARTED)
1048 continue;
1051 schedule_start(s, i);
1052 break;
1055 #ifdef FCGI_DEBUG
1056 if (i >= dynamicMaxClassProcs) {
1057 FCGIDBG1("ignore_job: slots are max'd");
1059 #endif
1060 break;
1061 case REQ_COMPLETE:
1062 /* only record stats if we have a structure */
1063 if (s) {
1064 #ifndef WIN32
1065 s->totalConnTime += req_usec;
1066 s->totalQueueTime += q_usec;
1067 #else
1068 s->totalConnTime += cjob->start_time;
1069 s->totalQueueTime += cjob->qsec;
1070 #endif
1072 break;
1075 NextJob:
1077 #ifdef WIN32
1078 /* Cleanup job data */
1079 free(cjob->fs_path);
1080 free(cjob->user);
1081 free(cjob->group);
1082 free(cjob);
1083 cjob = joblist;
1084 #endif
1086 continue;
1088 BagNewServer:
1089 ap_destroy_pool(sp);
1091 #ifdef WIN32
1092 free(cjob->fs_path);
1093 free(cjob);
1094 cjob = joblist;
1095 #endif
1098 #ifndef WIN32
1099 if (ptr1 == buf) {
1100 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1101 "FastCGI: really bogus message: \"%s\"", ptr1);
1102 ptr1 += strlen(buf);
1105 buflen -= ptr1 - buf;
1106 if (buflen) {
1107 memmove(buf, ptr1, buflen);
1109 #endif
1111 ap_destroy_pool(tp);
1115 *----------------------------------------------------------------------
1117 * dynamic_kill_idle_fs_procs
1119 * Implement a kill policy for the dynamic FastCGI applications.
1120 * We also update the data structures to reflect the changes.
1122 * Side effects:
1123 * Processes are marked for deletion possibly killed.
1125 *----------------------------------------------------------------------
1127 static void dynamic_kill_idle_fs_procs(void)
1129 fcgi_server *s;
1130 struct FuncData *funcData = NULL;
1131 unsigned long connTime; /* server's smoothed running time, or
1132 * if that's 0, the current total */
1133 unsigned long totalTime; /* maximum number of microseconds that all
1134 * of a server's running processes together
1135 * could have spent running since the
1136 * last check */
1137 double loadFactor; /* percentage, 0-100, of totalTime that
1138 * the processes actually used */
1139 unsigned int i, victims = 0;
1140 #ifndef WIN32
1141 const char *lockFileName;
1142 int lockFd;
1143 pid_t pid;
1144 #endif
1145 pool *tp = ap_make_sub_pool(fcgi_config_pool);
1147 /* pass 1 - locate and mark all victims */
1148 for(s=fcgi_servers; s!=NULL; s=s->next) {
1149 /* Only kill dynamic apps */
1150 if (s->directive != APP_CLASS_DYNAMIC)
1151 continue;
1153 /* If the number of non-victims is less than or equal to
1154 the minimum that may be running without being killed off,
1155 don't select any more victims. */
1156 if ((fcgi_dynamic_total_proc_count - victims) <= (int) dynamicMinProcs) {
1157 break;
1160 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1161 totalTime = (s->numProcesses)*(now - fcgi_dynamic_epoch)*1000000 + 1;
1163 /* XXX producing a heavy load with one client, I haven't been
1164 able to achieve a loadFactor greater than 0.5. Perhaps this
1165 should be scaled up by another order of magnitude or two. */
1166 loadFactor = connTime/totalTime*100.0;
1168 if ((s->numProcesses > 1
1169 && s->numProcesses/(s->numProcesses - 1)*loadFactor < dynamicThreshholdN)
1170 || (s->numProcesses == 1 && loadFactor < dynamicThreshhold1))
1172 int got_one = 0;
1174 for (i = 0; !got_one && i < dynamicMaxClassProcs; ++i) {
1175 if (s->procs[i].state == STATE_NEEDS_STARTING) {
1176 s->procs[i].state = STATE_READY;
1177 got_one = 1;
1179 else if (s->procs[i].state == STATE_VICTIM || s->procs[i].state == STATE_KILL) {
1180 got_one = 1;
1184 for (i = 0; !got_one && i < dynamicMaxClassProcs; ++i) {
1185 if (s->procs[i].state == STATE_STARTED) {
1186 s->procs[i].state = STATE_KILL;
1187 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1188 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination scheduled",
1189 s->fs_path, s->procs[i].pid);
1190 victims++;
1191 got_one = 1;
1197 /* pass 2 - kill procs off */
1198 for(s=fcgi_servers; s!=NULL; s=s->next) {
1199 /* Only kill dynamic apps */
1200 if (s->directive != APP_CLASS_DYNAMIC)
1201 continue;
1203 for(i = 0; i < dynamicMaxClassProcs; i++) {
1204 if (s->procs[i].state == STATE_KILL) {
1205 #ifndef WIN32
1206 lockFileName = fcgi_util_socket_get_lock_filename(tp, s->socket_path);
1207 if ((lockFd = ap_popenf(tp, lockFileName, O_RDWR, 0))<0) {
1209 * If we need to kill an application and the
1210 * corresponding lock file does not exist, then
1211 * that means we are in big trouble here
1213 /*@@@ this should be logged, but since all the lock
1214 * file stuff will be tossed, I'll leave it now */
1215 ap_pclosef(tp, lockFd);
1216 continue;
1219 if (fcgi_get_exclusive_write_lock_no_wait(lockFd) < 0) {
1220 #else
1221 if (fcgi_get_exclusive_write_lock_no_wait(s->dynamic_lock) < 0) {
1222 #endif
1223 FCGIDBG2("fcgi_get_exclusive_write_lock_no_wait() failed (%ld)", GetLastError());
1225 * Unable to lock the lockfile, indicative
1226 * of WS performing operation with the given
1227 * application class. The simplest solution
1228 * is to spawn off another process and block
1229 * on lock to kill it. This is under assumptions
1230 * that fork() is not very costly and this
1231 * situation occurs very rarely, which it should
1233 #ifndef WIN32
1234 funcData = ap_pcalloc(tp, sizeof(struct FuncData));
1235 funcData->lockFileName = lockFileName;
1236 #else
1237 funcData = malloc(sizeof(struct FuncData));
1238 funcData->lock = s->dynamic_lock;
1239 #endif
1240 funcData->process = &s->procs[i];
1242 #ifndef WIN32
1243 if((pid=fork())<0) {
1244 /*@@@ this should be logged, but since all the lock
1245 * file stuff will be tossed, I'll leave it now */
1246 ap_pclosef(tp, lockFd);
1247 continue;
1248 } else if(pid==0) {
1249 /* child */
1251 /* rename the process for ps - best we can easily */
1252 change_process_name("fcgiBlkKill");
1254 dynamic_blocking_kill(funcData);
1255 } else {
1256 /* parent */
1257 s->procs[i].state = STATE_VICTIM;
1258 ap_pclosef(tp, lockFd);
1260 #else
1261 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) dynamic_blocking_kill, (LPVOID) funcData, 0, NULL);
1262 s->procs[i].state = STATE_VICTIM;
1263 #endif
1265 else {
1266 FCGIDBG1("fcgi_get_exclusive_write_lock_no_wait() succeeded");
1267 s->procs[i].state = STATE_VICTIM;
1268 #ifndef WIN32
1269 fcgi_kill(&s->procs[i], SIGTERM);
1270 ap_pclosef(tp, lockFd);
1271 #else
1272 fcgi_kill(&s->procs[i], 1);
1273 fcgi_rdwr_unlock(s->dynamic_lock, WRITER);
1274 #endif
1279 ap_destroy_pool(tp);
1282 #ifdef WIN32
1284 // This is a little bogus, there's gotta be a better way to do this
1285 // Can we use WaitForMultipleObjects()
1286 #define FCGI_PROC_WAIT_TIME 100
1288 void child_wait_thread(void *dummy) {
1289 fcgi_server *s;
1290 DWORD dwRet = WAIT_TIMEOUT;
1291 int numChildren;
1292 int i;
1293 int waited;
1295 while (!bTimeToDie) {
1296 waited = 0;
1298 for (s = fcgi_servers; s != NULL; s = s->next) {
1299 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1300 continue;
1302 if (s->directive == APP_CLASS_DYNAMIC) {
1303 numChildren = dynamicMaxClassProcs;
1305 else {
1306 numChildren = s->numProcesses;
1309 for (i=0; i < numChildren; i++) {
1310 if (s->procs[i].handle != INVALID_HANDLE_VALUE) {
1311 /* timeout is currently set for 100 miliecond */
1312 /* it may need t longer or user customizable */
1313 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1315 waited = 1;
1317 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1318 /* a child fs has died */
1319 /* mark the child as dead */
1321 if (s->directive == APP_CLASS_STANDARD) {
1322 /* restart static app */
1323 s->procs[i].state = STATE_NEEDS_STARTING;
1324 s->numFailures++;
1326 else {
1327 s->numProcesses--;
1328 fcgi_dynamic_total_proc_count--;
1329 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1331 if (s->procs[i].state == STATE_VICTIM) {
1332 s->procs[i].state = STATE_KILLED;
1334 else {
1335 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1336 s->numFailures++;
1338 if (dynamicAutoRestart) {
1339 s->procs[i].state = STATE_NEEDS_STARTING;
1341 else {
1342 s->procs[i].state = STATE_READY;
1347 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1348 "FastCGI:%s server \"%s\" (pid %d) terminated",
1349 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1350 s->fs_path, s->procs[i].pid);
1352 CloseHandle(s->procs[i].handle);
1353 s->procs[i].handle = INVALID_HANDLE_VALUE;
1354 s->procs[i].pid = -1;
1356 /* wake up the main thread */
1357 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1362 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1365 #endif
1367 #ifndef WIN32
1368 static void setup_signals(void)
1370 sigset_t mask;
1371 struct sigaction sa;
1373 /* Ignore USR2 */
1374 sigemptyset(&mask);
1375 sigaddset(&mask, SIGUSR2);
1376 sigprocmask(SIG_BLOCK, &mask, NULL);
1378 /* Setup handlers */
1380 sa.sa_handler = signal_handler;
1381 sigemptyset(&sa.sa_mask);
1382 sa.sa_flags = 0;
1384 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1385 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1386 "sigaction(SIGTERM) failed");
1388 /* httpd restart */
1389 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1390 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1391 "sigaction(SIGHUP) failed");
1393 /* httpd graceful restart */
1394 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1395 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1396 "sigaction(SIGUSR1) failed");
1398 /* read messages from request handlers - kill interval expired */
1399 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1400 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1401 "sigaction(SIGALRM) failed");
1403 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1404 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1405 "sigaction(SIGCHLD) failed");
1408 #endif
1410 #ifndef WIN32
1411 int fcgi_pm_main(void *dummy, child_info *info)
1412 #else
1413 void fcgi_pm_main(void *dummy)
1414 #endif
1416 fcgi_server *s;
1417 unsigned int i;
1418 int read_ready = 0;
1419 int alarmLeft = 0;
1420 pool *tp;
1421 const char *err;
1423 #ifdef WIN32
1424 DWORD dwRet;
1425 int first_time = 1;
1426 HANDLE wait_thread = INVALID_HANDLE_VALUE;
1427 #else
1428 int callWaitPid, callDynamicProcs;
1429 #endif
1431 #ifdef WIN32
1432 // Add SystemRoot to the dyanmic environment
1433 char ** envp = dynamicEnvp;
1434 for (i = 0; *envp; ++i) {
1435 ++envp;
1437 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1439 #else
1440 reduce_priveleges();
1442 close(fcgi_pm_pipe[1]);
1443 change_process_name("fcgi-pm");
1444 setup_signals();
1446 if (fcgi_suexec) {
1447 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1448 "FastCGI: suEXEC mechanism enabled (wrapper: %s)", fcgi_suexec);
1450 #endif
1452 /* Initialize AppClass */
1453 tp = ap_make_sub_pool(fcgi_config_pool);
1454 for(s = fcgi_servers; s != NULL; s = s->next) {
1455 if (s->directive == APP_CLASS_EXTERNAL)
1456 continue;
1458 #ifdef WIN32
1459 if (s->socket_path)
1461 s->listenFd = 0;
1463 else
1464 #endif
1466 /* Create the socket */
1467 s->listenFd = ap_psocket(fcgi_config_pool, s->socket_addr->sa_family, SOCK_STREAM, 0);
1468 if (s->listenFd < 0) {
1469 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1470 "FastCGI: server \"%s\" disabled, socket() failed", s->fs_path);
1471 continue;
1474 /* bind() and listen() */
1475 err = bind_n_listen(tp, s->socket_addr, s->socket_addr_len,
1476 s->listenQueueDepth, s->listenFd);
1477 if (err) {
1478 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1479 "FastCGI: server \"%s\" disabled: %s",
1480 s->fs_path, err);
1481 ap_pclosesocket(fcgi_config_pool, s->listenFd);
1482 s->listenFd = -1;
1483 continue;
1487 for (i = 0; i < s->numProcesses; i++)
1488 s->procs[i].state = STATE_NEEDS_STARTING;
1491 ap_destroy_pool(tp);
1493 #ifdef WIN32
1494 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1495 "FastCGI: process manager initialized");
1496 #else
1497 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1498 "FastCGI: process manager initialized (pid %ld)", (long)getpid());
1499 #endif
1501 now = time(NULL);
1504 * Loop until SIGTERM
1506 for (;;) {
1507 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1508 #ifdef WIN32
1509 time_t expire;
1510 #else
1511 pid_t childPid;
1512 int waitStatus;
1513 #endif
1514 unsigned int numChildren;
1517 * If we came out of sigsuspend() for any reason other than
1518 * SIGALRM, pick up where we left off.
1520 if (alarmLeft)
1521 sleepSeconds = alarmLeft;
1524 * Examine each configured AppClass for a process that needs
1525 * starting. Compute the earliest time when the start should
1526 * be attempted, starting it now if the time has passed. Also,
1527 * remember that we do NOT need to restart externally managed
1528 * FastCGI applications.
1530 for (s = fcgi_servers; s != NULL; s = s->next) {
1531 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1532 continue;
1534 if (s->directive == APP_CLASS_DYNAMIC) {
1535 numChildren = dynamicMaxClassProcs;
1536 } else {
1537 numChildren = s->numProcesses;
1540 for (i = 0; i < numChildren; i++) {
1541 if ((s->procs[i].pid <= 0) &&
1542 (s->procs[i].state == STATE_NEEDS_STARTING))
1544 time_t restartTime;
1546 if (s->procs[i].pid == 0) {
1547 restartTime = s->restartTime + s->initStartDelay;
1548 } else {
1549 restartTime = s->restartTime + s->restartDelay;
1552 if (restartTime <= now) {
1553 int restart = (s->procs[i].pid < 0);
1555 s->restartTime = now;
1557 #ifndef WIN32
1558 if (caughtSigTerm) {
1559 goto ProcessSigTerm;
1561 #endif
1562 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1563 if (s->procs[i].pid <= 0) {
1564 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1565 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1566 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1567 s->fs_path);
1569 sleepSeconds = min(sleepSeconds,
1570 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1572 ap_assert(s->procs[i].pid < 0);
1573 break;
1576 if (s->directive == APP_CLASS_DYNAMIC) {
1577 s->numProcesses++;
1578 fcgi_dynamic_total_proc_count++;
1579 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1580 #ifdef WIN32
1581 if (i == 0 && !restart) {
1582 fcgi_rdwr_unlock(s->dynamic_lock, WRITER);
1584 #endif
1587 s->procs[i].state = STATE_STARTED;
1589 if (restart)
1590 s->numRestarts++;
1592 if (fcgi_suexec != NULL) {
1593 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1594 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1595 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1596 s->fs_path, (long)s->uid, (long)s->gid,
1597 restart ? "re" : "", (long)s->procs[i].pid);
1599 else {
1600 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1601 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1602 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1603 s->fs_path, restart ? "re" : "", (long)s->procs[i].pid);
1605 ap_assert(s->procs[i].pid > 0);
1606 } else {
1607 sleepSeconds = min(sleepSeconds, restartTime - now);
1613 #ifndef WIN32
1614 if(caughtSigTerm) {
1615 goto ProcessSigTerm;
1617 if((!caughtSigChld) && (!caughtSigUsr2)) {
1618 fd_set rfds;
1620 alarm(sleepSeconds);
1622 FD_ZERO(&rfds);
1623 FD_SET(fcgi_pm_pipe[0], &rfds);
1624 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1626 alarmLeft = alarm(0);
1628 callWaitPid = caughtSigChld;
1629 caughtSigChld = FALSE;
1630 callDynamicProcs = caughtSigUsr2;
1631 caughtSigUsr2 = FALSE;
1633 now = time(NULL);
1636 * Dynamic fcgi process management
1638 if((callDynamicProcs) || (!callWaitPid)) {
1639 dynamic_read_msgs(read_ready);
1640 if(fcgi_dynamic_epoch == 0) {
1641 fcgi_dynamic_epoch = now;
1643 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1644 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1645 dynamic_kill_idle_fs_procs();
1646 fcgi_dynamic_epoch = now;
1650 if(!callWaitPid) {
1651 continue;
1654 /* We've caught SIGCHLD, so find out who it was using waitpid,
1655 * write a log message and update its data structure. */
1657 for (;;) {
1658 if (caughtSigTerm)
1659 goto ProcessSigTerm;
1661 childPid = waitpid(-1, &waitStatus, WNOHANG);
1663 if (childPid == -1 || childPid == 0)
1664 break;
1666 for (s = fcgi_servers; s != NULL; s = s->next) {
1667 if (s->directive == APP_CLASS_EXTERNAL)
1668 continue;
1670 if (s->directive == APP_CLASS_DYNAMIC)
1671 numChildren = dynamicMaxClassProcs;
1672 else
1673 numChildren = s->numProcesses;
1675 for (i = 0; i < numChildren; i++) {
1676 if (s->procs[i].pid == childPid)
1677 goto ChildFound;
1681 /* @@@ This (comment) needs to go away when dynamic gets cleaned up.
1682 * If we get to this point, we have detected the
1683 * termination of the process that was spawned off by
1684 * the process manager to do a blocking kill above. */
1685 continue;
1687 ChildFound:
1688 s->procs[i].pid = -1;
1690 if (s->directive == APP_CLASS_STANDARD) {
1691 /* Always restart static apps */
1692 s->procs[i].state = STATE_NEEDS_STARTING;
1693 s->numFailures++;
1695 else {
1696 s->numProcesses--;
1697 fcgi_dynamic_total_proc_count--;
1699 if (s->procs[i].state == STATE_VICTIM) {
1700 s->procs[i].state = STATE_KILLED;
1702 else {
1703 /* A dynamic app died or exited without provacation from the PM */
1704 s->numFailures++;
1706 if (dynamicAutoRestart || s->numProcesses <= 0)
1707 s->procs[i].state = STATE_NEEDS_STARTING;
1708 else
1709 s->procs[i].state = STATE_READY;
1713 if (WIFEXITED(waitStatus)) {
1714 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1715 "FastCGI:%s server \"%s\" (pid %d) terminated by calling exit with status '%d'",
1716 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1717 s->fs_path, (int)childPid, WEXITSTATUS(waitStatus));
1719 else if (WIFSIGNALED(waitStatus)) {
1720 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1721 "FastCGI:%s server \"%s\" (pid %d) terminated due to uncaught signal '%d' (%s)%s",
1722 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1723 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)],
1724 #ifdef WCOREDUMP
1725 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1726 #else
1727 "");
1728 #endif
1730 else if (WIFSTOPPED(waitStatus)) {
1731 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1732 "FastCGI:%s server \"%s\" (pid %d) stopped due to uncaught signal '%d' (%s)",
1733 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1734 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)]);
1736 } /* for (;;), waitpid() */
1737 #else
1738 if (first_time) {
1739 /* Start the child wait thread */
1740 wait_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)child_wait_thread, NULL, 0, NULL);
1741 first_time = 0;
1744 /* wait for an event to occur or timer expires */
1745 expire = time(NULL) + sleepSeconds;
1746 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1748 if (dwRet == WAIT_FAILED) {
1749 /* There is something seriously wrong here */
1750 ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, fcgi_apache_main_server,
1751 "FastDGI: WaitForMultipleObjects on event handles -- pm is shuting down");
1752 bTimeToDie = TRUE;
1755 if (dwRet != WAIT_TIMEOUT) {
1756 now = time(NULL);
1758 if (now < expire)
1759 alarmLeft = expire - now;
1763 * Dynamic fcgi process management
1765 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1766 if (dwRet == MBOX_EVENT) {
1767 read_ready = 1;
1770 now = time(NULL);
1772 dynamic_read_msgs(read_ready);
1774 if(fcgi_dynamic_epoch == 0) {
1775 fcgi_dynamic_epoch = now;
1778 if (((long)(now-fcgi_dynamic_epoch) >= (int)dynamicKillInterval) ||
1779 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1780 dynamic_kill_idle_fs_procs();
1781 fcgi_dynamic_epoch = now;
1783 read_ready = 0;
1785 else if (dwRet == WAKE_EVENT) {
1786 continue;
1788 else if (dwRet == TERM_EVENT) {
1789 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1790 "FastCGI: Termination event received process manager shutting down");
1791 bTimeToDie = TRUE;
1793 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1794 goto ProcessSigTerm;
1796 else {
1797 // Have an received an unknown event - should not happen
1798 ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, fcgi_apache_main_server,
1799 "FastCGI: WaitForMultipleobjects return an unrecognized event");
1800 bTimeToDie = TRUE;
1801 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1802 goto ProcessSigTerm;
1804 #endif
1805 } /* for (;;), the whole shoot'n match */
1807 ProcessSigTerm:
1809 * Kill off the children, then exit.
1811 while (fcgi_servers != NULL) {
1812 kill_fs_procs(fcgi_config_pool, fcgi_servers);
1815 #ifdef WIN32
1816 return;
1817 #else
1818 exit(0);
1819 #endif
1822 #ifdef WIN32
1823 int fcgi_pm_add_job(fcgi_pm_job *new_job) {
1825 if (new_job == NULL)
1826 return 0;
1828 if (ap_acquire_mutex(fcgi_dynamic_mbox_mutex) != MULTI_OK) {
1829 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1830 "FastCGI: Failed to aquire the dynamic mbox mutex!");
1833 new_job->next = fcgi_dynamic_mbox;
1834 fcgi_dynamic_mbox = new_job;
1836 if (! ap_release_mutex(fcgi_dynamic_mbox_mutex)) {
1837 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1838 "FastCGI: Failed to release the dynamic mbox mutex!");
1841 return 1;
1843 #endif