use the real uid to do the setuid_root (this
[mod_fastcgi.git] / fcgi_pm.c
blob0ba49b5b703ca95b1691f300653c826b9ab6e915
1 /*
2 * $Id: fcgi_pm.c,v 1.82 2002/10/22 02:37:32 robs Exp $
3 */
6 #include "fcgi.h"
8 #if defined(APACHE2) && !defined(WIN32)
9 #include <pwd.h>
10 #include <unistd.h>
11 #include <utime.h>
12 #include "unixd.h"
13 #include "apr_signal.h"
14 #endif
16 #ifdef _HPUX_SOURCE
17 #include <unistd.h>
18 #define seteuid(arg) setresuid(-1, (arg), -1)
19 #endif
21 int fcgi_dynamic_total_proc_count = 0; /* number of running apps */
22 time_t fcgi_dynamic_epoch = 0; /* last time kill_procs was
23 * invoked by process mgr */
24 time_t fcgi_dynamic_last_analyzed = 0; /* last time calculation was
25 * made for the dynamic procs */
27 static time_t now = 0;
29 #ifdef WIN32
30 #pragma warning ( disable : 4100 4102 )
31 static BOOL bTimeToDie = FALSE; /* process termination flag */
32 HANDLE fcgi_event_handles[3];
33 #ifndef SIGKILL
34 #define SIGKILL 9
35 #endif
36 #endif
39 #ifndef WIN32
40 static int seteuid_root(void)
42 int rc = seteuid(getuid());
43 if (rc) {
44 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
45 "FastCGI: seteuid(0) failed");
47 return rc;
50 static int seteuid_user(void)
52 int rc = seteuid(ap_user_id);
53 if (rc) {
54 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
55 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id);
57 return rc;
59 #endif
62 * Signal the process to exit. How (or if) the process responds
63 * depends on the FastCGI application library (esp. on Win32) and
64 * possibly application code (signal handlers and whether or not
65 * SA_RESTART is on). At any rate, we send the signal with the
66 * hopes that the process will exit on its own. Later, as we
67 * review the state of application processes, if we see one marked
68 * for death, but that hasn't died within a specified period of
69 * time, fcgi_kill() is called again with a KILL)
71 static void fcgi_kill(ServerProcess *process, int sig)
73 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process->pid, sig);
75 process->state = FCGI_VICTIM_STATE;
77 #ifdef WIN32
79 if (sig == SIGTERM)
81 SetEvent(process->terminationEvent);
83 else if (sig == SIGKILL)
85 TerminateProcess(process->handle, 1);
87 else
89 ap_assert(0);
92 #else /* !WIN32 */
94 if (fcgi_wrapper)
96 seteuid_root();
99 kill(process->pid, sig);
101 if (fcgi_wrapper)
103 seteuid_user();
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 #ifdef WIN32
265 closesocket(fs->listenFd);
266 #else
267 close(fs->listenFd);
268 #endif
270 fs->listenFd = -1;
272 return -2;
276 *----------------------------------------------------------------------
278 * pm_main
280 * The FastCGI process manager, which runs as a separate
281 * process responsible for:
282 * - Starting all the FastCGI proceses.
283 * - Restarting any of these processes that die (indicated
284 * by SIGCHLD).
285 * - Catching SIGTERM and relaying it to all the FastCGI
286 * processes before exiting.
288 * Inputs:
289 * Uses global variable fcgi_servers.
291 * Results:
292 * Does not return.
294 * Side effects:
295 * Described above.
297 *----------------------------------------------------------------------
299 #ifndef WIN32
300 static int caughtSigTerm = FALSE;
301 static int caughtSigChld = FALSE;
302 static int caughtSigAlarm = FALSE;
304 static void signal_handler(int signo)
306 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
307 /* SIGUSR1 & SIGHUP are sent by apache to its process group
308 * when apache get 'em. Apache follows up (1.2.x) with attacks
309 * on each of its child processes, but we've got the KillMgr
310 * sitting between us so we never see the KILL. The main loop
311 * in ProcMgr also checks to see if the KillMgr has terminated,
312 * and if it has, we handl it as if we should shutdown too. */
313 caughtSigTerm = TRUE;
314 } else if(signo == SIGCHLD) {
315 caughtSigChld = TRUE;
316 } else if(signo == SIGALRM) {
317 caughtSigAlarm = TRUE;
320 #endif
323 *----------------------------------------------------------------------
325 * spawn_fs_process --
327 * Fork and exec the specified fcgi process.
329 * Results:
330 * 0 for successful fork, -1 for failed fork.
332 * In case the child fails before or in the exec, the child
333 * obtains the error log by calling getErrLog, logs
334 * the error, and exits with exit status = errno of
335 * the failed system call.
337 * Side effects:
338 * Child process created.
340 *----------------------------------------------------------------------
342 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
344 #ifndef WIN32
346 pid_t child_pid;
347 int i;
348 char *dirName;
349 char *dnEnd, *failedSysCall;
351 child_pid = fork();
352 if (child_pid) {
353 return child_pid;
356 /* We're the child. We're gonna exec() so pools don't matter. */
358 dnEnd = strrchr(fs->fs_path, '/');
359 if (dnEnd == NULL) {
360 dirName = "./";
361 } else {
362 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
363 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
365 if (chdir(dirName) < 0) {
366 failedSysCall = "chdir()";
367 goto FailedSystemCallExit;
370 #ifndef __EMX__
371 /* OS/2 dosen't support nice() */
372 if (fs->processPriority != 0) {
373 if (nice(fs->processPriority) == -1) {
374 failedSysCall = "nice()";
375 goto FailedSystemCallExit;
378 #endif
380 /* Open the listenFd on spec'd fd */
381 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
382 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
384 /* Close all other open fds, except stdout/stderr. Leave these two open so
385 * FastCGI applications don't have to find and fix ALL 3rd party libs that
386 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
387 * main server error_log - @@@ provide a directive control where this goes.
389 ap_error_log2stderr(fcgi_apache_main_server);
390 dup2(2, 1);
391 for (i = 0; i < FCGI_MAX_FD; i++) {
392 if (i != FCGI_LISTENSOCK_FILENO && i != 2 && i != 1) {
393 close(i);
397 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
398 * install its own handler. */
399 signal(SIGPIPE, SIG_IGN);
401 if (fcgi_wrapper && (fcgi_user_id != fs->uid || fcgi_group_id != fs->gid)) {
402 char *shortName = strrchr(fs->fs_path, '/') + 1;
404 /* Relinquish our root real uid powers */
405 seteuid_root();
406 setuid(ap_user_id);
408 do {
409 execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group, shortName, NULL, fs->envp);
410 } while (errno == EINTR);
412 else {
413 do {
414 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
415 } while (errno == EINTR);
418 failedSysCall = "execle()";
420 FailedSystemCallExit:
421 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
422 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
423 exit(-1);
425 /* avoid an irrelevant compiler warning */
426 return(0);
428 #else /* WIN32 */
430 #ifdef APACHE2
432 /* based on mod_cgi.c:run_cgi_child() */
434 apr_pool_t * tp;
435 char * termination_env_string;
436 HANDLE listen_handle = INVALID_HANDLE_VALUE;
437 apr_procattr_t * procattr;
438 apr_proc_t proc = { 0 };
439 apr_file_t * file;
440 int i = 0;
442 if (apr_pool_create(&tp, fcgi_config_pool))
443 return 0;
445 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
446 if (process->terminationEvent == NULL)
447 goto CLEANUP;
449 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
451 termination_env_string = ap_psprintf(tp,
452 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
454 while (fs->envp[i]) i++;
455 fs->envp[i++] = termination_env_string;
456 fs->envp[i] = (char *) fs->mutex_env_string;
458 ap_assert(fs->envp[i + 1] == NULL);
460 if (fs->socket_path)
462 SECURITY_ATTRIBUTES sa = { 0 };
464 sa.bInheritHandle = TRUE;
465 sa.nLength = sizeof(sa);
467 listen_handle = CreateNamedPipe(fs->socket_path,
468 PIPE_ACCESS_DUPLEX,
469 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
470 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
472 if (listen_handle == INVALID_HANDLE_VALUE)
474 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
475 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
476 fs->fs_path);
477 goto CLEANUP;
480 else
482 listen_handle = (HANDLE) fs->listenFd;
485 if (apr_procattr_create(&procattr, tp))
486 goto CLEANUP;
488 if (apr_procattr_dir_set(procattr, ap_make_dirstr_parent(tp, fs->fs_path)))
489 goto CLEANUP;
491 if (apr_procattr_detach_set(procattr, 1))
492 goto CLEANUP;
494 if (apr_os_file_put(&file, &listen_handle, 0, tp))
495 goto CLEANUP;
497 /* procattr is opaque so we have to use this - unfortuantely it dups */
498 if (apr_procattr_child_in_set(procattr, file, NULL))
499 goto CLEANUP;
501 if (apr_proc_create(&proc, fs->fs_path, NULL, fs->envp, procattr, tp))
502 goto CLEANUP;
504 process->handle = proc.hproc;
507 CLEANUP:
509 if (i)
511 fs->envp[i - 1] = NULL;
514 ap_destroy_pool(tp);
516 return proc.pid;
518 #else /* WIN32 && !APACHE2 */
520 /* Adapted from Apache's util_script.c ap_call_exec() */
521 char *interpreter = NULL;
522 char *quoted_filename;
523 char *pCommand;
524 char *pEnvBlock, *pNext;
526 int i = 0;
527 int iEnvBlockLen = 1;
529 file_type_e fileType;
531 STARTUPINFO si;
532 PROCESS_INFORMATION pi;
534 request_rec r;
535 pid_t pid = -1;
537 pool * tp = ap_make_sub_pool(fcgi_config_pool);
539 HANDLE listen_handle;
540 char * termination_env_string = NULL;
542 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
543 if (process->terminationEvent == NULL)
545 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
546 "FastCGI: can't create termination event for server \"%s\", "
547 "CreateEvent() failed", fs->fs_path);
548 goto CLEANUP;
550 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
552 termination_env_string = ap_psprintf(tp,
553 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
555 if (fs->socket_path)
557 SECURITY_ATTRIBUTES sa;
559 sa.lpSecurityDescriptor = NULL;
560 sa.bInheritHandle = TRUE;
561 sa.nLength = sizeof(sa);
563 listen_handle = CreateNamedPipe(fs->socket_path,
564 PIPE_ACCESS_DUPLEX,
565 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
566 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
568 if (listen_handle == INVALID_HANDLE_VALUE)
570 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
571 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
572 goto CLEANUP;
575 else
577 listen_handle = (HANDLE) fs->listenFd;
580 memset(&si, 0, sizeof(si));
581 memset(&pi, 0, sizeof(pi));
582 memset(&r, 0, sizeof(r));
584 // Can up a fake request to pass to ap_get_win32_interpreter()
585 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
586 r.server = fcgi_apache_main_server;
587 r.filename = (char *) fs->fs_path;
588 r.pool = tp;
590 fileType = ap_get_win32_interpreter(&r, &interpreter);
592 if (fileType == eFileTypeUNKNOWN) {
593 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
594 "FastCGI: %s is not executable; ensure interpreted scripts have "
595 "\"#!\" as their first line",
596 fs->fs_path);
597 ap_destroy_pool(tp);
598 goto CLEANUP;
602 * We have the interpreter (if there is one) and we have
603 * the arguments (if there are any).
604 * Build the command string to pass to CreateProcess.
606 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
607 if (interpreter && *interpreter) {
608 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
610 else {
611 pCommand = quoted_filename;
615 * Make child process use hPipeOutputWrite as standard out,
616 * and make sure it does not show on screen.
618 si.cb = sizeof(si);
619 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
620 si.wShowWindow = SW_HIDE;
621 si.hStdInput = listen_handle;
623 // XXX These should be open to the error_log
624 si.hStdOutput = INVALID_HANDLE_VALUE;
625 si.hStdError = INVALID_HANDLE_VALUE;
628 * Win32's CreateProcess call requires that the environment
629 * be passed in an environment block, a null terminated block of
630 * null terminated strings.
631 * @todo we should store the env in this format for win32.
633 while (fs->envp[i])
635 iEnvBlockLen += strlen(fs->envp[i]) + 1;
636 i++;
639 iEnvBlockLen += strlen(termination_env_string) + 1;
640 iEnvBlockLen += strlen(fs->mutex_env_string) + 1;
642 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
644 i = 0;
645 pNext = pEnvBlock;
646 while (fs->envp[i])
648 strcpy(pNext, fs->envp[i]);
649 pNext += strlen(pNext) + 1;
650 i++;
653 strcpy(pNext, termination_env_string);
654 pNext += strlen(pNext) + 1;
655 strcpy(pNext, fs->mutex_env_string);
657 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
659 pEnvBlock,
660 ap_make_dirstr_parent(tp, fs->fs_path),
661 &si, &pi))
663 /* Hack to get 16-bit CGI's working. It works for all the
664 * standard modules shipped with Apache. pi.dwProcessId is 0
665 * for 16-bit CGIs and all the Unix specific code that calls
666 * ap_call_exec interprets this as a failure case. And we can't
667 * use -1 either because it is mapped to 0 by the caller.
669 pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId;
671 process->handle = pi.hProcess;
672 CloseHandle(pi.hThread);
675 if (fs->socket_path)
677 CloseHandle(listen_handle);
680 CLEANUP:
682 ap_destroy_pool(tp);
684 return pid;
686 #endif /* !APACHE2 */
687 #endif /* WIN32 */
690 #ifndef WIN32
691 static void reduce_privileges(void)
693 const char *name;
695 if (geteuid() != 0)
696 return;
698 #ifndef __EMX__
699 /* Get username if passed as a uid */
700 if (ap_user_name[0] == '#') {
701 uid_t uid = atoi(&ap_user_name[1]);
702 struct passwd *ent = getpwuid(uid);
704 if (ent == NULL) {
705 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
706 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
707 "you probably need to modify the User directive", (unsigned)uid);
708 exit(1);
710 name = ent->pw_name;
712 else
713 name = ap_user_name;
715 /* Change Group */
716 if (setgid(ap_group_id) == -1) {
717 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
718 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
719 exit(1);
722 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
724 /* Initialize supplementary groups */
725 if (initgroups(name, ap_group_id) == -1) {
726 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
727 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
728 name, (unsigned)ap_group_id);
729 exit(1);
731 #endif /* __EMX__ */
733 /* Change User */
734 if (fcgi_wrapper) {
735 if (seteuid_user() == -1) {
736 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
737 "FastCGI: process manager exiting, failed to reduce privileges");
738 exit(1);
741 else {
742 if (setuid(ap_user_id) == -1) {
743 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
744 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
745 exit(1);
750 /*************
751 * Change the name of this process - best we can easily.
753 static void change_process_name(const char * const name)
755 /* under Apache2, ap_server_argv0 is const */
756 strncpy((char *) ap_server_argv0, name, strlen(ap_server_argv0));
758 #endif /* !WIN32 */
760 static void schedule_start(fcgi_server *s, int proc)
762 /* If we've started one recently, don't register another */
763 time_t time_passed = now - s->restartTime;
765 if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay))
766 || ((s->procs[proc].pid == 0) && (time_passed < s->initStartDelay)))
768 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);
769 return;
772 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
773 s->procs[proc].state = FCGI_START_STATE;
774 if (proc == dynamicMaxClassProcs - 1) {
775 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
776 "FastCGI: scheduled the %sstart of the last (dynamic) server "
777 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
778 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
783 *----------------------------------------------------------------------
785 * dynamic_read_msgs
787 * Removes the records written by request handlers and decodes them.
788 * We also update the data structures to reflect the changes.
790 *----------------------------------------------------------------------
793 static void dynamic_read_msgs(int read_ready)
795 fcgi_server *s;
796 int rc;
798 #ifndef WIN32
799 static int buflen = 0;
800 static char buf[FCGI_MSGS_BUFSIZE + 1];
801 char *ptr1, *ptr2, opcode;
802 char execName[FCGI_MAXPATH + 1];
803 char user[MAX_USER_NAME_LEN + 2];
804 char group[MAX_GID_CHAR_LEN + 1];
805 unsigned long q_usec = 0UL, req_usec = 0UL;
806 #else
807 fcgi_pm_job *joblist = NULL;
808 fcgi_pm_job *cjob = NULL;
809 #endif
811 pool *sp = NULL, *tp;
813 #ifndef WIN32
814 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
815 #endif
818 * To prevent the idle application from running indefinitely, we
819 * check the timer and if it is expired, we recompute the values
820 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
821 * message is received, only updates are made to the data structures.
823 if (fcgi_dynamic_last_analyzed == 0) {
824 fcgi_dynamic_last_analyzed = now;
826 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
827 for (s = fcgi_servers; s != NULL; s = s->next) {
828 if (s->directive != APP_CLASS_DYNAMIC)
829 break;
831 /* Advance the last analyzed timestamp by the elapsed time since
832 * it was last set. Round the increase down to the nearest
833 * multiple of dynamicUpdateInterval */
835 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
836 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
837 s->totalConnTime = 0UL;
838 s->totalQueueTime = 0UL;
842 if (read_ready <= 0) {
843 return;
846 #ifndef WIN32
847 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
848 if (rc <= 0) {
849 if (!caughtSigTerm) {
850 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
851 "FastCGI: read() from pipe failed (%d)", rc);
852 if (rc == 0) {
853 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
854 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
855 caughtSigTerm = TRUE;
858 return;
860 buflen += rc;
861 buf[buflen] = '\0';
863 #else
865 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
866 * request to do something) and/or when a timeout expires.
867 * There really should be no reason why this wait would get stuck
868 * but there's no point in waiting forever. */
870 rc = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
872 if (rc != WAIT_OBJECT_0 && rc != WAIT_ABANDONED)
874 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
875 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
876 return;
879 joblist = fcgi_dynamic_mbox;
880 fcgi_dynamic_mbox = NULL;
882 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
884 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
885 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
888 cjob = joblist;
889 #endif
891 #ifdef APACHE2
892 apr_pool_create(&tp, fcgi_config_pool);
893 #else
894 tp = ap_make_sub_pool(fcgi_config_pool);
895 #endif
897 #ifndef WIN32
898 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
899 int scan_failed = 0;
901 ptr2 = strchr(ptr1, '*');
902 if (ptr2) {
903 *ptr2++ = '\0';
905 else {
906 break;
909 opcode = *ptr1;
911 switch (opcode)
913 case FCGI_SERVER_START_JOB:
914 case FCGI_SERVER_RESTART_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_TIMEOUT_JOB:
925 if (sscanf(ptr1, "%c %s %16s %15s",
926 &opcode, execName, user, group) != 4)
928 scan_failed = 1;
930 break;
932 case FCGI_REQUEST_COMPLETE_JOB:
934 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
935 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
937 scan_failed = 1;
939 break;
941 default:
943 scan_failed = 1;
944 break;
947 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode, execName, user, group, q_usec, req_usec);
949 if (scan_failed) {
950 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
951 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
952 goto NextJob;
954 #else
955 /* Update data structures for processing */
956 while (cjob != NULL) {
957 joblist = cjob->next;
958 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
959 #endif
961 #ifndef WIN32
962 s = fcgi_util_fs_get(execName, user, group);
963 #else
964 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
965 #endif
967 #ifndef WIN32
968 if (s==NULL && opcode != FCGI_REQUEST_COMPLETE_JOB)
969 #else
970 if (s==NULL && cjob->id != FCGI_REQUEST_COMPLETE_JOB)
971 #endif
973 #ifdef WIN32
975 HANDLE mutex = CreateMutex(NULL, FALSE, cjob->fs_path);
977 if (mutex == NULL)
979 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
980 "FastCGI: can't create accept mutex "
981 "for (dynamic) server \"%s\"", cjob->fs_path);
982 goto BagNewServer;
985 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
986 #else
987 const char *err;
988 #endif
990 /* Create a perm subpool to hold the new server data,
991 * we can destroy it if something doesn't pan out */
992 #ifdef APACHE2
993 apr_pool_create(&sp, fcgi_config_pool);
994 #else
995 sp = ap_make_sub_pool(fcgi_config_pool);
996 #endif
998 /* Create a new "dynamic" server */
999 s = fcgi_util_fs_new(sp);
1001 s->directive = APP_CLASS_DYNAMIC;
1002 s->restartDelay = dynamicRestartDelay;
1003 s->listenQueueDepth = dynamicListenQueueDepth;
1004 s->initStartDelay = dynamicInitStartDelay;
1005 s->envp = dynamicEnvp;
1006 s->flush = dynamicFlush;
1008 #ifdef WIN32
1009 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
1010 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
1011 #else
1012 s->fs_path = ap_pstrdup(sp, execName);
1013 #endif
1014 ap_getparents(s->fs_path);
1015 ap_no2slash(s->fs_path);
1016 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
1018 /* XXX the socket_path (both Unix and Win) *is* deducible and
1019 * thus can and will be used by other apache instances without
1020 * the use of shared data regarding the processes serving the
1021 * requests. This can result in slightly unintuitive process
1022 * counts and security implications. This is prevented
1023 * if suexec (Unix) is in use. This is both a feature and a flaw.
1024 * Changing it now would break existing installations. */
1026 #ifndef WIN32
1027 /* Create socket file's path */
1028 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
1029 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1031 /* Create sockaddr, prealloc it so it won't get created in tp */
1032 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
1033 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
1034 &s->socket_addr_len, s->socket_path);
1035 if (err) {
1036 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1037 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
1038 goto BagNewServer;
1041 if (init_listen_sock(s)) {
1042 goto BagNewServer;
1045 /* If a wrapper is being used, config user/group info */
1046 if (fcgi_wrapper) {
1047 if (user[0] == '~') {
1048 /* its a user dir uri, the rest is a username, not a uid */
1049 struct passwd *pw = getpwnam(&user[1]);
1051 if (!pw) {
1052 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1053 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1054 execName, &user[1]);
1055 goto BagNewServer;
1057 s->uid = pw->pw_uid;
1058 s->user = ap_pstrdup(sp, user);
1059 s->username = s->user;
1061 s->gid = pw->pw_gid;
1062 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
1064 else {
1065 struct passwd *pw;
1067 s->uid = (uid_t)atol(user);
1068 pw = getpwuid(s->uid);
1069 if (!pw) {
1070 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1071 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1072 execName, (long)s->uid);
1073 goto BagNewServer;
1075 s->user = ap_pstrdup(sp, user);
1076 s->username = ap_pstrdup(sp, pw->pw_name);
1078 s->gid = (gid_t)atol(group);
1079 s->group = ap_pstrdup(sp, group);
1082 #else
1083 /* Create socket file's path */
1084 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
1085 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1086 s->listenFd = 0;
1087 #endif
1089 fcgi_util_fs_add(s);
1091 else {
1092 #ifndef WIN32
1093 if (opcode == FCGI_SERVER_RESTART_JOB) {
1094 #else
1095 if (cjob->id==FCGI_SERVER_RESTART_JOB) {
1096 #endif
1097 /* Check to see if the binary has changed. If so,
1098 * kill the FCGI application processes, and
1099 * restart them.
1101 struct stat stbuf;
1102 int i;
1103 #ifdef WIN32
1104 char * app_path = cjob->fs_path;
1105 #else
1106 char * app_path = execName;
1107 #endif
1109 if (stat(app_path, &stbuf) == 0 && stbuf.st_mtime > s->startTime)
1111 int do_restart = 0;
1113 /* prevent addition restart requests */
1114 s->startTime = now;
1115 #ifndef WIN32
1116 utime(s->socket_path, NULL);
1117 #endif
1119 /* kill old server(s) */
1120 for (i = 0; i < dynamicMaxClassProcs; i++)
1122 if (s->procs[i].pid > 0
1123 && stbuf.st_mtime > s->procs[i].start_time)
1125 fcgi_kill(&s->procs[i], SIGTERM);
1126 do_restart++;
1130 if (do_restart)
1132 ap_log_error(FCGI_LOG_WARN_NOERRNO,
1133 fcgi_apache_main_server, "FastCGI: restarting "
1134 "old server \"%s\" processes, newer version "
1135 "found", app_path);
1139 /* If dynamicAutoRestart, don't mark any new processes
1140 * for starting because we probably got the
1141 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1142 * will be restarting all of those we just killed.
1144 if (dynamicAutoRestart)
1145 goto NextJob;
1147 #ifndef WIN32
1148 else if (opcode == FCGI_SERVER_START_JOB) {
1149 #else
1150 else if (cjob->id==FCGI_SERVER_START_JOB) {
1151 #endif
1152 /* we've been asked to start a process--only start
1153 * it if we're not already running at least one
1154 * instance.
1156 int i;
1158 for (i = 0; i < dynamicMaxClassProcs; i++) {
1159 if (s->procs[i].state == FCGI_RUNNING_STATE)
1160 break;
1162 /* if already running, don't start another one */
1163 if (i < dynamicMaxClassProcs) {
1164 goto NextJob;
1169 #ifndef WIN32
1170 switch (opcode)
1171 #else
1172 switch (cjob->id)
1173 #endif
1175 int i, start;
1177 case FCGI_SERVER_RESTART_JOB:
1179 start = FALSE;
1181 /* We just waxed 'em all. Try to find an idle slot. */
1183 for (i = 0; i < dynamicMaxClassProcs; ++i)
1185 if (s->procs[i].state == FCGI_START_STATE
1186 || s->procs[i].state == FCGI_RUNNING_STATE)
1188 break;
1190 else if (s->procs[i].state == FCGI_KILLED_STATE
1191 || s->procs[i].state == FCGI_READY_STATE)
1193 start = TRUE;
1194 break;
1198 /* Nope, just use the first slot */
1199 if (i == dynamicMaxClassProcs)
1201 start = TRUE;
1202 i = 0;
1205 if (start)
1207 schedule_start(s, i);
1210 break;
1212 case FCGI_SERVER_START_JOB:
1213 case FCGI_REQUEST_TIMEOUT_JOB:
1215 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1217 * Extra instances should have been
1218 * terminated beforehand, probably need
1219 * to increase ProcessSlack parameter
1221 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1222 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1223 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1224 goto NextJob;
1227 /* find next free slot */
1228 for (i = 0; i < dynamicMaxClassProcs; i++)
1230 if (s->procs[i].state == FCGI_START_STATE)
1232 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1233 break;
1235 else if (s->procs[i].state == FCGI_RUNNING_STATE)
1237 continue;
1240 schedule_start(s, i);
1241 break;
1244 #ifdef FCGI_DEBUG
1245 if (i >= dynamicMaxClassProcs) {
1246 FCGIDBG1("ignore_job: slots are max'd");
1248 #endif
1249 break;
1250 case FCGI_REQUEST_COMPLETE_JOB:
1251 /* only record stats if we have a structure */
1252 if (s) {
1253 #ifndef WIN32
1254 s->totalConnTime += req_usec;
1255 s->totalQueueTime += q_usec;
1256 #else
1257 s->totalConnTime += cjob->start_time;
1258 s->totalQueueTime += cjob->qsec;
1259 #endif
1261 break;
1264 NextJob:
1266 #ifdef WIN32
1267 /* Cleanup job data */
1268 free(cjob->fs_path);
1269 free(cjob->user);
1270 free(cjob->group);
1271 free(cjob);
1272 cjob = joblist;
1273 #endif
1275 continue;
1277 BagNewServer:
1278 if (sp) ap_destroy_pool(sp);
1280 #ifdef WIN32
1281 free(cjob->fs_path);
1282 free(cjob);
1283 cjob = joblist;
1284 #endif
1287 #ifndef WIN32
1288 if (ptr1 == buf) {
1289 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1290 "FastCGI: really bogus message: \"%s\"", ptr1);
1291 ptr1 += strlen(buf);
1294 buflen -= ptr1 - buf;
1295 if (buflen) {
1296 memmove(buf, ptr1, buflen);
1298 #endif
1300 ap_destroy_pool(tp);
1304 *----------------------------------------------------------------------
1306 * dynamic_kill_idle_fs_procs
1308 * Implement a kill policy for the dynamic FastCGI applications.
1309 * We also update the data structures to reflect the changes.
1311 * Side effects:
1312 * Processes are marked for deletion possibly killed.
1314 *----------------------------------------------------------------------
1316 static void dynamic_kill_idle_fs_procs(void)
1318 fcgi_server *s;
1319 int victims = 0;
1321 for (s = fcgi_servers; s != NULL; s = s->next)
1324 * server's smoothed running time, or if that's 0, the current total
1326 unsigned long connTime;
1329 * maximum number of microseconds that all of a server's running
1330 * processes together could have spent running since the last check
1332 unsigned long totalTime;
1335 * percentage, 0-100, of totalTime that the processes actually used
1337 int loadFactor;
1339 int i;
1340 int really_running = 0;
1342 if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1344 continue;
1347 /* s->numProcesses includes pending kills so get the "active" count */
1348 for (i = 0; i < dynamicMaxClassProcs; ++i)
1350 if (s->procs[i].state == FCGI_RUNNING_STATE) ++really_running;
1353 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1354 totalTime = really_running * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1356 loadFactor = 100 * connTime / totalTime;
1358 if (really_running == 1)
1360 if (loadFactor >= dynamicThreshold1)
1362 continue;
1365 else
1367 int load = really_running / ( really_running - 1) * loadFactor;
1369 if (load >= dynamicThresholdN)
1371 continue;
1376 * Run through the procs to see if we can get away w/o waxing one.
1378 for (i = 0; i < dynamicMaxClassProcs; ++i)
1380 if (s->procs[i].state == FCGI_START_STATE)
1382 s->procs[i].state = FCGI_READY_STATE;
1383 break;
1385 else if (s->procs[i].state == FCGI_VICTIM_STATE)
1387 break;
1391 if (i >= dynamicMaxClassProcs)
1393 ServerProcess * procs = s->procs;
1394 int youngest = -1;
1396 for (i = 0; i < dynamicMaxClassProcs; ++i)
1398 if (procs[i].state == FCGI_RUNNING_STATE)
1400 if (youngest == -1 || procs[i].start_time >= procs[youngest].start_time)
1402 youngest = i;
1407 if (youngest != -1)
1409 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1410 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1411 s->fs_path, (long) s->procs[youngest].pid);
1413 fcgi_kill(&s->procs[youngest], SIGTERM);
1415 victims++;
1419 * If the number of non-victims is less than or equal to
1420 * the minimum that may be running without being killed off,
1421 * don't select any more victims.
1423 if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1425 break;
1431 #ifdef WIN32
1433 // This is a little bogus, there's gotta be a better way to do this
1434 // Can we use WaitForMultipleObjects()
1435 #define FCGI_PROC_WAIT_TIME 100
1437 void child_wait_thread_main(void *dummy) {
1438 fcgi_server *s;
1439 DWORD dwRet = WAIT_TIMEOUT;
1440 int numChildren;
1441 int i;
1442 int waited;
1444 while (!bTimeToDie) {
1445 waited = 0;
1447 for (s = fcgi_servers; s != NULL; s = s->next) {
1448 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1449 continue;
1451 if (s->directive == APP_CLASS_DYNAMIC) {
1452 numChildren = dynamicMaxClassProcs;
1454 else {
1455 numChildren = s->numProcesses;
1458 for (i=0; i < numChildren; i++) {
1459 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1461 DWORD exitStatus = 0;
1463 /* timeout is currently set for 100 miliecond */
1464 /* it may need to be longer or user customizable */
1465 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1467 waited = 1;
1469 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1470 /* a child fs has died */
1471 /* mark the child as dead */
1473 if (s->directive == APP_CLASS_STANDARD) {
1474 /* restart static app */
1475 s->procs[i].state = FCGI_START_STATE;
1476 s->numFailures++;
1478 else {
1479 s->numProcesses--;
1480 fcgi_dynamic_total_proc_count--;
1481 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1483 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1484 s->procs[i].state = FCGI_KILLED_STATE;
1486 else {
1487 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1488 s->numFailures++;
1490 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1491 s->procs[i].state = FCGI_START_STATE;
1493 else {
1494 s->procs[i].state = FCGI_READY_STATE;
1499 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1501 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1502 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1503 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1504 s->fs_path, (long) s->procs[i].pid, exitStatus);
1506 CloseHandle(s->procs[i].handle);
1507 s->procs[i].handle = INVALID_HANDLE_VALUE;
1508 s->procs[i].pid = -1;
1510 /* wake up the main thread */
1511 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1516 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1519 #endif
1521 #ifndef WIN32
1522 static void setup_signals(void)
1524 struct sigaction sa;
1526 /* Setup handlers */
1528 sa.sa_handler = signal_handler;
1529 sigemptyset(&sa.sa_mask);
1530 sa.sa_flags = 0;
1532 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1533 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1534 "sigaction(SIGTERM) failed");
1536 /* httpd restart */
1537 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1538 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1539 "sigaction(SIGHUP) failed");
1541 /* httpd graceful restart */
1542 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1543 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1544 "sigaction(SIGUSR1) failed");
1546 /* read messages from request handlers - kill interval expired */
1547 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1548 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1549 "sigaction(SIGALRM) failed");
1551 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1552 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1553 "sigaction(SIGCHLD) failed");
1556 #endif
1558 #if !defined(WIN32) && !defined(APACHE2)
1559 int fcgi_pm_main(void *dummy, child_info *info)
1560 #else
1561 void fcgi_pm_main(void *dummy)
1562 #endif
1564 fcgi_server *s;
1565 unsigned int i;
1566 int read_ready = 0;
1567 int alarmLeft = 0;
1569 #ifdef WIN32
1570 DWORD dwRet;
1571 HANDLE child_wait_thread = INVALID_HANDLE_VALUE;
1572 #else
1573 int callWaitPid, callDynamicProcs;
1574 #endif
1576 #ifdef WIN32
1577 // Add SystemRoot to the dynamic environment
1578 char ** envp = dynamicEnvp;
1579 for (i = 0; *envp; ++i) {
1580 ++envp;
1582 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1584 #else
1586 reduce_privileges();
1587 change_process_name("fcgi-pm");
1589 close(fcgi_pm_pipe[1]);
1590 setup_signals();
1592 if (fcgi_wrapper) {
1593 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1594 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1596 #endif
1598 /* Initialize AppClass */
1599 for (s = fcgi_servers; s != NULL; s = s->next)
1601 if (s->directive != APP_CLASS_STANDARD)
1602 continue;
1604 #ifdef WIN32
1605 if (s->socket_path)
1606 s->listenFd = 0;
1607 #endif
1609 for (i = 0; i < s->numProcesses; ++i)
1610 s->procs[i].state = FCGI_START_STATE;
1613 #ifdef WIN32
1614 child_wait_thread = (HANDLE) _beginthread(child_wait_thread_main, 0, NULL);
1616 if (child_wait_thread == (HANDLE) -1)
1618 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1619 "FastCGI: failed to create process manager's wait thread!");
1622 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1623 "FastCGI: process manager initialized");
1624 #else
1625 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1626 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1627 #endif
1629 now = time(NULL);
1632 * Loop until SIGTERM
1634 for (;;) {
1635 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1636 #ifdef WIN32
1637 time_t expire;
1638 #else
1639 pid_t childPid;
1640 int waitStatus;
1641 #endif
1642 unsigned int numChildren;
1645 * If we came out of sigsuspend() for any reason other than
1646 * SIGALRM, pick up where we left off.
1648 if (alarmLeft)
1649 sleepSeconds = alarmLeft;
1652 * Examine each configured AppClass for a process that needs
1653 * starting. Compute the earliest time when the start should
1654 * be attempted, starting it now if the time has passed. Also,
1655 * remember that we do NOT need to restart externally managed
1656 * FastCGI applications.
1658 for (s = fcgi_servers; s != NULL; s = s->next)
1660 if (s->directive == APP_CLASS_EXTERNAL)
1661 continue;
1663 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1664 ? dynamicMaxClassProcs
1665 : s->numProcesses;
1667 for (i = 0; i < numChildren; ++i)
1669 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1671 int restart = (s->procs[i].pid < 0);
1672 time_t restartTime = s->restartTime;
1674 if (s->bad)
1676 /* we've gone to using the badDelay, the only thing that
1677 resets bad is when badDelay has expired. but numFailures
1678 is only just set below its threshold. the proc's
1679 start_times are all reset when the bad is. the numFailures
1680 is reset when we see an app run for a period */
1682 s->procs[i].start_time = 0;
1685 if (s->numFailures > MAX_FAILED_STARTS)
1687 time_t last_start_time = s->procs[i].start_time;
1689 if (last_start_time && now - last_start_time > RUNTIME_SUCCESS_INTERVAL)
1691 s->bad = 0;
1692 s->numFailures = 0;
1693 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1694 "FastCGI:%s server \"%s\" has remained"
1695 " running for more than %d seconds, its restart"
1696 " interval has been restored to %d seconds",
1697 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1698 s->fs_path, RUNTIME_SUCCESS_INTERVAL, s->restartDelay);
1700 else
1702 unsigned int j;
1704 for (j = 0; j < numChildren; ++j)
1706 if (s->procs[j].pid <= 0) continue;
1707 if (s->procs[j].state != FCGI_RUNNING_STATE) continue;
1708 if (s->procs[j].start_time == 0) continue;
1709 if (now - s->procs[j].start_time > RUNTIME_SUCCESS_INTERVAL) break;
1712 if (j >= numChildren)
1714 s->bad = 1;
1715 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1716 "FastCGI:%s server \"%s\" has failed to remain"
1717 " running for %d seconds given %d attempts, its restart"
1718 " interval has been backed off to %d seconds",
1719 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1720 s->fs_path, RUNTIME_SUCCESS_INTERVAL, MAX_FAILED_STARTS,
1721 FAILED_STARTS_DELAY);
1723 else
1725 s->bad = 0;
1726 s->numFailures = 0;
1727 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1728 "FastCGI:%s server \"%s\" has remained"
1729 " running for more than %d seconds, its restart"
1730 " interval has been restored to %d seconds",
1731 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1732 s->fs_path, RUNTIME_SUCCESS_INTERVAL, s->restartDelay);
1737 if (s->bad)
1739 restartTime += FAILED_STARTS_DELAY;
1741 else
1743 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1746 if (restartTime <= now)
1748 if (s->bad)
1750 s->bad = 0;
1751 s->numFailures = MAX_FAILED_STARTS;
1754 if (s->listenFd < 0 && init_listen_sock(s))
1756 if (sleepSeconds > s->initStartDelay)
1757 sleepSeconds = s->initStartDelay;
1758 break;
1760 #ifndef WIN32
1761 if (caughtSigTerm) {
1762 goto ProcessSigTerm;
1764 #endif
1765 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1766 if (s->procs[i].pid <= 0) {
1767 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1768 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1769 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1770 s->fs_path);
1772 sleepSeconds = min(sleepSeconds,
1773 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1775 ap_assert(s->procs[i].pid < 0);
1776 break;
1779 s->procs[i].start_time = now;
1780 s->restartTime = now;
1782 if (s->startTime == 0) {
1783 s->startTime = now;
1786 if (s->directive == APP_CLASS_DYNAMIC) {
1787 s->numProcesses++;
1788 fcgi_dynamic_total_proc_count++;
1789 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1792 s->procs[i].state = FCGI_RUNNING_STATE;
1794 if (fcgi_wrapper) {
1795 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1796 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1797 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1798 s->fs_path, (long) s->uid, (long) s->gid,
1799 restart ? "re" : "", (long) s->procs[i].pid);
1801 else {
1802 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1803 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1804 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1805 s->fs_path, restart ? "re" : "", (long) s->procs[i].pid);
1807 ap_assert(s->procs[i].pid > 0);
1808 } else {
1809 sleepSeconds = min(sleepSeconds, restartTime - now);
1815 #ifndef WIN32
1817 if(caughtSigTerm) {
1818 goto ProcessSigTerm;
1820 if((!caughtSigChld) && (!caughtSigAlarm)) {
1821 fd_set rfds;
1823 alarm(sleepSeconds);
1825 FD_ZERO(&rfds);
1826 FD_SET(fcgi_pm_pipe[0], &rfds);
1827 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1829 alarmLeft = alarm(0);
1831 callWaitPid = caughtSigChld;
1832 caughtSigChld = FALSE;
1833 callDynamicProcs = caughtSigAlarm;
1834 caughtSigAlarm = FALSE;
1836 now = time(NULL);
1839 * Dynamic fcgi process management
1841 if((callDynamicProcs) || (!callWaitPid)) {
1842 dynamic_read_msgs(read_ready);
1843 if(fcgi_dynamic_epoch == 0) {
1844 fcgi_dynamic_epoch = now;
1846 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1847 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1848 dynamic_kill_idle_fs_procs();
1849 fcgi_dynamic_epoch = now;
1853 if(!callWaitPid) {
1854 continue;
1857 /* We've caught SIGCHLD, so find out who it was using waitpid,
1858 * write a log message and update its data structure. */
1860 for (;;) {
1861 if (caughtSigTerm)
1862 goto ProcessSigTerm;
1864 childPid = waitpid(-1, &waitStatus, WNOHANG);
1866 if (childPid == -1 || childPid == 0)
1867 break;
1869 for (s = fcgi_servers; s != NULL; s = s->next) {
1870 if (s->directive == APP_CLASS_EXTERNAL)
1871 continue;
1873 if (s->directive == APP_CLASS_DYNAMIC)
1874 numChildren = dynamicMaxClassProcs;
1875 else
1876 numChildren = s->numProcesses;
1878 for (i = 0; i < numChildren; i++) {
1879 if (s->procs[i].pid == childPid)
1880 goto ChildFound;
1884 /* TODO: print something about this unknown child */
1885 continue;
1887 ChildFound:
1888 s->procs[i].pid = -1;
1890 if (s->directive == APP_CLASS_STANDARD) {
1891 /* Always restart static apps */
1892 s->procs[i].state = FCGI_START_STATE;
1893 s->numFailures++;
1895 else {
1896 s->numProcesses--;
1897 fcgi_dynamic_total_proc_count--;
1899 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1900 s->procs[i].state = FCGI_KILLED_STATE;
1902 else {
1903 /* A dynamic app died or exited without provocation from the PM */
1904 s->numFailures++;
1906 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1907 s->procs[i].state = FCGI_START_STATE;
1908 else
1909 s->procs[i].state = FCGI_READY_STATE;
1913 if (WIFEXITED(waitStatus)) {
1914 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1915 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1916 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1917 s->fs_path, (long) childPid, WEXITSTATUS(waitStatus));
1919 else if (WIFSIGNALED(waitStatus)) {
1920 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1921 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1922 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1923 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus),
1924 #ifdef WCOREDUMP
1925 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1926 #else
1927 "");
1928 #endif
1930 else if (WIFSTOPPED(waitStatus)) {
1931 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1932 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1933 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1934 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus));
1936 } /* for (;;), waitpid() */
1938 #else /* WIN32 */
1940 /* wait for an event to occur or timer expires */
1941 expire = time(NULL) + sleepSeconds;
1942 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1944 if (dwRet == WAIT_FAILED) {
1945 /* There is something seriously wrong here */
1946 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1947 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1948 bTimeToDie = TRUE;
1951 if (dwRet != WAIT_TIMEOUT) {
1952 now = time(NULL);
1954 if (now < expire)
1955 alarmLeft = expire - now;
1959 * Dynamic fcgi process management
1961 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1962 if (dwRet == MBOX_EVENT) {
1963 read_ready = 1;
1966 now = time(NULL);
1968 dynamic_read_msgs(read_ready);
1970 if(fcgi_dynamic_epoch == 0) {
1971 fcgi_dynamic_epoch = now;
1974 if ((now-fcgi_dynamic_epoch >= (int) dynamicKillInterval) ||
1975 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1976 dynamic_kill_idle_fs_procs();
1977 fcgi_dynamic_epoch = now;
1979 read_ready = 0;
1981 else if (dwRet == WAKE_EVENT) {
1982 continue;
1984 else if (dwRet == TERM_EVENT) {
1985 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1986 "FastCGI: Termination event received process manager shutting down");
1988 bTimeToDie = TRUE;
1989 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
1991 goto ProcessSigTerm;
1993 else {
1994 // Have an received an unknown event - should not happen
1995 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1996 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1998 bTimeToDie = TRUE;
1999 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2001 goto ProcessSigTerm;
2004 #endif /* WIN32 */
2006 } /* for (;;), the whole shoot'n match */
2008 ProcessSigTerm:
2010 * Kill off the children, then exit.
2012 shutdown_all();
2014 #ifdef WIN32
2015 return;
2016 #else
2017 exit(0);
2018 #endif
2021 #ifdef WIN32
2022 int fcgi_pm_add_job(fcgi_pm_job *new_job)
2024 int rv = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
2026 if (rv != WAIT_OBJECT_0 && rv != WAIT_ABANDONED)
2028 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2029 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2030 return -1;
2033 new_job->next = fcgi_dynamic_mbox;
2034 fcgi_dynamic_mbox = new_job;
2036 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
2038 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2039 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
2042 return 0;
2044 #endif