Move a couple of preprocessor directives to col 1.
[mod_fastcgi.git] / fcgi_pm.c
blob38f630c41fb90cd986f02492118aed815a1fb996
1 /*
2 * $Id: fcgi_pm.c,v 1.42 2000/10/17 01:58:16 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 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
152 "FastCGI: can't create %sserver \"%s\": socket() failed",
153 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
154 fs->fs_path);
155 return -1;
158 #ifndef WIN32
159 if (fs->socket_addr->sa_family == AF_UNIX)
161 /* Remove any existing socket file.. just in case */
162 unlink(((struct sockaddr_un *)fs->socket_addr)->sun_path);
164 else
165 #endif
167 int flag = 1;
168 setsockopt(fs->listenFd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
171 /* Bind it to the socket_addr */
172 if (bind(fs->listenFd, fs->socket_addr, fs->socket_addr_len))
174 char port[11];
176 ap_snprintf(port, sizeof(port), "port=%d",
177 ((struct sockaddr_in *)fs->socket_addr)->sin_port);
179 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
180 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
181 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
182 fs->fs_path,
183 #ifndef WIN32
184 (fs->socket_addr->sa_family == AF_UNIX) ?
185 ((struct sockaddr_un *)fs->socket_addr)->sun_path :
186 #endif
187 port);
190 #ifndef WIN32
191 /* Twiddle Unix socket permissions */
192 else if (fs->socket_addr->sa_family == AF_UNIX
193 && chmod(((struct sockaddr_un *)fs->socket_addr)->sun_path, S_IRUSR | S_IWUSR))
195 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
196 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
197 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
198 fs->fs_path);
200 #endif
202 /* Set to listen */
203 else if (listen(fs->listenFd, fs->listenQueueDepth))
205 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
206 "FastCGI: can't create %sserver \"%s\": listen() failed",
207 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
208 fs->fs_path);
210 else
212 return 0;
215 ap_pclosesocket(fcgi_config_pool, fs->listenFd);
216 fs->listenFd = -1;
217 return -2;
221 *----------------------------------------------------------------------
223 * dynamic_blocking_kill
225 * Block on the lock file until it is available, and then
226 * issue a kill signal to the corresponding application.
227 * Since this function is executed in the child process,
228 * _exit() is called upon completion.
230 * Inputs
231 * Pointer to the data structure containing a process id to
232 * issue a signal to and the full pathname to the lockfile
233 * that needs to be locked before the issue of the signal.
235 * Notes
236 * Memory is allocated by the caller, but is freed by this
237 * function.
239 *----------------------------------------------------------------------
241 static void dynamic_blocking_kill(void *data)
243 struct FuncData *funcData = (struct FuncData *)data;
245 #ifndef WIN32
246 int lockFd;
248 ap_assert(funcData->lockFileName);
249 if ((lockFd = open(funcData->lockFileName, O_RDWR)) < 0) {
250 /* There is something terribly wrong here */
251 } else {
252 if (fcgi_wait_for_shared_write_lock(lockFd) < 0) {
253 /* This is a major problem */
254 } else {
255 fcgi_kill(funcData->process, SIGTERM);
258 /* exit() may flush stdio buffers inherited from the parent. */
259 _exit(0);
261 #else
262 FCGIDBG1("dynamic_blocking_kill()");
263 if (fcgi_wait_for_shared_write_lock(funcData->lock) < 0) {
264 // This is a major problem
265 FCGIDBG1("fcgi_wait_for_shared_write_lock() failed >> MAJOR PROBLEM");
267 else {
268 fcgi_kill(funcData->process, 1);
269 fcgi_rdwr_unlock(funcData->lock, WRITER);
271 free(data);
272 return;
273 #endif
277 *----------------------------------------------------------------------
279 * pm_main
281 * The FastCGI process manager, which runs as a separate
282 * process responsible for:
283 * - Starting all the FastCGI proceses.
284 * - Restarting any of these processes that die (indicated
285 * by SIGCHLD).
286 * - Catching SIGTERM and relaying it to all the FastCGI
287 * processes before exiting.
289 * Inputs:
290 * Uses global variable fcgi_servers.
292 * Results:
293 * Does not return.
295 * Side effects:
296 * Described above.
298 *----------------------------------------------------------------------
300 #ifndef WIN32
301 static int caughtSigTerm = FALSE;
302 static int caughtSigChld = FALSE;
303 static int caughtSigUsr2 = FALSE;
305 static void signal_handler(int signo)
307 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
308 /* SIGUSR1 & SIGHUP are sent by apache to its process group
309 * when apache get 'em. Apache follows up (1.2.x) with attacks
310 * on each of its child processes, but we've got the KillMgr
311 * sitting between us so we never see the KILL. The main loop
312 * in ProcMgr also checks to see if the KillMgr has terminated,
313 * and if it has, we handl it as if we should shutdown too. */
314 caughtSigTerm = TRUE;
315 } else if(signo == SIGCHLD) {
316 caughtSigChld = TRUE;
317 } else if(signo == SIGALRM) {
318 caughtSigUsr2 = TRUE;
321 #endif
324 *----------------------------------------------------------------------
326 * spawn_fs_process --
328 * Fork and exec the specified fcgi process.
330 * Results:
331 * 0 for successful fork, -1 for failed fork.
333 * In case the child fails before or in the exec, the child
334 * obtains the error log by calling getErrLog, logs
335 * the error, and exits with exit status = errno of
336 * the failed system call.
338 * Side effects:
339 * Child process created.
341 *----------------------------------------------------------------------
344 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
346 #ifndef WIN32
347 pid_t child_pid;
348 int i;
349 char *dirName;
350 char *dnEnd, *failedSysCall;
352 child_pid = fork();
353 if (child_pid) {
354 return child_pid;
357 /* We're the child. We're gonna exec() so pools don't matter. */
359 dnEnd = strrchr(fs->fs_path, '/');
360 if (dnEnd == NULL) {
361 dirName = "./";
362 } else {
363 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
364 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
366 if (chdir(dirName) < 0) {
367 failedSysCall = "chdir()";
368 goto FailedSystemCallExit;
371 #ifndef __EMX__
372 /* OS/2 dosen't support nice() */
373 if (fs->processPriority != 0) {
374 if (nice(fs->processPriority) == -1) {
375 failedSysCall = "nice()";
376 goto FailedSystemCallExit;
379 #endif
381 /* Open the listenFd on spec'd fd */
382 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
383 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
385 /* Close all other open fds, except stdout/stderr. Leave these two open so
386 * FastCGI applications don't have to find and fix ALL 3rd party libs that
387 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
388 * main server error_log - @@@ provide a directive control where this goes.
390 ap_error_log2stderr(fcgi_apache_main_server);
391 dup2(STDERR_FILENO, STDOUT_FILENO);
392 for (i = 0; i < FCGI_MAX_FD; i++) {
393 if (i != FCGI_LISTENSOCK_FILENO && i != STDERR_FILENO && i != STDOUT_FILENO) {
394 close(i);
398 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
399 * install its own handler. */
400 signal(SIGPIPE, SIG_IGN);
402 if (fcgi_wrapper && (fcgi_user_id != fs->uid || fcgi_group_id != fs->gid)) {
403 char *shortName = strrchr(fs->fs_path, '/') + 1;
405 /* Relinquish our root real uid powers */
406 seteuid_root();
407 setuid(ap_user_id);
409 do {
410 execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group, shortName, NULL, fs->envp);
411 } while (errno == EINTR);
413 else {
414 do {
415 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
416 } while (errno == EINTR);
419 failedSysCall = "execle()";
421 FailedSystemCallExit:
422 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
423 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
424 exit(-1);
426 /* avoid an irrelevant compiler warning */
427 return(0);
429 #else
431 /* Adapted from Apache's util_script.c ap_call_exec() */
432 char *interpreter = NULL;
433 char *ext = NULL;
434 char *exename = NULL;
435 char *s = NULL;
436 char *quoted_filename;
437 char *pCommand;
438 char *pEnvBlock, *pNext;
440 int i;
441 int iEnvBlockLen;
443 file_type_e fileType;
445 STARTUPINFO si;
446 PROCESS_INFORMATION pi;
448 request_rec r;
449 pid_t pid = -1;
451 HANDLE listen_handle, mutex;
452 char * mutex_string = NULL;
454 pool * tp = ap_make_sub_pool(fcgi_config_pool);
456 if (fs->socket_path)
458 SECURITY_ATTRIBUTES sa;
460 sa.nLength = sizeof(sa);
461 sa.lpSecurityDescriptor = NULL;
462 sa.bInheritHandle = TRUE;
464 listen_handle = CreateNamedPipe(fs->socket_path, PIPE_ACCESS_DUPLEX,
465 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
466 PIPE_UNLIMITED_INSTANCES, 4096,4096,0, &sa);
467 if (listen_handle == INVALID_HANDLE_VALUE)
469 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
470 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
471 exit(0);
474 // This mutex is not really necessary, but for compatibility with
475 // the existing library, we need it.
477 mutex = ap_create_mutex(NULL);
478 if (mutex == NULL)
480 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
481 "FastCGI: can't exec server \"%s\", ap_create_mutex() failed", fs->fs_path);
482 CloseHandle(listen_handle);
483 exit(0);
486 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
488 mutex_string = ap_psprintf(tp, "_FCGI_MUTEX_=%ld", mutex);
490 else
492 listen_handle = (HANDLE) fs->listenFd;
495 memset(&si, 0, sizeof(si));
496 memset(&pi, 0, sizeof(pi));
497 memset(&r, 0, sizeof(r));
499 // Can up a fake request to pass to ap_get_win32_interpreter()
500 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
501 r.server = fcgi_apache_main_server;
502 r.filename = (char *) fs->fs_path;
503 r.pool = tp;
505 fileType = ap_get_win32_interpreter(&r, &interpreter);
507 if (fileType == eFileTypeUNKNOWN) {
508 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, fcgi_apache_main_server,
509 "FastCGI: %s is not executable; ensure interpreted scripts have "
510 "\"#!\" as their first line",
511 fs->fs_path);
512 ap_destroy_pool(tp);
513 return (pid);
517 * We have the interpreter (if there is one) and we have
518 * the arguments (if there are any).
519 * Build the command string to pass to CreateProcess.
521 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
522 if (interpreter && *interpreter) {
523 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
525 else {
526 pCommand = quoted_filename;
530 * Make child process use hPipeOutputWrite as standard out,
531 * and make sure it does not show on screen.
533 si.cb = sizeof(si);
534 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
535 si.wShowWindow = SW_HIDE;
536 si.hStdInput = listen_handle;
538 // XXX These should be open to the error_log
539 si.hStdOutput = INVALID_HANDLE_VALUE;
540 si.hStdError = INVALID_HANDLE_VALUE;
543 * Win32's CreateProcess call requires that the environment
544 * be passed in an environment block, a null terminated block of
545 * null terminated strings.
547 i = 0;
548 iEnvBlockLen = 1;
549 while (fs->envp[i]) {
550 iEnvBlockLen += strlen(fs->envp[i]) + 1;
551 i++;
554 if (fs->socket_path)
556 iEnvBlockLen += strlen(mutex_string) + 1;
559 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
561 i = 0;
562 pNext = pEnvBlock;
563 while (fs->envp[i]) {
564 strcpy(pNext, fs->envp[i]);
565 pNext = pNext + strlen(pNext) + 1;
566 i++;
569 if (fs->socket_path)
571 strcpy(pNext, mutex_string);
574 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
576 pEnvBlock,
577 ap_make_dirstr_parent(tp, fs->fs_path),
578 &si, &pi)) {
579 if (fileType == eFileTypeEXE16) {
580 /* Hack to get 16-bit CGI's working. It works for all the
581 * standard modules shipped with Apache. pi.dwProcessId is 0
582 * for 16-bit CGIs and all the Unix specific code that calls
583 * ap_call_exec interprets this as a failure case. And we can't
584 * use -1 either because it is mapped to 0 by the caller.
586 pid = -2;
588 else {
589 pid = pi.dwProcessId;
590 process->handle = pi.hProcess;
591 CloseHandle(pi.hThread);
595 // We don't need these anymore..
596 if (fs->socket_path)
598 CloseHandle(listen_handle);
599 CloseHandle(mutex);
602 ap_destroy_pool(tp);
604 return pid;
606 #endif
609 #ifndef WIN32
610 static void reduce_priveleges(void)
612 char *name;
614 if (geteuid() != 0)
615 return;
617 #ifndef __EMX__
618 /* Get username if passed as a uid */
619 if (ap_user_name[0] == '#') {
620 uid_t uid = atoi(&ap_user_name[1]);
621 struct passwd *ent = getpwuid(uid);
623 if (ent == NULL) {
624 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
625 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
626 "you probably need to modify the User directive", (unsigned)uid);
627 exit(1);
629 name = ent->pw_name;
631 else
632 name = ap_user_name;
634 /* Change Group */
635 if (setgid(ap_group_id) == -1) {
636 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
637 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
638 exit(1);
641 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
643 /* Initialize supplementary groups */
644 if (initgroups(name, ap_group_id) == -1) {
645 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
646 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
647 name, (unsigned)ap_group_id);
648 exit(1);
650 #endif /* __EMX__ */
652 /* Change User */
653 if (fcgi_wrapper) {
654 if (seteuid_user() == -1) {
655 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
656 "FastCGI: process manager exiting, failed to reduce priveleges");
657 exit(1);
660 else {
661 if (setuid(ap_user_id) == -1) {
662 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
663 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
664 exit(1);
669 /*************
670 * Change the name of this process - best we can easily.
672 static void change_process_name(const char * const name)
674 strncpy(ap_server_argv0, name, strlen(ap_server_argv0));
676 #endif
678 static void schedule_start(fcgi_server *s, int proc)
680 /* If we've started one recently, don't register another */
681 time_t time_passed = now - s->restartTime;
683 if ((s->procs[proc].pid && time_passed < (int) s->restartDelay)
684 || (s->procs[proc].pid == 0 && time_passed < (int) s->initStartDelay))
686 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);
687 return;
690 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
691 s->procs[proc].state = STATE_NEEDS_STARTING;
692 if (proc == (int)dynamicMaxClassProcs - 1) {
693 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
694 "FastCGI: scheduled the %sstart of the last (dynamic) server "
695 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
696 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
701 *----------------------------------------------------------------------
703 * dynamic_read_msgs
705 * Removes the records written by request handlers and decodes them.
706 * We also update the data structures to reflect the changes.
708 *----------------------------------------------------------------------
711 static void dynamic_read_msgs(int read_ready)
713 fcgi_server *s;
715 #ifndef WIN32
716 int rc;
717 static int buflen = 0;
718 static char buf[FCGI_MSGS_BUFSIZE + 1];
719 char *ptr1, *ptr2, opcode;
720 char execName[FCGI_MAXPATH + 1];
721 char user[MAX_USER_NAME_LEN + 2];
722 char group[MAX_GID_CHAR_LEN + 1];
723 unsigned long q_usec = 0UL, req_usec = 0UL;
724 #else
725 fcgi_pm_job *joblist = NULL;
726 fcgi_pm_job *cjob = NULL;
727 #endif
729 pool *sp, *tp;
731 #ifndef WIN32
732 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
733 #endif
736 * To prevent the idle application from running indefinitely, we
737 * check the timer and if it is expired, we recompute the values
738 * for each running application class. Then, when REQ_COMPLETE
739 * message is recieved, only updates are made to the data structures.
741 if (fcgi_dynamic_last_analyzed == 0) {
742 fcgi_dynamic_last_analyzed = now;
744 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
745 for (s = fcgi_servers; s != NULL; s = s->next) {
746 if (s->directive != APP_CLASS_DYNAMIC)
747 break;
748 /* XXX what does this adjustment do? */
749 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
750 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
751 s->totalConnTime = 0UL;
752 s->totalQueueTime = 0UL;
756 if (read_ready <= 0) {
757 return;
760 #ifndef WIN32
761 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
762 if (rc <= 0) {
763 if (!caughtSigTerm) {
764 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
765 "FastCGI: read() from pipe failed (%d)", rc);
766 if (rc == 0) {
767 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
768 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
769 caughtSigTerm = TRUE;
772 return;
774 buflen += rc;
775 buf[buflen] = '\0';
776 #else
777 if (ap_acquire_mutex(fcgi_dynamic_mbox_mutex) != MULTI_OK) {
778 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
779 "FastCGI: Failed to aquire the dynamic mbox mutex!");
782 joblist = fcgi_dynamic_mbox;
783 fcgi_dynamic_mbox = NULL;
785 if (! ap_release_mutex(fcgi_dynamic_mbox_mutex)) {
786 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
787 "FastCGI: Failed to release the dynamic mbox mutex!");
790 cjob = joblist;
791 #endif
793 tp = ap_make_sub_pool(fcgi_config_pool);
795 #ifndef WIN32
796 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
797 int scan_failed = 0;
799 ptr2 = strchr(ptr1, '*');
800 if (ptr2) {
801 *ptr2++ = '\0';
803 else {
804 break;
807 opcode = *ptr1;
809 switch (opcode) {
810 case PLEASE_START:
811 if (sscanf(ptr1, "%c %s %16s %15s",
812 &opcode, execName, user, group) != 4)
814 scan_failed = 1;
816 break;
817 case CONN_TIMEOUT:
818 if (sscanf(ptr1, "%c %s %16s %15s",
819 &opcode, execName, user, group) != 4)
821 scan_failed = 1;
823 break;
824 case REQ_COMPLETE:
825 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
826 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
828 scan_failed = 1;
830 break;
831 default:
832 scan_failed = 1;
833 break;
836 if (scan_failed) {
837 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
838 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
839 goto NextJob;
841 #else
842 /* Update data structures for processing */
843 while (cjob != NULL) {
844 joblist = cjob->next;
845 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
846 #endif
848 #ifndef WIN32
849 s = fcgi_util_fs_get(execName, user, group);
850 #else
851 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
852 #endif
854 #ifndef WIN32
855 if (s==NULL && opcode != REQ_COMPLETE)
856 #else
857 if (s==NULL && cjob->id != REQ_COMPLETE)
858 #endif
860 #ifndef WIN32
861 int fd;
862 const char *err, *lockPath;
863 #endif
865 /* Create a perm subpool to hold the new server data,
866 * we can destroy it if something doesn't pan out */
867 sp = ap_make_sub_pool(fcgi_config_pool);
869 /* Create a new "dynamic" server */
870 s = fcgi_util_fs_new(sp);
871 s->directive = APP_CLASS_DYNAMIC;
872 s->restartDelay = dynamicRestartDelay;
873 s->listenQueueDepth = dynamicListenQueueDepth;
874 s->initStartDelay = dynamicInitStartDelay;
875 s->envp = dynamicEnvp;
876 #ifdef WIN32
877 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
878 #else
879 s->fs_path = ap_pstrdup(sp, execName);
880 #endif
881 ap_getparents(s->fs_path);
882 ap_no2slash(s->fs_path);
883 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
885 #ifndef WIN32
886 /* Create socket file's path */
887 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
888 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
890 /* Create sockaddr, prealloc it so it won't get created in tp */
891 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
892 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
893 &s->socket_addr_len, s->socket_path);
894 if (err) {
895 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
896 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
897 goto BagNewServer;
900 if (init_listen_sock(s)) {
901 goto BagNewServer;
904 /* Create the lock file */
905 lockPath = fcgi_util_socket_get_lock_filename(tp, s->socket_path);
906 fd = ap_popenf(tp, lockPath,
907 O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
908 if (fd < 0) {
909 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
910 "FastCGI: can't create (dynamic) server \"%s\": can't open lock file \"%s\": popenf() failed",
911 execName, lockPath);
912 goto BagNewServer;
914 ap_pclosef(tp, fd);
916 /* If a wrapper is being used, config user/group info */
917 if (fcgi_wrapper) {
918 if (user[0] == '~') {
919 /* its a user dir uri, the rest is a username, not a uid */
920 struct passwd *pw = getpwnam(&user[1]);
922 if (!pw) {
923 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
924 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
925 execName, &user[1]);
926 goto BagNewServer;
928 s->uid = pw->pw_uid;
929 s->user = ap_pstrdup(sp, user);
930 s->username = s->user;
932 s->gid = pw->pw_gid;
933 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
935 else {
936 struct passwd *pw;
938 s->uid = (uid_t)atol(user);
939 pw = getpwuid(s->uid);
940 if (!pw) {
941 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
942 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
943 execName, (long)s->uid);
944 goto BagNewServer;
946 s->user = ap_pstrdup(sp, user);
947 s->username = ap_pstrdup(sp, pw->pw_name);
949 s->gid = (gid_t)atol(group);
950 s->group = ap_pstrdup(sp, group);
953 #else
954 /* Create socket file's path */
955 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
956 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
957 s->listenFd = 0;
959 /* Create the application lock */
960 if ((s->dynamic_lock = fcgi_rdwr_create()) == NULL) {
961 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
962 "FastCGI: can;t create (dynamic) server \"%s\": fcgi_rdwr_create() failed",
963 cjob->fs_path);
964 goto BagNewServer;
967 // Lock it until the first process is started
968 fcgi_rdwr_lock(s->dynamic_lock, WRITER);
969 #endif
971 fcgi_util_fs_add(s);
973 else {
974 #ifndef WIN32
975 if (opcode == PLEASE_START) {
976 #else
977 if(cjob->id==PLEASE_START) {
978 #endif
979 if (dynamicAutoUpdate) {
980 /* Check to see if the binary has changed. If so,
981 * kill the FCGI application processes, and
982 * restart them.
984 struct stat stbuf;
985 unsigned int i;
987 #ifndef WIN32
988 if ((stat(execName, &stbuf)==0) &&
989 #else
990 if ((stat(cjob->fs_path, &stbuf)==0) &&
991 #endif
992 (stbuf.st_mtime > s->restartTime)) {
993 /* kill old server(s) */
994 for (i = 0; i < dynamicMaxClassProcs; i++) {
995 if (s->procs[i].pid > 0) {
996 fcgi_kill(&s->procs[i], SIGTERM);
1000 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1001 "FastCGI: restarting server \"%s\" processes, newer version found",
1002 #ifndef WIN32
1003 execName);
1004 #else
1005 cjob->fs_path);
1006 #endif
1009 /* If dynamicAutoRestart, don't mark any new processes
1010 * for starting because we probably got the
1011 * PLEASE_START due to dynamicAutoUpdate and the ProcMgr
1012 * will be restarting all of those we just killed.
1014 if (dynamicAutoRestart)
1015 goto NextJob;
1016 } else {
1017 /* we've been asked to start a process--only start
1018 * it if we're not already running at least one
1019 * instance.
1021 unsigned int i;
1023 for (i = 0; i < dynamicMaxClassProcs; i++) {
1024 if (s->procs[i].state == STATE_STARTED)
1025 break;
1027 /* if already running, don't start another one */
1028 if (i < dynamicMaxClassProcs) {
1029 goto NextJob;
1034 #ifndef WIN32
1035 switch (opcode)
1036 #else
1037 switch (cjob->id)
1038 #endif
1040 unsigned int i;
1042 case PLEASE_START:
1043 case CONN_TIMEOUT:
1045 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1047 * Extra instances should have been
1048 * terminated beforehand, probably need
1049 * to increase ProcessSlack parameter
1051 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1052 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1053 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1054 goto NextJob;
1057 /* find next free slot */
1058 for (i = 0; i < dynamicMaxClassProcs; i++)
1060 if (s->procs[i].state == STATE_NEEDS_STARTING)
1062 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1063 break;
1065 else if (s->procs[i].state == STATE_STARTED)
1067 continue;
1070 schedule_start(s, i);
1071 break;
1074 #ifdef FCGI_DEBUG
1075 if (i >= dynamicMaxClassProcs) {
1076 FCGIDBG1("ignore_job: slots are max'd");
1078 #endif
1079 break;
1080 case REQ_COMPLETE:
1081 /* only record stats if we have a structure */
1082 if (s) {
1083 #ifndef WIN32
1084 s->totalConnTime += req_usec;
1085 s->totalQueueTime += q_usec;
1086 #else
1087 s->totalConnTime += cjob->start_time;
1088 s->totalQueueTime += cjob->qsec;
1089 #endif
1091 break;
1094 NextJob:
1096 #ifdef WIN32
1097 /* Cleanup job data */
1098 free(cjob->fs_path);
1099 free(cjob->user);
1100 free(cjob->group);
1101 free(cjob);
1102 cjob = joblist;
1103 #endif
1105 continue;
1107 BagNewServer:
1108 ap_destroy_pool(sp);
1110 #ifdef WIN32
1111 free(cjob->fs_path);
1112 free(cjob);
1113 cjob = joblist;
1114 #endif
1117 #ifndef WIN32
1118 if (ptr1 == buf) {
1119 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1120 "FastCGI: really bogus message: \"%s\"", ptr1);
1121 ptr1 += strlen(buf);
1124 buflen -= ptr1 - buf;
1125 if (buflen) {
1126 memmove(buf, ptr1, buflen);
1128 #endif
1130 ap_destroy_pool(tp);
1134 *----------------------------------------------------------------------
1136 * dynamic_kill_idle_fs_procs
1138 * Implement a kill policy for the dynamic FastCGI applications.
1139 * We also update the data structures to reflect the changes.
1141 * Side effects:
1142 * Processes are marked for deletion possibly killed.
1144 *----------------------------------------------------------------------
1146 static void dynamic_kill_idle_fs_procs(void)
1148 fcgi_server *s;
1149 struct FuncData *funcData = NULL;
1150 unsigned long connTime; /* server's smoothed running time, or
1151 * if that's 0, the current total */
1152 unsigned long totalTime; /* maximum number of microseconds that all
1153 * of a server's running processes together
1154 * could have spent running since the
1155 * last check */
1156 double loadFactor; /* percentage, 0-100, of totalTime that
1157 * the processes actually used */
1158 unsigned int i, victims = 0;
1159 #ifndef WIN32
1160 const char *lockFileName;
1161 int lockFd;
1162 pid_t pid;
1163 #endif
1164 pool *tp = ap_make_sub_pool(fcgi_config_pool);
1166 /* pass 1 - locate and mark all victims */
1167 for(s=fcgi_servers; s!=NULL; s=s->next) {
1168 /* Only kill dynamic apps */
1169 if (s->directive != APP_CLASS_DYNAMIC)
1170 continue;
1172 /* If the number of non-victims is less than or equal to
1173 the minimum that may be running without being killed off,
1174 don't select any more victims. */
1175 if ((fcgi_dynamic_total_proc_count - victims) <= (int) dynamicMinProcs) {
1176 break;
1179 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1180 totalTime = (s->numProcesses)*(now - fcgi_dynamic_epoch)*1000000 + 1;
1182 /* XXX producing a heavy load with one client, I haven't been
1183 able to achieve a loadFactor greater than 0.5. Perhaps this
1184 should be scaled up by another order of magnitude or two. */
1185 loadFactor = connTime/totalTime*100.0;
1187 if ((s->numProcesses > 1
1188 && s->numProcesses/(s->numProcesses - 1)*loadFactor < dynamicThreshholdN)
1189 || (s->numProcesses == 1 && loadFactor < dynamicThreshhold1))
1191 int got_one = 0;
1193 for (i = 0; !got_one && i < dynamicMaxClassProcs; ++i) {
1194 if (s->procs[i].state == STATE_NEEDS_STARTING) {
1195 s->procs[i].state = STATE_READY;
1196 got_one = 1;
1198 else if (s->procs[i].state == STATE_VICTIM || s->procs[i].state == STATE_KILL) {
1199 got_one = 1;
1203 for (i = 0; !got_one && i < dynamicMaxClassProcs; ++i) {
1204 if (s->procs[i].state == STATE_STARTED) {
1205 s->procs[i].state = STATE_KILL;
1206 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1207 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination scheduled",
1208 s->fs_path, s->procs[i].pid);
1209 victims++;
1210 got_one = 1;
1216 /* pass 2 - kill procs off */
1217 for(s=fcgi_servers; s!=NULL; s=s->next) {
1218 /* Only kill dynamic apps */
1219 if (s->directive != APP_CLASS_DYNAMIC)
1220 continue;
1222 for(i = 0; i < dynamicMaxClassProcs; i++) {
1223 if (s->procs[i].state == STATE_KILL) {
1224 #ifndef WIN32
1225 lockFileName = fcgi_util_socket_get_lock_filename(tp, s->socket_path);
1226 if ((lockFd = ap_popenf(tp, lockFileName, O_RDWR, 0))<0) {
1228 * If we need to kill an application and the
1229 * corresponding lock file does not exist, then
1230 * that means we are in big trouble here
1232 /*@@@ this should be logged, but since all the lock
1233 * file stuff will be tossed, I'll leave it now */
1234 ap_pclosef(tp, lockFd);
1235 continue;
1238 if (fcgi_get_exclusive_write_lock_no_wait(lockFd) < 0) {
1239 #else
1240 if (fcgi_get_exclusive_write_lock_no_wait(s->dynamic_lock) < 0) {
1241 #endif
1242 FCGIDBG2("fcgi_get_exclusive_write_lock_no_wait() failed (%ld)", GetLastError());
1244 * Unable to lock the lockfile, indicative
1245 * of WS performing operation with the given
1246 * application class. The simplest solution
1247 * is to spawn off another process and block
1248 * on lock to kill it. This is under assumptions
1249 * that fork() is not very costly and this
1250 * situation occurs very rarely, which it should
1252 #ifndef WIN32
1253 funcData = ap_pcalloc(tp, sizeof(struct FuncData));
1254 funcData->lockFileName = lockFileName;
1255 #else
1256 funcData = malloc(sizeof(struct FuncData));
1257 funcData->lock = s->dynamic_lock;
1258 #endif
1259 funcData->process = &s->procs[i];
1261 #ifndef WIN32
1262 if((pid=fork())<0) {
1263 /*@@@ this should be logged, but since all the lock
1264 * file stuff will be tossed, I'll leave it now */
1265 ap_pclosef(tp, lockFd);
1266 continue;
1267 } else if(pid==0) {
1268 /* child */
1270 /* rename the process for ps - best we can easily */
1271 change_process_name("fcgiBlkKill");
1273 dynamic_blocking_kill(funcData);
1274 } else {
1275 /* parent */
1276 s->procs[i].state = STATE_VICTIM;
1277 ap_pclosef(tp, lockFd);
1279 #else
1280 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) dynamic_blocking_kill, (LPVOID) funcData, 0, NULL);
1281 s->procs[i].state = STATE_VICTIM;
1282 #endif
1284 else {
1285 FCGIDBG1("fcgi_get_exclusive_write_lock_no_wait() succeeded");
1286 s->procs[i].state = STATE_VICTIM;
1287 #ifndef WIN32
1288 fcgi_kill(&s->procs[i], SIGTERM);
1289 ap_pclosef(tp, lockFd);
1290 #else
1291 fcgi_kill(&s->procs[i], 1);
1292 fcgi_rdwr_unlock(s->dynamic_lock, WRITER);
1293 #endif
1298 ap_destroy_pool(tp);
1301 #ifdef WIN32
1303 // This is a little bogus, there's gotta be a better way to do this
1304 // Can we use WaitForMultipleObjects()
1305 #define FCGI_PROC_WAIT_TIME 100
1307 void child_wait_thread(void *dummy) {
1308 fcgi_server *s;
1309 DWORD dwRet = WAIT_TIMEOUT;
1310 int numChildren;
1311 int i;
1312 int waited;
1314 while (!bTimeToDie) {
1315 waited = 0;
1317 for (s = fcgi_servers; s != NULL; s = s->next) {
1318 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1319 continue;
1321 if (s->directive == APP_CLASS_DYNAMIC) {
1322 numChildren = dynamicMaxClassProcs;
1324 else {
1325 numChildren = s->numProcesses;
1328 for (i=0; i < numChildren; i++) {
1329 if (s->procs[i].handle != INVALID_HANDLE_VALUE) {
1330 /* timeout is currently set for 100 miliecond */
1331 /* it may need t longer or user customizable */
1332 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1334 waited = 1;
1336 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1337 /* a child fs has died */
1338 /* mark the child as dead */
1340 if (s->directive == APP_CLASS_STANDARD) {
1341 /* restart static app */
1342 s->procs[i].state = STATE_NEEDS_STARTING;
1343 s->numFailures++;
1345 else {
1346 s->numProcesses--;
1347 fcgi_dynamic_total_proc_count--;
1348 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1350 if (s->procs[i].state == STATE_VICTIM) {
1351 s->procs[i].state = STATE_KILLED;
1353 else {
1354 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1355 s->numFailures++;
1357 if (dynamicAutoRestart) {
1358 s->procs[i].state = STATE_NEEDS_STARTING;
1360 else {
1361 s->procs[i].state = STATE_READY;
1366 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1367 "FastCGI:%s server \"%s\" (pid %d) terminated",
1368 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1369 s->fs_path, s->procs[i].pid);
1371 CloseHandle(s->procs[i].handle);
1372 s->procs[i].handle = INVALID_HANDLE_VALUE;
1373 s->procs[i].pid = -1;
1375 /* wake up the main thread */
1376 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1381 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1384 #endif
1386 #ifndef WIN32
1387 static void setup_signals(void)
1389 sigset_t mask;
1390 struct sigaction sa;
1392 /* Ignore USR2 */
1393 sigemptyset(&mask);
1394 sigaddset(&mask, SIGUSR2);
1395 sigprocmask(SIG_BLOCK, &mask, NULL);
1397 /* Setup handlers */
1399 sa.sa_handler = signal_handler;
1400 sigemptyset(&sa.sa_mask);
1401 sa.sa_flags = 0;
1403 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1404 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1405 "sigaction(SIGTERM) failed");
1407 /* httpd restart */
1408 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1409 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1410 "sigaction(SIGHUP) failed");
1412 /* httpd graceful restart */
1413 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1414 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1415 "sigaction(SIGUSR1) failed");
1417 /* read messages from request handlers - kill interval expired */
1418 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1419 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1420 "sigaction(SIGALRM) failed");
1422 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1423 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1424 "sigaction(SIGCHLD) failed");
1427 #endif
1429 #ifndef WIN32
1430 int fcgi_pm_main(void *dummy, child_info *info)
1431 #else
1432 void fcgi_pm_main(void *dummy)
1433 #endif
1435 fcgi_server *s;
1436 unsigned int i;
1437 int read_ready = 0;
1438 int alarmLeft = 0;
1440 #ifdef WIN32
1441 DWORD dwRet;
1442 int first_time = 1;
1443 HANDLE wait_thread = INVALID_HANDLE_VALUE;
1444 #else
1445 int callWaitPid, callDynamicProcs;
1446 #endif
1448 #ifdef WIN32
1449 // Add SystemRoot to the dyanmic environment
1450 char ** envp = dynamicEnvp;
1451 for (i = 0; *envp; ++i) {
1452 ++envp;
1454 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1456 #else
1457 reduce_priveleges();
1459 close(fcgi_pm_pipe[1]);
1460 change_process_name("fcgi-pm");
1461 setup_signals();
1463 if (fcgi_wrapper) {
1464 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1465 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1467 #endif
1469 /* Initialize AppClass */
1470 for (s = fcgi_servers; s != NULL; s = s->next)
1472 if (s->directive != APP_CLASS_STANDARD)
1473 continue;
1475 #ifdef WIN32
1476 if (s->socket_path)
1477 s->listenFd = 0;
1478 #endif
1480 for (i = 0; i < s->numProcesses; ++i)
1481 s->procs[i].state = STATE_NEEDS_STARTING;
1484 #ifdef WIN32
1485 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1486 "FastCGI: process manager initialized");
1487 #else
1488 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1489 "FastCGI: process manager initialized (pid %ld)", (long)getpid());
1490 #endif
1492 now = time(NULL);
1495 * Loop until SIGTERM
1497 for (;;) {
1498 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1499 #ifdef WIN32
1500 time_t expire;
1501 #else
1502 pid_t childPid;
1503 int waitStatus;
1504 #endif
1505 unsigned int numChildren;
1508 * If we came out of sigsuspend() for any reason other than
1509 * SIGALRM, pick up where we left off.
1511 if (alarmLeft)
1512 sleepSeconds = alarmLeft;
1515 * Examine each configured AppClass for a process that needs
1516 * starting. Compute the earliest time when the start should
1517 * be attempted, starting it now if the time has passed. Also,
1518 * remember that we do NOT need to restart externally managed
1519 * FastCGI applications.
1521 for (s = fcgi_servers; s != NULL; s = s->next)
1523 if (s->directive == APP_CLASS_EXTERNAL)
1524 continue;
1526 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1527 ? dynamicMaxClassProcs
1528 : s->numProcesses;
1530 for (i = 0; i < numChildren; ++i)
1532 if (s->procs[i].pid <= 0 && s->procs[i].state == STATE_NEEDS_STARTING)
1534 int restart = (s->procs[i].pid < 0);
1535 time_t restartTime = s->restartTime;
1537 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1539 if (restartTime <= now)
1541 s->restartTime = now;
1543 if (s->listenFd < 0 && init_listen_sock(s))
1545 if (sleepSeconds > s->initStartDelay)
1546 sleepSeconds = s->initStartDelay;
1547 break;
1550 #ifndef WIN32
1551 if (caughtSigTerm) {
1552 goto ProcessSigTerm;
1554 #endif
1555 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1556 if (s->procs[i].pid <= 0) {
1557 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1558 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1559 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1560 s->fs_path);
1562 sleepSeconds = min(sleepSeconds,
1563 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1565 ap_assert(s->procs[i].pid < 0);
1566 break;
1569 if (s->directive == APP_CLASS_DYNAMIC) {
1570 s->numProcesses++;
1571 fcgi_dynamic_total_proc_count++;
1572 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1573 #ifdef WIN32
1574 if (i == 0 && !restart) {
1575 fcgi_rdwr_unlock(s->dynamic_lock, WRITER);
1577 #endif
1580 s->procs[i].state = STATE_STARTED;
1582 if (restart)
1583 s->numRestarts++;
1585 if (fcgi_wrapper) {
1586 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1587 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1588 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1589 s->fs_path, (long)s->uid, (long)s->gid,
1590 restart ? "re" : "", (long)s->procs[i].pid);
1592 else {
1593 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1594 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1595 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1596 s->fs_path, restart ? "re" : "", (long)s->procs[i].pid);
1598 ap_assert(s->procs[i].pid > 0);
1599 } else {
1600 sleepSeconds = min(sleepSeconds, restartTime - now);
1606 #ifndef WIN32
1607 if(caughtSigTerm) {
1608 goto ProcessSigTerm;
1610 if((!caughtSigChld) && (!caughtSigUsr2)) {
1611 fd_set rfds;
1613 alarm(sleepSeconds);
1615 FD_ZERO(&rfds);
1616 FD_SET(fcgi_pm_pipe[0], &rfds);
1617 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1619 alarmLeft = alarm(0);
1621 callWaitPid = caughtSigChld;
1622 caughtSigChld = FALSE;
1623 callDynamicProcs = caughtSigUsr2;
1624 caughtSigUsr2 = FALSE;
1626 now = time(NULL);
1629 * Dynamic fcgi process management
1631 if((callDynamicProcs) || (!callWaitPid)) {
1632 dynamic_read_msgs(read_ready);
1633 if(fcgi_dynamic_epoch == 0) {
1634 fcgi_dynamic_epoch = now;
1636 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1637 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1638 dynamic_kill_idle_fs_procs();
1639 fcgi_dynamic_epoch = now;
1643 if(!callWaitPid) {
1644 continue;
1647 /* We've caught SIGCHLD, so find out who it was using waitpid,
1648 * write a log message and update its data structure. */
1650 for (;;) {
1651 if (caughtSigTerm)
1652 goto ProcessSigTerm;
1654 childPid = waitpid(-1, &waitStatus, WNOHANG);
1656 if (childPid == -1 || childPid == 0)
1657 break;
1659 for (s = fcgi_servers; s != NULL; s = s->next) {
1660 if (s->directive == APP_CLASS_EXTERNAL)
1661 continue;
1663 if (s->directive == APP_CLASS_DYNAMIC)
1664 numChildren = dynamicMaxClassProcs;
1665 else
1666 numChildren = s->numProcesses;
1668 for (i = 0; i < numChildren; i++) {
1669 if (s->procs[i].pid == childPid)
1670 goto ChildFound;
1674 /* @@@ This (comment) needs to go away when dynamic gets cleaned up.
1675 * If we get to this point, we have detected the
1676 * termination of the process that was spawned off by
1677 * the process manager to do a blocking kill above. */
1678 continue;
1680 ChildFound:
1681 s->procs[i].pid = -1;
1683 if (s->directive == APP_CLASS_STANDARD) {
1684 /* Always restart static apps */
1685 s->procs[i].state = STATE_NEEDS_STARTING;
1686 s->numFailures++;
1688 else {
1689 s->numProcesses--;
1690 fcgi_dynamic_total_proc_count--;
1692 if (s->procs[i].state == STATE_VICTIM) {
1693 s->procs[i].state = STATE_KILLED;
1695 else {
1696 /* A dynamic app died or exited without provacation from the PM */
1697 s->numFailures++;
1699 if (dynamicAutoRestart || s->numProcesses <= 0)
1700 s->procs[i].state = STATE_NEEDS_STARTING;
1701 else
1702 s->procs[i].state = STATE_READY;
1706 if (WIFEXITED(waitStatus)) {
1707 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1708 "FastCGI:%s server \"%s\" (pid %d) terminated by calling exit with status '%d'",
1709 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1710 s->fs_path, (int)childPid, WEXITSTATUS(waitStatus));
1712 else if (WIFSIGNALED(waitStatus)) {
1713 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1714 "FastCGI:%s server \"%s\" (pid %d) terminated due to uncaught signal '%d' (%s)%s",
1715 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1716 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)],
1717 #ifdef WCOREDUMP
1718 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1719 #else
1720 "");
1721 #endif
1723 else if (WIFSTOPPED(waitStatus)) {
1724 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1725 "FastCGI:%s server \"%s\" (pid %d) stopped due to uncaught signal '%d' (%s)",
1726 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1727 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)]);
1729 } /* for (;;), waitpid() */
1730 #else
1731 if (first_time) {
1732 /* Start the child wait thread */
1733 wait_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)child_wait_thread, NULL, 0, NULL);
1734 first_time = 0;
1737 /* wait for an event to occur or timer expires */
1738 expire = time(NULL) + sleepSeconds;
1739 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1741 if (dwRet == WAIT_FAILED) {
1742 /* There is something seriously wrong here */
1743 ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, fcgi_apache_main_server,
1744 "FastDGI: WaitForMultipleObjects on event handles -- pm is shuting down");
1745 bTimeToDie = TRUE;
1748 if (dwRet != WAIT_TIMEOUT) {
1749 now = time(NULL);
1751 if (now < expire)
1752 alarmLeft = expire - now;
1756 * Dynamic fcgi process management
1758 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1759 if (dwRet == MBOX_EVENT) {
1760 read_ready = 1;
1763 now = time(NULL);
1765 dynamic_read_msgs(read_ready);
1767 if(fcgi_dynamic_epoch == 0) {
1768 fcgi_dynamic_epoch = now;
1771 if (((long)(now-fcgi_dynamic_epoch) >= (int)dynamicKillInterval) ||
1772 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1773 dynamic_kill_idle_fs_procs();
1774 fcgi_dynamic_epoch = now;
1776 read_ready = 0;
1778 else if (dwRet == WAKE_EVENT) {
1779 continue;
1781 else if (dwRet == TERM_EVENT) {
1782 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1783 "FastCGI: Termination event received process manager shutting down");
1784 bTimeToDie = TRUE;
1786 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1787 goto ProcessSigTerm;
1789 else {
1790 // Have an received an unknown event - should not happen
1791 ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, fcgi_apache_main_server,
1792 "FastCGI: WaitForMultipleobjects return an unrecognized event");
1793 bTimeToDie = TRUE;
1794 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1795 goto ProcessSigTerm;
1797 #endif
1798 } /* for (;;), the whole shoot'n match */
1800 ProcessSigTerm:
1802 * Kill off the children, then exit.
1804 while (fcgi_servers != NULL) {
1805 kill_fs_procs(fcgi_config_pool, fcgi_servers);
1808 #ifdef WIN32
1809 return;
1810 #else
1811 exit(0);
1812 #endif
1815 #ifdef WIN32
1816 int fcgi_pm_add_job(fcgi_pm_job *new_job) {
1818 if (new_job == NULL)
1819 return 0;
1821 if (ap_acquire_mutex(fcgi_dynamic_mbox_mutex) != MULTI_OK) {
1822 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1823 "FastCGI: Failed to aquire the dynamic mbox mutex!");
1826 new_job->next = fcgi_dynamic_mbox;
1827 fcgi_dynamic_mbox = new_job;
1829 if (! ap_release_mutex(fcgi_dynamic_mbox_mutex)) {
1830 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1831 "FastCGI: Failed to release the dynamic mbox mutex!");
1834 return 1;
1836 #endif