Add log macros for handling Win32 errors.
[mod_fastcgi.git] / fcgi_pm.c
blobd9e3885e70c0547812d618d1f9c7f8202b1737de
1 /*
2 * $Id: fcgi_pm.c,v 1.50 2001/03/05 14:20:20 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_privileges(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 privileges");
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 received, 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;
749 /* Advance the last analyzed timestamp by the elapsed time since
750 * it was last set. Round the increase down to the nearest
751 * multiple of dynamicUpdateInterval */
753 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
754 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
755 s->totalConnTime = 0UL;
756 s->totalQueueTime = 0UL;
760 if (read_ready <= 0) {
761 return;
764 #ifndef WIN32
765 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
766 if (rc <= 0) {
767 if (!caughtSigTerm) {
768 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
769 "FastCGI: read() from pipe failed (%d)", rc);
770 if (rc == 0) {
771 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
772 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
773 caughtSigTerm = TRUE;
776 return;
778 buflen += rc;
779 buf[buflen] = '\0';
780 #else
781 if (ap_acquire_mutex(fcgi_dynamic_mbox_mutex) != MULTI_OK) {
782 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
783 "FastCGI: Failed to aquire the dynamic mbox mutex!");
786 joblist = fcgi_dynamic_mbox;
787 fcgi_dynamic_mbox = NULL;
789 if (! ap_release_mutex(fcgi_dynamic_mbox_mutex)) {
790 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
791 "FastCGI: Failed to release the dynamic mbox mutex!");
794 cjob = joblist;
795 #endif
797 tp = ap_make_sub_pool(fcgi_config_pool);
799 #ifndef WIN32
800 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
801 int scan_failed = 0;
803 ptr2 = strchr(ptr1, '*');
804 if (ptr2) {
805 *ptr2++ = '\0';
807 else {
808 break;
811 opcode = *ptr1;
813 switch (opcode) {
814 case PLEASE_START:
815 if (sscanf(ptr1, "%c %s %16s %15s",
816 &opcode, execName, user, group) != 4)
818 scan_failed = 1;
820 break;
821 case CONN_TIMEOUT:
822 if (sscanf(ptr1, "%c %s %16s %15s",
823 &opcode, execName, user, group) != 4)
825 scan_failed = 1;
827 break;
828 case REQ_COMPLETE:
829 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
830 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
832 scan_failed = 1;
834 break;
835 default:
836 scan_failed = 1;
837 break;
840 if (scan_failed) {
841 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
842 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
843 goto NextJob;
845 #else
846 /* Update data structures for processing */
847 while (cjob != NULL) {
848 joblist = cjob->next;
849 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
850 #endif
852 #ifndef WIN32
853 s = fcgi_util_fs_get(execName, user, group);
854 #else
855 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
856 #endif
858 #ifndef WIN32
859 if (s==NULL && opcode != REQ_COMPLETE)
860 #else
861 if (s==NULL && cjob->id != REQ_COMPLETE)
862 #endif
864 #ifndef WIN32
865 int fd;
866 const char *err, *lockPath;
867 #endif
869 /* Create a perm subpool to hold the new server data,
870 * we can destroy it if something doesn't pan out */
871 sp = ap_make_sub_pool(fcgi_config_pool);
873 /* Create a new "dynamic" server */
874 s = fcgi_util_fs_new(sp);
875 s->directive = APP_CLASS_DYNAMIC;
876 s->restartDelay = dynamicRestartDelay;
877 s->listenQueueDepth = dynamicListenQueueDepth;
878 s->initStartDelay = dynamicInitStartDelay;
879 s->envp = dynamicEnvp;
880 #ifdef WIN32
881 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
882 #else
883 s->fs_path = ap_pstrdup(sp, execName);
884 #endif
885 ap_getparents(s->fs_path);
886 ap_no2slash(s->fs_path);
887 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
889 #ifndef WIN32
890 /* Create socket file's path */
891 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
892 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
894 /* Create sockaddr, prealloc it so it won't get created in tp */
895 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
896 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
897 &s->socket_addr_len, s->socket_path);
898 if (err) {
899 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
900 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
901 goto BagNewServer;
904 if (init_listen_sock(s)) {
905 goto BagNewServer;
908 /* Create the lock file */
909 lockPath = fcgi_util_socket_get_lock_filename(tp, s->socket_path);
910 fd = ap_popenf(tp, lockPath,
911 O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
912 if (fd < 0) {
913 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
914 "FastCGI: can't create (dynamic) server \"%s\": can't open lock file \"%s\": popenf() failed",
915 execName, lockPath);
916 goto BagNewServer;
918 ap_pclosef(tp, fd);
920 /* If a wrapper is being used, config user/group info */
921 if (fcgi_wrapper) {
922 if (user[0] == '~') {
923 /* its a user dir uri, the rest is a username, not a uid */
924 struct passwd *pw = getpwnam(&user[1]);
926 if (!pw) {
927 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
928 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
929 execName, &user[1]);
930 goto BagNewServer;
932 s->uid = pw->pw_uid;
933 s->user = ap_pstrdup(sp, user);
934 s->username = s->user;
936 s->gid = pw->pw_gid;
937 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
939 else {
940 struct passwd *pw;
942 s->uid = (uid_t)atol(user);
943 pw = getpwuid(s->uid);
944 if (!pw) {
945 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
946 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
947 execName, (long)s->uid);
948 goto BagNewServer;
950 s->user = ap_pstrdup(sp, user);
951 s->username = ap_pstrdup(sp, pw->pw_name);
953 s->gid = (gid_t)atol(group);
954 s->group = ap_pstrdup(sp, group);
957 #else
958 /* Create socket file's path */
959 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
960 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
961 s->listenFd = 0;
963 /* Create the application lock */
964 if ((s->dynamic_lock = fcgi_rdwr_create()) == NULL) {
965 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
966 "FastCGI: can;t create (dynamic) server \"%s\": fcgi_rdwr_create() failed",
967 cjob->fs_path);
968 goto BagNewServer;
971 // Lock it until the first process is started
972 fcgi_rdwr_lock(s->dynamic_lock, WRITER);
973 #endif
975 fcgi_util_fs_add(s);
977 else {
978 #ifndef WIN32
979 if (opcode == PLEASE_START) {
980 #else
981 if(cjob->id==PLEASE_START) {
982 #endif
983 if (dynamicAutoUpdate) {
984 /* Check to see if the binary has changed. If so,
985 * kill the FCGI application processes, and
986 * restart them.
988 struct stat stbuf;
989 unsigned int i;
991 #ifndef WIN32
992 if ((stat(execName, &stbuf)==0) &&
993 #else
994 if ((stat(cjob->fs_path, &stbuf)==0) &&
995 #endif
996 (stbuf.st_mtime > s->restartTime)) {
997 /* kill old server(s) */
998 for (i = 0; i < dynamicMaxClassProcs; i++) {
999 if (s->procs[i].pid > 0) {
1000 fcgi_kill(&s->procs[i], SIGTERM);
1004 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1005 "FastCGI: restarting server \"%s\" processes, newer version found",
1006 #ifndef WIN32
1007 execName);
1008 #else
1009 cjob->fs_path);
1010 #endif
1013 /* If dynamicAutoRestart, don't mark any new processes
1014 * for starting because we probably got the
1015 * PLEASE_START due to dynamicAutoUpdate and the ProcMgr
1016 * will be restarting all of those we just killed.
1018 if (dynamicAutoRestart)
1019 goto NextJob;
1020 } else {
1021 /* we've been asked to start a process--only start
1022 * it if we're not already running at least one
1023 * instance.
1025 unsigned int i;
1027 for (i = 0; i < dynamicMaxClassProcs; i++) {
1028 if (s->procs[i].state == STATE_STARTED)
1029 break;
1031 /* if already running, don't start another one */
1032 if (i < dynamicMaxClassProcs) {
1033 goto NextJob;
1038 #ifndef WIN32
1039 switch (opcode)
1040 #else
1041 switch (cjob->id)
1042 #endif
1044 unsigned int i;
1046 case PLEASE_START:
1047 case CONN_TIMEOUT:
1049 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1051 * Extra instances should have been
1052 * terminated beforehand, probably need
1053 * to increase ProcessSlack parameter
1055 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1056 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1057 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1058 goto NextJob;
1061 /* find next free slot */
1062 for (i = 0; i < dynamicMaxClassProcs; i++)
1064 if (s->procs[i].state == STATE_NEEDS_STARTING)
1066 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1067 break;
1069 else if (s->procs[i].state == STATE_STARTED)
1071 continue;
1074 schedule_start(s, i);
1075 break;
1078 #ifdef FCGI_DEBUG
1079 if (i >= dynamicMaxClassProcs) {
1080 FCGIDBG1("ignore_job: slots are max'd");
1082 #endif
1083 break;
1084 case REQ_COMPLETE:
1085 /* only record stats if we have a structure */
1086 if (s) {
1087 #ifndef WIN32
1088 s->totalConnTime += req_usec;
1089 s->totalQueueTime += q_usec;
1090 #else
1091 s->totalConnTime += cjob->start_time;
1092 s->totalQueueTime += cjob->qsec;
1093 #endif
1095 break;
1098 NextJob:
1100 #ifdef WIN32
1101 /* Cleanup job data */
1102 free(cjob->fs_path);
1103 free(cjob->user);
1104 free(cjob->group);
1105 free(cjob);
1106 cjob = joblist;
1107 #endif
1109 continue;
1111 BagNewServer:
1112 ap_destroy_pool(sp);
1114 #ifdef WIN32
1115 free(cjob->fs_path);
1116 free(cjob);
1117 cjob = joblist;
1118 #endif
1121 #ifndef WIN32
1122 if (ptr1 == buf) {
1123 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1124 "FastCGI: really bogus message: \"%s\"", ptr1);
1125 ptr1 += strlen(buf);
1128 buflen -= ptr1 - buf;
1129 if (buflen) {
1130 memmove(buf, ptr1, buflen);
1132 #endif
1134 ap_destroy_pool(tp);
1138 *----------------------------------------------------------------------
1140 * dynamic_kill_idle_fs_procs
1142 * Implement a kill policy for the dynamic FastCGI applications.
1143 * We also update the data structures to reflect the changes.
1145 * Side effects:
1146 * Processes are marked for deletion possibly killed.
1148 *----------------------------------------------------------------------
1150 static void dynamic_kill_idle_fs_procs(void)
1152 fcgi_server *s;
1153 struct FuncData *funcData = NULL;
1154 unsigned long connTime; /* server's smoothed running time, or
1155 * if that's 0, the current total */
1156 unsigned long totalTime; /* maximum number of microseconds that all
1157 * of a server's running processes together
1158 * could have spent running since the
1159 * last check */
1160 double loadFactor; /* percentage, 0-100, of totalTime that
1161 * the processes actually used */
1162 unsigned int i, victims = 0;
1163 #ifndef WIN32
1164 const char *lockFileName;
1165 int lockFd;
1166 pid_t pid;
1167 #endif
1168 pool *tp = ap_make_sub_pool(fcgi_config_pool);
1170 /* pass 1 - locate and mark all victims */
1171 for(s=fcgi_servers; s!=NULL; s=s->next) {
1172 /* Only kill dynamic apps */
1173 if (s->directive != APP_CLASS_DYNAMIC)
1174 continue;
1176 /* If the number of non-victims is less than or equal to
1177 the minimum that may be running without being killed off,
1178 don't select any more victims. */
1179 if ((fcgi_dynamic_total_proc_count - victims) <= (int) dynamicMinProcs) {
1180 break;
1183 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1184 totalTime = (s->numProcesses)*(now - fcgi_dynamic_epoch)*1000000 + 1;
1186 /* XXX producing a heavy load with one client, I haven't been
1187 able to achieve a loadFactor greater than 0.5. Perhaps this
1188 should be scaled up by another order of magnitude or two. */
1189 loadFactor = 100.0 * connTime / totalTime;
1191 if ((s->numProcesses > 1
1192 && s->numProcesses/(s->numProcesses - 1)*loadFactor < dynamicThresholdN)
1193 || (s->numProcesses == 1 && loadFactor < dynamicThreshold1))
1195 int got_one = 0;
1197 for (i = 0; !got_one && i < dynamicMaxClassProcs; ++i) {
1198 if (s->procs[i].state == STATE_NEEDS_STARTING) {
1199 s->procs[i].state = STATE_READY;
1200 got_one = 1;
1202 else if (s->procs[i].state == STATE_VICTIM || s->procs[i].state == STATE_KILL) {
1203 got_one = 1;
1207 for (i = 0; !got_one && i < dynamicMaxClassProcs; ++i) {
1208 if (s->procs[i].state == STATE_STARTED) {
1209 s->procs[i].state = STATE_KILL;
1210 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1211 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination scheduled",
1212 s->fs_path, s->procs[i].pid);
1213 victims++;
1214 got_one = 1;
1220 /* pass 2 - kill procs off */
1221 for(s=fcgi_servers; s!=NULL; s=s->next) {
1222 /* Only kill dynamic apps */
1223 if (s->directive != APP_CLASS_DYNAMIC)
1224 continue;
1226 for(i = 0; i < dynamicMaxClassProcs; i++) {
1227 if (s->procs[i].state == STATE_KILL) {
1228 #ifndef WIN32
1229 lockFileName = fcgi_util_socket_get_lock_filename(tp, s->socket_path);
1230 if ((lockFd = ap_popenf(tp, lockFileName, O_RDWR, 0))<0) {
1232 * If we need to kill an application and the
1233 * corresponding lock file does not exist, then
1234 * that means we are in big trouble here
1236 /*@@@ this should be logged, but since all the lock
1237 * file stuff will be tossed, I'll leave it now */
1238 ap_pclosef(tp, lockFd);
1239 continue;
1242 if (fcgi_get_exclusive_write_lock_no_wait(lockFd) < 0) {
1243 #else
1244 if (fcgi_get_exclusive_write_lock_no_wait(s->dynamic_lock) < 0) {
1245 #endif
1246 FCGIDBG2("fcgi_get_exclusive_write_lock_no_wait() failed (%ld)", GetLastError());
1248 * Unable to lock the lockfile, indicative
1249 * of WS performing operation with the given
1250 * application class. The simplest solution
1251 * is to spawn off another process and block
1252 * on lock to kill it. This is under assumptions
1253 * that fork() is not very costly and this
1254 * situation occurs very rarely, which it should
1256 #ifndef WIN32
1257 funcData = ap_pcalloc(tp, sizeof(struct FuncData));
1258 funcData->lockFileName = lockFileName;
1259 #else
1260 funcData = malloc(sizeof(struct FuncData));
1261 funcData->lock = s->dynamic_lock;
1262 #endif
1263 funcData->process = &s->procs[i];
1265 #ifndef WIN32
1266 if((pid=fork())<0) {
1267 /*@@@ this should be logged, but since all the lock
1268 * file stuff will be tossed, I'll leave it now */
1269 ap_pclosef(tp, lockFd);
1270 continue;
1271 } else if(pid==0) {
1272 /* child */
1274 /* rename the process for ps - best we can easily */
1275 change_process_name("fcgiBlkKill");
1277 dynamic_blocking_kill(funcData);
1278 } else {
1279 /* parent */
1280 s->procs[i].state = STATE_VICTIM;
1281 ap_pclosef(tp, lockFd);
1283 #else
1284 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) dynamic_blocking_kill, (LPVOID) funcData, 0, NULL);
1285 s->procs[i].state = STATE_VICTIM;
1286 #endif
1288 else {
1289 FCGIDBG1("fcgi_get_exclusive_write_lock_no_wait() succeeded");
1290 s->procs[i].state = STATE_VICTIM;
1291 #ifndef WIN32
1292 fcgi_kill(&s->procs[i], SIGTERM);
1293 ap_pclosef(tp, lockFd);
1294 #else
1295 fcgi_kill(&s->procs[i], 1);
1296 fcgi_rdwr_unlock(s->dynamic_lock, WRITER);
1297 #endif
1302 ap_destroy_pool(tp);
1305 #ifdef WIN32
1307 // This is a little bogus, there's gotta be a better way to do this
1308 // Can we use WaitForMultipleObjects()
1309 #define FCGI_PROC_WAIT_TIME 100
1311 void child_wait_thread(void *dummy) {
1312 fcgi_server *s;
1313 DWORD dwRet = WAIT_TIMEOUT;
1314 int numChildren;
1315 int i;
1316 int waited;
1318 while (!bTimeToDie) {
1319 waited = 0;
1321 for (s = fcgi_servers; s != NULL; s = s->next) {
1322 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1323 continue;
1325 if (s->directive == APP_CLASS_DYNAMIC) {
1326 numChildren = dynamicMaxClassProcs;
1328 else {
1329 numChildren = s->numProcesses;
1332 for (i=0; i < numChildren; i++) {
1333 if (s->procs[i].handle != INVALID_HANDLE_VALUE) {
1334 /* timeout is currently set for 100 miliecond */
1335 /* it may need t longer or user customizable */
1336 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1338 waited = 1;
1340 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1341 /* a child fs has died */
1342 /* mark the child as dead */
1344 if (s->directive == APP_CLASS_STANDARD) {
1345 /* restart static app */
1346 s->procs[i].state = STATE_NEEDS_STARTING;
1347 s->numFailures++;
1349 else {
1350 s->numProcesses--;
1351 fcgi_dynamic_total_proc_count--;
1352 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1354 if (s->procs[i].state == STATE_VICTIM) {
1355 s->procs[i].state = STATE_KILLED;
1357 else {
1358 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1359 s->numFailures++;
1361 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1362 s->procs[i].state = STATE_NEEDS_STARTING;
1364 else {
1365 s->procs[i].state = STATE_READY;
1370 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1371 "FastCGI:%s server \"%s\" (pid %d) terminated",
1372 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1373 s->fs_path, s->procs[i].pid);
1375 CloseHandle(s->procs[i].handle);
1376 s->procs[i].handle = INVALID_HANDLE_VALUE;
1377 s->procs[i].pid = -1;
1379 /* wake up the main thread */
1380 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1385 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1388 #endif
1390 #ifndef WIN32
1391 static void setup_signals(void)
1393 sigset_t mask;
1394 struct sigaction sa;
1396 /* Ignore USR2 */
1397 sigemptyset(&mask);
1398 sigaddset(&mask, SIGUSR2);
1399 sigprocmask(SIG_BLOCK, &mask, NULL);
1401 /* Setup handlers */
1403 sa.sa_handler = signal_handler;
1404 sigemptyset(&sa.sa_mask);
1405 sa.sa_flags = 0;
1407 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1408 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1409 "sigaction(SIGTERM) failed");
1411 /* httpd restart */
1412 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1413 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1414 "sigaction(SIGHUP) failed");
1416 /* httpd graceful restart */
1417 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1418 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1419 "sigaction(SIGUSR1) failed");
1421 /* read messages from request handlers - kill interval expired */
1422 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1423 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1424 "sigaction(SIGALRM) failed");
1426 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1427 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1428 "sigaction(SIGCHLD) failed");
1431 #endif
1433 #ifndef WIN32
1434 int fcgi_pm_main(void *dummy, child_info *info)
1435 #else
1436 void fcgi_pm_main(void *dummy)
1437 #endif
1439 fcgi_server *s;
1440 unsigned int i;
1441 int read_ready = 0;
1442 int alarmLeft = 0;
1444 #ifdef WIN32
1445 DWORD dwRet;
1446 int first_time = 1;
1447 HANDLE wait_thread = INVALID_HANDLE_VALUE;
1448 #else
1449 int callWaitPid, callDynamicProcs;
1450 #endif
1452 #ifdef WIN32
1453 // Add SystemRoot to the dynamic environment
1454 char ** envp = dynamicEnvp;
1455 for (i = 0; *envp; ++i) {
1456 ++envp;
1458 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1460 #else
1461 reduce_privileges();
1463 close(fcgi_pm_pipe[1]);
1464 change_process_name("fcgi-pm");
1465 setup_signals();
1467 if (fcgi_wrapper) {
1468 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1469 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1471 #endif
1473 /* Initialize AppClass */
1474 for (s = fcgi_servers; s != NULL; s = s->next)
1476 if (s->directive != APP_CLASS_STANDARD)
1477 continue;
1479 #ifdef WIN32
1480 if (s->socket_path)
1481 s->listenFd = 0;
1482 #endif
1484 for (i = 0; i < s->numProcesses; ++i)
1485 s->procs[i].state = STATE_NEEDS_STARTING;
1488 #ifdef WIN32
1489 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1490 "FastCGI: process manager initialized");
1491 #else
1492 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1493 "FastCGI: process manager initialized (pid %ld)", (long)getpid());
1494 #endif
1496 now = time(NULL);
1499 * Loop until SIGTERM
1501 for (;;) {
1502 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1503 #ifdef WIN32
1504 time_t expire;
1505 #else
1506 pid_t childPid;
1507 int waitStatus;
1508 #endif
1509 unsigned int numChildren;
1512 * If we came out of sigsuspend() for any reason other than
1513 * SIGALRM, pick up where we left off.
1515 if (alarmLeft)
1516 sleepSeconds = alarmLeft;
1519 * Examine each configured AppClass for a process that needs
1520 * starting. Compute the earliest time when the start should
1521 * be attempted, starting it now if the time has passed. Also,
1522 * remember that we do NOT need to restart externally managed
1523 * FastCGI applications.
1525 for (s = fcgi_servers; s != NULL; s = s->next)
1527 if (s->directive == APP_CLASS_EXTERNAL)
1528 continue;
1530 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1531 ? dynamicMaxClassProcs
1532 : s->numProcesses;
1534 for (i = 0; i < numChildren; ++i)
1536 if (s->procs[i].pid <= 0 && s->procs[i].state == STATE_NEEDS_STARTING)
1538 int restart = (s->procs[i].pid < 0);
1539 time_t restartTime = s->restartTime;
1541 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1543 if (restartTime <= now)
1545 s->restartTime = now;
1547 if (s->listenFd < 0 && init_listen_sock(s))
1549 if (sleepSeconds > s->initStartDelay)
1550 sleepSeconds = s->initStartDelay;
1551 break;
1554 #ifndef WIN32
1555 if (caughtSigTerm) {
1556 goto ProcessSigTerm;
1558 #endif
1559 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1560 if (s->procs[i].pid <= 0) {
1561 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1562 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1563 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1564 s->fs_path);
1566 sleepSeconds = min(sleepSeconds,
1567 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1569 ap_assert(s->procs[i].pid < 0);
1570 break;
1573 if (s->directive == APP_CLASS_DYNAMIC) {
1574 s->numProcesses++;
1575 fcgi_dynamic_total_proc_count++;
1576 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1577 #ifdef WIN32
1578 if (i == 0 && !restart) {
1579 fcgi_rdwr_unlock(s->dynamic_lock, WRITER);
1581 #endif
1584 s->procs[i].state = STATE_STARTED;
1586 if (restart)
1587 s->numRestarts++;
1589 if (fcgi_wrapper) {
1590 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1591 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1592 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1593 s->fs_path, (long)s->uid, (long)s->gid,
1594 restart ? "re" : "", (long)s->procs[i].pid);
1596 else {
1597 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1598 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1599 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1600 s->fs_path, restart ? "re" : "", (long)s->procs[i].pid);
1602 ap_assert(s->procs[i].pid > 0);
1603 } else {
1604 sleepSeconds = min(sleepSeconds, restartTime - now);
1610 #ifndef WIN32
1611 if(caughtSigTerm) {
1612 goto ProcessSigTerm;
1614 if((!caughtSigChld) && (!caughtSigUsr2)) {
1615 fd_set rfds;
1617 alarm(sleepSeconds);
1619 FD_ZERO(&rfds);
1620 FD_SET(fcgi_pm_pipe[0], &rfds);
1621 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1623 alarmLeft = alarm(0);
1625 callWaitPid = caughtSigChld;
1626 caughtSigChld = FALSE;
1627 callDynamicProcs = caughtSigUsr2;
1628 caughtSigUsr2 = FALSE;
1630 now = time(NULL);
1633 * Dynamic fcgi process management
1635 if((callDynamicProcs) || (!callWaitPid)) {
1636 dynamic_read_msgs(read_ready);
1637 if(fcgi_dynamic_epoch == 0) {
1638 fcgi_dynamic_epoch = now;
1640 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1641 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1642 dynamic_kill_idle_fs_procs();
1643 fcgi_dynamic_epoch = now;
1647 if(!callWaitPid) {
1648 continue;
1651 /* We've caught SIGCHLD, so find out who it was using waitpid,
1652 * write a log message and update its data structure. */
1654 for (;;) {
1655 if (caughtSigTerm)
1656 goto ProcessSigTerm;
1658 childPid = waitpid(-1, &waitStatus, WNOHANG);
1660 if (childPid == -1 || childPid == 0)
1661 break;
1663 for (s = fcgi_servers; s != NULL; s = s->next) {
1664 if (s->directive == APP_CLASS_EXTERNAL)
1665 continue;
1667 if (s->directive == APP_CLASS_DYNAMIC)
1668 numChildren = dynamicMaxClassProcs;
1669 else
1670 numChildren = s->numProcesses;
1672 for (i = 0; i < numChildren; i++) {
1673 if (s->procs[i].pid == childPid)
1674 goto ChildFound;
1678 /* @@@ This (comment) needs to go away when dynamic gets cleaned up.
1679 * If we get to this point, we have detected the
1680 * termination of the process that was spawned off by
1681 * the process manager to do a blocking kill above. */
1682 continue;
1684 ChildFound:
1685 s->procs[i].pid = -1;
1687 if (s->directive == APP_CLASS_STANDARD) {
1688 /* Always restart static apps */
1689 s->procs[i].state = STATE_NEEDS_STARTING;
1690 s->numFailures++;
1692 else {
1693 s->numProcesses--;
1694 fcgi_dynamic_total_proc_count--;
1696 if (s->procs[i].state == STATE_VICTIM) {
1697 s->procs[i].state = STATE_KILLED;
1699 else {
1700 /* A dynamic app died or exited without provocation from the PM */
1701 s->numFailures++;
1703 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1704 s->procs[i].state = STATE_NEEDS_STARTING;
1705 else
1706 s->procs[i].state = STATE_READY;
1710 if (WIFEXITED(waitStatus)) {
1711 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1712 "FastCGI:%s server \"%s\" (pid %d) terminated by calling exit with status '%d'",
1713 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1714 s->fs_path, (int)childPid, WEXITSTATUS(waitStatus));
1716 else if (WIFSIGNALED(waitStatus)) {
1717 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1718 "FastCGI:%s server \"%s\" (pid %d) terminated due to uncaught signal '%d' (%s)%s",
1719 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1720 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)],
1721 #ifdef WCOREDUMP
1722 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1723 #else
1724 "");
1725 #endif
1727 else if (WIFSTOPPED(waitStatus)) {
1728 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1729 "FastCGI:%s server \"%s\" (pid %d) stopped due to uncaught signal '%d' (%s)",
1730 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1731 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)]);
1733 } /* for (;;), waitpid() */
1734 #else
1735 if (first_time) {
1736 /* Start the child wait thread */
1737 wait_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)child_wait_thread, NULL, 0, NULL);
1738 first_time = 0;
1741 /* wait for an event to occur or timer expires */
1742 expire = time(NULL) + sleepSeconds;
1743 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1745 if (dwRet == WAIT_FAILED) {
1746 /* There is something seriously wrong here */
1747 ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, fcgi_apache_main_server,
1748 "FastDGI: WaitForMultipleObjects on event handles -- pm is shuting down");
1749 bTimeToDie = TRUE;
1752 if (dwRet != WAIT_TIMEOUT) {
1753 now = time(NULL);
1755 if (now < expire)
1756 alarmLeft = expire - now;
1760 * Dynamic fcgi process management
1762 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1763 if (dwRet == MBOX_EVENT) {
1764 read_ready = 1;
1767 now = time(NULL);
1769 dynamic_read_msgs(read_ready);
1771 if(fcgi_dynamic_epoch == 0) {
1772 fcgi_dynamic_epoch = now;
1775 if (((long)(now-fcgi_dynamic_epoch) >= (int)dynamicKillInterval) ||
1776 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1777 dynamic_kill_idle_fs_procs();
1778 fcgi_dynamic_epoch = now;
1780 read_ready = 0;
1782 else if (dwRet == WAKE_EVENT) {
1783 continue;
1785 else if (dwRet == TERM_EVENT) {
1786 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1787 "FastCGI: Termination event received process manager shutting down");
1788 bTimeToDie = TRUE;
1790 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1791 goto ProcessSigTerm;
1793 else {
1794 // Have an received an unknown event - should not happen
1795 ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, fcgi_apache_main_server,
1796 "FastCGI: WaitForMultipleobjects return an unrecognized event");
1797 bTimeToDie = TRUE;
1798 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1799 goto ProcessSigTerm;
1801 #endif
1802 } /* for (;;), the whole shoot'n match */
1804 ProcessSigTerm:
1806 * Kill off the children, then exit.
1808 while (fcgi_servers != NULL) {
1809 kill_fs_procs(fcgi_config_pool, fcgi_servers);
1812 #ifdef WIN32
1813 return;
1814 #else
1815 exit(0);
1816 #endif
1819 #ifdef WIN32
1820 int fcgi_pm_add_job(fcgi_pm_job *new_job) {
1822 if (new_job == NULL)
1823 return 0;
1825 if (ap_acquire_mutex(fcgi_dynamic_mbox_mutex) != MULTI_OK) {
1826 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1827 "FastCGI: Failed to aquire the dynamic mbox mutex!");
1830 new_job->next = fcgi_dynamic_mbox;
1831 fcgi_dynamic_mbox = new_job;
1833 if (! ap_release_mutex(fcgi_dynamic_mbox_mutex)) {
1834 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1835 "FastCGI: Failed to release the dynamic mbox mutex!");
1838 return 1;
1840 #endif