*** empty log message ***
[mod_fastcgi.git] / fcgi_pm.c
blob14a6e579c3fe2deb7d9f747b99743d5bd29a0c5e
1 /*
2 * $Id: fcgi_pm.c,v 1.36 2000/08/03 16:47:32 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 "bind() failed";
168 #ifndef WIN32
169 /* Twiddle permissions */
170 if (socket_addr->sa_family == AF_UNIX) {
171 if (chmod(((struct sockaddr_un *)socket_addr)->sun_path, S_IRUSR | S_IWUSR))
172 return "chmod() of socket failed";
174 #endif
176 /* Set to listen */
177 if (listen(sock, backlog) != 0)
178 return "listen() failed";
180 return NULL;
184 *----------------------------------------------------------------------
186 * dynamic_blocking_kill
188 * Block on the lock file until it is available, and then
189 * issue a kill signal to the corresponding application.
190 * Since this function is executed in the child process,
191 * _exit() is called upon completion.
193 * Inputs
194 * Pointer to the data structure containing a process id to
195 * issue a signal to and the full pathname to the lockfile
196 * that needs to be locked before the issue of the signal.
198 * Notes
199 * Memory is allocated by the caller, but is freed by this
200 * function.
202 *----------------------------------------------------------------------
204 static void dynamic_blocking_kill(void *data)
206 struct FuncData *funcData = (struct FuncData *)data;
208 #ifndef WIN32
209 int lockFd;
211 ap_assert(funcData->lockFileName);
212 if ((lockFd = open(funcData->lockFileName, O_RDWR)) < 0) {
213 /* There is something terribly wrong here */
214 } else {
215 if (fcgi_wait_for_shared_write_lock(lockFd) < 0) {
216 /* This is a major problem */
217 } else {
218 fcgi_kill(funcData->process, SIGTERM);
221 /* exit() may flush stdio buffers inherited from the parent. */
222 _exit(0);
224 #else
225 FCGIDBG1("dynamic_blocking_kill()");
226 if (fcgi_wait_for_shared_write_lock(funcData->lock) < 0) {
227 // This is a major problem
228 FCGIDBG1("fcgi_wait_for_shared_write_lock() failed >> MAJOR PROBLEM");
230 else {
231 fcgi_kill(funcData->process, 1);
232 fcgi_rdwr_unlock(funcData->lock, WRITER);
234 free(data);
235 return;
236 #endif
240 *----------------------------------------------------------------------
242 * pm_main
244 * The FastCGI process manager, which runs as a separate
245 * process responsible for:
246 * - Starting all the FastCGI proceses.
247 * - Restarting any of these processes that die (indicated
248 * by SIGCHLD).
249 * - Catching SIGTERM and relaying it to all the FastCGI
250 * processes before exiting.
252 * Inputs:
253 * Uses global variable fcgi_servers.
255 * Results:
256 * Does not return.
258 * Side effects:
259 * Described above.
261 *----------------------------------------------------------------------
263 #ifndef WIN32
264 static int caughtSigTerm = FALSE;
265 static int caughtSigChld = FALSE;
266 static int caughtSigUsr2 = FALSE;
268 static void signal_handler(int signo)
270 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
271 /* SIGUSR1 & SIGHUP are sent by apache to its process group
272 * when apache get 'em. Apache follows up (1.2.x) with attacks
273 * on each of its child processes, but we've got the KillMgr
274 * sitting between us so we never see the KILL. The main loop
275 * in ProcMgr also checks to see if the KillMgr has terminated,
276 * and if it has, we handl it as if we should shutdown too. */
277 caughtSigTerm = TRUE;
278 } else if(signo == SIGCHLD) {
279 caughtSigChld = TRUE;
280 } else if(signo == SIGALRM) {
281 caughtSigUsr2 = TRUE;
284 #endif
287 *----------------------------------------------------------------------
289 * spawn_fs_process --
291 * Fork and exec the specified fcgi process.
293 * Results:
294 * 0 for successful fork, -1 for failed fork.
296 * In case the child fails before or in the exec, the child
297 * obtains the error log by calling getErrLog, logs
298 * the error, and exits with exit status = errno of
299 * the failed system call.
301 * Side effects:
302 * Child process created.
304 *----------------------------------------------------------------------
307 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
309 #ifndef WIN32
310 pid_t child_pid;
311 int i;
312 char *dirName;
313 char *dnEnd, *failedSysCall;
315 child_pid = fork();
316 if (child_pid) {
317 return child_pid;
320 /* We're the child. We're gonna exec() so pools don't matter. */
322 dnEnd = strrchr(fs->fs_path, '/');
323 if (dnEnd == NULL) {
324 dirName = "./";
325 } else {
326 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
327 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
329 if (chdir(dirName) < 0) {
330 failedSysCall = "chdir()";
331 goto FailedSystemCallExit;
334 #ifndef __EMX__
335 /* OS/2 dosen't support nice() */
336 if (fs->processPriority != 0) {
337 if (nice(fs->processPriority) == -1) {
338 failedSysCall = "nice()";
339 goto FailedSystemCallExit;
342 #endif
344 /* Open the listenFd on spec'd fd */
345 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
346 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
348 /* Close all other open fds, except stdout/stderr. Leave these two open so
349 * FastCGI applications don't have to find and fix ALL 3rd party libs that
350 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
351 * main server error_log - @@@ provide a directive control where this goes.
353 ap_error_log2stderr(fcgi_apache_main_server);
354 dup2(STDERR_FILENO, STDOUT_FILENO);
355 for (i = 0; i < FCGI_MAX_FD; i++) {
356 if (i != FCGI_LISTENSOCK_FILENO && i != STDERR_FILENO && i != STDOUT_FILENO) {
357 close(i);
361 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
362 * install its own handler. */
363 signal(SIGPIPE, SIG_IGN);
365 if (fcgi_suexec != NULL) {
366 char *shortName = strrchr(fs->fs_path, '/') + 1;
368 /* Relinquish our root real uid powers */
369 seteuid_root();
370 setuid(ap_user_id);
372 do {
373 execle(fcgi_suexec, fcgi_suexec, fs->username, fs->group, shortName, NULL, fs->envp);
374 } while (errno == EINTR);
376 else {
377 do {
378 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
379 } while (errno == EINTR);
382 failedSysCall = "execle()";
384 FailedSystemCallExit:
385 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
386 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
387 exit(-1);
389 /* avoid an irrelevant compiler warning */
390 return(0);
392 #else
394 /* Adapted from Apache's util_script.c ap_call_exec() */
395 char *interpreter = NULL;
396 char *ext = NULL;
397 char *exename = NULL;
398 char *s = NULL;
399 char *quoted_filename;
400 char *pCommand;
401 char *pEnvBlock, *pNext;
403 int i;
404 int iEnvBlockLen;
406 file_type_e fileType;
408 STARTUPINFO si;
409 PROCESS_INFORMATION pi;
411 request_rec r;
412 pid_t pid = -1;
414 HANDLE listen_handle, mutex;
415 char * mutex_string = NULL;
417 pool * tp = ap_make_sub_pool(fcgi_config_pool);
419 if (fs->socket_path)
421 SECURITY_ATTRIBUTES sa;
423 sa.nLength = sizeof(sa);
424 sa.lpSecurityDescriptor = NULL;
425 sa.bInheritHandle = TRUE;
427 listen_handle = CreateNamedPipe(fs->socket_path, PIPE_ACCESS_DUPLEX,
428 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
429 PIPE_UNLIMITED_INSTANCES, 4096,4096,0, &sa);
430 if (listen_handle == INVALID_HANDLE_VALUE)
432 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
433 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
434 exit(0);
437 // This mutex is not really necessary, but for compatibility with
438 // the existing library, we need it.
440 mutex = ap_create_mutex(NULL);
441 if (mutex == NULL)
443 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
444 "FastCGI: can't exec server \"%s\", ap_create_mutex() failed", fs->fs_path);
445 CloseHandle(listen_handle);
446 exit(0);
449 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
451 mutex_string = ap_psprintf(tp, "_FCGI_MUTEX_=%ld", mutex);
453 else
455 listen_handle = (HANDLE) fs->listenFd;
458 memset(&si, 0, sizeof(si));
459 memset(&pi, 0, sizeof(pi));
460 memset(&r, 0, sizeof(r));
462 // Can up a fake request to pass to ap_get_win32_interpreter()
463 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
464 r.server = fcgi_apache_main_server;
465 r.filename = (char *) fs->fs_path;
466 r.pool = tp;
468 fileType = ap_get_win32_interpreter(&r, &interpreter);
470 if (fileType == eFileTypeUNKNOWN) {
471 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, fcgi_apache_main_server,
472 "FastCGI: %s is not executable; ensure interpreted scripts have "
473 "\"#!\" as their first line",
474 fs->fs_path);
475 ap_destroy_pool(tp);
476 return (pid);
480 * We have the interpreter (if there is one) and we have
481 * the arguments (if there are any).
482 * Build the command string to pass to CreateProcess.
484 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
485 if (interpreter && *interpreter) {
486 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
488 else {
489 pCommand = quoted_filename;
493 * Make child process use hPipeOutputWrite as standard out,
494 * and make sure it does not show on screen.
496 si.cb = sizeof(si);
497 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
498 si.wShowWindow = SW_HIDE;
499 si.hStdInput = listen_handle;
501 // XXX These should be open to the error_log
502 si.hStdOutput = INVALID_HANDLE_VALUE;
503 si.hStdError = INVALID_HANDLE_VALUE;
506 * Win32's CreateProcess call requires that the environment
507 * be passed in an environment block, a null terminated block of
508 * null terminated strings.
510 i = 0;
511 iEnvBlockLen = 1;
512 while (fs->envp[i]) {
513 iEnvBlockLen += strlen(fs->envp[i]) + 1;
514 i++;
517 if (fs->socket_path)
519 iEnvBlockLen += strlen(mutex_string) + 1;
522 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
524 i = 0;
525 pNext = pEnvBlock;
526 while (fs->envp[i]) {
527 strcpy(pNext, fs->envp[i]);
528 pNext = pNext + strlen(pNext) + 1;
529 i++;
532 if (fs->socket_path)
534 strcpy(pNext, mutex_string);
537 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
539 pEnvBlock,
540 ap_make_dirstr_parent(tp, fs->fs_path),
541 &si, &pi)) {
542 if (fileType == eFileTypeEXE16) {
543 /* Hack to get 16-bit CGI's working. It works for all the
544 * standard modules shipped with Apache. pi.dwProcessId is 0
545 * for 16-bit CGIs and all the Unix specific code that calls
546 * ap_call_exec interprets this as a failure case. And we can't
547 * use -1 either because it is mapped to 0 by the caller.
549 pid = -2;
551 else {
552 pid = pi.dwProcessId;
553 process->handle = pi.hProcess;
554 CloseHandle(pi.hThread);
558 // We don't need these anymore..
559 if (fs->socket_path)
561 CloseHandle(listen_handle);
562 CloseHandle(mutex);
565 ap_destroy_pool(tp);
567 return pid;
569 #endif
572 #ifndef WIN32
573 static void reduce_priveleges(void)
575 char *name;
577 if (geteuid() != 0)
578 return;
580 #ifndef __EMX__
581 /* Get username if passed as a uid */
582 if (ap_user_name[0] == '#') {
583 uid_t uid = atoi(&ap_user_name[1]);
584 struct passwd *ent = getpwuid(uid);
586 if (ent == NULL) {
587 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
588 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
589 "you probably need to modify the User directive", (unsigned)uid);
590 exit(1);
592 name = ent->pw_name;
594 else
595 name = ap_user_name;
597 /* Change Group */
598 if (setgid(ap_group_id) == -1) {
599 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
600 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
601 exit(1);
604 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
606 /* Initialize supplementary groups */
607 if (initgroups(name, ap_group_id) == -1) {
608 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
609 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
610 name, (unsigned)ap_group_id);
611 exit(1);
613 #endif /* __EMX__ */
615 /* Change User */
616 if (fcgi_suexec) {
617 if (seteuid_user() == -1) {
618 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
619 "FastCGI: process manager exiting, failed to reduce priveleges");
620 exit(1);
623 else {
624 if (setuid(ap_user_id) == -1) {
625 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
626 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
627 exit(1);
632 /*************
633 * Change the name of this process - best we can easily.
635 static void change_process_name(const char * const name)
637 strncpy(ap_server_argv0, name, strlen(ap_server_argv0));
639 #endif
641 static void schedule_start(fcgi_server *s, int proc)
643 /* If we've started one recently, don't register another */
644 time_t time_passed = now - s->restartTime;
646 if ((s->procs[proc].pid && time_passed < (int) s->restartDelay)
647 || (s->procs[proc].pid == 0 && time_passed < (int) s->initStartDelay))
649 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);
650 return;
653 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
654 s->procs[proc].state = STATE_NEEDS_STARTING;
655 if (proc == (int)dynamicMaxClassProcs - 1) {
656 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
657 "FastCGI: scheduled the %sstart of the last (dynamic) server "
658 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
659 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
664 *----------------------------------------------------------------------
666 * dynamic_read_msgs
668 * Removes the records written by request handlers and decodes them.
669 * We also update the data structures to reflect the changes.
671 *----------------------------------------------------------------------
674 static void dynamic_read_msgs(int read_ready)
676 fcgi_server *s;
678 #ifndef WIN32
679 int rc;
680 static int buflen = 0;
681 static char buf[FCGI_MSGS_BUFSIZE + 1];
682 char *ptr1, *ptr2, opcode;
683 char execName[FCGI_MAXPATH + 1];
684 char user[MAX_USER_NAME_LEN + 2];
685 char group[MAX_GID_CHAR_LEN + 1];
686 unsigned long q_usec = 0UL, req_usec = 0UL;
687 #else
688 fcgi_pm_job *joblist = NULL;
689 fcgi_pm_job *cjob = NULL;
690 #endif
692 pool *sp, *tp;
694 #ifndef WIN32
695 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
696 #endif
699 * To prevent the idle application from running indefinitely, we
700 * check the timer and if it is expired, we recompute the values
701 * for each running application class. Then, when REQ_COMPLETE
702 * message is recieved, only updates are made to the data structures.
704 if (fcgi_dynamic_last_analyzed == 0) {
705 fcgi_dynamic_last_analyzed = now;
707 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
708 for (s = fcgi_servers; s != NULL; s = s->next) {
709 if (s->directive != APP_CLASS_DYNAMIC)
710 break;
711 /* XXX what does this adjustment do? */
712 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
713 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
714 s->totalConnTime = 0UL;
715 s->totalQueueTime = 0UL;
719 if (read_ready <= 0) {
720 return;
723 #ifndef WIN32
724 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
725 if (rc <= 0) {
726 if (!caughtSigTerm) {
727 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
728 "FastCGI: read() from pipe failed (%d)", rc);
729 if (rc == 0) {
730 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
731 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
732 caughtSigTerm = TRUE;
735 return;
737 buflen += rc;
738 buf[buflen] = '\0';
739 #else
740 if (ap_acquire_mutex(fcgi_dynamic_mbox_mutex) != MULTI_OK) {
741 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
742 "FastCGI: Failed to aquire the dynamic mbox mutex!");
745 joblist = fcgi_dynamic_mbox;
746 fcgi_dynamic_mbox = NULL;
748 if (! ap_release_mutex(fcgi_dynamic_mbox_mutex)) {
749 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
750 "FastCGI: Failed to release the dynamic mbox mutex!");
753 cjob = joblist;
754 #endif
756 tp = ap_make_sub_pool(fcgi_config_pool);
758 #ifndef WIN32
759 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
760 int scan_failed = 0;
762 ptr2 = strchr(ptr1, '*');
763 if (ptr2) {
764 *ptr2++ = '\0';
766 else {
767 break;
770 opcode = *ptr1;
772 switch (opcode) {
773 case PLEASE_START:
774 if (sscanf(ptr1, "%c %s %16s %15s",
775 &opcode, execName, user, group) != 4)
777 scan_failed = 1;
779 break;
780 case CONN_TIMEOUT:
781 if (sscanf(ptr1, "%c %s %16s %15s",
782 &opcode, execName, user, group) != 4)
784 scan_failed = 1;
786 break;
787 case REQ_COMPLETE:
788 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
789 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
791 scan_failed = 1;
793 break;
794 default:
795 scan_failed = 1;
796 break;
799 if (scan_failed) {
800 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
801 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
802 goto NextJob;
804 #else
805 /* Update data structures for processing */
806 while (cjob != NULL) {
807 joblist = cjob->next;
808 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
809 #endif
811 #ifndef WIN32
812 s = fcgi_util_fs_get(execName, user, group);
813 #else
814 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
815 #endif
817 #ifndef WIN32
818 if (s==NULL && opcode != REQ_COMPLETE)
819 #else
820 if (s==NULL && cjob->id != REQ_COMPLETE)
821 #endif
823 #ifndef WIN32
824 int fd;
825 const char *err, *lockPath;
826 #endif
828 /* Create a perm subpool to hold the new server data,
829 * we can destroy it if something doesn't pan out */
830 sp = ap_make_sub_pool(fcgi_config_pool);
832 /* Create a new "dynamic" server */
833 s = fcgi_util_fs_new(sp);
834 s->directive = APP_CLASS_DYNAMIC;
835 s->restartDelay = dynamicRestartDelay;
836 s->listenQueueDepth = dynamicListenQueueDepth;
837 s->initStartDelay = dynamicInitStartDelay;
838 s->envp = dynamicEnvp;
839 #ifdef WIN32
840 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
841 #else
842 s->fs_path = ap_pstrdup(sp, execName);
843 #endif
844 ap_getparents(s->fs_path);
845 ap_no2slash(s->fs_path);
846 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
848 #ifndef WIN32
849 /* Create socket file's path */
850 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
851 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
853 /* Create sockaddr, prealloc it so it won't get created in tp */
854 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
855 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
856 &s->socket_addr_len, s->socket_path);
857 if (err) {
858 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
859 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
860 goto BagNewServer;
863 /* Create the socket */
864 if ((s->listenFd = ap_psocket(sp, s->socket_addr->sa_family, SOCK_STREAM, 0)) < 0) {
865 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
866 "FastCGI: can't create (dynamic) server \"%s\": socket() failed", execName);
867 goto BagNewServer;
870 /* bind() and listen() */
871 err = bind_n_listen(tp, s->socket_addr, s->socket_addr_len,
872 s->listenQueueDepth, s->listenFd);
873 if (err) {
874 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
875 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
876 goto BagNewServer;
879 /* Create the lock file */
880 lockPath = fcgi_util_socket_get_lock_filename(tp, s->socket_path);
881 fd = ap_popenf(tp, lockPath,
882 O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
883 if (fd < 0) {
884 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
885 "FastCGI: can't create (dynamic) server \"%s\": can't open lock file \"%s\": popenf() failed",
886 execName, lockPath);
887 goto BagNewServer;
889 ap_pclosef(tp, fd);
891 /* If suexec is being used, config user/group info */
892 if (fcgi_suexec) {
893 if (user[0] == '~') {
894 /* its a user dir uri, the rest is a username, not a uid */
895 struct passwd *pw = getpwnam(&user[1]);
897 if (!pw) {
898 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
899 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for suexec: getpwnam(%s) failed",
900 execName, &user[1]);
901 goto BagNewServer;
903 s->uid = pw->pw_uid;
904 s->user = ap_pstrdup(sp, user);
905 s->username = s->user;
907 s->gid = pw->pw_gid;
908 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
910 else {
911 struct passwd *pw;
913 s->uid = (uid_t)atol(user);
914 pw = getpwuid(s->uid);
915 if (!pw) {
916 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
917 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for suexec: getwpuid(%ld) failed",
918 execName, (long)s->uid);
919 goto BagNewServer;
921 s->user = ap_pstrdup(sp, user);
922 s->username = ap_pstrdup(sp, pw->pw_name);
924 s->gid = (gid_t)atol(group);
925 s->group = ap_pstrdup(sp, group);
928 #else
929 /* Create socket file's path */
930 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
931 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
932 s->listenFd = 0;
934 /* Create the application lock */
935 if ((s->dynamic_lock = fcgi_rdwr_create()) == NULL) {
936 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
937 "FastCGI: can;t create (dynamic) server \"%s\": fcgi_rdwr_create() failed",
938 cjob->fs_path);
939 goto BagNewServer;
942 // Lock it until the first process is started
943 fcgi_rdwr_lock(s->dynamic_lock, WRITER);
944 #endif
946 fcgi_util_fs_add(s);
948 else {
949 #ifndef WIN32
950 if (opcode == PLEASE_START) {
951 #else
952 if(cjob->id==PLEASE_START) {
953 #endif
954 if (dynamicAutoUpdate) {
955 /* Check to see if the binary has changed. If so,
956 * kill the FCGI application processes, and
957 * restart them.
959 struct stat stbuf;
960 unsigned int i;
962 #ifndef WIN32
963 if ((stat(execName, &stbuf)==0) &&
964 #else
965 if ((stat(cjob->fs_path, &stbuf)==0) &&
966 #endif
967 (stbuf.st_mtime > s->restartTime)) {
968 /* kill old server(s) */
969 for (i = 0; i < dynamicMaxClassProcs; i++) {
970 if (s->procs[i].pid > 0) {
971 fcgi_kill(&s->procs[i], SIGTERM);
975 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
976 "FastCGI: restarting server \"%s\" processes, newer version found",
977 #ifndef WIN32
978 execName);
979 #else
980 cjob->fs_path);
981 #endif
984 /* If dynamicAutoRestart, don't mark any new processes
985 * for starting because we probably got the
986 * PLEASE_START due to dynamicAutoUpdate and the ProcMgr
987 * will be restarting all of those we just killed.
989 if (dynamicAutoRestart)
990 goto NextJob;
991 } else {
992 /* we've been asked to start a process--only start
993 * it if we're not already running at least one
994 * instance.
996 unsigned int i;
998 for (i = 0; i < dynamicMaxClassProcs; i++) {
999 if (s->procs[i].state == STATE_STARTED)
1000 break;
1002 /* if already running, don't start another one */
1003 if (i < dynamicMaxClassProcs) {
1004 goto NextJob;
1009 #ifndef WIN32
1010 switch (opcode)
1011 #else
1012 switch (cjob->id)
1013 #endif
1015 unsigned int i;
1017 case PLEASE_START:
1018 case CONN_TIMEOUT:
1020 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1022 * Extra instances should have been
1023 * terminated beforehand, probably need
1024 * to increase ProcessSlack parameter
1026 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1027 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1028 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1029 goto NextJob;
1032 /* find next free slot */
1033 for (i = 0; i < dynamicMaxClassProcs; i++)
1035 if (s->procs[i].state == STATE_NEEDS_STARTING)
1037 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1038 break;
1040 else if (s->procs[i].state == STATE_STARTED)
1042 continue;
1045 schedule_start(s, i);
1046 break;
1049 #ifdef FCGI_DEBUG
1050 if (i >= dynamicMaxClassProcs) {
1051 FCGIDBG1("ignore_job: slots are max'd");
1053 #endif
1054 break;
1055 case REQ_COMPLETE:
1056 /* only record stats if we have a structure */
1057 if (s) {
1058 #ifndef WIN32
1059 s->totalConnTime += req_usec;
1060 s->totalQueueTime += q_usec;
1061 #else
1062 s->totalConnTime += cjob->start_time;
1063 s->totalQueueTime += cjob->qsec;
1064 #endif
1066 break;
1069 NextJob:
1071 #ifdef WIN32
1072 /* Cleanup job data */
1073 free(cjob->fs_path);
1074 free(cjob->user);
1075 free(cjob->group);
1076 free(cjob);
1077 cjob = joblist;
1078 #endif
1080 continue;
1082 BagNewServer:
1083 ap_destroy_pool(sp);
1085 #ifdef WIN32
1086 free(cjob->fs_path);
1087 free(cjob);
1088 cjob = joblist;
1089 #endif
1092 #ifndef WIN32
1093 if (ptr1 == buf) {
1094 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1095 "FastCGI: really bogus message: \"%s\"", ptr1);
1096 ptr1 += strlen(buf);
1099 buflen -= ptr1 - buf;
1100 if (buflen) {
1101 memmove(buf, ptr1, buflen);
1103 #endif
1105 ap_destroy_pool(tp);
1109 *----------------------------------------------------------------------
1111 * dynamic_kill_idle_fs_procs
1113 * Implement a kill policy for the dynamic FastCGI applications.
1114 * We also update the data structures to reflect the changes.
1116 * Side effects:
1117 * Processes are marked for deletion possibly killed.
1119 *----------------------------------------------------------------------
1121 static void dynamic_kill_idle_fs_procs(void)
1123 fcgi_server *s;
1124 struct FuncData *funcData = NULL;
1125 unsigned long connTime; /* server's smoothed running time, or
1126 * if that's 0, the current total */
1127 unsigned long totalTime; /* maximum number of microseconds that all
1128 * of a server's running processes together
1129 * could have spent running since the
1130 * last check */
1131 double loadFactor; /* percentage, 0-100, of totalTime that
1132 * the processes actually used */
1133 unsigned int i, victims = 0;
1134 #ifndef WIN32
1135 const char *lockFileName;
1136 int lockFd;
1137 pid_t pid;
1138 #endif
1139 pool *tp = ap_make_sub_pool(fcgi_config_pool);
1141 /* pass 1 - locate and mark all victims */
1142 for(s=fcgi_servers; s!=NULL; s=s->next) {
1143 /* Only kill dynamic apps */
1144 if (s->directive != APP_CLASS_DYNAMIC)
1145 continue;
1147 /* If the number of non-victims is less than or equal to
1148 the minimum that may be running without being killed off,
1149 don't select any more victims. */
1150 if ((fcgi_dynamic_total_proc_count - victims) <= (int) dynamicMinProcs) {
1151 break;
1154 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1155 totalTime = (s->numProcesses)*(now - fcgi_dynamic_epoch)*1000000 + 1;
1157 /* XXX producing a heavy load with one client, I haven't been
1158 able to achieve a loadFactor greater than 0.5. Perhaps this
1159 should be scaled up by another order of magnitude or two. */
1160 loadFactor = connTime/totalTime*100.0;
1162 if ((s->numProcesses > 1
1163 && s->numProcesses/(s->numProcesses - 1)*loadFactor < dynamicThreshholdN)
1164 || (s->numProcesses == 1 && loadFactor < dynamicThreshhold1))
1166 int got_one = 0;
1168 for (i = 0; !got_one && i < dynamicMaxClassProcs; ++i) {
1169 if (s->procs[i].state == STATE_NEEDS_STARTING) {
1170 s->procs[i].state = STATE_READY;
1171 got_one = 1;
1173 else if (s->procs[i].state == STATE_VICTIM || s->procs[i].state == STATE_KILL) {
1174 got_one = 1;
1178 for (i = 0; !got_one && i < dynamicMaxClassProcs; ++i) {
1179 if (s->procs[i].state == STATE_STARTED) {
1180 s->procs[i].state = STATE_KILL;
1181 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1182 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination scheduled",
1183 s->fs_path, s->procs[i].pid);
1184 victims++;
1185 got_one = 1;
1191 /* pass 2 - kill procs off */
1192 for(s=fcgi_servers; s!=NULL; s=s->next) {
1193 /* Only kill dynamic apps */
1194 if (s->directive != APP_CLASS_DYNAMIC)
1195 continue;
1197 for(i = 0; i < dynamicMaxClassProcs; i++) {
1198 if (s->procs[i].state == STATE_KILL) {
1199 #ifndef WIN32
1200 lockFileName = fcgi_util_socket_get_lock_filename(tp, s->socket_path);
1201 if ((lockFd = ap_popenf(tp, lockFileName, O_RDWR, 0))<0) {
1203 * If we need to kill an application and the
1204 * corresponding lock file does not exist, then
1205 * that means we are in big trouble here
1207 /*@@@ this should be logged, but since all the lock
1208 * file stuff will be tossed, I'll leave it now */
1209 ap_pclosef(tp, lockFd);
1210 continue;
1213 if (fcgi_get_exclusive_write_lock_no_wait(lockFd) < 0) {
1214 #else
1215 if (fcgi_get_exclusive_write_lock_no_wait(s->dynamic_lock) < 0) {
1216 #endif
1217 FCGIDBG2("fcgi_get_exclusive_write_lock_no_wait() failed (%ld)", GetLastError());
1219 * Unable to lock the lockfile, indicative
1220 * of WS performing operation with the given
1221 * application class. The simplest solution
1222 * is to spawn off another process and block
1223 * on lock to kill it. This is under assumptions
1224 * that fork() is not very costly and this
1225 * situation occurs very rarely, which it should
1227 #ifndef WIN32
1228 funcData = ap_pcalloc(tp, sizeof(struct FuncData));
1229 funcData->lockFileName = lockFileName;
1230 #else
1231 funcData = malloc(sizeof(struct FuncData));
1232 funcData->lock = s->dynamic_lock;
1233 #endif
1234 funcData->process = &s->procs[i];
1236 #ifndef WIN32
1237 if((pid=fork())<0) {
1238 /*@@@ this should be logged, but since all the lock
1239 * file stuff will be tossed, I'll leave it now */
1240 ap_pclosef(tp, lockFd);
1241 continue;
1242 } else if(pid==0) {
1243 /* child */
1245 /* rename the process for ps - best we can easily */
1246 change_process_name("fcgiBlkKill");
1248 dynamic_blocking_kill(funcData);
1249 } else {
1250 /* parent */
1251 s->procs[i].state = STATE_VICTIM;
1252 ap_pclosef(tp, lockFd);
1254 #else
1255 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) dynamic_blocking_kill, (LPVOID) funcData, 0, NULL);
1256 s->procs[i].state = STATE_VICTIM;
1257 #endif
1259 else {
1260 FCGIDBG1("fcgi_get_exclusive_write_lock_no_wait() succeeded");
1261 s->procs[i].state = STATE_VICTIM;
1262 #ifndef WIN32
1263 fcgi_kill(&s->procs[i], SIGTERM);
1264 ap_pclosef(tp, lockFd);
1265 #else
1266 fcgi_kill(&s->procs[i], 1);
1267 fcgi_rdwr_unlock(s->dynamic_lock, WRITER);
1268 #endif
1273 ap_destroy_pool(tp);
1276 #ifdef WIN32
1278 // This is a little bogus, there's gotta be a better way to do this
1279 // Can we use WaitForMultipleObjects()
1280 #define FCGI_PROC_WAIT_TIME 100
1282 void child_wait_thread(void *dummy) {
1283 fcgi_server *s;
1284 DWORD dwRet = WAIT_TIMEOUT;
1285 int numChildren;
1286 int i;
1287 int waited;
1289 while (!bTimeToDie) {
1290 waited = 0;
1292 for (s = fcgi_servers; s != NULL; s = s->next) {
1293 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1294 continue;
1296 if (s->directive == APP_CLASS_DYNAMIC) {
1297 numChildren = dynamicMaxClassProcs;
1299 else {
1300 numChildren = s->numProcesses;
1303 for (i=0; i < numChildren; i++) {
1304 if (s->procs[i].handle != INVALID_HANDLE_VALUE) {
1305 /* timeout is currently set for 100 miliecond */
1306 /* it may need t longer or user customizable */
1307 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1309 waited = 1;
1311 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1312 /* a child fs has died */
1313 /* mark the child as dead */
1315 if (s->directive == APP_CLASS_STANDARD) {
1316 /* restart static app */
1317 s->procs[i].state = STATE_NEEDS_STARTING;
1318 s->numFailures++;
1320 else {
1321 s->numProcesses--;
1322 fcgi_dynamic_total_proc_count--;
1323 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1325 if (s->procs[i].state == STATE_VICTIM) {
1326 s->procs[i].state = STATE_KILLED;
1328 else {
1329 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1330 s->numFailures++;
1332 if (dynamicAutoRestart) {
1333 s->procs[i].state = STATE_NEEDS_STARTING;
1335 else {
1336 s->procs[i].state = STATE_READY;
1341 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1342 "FastCGI:%s server \"%s\" (pid %d) terminated",
1343 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1344 s->fs_path, s->procs[i].pid);
1346 CloseHandle(s->procs[i].handle);
1347 s->procs[i].handle = INVALID_HANDLE_VALUE;
1348 s->procs[i].pid = -1;
1350 /* wake up the main thread */
1351 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1356 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1359 #endif
1361 #ifndef WIN32
1362 static void setup_signals(void)
1364 sigset_t mask;
1365 struct sigaction sa;
1367 /* Ignore USR2 */
1368 sigemptyset(&mask);
1369 sigaddset(&mask, SIGUSR2);
1370 sigprocmask(SIG_BLOCK, &mask, NULL);
1372 /* Setup handlers */
1374 sa.sa_handler = signal_handler;
1375 sigemptyset(&sa.sa_mask);
1376 sa.sa_flags = 0;
1378 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1379 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1380 "sigaction(SIGTERM) failed");
1382 /* httpd restart */
1383 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1384 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1385 "sigaction(SIGHUP) failed");
1387 /* httpd graceful restart */
1388 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1389 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1390 "sigaction(SIGUSR1) failed");
1392 /* read messages from request handlers - kill interval expired */
1393 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1394 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1395 "sigaction(SIGALRM) failed");
1397 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1398 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1399 "sigaction(SIGCHLD) failed");
1402 #endif
1404 #ifndef WIN32
1405 int fcgi_pm_main(void *dummy, child_info *info)
1406 #else
1407 void fcgi_pm_main(void *dummy)
1408 #endif
1410 fcgi_server *s;
1411 unsigned int i;
1412 int read_ready = 0;
1413 int alarmLeft = 0;
1414 pool *tp;
1415 const char *err;
1417 #ifdef WIN32
1418 DWORD dwRet;
1419 int first_time = 1;
1420 HANDLE wait_thread = INVALID_HANDLE_VALUE;
1421 #else
1422 int callWaitPid, callDynamicProcs;
1423 #endif
1425 #ifdef WIN32
1426 // Add SystemRoot to the dyanmic environment
1427 char ** envp = dynamicEnvp;
1428 for (i = 0; *envp; ++i) {
1429 ++envp;
1431 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1433 #else
1434 reduce_priveleges();
1436 close(fcgi_pm_pipe[1]);
1437 change_process_name("fcgi-pm");
1438 setup_signals();
1440 if (fcgi_suexec) {
1441 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1442 "FastCGI: suEXEC mechanism enabled (wrapper: %s)", fcgi_suexec);
1444 #endif
1446 /* Initialize AppClass */
1447 tp = ap_make_sub_pool(fcgi_config_pool);
1448 for(s = fcgi_servers; s != NULL; s = s->next) {
1449 if (s->directive == APP_CLASS_EXTERNAL)
1450 continue;
1452 #ifdef WIN32
1453 if (s->socket_path)
1455 s->listenFd = 0;
1457 else
1458 #endif
1460 /* Create the socket */
1461 s->listenFd = ap_psocket(fcgi_config_pool, s->socket_addr->sa_family, SOCK_STREAM, 0);
1462 if (s->listenFd < 0) {
1463 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1464 "FastCGI: server \"%s\" disabled, socket() failed", s->fs_path);
1465 continue;
1468 /* bind() and listen() */
1469 err = bind_n_listen(tp, s->socket_addr, s->socket_addr_len,
1470 s->listenQueueDepth, s->listenFd);
1471 if (err) {
1472 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1473 "FastCGI: server \"%s\" disabled: %s",
1474 s->fs_path, err);
1475 ap_pclosesocket(fcgi_config_pool, s->listenFd);
1476 s->listenFd = -1;
1477 continue;
1481 for (i = 0; i < s->numProcesses; i++)
1482 s->procs[i].state = STATE_NEEDS_STARTING;
1485 ap_destroy_pool(tp);
1487 #ifdef WIN32
1488 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1489 "FastCGI: process manager initialized");
1490 #else
1491 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1492 "FastCGI: process manager initialized (pid %ld)", (long)getpid());
1493 #endif
1495 now = time(NULL);
1498 * Loop until SIGTERM
1500 for (;;) {
1501 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1502 #ifdef WIN32
1503 time_t expire;
1504 #else
1505 pid_t childPid;
1506 int waitStatus;
1507 #endif
1508 unsigned int numChildren;
1511 * If we came out of sigsuspend() for any reason other than
1512 * SIGALRM, pick up where we left off.
1514 if (alarmLeft)
1515 sleepSeconds = alarmLeft;
1518 * Examine each configured AppClass for a process that needs
1519 * starting. Compute the earliest time when the start should
1520 * be attempted, starting it now if the time has passed. Also,
1521 * remember that we do NOT need to restart externally managed
1522 * FastCGI applications.
1524 for (s = fcgi_servers; s != NULL; s = s->next) {
1525 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1526 continue;
1528 if (s->directive == APP_CLASS_DYNAMIC) {
1529 numChildren = dynamicMaxClassProcs;
1530 } else {
1531 numChildren = s->numProcesses;
1534 for (i = 0; i < numChildren; i++) {
1535 if ((s->procs[i].pid <= 0) &&
1536 (s->procs[i].state == STATE_NEEDS_STARTING))
1538 time_t restartTime;
1540 if (s->procs[i].pid == 0) {
1541 restartTime = s->restartTime + s->initStartDelay;
1542 } else {
1543 restartTime = s->restartTime + s->restartDelay;
1546 if (restartTime <= now) {
1547 int restart = (s->procs[i].pid < 0);
1549 s->restartTime = now;
1551 #ifndef WIN32
1552 if (caughtSigTerm) {
1553 goto ProcessSigTerm;
1555 #endif
1556 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1557 if (s->procs[i].pid <= 0) {
1558 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1559 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1560 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1561 s->fs_path);
1563 sleepSeconds = min(sleepSeconds,
1564 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1566 ap_assert(s->procs[i].pid < 0);
1567 break;
1570 if (s->directive == APP_CLASS_DYNAMIC) {
1571 s->numProcesses++;
1572 fcgi_dynamic_total_proc_count++;
1573 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1574 #ifdef WIN32
1575 if (i == 0 && !restart) {
1576 fcgi_rdwr_unlock(s->dynamic_lock, WRITER);
1578 #endif
1581 s->procs[i].state = STATE_STARTED;
1583 if (restart)
1584 s->numRestarts++;
1586 if (fcgi_suexec != NULL) {
1587 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1588 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1589 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1590 s->fs_path, (long)s->uid, (long)s->gid,
1591 restart ? "re" : "", (long)s->procs[i].pid);
1593 else {
1594 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1595 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1596 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1597 s->fs_path, restart ? "re" : "", (long)s->procs[i].pid);
1599 ap_assert(s->procs[i].pid > 0);
1600 } else {
1601 sleepSeconds = min(sleepSeconds, restartTime - now);
1607 #ifndef WIN32
1608 if(caughtSigTerm) {
1609 goto ProcessSigTerm;
1611 if((!caughtSigChld) && (!caughtSigUsr2)) {
1612 fd_set rfds;
1614 alarm(sleepSeconds);
1616 FD_ZERO(&rfds);
1617 FD_SET(fcgi_pm_pipe[0], &rfds);
1618 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1620 alarmLeft = alarm(0);
1622 callWaitPid = caughtSigChld;
1623 caughtSigChld = FALSE;
1624 callDynamicProcs = caughtSigUsr2;
1625 caughtSigUsr2 = FALSE;
1627 now = time(NULL);
1630 * Dynamic fcgi process management
1632 if((callDynamicProcs) || (!callWaitPid)) {
1633 dynamic_read_msgs(read_ready);
1634 if(fcgi_dynamic_epoch == 0) {
1635 fcgi_dynamic_epoch = now;
1637 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1638 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1639 dynamic_kill_idle_fs_procs();
1640 fcgi_dynamic_epoch = now;
1644 if(!callWaitPid) {
1645 continue;
1648 /* We've caught SIGCHLD, so find out who it was using waitpid,
1649 * write a log message and update its data structure. */
1651 for (;;) {
1652 if (caughtSigTerm)
1653 goto ProcessSigTerm;
1655 childPid = waitpid(-1, &waitStatus, WNOHANG);
1657 if (childPid == -1 || childPid == 0)
1658 break;
1660 for (s = fcgi_servers; s != NULL; s = s->next) {
1661 if (s->directive == APP_CLASS_EXTERNAL)
1662 continue;
1664 if (s->directive == APP_CLASS_DYNAMIC)
1665 numChildren = dynamicMaxClassProcs;
1666 else
1667 numChildren = s->numProcesses;
1669 for (i = 0; i < numChildren; i++) {
1670 if (s->procs[i].pid == childPid)
1671 goto ChildFound;
1675 /* @@@ This (comment) needs to go away when dynamic gets cleaned up.
1676 * If we get to this point, we have detected the
1677 * termination of the process that was spawned off by
1678 * the process manager to do a blocking kill above. */
1679 continue;
1681 ChildFound:
1682 s->procs[i].pid = -1;
1684 if (s->directive == APP_CLASS_STANDARD) {
1685 /* Always restart static apps */
1686 s->procs[i].state = STATE_NEEDS_STARTING;
1687 s->numFailures++;
1689 else {
1690 s->numProcesses--;
1691 fcgi_dynamic_total_proc_count--;
1693 if (s->procs[i].state == STATE_VICTIM) {
1694 s->procs[i].state = STATE_KILLED;
1696 else {
1697 /* A dynamic app died or exited without provacation from the PM */
1698 s->numFailures++;
1700 if (dynamicAutoRestart || s->numProcesses <= 0)
1701 s->procs[i].state = STATE_NEEDS_STARTING;
1702 else
1703 s->procs[i].state = STATE_READY;
1707 if (WIFEXITED(waitStatus)) {
1708 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1709 "FastCGI:%s server \"%s\" (pid %d) terminated by calling exit with status '%d'",
1710 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1711 s->fs_path, (int)childPid, WEXITSTATUS(waitStatus));
1713 else if (WIFSIGNALED(waitStatus)) {
1714 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1715 "FastCGI:%s server \"%s\" (pid %d) terminated due to uncaught signal '%d' (%s)%s",
1716 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1717 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)],
1718 #ifdef WCOREDUMP
1719 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1720 #else
1721 "");
1722 #endif
1724 else if (WIFSTOPPED(waitStatus)) {
1725 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1726 "FastCGI:%s server \"%s\" (pid %d) stopped due to uncaught signal '%d' (%s)",
1727 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1728 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)]);
1730 } /* for (;;), waitpid() */
1731 #else
1732 if (first_time) {
1733 /* Start the child wait thread */
1734 wait_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)child_wait_thread, NULL, 0, NULL);
1735 first_time = 0;
1738 /* wait for an event to occur or timer expires */
1739 expire = time(NULL) + sleepSeconds;
1740 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1742 if (dwRet == WAIT_FAILED) {
1743 /* There is something seriously wrong here */
1744 ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, fcgi_apache_main_server,
1745 "FastDGI: WaitForMultipleObjects on event handles -- pm is shuting down");
1746 bTimeToDie = TRUE;
1749 if (dwRet != WAIT_TIMEOUT) {
1750 now = time(NULL);
1752 if (now < expire)
1753 alarmLeft = expire - now;
1757 * Dynamic fcgi process management
1759 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1760 if (dwRet == MBOX_EVENT) {
1761 read_ready = 1;
1764 now = time(NULL);
1766 dynamic_read_msgs(read_ready);
1768 if(fcgi_dynamic_epoch == 0) {
1769 fcgi_dynamic_epoch = now;
1772 if (((long)(now-fcgi_dynamic_epoch) >= (int)dynamicKillInterval) ||
1773 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1774 dynamic_kill_idle_fs_procs();
1775 fcgi_dynamic_epoch = now;
1777 read_ready = 0;
1779 else if (dwRet == WAKE_EVENT) {
1780 continue;
1782 else if (dwRet == TERM_EVENT) {
1783 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1784 "FastCGI: Termination event received process manager shutting down");
1785 bTimeToDie = TRUE;
1787 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1788 goto ProcessSigTerm;
1790 else {
1791 // Have an received an unknown event - should not happen
1792 ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, fcgi_apache_main_server,
1793 "FastCGI: WaitForMultipleobjects return an unrecognized event");
1794 bTimeToDie = TRUE;
1795 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1796 goto ProcessSigTerm;
1798 #endif
1799 } /* for (;;), the whole shoot'n match */
1801 ProcessSigTerm:
1803 * Kill off the children, then exit.
1805 while (fcgi_servers != NULL) {
1806 kill_fs_procs(fcgi_config_pool, fcgi_servers);
1809 #ifdef WIN32
1810 return;
1811 #else
1812 exit(0);
1813 #endif
1816 #ifdef WIN32
1817 int fcgi_pm_add_job(fcgi_pm_job *new_job) {
1819 if (new_job == NULL)
1820 return 0;
1822 if (ap_acquire_mutex(fcgi_dynamic_mbox_mutex) != MULTI_OK) {
1823 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1824 "FastCGI: Failed to aquire the dynamic mbox mutex!");
1827 new_job->next = fcgi_dynamic_mbox;
1828 fcgi_dynamic_mbox = new_job;
1830 if (! ap_release_mutex(fcgi_dynamic_mbox_mutex)) {
1831 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1832 "FastCGI: Failed to release the dynamic mbox mutex!");
1835 return 1;
1837 #endif