*** empty log message ***
[mod_fastcgi.git] / fcgi_pm.c
blob7adff91bf36fd86054a9e312af6ff758cd18bc87
1 /*
2 * $Id: fcgi_pm.c,v 1.75 2002/07/29 00:07:28 robs Exp $
3 */
6 #include "fcgi.h"
8 #if defined(APACHE2) && !defined(WIN32)
9 #include <pwd.h>
10 #endif
12 #ifdef _HPUX_SOURCE
13 #include <unistd.h>
14 #define seteuid(arg) setresuid(-1, (arg), -1)
15 #endif
17 int fcgi_dynamic_total_proc_count = 0; /* number of running apps */
18 time_t fcgi_dynamic_epoch = 0; /* last time kill_procs was
19 * invoked by process mgr */
20 time_t fcgi_dynamic_last_analyzed = 0; /* last time calculation was
21 * made for the dynamic procs */
23 static time_t now = 0;
25 #ifdef WIN32
26 #pragma warning ( disable : 4100 4102 )
27 static BOOL bTimeToDie = FALSE; /* process termination flag */
28 HANDLE fcgi_event_handles[3];
29 #ifndef SIGKILL
30 #define SIGKILL 9
31 #endif
32 #endif
35 #if !defined(WIN32) && !defined(APACHE2)
36 static int seteuid_root(void)
38 int rc = seteuid((uid_t)0);
39 if (rc == -1) {
40 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
41 "FastCGI: seteuid(0) failed");
43 return rc;
46 static int seteuid_user(void)
48 int rc = seteuid(ap_user_id);
49 if (rc == -1) {
50 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
51 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id);
53 return rc;
55 #endif
58 * Signal the process to exit. How (or if) the process responds
59 * depends on the FastCGI application library (esp. on Win32) and
60 * possibly application code (signal handlers and whether or not
61 * SA_RESTART is on). At any rate, we send the signal with the
62 * hopes that the process will exit on its own. Later, as we
63 * review the state of application processes, if we see one marked
64 * for death, but that hasn't died within a specified period of
65 * time, fcgi_kill() is called again with a KILL)
67 static void fcgi_kill(ServerProcess *process, int sig)
69 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process->pid, sig);
71 process->state = FCGI_VICTIM_STATE;
73 #ifdef WIN32
75 if (sig == SIGTERM)
77 SetEvent(process->terminationEvent);
79 else if (sig == SIGKILL)
81 TerminateProcess(process->handle, 1);
83 else
85 ap_assert(0);
88 #else /* !WIN32 */
90 #ifndef APACHE2
91 if (fcgi_wrapper)
93 seteuid_root();
95 #endif
97 kill(process->pid, sig);
99 #ifndef APACHE2
100 if (fcgi_wrapper)
102 seteuid_user();
104 #endif
106 #endif /* !WIN32 */
109 /*******************************************************************************
110 * Send SIGTERM to each process in the server class, remove socket
111 * file if appropriate. Currently this is only called when the PM is shutting
112 * down and thus memory isn't freed and sockets and files aren't closed.
114 static void shutdown_all()
116 fcgi_server *s = fcgi_servers;
118 while (s)
120 ServerProcess *proc = s->procs;
121 int i;
122 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
123 ? dynamicMaxClassProcs
124 : s->numProcesses;
126 #ifndef WIN32
127 if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL)
129 /* Remove the socket file */
130 if (unlink(s->socket_path) != 0 && errno != ENOENT) {
131 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
132 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
133 s->socket_path,
134 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path);
137 #endif
139 /* Send TERM to all processes */
140 for (i = 0; i < numChildren; i++, proc++)
142 if (proc->state == FCGI_RUNNING_STATE)
144 fcgi_kill(proc, SIGTERM);
148 s = s->next;
151 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
154 * WIN32 applications may not have support for the shutdown event
155 * depending on their application library version
158 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT);
159 s = fcgi_servers;
161 while (s)
163 ServerProcess *proc = s->procs;
164 int i;
165 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
166 ? dynamicMaxClassProcs
167 : s->numProcesses;
169 /* Send KILL to all processes */
170 for (i = 0; i < numChildren; i++, proc++)
172 if (proc->state == FCGI_RUNNING_STATE)
174 fcgi_kill(proc, SIGKILL);
178 s = s->next;
181 #endif /* WIN32 */
184 static int init_listen_sock(fcgi_server * fs)
186 ap_assert(fs->directive != APP_CLASS_EXTERNAL);
188 /* Create the socket */
189 if ((fs->listenFd = socket(fs->socket_addr->sa_family, SOCK_STREAM, 0)) < 0)
191 #ifdef WIN32
192 errno = WSAGetLastError(); // Not sure if this will work as expected
193 #endif
194 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
195 "FastCGI: can't create %sserver \"%s\": socket() failed",
196 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
197 fs->fs_path);
198 return -1;
201 #ifndef WIN32
202 if (fs->socket_addr->sa_family == AF_UNIX)
204 /* Remove any existing socket file.. just in case */
205 unlink(((struct sockaddr_un *)fs->socket_addr)->sun_path);
207 else
208 #endif
210 int flag = 1;
211 setsockopt(fs->listenFd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
214 /* Bind it to the socket_addr */
215 if (bind(fs->listenFd, fs->socket_addr, fs->socket_addr_len))
217 char port[11];
219 #ifdef WIN32
220 errno = WSAGetLastError();
221 #endif
222 ap_snprintf(port, sizeof(port), "port=%d",
223 ((struct sockaddr_in *)fs->socket_addr)->sin_port);
225 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
226 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
227 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
228 fs->fs_path,
229 #ifndef WIN32
230 (fs->socket_addr->sa_family == AF_UNIX) ?
231 ((struct sockaddr_un *)fs->socket_addr)->sun_path :
232 #endif
233 port);
236 #ifndef WIN32
237 /* Twiddle Unix socket permissions */
238 else if (fs->socket_addr->sa_family == AF_UNIX
239 && chmod(((struct sockaddr_un *)fs->socket_addr)->sun_path, S_IRUSR | S_IWUSR))
241 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
242 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
243 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
244 fs->fs_path);
246 #endif
248 /* Set to listen */
249 else if (listen(fs->listenFd, fs->listenQueueDepth))
251 #ifdef WIN32
252 errno = WSAGetLastError();
253 #endif
254 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
255 "FastCGI: can't create %sserver \"%s\": listen() failed",
256 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
257 fs->fs_path);
259 else
261 return 0;
264 close(fs->listenFd);
265 fs->listenFd = -1;
266 return -2;
270 *----------------------------------------------------------------------
272 * pm_main
274 * The FastCGI process manager, which runs as a separate
275 * process responsible for:
276 * - Starting all the FastCGI proceses.
277 * - Restarting any of these processes that die (indicated
278 * by SIGCHLD).
279 * - Catching SIGTERM and relaying it to all the FastCGI
280 * processes before exiting.
282 * Inputs:
283 * Uses global variable fcgi_servers.
285 * Results:
286 * Does not return.
288 * Side effects:
289 * Described above.
291 *----------------------------------------------------------------------
293 #ifndef WIN32
294 static int caughtSigTerm = FALSE;
295 static int caughtSigChld = FALSE;
296 static int caughtSigAlarm = FALSE;
298 static void signal_handler(int signo)
300 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
301 /* SIGUSR1 & SIGHUP are sent by apache to its process group
302 * when apache get 'em. Apache follows up (1.2.x) with attacks
303 * on each of its child processes, but we've got the KillMgr
304 * sitting between us so we never see the KILL. The main loop
305 * in ProcMgr also checks to see if the KillMgr has terminated,
306 * and if it has, we handl it as if we should shutdown too. */
307 caughtSigTerm = TRUE;
308 } else if(signo == SIGCHLD) {
309 caughtSigChld = TRUE;
310 } else if(signo == SIGALRM) {
311 caughtSigAlarm = TRUE;
314 #endif
317 *----------------------------------------------------------------------
319 * spawn_fs_process --
321 * Fork and exec the specified fcgi process.
323 * Results:
324 * 0 for successful fork, -1 for failed fork.
326 * In case the child fails before or in the exec, the child
327 * obtains the error log by calling getErrLog, logs
328 * the error, and exits with exit status = errno of
329 * the failed system call.
331 * Side effects:
332 * Child process created.
334 *----------------------------------------------------------------------
336 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
338 #ifndef WIN32
340 pid_t child_pid;
341 int i;
342 char *dirName;
343 char *dnEnd, *failedSysCall;
345 child_pid = fork();
346 if (child_pid) {
347 return child_pid;
350 /* We're the child. We're gonna exec() so pools don't matter. */
352 dnEnd = strrchr(fs->fs_path, '/');
353 if (dnEnd == NULL) {
354 dirName = "./";
355 } else {
356 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
357 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
359 if (chdir(dirName) < 0) {
360 failedSysCall = "chdir()";
361 goto FailedSystemCallExit;
364 #ifndef __EMX__
365 /* OS/2 dosen't support nice() */
366 if (fs->processPriority != 0) {
367 if (nice(fs->processPriority) == -1) {
368 failedSysCall = "nice()";
369 goto FailedSystemCallExit;
372 #endif
374 /* Open the listenFd on spec'd fd */
375 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
376 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
378 /* Close all other open fds, except stdout/stderr. Leave these two open so
379 * FastCGI applications don't have to find and fix ALL 3rd party libs that
380 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
381 * main server error_log - @@@ provide a directive control where this goes.
383 ap_error_log2stderr(fcgi_apache_main_server);
384 dup2(2, 1);
385 for (i = 0; i < FCGI_MAX_FD; i++) {
386 if (i != FCGI_LISTENSOCK_FILENO && i != 2 && i != 1) {
387 close(i);
391 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
392 * install its own handler. */
393 signal(SIGPIPE, SIG_IGN);
395 if (fcgi_wrapper && (fcgi_user_id != fs->uid || fcgi_group_id != fs->gid)) {
396 char *shortName = strrchr(fs->fs_path, '/') + 1;
398 #ifndef APACHE2
399 /* Relinquish our root real uid powers */
400 seteuid_root();
401 setuid(ap_user_id);
402 #endif
404 do {
405 execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group, shortName, NULL, fs->envp);
406 } while (errno == EINTR);
408 else {
409 do {
410 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
411 } while (errno == EINTR);
414 failedSysCall = "execle()";
416 FailedSystemCallExit:
417 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
418 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
419 exit(-1);
421 /* avoid an irrelevant compiler warning */
422 return(0);
424 #else /* WIN32 */
426 #ifdef APACHE2
428 /* based on mod_cgi.c:run_cgi_child() */
430 apr_pool_t * tp;
431 char * termination_env_string;
432 HANDLE listen_handle = INVALID_HANDLE_VALUE;
433 apr_procattr_t * procattr;
434 apr_proc_t proc = { 0 };
435 apr_file_t * file;
436 int i = 0;
438 if (apr_pool_create(&tp, fcgi_config_pool))
439 return 0;
441 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
442 if (process->terminationEvent == NULL)
443 goto CLEANUP;
445 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
447 termination_env_string = ap_psprintf(tp,
448 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
450 while (fs->envp[i]) i++;
451 fs->envp[i++] = termination_env_string;
452 fs->envp[i] = (char *) fs->mutex_env_string;
454 ap_assert(fs->envp[i + 1] == NULL);
456 if (fs->socket_path)
458 SECURITY_ATTRIBUTES sa = { 0 };
460 sa.bInheritHandle = TRUE;
461 sa.nLength = sizeof(sa);
463 listen_handle = CreateNamedPipe(fs->socket_path,
464 PIPE_ACCESS_DUPLEX,
465 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
466 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
468 if (listen_handle == INVALID_HANDLE_VALUE)
470 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
471 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
472 fs->fs_path);
473 goto CLEANUP;
476 else
478 listen_handle = (HANDLE) fs->listenFd;
481 if (apr_procattr_create(&procattr, tp))
482 goto CLEANUP;
484 if (apr_procattr_dir_set(procattr, ap_make_dirstr_parent(tp, fs->fs_path)))
485 goto CLEANUP;
487 if (apr_procattr_detach_set(procattr, 1))
488 goto CLEANUP;
490 if (apr_os_file_put(&file, &listen_handle, 0, tp))
491 goto CLEANUP;
493 /* procattr is opaque so we have to use this - unfortuantely it dups */
494 if (apr_procattr_child_in_set(procattr, file, NULL))
495 goto CLEANUP;
497 if (apr_proc_create(&proc, fs->fs_path, NULL, fs->envp, procattr, tp))
498 goto CLEANUP;
500 process->handle = proc.hproc;
503 CLEANUP:
505 if (i)
507 fs->envp[i - 1] = NULL;
510 ap_destroy_pool(tp);
512 return proc.pid;
514 #else /* WIN32 && !APACHE2 */
516 /* Adapted from Apache's util_script.c ap_call_exec() */
517 char *interpreter = NULL;
518 char *quoted_filename;
519 char *pCommand;
520 char *pEnvBlock, *pNext;
522 int i = 0;
523 int iEnvBlockLen = 1;
525 file_type_e fileType;
527 STARTUPINFO si;
528 PROCESS_INFORMATION pi;
530 request_rec r;
531 pid_t pid = -1;
533 pool * tp = ap_make_sub_pool(fcgi_config_pool);
535 HANDLE listen_handle;
536 char * termination_env_string = NULL;
538 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
539 if (process->terminationEvent == NULL)
541 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
542 "FastCGI: can't create termination event for server \"%s\", "
543 "CreateEvent() failed", fs->fs_path);
544 goto CLEANUP;
546 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
548 termination_env_string = ap_psprintf(tp,
549 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
551 if (fs->socket_path)
553 SECURITY_ATTRIBUTES sa;
555 sa.lpSecurityDescriptor = NULL;
556 sa.bInheritHandle = TRUE;
557 sa.nLength = sizeof(sa);
559 listen_handle = CreateNamedPipe(fs->socket_path,
560 PIPE_ACCESS_DUPLEX,
561 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
562 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
564 if (listen_handle == INVALID_HANDLE_VALUE)
566 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
567 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
568 goto CLEANUP;
571 else
573 listen_handle = (HANDLE) fs->listenFd;
576 memset(&si, 0, sizeof(si));
577 memset(&pi, 0, sizeof(pi));
578 memset(&r, 0, sizeof(r));
580 // Can up a fake request to pass to ap_get_win32_interpreter()
581 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
582 r.server = fcgi_apache_main_server;
583 r.filename = (char *) fs->fs_path;
584 r.pool = tp;
586 fileType = ap_get_win32_interpreter(&r, &interpreter);
588 if (fileType == eFileTypeUNKNOWN) {
589 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
590 "FastCGI: %s is not executable; ensure interpreted scripts have "
591 "\"#!\" as their first line",
592 fs->fs_path);
593 ap_destroy_pool(tp);
594 goto CLEANUP;
598 * We have the interpreter (if there is one) and we have
599 * the arguments (if there are any).
600 * Build the command string to pass to CreateProcess.
602 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
603 if (interpreter && *interpreter) {
604 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
606 else {
607 pCommand = quoted_filename;
611 * Make child process use hPipeOutputWrite as standard out,
612 * and make sure it does not show on screen.
614 si.cb = sizeof(si);
615 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
616 si.wShowWindow = SW_HIDE;
617 si.hStdInput = listen_handle;
619 // XXX These should be open to the error_log
620 si.hStdOutput = INVALID_HANDLE_VALUE;
621 si.hStdError = INVALID_HANDLE_VALUE;
624 * Win32's CreateProcess call requires that the environment
625 * be passed in an environment block, a null terminated block of
626 * null terminated strings.
627 * @todo we should store the env in this format for win32.
629 while (fs->envp[i])
631 iEnvBlockLen += strlen(fs->envp[i]) + 1;
632 i++;
635 iEnvBlockLen += strlen(termination_env_string) + 1;
636 iEnvBlockLen += strlen(fs->mutex_env_string) + 1;
638 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
640 i = 0;
641 pNext = pEnvBlock;
642 while (fs->envp[i])
644 strcpy(pNext, fs->envp[i]);
645 pNext += strlen(pNext) + 1;
646 i++;
649 strcpy(pNext, termination_env_string);
650 pNext += strlen(pNext) + 1;
651 strcpy(pNext, fs->mutex_env_string);
653 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
655 pEnvBlock,
656 ap_make_dirstr_parent(tp, fs->fs_path),
657 &si, &pi))
659 /* Hack to get 16-bit CGI's working. It works for all the
660 * standard modules shipped with Apache. pi.dwProcessId is 0
661 * for 16-bit CGIs and all the Unix specific code that calls
662 * ap_call_exec interprets this as a failure case. And we can't
663 * use -1 either because it is mapped to 0 by the caller.
665 pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId;
667 process->handle = pi.hProcess;
668 CloseHandle(pi.hThread);
671 if (fs->socket_path)
673 CloseHandle(listen_handle);
676 CLEANUP:
678 ap_destroy_pool(tp);
680 return pid;
682 #endif /* !APACHE2 */
683 #endif /* WIN32 */
686 #if !defined(WIN32) && !defined(APACHE2)
687 static void reduce_privileges(void)
689 char *name;
691 if (geteuid() != 0)
692 return;
694 #ifndef __EMX__
695 /* Get username if passed as a uid */
696 if (ap_user_name[0] == '#') {
697 uid_t uid = atoi(&ap_user_name[1]);
698 struct passwd *ent = getpwuid(uid);
700 if (ent == NULL) {
701 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
702 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
703 "you probably need to modify the User directive", (unsigned)uid);
704 exit(1);
706 name = ent->pw_name;
708 else
709 name = ap_user_name;
711 /* Change Group */
712 if (setgid(ap_group_id) == -1) {
713 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
714 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
715 exit(1);
718 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
720 /* Initialize supplementary groups */
721 if (initgroups(name, ap_group_id) == -1) {
722 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
723 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
724 name, (unsigned)ap_group_id);
725 exit(1);
727 #endif /* __EMX__ */
729 /* Change User */
730 if (fcgi_wrapper) {
731 if (seteuid_user() == -1) {
732 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
733 "FastCGI: process manager exiting, failed to reduce privileges");
734 exit(1);
737 else {
738 if (setuid(ap_user_id) == -1) {
739 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
740 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
741 exit(1);
746 /*************
747 * Change the name of this process - best we can easily.
749 static void change_process_name(const char * const name)
751 strncpy(ap_server_argv0, name, strlen(ap_server_argv0));
753 #endif
755 static void schedule_start(fcgi_server *s, int proc)
757 /* If we've started one recently, don't register another */
758 time_t time_passed = now - s->restartTime;
760 if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay))
761 || ((s->procs[proc].pid == 0) && (time_passed < s->initStartDelay)))
763 FCGIDBG6("ignore_job: slot=%d, pid=%ld, time_passed=%ld, initStartDelay=%ld, restartDelay=%ld", proc, (long) s->procs[proc].pid, time_passed, s->initStartDelay, s->restartDelay);
764 return;
767 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
768 s->procs[proc].state = FCGI_START_STATE;
769 if (proc == dynamicMaxClassProcs - 1) {
770 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
771 "FastCGI: scheduled the %sstart of the last (dynamic) server "
772 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
773 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
778 *----------------------------------------------------------------------
780 * dynamic_read_msgs
782 * Removes the records written by request handlers and decodes them.
783 * We also update the data structures to reflect the changes.
785 *----------------------------------------------------------------------
788 static void dynamic_read_msgs(int read_ready)
790 fcgi_server *s;
791 int rc;
793 #ifndef WIN32
794 static int buflen = 0;
795 static char buf[FCGI_MSGS_BUFSIZE + 1];
796 char *ptr1, *ptr2, opcode;
797 char execName[FCGI_MAXPATH + 1];
798 char user[MAX_USER_NAME_LEN + 2];
799 char group[MAX_GID_CHAR_LEN + 1];
800 unsigned long q_usec = 0UL, req_usec = 0UL;
801 #else
802 fcgi_pm_job *joblist = NULL;
803 fcgi_pm_job *cjob = NULL;
804 #endif
806 pool *sp = NULL, *tp;
808 #ifndef WIN32
809 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
810 #endif
813 * To prevent the idle application from running indefinitely, we
814 * check the timer and if it is expired, we recompute the values
815 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
816 * message is received, only updates are made to the data structures.
818 if (fcgi_dynamic_last_analyzed == 0) {
819 fcgi_dynamic_last_analyzed = now;
821 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
822 for (s = fcgi_servers; s != NULL; s = s->next) {
823 if (s->directive != APP_CLASS_DYNAMIC)
824 break;
826 /* Advance the last analyzed timestamp by the elapsed time since
827 * it was last set. Round the increase down to the nearest
828 * multiple of dynamicUpdateInterval */
830 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
831 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
832 s->totalConnTime = 0UL;
833 s->totalQueueTime = 0UL;
837 if (read_ready <= 0) {
838 return;
841 #ifndef WIN32
842 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
843 if (rc <= 0) {
844 if (!caughtSigTerm) {
845 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
846 "FastCGI: read() from pipe failed (%d)", rc);
847 if (rc == 0) {
848 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
849 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
850 caughtSigTerm = TRUE;
853 return;
855 buflen += rc;
856 buf[buflen] = '\0';
858 #else
860 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
861 * request to do something) and/or when a timeout expires.
862 * There really should be no reason why this wait would get stuck
863 * but there's no point in waiting forever. */
865 rc = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
867 if (rc != WAIT_OBJECT_0 && rc != WAIT_ABANDONED)
869 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
870 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
871 return;
874 joblist = fcgi_dynamic_mbox;
875 fcgi_dynamic_mbox = NULL;
877 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
879 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
880 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
883 cjob = joblist;
884 #endif
886 #ifdef APACHE2
887 apr_pool_create(&tp, fcgi_config_pool);
888 #else
889 tp = ap_make_sub_pool(fcgi_config_pool);
890 #endif
892 #ifndef WIN32
893 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
894 int scan_failed = 0;
896 ptr2 = strchr(ptr1, '*');
897 if (ptr2) {
898 *ptr2++ = '\0';
900 else {
901 break;
904 opcode = *ptr1;
906 switch (opcode)
908 case FCGI_SERVER_START_JOB:
909 case FCGI_SERVER_RESTART_JOB:
911 if (sscanf(ptr1, "%c %s %16s %15s",
912 &opcode, execName, user, group) != 4)
914 scan_failed = 1;
916 break;
918 case FCGI_REQUEST_TIMEOUT_JOB:
920 if (sscanf(ptr1, "%c %s %16s %15s",
921 &opcode, execName, user, group) != 4)
923 scan_failed = 1;
925 break;
927 case FCGI_REQUEST_COMPLETE_JOB:
929 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
930 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
932 scan_failed = 1;
934 break;
936 default:
938 scan_failed = 1;
939 break;
942 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode, execName, user, group, q_usec, req_usec);
944 if (scan_failed) {
945 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
946 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
947 goto NextJob;
949 #else
950 /* Update data structures for processing */
951 while (cjob != NULL) {
952 joblist = cjob->next;
953 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
954 #endif
956 #ifndef WIN32
957 s = fcgi_util_fs_get(execName, user, group);
958 #else
959 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
960 #endif
962 #ifndef WIN32
963 if (s==NULL && opcode != FCGI_REQUEST_COMPLETE_JOB)
964 #else
965 if (s==NULL && cjob->id != FCGI_REQUEST_COMPLETE_JOB)
966 #endif
968 #ifdef WIN32
970 HANDLE mutex = CreateMutex(NULL, FALSE, cjob->fs_path);
972 if (mutex == NULL)
974 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
975 "FastCGI: can't create accept mutex "
976 "for (dynamic) server \"%s\"", cjob->fs_path);
977 goto BagNewServer;
980 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
981 #else
982 const char *err;
983 #endif
985 /* Create a perm subpool to hold the new server data,
986 * we can destroy it if something doesn't pan out */
987 #ifdef APACHE2
988 apr_pool_create(&sp, fcgi_config_pool);
989 #else
990 sp = ap_make_sub_pool(fcgi_config_pool);
991 #endif
993 /* Create a new "dynamic" server */
994 s = fcgi_util_fs_new(sp);
996 s->directive = APP_CLASS_DYNAMIC;
997 s->restartDelay = dynamicRestartDelay;
998 s->listenQueueDepth = dynamicListenQueueDepth;
999 s->initStartDelay = dynamicInitStartDelay;
1000 s->envp = dynamicEnvp;
1001 s->flush = dynamicFlush;
1003 #ifdef WIN32
1004 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
1005 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
1006 #else
1007 s->fs_path = ap_pstrdup(sp, execName);
1008 #endif
1009 ap_getparents(s->fs_path);
1010 ap_no2slash(s->fs_path);
1011 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
1013 /* XXX the socket_path (both Unix and Win) *is* deducible and
1014 * thus can and will be used by other apache instances without
1015 * the use of shared data regarding the processes serving the
1016 * requests. This can result in slightly unintuitive process
1017 * counts and security implications. This is prevented
1018 * if suexec (Unix) is in use. This is both a feature and a flaw.
1019 * Changing it now would break existing installations. */
1021 #ifndef WIN32
1022 /* Create socket file's path */
1023 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
1024 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1026 /* Create sockaddr, prealloc it so it won't get created in tp */
1027 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
1028 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
1029 &s->socket_addr_len, s->socket_path);
1030 if (err) {
1031 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1032 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
1033 goto BagNewServer;
1036 if (init_listen_sock(s)) {
1037 goto BagNewServer;
1040 /* If a wrapper is being used, config user/group info */
1041 if (fcgi_wrapper) {
1042 if (user[0] == '~') {
1043 /* its a user dir uri, the rest is a username, not a uid */
1044 struct passwd *pw = getpwnam(&user[1]);
1046 if (!pw) {
1047 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1048 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1049 execName, &user[1]);
1050 goto BagNewServer;
1052 s->uid = pw->pw_uid;
1053 s->user = ap_pstrdup(sp, user);
1054 s->username = s->user;
1056 s->gid = pw->pw_gid;
1057 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
1059 else {
1060 struct passwd *pw;
1062 s->uid = (uid_t)atol(user);
1063 pw = getpwuid(s->uid);
1064 if (!pw) {
1065 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1066 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1067 execName, (long)s->uid);
1068 goto BagNewServer;
1070 s->user = ap_pstrdup(sp, user);
1071 s->username = ap_pstrdup(sp, pw->pw_name);
1073 s->gid = (gid_t)atol(group);
1074 s->group = ap_pstrdup(sp, group);
1077 #else
1078 /* Create socket file's path */
1079 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
1080 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1081 s->listenFd = 0;
1082 #endif
1084 fcgi_util_fs_add(s);
1086 else {
1087 #ifndef WIN32
1088 if (opcode == FCGI_SERVER_RESTART_JOB) {
1089 #else
1090 if (cjob->id==FCGI_SERVER_RESTART_JOB) {
1091 #endif
1092 /* Check to see if the binary has changed. If so,
1093 * kill the FCGI application processes, and
1094 * restart them.
1096 struct stat stbuf;
1097 int i;
1098 #ifdef WIN32
1099 char * app_path = cjob->fs_path;
1100 #else
1101 char * app_path = execName;
1102 #endif
1104 if (stat(app_path, &stbuf) == 0 && stbuf.st_mtime > s->startTime)
1106 int do_restart = 0;
1108 /* prevent addition restart requests */
1109 s->startTime = now;
1110 #ifndef WIN32
1111 utime(s->socket_path, NULL);
1112 #endif
1114 /* kill old server(s) */
1115 for (i = 0; i < dynamicMaxClassProcs; i++)
1117 if (s->procs[i].pid > 0
1118 && stbuf.st_mtime > s->procs[i].start_time)
1120 fcgi_kill(&s->procs[i], SIGTERM);
1121 do_restart++;
1125 if (do_restart)
1127 ap_log_error(FCGI_LOG_WARN_NOERRNO,
1128 fcgi_apache_main_server, "FastCGI: restarting "
1129 "old server \"%s\" processes, newer version "
1130 "found", app_path);
1134 /* If dynamicAutoRestart, don't mark any new processes
1135 * for starting because we probably got the
1136 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1137 * will be restarting all of those we just killed.
1139 if (dynamicAutoRestart)
1140 goto NextJob;
1142 #ifndef WIN32
1143 else if (opcode == FCGI_SERVER_START_JOB) {
1144 #else
1145 else if (cjob->id==FCGI_SERVER_START_JOB) {
1146 #endif
1147 /* we've been asked to start a process--only start
1148 * it if we're not already running at least one
1149 * instance.
1151 int i;
1153 for (i = 0; i < dynamicMaxClassProcs; i++) {
1154 if (s->procs[i].state == FCGI_RUNNING_STATE)
1155 break;
1157 /* if already running, don't start another one */
1158 if (i < dynamicMaxClassProcs) {
1159 goto NextJob;
1164 #ifndef WIN32
1165 switch (opcode)
1166 #else
1167 switch (cjob->id)
1168 #endif
1170 int i, start;
1172 case FCGI_SERVER_RESTART_JOB:
1174 start = FALSE;
1176 /* We just waxed 'em all. Try to find an idle slot. */
1178 for (i = 0; i < dynamicMaxClassProcs; ++i)
1180 if (s->procs[i].state == FCGI_START_STATE
1181 || s->procs[i].state == FCGI_RUNNING_STATE)
1183 break;
1185 else if (s->procs[i].state == FCGI_KILLED_STATE
1186 || s->procs[i].state == FCGI_READY_STATE)
1188 start = TRUE;
1189 break;
1193 /* Nope, just use the first slot */
1194 if (i == dynamicMaxClassProcs)
1196 start = TRUE;
1197 i = 0;
1200 if (start)
1202 schedule_start(s, i);
1205 break;
1207 case FCGI_SERVER_START_JOB:
1208 case FCGI_REQUEST_TIMEOUT_JOB:
1210 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1212 * Extra instances should have been
1213 * terminated beforehand, probably need
1214 * to increase ProcessSlack parameter
1216 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1217 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1218 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1219 goto NextJob;
1222 /* find next free slot */
1223 for (i = 0; i < dynamicMaxClassProcs; i++)
1225 if (s->procs[i].state == FCGI_START_STATE)
1227 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1228 break;
1230 else if (s->procs[i].state == FCGI_RUNNING_STATE)
1232 continue;
1235 schedule_start(s, i);
1236 break;
1239 #ifdef FCGI_DEBUG
1240 if (i >= dynamicMaxClassProcs) {
1241 FCGIDBG1("ignore_job: slots are max'd");
1243 #endif
1244 break;
1245 case FCGI_REQUEST_COMPLETE_JOB:
1246 /* only record stats if we have a structure */
1247 if (s) {
1248 #ifndef WIN32
1249 s->totalConnTime += req_usec;
1250 s->totalQueueTime += q_usec;
1251 #else
1252 s->totalConnTime += cjob->start_time;
1253 s->totalQueueTime += cjob->qsec;
1254 #endif
1256 break;
1259 NextJob:
1261 #ifdef WIN32
1262 /* Cleanup job data */
1263 free(cjob->fs_path);
1264 free(cjob->user);
1265 free(cjob->group);
1266 free(cjob);
1267 cjob = joblist;
1268 #endif
1270 continue;
1272 BagNewServer:
1273 if (sp) ap_destroy_pool(sp);
1275 #ifdef WIN32
1276 free(cjob->fs_path);
1277 free(cjob);
1278 cjob = joblist;
1279 #endif
1282 #ifndef WIN32
1283 if (ptr1 == buf) {
1284 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1285 "FastCGI: really bogus message: \"%s\"", ptr1);
1286 ptr1 += strlen(buf);
1289 buflen -= ptr1 - buf;
1290 if (buflen) {
1291 memmove(buf, ptr1, buflen);
1293 #endif
1295 ap_destroy_pool(tp);
1299 *----------------------------------------------------------------------
1301 * dynamic_kill_idle_fs_procs
1303 * Implement a kill policy for the dynamic FastCGI applications.
1304 * We also update the data structures to reflect the changes.
1306 * Side effects:
1307 * Processes are marked for deletion possibly killed.
1309 *----------------------------------------------------------------------
1311 static void dynamic_kill_idle_fs_procs(void)
1313 fcgi_server *s;
1314 int victims = 0;
1316 for (s = fcgi_servers; s != NULL; s = s->next)
1319 * server's smoothed running time, or if that's 0, the current total
1321 unsigned long connTime;
1324 * maximum number of microseconds that all of a server's running
1325 * processes together could have spent running since the last check
1327 unsigned long totalTime;
1330 * percentage, 0-100, of totalTime that the processes actually used
1332 int loadFactor;
1334 int i;
1335 int really_running = 0;
1337 if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1339 continue;
1342 /* s->numProcesses includes pending kills so get the "active" count */
1343 for (i = 0; i < dynamicMaxClassProcs; ++i)
1345 if (s->procs[i].state == FCGI_RUNNING_STATE) ++really_running;
1348 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1349 totalTime = really_running * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1351 loadFactor = 100 * connTime / totalTime;
1353 if (really_running == 1)
1355 if (loadFactor >= dynamicThreshold1)
1357 continue;
1360 else
1362 int load = really_running / ( really_running - 1) * loadFactor;
1364 if (load >= dynamicThresholdN)
1366 continue;
1371 * Run through the procs to see if we can get away w/o waxing one.
1373 for (i = 0; i < dynamicMaxClassProcs; ++i)
1375 if (s->procs[i].state == FCGI_START_STATE)
1377 s->procs[i].state = FCGI_READY_STATE;
1378 break;
1380 else if (s->procs[i].state == FCGI_VICTIM_STATE)
1382 break;
1386 if (i >= dynamicMaxClassProcs)
1388 ServerProcess * procs = s->procs;
1389 int youngest = -1;
1391 for (i = 0; i < dynamicMaxClassProcs; ++i)
1393 if (procs[i].state == FCGI_RUNNING_STATE)
1395 if (youngest == -1 || procs[i].start_time >= procs[youngest].start_time)
1397 youngest = i;
1402 if (youngest != -1)
1404 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1405 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1406 s->fs_path, (long) s->procs[youngest].pid);
1408 fcgi_kill(&s->procs[youngest], SIGTERM);
1410 victims++;
1414 * If the number of non-victims is less than or equal to
1415 * the minimum that may be running without being killed off,
1416 * don't select any more victims.
1418 if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1420 break;
1426 #ifdef WIN32
1428 // This is a little bogus, there's gotta be a better way to do this
1429 // Can we use WaitForMultipleObjects()
1430 #define FCGI_PROC_WAIT_TIME 100
1432 void child_wait_thread_main(void *dummy) {
1433 fcgi_server *s;
1434 DWORD dwRet = WAIT_TIMEOUT;
1435 int numChildren;
1436 int i;
1437 int waited;
1439 while (!bTimeToDie) {
1440 waited = 0;
1442 for (s = fcgi_servers; s != NULL; s = s->next) {
1443 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1444 continue;
1446 if (s->directive == APP_CLASS_DYNAMIC) {
1447 numChildren = dynamicMaxClassProcs;
1449 else {
1450 numChildren = s->numProcesses;
1453 for (i=0; i < numChildren; i++) {
1454 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1456 DWORD exitStatus = 0;
1458 /* timeout is currently set for 100 miliecond */
1459 /* it may need to be longer or user customizable */
1460 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1462 waited = 1;
1464 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1465 /* a child fs has died */
1466 /* mark the child as dead */
1468 if (s->directive == APP_CLASS_STANDARD) {
1469 /* restart static app */
1470 s->procs[i].state = FCGI_START_STATE;
1471 s->numFailures++;
1473 else {
1474 s->numProcesses--;
1475 fcgi_dynamic_total_proc_count--;
1476 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1478 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1479 s->procs[i].state = FCGI_KILLED_STATE;
1481 else {
1482 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1483 s->numFailures++;
1485 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1486 s->procs[i].state = FCGI_START_STATE;
1488 else {
1489 s->procs[i].state = FCGI_READY_STATE;
1494 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1496 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1497 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1498 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1499 s->fs_path, (long) s->procs[i].pid, exitStatus);
1501 CloseHandle(s->procs[i].handle);
1502 s->procs[i].handle = INVALID_HANDLE_VALUE;
1503 s->procs[i].pid = -1;
1505 /* wake up the main thread */
1506 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1511 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1514 #endif
1516 #ifndef WIN32
1517 static void setup_signals(void)
1519 struct sigaction sa;
1521 /* Setup handlers */
1523 sa.sa_handler = signal_handler;
1524 sigemptyset(&sa.sa_mask);
1525 sa.sa_flags = 0;
1527 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1528 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1529 "sigaction(SIGTERM) failed");
1531 /* httpd restart */
1532 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1533 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1534 "sigaction(SIGHUP) failed");
1536 /* httpd graceful restart */
1537 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1538 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1539 "sigaction(SIGUSR1) failed");
1541 /* read messages from request handlers - kill interval expired */
1542 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1543 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1544 "sigaction(SIGALRM) failed");
1546 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1547 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1548 "sigaction(SIGCHLD) failed");
1551 #endif
1553 #if !defined(WIN32) && !defined(APACHE2)
1554 int fcgi_pm_main(void *dummy, child_info *info)
1555 #else
1556 void fcgi_pm_main(void *dummy)
1557 #endif
1559 fcgi_server *s;
1560 unsigned int i;
1561 int read_ready = 0;
1562 int alarmLeft = 0;
1564 #ifdef WIN32
1565 DWORD dwRet;
1566 HANDLE child_wait_thread = INVALID_HANDLE_VALUE;
1567 #else
1568 int callWaitPid, callDynamicProcs;
1569 #endif
1571 #ifdef WIN32
1572 // Add SystemRoot to the dynamic environment
1573 char ** envp = dynamicEnvp;
1574 for (i = 0; *envp; ++i) {
1575 ++envp;
1577 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1579 #else
1581 #ifndef APACHE2
1582 reduce_privileges();
1583 change_process_name("fcgi-pm");
1584 #endif
1586 close(fcgi_pm_pipe[1]);
1587 setup_signals();
1589 if (fcgi_wrapper) {
1590 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1591 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1593 #endif
1595 /* Initialize AppClass */
1596 for (s = fcgi_servers; s != NULL; s = s->next)
1598 if (s->directive != APP_CLASS_STANDARD)
1599 continue;
1601 #ifdef WIN32
1602 if (s->socket_path)
1603 s->listenFd = 0;
1604 #endif
1606 for (i = 0; i < s->numProcesses; ++i)
1607 s->procs[i].state = FCGI_START_STATE;
1610 #ifdef WIN32
1611 child_wait_thread = (HANDLE) _beginthread(child_wait_thread_main, 0, NULL);
1613 if (child_wait_thread == (HANDLE) -1)
1615 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1616 "FastCGI: failed to create process manager's wait thread!");
1619 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1620 "FastCGI: process manager initialized");
1621 #else
1622 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1623 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1624 #endif
1626 now = time(NULL);
1629 * Loop until SIGTERM
1631 for (;;) {
1632 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1633 #ifdef WIN32
1634 time_t expire;
1635 #else
1636 pid_t childPid;
1637 int waitStatus;
1638 #endif
1639 unsigned int numChildren;
1642 * If we came out of sigsuspend() for any reason other than
1643 * SIGALRM, pick up where we left off.
1645 if (alarmLeft)
1646 sleepSeconds = alarmLeft;
1649 * Examine each configured AppClass for a process that needs
1650 * starting. Compute the earliest time when the start should
1651 * be attempted, starting it now if the time has passed. Also,
1652 * remember that we do NOT need to restart externally managed
1653 * FastCGI applications.
1655 for (s = fcgi_servers; s != NULL; s = s->next)
1657 if (s->directive == APP_CLASS_EXTERNAL)
1658 continue;
1660 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1661 ? dynamicMaxClassProcs
1662 : s->numProcesses;
1664 for (i = 0; i < numChildren; ++i)
1666 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1668 int restart = (s->procs[i].pid < 0);
1669 time_t restartTime = s->restartTime;
1671 if (s->bad)
1673 /* we've gone to using the badDelay, the only thing that
1674 resets bad is when badDelay has expired. but numFailures
1675 is only just set below its threshold. the proc's
1676 start_times are all reset when the bad is. the numFailures
1677 is reset when we see an app run for a period */
1679 s->procs[i].start_time = 0;
1682 if (s->numFailures > MAX_FAILED_STARTS)
1684 time_t last_start_time = s->procs[i].start_time;
1686 if (last_start_time && now - last_start_time > RUNTIME_SUCCESS_INTERVAL)
1688 s->bad = 0;
1689 s->numFailures = 0;
1691 else
1693 unsigned int j;
1695 for (j = 0; j < numChildren; ++j)
1697 if (s->procs[j].pid <= 0) continue;
1698 if (s->procs[j].state != FCGI_RUNNING_STATE) continue;
1699 if (s->procs[j].start_time == 0) continue;
1700 if (now - s->procs[j].start_time > RUNTIME_SUCCESS_INTERVAL) break;
1703 if (j >= numChildren)
1705 s->bad = 1;
1707 else
1709 s->bad = 0;
1710 s->numFailures = 0;
1715 if (s->bad)
1717 restartTime += FAILED_STARTS_DELAY;
1719 else
1721 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1724 if (restartTime <= now)
1726 if (s->bad)
1728 s->bad = 0;
1729 s->numFailures = MAX_FAILED_STARTS;
1732 if (s->listenFd < 0 && init_listen_sock(s))
1734 if (sleepSeconds > s->initStartDelay)
1735 sleepSeconds = s->initStartDelay;
1736 break;
1738 #ifndef WIN32
1739 if (caughtSigTerm) {
1740 goto ProcessSigTerm;
1742 #endif
1743 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1744 if (s->procs[i].pid <= 0) {
1745 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1746 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1747 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1748 s->fs_path);
1750 sleepSeconds = min(sleepSeconds,
1751 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1753 ap_assert(s->procs[i].pid < 0);
1754 break;
1757 s->procs[i].start_time = now;
1758 s->restartTime = now;
1760 if (s->startTime == 0) {
1761 s->startTime = now;
1764 if (s->directive == APP_CLASS_DYNAMIC) {
1765 s->numProcesses++;
1766 fcgi_dynamic_total_proc_count++;
1767 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1770 s->procs[i].state = FCGI_RUNNING_STATE;
1772 if (fcgi_wrapper) {
1773 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1774 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1775 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1776 s->fs_path, (long) s->uid, (long) s->gid,
1777 restart ? "re" : "", (long) s->procs[i].pid);
1779 else {
1780 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1781 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1782 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1783 s->fs_path, restart ? "re" : "", (long) s->procs[i].pid);
1785 ap_assert(s->procs[i].pid > 0);
1786 } else {
1787 sleepSeconds = min(sleepSeconds, restartTime - now);
1793 #ifndef WIN32
1795 if(caughtSigTerm) {
1796 goto ProcessSigTerm;
1798 if((!caughtSigChld) && (!caughtSigAlarm)) {
1799 fd_set rfds;
1801 alarm(sleepSeconds);
1803 FD_ZERO(&rfds);
1804 FD_SET(fcgi_pm_pipe[0], &rfds);
1805 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1807 alarmLeft = alarm(0);
1809 callWaitPid = caughtSigChld;
1810 caughtSigChld = FALSE;
1811 callDynamicProcs = caughtSigAlarm;
1812 caughtSigAlarm = FALSE;
1814 now = time(NULL);
1817 * Dynamic fcgi process management
1819 if((callDynamicProcs) || (!callWaitPid)) {
1820 dynamic_read_msgs(read_ready);
1821 if(fcgi_dynamic_epoch == 0) {
1822 fcgi_dynamic_epoch = now;
1824 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1825 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1826 dynamic_kill_idle_fs_procs();
1827 fcgi_dynamic_epoch = now;
1831 if(!callWaitPid) {
1832 continue;
1835 /* We've caught SIGCHLD, so find out who it was using waitpid,
1836 * write a log message and update its data structure. */
1838 for (;;) {
1839 if (caughtSigTerm)
1840 goto ProcessSigTerm;
1842 childPid = waitpid(-1, &waitStatus, WNOHANG);
1844 if (childPid == -1 || childPid == 0)
1845 break;
1847 for (s = fcgi_servers; s != NULL; s = s->next) {
1848 if (s->directive == APP_CLASS_EXTERNAL)
1849 continue;
1851 if (s->directive == APP_CLASS_DYNAMIC)
1852 numChildren = dynamicMaxClassProcs;
1853 else
1854 numChildren = s->numProcesses;
1856 for (i = 0; i < numChildren; i++) {
1857 if (s->procs[i].pid == childPid)
1858 goto ChildFound;
1862 /* TODO: print something about this unknown child */
1863 continue;
1865 ChildFound:
1866 s->procs[i].pid = -1;
1868 if (s->directive == APP_CLASS_STANDARD) {
1869 /* Always restart static apps */
1870 s->procs[i].state = FCGI_START_STATE;
1871 s->numFailures++;
1873 else {
1874 s->numProcesses--;
1875 fcgi_dynamic_total_proc_count--;
1877 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1878 s->procs[i].state = FCGI_KILLED_STATE;
1880 else {
1881 /* A dynamic app died or exited without provocation from the PM */
1882 s->numFailures++;
1884 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1885 s->procs[i].state = FCGI_START_STATE;
1886 else
1887 s->procs[i].state = FCGI_READY_STATE;
1891 if (WIFEXITED(waitStatus)) {
1892 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1893 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1894 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1895 s->fs_path, (long) childPid, WEXITSTATUS(waitStatus));
1897 else if (WIFSIGNALED(waitStatus)) {
1898 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1899 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1900 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1901 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus),
1902 #ifdef WCOREDUMP
1903 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1904 #else
1905 "");
1906 #endif
1908 else if (WIFSTOPPED(waitStatus)) {
1909 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1910 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1911 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1912 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus));
1914 } /* for (;;), waitpid() */
1916 #else /* WIN32 */
1918 /* wait for an event to occur or timer expires */
1919 expire = time(NULL) + sleepSeconds;
1920 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1922 if (dwRet == WAIT_FAILED) {
1923 /* There is something seriously wrong here */
1924 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1925 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1926 bTimeToDie = TRUE;
1929 if (dwRet != WAIT_TIMEOUT) {
1930 now = time(NULL);
1932 if (now < expire)
1933 alarmLeft = expire - now;
1937 * Dynamic fcgi process management
1939 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1940 if (dwRet == MBOX_EVENT) {
1941 read_ready = 1;
1944 now = time(NULL);
1946 dynamic_read_msgs(read_ready);
1948 if(fcgi_dynamic_epoch == 0) {
1949 fcgi_dynamic_epoch = now;
1952 if ((now-fcgi_dynamic_epoch >= (int) dynamicKillInterval) ||
1953 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1954 dynamic_kill_idle_fs_procs();
1955 fcgi_dynamic_epoch = now;
1957 read_ready = 0;
1959 else if (dwRet == WAKE_EVENT) {
1960 continue;
1962 else if (dwRet == TERM_EVENT) {
1963 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1964 "FastCGI: Termination event received process manager shutting down");
1966 bTimeToDie = TRUE;
1967 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
1969 goto ProcessSigTerm;
1971 else {
1972 // Have an received an unknown event - should not happen
1973 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1974 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1976 bTimeToDie = TRUE;
1977 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
1979 goto ProcessSigTerm;
1982 #endif /* WIN32 */
1984 } /* for (;;), the whole shoot'n match */
1986 ProcessSigTerm:
1988 * Kill off the children, then exit.
1990 shutdown_all();
1992 #ifdef WIN32
1993 return;
1994 #else
1995 exit(0);
1996 #endif
1999 #ifdef WIN32
2000 int fcgi_pm_add_job(fcgi_pm_job *new_job)
2002 int rv = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
2004 if (rv != WAIT_OBJECT_0 && rv != WAIT_ABANDONED)
2006 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2007 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2008 return -1;
2011 new_job->next = fcgi_dynamic_mbox;
2012 fcgi_dynamic_mbox = new_job;
2014 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
2016 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2017 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
2020 return 0;
2022 #endif