Add -user and -group options to FastCgiServer and FastCgiExternalServer.
[mod_fastcgi.git] / fcgi_pm.c
blobe55b8e7ab2a5ee08b17512d28ffc30a6a82649cf
1 /*
2 * $Id: fcgi_pm.c,v 1.78 2002/09/21 14:22:46 robs Exp $
3 */
6 #include "fcgi.h"
8 #if defined(APACHE2) && !defined(WIN32)
9 #include <pwd.h>
10 #include "unixd.h"
11 #endif
13 #ifdef _HPUX_SOURCE
14 #include <unistd.h>
15 #define seteuid(arg) setresuid(-1, (arg), -1)
16 #endif
18 int fcgi_dynamic_total_proc_count = 0; /* number of running apps */
19 time_t fcgi_dynamic_epoch = 0; /* last time kill_procs was
20 * invoked by process mgr */
21 time_t fcgi_dynamic_last_analyzed = 0; /* last time calculation was
22 * made for the dynamic procs */
24 static time_t now = 0;
26 #ifdef WIN32
27 #pragma warning ( disable : 4100 4102 )
28 static BOOL bTimeToDie = FALSE; /* process termination flag */
29 HANDLE fcgi_event_handles[3];
30 #ifndef SIGKILL
31 #define SIGKILL 9
32 #endif
33 #endif
36 #ifndef WIN32
37 static int seteuid_root(void)
39 int rc = seteuid((uid_t)0);
40 if (rc == -1) {
41 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
42 "FastCGI: seteuid(0) failed");
44 return rc;
47 static int seteuid_user(void)
49 int rc = seteuid(ap_user_id);
50 if (rc == -1) {
51 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
52 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id);
54 return rc;
56 #endif
59 * Signal the process to exit. How (or if) the process responds
60 * depends on the FastCGI application library (esp. on Win32) and
61 * possibly application code (signal handlers and whether or not
62 * SA_RESTART is on). At any rate, we send the signal with the
63 * hopes that the process will exit on its own. Later, as we
64 * review the state of application processes, if we see one marked
65 * for death, but that hasn't died within a specified period of
66 * time, fcgi_kill() is called again with a KILL)
68 static void fcgi_kill(ServerProcess *process, int sig)
70 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process->pid, sig);
72 process->state = FCGI_VICTIM_STATE;
74 #ifdef WIN32
76 if (sig == SIGTERM)
78 SetEvent(process->terminationEvent);
80 else if (sig == SIGKILL)
82 TerminateProcess(process->handle, 1);
84 else
86 ap_assert(0);
89 #else /* !WIN32 */
91 if (fcgi_wrapper)
93 seteuid_root();
96 kill(process->pid, sig);
98 if (fcgi_wrapper)
100 seteuid_user();
103 #endif /* !WIN32 */
106 /*******************************************************************************
107 * Send SIGTERM to each process in the server class, remove socket
108 * file if appropriate. Currently this is only called when the PM is shutting
109 * down and thus memory isn't freed and sockets and files aren't closed.
111 static void shutdown_all()
113 fcgi_server *s = fcgi_servers;
115 while (s)
117 ServerProcess *proc = s->procs;
118 int i;
119 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
120 ? dynamicMaxClassProcs
121 : s->numProcesses;
123 #ifndef WIN32
124 if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL)
126 /* Remove the socket file */
127 if (unlink(s->socket_path) != 0 && errno != ENOENT) {
128 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
129 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
130 s->socket_path,
131 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path);
134 #endif
136 /* Send TERM to all processes */
137 for (i = 0; i < numChildren; i++, proc++)
139 if (proc->state == FCGI_RUNNING_STATE)
141 fcgi_kill(proc, SIGTERM);
145 s = s->next;
148 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
151 * WIN32 applications may not have support for the shutdown event
152 * depending on their application library version
155 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT);
156 s = fcgi_servers;
158 while (s)
160 ServerProcess *proc = s->procs;
161 int i;
162 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
163 ? dynamicMaxClassProcs
164 : s->numProcesses;
166 /* Send KILL to all processes */
167 for (i = 0; i < numChildren; i++, proc++)
169 if (proc->state == FCGI_RUNNING_STATE)
171 fcgi_kill(proc, SIGKILL);
175 s = s->next;
178 #endif /* WIN32 */
181 static int init_listen_sock(fcgi_server * fs)
183 ap_assert(fs->directive != APP_CLASS_EXTERNAL);
185 /* Create the socket */
186 if ((fs->listenFd = socket(fs->socket_addr->sa_family, SOCK_STREAM, 0)) < 0)
188 #ifdef WIN32
189 errno = WSAGetLastError(); // Not sure if this will work as expected
190 #endif
191 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
192 "FastCGI: can't create %sserver \"%s\": socket() failed",
193 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
194 fs->fs_path);
195 return -1;
198 #ifndef WIN32
199 if (fs->socket_addr->sa_family == AF_UNIX)
201 /* Remove any existing socket file.. just in case */
202 unlink(((struct sockaddr_un *)fs->socket_addr)->sun_path);
204 else
205 #endif
207 int flag = 1;
208 setsockopt(fs->listenFd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
211 /* Bind it to the socket_addr */
212 if (bind(fs->listenFd, fs->socket_addr, fs->socket_addr_len))
214 char port[11];
216 #ifdef WIN32
217 errno = WSAGetLastError();
218 #endif
219 ap_snprintf(port, sizeof(port), "port=%d",
220 ((struct sockaddr_in *)fs->socket_addr)->sin_port);
222 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
223 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
224 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
225 fs->fs_path,
226 #ifndef WIN32
227 (fs->socket_addr->sa_family == AF_UNIX) ?
228 ((struct sockaddr_un *)fs->socket_addr)->sun_path :
229 #endif
230 port);
233 #ifndef WIN32
234 /* Twiddle Unix socket permissions */
235 else if (fs->socket_addr->sa_family == AF_UNIX
236 && chmod(((struct sockaddr_un *)fs->socket_addr)->sun_path, S_IRUSR | S_IWUSR))
238 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
239 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
240 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
241 fs->fs_path);
243 #endif
245 /* Set to listen */
246 else if (listen(fs->listenFd, fs->listenQueueDepth))
248 #ifdef WIN32
249 errno = WSAGetLastError();
250 #endif
251 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
252 "FastCGI: can't create %sserver \"%s\": listen() failed",
253 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
254 fs->fs_path);
256 else
258 return 0;
261 close(fs->listenFd);
262 fs->listenFd = -1;
263 return -2;
267 *----------------------------------------------------------------------
269 * pm_main
271 * The FastCGI process manager, which runs as a separate
272 * process responsible for:
273 * - Starting all the FastCGI proceses.
274 * - Restarting any of these processes that die (indicated
275 * by SIGCHLD).
276 * - Catching SIGTERM and relaying it to all the FastCGI
277 * processes before exiting.
279 * Inputs:
280 * Uses global variable fcgi_servers.
282 * Results:
283 * Does not return.
285 * Side effects:
286 * Described above.
288 *----------------------------------------------------------------------
290 #ifndef WIN32
291 static int caughtSigTerm = FALSE;
292 static int caughtSigChld = FALSE;
293 static int caughtSigAlarm = FALSE;
295 static void signal_handler(int signo)
297 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
298 /* SIGUSR1 & SIGHUP are sent by apache to its process group
299 * when apache get 'em. Apache follows up (1.2.x) with attacks
300 * on each of its child processes, but we've got the KillMgr
301 * sitting between us so we never see the KILL. The main loop
302 * in ProcMgr also checks to see if the KillMgr has terminated,
303 * and if it has, we handl it as if we should shutdown too. */
304 caughtSigTerm = TRUE;
305 } else if(signo == SIGCHLD) {
306 caughtSigChld = TRUE;
307 } else if(signo == SIGALRM) {
308 caughtSigAlarm = TRUE;
311 #endif
314 *----------------------------------------------------------------------
316 * spawn_fs_process --
318 * Fork and exec the specified fcgi process.
320 * Results:
321 * 0 for successful fork, -1 for failed fork.
323 * In case the child fails before or in the exec, the child
324 * obtains the error log by calling getErrLog, logs
325 * the error, and exits with exit status = errno of
326 * the failed system call.
328 * Side effects:
329 * Child process created.
331 *----------------------------------------------------------------------
333 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
335 #ifndef WIN32
337 pid_t child_pid;
338 int i;
339 char *dirName;
340 char *dnEnd, *failedSysCall;
342 child_pid = fork();
343 if (child_pid) {
344 return child_pid;
347 /* We're the child. We're gonna exec() so pools don't matter. */
349 dnEnd = strrchr(fs->fs_path, '/');
350 if (dnEnd == NULL) {
351 dirName = "./";
352 } else {
353 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
354 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
356 if (chdir(dirName) < 0) {
357 failedSysCall = "chdir()";
358 goto FailedSystemCallExit;
361 #ifndef __EMX__
362 /* OS/2 dosen't support nice() */
363 if (fs->processPriority != 0) {
364 if (nice(fs->processPriority) == -1) {
365 failedSysCall = "nice()";
366 goto FailedSystemCallExit;
369 #endif
371 /* Open the listenFd on spec'd fd */
372 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
373 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
375 /* Close all other open fds, except stdout/stderr. Leave these two open so
376 * FastCGI applications don't have to find and fix ALL 3rd party libs that
377 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
378 * main server error_log - @@@ provide a directive control where this goes.
380 ap_error_log2stderr(fcgi_apache_main_server);
381 dup2(2, 1);
382 for (i = 0; i < FCGI_MAX_FD; i++) {
383 if (i != FCGI_LISTENSOCK_FILENO && i != 2 && i != 1) {
384 close(i);
388 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
389 * install its own handler. */
390 signal(SIGPIPE, SIG_IGN);
392 if (fcgi_wrapper && (fcgi_user_id != fs->uid || fcgi_group_id != fs->gid)) {
393 char *shortName = strrchr(fs->fs_path, '/') + 1;
395 /* Relinquish our root real uid powers */
396 seteuid_root();
397 setuid(ap_user_id);
399 do {
400 execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group, shortName, NULL, fs->envp);
401 } while (errno == EINTR);
403 else {
404 do {
405 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
406 } while (errno == EINTR);
409 failedSysCall = "execle()";
411 FailedSystemCallExit:
412 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
413 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
414 exit(-1);
416 /* avoid an irrelevant compiler warning */
417 return(0);
419 #else /* WIN32 */
421 #ifdef APACHE2
423 /* based on mod_cgi.c:run_cgi_child() */
425 apr_pool_t * tp;
426 char * termination_env_string;
427 HANDLE listen_handle = INVALID_HANDLE_VALUE;
428 apr_procattr_t * procattr;
429 apr_proc_t proc = { 0 };
430 apr_file_t * file;
431 int i = 0;
433 if (apr_pool_create(&tp, fcgi_config_pool))
434 return 0;
436 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
437 if (process->terminationEvent == NULL)
438 goto CLEANUP;
440 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
442 termination_env_string = ap_psprintf(tp,
443 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
445 while (fs->envp[i]) i++;
446 fs->envp[i++] = termination_env_string;
447 fs->envp[i] = (char *) fs->mutex_env_string;
449 ap_assert(fs->envp[i + 1] == NULL);
451 if (fs->socket_path)
453 SECURITY_ATTRIBUTES sa = { 0 };
455 sa.bInheritHandle = TRUE;
456 sa.nLength = sizeof(sa);
458 listen_handle = CreateNamedPipe(fs->socket_path,
459 PIPE_ACCESS_DUPLEX,
460 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
461 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
463 if (listen_handle == INVALID_HANDLE_VALUE)
465 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
466 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
467 fs->fs_path);
468 goto CLEANUP;
471 else
473 listen_handle = (HANDLE) fs->listenFd;
476 if (apr_procattr_create(&procattr, tp))
477 goto CLEANUP;
479 if (apr_procattr_dir_set(procattr, ap_make_dirstr_parent(tp, fs->fs_path)))
480 goto CLEANUP;
482 if (apr_procattr_detach_set(procattr, 1))
483 goto CLEANUP;
485 if (apr_os_file_put(&file, &listen_handle, 0, tp))
486 goto CLEANUP;
488 /* procattr is opaque so we have to use this - unfortuantely it dups */
489 if (apr_procattr_child_in_set(procattr, file, NULL))
490 goto CLEANUP;
492 if (apr_proc_create(&proc, fs->fs_path, NULL, fs->envp, procattr, tp))
493 goto CLEANUP;
495 process->handle = proc.hproc;
498 CLEANUP:
500 if (i)
502 fs->envp[i - 1] = NULL;
505 ap_destroy_pool(tp);
507 return proc.pid;
509 #else /* WIN32 && !APACHE2 */
511 /* Adapted from Apache's util_script.c ap_call_exec() */
512 char *interpreter = NULL;
513 char *quoted_filename;
514 char *pCommand;
515 char *pEnvBlock, *pNext;
517 int i = 0;
518 int iEnvBlockLen = 1;
520 file_type_e fileType;
522 STARTUPINFO si;
523 PROCESS_INFORMATION pi;
525 request_rec r;
526 pid_t pid = -1;
528 pool * tp = ap_make_sub_pool(fcgi_config_pool);
530 HANDLE listen_handle;
531 char * termination_env_string = NULL;
533 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
534 if (process->terminationEvent == NULL)
536 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
537 "FastCGI: can't create termination event for server \"%s\", "
538 "CreateEvent() failed", fs->fs_path);
539 goto CLEANUP;
541 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
543 termination_env_string = ap_psprintf(tp,
544 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
546 if (fs->socket_path)
548 SECURITY_ATTRIBUTES sa;
550 sa.lpSecurityDescriptor = NULL;
551 sa.bInheritHandle = TRUE;
552 sa.nLength = sizeof(sa);
554 listen_handle = CreateNamedPipe(fs->socket_path,
555 PIPE_ACCESS_DUPLEX,
556 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
557 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
559 if (listen_handle == INVALID_HANDLE_VALUE)
561 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
562 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
563 goto CLEANUP;
566 else
568 listen_handle = (HANDLE) fs->listenFd;
571 memset(&si, 0, sizeof(si));
572 memset(&pi, 0, sizeof(pi));
573 memset(&r, 0, sizeof(r));
575 // Can up a fake request to pass to ap_get_win32_interpreter()
576 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
577 r.server = fcgi_apache_main_server;
578 r.filename = (char *) fs->fs_path;
579 r.pool = tp;
581 fileType = ap_get_win32_interpreter(&r, &interpreter);
583 if (fileType == eFileTypeUNKNOWN) {
584 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
585 "FastCGI: %s is not executable; ensure interpreted scripts have "
586 "\"#!\" as their first line",
587 fs->fs_path);
588 ap_destroy_pool(tp);
589 goto CLEANUP;
593 * We have the interpreter (if there is one) and we have
594 * the arguments (if there are any).
595 * Build the command string to pass to CreateProcess.
597 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
598 if (interpreter && *interpreter) {
599 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
601 else {
602 pCommand = quoted_filename;
606 * Make child process use hPipeOutputWrite as standard out,
607 * and make sure it does not show on screen.
609 si.cb = sizeof(si);
610 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
611 si.wShowWindow = SW_HIDE;
612 si.hStdInput = listen_handle;
614 // XXX These should be open to the error_log
615 si.hStdOutput = INVALID_HANDLE_VALUE;
616 si.hStdError = INVALID_HANDLE_VALUE;
619 * Win32's CreateProcess call requires that the environment
620 * be passed in an environment block, a null terminated block of
621 * null terminated strings.
622 * @todo we should store the env in this format for win32.
624 while (fs->envp[i])
626 iEnvBlockLen += strlen(fs->envp[i]) + 1;
627 i++;
630 iEnvBlockLen += strlen(termination_env_string) + 1;
631 iEnvBlockLen += strlen(fs->mutex_env_string) + 1;
633 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
635 i = 0;
636 pNext = pEnvBlock;
637 while (fs->envp[i])
639 strcpy(pNext, fs->envp[i]);
640 pNext += strlen(pNext) + 1;
641 i++;
644 strcpy(pNext, termination_env_string);
645 pNext += strlen(pNext) + 1;
646 strcpy(pNext, fs->mutex_env_string);
648 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
650 pEnvBlock,
651 ap_make_dirstr_parent(tp, fs->fs_path),
652 &si, &pi))
654 /* Hack to get 16-bit CGI's working. It works for all the
655 * standard modules shipped with Apache. pi.dwProcessId is 0
656 * for 16-bit CGIs and all the Unix specific code that calls
657 * ap_call_exec interprets this as a failure case. And we can't
658 * use -1 either because it is mapped to 0 by the caller.
660 pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId;
662 process->handle = pi.hProcess;
663 CloseHandle(pi.hThread);
666 if (fs->socket_path)
668 CloseHandle(listen_handle);
671 CLEANUP:
673 ap_destroy_pool(tp);
675 return pid;
677 #endif /* !APACHE2 */
678 #endif /* WIN32 */
681 #ifndef WIN32
682 static void reduce_privileges(void)
684 const char *name;
686 if (geteuid() != 0)
687 return;
689 #ifndef __EMX__
690 /* Get username if passed as a uid */
691 if (ap_user_name[0] == '#') {
692 uid_t uid = atoi(&ap_user_name[1]);
693 struct passwd *ent = getpwuid(uid);
695 if (ent == NULL) {
696 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
697 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
698 "you probably need to modify the User directive", (unsigned)uid);
699 exit(1);
701 name = ent->pw_name;
703 else
704 name = ap_user_name;
706 /* Change Group */
707 if (setgid(ap_group_id) == -1) {
708 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
709 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
710 exit(1);
713 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
715 /* Initialize supplementary groups */
716 if (initgroups(name, ap_group_id) == -1) {
717 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
718 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
719 name, (unsigned)ap_group_id);
720 exit(1);
722 #endif /* __EMX__ */
724 /* Change User */
725 if (fcgi_wrapper) {
726 if (seteuid_user() == -1) {
727 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
728 "FastCGI: process manager exiting, failed to reduce privileges");
729 exit(1);
732 else {
733 if (setuid(ap_user_id) == -1) {
734 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
735 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
736 exit(1);
741 /*************
742 * Change the name of this process - best we can easily.
744 static void change_process_name(const char * const name)
746 /* under Apache2, ap_server_argv0 is const */
747 strncpy((char *) ap_server_argv0, name, strlen(ap_server_argv0));
749 #endif /* !WIN32 */
751 static void schedule_start(fcgi_server *s, int proc)
753 /* If we've started one recently, don't register another */
754 time_t time_passed = now - s->restartTime;
756 if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay))
757 || ((s->procs[proc].pid == 0) && (time_passed < s->initStartDelay)))
759 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);
760 return;
763 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
764 s->procs[proc].state = FCGI_START_STATE;
765 if (proc == dynamicMaxClassProcs - 1) {
766 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
767 "FastCGI: scheduled the %sstart of the last (dynamic) server "
768 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
769 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
774 *----------------------------------------------------------------------
776 * dynamic_read_msgs
778 * Removes the records written by request handlers and decodes them.
779 * We also update the data structures to reflect the changes.
781 *----------------------------------------------------------------------
784 static void dynamic_read_msgs(int read_ready)
786 fcgi_server *s;
787 int rc;
789 #ifndef WIN32
790 static int buflen = 0;
791 static char buf[FCGI_MSGS_BUFSIZE + 1];
792 char *ptr1, *ptr2, opcode;
793 char execName[FCGI_MAXPATH + 1];
794 char user[MAX_USER_NAME_LEN + 2];
795 char group[MAX_GID_CHAR_LEN + 1];
796 unsigned long q_usec = 0UL, req_usec = 0UL;
797 #else
798 fcgi_pm_job *joblist = NULL;
799 fcgi_pm_job *cjob = NULL;
800 #endif
802 pool *sp = NULL, *tp;
804 #ifndef WIN32
805 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
806 #endif
809 * To prevent the idle application from running indefinitely, we
810 * check the timer and if it is expired, we recompute the values
811 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
812 * message is received, only updates are made to the data structures.
814 if (fcgi_dynamic_last_analyzed == 0) {
815 fcgi_dynamic_last_analyzed = now;
817 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
818 for (s = fcgi_servers; s != NULL; s = s->next) {
819 if (s->directive != APP_CLASS_DYNAMIC)
820 break;
822 /* Advance the last analyzed timestamp by the elapsed time since
823 * it was last set. Round the increase down to the nearest
824 * multiple of dynamicUpdateInterval */
826 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
827 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
828 s->totalConnTime = 0UL;
829 s->totalQueueTime = 0UL;
833 if (read_ready <= 0) {
834 return;
837 #ifndef WIN32
838 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
839 if (rc <= 0) {
840 if (!caughtSigTerm) {
841 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
842 "FastCGI: read() from pipe failed (%d)", rc);
843 if (rc == 0) {
844 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
845 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
846 caughtSigTerm = TRUE;
849 return;
851 buflen += rc;
852 buf[buflen] = '\0';
854 #else
856 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
857 * request to do something) and/or when a timeout expires.
858 * There really should be no reason why this wait would get stuck
859 * but there's no point in waiting forever. */
861 rc = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
863 if (rc != WAIT_OBJECT_0 && rc != WAIT_ABANDONED)
865 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
866 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
867 return;
870 joblist = fcgi_dynamic_mbox;
871 fcgi_dynamic_mbox = NULL;
873 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
875 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
876 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
879 cjob = joblist;
880 #endif
882 #ifdef APACHE2
883 apr_pool_create(&tp, fcgi_config_pool);
884 #else
885 tp = ap_make_sub_pool(fcgi_config_pool);
886 #endif
888 #ifndef WIN32
889 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
890 int scan_failed = 0;
892 ptr2 = strchr(ptr1, '*');
893 if (ptr2) {
894 *ptr2++ = '\0';
896 else {
897 break;
900 opcode = *ptr1;
902 switch (opcode)
904 case FCGI_SERVER_START_JOB:
905 case FCGI_SERVER_RESTART_JOB:
907 if (sscanf(ptr1, "%c %s %16s %15s",
908 &opcode, execName, user, group) != 4)
910 scan_failed = 1;
912 break;
914 case FCGI_REQUEST_TIMEOUT_JOB:
916 if (sscanf(ptr1, "%c %s %16s %15s",
917 &opcode, execName, user, group) != 4)
919 scan_failed = 1;
921 break;
923 case FCGI_REQUEST_COMPLETE_JOB:
925 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
926 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
928 scan_failed = 1;
930 break;
932 default:
934 scan_failed = 1;
935 break;
938 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode, execName, user, group, q_usec, req_usec);
940 if (scan_failed) {
941 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
942 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
943 goto NextJob;
945 #else
946 /* Update data structures for processing */
947 while (cjob != NULL) {
948 joblist = cjob->next;
949 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
950 #endif
952 #ifndef WIN32
953 s = fcgi_util_fs_get(execName, user, group);
954 #else
955 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
956 #endif
958 #ifndef WIN32
959 if (s==NULL && opcode != FCGI_REQUEST_COMPLETE_JOB)
960 #else
961 if (s==NULL && cjob->id != FCGI_REQUEST_COMPLETE_JOB)
962 #endif
964 #ifdef WIN32
966 HANDLE mutex = CreateMutex(NULL, FALSE, cjob->fs_path);
968 if (mutex == NULL)
970 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
971 "FastCGI: can't create accept mutex "
972 "for (dynamic) server \"%s\"", cjob->fs_path);
973 goto BagNewServer;
976 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
977 #else
978 const char *err;
979 #endif
981 /* Create a perm subpool to hold the new server data,
982 * we can destroy it if something doesn't pan out */
983 #ifdef APACHE2
984 apr_pool_create(&sp, fcgi_config_pool);
985 #else
986 sp = ap_make_sub_pool(fcgi_config_pool);
987 #endif
989 /* Create a new "dynamic" server */
990 s = fcgi_util_fs_new(sp);
992 s->directive = APP_CLASS_DYNAMIC;
993 s->restartDelay = dynamicRestartDelay;
994 s->listenQueueDepth = dynamicListenQueueDepth;
995 s->initStartDelay = dynamicInitStartDelay;
996 s->envp = dynamicEnvp;
997 s->flush = dynamicFlush;
999 #ifdef WIN32
1000 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
1001 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
1002 #else
1003 s->fs_path = ap_pstrdup(sp, execName);
1004 #endif
1005 ap_getparents(s->fs_path);
1006 ap_no2slash(s->fs_path);
1007 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
1009 /* XXX the socket_path (both Unix and Win) *is* deducible and
1010 * thus can and will be used by other apache instances without
1011 * the use of shared data regarding the processes serving the
1012 * requests. This can result in slightly unintuitive process
1013 * counts and security implications. This is prevented
1014 * if suexec (Unix) is in use. This is both a feature and a flaw.
1015 * Changing it now would break existing installations. */
1017 #ifndef WIN32
1018 /* Create socket file's path */
1019 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
1020 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1022 /* Create sockaddr, prealloc it so it won't get created in tp */
1023 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
1024 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
1025 &s->socket_addr_len, s->socket_path);
1026 if (err) {
1027 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1028 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
1029 goto BagNewServer;
1032 if (init_listen_sock(s)) {
1033 goto BagNewServer;
1036 /* If a wrapper is being used, config user/group info */
1037 if (fcgi_wrapper) {
1038 if (user[0] == '~') {
1039 /* its a user dir uri, the rest is a username, not a uid */
1040 struct passwd *pw = getpwnam(&user[1]);
1042 if (!pw) {
1043 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1044 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1045 execName, &user[1]);
1046 goto BagNewServer;
1048 s->uid = pw->pw_uid;
1049 s->user = ap_pstrdup(sp, user);
1050 s->username = s->user;
1052 s->gid = pw->pw_gid;
1053 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
1055 else {
1056 struct passwd *pw;
1058 s->uid = (uid_t)atol(user);
1059 pw = getpwuid(s->uid);
1060 if (!pw) {
1061 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1062 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1063 execName, (long)s->uid);
1064 goto BagNewServer;
1066 s->user = ap_pstrdup(sp, user);
1067 s->username = ap_pstrdup(sp, pw->pw_name);
1069 s->gid = (gid_t)atol(group);
1070 s->group = ap_pstrdup(sp, group);
1073 #else
1074 /* Create socket file's path */
1075 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
1076 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1077 s->listenFd = 0;
1078 #endif
1080 fcgi_util_fs_add(s);
1082 else {
1083 #ifndef WIN32
1084 if (opcode == FCGI_SERVER_RESTART_JOB) {
1085 #else
1086 if (cjob->id==FCGI_SERVER_RESTART_JOB) {
1087 #endif
1088 /* Check to see if the binary has changed. If so,
1089 * kill the FCGI application processes, and
1090 * restart them.
1092 struct stat stbuf;
1093 int i;
1094 #ifdef WIN32
1095 char * app_path = cjob->fs_path;
1096 #else
1097 char * app_path = execName;
1098 #endif
1100 if (stat(app_path, &stbuf) == 0 && stbuf.st_mtime > s->startTime)
1102 int do_restart = 0;
1104 /* prevent addition restart requests */
1105 s->startTime = now;
1106 #ifndef WIN32
1107 utime(s->socket_path, NULL);
1108 #endif
1110 /* kill old server(s) */
1111 for (i = 0; i < dynamicMaxClassProcs; i++)
1113 if (s->procs[i].pid > 0
1114 && stbuf.st_mtime > s->procs[i].start_time)
1116 fcgi_kill(&s->procs[i], SIGTERM);
1117 do_restart++;
1121 if (do_restart)
1123 ap_log_error(FCGI_LOG_WARN_NOERRNO,
1124 fcgi_apache_main_server, "FastCGI: restarting "
1125 "old server \"%s\" processes, newer version "
1126 "found", app_path);
1130 /* If dynamicAutoRestart, don't mark any new processes
1131 * for starting because we probably got the
1132 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1133 * will be restarting all of those we just killed.
1135 if (dynamicAutoRestart)
1136 goto NextJob;
1138 #ifndef WIN32
1139 else if (opcode == FCGI_SERVER_START_JOB) {
1140 #else
1141 else if (cjob->id==FCGI_SERVER_START_JOB) {
1142 #endif
1143 /* we've been asked to start a process--only start
1144 * it if we're not already running at least one
1145 * instance.
1147 int i;
1149 for (i = 0; i < dynamicMaxClassProcs; i++) {
1150 if (s->procs[i].state == FCGI_RUNNING_STATE)
1151 break;
1153 /* if already running, don't start another one */
1154 if (i < dynamicMaxClassProcs) {
1155 goto NextJob;
1160 #ifndef WIN32
1161 switch (opcode)
1162 #else
1163 switch (cjob->id)
1164 #endif
1166 int i, start;
1168 case FCGI_SERVER_RESTART_JOB:
1170 start = FALSE;
1172 /* We just waxed 'em all. Try to find an idle slot. */
1174 for (i = 0; i < dynamicMaxClassProcs; ++i)
1176 if (s->procs[i].state == FCGI_START_STATE
1177 || s->procs[i].state == FCGI_RUNNING_STATE)
1179 break;
1181 else if (s->procs[i].state == FCGI_KILLED_STATE
1182 || s->procs[i].state == FCGI_READY_STATE)
1184 start = TRUE;
1185 break;
1189 /* Nope, just use the first slot */
1190 if (i == dynamicMaxClassProcs)
1192 start = TRUE;
1193 i = 0;
1196 if (start)
1198 schedule_start(s, i);
1201 break;
1203 case FCGI_SERVER_START_JOB:
1204 case FCGI_REQUEST_TIMEOUT_JOB:
1206 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1208 * Extra instances should have been
1209 * terminated beforehand, probably need
1210 * to increase ProcessSlack parameter
1212 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1213 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1214 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1215 goto NextJob;
1218 /* find next free slot */
1219 for (i = 0; i < dynamicMaxClassProcs; i++)
1221 if (s->procs[i].state == FCGI_START_STATE)
1223 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1224 break;
1226 else if (s->procs[i].state == FCGI_RUNNING_STATE)
1228 continue;
1231 schedule_start(s, i);
1232 break;
1235 #ifdef FCGI_DEBUG
1236 if (i >= dynamicMaxClassProcs) {
1237 FCGIDBG1("ignore_job: slots are max'd");
1239 #endif
1240 break;
1241 case FCGI_REQUEST_COMPLETE_JOB:
1242 /* only record stats if we have a structure */
1243 if (s) {
1244 #ifndef WIN32
1245 s->totalConnTime += req_usec;
1246 s->totalQueueTime += q_usec;
1247 #else
1248 s->totalConnTime += cjob->start_time;
1249 s->totalQueueTime += cjob->qsec;
1250 #endif
1252 break;
1255 NextJob:
1257 #ifdef WIN32
1258 /* Cleanup job data */
1259 free(cjob->fs_path);
1260 free(cjob->user);
1261 free(cjob->group);
1262 free(cjob);
1263 cjob = joblist;
1264 #endif
1266 continue;
1268 BagNewServer:
1269 if (sp) ap_destroy_pool(sp);
1271 #ifdef WIN32
1272 free(cjob->fs_path);
1273 free(cjob);
1274 cjob = joblist;
1275 #endif
1278 #ifndef WIN32
1279 if (ptr1 == buf) {
1280 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1281 "FastCGI: really bogus message: \"%s\"", ptr1);
1282 ptr1 += strlen(buf);
1285 buflen -= ptr1 - buf;
1286 if (buflen) {
1287 memmove(buf, ptr1, buflen);
1289 #endif
1291 ap_destroy_pool(tp);
1295 *----------------------------------------------------------------------
1297 * dynamic_kill_idle_fs_procs
1299 * Implement a kill policy for the dynamic FastCGI applications.
1300 * We also update the data structures to reflect the changes.
1302 * Side effects:
1303 * Processes are marked for deletion possibly killed.
1305 *----------------------------------------------------------------------
1307 static void dynamic_kill_idle_fs_procs(void)
1309 fcgi_server *s;
1310 int victims = 0;
1312 for (s = fcgi_servers; s != NULL; s = s->next)
1315 * server's smoothed running time, or if that's 0, the current total
1317 unsigned long connTime;
1320 * maximum number of microseconds that all of a server's running
1321 * processes together could have spent running since the last check
1323 unsigned long totalTime;
1326 * percentage, 0-100, of totalTime that the processes actually used
1328 int loadFactor;
1330 int i;
1331 int really_running = 0;
1333 if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1335 continue;
1338 /* s->numProcesses includes pending kills so get the "active" count */
1339 for (i = 0; i < dynamicMaxClassProcs; ++i)
1341 if (s->procs[i].state == FCGI_RUNNING_STATE) ++really_running;
1344 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1345 totalTime = really_running * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1347 loadFactor = 100 * connTime / totalTime;
1349 if (really_running == 1)
1351 if (loadFactor >= dynamicThreshold1)
1353 continue;
1356 else
1358 int load = really_running / ( really_running - 1) * loadFactor;
1360 if (load >= dynamicThresholdN)
1362 continue;
1367 * Run through the procs to see if we can get away w/o waxing one.
1369 for (i = 0; i < dynamicMaxClassProcs; ++i)
1371 if (s->procs[i].state == FCGI_START_STATE)
1373 s->procs[i].state = FCGI_READY_STATE;
1374 break;
1376 else if (s->procs[i].state == FCGI_VICTIM_STATE)
1378 break;
1382 if (i >= dynamicMaxClassProcs)
1384 ServerProcess * procs = s->procs;
1385 int youngest = -1;
1387 for (i = 0; i < dynamicMaxClassProcs; ++i)
1389 if (procs[i].state == FCGI_RUNNING_STATE)
1391 if (youngest == -1 || procs[i].start_time >= procs[youngest].start_time)
1393 youngest = i;
1398 if (youngest != -1)
1400 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1401 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1402 s->fs_path, (long) s->procs[youngest].pid);
1404 fcgi_kill(&s->procs[youngest], SIGTERM);
1406 victims++;
1410 * If the number of non-victims is less than or equal to
1411 * the minimum that may be running without being killed off,
1412 * don't select any more victims.
1414 if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1416 break;
1422 #ifdef WIN32
1424 // This is a little bogus, there's gotta be a better way to do this
1425 // Can we use WaitForMultipleObjects()
1426 #define FCGI_PROC_WAIT_TIME 100
1428 void child_wait_thread_main(void *dummy) {
1429 fcgi_server *s;
1430 DWORD dwRet = WAIT_TIMEOUT;
1431 int numChildren;
1432 int i;
1433 int waited;
1435 while (!bTimeToDie) {
1436 waited = 0;
1438 for (s = fcgi_servers; s != NULL; s = s->next) {
1439 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1440 continue;
1442 if (s->directive == APP_CLASS_DYNAMIC) {
1443 numChildren = dynamicMaxClassProcs;
1445 else {
1446 numChildren = s->numProcesses;
1449 for (i=0; i < numChildren; i++) {
1450 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1452 DWORD exitStatus = 0;
1454 /* timeout is currently set for 100 miliecond */
1455 /* it may need to be longer or user customizable */
1456 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1458 waited = 1;
1460 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1461 /* a child fs has died */
1462 /* mark the child as dead */
1464 if (s->directive == APP_CLASS_STANDARD) {
1465 /* restart static app */
1466 s->procs[i].state = FCGI_START_STATE;
1467 s->numFailures++;
1469 else {
1470 s->numProcesses--;
1471 fcgi_dynamic_total_proc_count--;
1472 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1474 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1475 s->procs[i].state = FCGI_KILLED_STATE;
1477 else {
1478 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1479 s->numFailures++;
1481 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1482 s->procs[i].state = FCGI_START_STATE;
1484 else {
1485 s->procs[i].state = FCGI_READY_STATE;
1490 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1492 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1493 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1494 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1495 s->fs_path, (long) s->procs[i].pid, exitStatus);
1497 CloseHandle(s->procs[i].handle);
1498 s->procs[i].handle = INVALID_HANDLE_VALUE;
1499 s->procs[i].pid = -1;
1501 /* wake up the main thread */
1502 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1507 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1510 #endif
1512 #ifndef WIN32
1513 static void setup_signals(void)
1515 struct sigaction sa;
1517 /* Setup handlers */
1519 sa.sa_handler = signal_handler;
1520 sigemptyset(&sa.sa_mask);
1521 sa.sa_flags = 0;
1523 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1524 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1525 "sigaction(SIGTERM) failed");
1527 /* httpd restart */
1528 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1529 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1530 "sigaction(SIGHUP) failed");
1532 /* httpd graceful restart */
1533 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1534 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1535 "sigaction(SIGUSR1) failed");
1537 /* read messages from request handlers - kill interval expired */
1538 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1539 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1540 "sigaction(SIGALRM) failed");
1542 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1543 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1544 "sigaction(SIGCHLD) failed");
1547 #endif
1549 #if !defined(WIN32) && !defined(APACHE2)
1550 int fcgi_pm_main(void *dummy, child_info *info)
1551 #else
1552 void fcgi_pm_main(void *dummy)
1553 #endif
1555 fcgi_server *s;
1556 unsigned int i;
1557 int read_ready = 0;
1558 int alarmLeft = 0;
1560 #ifdef WIN32
1561 DWORD dwRet;
1562 HANDLE child_wait_thread = INVALID_HANDLE_VALUE;
1563 #else
1564 int callWaitPid, callDynamicProcs;
1565 #endif
1567 #ifdef WIN32
1568 // Add SystemRoot to the dynamic environment
1569 char ** envp = dynamicEnvp;
1570 for (i = 0; *envp; ++i) {
1571 ++envp;
1573 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1575 #else
1577 reduce_privileges();
1578 change_process_name("fcgi-pm");
1580 close(fcgi_pm_pipe[1]);
1581 setup_signals();
1583 if (fcgi_wrapper) {
1584 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1585 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1587 #endif
1589 /* Initialize AppClass */
1590 for (s = fcgi_servers; s != NULL; s = s->next)
1592 if (s->directive != APP_CLASS_STANDARD)
1593 continue;
1595 #ifdef WIN32
1596 if (s->socket_path)
1597 s->listenFd = 0;
1598 #endif
1600 for (i = 0; i < s->numProcesses; ++i)
1601 s->procs[i].state = FCGI_START_STATE;
1604 #ifdef WIN32
1605 child_wait_thread = (HANDLE) _beginthread(child_wait_thread_main, 0, NULL);
1607 if (child_wait_thread == (HANDLE) -1)
1609 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1610 "FastCGI: failed to create process manager's wait thread!");
1613 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1614 "FastCGI: process manager initialized");
1615 #else
1616 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1617 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1618 #endif
1620 now = time(NULL);
1623 * Loop until SIGTERM
1625 for (;;) {
1626 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1627 #ifdef WIN32
1628 time_t expire;
1629 #else
1630 pid_t childPid;
1631 int waitStatus;
1632 #endif
1633 unsigned int numChildren;
1636 * If we came out of sigsuspend() for any reason other than
1637 * SIGALRM, pick up where we left off.
1639 if (alarmLeft)
1640 sleepSeconds = alarmLeft;
1643 * Examine each configured AppClass for a process that needs
1644 * starting. Compute the earliest time when the start should
1645 * be attempted, starting it now if the time has passed. Also,
1646 * remember that we do NOT need to restart externally managed
1647 * FastCGI applications.
1649 for (s = fcgi_servers; s != NULL; s = s->next)
1651 if (s->directive == APP_CLASS_EXTERNAL)
1652 continue;
1654 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1655 ? dynamicMaxClassProcs
1656 : s->numProcesses;
1658 for (i = 0; i < numChildren; ++i)
1660 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1662 int restart = (s->procs[i].pid < 0);
1663 time_t restartTime = s->restartTime;
1665 if (s->bad)
1667 /* we've gone to using the badDelay, the only thing that
1668 resets bad is when badDelay has expired. but numFailures
1669 is only just set below its threshold. the proc's
1670 start_times are all reset when the bad is. the numFailures
1671 is reset when we see an app run for a period */
1673 s->procs[i].start_time = 0;
1676 if (s->numFailures > MAX_FAILED_STARTS)
1678 time_t last_start_time = s->procs[i].start_time;
1680 if (last_start_time && now - last_start_time > RUNTIME_SUCCESS_INTERVAL)
1682 s->bad = 0;
1683 s->numFailures = 0;
1684 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1685 "FastCGI:%s server \"%s\" has remained"
1686 " running for more than %d seconds, its restart"
1687 " interval has been restored to %d seconds",
1688 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1689 s->fs_path, RUNTIME_SUCCESS_INTERVAL, s->restartDelay);
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;
1706 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1707 "FastCGI:%s server \"%s\" has failed to remain"
1708 " running for %d seconds given %d attempts, its restart"
1709 " interval has been backed off to %d seconds",
1710 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1711 s->fs_path, RUNTIME_SUCCESS_INTERVAL, MAX_FAILED_STARTS,
1712 FAILED_STARTS_DELAY);
1714 else
1716 s->bad = 0;
1717 s->numFailures = 0;
1718 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1719 "FastCGI:%s server \"%s\" has remained"
1720 " running for more than %d seconds, its restart"
1721 " interval has been restored to %d seconds",
1722 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1723 s->fs_path, RUNTIME_SUCCESS_INTERVAL, s->restartDelay);
1728 if (s->bad)
1730 restartTime += FAILED_STARTS_DELAY;
1732 else
1734 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1737 if (restartTime <= now)
1739 if (s->bad)
1741 s->bad = 0;
1742 s->numFailures = MAX_FAILED_STARTS;
1745 if (s->listenFd < 0 && init_listen_sock(s))
1747 if (sleepSeconds > s->initStartDelay)
1748 sleepSeconds = s->initStartDelay;
1749 break;
1751 #ifndef WIN32
1752 if (caughtSigTerm) {
1753 goto ProcessSigTerm;
1755 #endif
1756 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1757 if (s->procs[i].pid <= 0) {
1758 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1759 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1760 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1761 s->fs_path);
1763 sleepSeconds = min(sleepSeconds,
1764 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1766 ap_assert(s->procs[i].pid < 0);
1767 break;
1770 s->procs[i].start_time = now;
1771 s->restartTime = now;
1773 if (s->startTime == 0) {
1774 s->startTime = now;
1777 if (s->directive == APP_CLASS_DYNAMIC) {
1778 s->numProcesses++;
1779 fcgi_dynamic_total_proc_count++;
1780 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1783 s->procs[i].state = FCGI_RUNNING_STATE;
1785 if (fcgi_wrapper) {
1786 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1787 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1788 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1789 s->fs_path, (long) s->uid, (long) s->gid,
1790 restart ? "re" : "", (long) s->procs[i].pid);
1792 else {
1793 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1794 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1795 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1796 s->fs_path, restart ? "re" : "", (long) s->procs[i].pid);
1798 ap_assert(s->procs[i].pid > 0);
1799 } else {
1800 sleepSeconds = min(sleepSeconds, restartTime - now);
1806 #ifndef WIN32
1808 if(caughtSigTerm) {
1809 goto ProcessSigTerm;
1811 if((!caughtSigChld) && (!caughtSigAlarm)) {
1812 fd_set rfds;
1814 alarm(sleepSeconds);
1816 FD_ZERO(&rfds);
1817 FD_SET(fcgi_pm_pipe[0], &rfds);
1818 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1820 alarmLeft = alarm(0);
1822 callWaitPid = caughtSigChld;
1823 caughtSigChld = FALSE;
1824 callDynamicProcs = caughtSigAlarm;
1825 caughtSigAlarm = FALSE;
1827 now = time(NULL);
1830 * Dynamic fcgi process management
1832 if((callDynamicProcs) || (!callWaitPid)) {
1833 dynamic_read_msgs(read_ready);
1834 if(fcgi_dynamic_epoch == 0) {
1835 fcgi_dynamic_epoch = now;
1837 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1838 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1839 dynamic_kill_idle_fs_procs();
1840 fcgi_dynamic_epoch = now;
1844 if(!callWaitPid) {
1845 continue;
1848 /* We've caught SIGCHLD, so find out who it was using waitpid,
1849 * write a log message and update its data structure. */
1851 for (;;) {
1852 if (caughtSigTerm)
1853 goto ProcessSigTerm;
1855 childPid = waitpid(-1, &waitStatus, WNOHANG);
1857 if (childPid == -1 || childPid == 0)
1858 break;
1860 for (s = fcgi_servers; s != NULL; s = s->next) {
1861 if (s->directive == APP_CLASS_EXTERNAL)
1862 continue;
1864 if (s->directive == APP_CLASS_DYNAMIC)
1865 numChildren = dynamicMaxClassProcs;
1866 else
1867 numChildren = s->numProcesses;
1869 for (i = 0; i < numChildren; i++) {
1870 if (s->procs[i].pid == childPid)
1871 goto ChildFound;
1875 /* TODO: print something about this unknown child */
1876 continue;
1878 ChildFound:
1879 s->procs[i].pid = -1;
1881 if (s->directive == APP_CLASS_STANDARD) {
1882 /* Always restart static apps */
1883 s->procs[i].state = FCGI_START_STATE;
1884 s->numFailures++;
1886 else {
1887 s->numProcesses--;
1888 fcgi_dynamic_total_proc_count--;
1890 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1891 s->procs[i].state = FCGI_KILLED_STATE;
1893 else {
1894 /* A dynamic app died or exited without provocation from the PM */
1895 s->numFailures++;
1897 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1898 s->procs[i].state = FCGI_START_STATE;
1899 else
1900 s->procs[i].state = FCGI_READY_STATE;
1904 if (WIFEXITED(waitStatus)) {
1905 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1906 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1907 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1908 s->fs_path, (long) childPid, WEXITSTATUS(waitStatus));
1910 else if (WIFSIGNALED(waitStatus)) {
1911 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1912 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1913 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1914 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus),
1915 #ifdef WCOREDUMP
1916 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1917 #else
1918 "");
1919 #endif
1921 else if (WIFSTOPPED(waitStatus)) {
1922 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1923 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1924 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1925 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus));
1927 } /* for (;;), waitpid() */
1929 #else /* WIN32 */
1931 /* wait for an event to occur or timer expires */
1932 expire = time(NULL) + sleepSeconds;
1933 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1935 if (dwRet == WAIT_FAILED) {
1936 /* There is something seriously wrong here */
1937 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1938 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1939 bTimeToDie = TRUE;
1942 if (dwRet != WAIT_TIMEOUT) {
1943 now = time(NULL);
1945 if (now < expire)
1946 alarmLeft = expire - now;
1950 * Dynamic fcgi process management
1952 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1953 if (dwRet == MBOX_EVENT) {
1954 read_ready = 1;
1957 now = time(NULL);
1959 dynamic_read_msgs(read_ready);
1961 if(fcgi_dynamic_epoch == 0) {
1962 fcgi_dynamic_epoch = now;
1965 if ((now-fcgi_dynamic_epoch >= (int) dynamicKillInterval) ||
1966 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1967 dynamic_kill_idle_fs_procs();
1968 fcgi_dynamic_epoch = now;
1970 read_ready = 0;
1972 else if (dwRet == WAKE_EVENT) {
1973 continue;
1975 else if (dwRet == TERM_EVENT) {
1976 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1977 "FastCGI: Termination event received process manager shutting down");
1979 bTimeToDie = TRUE;
1980 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
1982 goto ProcessSigTerm;
1984 else {
1985 // Have an received an unknown event - should not happen
1986 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1987 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1989 bTimeToDie = TRUE;
1990 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
1992 goto ProcessSigTerm;
1995 #endif /* WIN32 */
1997 } /* for (;;), the whole shoot'n match */
1999 ProcessSigTerm:
2001 * Kill off the children, then exit.
2003 shutdown_all();
2005 #ifdef WIN32
2006 return;
2007 #else
2008 exit(0);
2009 #endif
2012 #ifdef WIN32
2013 int fcgi_pm_add_job(fcgi_pm_job *new_job)
2015 int rv = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
2017 if (rv != WAIT_OBJECT_0 && rv != WAIT_ABANDONED)
2019 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2020 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2021 return -1;
2024 new_job->next = fcgi_dynamic_mbox;
2025 fcgi_dynamic_mbox = new_job;
2027 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
2029 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2030 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
2033 return 0;
2035 #endif