Properly log Win32 errors.
[mod_fastcgi.git] / fcgi_pm.c
blob1e635d1fd26054050da2bfe18b9c05d4aa49b15a
1 /*
2 * $Id: fcgi_pm.c,v 1.51 2001/03/05 18:16:48 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_wrapper) {
66 seteuid_root();
69 rc = kill(process->pid, sig);
71 if (fcgi_wrapper) {
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 static int init_listen_sock(fcgi_server * fs)
146 ap_assert(fs->directive != APP_CLASS_EXTERNAL);
148 /* Create the socket */
149 if ((fs->listenFd = ap_psocket(fcgi_config_pool, fs->socket_addr->sa_family, SOCK_STREAM, 0)) < 0)
151 #ifdef WIN32
152 errno = WSAGetLastError(); // Not sure if this will work as expected
153 #endif
154 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
155 "FastCGI: can't create %sserver \"%s\": socket() failed",
156 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
157 fs->fs_path);
158 return -1;
161 #ifndef WIN32
162 if (fs->socket_addr->sa_family == AF_UNIX)
164 /* Remove any existing socket file.. just in case */
165 unlink(((struct sockaddr_un *)fs->socket_addr)->sun_path);
167 else
168 #endif
170 int flag = 1;
171 setsockopt(fs->listenFd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
174 /* Bind it to the socket_addr */
175 if (bind(fs->listenFd, fs->socket_addr, fs->socket_addr_len))
177 char port[11];
179 #ifdef WIN32
180 errno = WSAGetLastError();
181 #endif
182 ap_snprintf(port, sizeof(port), "port=%d",
183 ((struct sockaddr_in *)fs->socket_addr)->sin_port);
185 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
186 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
187 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
188 fs->fs_path,
189 #ifndef WIN32
190 (fs->socket_addr->sa_family == AF_UNIX) ?
191 ((struct sockaddr_un *)fs->socket_addr)->sun_path :
192 #endif
193 port);
196 #ifndef WIN32
197 /* Twiddle Unix socket permissions */
198 else if (fs->socket_addr->sa_family == AF_UNIX
199 && chmod(((struct sockaddr_un *)fs->socket_addr)->sun_path, S_IRUSR | S_IWUSR))
201 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
202 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
203 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
204 fs->fs_path);
206 #endif
208 /* Set to listen */
209 else if (listen(fs->listenFd, fs->listenQueueDepth))
211 #ifdef WIN32
212 errno = WSAGetLastError();
213 #endif
214 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
215 "FastCGI: can't create %sserver \"%s\": listen() failed",
216 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
217 fs->fs_path);
219 else
221 return 0;
224 ap_pclosesocket(fcgi_config_pool, fs->listenFd);
225 fs->listenFd = -1;
226 return -2;
230 *----------------------------------------------------------------------
232 * dynamic_blocking_kill
234 * Block on the lock file until it is available, and then
235 * issue a kill signal to the corresponding application.
236 * Since this function is executed in the child process,
237 * _exit() is called upon completion.
239 * Inputs
240 * Pointer to the data structure containing a process id to
241 * issue a signal to and the full pathname to the lockfile
242 * that needs to be locked before the issue of the signal.
244 * Notes
245 * Memory is allocated by the caller, but is freed by this
246 * function.
248 *----------------------------------------------------------------------
250 static void dynamic_blocking_kill(void *data)
252 struct FuncData *funcData = (struct FuncData *)data;
254 #ifndef WIN32
255 int lockFd;
257 ap_assert(funcData->lockFileName);
258 if ((lockFd = open(funcData->lockFileName, O_RDWR)) < 0) {
259 /* There is something terribly wrong here */
260 } else {
261 if (fcgi_wait_for_shared_write_lock(lockFd) < 0) {
262 /* This is a major problem */
263 } else {
264 fcgi_kill(funcData->process, SIGTERM);
267 /* exit() may flush stdio buffers inherited from the parent. */
268 _exit(0);
270 #else
271 FCGIDBG1("dynamic_blocking_kill()");
272 if (fcgi_wait_for_shared_write_lock(funcData->lock) < 0) {
273 // This is a major problem
274 FCGIDBG1("fcgi_wait_for_shared_write_lock() failed >> MAJOR PROBLEM");
276 else {
277 fcgi_kill(funcData->process, 1);
278 fcgi_rdwr_unlock(funcData->lock, WRITER);
280 free(data);
281 return;
282 #endif
286 *----------------------------------------------------------------------
288 * pm_main
290 * The FastCGI process manager, which runs as a separate
291 * process responsible for:
292 * - Starting all the FastCGI proceses.
293 * - Restarting any of these processes that die (indicated
294 * by SIGCHLD).
295 * - Catching SIGTERM and relaying it to all the FastCGI
296 * processes before exiting.
298 * Inputs:
299 * Uses global variable fcgi_servers.
301 * Results:
302 * Does not return.
304 * Side effects:
305 * Described above.
307 *----------------------------------------------------------------------
309 #ifndef WIN32
310 static int caughtSigTerm = FALSE;
311 static int caughtSigChld = FALSE;
312 static int caughtSigUsr2 = FALSE;
314 static void signal_handler(int signo)
316 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
317 /* SIGUSR1 & SIGHUP are sent by apache to its process group
318 * when apache get 'em. Apache follows up (1.2.x) with attacks
319 * on each of its child processes, but we've got the KillMgr
320 * sitting between us so we never see the KILL. The main loop
321 * in ProcMgr also checks to see if the KillMgr has terminated,
322 * and if it has, we handl it as if we should shutdown too. */
323 caughtSigTerm = TRUE;
324 } else if(signo == SIGCHLD) {
325 caughtSigChld = TRUE;
326 } else if(signo == SIGALRM) {
327 caughtSigUsr2 = TRUE;
330 #endif
333 *----------------------------------------------------------------------
335 * spawn_fs_process --
337 * Fork and exec the specified fcgi process.
339 * Results:
340 * 0 for successful fork, -1 for failed fork.
342 * In case the child fails before or in the exec, the child
343 * obtains the error log by calling getErrLog, logs
344 * the error, and exits with exit status = errno of
345 * the failed system call.
347 * Side effects:
348 * Child process created.
350 *----------------------------------------------------------------------
353 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
355 #ifndef WIN32
356 pid_t child_pid;
357 int i;
358 char *dirName;
359 char *dnEnd, *failedSysCall;
361 child_pid = fork();
362 if (child_pid) {
363 return child_pid;
366 /* We're the child. We're gonna exec() so pools don't matter. */
368 dnEnd = strrchr(fs->fs_path, '/');
369 if (dnEnd == NULL) {
370 dirName = "./";
371 } else {
372 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
373 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
375 if (chdir(dirName) < 0) {
376 failedSysCall = "chdir()";
377 goto FailedSystemCallExit;
380 #ifndef __EMX__
381 /* OS/2 dosen't support nice() */
382 if (fs->processPriority != 0) {
383 if (nice(fs->processPriority) == -1) {
384 failedSysCall = "nice()";
385 goto FailedSystemCallExit;
388 #endif
390 /* Open the listenFd on spec'd fd */
391 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
392 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
394 /* Close all other open fds, except stdout/stderr. Leave these two open so
395 * FastCGI applications don't have to find and fix ALL 3rd party libs that
396 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
397 * main server error_log - @@@ provide a directive control where this goes.
399 ap_error_log2stderr(fcgi_apache_main_server);
400 dup2(STDERR_FILENO, STDOUT_FILENO);
401 for (i = 0; i < FCGI_MAX_FD; i++) {
402 if (i != FCGI_LISTENSOCK_FILENO && i != STDERR_FILENO && i != STDOUT_FILENO) {
403 close(i);
407 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
408 * install its own handler. */
409 signal(SIGPIPE, SIG_IGN);
411 if (fcgi_wrapper && (fcgi_user_id != fs->uid || fcgi_group_id != fs->gid)) {
412 char *shortName = strrchr(fs->fs_path, '/') + 1;
414 /* Relinquish our root real uid powers */
415 seteuid_root();
416 setuid(ap_user_id);
418 do {
419 execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group, shortName, NULL, fs->envp);
420 } while (errno == EINTR);
422 else {
423 do {
424 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
425 } while (errno == EINTR);
428 failedSysCall = "execle()";
430 FailedSystemCallExit:
431 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
432 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
433 exit(-1);
435 /* avoid an irrelevant compiler warning */
436 return(0);
438 #else
440 /* Adapted from Apache's util_script.c ap_call_exec() */
441 char *interpreter = NULL;
442 char *ext = NULL;
443 char *exename = NULL;
444 char *s = NULL;
445 char *quoted_filename;
446 char *pCommand;
447 char *pEnvBlock, *pNext;
449 int i;
450 int iEnvBlockLen;
452 file_type_e fileType;
454 STARTUPINFO si;
455 PROCESS_INFORMATION pi;
457 request_rec r;
458 pid_t pid = -1;
460 HANDLE listen_handle, mutex;
461 char * mutex_string = NULL;
463 pool * tp = ap_make_sub_pool(fcgi_config_pool);
465 if (fs->socket_path)
467 SECURITY_ATTRIBUTES sa;
469 sa.nLength = sizeof(sa);
470 sa.lpSecurityDescriptor = NULL;
471 sa.bInheritHandle = TRUE;
473 listen_handle = CreateNamedPipe(fs->socket_path, PIPE_ACCESS_DUPLEX,
474 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
475 PIPE_UNLIMITED_INSTANCES, 4096,4096,0, &sa);
476 if (listen_handle == INVALID_HANDLE_VALUE)
478 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
479 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
480 exit(0);
483 // This mutex is not really necessary, but for compatibility with
484 // the existing library, we need it.
486 mutex = ap_create_mutex(NULL);
487 if (mutex == NULL)
489 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
490 "FastCGI: can't exec server \"%s\", ap_create_mutex() failed", fs->fs_path);
491 CloseHandle(listen_handle);
492 exit(0);
495 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
497 mutex_string = ap_psprintf(tp, "_FCGI_MUTEX_=%ld", mutex);
499 else
501 listen_handle = (HANDLE) fs->listenFd;
504 memset(&si, 0, sizeof(si));
505 memset(&pi, 0, sizeof(pi));
506 memset(&r, 0, sizeof(r));
508 // Can up a fake request to pass to ap_get_win32_interpreter()
509 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
510 r.server = fcgi_apache_main_server;
511 r.filename = (char *) fs->fs_path;
512 r.pool = tp;
514 fileType = ap_get_win32_interpreter(&r, &interpreter);
516 if (fileType == eFileTypeUNKNOWN) {
517 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
518 "FastCGI: %s is not executable; ensure interpreted scripts have "
519 "\"#!\" as their first line",
520 fs->fs_path);
521 ap_destroy_pool(tp);
522 return (pid);
526 * We have the interpreter (if there is one) and we have
527 * the arguments (if there are any).
528 * Build the command string to pass to CreateProcess.
530 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
531 if (interpreter && *interpreter) {
532 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
534 else {
535 pCommand = quoted_filename;
539 * Make child process use hPipeOutputWrite as standard out,
540 * and make sure it does not show on screen.
542 si.cb = sizeof(si);
543 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
544 si.wShowWindow = SW_HIDE;
545 si.hStdInput = listen_handle;
547 // XXX These should be open to the error_log
548 si.hStdOutput = INVALID_HANDLE_VALUE;
549 si.hStdError = INVALID_HANDLE_VALUE;
552 * Win32's CreateProcess call requires that the environment
553 * be passed in an environment block, a null terminated block of
554 * null terminated strings.
556 i = 0;
557 iEnvBlockLen = 1;
558 while (fs->envp[i]) {
559 iEnvBlockLen += strlen(fs->envp[i]) + 1;
560 i++;
563 if (fs->socket_path)
565 iEnvBlockLen += strlen(mutex_string) + 1;
568 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
570 i = 0;
571 pNext = pEnvBlock;
572 while (fs->envp[i]) {
573 strcpy(pNext, fs->envp[i]);
574 pNext = pNext + strlen(pNext) + 1;
575 i++;
578 if (fs->socket_path)
580 strcpy(pNext, mutex_string);
583 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
585 pEnvBlock,
586 ap_make_dirstr_parent(tp, fs->fs_path),
587 &si, &pi)) {
588 if (fileType == eFileTypeEXE16) {
589 /* Hack to get 16-bit CGI's working. It works for all the
590 * standard modules shipped with Apache. pi.dwProcessId is 0
591 * for 16-bit CGIs and all the Unix specific code that calls
592 * ap_call_exec interprets this as a failure case. And we can't
593 * use -1 either because it is mapped to 0 by the caller.
595 pid = -2;
597 else {
598 pid = pi.dwProcessId;
599 process->handle = pi.hProcess;
600 CloseHandle(pi.hThread);
604 // We don't need these anymore..
605 if (fs->socket_path)
607 CloseHandle(listen_handle);
608 CloseHandle(mutex);
611 ap_destroy_pool(tp);
613 return pid;
615 #endif
618 #ifndef WIN32
619 static void reduce_privileges(void)
621 char *name;
623 if (geteuid() != 0)
624 return;
626 #ifndef __EMX__
627 /* Get username if passed as a uid */
628 if (ap_user_name[0] == '#') {
629 uid_t uid = atoi(&ap_user_name[1]);
630 struct passwd *ent = getpwuid(uid);
632 if (ent == NULL) {
633 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
634 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
635 "you probably need to modify the User directive", (unsigned)uid);
636 exit(1);
638 name = ent->pw_name;
640 else
641 name = ap_user_name;
643 /* Change Group */
644 if (setgid(ap_group_id) == -1) {
645 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
646 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
647 exit(1);
650 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
652 /* Initialize supplementary groups */
653 if (initgroups(name, ap_group_id) == -1) {
654 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
655 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
656 name, (unsigned)ap_group_id);
657 exit(1);
659 #endif /* __EMX__ */
661 /* Change User */
662 if (fcgi_wrapper) {
663 if (seteuid_user() == -1) {
664 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
665 "FastCGI: process manager exiting, failed to reduce privileges");
666 exit(1);
669 else {
670 if (setuid(ap_user_id) == -1) {
671 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
672 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
673 exit(1);
678 /*************
679 * Change the name of this process - best we can easily.
681 static void change_process_name(const char * const name)
683 strncpy(ap_server_argv0, name, strlen(ap_server_argv0));
685 #endif
687 static void schedule_start(fcgi_server *s, int proc)
689 /* If we've started one recently, don't register another */
690 time_t time_passed = now - s->restartTime;
692 if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay))
693 || ((s->procs[proc].pid == 0) && (time_passed < (int) s->initStartDelay)))
695 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);
696 return;
699 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
700 s->procs[proc].state = STATE_NEEDS_STARTING;
701 if (proc == (int)dynamicMaxClassProcs - 1) {
702 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
703 "FastCGI: scheduled the %sstart of the last (dynamic) server "
704 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
705 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
710 *----------------------------------------------------------------------
712 * dynamic_read_msgs
714 * Removes the records written by request handlers and decodes them.
715 * We also update the data structures to reflect the changes.
717 *----------------------------------------------------------------------
720 static void dynamic_read_msgs(int read_ready)
722 fcgi_server *s;
724 #ifndef WIN32
725 int rc;
726 static int buflen = 0;
727 static char buf[FCGI_MSGS_BUFSIZE + 1];
728 char *ptr1, *ptr2, opcode;
729 char execName[FCGI_MAXPATH + 1];
730 char user[MAX_USER_NAME_LEN + 2];
731 char group[MAX_GID_CHAR_LEN + 1];
732 unsigned long q_usec = 0UL, req_usec = 0UL;
733 #else
734 fcgi_pm_job *joblist = NULL;
735 fcgi_pm_job *cjob = NULL;
736 #endif
738 pool *sp, *tp;
740 #ifndef WIN32
741 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
742 #endif
745 * To prevent the idle application from running indefinitely, we
746 * check the timer and if it is expired, we recompute the values
747 * for each running application class. Then, when REQ_COMPLETE
748 * message is received, only updates are made to the data structures.
750 if (fcgi_dynamic_last_analyzed == 0) {
751 fcgi_dynamic_last_analyzed = now;
753 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
754 for (s = fcgi_servers; s != NULL; s = s->next) {
755 if (s->directive != APP_CLASS_DYNAMIC)
756 break;
758 /* Advance the last analyzed timestamp by the elapsed time since
759 * it was last set. Round the increase down to the nearest
760 * multiple of dynamicUpdateInterval */
762 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
763 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
764 s->totalConnTime = 0UL;
765 s->totalQueueTime = 0UL;
769 if (read_ready <= 0) {
770 return;
773 #ifndef WIN32
774 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
775 if (rc <= 0) {
776 if (!caughtSigTerm) {
777 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
778 "FastCGI: read() from pipe failed (%d)", rc);
779 if (rc == 0) {
780 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
781 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
782 caughtSigTerm = TRUE;
785 return;
787 buflen += rc;
788 buf[buflen] = '\0';
789 #else
790 if (ap_acquire_mutex(fcgi_dynamic_mbox_mutex) != MULTI_OK) {
791 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
792 "FastCGI: Failed to aquire the dynamic mbox mutex!");
795 joblist = fcgi_dynamic_mbox;
796 fcgi_dynamic_mbox = NULL;
798 if (! ap_release_mutex(fcgi_dynamic_mbox_mutex)) {
799 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
800 "FastCGI: Failed to release the dynamic mbox mutex!");
803 cjob = joblist;
804 #endif
806 tp = ap_make_sub_pool(fcgi_config_pool);
808 #ifndef WIN32
809 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
810 int scan_failed = 0;
812 ptr2 = strchr(ptr1, '*');
813 if (ptr2) {
814 *ptr2++ = '\0';
816 else {
817 break;
820 opcode = *ptr1;
822 switch (opcode) {
823 case PLEASE_START:
824 if (sscanf(ptr1, "%c %s %16s %15s",
825 &opcode, execName, user, group) != 4)
827 scan_failed = 1;
829 break;
830 case CONN_TIMEOUT:
831 if (sscanf(ptr1, "%c %s %16s %15s",
832 &opcode, execName, user, group) != 4)
834 scan_failed = 1;
836 break;
837 case REQ_COMPLETE:
838 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
839 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
841 scan_failed = 1;
843 break;
844 default:
845 scan_failed = 1;
846 break;
849 if (scan_failed) {
850 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
851 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
852 goto NextJob;
854 #else
855 /* Update data structures for processing */
856 while (cjob != NULL) {
857 joblist = cjob->next;
858 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
859 #endif
861 #ifndef WIN32
862 s = fcgi_util_fs_get(execName, user, group);
863 #else
864 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
865 #endif
867 #ifndef WIN32
868 if (s==NULL && opcode != REQ_COMPLETE)
869 #else
870 if (s==NULL && cjob->id != REQ_COMPLETE)
871 #endif
873 #ifndef WIN32
874 int fd;
875 const char *err, *lockPath;
876 #endif
878 /* Create a perm subpool to hold the new server data,
879 * we can destroy it if something doesn't pan out */
880 sp = ap_make_sub_pool(fcgi_config_pool);
882 /* Create a new "dynamic" server */
883 s = fcgi_util_fs_new(sp);
884 s->directive = APP_CLASS_DYNAMIC;
885 s->restartDelay = dynamicRestartDelay;
886 s->listenQueueDepth = dynamicListenQueueDepth;
887 s->initStartDelay = dynamicInitStartDelay;
888 s->envp = dynamicEnvp;
889 #ifdef WIN32
890 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
891 #else
892 s->fs_path = ap_pstrdup(sp, execName);
893 #endif
894 ap_getparents(s->fs_path);
895 ap_no2slash(s->fs_path);
896 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
898 #ifndef WIN32
899 /* Create socket file's path */
900 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
901 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
903 /* Create sockaddr, prealloc it so it won't get created in tp */
904 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
905 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
906 &s->socket_addr_len, s->socket_path);
907 if (err) {
908 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
909 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
910 goto BagNewServer;
913 if (init_listen_sock(s)) {
914 goto BagNewServer;
917 /* Create the lock file */
918 lockPath = fcgi_util_socket_get_lock_filename(tp, s->socket_path);
919 fd = ap_popenf(tp, lockPath,
920 O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
921 if (fd < 0) {
922 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
923 "FastCGI: can't create (dynamic) server \"%s\": can't open lock file \"%s\": popenf() failed",
924 execName, lockPath);
925 goto BagNewServer;
927 ap_pclosef(tp, fd);
929 /* If a wrapper is being used, config user/group info */
930 if (fcgi_wrapper) {
931 if (user[0] == '~') {
932 /* its a user dir uri, the rest is a username, not a uid */
933 struct passwd *pw = getpwnam(&user[1]);
935 if (!pw) {
936 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
937 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
938 execName, &user[1]);
939 goto BagNewServer;
941 s->uid = pw->pw_uid;
942 s->user = ap_pstrdup(sp, user);
943 s->username = s->user;
945 s->gid = pw->pw_gid;
946 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
948 else {
949 struct passwd *pw;
951 s->uid = (uid_t)atol(user);
952 pw = getpwuid(s->uid);
953 if (!pw) {
954 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
955 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
956 execName, (long)s->uid);
957 goto BagNewServer;
959 s->user = ap_pstrdup(sp, user);
960 s->username = ap_pstrdup(sp, pw->pw_name);
962 s->gid = (gid_t)atol(group);
963 s->group = ap_pstrdup(sp, group);
966 #else
967 /* Create socket file's path */
968 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
969 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
970 s->listenFd = 0;
972 /* Create the application lock */
973 if ((s->dynamic_lock = fcgi_rdwr_create()) == NULL) {
974 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
975 "FastCGI: can;t create (dynamic) server \"%s\": fcgi_rdwr_create() failed",
976 cjob->fs_path);
977 goto BagNewServer;
980 // Lock it until the first process is started
981 fcgi_rdwr_lock(s->dynamic_lock, WRITER);
982 #endif
984 fcgi_util_fs_add(s);
986 else {
987 #ifndef WIN32
988 if (opcode == PLEASE_START) {
989 #else
990 if(cjob->id==PLEASE_START) {
991 #endif
992 if (dynamicAutoUpdate) {
993 /* Check to see if the binary has changed. If so,
994 * kill the FCGI application processes, and
995 * restart them.
997 struct stat stbuf;
998 unsigned int i;
1000 #ifndef WIN32
1001 if ((stat(execName, &stbuf)==0) &&
1002 #else
1003 if ((stat(cjob->fs_path, &stbuf)==0) &&
1004 #endif
1005 (stbuf.st_mtime > s->restartTime)) {
1006 /* kill old server(s) */
1007 for (i = 0; i < dynamicMaxClassProcs; i++) {
1008 if (s->procs[i].pid > 0) {
1009 fcgi_kill(&s->procs[i], SIGTERM);
1013 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1014 "FastCGI: restarting server \"%s\" processes, newer version found",
1015 #ifndef WIN32
1016 execName);
1017 #else
1018 cjob->fs_path);
1019 #endif
1022 /* If dynamicAutoRestart, don't mark any new processes
1023 * for starting because we probably got the
1024 * PLEASE_START due to dynamicAutoUpdate and the ProcMgr
1025 * will be restarting all of those we just killed.
1027 if (dynamicAutoRestart)
1028 goto NextJob;
1029 } else {
1030 /* we've been asked to start a process--only start
1031 * it if we're not already running at least one
1032 * instance.
1034 unsigned int i;
1036 for (i = 0; i < dynamicMaxClassProcs; i++) {
1037 if (s->procs[i].state == STATE_STARTED)
1038 break;
1040 /* if already running, don't start another one */
1041 if (i < dynamicMaxClassProcs) {
1042 goto NextJob;
1047 #ifndef WIN32
1048 switch (opcode)
1049 #else
1050 switch (cjob->id)
1051 #endif
1053 unsigned int i;
1055 case PLEASE_START:
1056 case CONN_TIMEOUT:
1058 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1060 * Extra instances should have been
1061 * terminated beforehand, probably need
1062 * to increase ProcessSlack parameter
1064 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1065 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1066 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1067 goto NextJob;
1070 /* find next free slot */
1071 for (i = 0; i < dynamicMaxClassProcs; i++)
1073 if (s->procs[i].state == STATE_NEEDS_STARTING)
1075 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1076 break;
1078 else if (s->procs[i].state == STATE_STARTED)
1080 continue;
1083 schedule_start(s, i);
1084 break;
1087 #ifdef FCGI_DEBUG
1088 if (i >= dynamicMaxClassProcs) {
1089 FCGIDBG1("ignore_job: slots are max'd");
1091 #endif
1092 break;
1093 case REQ_COMPLETE:
1094 /* only record stats if we have a structure */
1095 if (s) {
1096 #ifndef WIN32
1097 s->totalConnTime += req_usec;
1098 s->totalQueueTime += q_usec;
1099 #else
1100 s->totalConnTime += cjob->start_time;
1101 s->totalQueueTime += cjob->qsec;
1102 #endif
1104 break;
1107 NextJob:
1109 #ifdef WIN32
1110 /* Cleanup job data */
1111 free(cjob->fs_path);
1112 free(cjob->user);
1113 free(cjob->group);
1114 free(cjob);
1115 cjob = joblist;
1116 #endif
1118 continue;
1120 BagNewServer:
1121 ap_destroy_pool(sp);
1123 #ifdef WIN32
1124 free(cjob->fs_path);
1125 free(cjob);
1126 cjob = joblist;
1127 #endif
1130 #ifndef WIN32
1131 if (ptr1 == buf) {
1132 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1133 "FastCGI: really bogus message: \"%s\"", ptr1);
1134 ptr1 += strlen(buf);
1137 buflen -= ptr1 - buf;
1138 if (buflen) {
1139 memmove(buf, ptr1, buflen);
1141 #endif
1143 ap_destroy_pool(tp);
1147 *----------------------------------------------------------------------
1149 * dynamic_kill_idle_fs_procs
1151 * Implement a kill policy for the dynamic FastCGI applications.
1152 * We also update the data structures to reflect the changes.
1154 * Side effects:
1155 * Processes are marked for deletion possibly killed.
1157 *----------------------------------------------------------------------
1159 static void dynamic_kill_idle_fs_procs(void)
1161 fcgi_server *s;
1162 struct FuncData *funcData = NULL;
1163 unsigned long connTime; /* server's smoothed running time, or
1164 * if that's 0, the current total */
1165 unsigned long totalTime; /* maximum number of microseconds that all
1166 * of a server's running processes together
1167 * could have spent running since the
1168 * last check */
1169 double loadFactor; /* percentage, 0-100, of totalTime that
1170 * the processes actually used */
1171 unsigned int i, victims = 0;
1172 #ifndef WIN32
1173 const char *lockFileName;
1174 int lockFd;
1175 pid_t pid;
1176 #endif
1177 pool *tp = ap_make_sub_pool(fcgi_config_pool);
1179 /* pass 1 - locate and mark all victims */
1180 for(s=fcgi_servers; s!=NULL; s=s->next) {
1181 /* Only kill dynamic apps */
1182 if (s->directive != APP_CLASS_DYNAMIC)
1183 continue;
1185 /* If the number of non-victims is less than or equal to
1186 the minimum that may be running without being killed off,
1187 don't select any more victims. */
1188 if ((fcgi_dynamic_total_proc_count - victims) <= (int) dynamicMinProcs) {
1189 break;
1192 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1193 totalTime = (s->numProcesses)*(now - fcgi_dynamic_epoch)*1000000 + 1;
1195 /* XXX producing a heavy load with one client, I haven't been
1196 able to achieve a loadFactor greater than 0.5. Perhaps this
1197 should be scaled up by another order of magnitude or two. */
1198 loadFactor = 100.0 * connTime / totalTime;
1200 if ((s->numProcesses > 1
1201 && s->numProcesses/(s->numProcesses - 1)*loadFactor < dynamicThresholdN)
1202 || (s->numProcesses == 1 && loadFactor < dynamicThreshold1))
1204 int got_one = 0;
1206 for (i = 0; !got_one && i < dynamicMaxClassProcs; ++i) {
1207 if (s->procs[i].state == STATE_NEEDS_STARTING) {
1208 s->procs[i].state = STATE_READY;
1209 got_one = 1;
1211 else if (s->procs[i].state == STATE_VICTIM || s->procs[i].state == STATE_KILL) {
1212 got_one = 1;
1216 for (i = 0; !got_one && i < dynamicMaxClassProcs; ++i) {
1217 if (s->procs[i].state == STATE_STARTED) {
1218 s->procs[i].state = STATE_KILL;
1219 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1220 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination scheduled",
1221 s->fs_path, s->procs[i].pid);
1222 victims++;
1223 got_one = 1;
1229 /* pass 2 - kill procs off */
1230 for(s=fcgi_servers; s!=NULL; s=s->next) {
1231 /* Only kill dynamic apps */
1232 if (s->directive != APP_CLASS_DYNAMIC)
1233 continue;
1235 for(i = 0; i < dynamicMaxClassProcs; i++) {
1236 if (s->procs[i].state == STATE_KILL) {
1237 #ifndef WIN32
1238 lockFileName = fcgi_util_socket_get_lock_filename(tp, s->socket_path);
1239 if ((lockFd = ap_popenf(tp, lockFileName, O_RDWR, 0))<0) {
1241 * If we need to kill an application and the
1242 * corresponding lock file does not exist, then
1243 * that means we are in big trouble here
1245 /*@@@ this should be logged, but since all the lock
1246 * file stuff will be tossed, I'll leave it now */
1247 ap_pclosef(tp, lockFd);
1248 continue;
1251 if (fcgi_get_exclusive_write_lock_no_wait(lockFd) < 0) {
1252 #else
1253 if (fcgi_get_exclusive_write_lock_no_wait(s->dynamic_lock) < 0) {
1254 #endif
1255 FCGIDBG2("fcgi_get_exclusive_write_lock_no_wait() failed (%ld)", GetLastError());
1257 * Unable to lock the lockfile, indicative
1258 * of WS performing operation with the given
1259 * application class. The simplest solution
1260 * is to spawn off another process and block
1261 * on lock to kill it. This is under assumptions
1262 * that fork() is not very costly and this
1263 * situation occurs very rarely, which it should
1265 #ifndef WIN32
1266 funcData = ap_pcalloc(tp, sizeof(struct FuncData));
1267 funcData->lockFileName = lockFileName;
1268 #else
1269 funcData = malloc(sizeof(struct FuncData));
1270 funcData->lock = s->dynamic_lock;
1271 #endif
1272 funcData->process = &s->procs[i];
1274 #ifndef WIN32
1275 if((pid=fork())<0) {
1276 /*@@@ this should be logged, but since all the lock
1277 * file stuff will be tossed, I'll leave it now */
1278 ap_pclosef(tp, lockFd);
1279 continue;
1280 } else if(pid==0) {
1281 /* child */
1283 /* rename the process for ps - best we can easily */
1284 change_process_name("fcgiBlkKill");
1286 dynamic_blocking_kill(funcData);
1287 } else {
1288 /* parent */
1289 s->procs[i].state = STATE_VICTIM;
1290 ap_pclosef(tp, lockFd);
1292 #else
1293 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) dynamic_blocking_kill, (LPVOID) funcData, 0, NULL);
1294 s->procs[i].state = STATE_VICTIM;
1295 #endif
1297 else {
1298 FCGIDBG1("fcgi_get_exclusive_write_lock_no_wait() succeeded");
1299 s->procs[i].state = STATE_VICTIM;
1300 #ifndef WIN32
1301 fcgi_kill(&s->procs[i], SIGTERM);
1302 ap_pclosef(tp, lockFd);
1303 #else
1304 fcgi_kill(&s->procs[i], 1);
1305 fcgi_rdwr_unlock(s->dynamic_lock, WRITER);
1306 #endif
1311 ap_destroy_pool(tp);
1314 #ifdef WIN32
1316 // This is a little bogus, there's gotta be a better way to do this
1317 // Can we use WaitForMultipleObjects()
1318 #define FCGI_PROC_WAIT_TIME 100
1320 void child_wait_thread(void *dummy) {
1321 fcgi_server *s;
1322 DWORD dwRet = WAIT_TIMEOUT;
1323 int numChildren;
1324 int i;
1325 int waited;
1327 while (!bTimeToDie) {
1328 waited = 0;
1330 for (s = fcgi_servers; s != NULL; s = s->next) {
1331 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1332 continue;
1334 if (s->directive == APP_CLASS_DYNAMIC) {
1335 numChildren = dynamicMaxClassProcs;
1337 else {
1338 numChildren = s->numProcesses;
1341 for (i=0; i < numChildren; i++) {
1342 if (s->procs[i].handle != INVALID_HANDLE_VALUE) {
1343 /* timeout is currently set for 100 miliecond */
1344 /* it may need t longer or user customizable */
1345 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1347 waited = 1;
1349 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1350 /* a child fs has died */
1351 /* mark the child as dead */
1353 if (s->directive == APP_CLASS_STANDARD) {
1354 /* restart static app */
1355 s->procs[i].state = STATE_NEEDS_STARTING;
1356 s->numFailures++;
1358 else {
1359 s->numProcesses--;
1360 fcgi_dynamic_total_proc_count--;
1361 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1363 if (s->procs[i].state == STATE_VICTIM) {
1364 s->procs[i].state = STATE_KILLED;
1366 else {
1367 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1368 s->numFailures++;
1370 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1371 s->procs[i].state = STATE_NEEDS_STARTING;
1373 else {
1374 s->procs[i].state = STATE_READY;
1379 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1380 "FastCGI:%s server \"%s\" (pid %d) terminated",
1381 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1382 s->fs_path, s->procs[i].pid);
1384 CloseHandle(s->procs[i].handle);
1385 s->procs[i].handle = INVALID_HANDLE_VALUE;
1386 s->procs[i].pid = -1;
1388 /* wake up the main thread */
1389 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1394 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1397 #endif
1399 #ifndef WIN32
1400 static void setup_signals(void)
1402 sigset_t mask;
1403 struct sigaction sa;
1405 /* Ignore USR2 */
1406 sigemptyset(&mask);
1407 sigaddset(&mask, SIGUSR2);
1408 sigprocmask(SIG_BLOCK, &mask, NULL);
1410 /* Setup handlers */
1412 sa.sa_handler = signal_handler;
1413 sigemptyset(&sa.sa_mask);
1414 sa.sa_flags = 0;
1416 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1417 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1418 "sigaction(SIGTERM) failed");
1420 /* httpd restart */
1421 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1422 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1423 "sigaction(SIGHUP) failed");
1425 /* httpd graceful restart */
1426 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1427 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1428 "sigaction(SIGUSR1) failed");
1430 /* read messages from request handlers - kill interval expired */
1431 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1432 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1433 "sigaction(SIGALRM) failed");
1435 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1436 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1437 "sigaction(SIGCHLD) failed");
1440 #endif
1442 #ifndef WIN32
1443 int fcgi_pm_main(void *dummy, child_info *info)
1444 #else
1445 void fcgi_pm_main(void *dummy)
1446 #endif
1448 fcgi_server *s;
1449 unsigned int i;
1450 int read_ready = 0;
1451 int alarmLeft = 0;
1453 #ifdef WIN32
1454 DWORD dwRet;
1455 int first_time = 1;
1456 HANDLE wait_thread = INVALID_HANDLE_VALUE;
1457 #else
1458 int callWaitPid, callDynamicProcs;
1459 #endif
1461 #ifdef WIN32
1462 // Add SystemRoot to the dynamic environment
1463 char ** envp = dynamicEnvp;
1464 for (i = 0; *envp; ++i) {
1465 ++envp;
1467 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1469 #else
1470 reduce_privileges();
1472 close(fcgi_pm_pipe[1]);
1473 change_process_name("fcgi-pm");
1474 setup_signals();
1476 if (fcgi_wrapper) {
1477 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1478 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1480 #endif
1482 /* Initialize AppClass */
1483 for (s = fcgi_servers; s != NULL; s = s->next)
1485 if (s->directive != APP_CLASS_STANDARD)
1486 continue;
1488 #ifdef WIN32
1489 if (s->socket_path)
1490 s->listenFd = 0;
1491 #endif
1493 for (i = 0; i < s->numProcesses; ++i)
1494 s->procs[i].state = STATE_NEEDS_STARTING;
1497 #ifdef WIN32
1498 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1499 "FastCGI: process manager initialized");
1500 #else
1501 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1502 "FastCGI: process manager initialized (pid %ld)", (long)getpid());
1503 #endif
1505 now = time(NULL);
1508 * Loop until SIGTERM
1510 for (;;) {
1511 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1512 #ifdef WIN32
1513 time_t expire;
1514 #else
1515 pid_t childPid;
1516 int waitStatus;
1517 #endif
1518 unsigned int numChildren;
1521 * If we came out of sigsuspend() for any reason other than
1522 * SIGALRM, pick up where we left off.
1524 if (alarmLeft)
1525 sleepSeconds = alarmLeft;
1528 * Examine each configured AppClass for a process that needs
1529 * starting. Compute the earliest time when the start should
1530 * be attempted, starting it now if the time has passed. Also,
1531 * remember that we do NOT need to restart externally managed
1532 * FastCGI applications.
1534 for (s = fcgi_servers; s != NULL; s = s->next)
1536 if (s->directive == APP_CLASS_EXTERNAL)
1537 continue;
1539 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1540 ? dynamicMaxClassProcs
1541 : s->numProcesses;
1543 for (i = 0; i < numChildren; ++i)
1545 if (s->procs[i].pid <= 0 && s->procs[i].state == STATE_NEEDS_STARTING)
1547 int restart = (s->procs[i].pid < 0);
1548 time_t restartTime = s->restartTime;
1550 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1552 if (restartTime <= now)
1554 s->restartTime = now;
1556 if (s->listenFd < 0 && init_listen_sock(s))
1558 if (sleepSeconds > s->initStartDelay)
1559 sleepSeconds = s->initStartDelay;
1560 break;
1563 #ifndef WIN32
1564 if (caughtSigTerm) {
1565 goto ProcessSigTerm;
1567 #endif
1568 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1569 if (s->procs[i].pid <= 0) {
1570 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1571 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1572 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1573 s->fs_path);
1575 sleepSeconds = min(sleepSeconds,
1576 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1578 ap_assert(s->procs[i].pid < 0);
1579 break;
1582 if (s->directive == APP_CLASS_DYNAMIC) {
1583 s->numProcesses++;
1584 fcgi_dynamic_total_proc_count++;
1585 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1586 #ifdef WIN32
1587 if (i == 0 && !restart) {
1588 fcgi_rdwr_unlock(s->dynamic_lock, WRITER);
1590 #endif
1593 s->procs[i].state = STATE_STARTED;
1595 if (restart)
1596 s->numRestarts++;
1598 if (fcgi_wrapper) {
1599 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1600 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1601 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1602 s->fs_path, (long)s->uid, (long)s->gid,
1603 restart ? "re" : "", (long)s->procs[i].pid);
1605 else {
1606 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1607 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1608 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1609 s->fs_path, restart ? "re" : "", (long)s->procs[i].pid);
1611 ap_assert(s->procs[i].pid > 0);
1612 } else {
1613 sleepSeconds = min(sleepSeconds, restartTime - now);
1619 #ifndef WIN32
1620 if(caughtSigTerm) {
1621 goto ProcessSigTerm;
1623 if((!caughtSigChld) && (!caughtSigUsr2)) {
1624 fd_set rfds;
1626 alarm(sleepSeconds);
1628 FD_ZERO(&rfds);
1629 FD_SET(fcgi_pm_pipe[0], &rfds);
1630 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1632 alarmLeft = alarm(0);
1634 callWaitPid = caughtSigChld;
1635 caughtSigChld = FALSE;
1636 callDynamicProcs = caughtSigUsr2;
1637 caughtSigUsr2 = FALSE;
1639 now = time(NULL);
1642 * Dynamic fcgi process management
1644 if((callDynamicProcs) || (!callWaitPid)) {
1645 dynamic_read_msgs(read_ready);
1646 if(fcgi_dynamic_epoch == 0) {
1647 fcgi_dynamic_epoch = now;
1649 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1650 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1651 dynamic_kill_idle_fs_procs();
1652 fcgi_dynamic_epoch = now;
1656 if(!callWaitPid) {
1657 continue;
1660 /* We've caught SIGCHLD, so find out who it was using waitpid,
1661 * write a log message and update its data structure. */
1663 for (;;) {
1664 if (caughtSigTerm)
1665 goto ProcessSigTerm;
1667 childPid = waitpid(-1, &waitStatus, WNOHANG);
1669 if (childPid == -1 || childPid == 0)
1670 break;
1672 for (s = fcgi_servers; s != NULL; s = s->next) {
1673 if (s->directive == APP_CLASS_EXTERNAL)
1674 continue;
1676 if (s->directive == APP_CLASS_DYNAMIC)
1677 numChildren = dynamicMaxClassProcs;
1678 else
1679 numChildren = s->numProcesses;
1681 for (i = 0; i < numChildren; i++) {
1682 if (s->procs[i].pid == childPid)
1683 goto ChildFound;
1687 /* @@@ This (comment) needs to go away when dynamic gets cleaned up.
1688 * If we get to this point, we have detected the
1689 * termination of the process that was spawned off by
1690 * the process manager to do a blocking kill above. */
1691 continue;
1693 ChildFound:
1694 s->procs[i].pid = -1;
1696 if (s->directive == APP_CLASS_STANDARD) {
1697 /* Always restart static apps */
1698 s->procs[i].state = STATE_NEEDS_STARTING;
1699 s->numFailures++;
1701 else {
1702 s->numProcesses--;
1703 fcgi_dynamic_total_proc_count--;
1705 if (s->procs[i].state == STATE_VICTIM) {
1706 s->procs[i].state = STATE_KILLED;
1708 else {
1709 /* A dynamic app died or exited without provocation from the PM */
1710 s->numFailures++;
1712 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1713 s->procs[i].state = STATE_NEEDS_STARTING;
1714 else
1715 s->procs[i].state = STATE_READY;
1719 if (WIFEXITED(waitStatus)) {
1720 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1721 "FastCGI:%s server \"%s\" (pid %d) terminated by calling exit with status '%d'",
1722 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1723 s->fs_path, (int)childPid, WEXITSTATUS(waitStatus));
1725 else if (WIFSIGNALED(waitStatus)) {
1726 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1727 "FastCGI:%s server \"%s\" (pid %d) terminated due to uncaught signal '%d' (%s)%s",
1728 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1729 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)],
1730 #ifdef WCOREDUMP
1731 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1732 #else
1733 "");
1734 #endif
1736 else if (WIFSTOPPED(waitStatus)) {
1737 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1738 "FastCGI:%s server \"%s\" (pid %d) stopped due to uncaught signal '%d' (%s)",
1739 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1740 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)]);
1742 } /* for (;;), waitpid() */
1743 #else
1744 if (first_time) {
1745 /* Start the child wait thread */
1746 wait_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)child_wait_thread, NULL, 0, NULL);
1747 first_time = 0;
1750 /* wait for an event to occur or timer expires */
1751 expire = time(NULL) + sleepSeconds;
1752 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1754 if (dwRet == WAIT_FAILED) {
1755 /* There is something seriously wrong here */
1756 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1757 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1758 bTimeToDie = TRUE;
1761 if (dwRet != WAIT_TIMEOUT) {
1762 now = time(NULL);
1764 if (now < expire)
1765 alarmLeft = expire - now;
1769 * Dynamic fcgi process management
1771 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1772 if (dwRet == MBOX_EVENT) {
1773 read_ready = 1;
1776 now = time(NULL);
1778 dynamic_read_msgs(read_ready);
1780 if(fcgi_dynamic_epoch == 0) {
1781 fcgi_dynamic_epoch = now;
1784 if (((long)(now-fcgi_dynamic_epoch) >= (int)dynamicKillInterval) ||
1785 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1786 dynamic_kill_idle_fs_procs();
1787 fcgi_dynamic_epoch = now;
1789 read_ready = 0;
1791 else if (dwRet == WAKE_EVENT) {
1792 continue;
1794 else if (dwRet == TERM_EVENT) {
1795 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1796 "FastCGI: Termination event received process manager shutting down");
1797 bTimeToDie = TRUE;
1799 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1800 goto ProcessSigTerm;
1802 else {
1803 // Have an received an unknown event - should not happen
1804 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1805 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1806 bTimeToDie = TRUE;
1807 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1808 goto ProcessSigTerm;
1810 #endif
1811 } /* for (;;), the whole shoot'n match */
1813 ProcessSigTerm:
1815 * Kill off the children, then exit.
1817 while (fcgi_servers != NULL) {
1818 kill_fs_procs(fcgi_config_pool, fcgi_servers);
1821 #ifdef WIN32
1822 return;
1823 #else
1824 exit(0);
1825 #endif
1828 #ifdef WIN32
1829 int fcgi_pm_add_job(fcgi_pm_job *new_job) {
1831 if (new_job == NULL)
1832 return 0;
1834 if (ap_acquire_mutex(fcgi_dynamic_mbox_mutex) != MULTI_OK) {
1835 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1836 "FastCGI: Failed to aquire the dynamic mbox mutex!");
1839 new_job->next = fcgi_dynamic_mbox;
1840 fcgi_dynamic_mbox = new_job;
1842 if (! ap_release_mutex(fcgi_dynamic_mbox_mutex)) {
1843 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1844 "FastCGI: Failed to release the dynamic mbox mutex!");
1847 return 1;
1849 #endif