initial AP2 support (win)
[mod_fastcgi.git] / fcgi_pm.c
blobd2c6e6f4367f6dbdbb4de99278aba60227beece4
1 /*
2 * $Id: fcgi_pm.c,v 1.72 2002/07/23 00:54:18 robs Exp $
3 */
6 #include "fcgi.h"
8 #ifdef _HPUX_SOURCE
9 #include <unistd.h>
10 #define seteuid(arg) setresuid(-1, (arg), -1)
11 #endif
13 int fcgi_dynamic_total_proc_count = 0; /* number of running apps */
14 time_t fcgi_dynamic_epoch = 0; /* last time kill_procs was
15 * invoked by process mgr */
16 time_t fcgi_dynamic_last_analyzed = 0; /* last time calculation was
17 * made for the dynamic procs */
19 static time_t now = 0;
21 #ifdef WIN32
22 #pragma warning ( disable : 4100 4102 )
23 static BOOL bTimeToDie = FALSE; /* process termination flag */
24 HANDLE fcgi_event_handles[3];
25 #ifndef SIGKILL
26 #define SIGKILL 9
27 #endif
28 #endif
31 #ifndef WIN32
32 static int seteuid_root(void)
34 int rc = seteuid((uid_t)0);
35 if (rc == -1) {
36 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
37 "FastCGI: seteuid(0) failed");
39 return rc;
42 static int seteuid_user(void)
44 int rc = seteuid(ap_user_id);
45 if (rc == -1) {
46 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
47 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id);
49 return rc;
51 #endif
54 * Signal the process to exit. How (or if) the process responds
55 * depends on the FastCGI application library (esp. on Win32) and
56 * possibly application code (signal handlers and whether or not
57 * SA_RESTART is on). At any rate, we send the signal with the
58 * hopes that the process will exit on its own. Later, as we
59 * review the state of application processes, if we see one marked
60 * for death, but that hasn't died within a specified period of
61 * time, fcgi_kill() is called again with a KILL)
63 static void fcgi_kill(ServerProcess *process, int sig)
65 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process->pid, sig);
67 process->state = FCGI_VICTIM_STATE;
69 #ifdef WIN32
70 if (sig == SIGTERM)
72 SetEvent(process->terminationEvent);
74 else if (sig == SIGKILL)
76 TerminateProcess(process->handle, 1);
78 else
80 ap_assert(0);
82 #else
83 if (fcgi_wrapper)
85 seteuid_root();
88 kill(process->pid, sig);
90 if (fcgi_wrapper)
92 seteuid_user();
94 #endif
97 /*******************************************************************************
98 * Send SIGTERM to each process in the server class, remove socket
99 * file if appropriate. Currently this is only called when the PM is shutting
100 * down and thus memory isn't freed and sockets and files aren't closed.
102 static void shutdown_all()
104 fcgi_server *s = fcgi_servers;
106 while (s)
108 ServerProcess *proc = s->procs;
109 int i;
110 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
111 ? dynamicMaxClassProcs
112 : s->numProcesses;
114 #ifndef WIN32
115 if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL)
117 /* Remove the socket file */
118 if (unlink(s->socket_path) != 0 && errno != ENOENT) {
119 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
120 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
121 s->socket_path,
122 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path);
125 #endif
127 /* Send TERM to all processes */
128 for (i = 0; i < numChildren; i++, proc++)
130 if (proc->state == FCGI_RUNNING_STATE)
132 fcgi_kill(proc, SIGTERM);
136 s = s->next;
139 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
142 * WIN32 applications may not have support for the shutdown event
143 * depending on their application library version
146 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT);
147 s = fcgi_servers;
149 while (s)
151 ServerProcess *proc = s->procs;
152 int i;
153 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
154 ? dynamicMaxClassProcs
155 : s->numProcesses;
157 /* Send KILL to all processes */
158 for (i = 0; i < numChildren; i++, proc++)
160 if (proc->state == FCGI_RUNNING_STATE)
162 fcgi_kill(proc, SIGKILL);
166 s = s->next;
169 #endif /* WIN32 */
172 static int init_listen_sock(fcgi_server * fs)
174 ap_assert(fs->directive != APP_CLASS_EXTERNAL);
176 /* Create the socket */
177 if ((fs->listenFd = socket(fs->socket_addr->sa_family, SOCK_STREAM, 0)) < 0)
179 #ifdef WIN32
180 errno = WSAGetLastError(); // Not sure if this will work as expected
181 #endif
182 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
183 "FastCGI: can't create %sserver \"%s\": socket() failed",
184 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
185 fs->fs_path);
186 return -1;
189 #ifndef WIN32
190 if (fs->socket_addr->sa_family == AF_UNIX)
192 /* Remove any existing socket file.. just in case */
193 unlink(((struct sockaddr_un *)fs->socket_addr)->sun_path);
195 else
196 #endif
198 int flag = 1;
199 setsockopt(fs->listenFd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
202 /* Bind it to the socket_addr */
203 if (bind(fs->listenFd, fs->socket_addr, fs->socket_addr_len))
205 char port[11];
207 #ifdef WIN32
208 errno = WSAGetLastError();
209 #endif
210 ap_snprintf(port, sizeof(port), "port=%d",
211 ((struct sockaddr_in *)fs->socket_addr)->sin_port);
213 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
214 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
215 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
216 fs->fs_path,
217 #ifndef WIN32
218 (fs->socket_addr->sa_family == AF_UNIX) ?
219 ((struct sockaddr_un *)fs->socket_addr)->sun_path :
220 #endif
221 port);
224 #ifndef WIN32
225 /* Twiddle Unix socket permissions */
226 else if (fs->socket_addr->sa_family == AF_UNIX
227 && chmod(((struct sockaddr_un *)fs->socket_addr)->sun_path, S_IRUSR | S_IWUSR))
229 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
230 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
231 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
232 fs->fs_path);
234 #endif
236 /* Set to listen */
237 else if (listen(fs->listenFd, fs->listenQueueDepth))
239 #ifdef WIN32
240 errno = WSAGetLastError();
241 #endif
242 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
243 "FastCGI: can't create %sserver \"%s\": listen() failed",
244 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
245 fs->fs_path);
247 else
249 return 0;
252 close(fs->listenFd);
253 fs->listenFd = -1;
254 return -2;
258 *----------------------------------------------------------------------
260 * pm_main
262 * The FastCGI process manager, which runs as a separate
263 * process responsible for:
264 * - Starting all the FastCGI proceses.
265 * - Restarting any of these processes that die (indicated
266 * by SIGCHLD).
267 * - Catching SIGTERM and relaying it to all the FastCGI
268 * processes before exiting.
270 * Inputs:
271 * Uses global variable fcgi_servers.
273 * Results:
274 * Does not return.
276 * Side effects:
277 * Described above.
279 *----------------------------------------------------------------------
281 #ifndef WIN32
282 static int caughtSigTerm = FALSE;
283 static int caughtSigChld = FALSE;
284 static int caughtSigAlarm = FALSE;
286 static void signal_handler(int signo)
288 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
289 /* SIGUSR1 & SIGHUP are sent by apache to its process group
290 * when apache get 'em. Apache follows up (1.2.x) with attacks
291 * on each of its child processes, but we've got the KillMgr
292 * sitting between us so we never see the KILL. The main loop
293 * in ProcMgr also checks to see if the KillMgr has terminated,
294 * and if it has, we handl it as if we should shutdown too. */
295 caughtSigTerm = TRUE;
296 } else if(signo == SIGCHLD) {
297 caughtSigChld = TRUE;
298 } else if(signo == SIGALRM) {
299 caughtSigAlarm = TRUE;
302 #endif
305 *----------------------------------------------------------------------
307 * spawn_fs_process --
309 * Fork and exec the specified fcgi process.
311 * Results:
312 * 0 for successful fork, -1 for failed fork.
314 * In case the child fails before or in the exec, the child
315 * obtains the error log by calling getErrLog, logs
316 * the error, and exits with exit status = errno of
317 * the failed system call.
319 * Side effects:
320 * Child process created.
322 *----------------------------------------------------------------------
324 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
326 #ifndef WIN32
328 pid_t child_pid;
329 int i;
330 char *dirName;
331 char *dnEnd, *failedSysCall;
333 child_pid = fork();
334 if (child_pid) {
335 return child_pid;
338 /* We're the child. We're gonna exec() so pools don't matter. */
340 dnEnd = strrchr(fs->fs_path, '/');
341 if (dnEnd == NULL) {
342 dirName = "./";
343 } else {
344 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
345 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
347 if (chdir(dirName) < 0) {
348 failedSysCall = "chdir()";
349 goto FailedSystemCallExit;
352 #ifndef __EMX__
353 /* OS/2 dosen't support nice() */
354 if (fs->processPriority != 0) {
355 if (nice(fs->processPriority) == -1) {
356 failedSysCall = "nice()";
357 goto FailedSystemCallExit;
360 #endif
362 /* Open the listenFd on spec'd fd */
363 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
364 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
366 /* Close all other open fds, except stdout/stderr. Leave these two open so
367 * FastCGI applications don't have to find and fix ALL 3rd party libs that
368 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
369 * main server error_log - @@@ provide a directive control where this goes.
371 ap_error_log2stderr(fcgi_apache_main_server);
372 dup2(STDERR_FILENO, STDOUT_FILENO);
373 for (i = 0; i < FCGI_MAX_FD; i++) {
374 if (i != FCGI_LISTENSOCK_FILENO && i != STDERR_FILENO && i != STDOUT_FILENO) {
375 close(i);
379 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
380 * install its own handler. */
381 signal(SIGPIPE, SIG_IGN);
383 if (fcgi_wrapper && (fcgi_user_id != fs->uid || fcgi_group_id != fs->gid)) {
384 char *shortName = strrchr(fs->fs_path, '/') + 1;
386 /* Relinquish our root real uid powers */
387 seteuid_root();
388 setuid(ap_user_id);
390 do {
391 execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group, shortName, NULL, fs->envp);
392 } while (errno == EINTR);
394 else {
395 do {
396 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
397 } while (errno == EINTR);
400 failedSysCall = "execle()";
402 FailedSystemCallExit:
403 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
404 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
405 exit(-1);
407 /* avoid an irrelevant compiler warning */
408 return(0);
410 #else /* WIN32 */
412 #ifdef APACHE2
414 /* based on mod_cgi.c:run_cgi_child() */
416 apr_pool_t * tp;
417 char * termination_env_string;
418 HANDLE listen_handle = INVALID_HANDLE_VALUE;
419 apr_procattr_t * procattr;
420 apr_proc_t proc = { 0 };
421 apr_file_t * file;
422 int i = 0;
424 if (apr_pool_create(&tp, fcgi_config_pool))
425 return 0;
427 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
428 if (process->terminationEvent == NULL)
429 goto CLEANUP;
431 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
433 termination_env_string = ap_psprintf(tp,
434 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
436 while (fs->envp[i]) i++;
437 fs->envp[i++] = termination_env_string;
438 fs->envp[i] = (char *) fs->mutex_env_string;
440 ap_assert(fs->envp[i + 1] == NULL);
442 if (fs->socket_path)
444 SECURITY_ATTRIBUTES sa = { 0 };
446 sa.bInheritHandle = TRUE;
447 sa.nLength = sizeof(sa);
449 listen_handle = CreateNamedPipe(fs->socket_path,
450 PIPE_ACCESS_DUPLEX,
451 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
452 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
454 if (listen_handle == INVALID_HANDLE_VALUE)
456 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
457 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
458 fs->fs_path);
459 goto CLEANUP;
462 else
464 listen_handle = (HANDLE) fs->listenFd;
467 if (apr_procattr_create(&procattr, tp))
468 goto CLEANUP;
470 if (apr_procattr_dir_set(procattr, ap_make_dirstr_parent(tp, fs->fs_path)))
471 goto CLEANUP;
473 if (apr_procattr_detach_set(procattr, 1))
474 goto CLEANUP;
476 if (apr_os_file_put(&file, &listen_handle, 0, tp))
477 goto CLEANUP;
479 /* procattr is opaque so we have to use this - unfortuantely it dups */
480 if (apr_procattr_child_in_set(procattr, file, NULL))
481 goto CLEANUP;
483 if (apr_proc_create(&proc, fs->fs_path, NULL, fs->envp, procattr, tp))
484 goto CLEANUP;
486 process->handle = proc.hproc;
489 CLEANUP:
491 if (i)
493 fs->envp[i - 1] = NULL;
496 ap_destroy_pool(tp);
498 return proc.pid;
500 #else /* WIN32 && !APACHE2 */
502 /* Adapted from Apache's util_script.c ap_call_exec() */
503 char *interpreter = NULL;
504 char *quoted_filename;
505 char *pCommand;
506 char *pEnvBlock, *pNext;
508 int i = 0;
509 int iEnvBlockLen = 1;
511 file_type_e fileType;
513 STARTUPINFO si;
514 PROCESS_INFORMATION pi;
516 request_rec r;
517 pid_t pid = -1;
519 pool * tp = ap_make_sub_pool(fcgi_config_pool);
521 HANDLE listen_handle;
522 char * termination_env_string = NULL;
524 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
525 if (process->terminationEvent == NULL)
527 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
528 "FastCGI: can't create termination event for server \"%s\", "
529 "CreateEvent() failed", fs->fs_path);
530 goto CLEANUP;
532 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
534 termination_env_string = ap_psprintf(tp,
535 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
537 if (fs->socket_path)
539 SECURITY_ATTRIBUTES sa;
541 sa.lpSecurityDescriptor = NULL;
542 sa.bInheritHandle = TRUE;
543 sa.nLength = sizeof(sa);
545 listen_handle = CreateNamedPipe(fs->socket_path,
546 PIPE_ACCESS_DUPLEX,
547 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
548 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
550 if (listen_handle == INVALID_HANDLE_VALUE)
552 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
553 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
554 goto CLEANUP;
557 else
559 listen_handle = (HANDLE) fs->listenFd;
562 memset(&si, 0, sizeof(si));
563 memset(&pi, 0, sizeof(pi));
564 memset(&r, 0, sizeof(r));
566 // Can up a fake request to pass to ap_get_win32_interpreter()
567 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
568 r.server = fcgi_apache_main_server;
569 r.filename = (char *) fs->fs_path;
570 r.pool = tp;
572 fileType = ap_get_win32_interpreter(&r, &interpreter);
574 if (fileType == eFileTypeUNKNOWN) {
575 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
576 "FastCGI: %s is not executable; ensure interpreted scripts have "
577 "\"#!\" as their first line",
578 fs->fs_path);
579 ap_destroy_pool(tp);
580 goto CLEANUP;
584 * We have the interpreter (if there is one) and we have
585 * the arguments (if there are any).
586 * Build the command string to pass to CreateProcess.
588 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
589 if (interpreter && *interpreter) {
590 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
592 else {
593 pCommand = quoted_filename;
597 * Make child process use hPipeOutputWrite as standard out,
598 * and make sure it does not show on screen.
600 si.cb = sizeof(si);
601 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
602 si.wShowWindow = SW_HIDE;
603 si.hStdInput = listen_handle;
605 // XXX These should be open to the error_log
606 si.hStdOutput = INVALID_HANDLE_VALUE;
607 si.hStdError = INVALID_HANDLE_VALUE;
610 * Win32's CreateProcess call requires that the environment
611 * be passed in an environment block, a null terminated block of
612 * null terminated strings.
613 * @todo we should store the env in this format for win32.
615 while (fs->envp[i])
617 iEnvBlockLen += strlen(fs->envp[i]) + 1;
618 i++;
621 iEnvBlockLen += strlen(termination_env_string) + 1;
622 iEnvBlockLen += strlen(fs->mutex_env_string) + 1;
624 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
626 i = 0;
627 pNext = pEnvBlock;
628 while (fs->envp[i])
630 strcpy(pNext, fs->envp[i]);
631 pNext += strlen(pNext) + 1;
632 i++;
635 strcpy(pNext, termination_env_string);
636 pNext += strlen(pNext) + 1;
637 strcpy(pNext, fs->mutex_env_string);
639 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
641 pEnvBlock,
642 ap_make_dirstr_parent(tp, fs->fs_path),
643 &si, &pi))
645 /* Hack to get 16-bit CGI's working. It works for all the
646 * standard modules shipped with Apache. pi.dwProcessId is 0
647 * for 16-bit CGIs and all the Unix specific code that calls
648 * ap_call_exec interprets this as a failure case. And we can't
649 * use -1 either because it is mapped to 0 by the caller.
651 pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId;
653 process->handle = pi.hProcess;
654 CloseHandle(pi.hThread);
657 if (fs->socket_path)
659 CloseHandle(listen_handle);
662 CLEANUP:
664 ap_destroy_pool(tp);
666 return pid;
668 #endif /* !APACHE2 */
669 #endif /* WIN32 */
672 #ifndef WIN32
673 static void reduce_privileges(void)
675 char *name;
677 if (geteuid() != 0)
678 return;
680 #ifndef __EMX__
681 /* Get username if passed as a uid */
682 if (ap_user_name[0] == '#') {
683 uid_t uid = atoi(&ap_user_name[1]);
684 struct passwd *ent = getpwuid(uid);
686 if (ent == NULL) {
687 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
688 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
689 "you probably need to modify the User directive", (unsigned)uid);
690 exit(1);
692 name = ent->pw_name;
694 else
695 name = ap_user_name;
697 /* Change Group */
698 if (setgid(ap_group_id) == -1) {
699 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
700 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
701 exit(1);
704 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
706 /* Initialize supplementary groups */
707 if (initgroups(name, ap_group_id) == -1) {
708 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
709 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
710 name, (unsigned)ap_group_id);
711 exit(1);
713 #endif /* __EMX__ */
715 /* Change User */
716 if (fcgi_wrapper) {
717 if (seteuid_user() == -1) {
718 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
719 "FastCGI: process manager exiting, failed to reduce privileges");
720 exit(1);
723 else {
724 if (setuid(ap_user_id) == -1) {
725 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
726 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
727 exit(1);
732 /*************
733 * Change the name of this process - best we can easily.
735 static void change_process_name(const char * const name)
737 strncpy(ap_server_argv0, name, strlen(ap_server_argv0));
739 #endif
741 static void schedule_start(fcgi_server *s, int proc)
743 /* If we've started one recently, don't register another */
744 time_t time_passed = now - s->restartTime;
746 if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay))
747 || ((s->procs[proc].pid == 0) && (time_passed < s->initStartDelay)))
749 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);
750 return;
753 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
754 s->procs[proc].state = FCGI_START_STATE;
755 if (proc == dynamicMaxClassProcs - 1) {
756 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
757 "FastCGI: scheduled the %sstart of the last (dynamic) server "
758 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
759 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
764 *----------------------------------------------------------------------
766 * dynamic_read_msgs
768 * Removes the records written by request handlers and decodes them.
769 * We also update the data structures to reflect the changes.
771 *----------------------------------------------------------------------
774 static void dynamic_read_msgs(int read_ready)
776 fcgi_server *s;
777 int rc;
779 #ifndef WIN32
780 static int buflen = 0;
781 static char buf[FCGI_MSGS_BUFSIZE + 1];
782 char *ptr1, *ptr2, opcode;
783 char execName[FCGI_MAXPATH + 1];
784 char user[MAX_USER_NAME_LEN + 2];
785 char group[MAX_GID_CHAR_LEN + 1];
786 unsigned long q_usec = 0UL, req_usec = 0UL;
787 #else
788 fcgi_pm_job *joblist = NULL;
789 fcgi_pm_job *cjob = NULL;
790 #endif
792 pool *sp = NULL, *tp;
794 #ifndef WIN32
795 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
796 #endif
799 * To prevent the idle application from running indefinitely, we
800 * check the timer and if it is expired, we recompute the values
801 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
802 * message is received, only updates are made to the data structures.
804 if (fcgi_dynamic_last_analyzed == 0) {
805 fcgi_dynamic_last_analyzed = now;
807 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
808 for (s = fcgi_servers; s != NULL; s = s->next) {
809 if (s->directive != APP_CLASS_DYNAMIC)
810 break;
812 /* Advance the last analyzed timestamp by the elapsed time since
813 * it was last set. Round the increase down to the nearest
814 * multiple of dynamicUpdateInterval */
816 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
817 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
818 s->totalConnTime = 0UL;
819 s->totalQueueTime = 0UL;
823 if (read_ready <= 0) {
824 return;
827 #ifndef WIN32
828 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
829 if (rc <= 0) {
830 if (!caughtSigTerm) {
831 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
832 "FastCGI: read() from pipe failed (%d)", rc);
833 if (rc == 0) {
834 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
835 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
836 caughtSigTerm = TRUE;
839 return;
841 buflen += rc;
842 buf[buflen] = '\0';
844 #else
846 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
847 * request to do something) and/or when a timeout expires.
848 * There really should be no reason why this wait would get stuck
849 * but there's no point in waiting forever. */
851 rc = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
853 if (rc != WAIT_OBJECT_0 && rc != WAIT_ABANDONED)
855 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
856 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
857 return;
860 joblist = fcgi_dynamic_mbox;
861 fcgi_dynamic_mbox = NULL;
863 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
865 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
866 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
869 cjob = joblist;
870 #endif
872 #ifdef APACHE2
873 apr_pool_create(&tp, fcgi_config_pool);
874 #else
875 tp = ap_make_sub_pool(fcgi_config_pool);
876 #endif
878 #ifndef WIN32
879 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
880 int scan_failed = 0;
882 ptr2 = strchr(ptr1, '*');
883 if (ptr2) {
884 *ptr2++ = '\0';
886 else {
887 break;
890 opcode = *ptr1;
892 switch (opcode)
894 case FCGI_SERVER_START_JOB:
895 case FCGI_SERVER_RESTART_JOB:
897 if (sscanf(ptr1, "%c %s %16s %15s",
898 &opcode, execName, user, group) != 4)
900 scan_failed = 1;
902 break;
904 case FCGI_REQUEST_TIMEOUT_JOB:
906 if (sscanf(ptr1, "%c %s %16s %15s",
907 &opcode, execName, user, group) != 4)
909 scan_failed = 1;
911 break;
913 case FCGI_REQUEST_COMPLETE_JOB:
915 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
916 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
918 scan_failed = 1;
920 break;
922 default:
924 scan_failed = 1;
925 break;
928 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode, execName, user, group, q_usec, req_usec);
930 if (scan_failed) {
931 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
932 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
933 goto NextJob;
935 #else
936 /* Update data structures for processing */
937 while (cjob != NULL) {
938 joblist = cjob->next;
939 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
940 #endif
942 #ifndef WIN32
943 s = fcgi_util_fs_get(execName, user, group);
944 #else
945 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
946 #endif
948 #ifndef WIN32
949 if (s==NULL && opcode != FCGI_REQUEST_COMPLETE_JOB)
950 #else
951 if (s==NULL && cjob->id != FCGI_REQUEST_COMPLETE_JOB)
952 #endif
954 #ifdef WIN32
956 HANDLE mutex = CreateMutex(NULL, FALSE, cjob->fs_path);
958 if (mutex == NULL)
960 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
961 "FastCGI: can't create accept mutex "
962 "for (dynamic) server \"%s\"", cjob->fs_path);
963 goto BagNewServer;
966 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
967 #else
968 const char *err;
969 #endif
971 /* Create a perm subpool to hold the new server data,
972 * we can destroy it if something doesn't pan out */
973 #ifdef APACHE2
974 apr_pool_create(&sp, fcgi_config_pool);
975 #else
976 sp = ap_make_sub_pool(fcgi_config_pool);
977 #endif
979 /* Create a new "dynamic" server */
980 s = fcgi_util_fs_new(sp);
982 s->directive = APP_CLASS_DYNAMIC;
983 s->restartDelay = dynamicRestartDelay;
984 s->listenQueueDepth = dynamicListenQueueDepth;
985 s->initStartDelay = dynamicInitStartDelay;
986 s->envp = dynamicEnvp;
987 s->flush = dynamicFlush;
989 #ifdef WIN32
990 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
991 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
992 #else
993 s->fs_path = ap_pstrdup(sp, execName);
994 #endif
995 ap_getparents(s->fs_path);
996 ap_no2slash(s->fs_path);
997 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
999 /* XXX the socket_path (both Unix and Win) *is* deducible and
1000 * thus can and will be used by other apache instances without
1001 * the use of shared data regarding the processes serving the
1002 * requests. This can result in slightly unintuitive process
1003 * counts and security implications. This is prevented
1004 * if suexec (Unix) is in use. This is both a feature and a flaw.
1005 * Changing it now would break existing installations. */
1007 #ifndef WIN32
1008 /* Create socket file's path */
1009 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
1010 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1012 /* Create sockaddr, prealloc it so it won't get created in tp */
1013 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
1014 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
1015 &s->socket_addr_len, s->socket_path);
1016 if (err) {
1017 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1018 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
1019 goto BagNewServer;
1022 if (init_listen_sock(s)) {
1023 goto BagNewServer;
1026 /* If a wrapper is being used, config user/group info */
1027 if (fcgi_wrapper) {
1028 if (user[0] == '~') {
1029 /* its a user dir uri, the rest is a username, not a uid */
1030 struct passwd *pw = getpwnam(&user[1]);
1032 if (!pw) {
1033 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1034 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1035 execName, &user[1]);
1036 goto BagNewServer;
1038 s->uid = pw->pw_uid;
1039 s->user = ap_pstrdup(sp, user);
1040 s->username = s->user;
1042 s->gid = pw->pw_gid;
1043 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
1045 else {
1046 struct passwd *pw;
1048 s->uid = (uid_t)atol(user);
1049 pw = getpwuid(s->uid);
1050 if (!pw) {
1051 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1052 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1053 execName, (long)s->uid);
1054 goto BagNewServer;
1056 s->user = ap_pstrdup(sp, user);
1057 s->username = ap_pstrdup(sp, pw->pw_name);
1059 s->gid = (gid_t)atol(group);
1060 s->group = ap_pstrdup(sp, group);
1063 #else
1064 /* Create socket file's path */
1065 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
1066 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1067 s->listenFd = 0;
1068 #endif
1070 fcgi_util_fs_add(s);
1072 else {
1073 #ifndef WIN32
1074 if (opcode == FCGI_SERVER_RESTART_JOB) {
1075 #else
1076 if (cjob->id==FCGI_SERVER_RESTART_JOB) {
1077 #endif
1078 /* Check to see if the binary has changed. If so,
1079 * kill the FCGI application processes, and
1080 * restart them.
1082 struct stat stbuf;
1083 int i;
1085 #ifndef WIN32
1086 if ((stat(execName, &stbuf)==0) &&
1087 #else
1088 if ((stat(cjob->fs_path, &stbuf)==0) &&
1089 #endif
1090 (stbuf.st_mtime > s->restartTime)) {
1091 /* kill old server(s) */
1092 for (i = 0; i < dynamicMaxClassProcs; i++) {
1093 if (s->procs[i].pid > 0) {
1094 fcgi_kill(&s->procs[i], SIGTERM);
1098 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1099 "FastCGI: restarting server \"%s\" processes, newer version found",
1100 #ifndef WIN32
1101 execName);
1102 #else
1103 cjob->fs_path);
1104 #endif
1107 /* If dynamicAutoRestart, don't mark any new processes
1108 * for starting because we probably got the
1109 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1110 * will be restarting all of those we just killed.
1112 if (dynamicAutoRestart)
1113 goto NextJob;
1115 #ifndef WIN32
1116 else if (opcode == FCGI_SERVER_START_JOB) {
1117 #else
1118 else if (cjob->id==FCGI_SERVER_START_JOB) {
1119 #endif
1120 /* we've been asked to start a process--only start
1121 * it if we're not already running at least one
1122 * instance.
1124 int i;
1126 for (i = 0; i < dynamicMaxClassProcs; i++) {
1127 if (s->procs[i].state == FCGI_RUNNING_STATE)
1128 break;
1130 /* if already running, don't start another one */
1131 if (i < dynamicMaxClassProcs) {
1132 goto NextJob;
1137 #ifndef WIN32
1138 switch (opcode)
1139 #else
1140 switch (cjob->id)
1141 #endif
1143 int i, start;
1145 case FCGI_SERVER_RESTART_JOB:
1147 start = FALSE;
1149 /* We just waxed 'em all. Try to find an idle slot. */
1151 for (i = 0; i < dynamicMaxClassProcs; ++i)
1153 if (s->procs[i].state == FCGI_START_STATE
1154 || s->procs[i].state == FCGI_RUNNING_STATE)
1156 break;
1158 else if (s->procs[i].state == FCGI_KILLED_STATE
1159 || s->procs[i].state == FCGI_READY_STATE)
1161 start = TRUE;
1162 break;
1166 /* Nope, just use the first slot */
1167 if (i == dynamicMaxClassProcs)
1169 start = TRUE;
1170 i = 0;
1173 if (start)
1175 schedule_start(s, i);
1178 break;
1180 case FCGI_SERVER_START_JOB:
1181 case FCGI_REQUEST_TIMEOUT_JOB:
1183 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1185 * Extra instances should have been
1186 * terminated beforehand, probably need
1187 * to increase ProcessSlack parameter
1189 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1190 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1191 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1192 goto NextJob;
1195 /* find next free slot */
1196 for (i = 0; i < dynamicMaxClassProcs; i++)
1198 if (s->procs[i].state == FCGI_START_STATE)
1200 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1201 break;
1203 else if (s->procs[i].state == FCGI_RUNNING_STATE)
1205 continue;
1208 schedule_start(s, i);
1209 break;
1212 #ifdef FCGI_DEBUG
1213 if (i >= dynamicMaxClassProcs) {
1214 FCGIDBG1("ignore_job: slots are max'd");
1216 #endif
1217 break;
1218 case FCGI_REQUEST_COMPLETE_JOB:
1219 /* only record stats if we have a structure */
1220 if (s) {
1221 #ifndef WIN32
1222 s->totalConnTime += req_usec;
1223 s->totalQueueTime += q_usec;
1224 #else
1225 s->totalConnTime += cjob->start_time;
1226 s->totalQueueTime += cjob->qsec;
1227 #endif
1229 break;
1232 NextJob:
1234 #ifdef WIN32
1235 /* Cleanup job data */
1236 free(cjob->fs_path);
1237 free(cjob->user);
1238 free(cjob->group);
1239 free(cjob);
1240 cjob = joblist;
1241 #endif
1243 continue;
1245 BagNewServer:
1246 if (sp) ap_destroy_pool(sp);
1248 #ifdef WIN32
1249 free(cjob->fs_path);
1250 free(cjob);
1251 cjob = joblist;
1252 #endif
1255 #ifndef WIN32
1256 if (ptr1 == buf) {
1257 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1258 "FastCGI: really bogus message: \"%s\"", ptr1);
1259 ptr1 += strlen(buf);
1262 buflen -= ptr1 - buf;
1263 if (buflen) {
1264 memmove(buf, ptr1, buflen);
1266 #endif
1268 ap_destroy_pool(tp);
1272 *----------------------------------------------------------------------
1274 * dynamic_kill_idle_fs_procs
1276 * Implement a kill policy for the dynamic FastCGI applications.
1277 * We also update the data structures to reflect the changes.
1279 * Side effects:
1280 * Processes are marked for deletion possibly killed.
1282 *----------------------------------------------------------------------
1284 static void dynamic_kill_idle_fs_procs(void)
1286 fcgi_server *s;
1287 int victims = 0;
1289 for (s = fcgi_servers; s != NULL; s = s->next)
1292 * server's smoothed running time, or if that's 0, the current total
1294 unsigned long connTime;
1297 * maximum number of microseconds that all of a server's running
1298 * processes together could have spent running since the last check
1300 unsigned long totalTime;
1303 * percentage, 0-100, of totalTime that the processes actually used
1305 int loadFactor;
1307 int i;
1308 int really_running = 0;
1310 if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1312 continue;
1315 /* s->numProcesses includes pending kills so get the "active" count */
1316 for (i = 0; i < dynamicMaxClassProcs; ++i)
1318 if (s->procs[i].state == FCGI_RUNNING_STATE) ++really_running;
1321 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1322 totalTime = really_running * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1324 loadFactor = 100 * connTime / totalTime;
1326 if (really_running == 1)
1328 if (loadFactor >= dynamicThreshold1)
1330 continue;
1333 else
1335 int load = really_running / ( really_running - 1) * loadFactor;
1337 if (load >= dynamicThresholdN)
1339 continue;
1344 * Run through the procs to see if we can get away w/o waxing one.
1346 for (i = 0; i < dynamicMaxClassProcs; ++i)
1348 if (s->procs[i].state == FCGI_START_STATE)
1350 s->procs[i].state = FCGI_READY_STATE;
1351 break;
1353 else if (s->procs[i].state == FCGI_VICTIM_STATE)
1355 break;
1359 if (i >= dynamicMaxClassProcs)
1361 ServerProcess * procs = s->procs;
1362 int youngest = -1;
1364 for (i = 0; i < dynamicMaxClassProcs; ++i)
1366 if (procs[i].state == FCGI_RUNNING_STATE)
1368 if (youngest == -1 || procs[i].start_time >= procs[youngest].start_time)
1370 youngest = i;
1375 if (youngest != -1)
1377 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1378 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1379 s->fs_path, (long) s->procs[youngest].pid);
1381 fcgi_kill(&s->procs[youngest], SIGTERM);
1383 victims++;
1387 * If the number of non-victims is less than or equal to
1388 * the minimum that may be running without being killed off,
1389 * don't select any more victims.
1391 if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1393 break;
1399 #ifdef WIN32
1401 // This is a little bogus, there's gotta be a better way to do this
1402 // Can we use WaitForMultipleObjects()
1403 #define FCGI_PROC_WAIT_TIME 100
1405 void child_wait_thread_main(void *dummy) {
1406 fcgi_server *s;
1407 DWORD dwRet = WAIT_TIMEOUT;
1408 int numChildren;
1409 int i;
1410 int waited;
1412 while (!bTimeToDie) {
1413 waited = 0;
1415 for (s = fcgi_servers; s != NULL; s = s->next) {
1416 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1417 continue;
1419 if (s->directive == APP_CLASS_DYNAMIC) {
1420 numChildren = dynamicMaxClassProcs;
1422 else {
1423 numChildren = s->numProcesses;
1426 for (i=0; i < numChildren; i++) {
1427 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1429 DWORD exitStatus = 0;
1431 /* timeout is currently set for 100 miliecond */
1432 /* it may need to be longer or user customizable */
1433 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1435 waited = 1;
1437 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1438 /* a child fs has died */
1439 /* mark the child as dead */
1441 if (s->directive == APP_CLASS_STANDARD) {
1442 /* restart static app */
1443 s->procs[i].state = FCGI_START_STATE;
1444 s->numFailures++;
1446 else {
1447 s->numProcesses--;
1448 fcgi_dynamic_total_proc_count--;
1449 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1451 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1452 s->procs[i].state = FCGI_KILLED_STATE;
1454 else {
1455 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1456 s->numFailures++;
1458 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1459 s->procs[i].state = FCGI_START_STATE;
1461 else {
1462 s->procs[i].state = FCGI_READY_STATE;
1467 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1469 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1470 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1471 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1472 s->fs_path, (long) s->procs[i].pid, exitStatus);
1474 CloseHandle(s->procs[i].handle);
1475 s->procs[i].handle = INVALID_HANDLE_VALUE;
1476 s->procs[i].pid = -1;
1478 /* wake up the main thread */
1479 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1484 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1487 #endif
1489 #ifndef WIN32
1490 static void setup_signals(void)
1492 struct sigaction sa;
1494 /* Setup handlers */
1496 sa.sa_handler = signal_handler;
1497 sigemptyset(&sa.sa_mask);
1498 sa.sa_flags = 0;
1500 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1501 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1502 "sigaction(SIGTERM) failed");
1504 /* httpd restart */
1505 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1506 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1507 "sigaction(SIGHUP) failed");
1509 /* httpd graceful restart */
1510 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1511 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1512 "sigaction(SIGUSR1) failed");
1514 /* read messages from request handlers - kill interval expired */
1515 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1516 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1517 "sigaction(SIGALRM) failed");
1519 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1520 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1521 "sigaction(SIGCHLD) failed");
1524 #endif
1526 #ifndef WIN32
1527 int fcgi_pm_main(void *dummy, child_info *info)
1528 #else
1529 void fcgi_pm_main(void *dummy)
1530 #endif
1532 fcgi_server *s;
1533 unsigned int i;
1534 int read_ready = 0;
1535 int alarmLeft = 0;
1537 #ifdef WIN32
1538 DWORD dwRet;
1539 HANDLE child_wait_thread = INVALID_HANDLE_VALUE;
1540 #else
1541 int callWaitPid, callDynamicProcs;
1542 #endif
1544 #ifdef WIN32
1545 // Add SystemRoot to the dynamic environment
1546 char ** envp = dynamicEnvp;
1547 for (i = 0; *envp; ++i) {
1548 ++envp;
1550 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1552 #else
1553 reduce_privileges();
1555 close(fcgi_pm_pipe[1]);
1556 change_process_name("fcgi-pm");
1557 setup_signals();
1559 if (fcgi_wrapper) {
1560 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1561 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1563 #endif
1565 /* Initialize AppClass */
1566 for (s = fcgi_servers; s != NULL; s = s->next)
1568 if (s->directive != APP_CLASS_STANDARD)
1569 continue;
1571 #ifdef WIN32
1572 if (s->socket_path)
1573 s->listenFd = 0;
1574 #endif
1576 for (i = 0; i < s->numProcesses; ++i)
1577 s->procs[i].state = FCGI_START_STATE;
1580 #ifdef WIN32
1581 child_wait_thread = (HANDLE) _beginthread(child_wait_thread_main, 0, NULL);
1583 if (child_wait_thread == (HANDLE) -1)
1585 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1586 "FastCGI: failed to create process manager's wait thread!");
1589 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1590 "FastCGI: process manager initialized");
1591 #else
1592 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1593 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1594 #endif
1596 now = time(NULL);
1599 * Loop until SIGTERM
1601 for (;;) {
1602 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1603 #ifdef WIN32
1604 time_t expire;
1605 #else
1606 pid_t childPid;
1607 int waitStatus;
1608 #endif
1609 unsigned int numChildren;
1612 * If we came out of sigsuspend() for any reason other than
1613 * SIGALRM, pick up where we left off.
1615 if (alarmLeft)
1616 sleepSeconds = alarmLeft;
1619 * Examine each configured AppClass for a process that needs
1620 * starting. Compute the earliest time when the start should
1621 * be attempted, starting it now if the time has passed. Also,
1622 * remember that we do NOT need to restart externally managed
1623 * FastCGI applications.
1625 for (s = fcgi_servers; s != NULL; s = s->next)
1627 if (s->directive == APP_CLASS_EXTERNAL)
1628 continue;
1630 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1631 ? dynamicMaxClassProcs
1632 : s->numProcesses;
1634 for (i = 0; i < numChildren; ++i)
1636 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1638 int restart = (s->procs[i].pid < 0);
1639 time_t restartTime = s->restartTime;
1641 if (s->bad)
1643 /* we've gone to using the badDelay, the only thing that
1644 resets bad is when badDelay has expired. but numFailures
1645 is only just set below its threshold. the proc's
1646 start_times are all reset when the bad is. the numFailures
1647 is reset when we see an app run for a period */
1649 s->procs[i].start_time = 0;
1652 if (s->numFailures > MAX_FAILED_STARTS)
1654 time_t last_start_time = s->procs[i].start_time;
1656 if (last_start_time && now - last_start_time > RUNTIME_SUCCESS_INTERVAL)
1658 s->bad = 0;
1659 s->numFailures = 0;
1661 else
1663 unsigned int j;
1665 for (j = 0; j < numChildren; ++j)
1667 if (s->procs[j].pid <= 0) continue;
1668 if (s->procs[j].state != FCGI_RUNNING_STATE) continue;
1669 if (s->procs[j].start_time == 0) continue;
1670 if (now - s->procs[j].start_time > RUNTIME_SUCCESS_INTERVAL) break;
1673 if (j >= numChildren)
1675 s->bad = 1;
1677 else
1679 s->bad = 0;
1680 s->numFailures = 0;
1685 if (s->bad)
1687 restartTime += FAILED_STARTS_DELAY;
1689 else
1691 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1694 if (restartTime <= now)
1696 if (s->bad)
1698 s->bad = 0;
1699 s->numFailures = MAX_FAILED_STARTS;
1702 if (s->listenFd < 0 && init_listen_sock(s))
1704 if (sleepSeconds > s->initStartDelay)
1705 sleepSeconds = s->initStartDelay;
1706 break;
1708 #ifndef WIN32
1709 if (caughtSigTerm) {
1710 goto ProcessSigTerm;
1712 #endif
1713 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1714 if (s->procs[i].pid <= 0) {
1715 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1716 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1717 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1718 s->fs_path);
1720 sleepSeconds = min(sleepSeconds,
1721 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1723 ap_assert(s->procs[i].pid < 0);
1724 break;
1727 s->procs[i].start_time = now;
1728 s->restartTime = now;
1730 if (s->directive == APP_CLASS_DYNAMIC) {
1731 s->numProcesses++;
1732 fcgi_dynamic_total_proc_count++;
1733 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1736 s->procs[i].state = FCGI_RUNNING_STATE;
1738 if (fcgi_wrapper) {
1739 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1740 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1741 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1742 s->fs_path, (long) s->uid, (long) s->gid,
1743 restart ? "re" : "", (long) s->procs[i].pid);
1745 else {
1746 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1747 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1748 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1749 s->fs_path, restart ? "re" : "", (long) s->procs[i].pid);
1751 ap_assert(s->procs[i].pid > 0);
1752 } else {
1753 sleepSeconds = min(sleepSeconds, restartTime - now);
1759 #ifndef WIN32
1761 if(caughtSigTerm) {
1762 goto ProcessSigTerm;
1764 if((!caughtSigChld) && (!caughtSigAlarm)) {
1765 fd_set rfds;
1767 alarm(sleepSeconds);
1769 FD_ZERO(&rfds);
1770 FD_SET(fcgi_pm_pipe[0], &rfds);
1771 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1773 alarmLeft = alarm(0);
1775 callWaitPid = caughtSigChld;
1776 caughtSigChld = FALSE;
1777 callDynamicProcs = caughtSigAlarm;
1778 caughtSigAlarm = FALSE;
1780 now = time(NULL);
1783 * Dynamic fcgi process management
1785 if((callDynamicProcs) || (!callWaitPid)) {
1786 dynamic_read_msgs(read_ready);
1787 if(fcgi_dynamic_epoch == 0) {
1788 fcgi_dynamic_epoch = now;
1790 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1791 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1792 dynamic_kill_idle_fs_procs();
1793 fcgi_dynamic_epoch = now;
1797 if(!callWaitPid) {
1798 continue;
1801 /* We've caught SIGCHLD, so find out who it was using waitpid,
1802 * write a log message and update its data structure. */
1804 for (;;) {
1805 if (caughtSigTerm)
1806 goto ProcessSigTerm;
1808 childPid = waitpid(-1, &waitStatus, WNOHANG);
1810 if (childPid == -1 || childPid == 0)
1811 break;
1813 for (s = fcgi_servers; s != NULL; s = s->next) {
1814 if (s->directive == APP_CLASS_EXTERNAL)
1815 continue;
1817 if (s->directive == APP_CLASS_DYNAMIC)
1818 numChildren = dynamicMaxClassProcs;
1819 else
1820 numChildren = s->numProcesses;
1822 for (i = 0; i < numChildren; i++) {
1823 if (s->procs[i].pid == childPid)
1824 goto ChildFound;
1828 /* TODO: print something about this unknown child */
1829 continue;
1831 ChildFound:
1832 s->procs[i].pid = -1;
1834 if (s->directive == APP_CLASS_STANDARD) {
1835 /* Always restart static apps */
1836 s->procs[i].state = FCGI_START_STATE;
1837 s->numFailures++;
1839 else {
1840 s->numProcesses--;
1841 fcgi_dynamic_total_proc_count--;
1843 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1844 s->procs[i].state = FCGI_KILLED_STATE;
1846 else {
1847 /* A dynamic app died or exited without provocation from the PM */
1848 s->numFailures++;
1850 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1851 s->procs[i].state = FCGI_START_STATE;
1852 else
1853 s->procs[i].state = FCGI_READY_STATE;
1857 if (WIFEXITED(waitStatus)) {
1858 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1859 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1860 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1861 s->fs_path, (long) childPid, WEXITSTATUS(waitStatus));
1863 else if (WIFSIGNALED(waitStatus)) {
1864 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1865 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1866 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1867 s->fs_path, (long) childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)],
1868 #ifdef WCOREDUMP
1869 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1870 #else
1871 "");
1872 #endif
1874 else if (WIFSTOPPED(waitStatus)) {
1875 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1876 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1877 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1878 s->fs_path, (long) childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)]);
1880 } /* for (;;), waitpid() */
1882 #else /* WIN32 */
1884 /* wait for an event to occur or timer expires */
1885 expire = time(NULL) + sleepSeconds;
1886 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1888 if (dwRet == WAIT_FAILED) {
1889 /* There is something seriously wrong here */
1890 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1891 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1892 bTimeToDie = TRUE;
1895 if (dwRet != WAIT_TIMEOUT) {
1896 now = time(NULL);
1898 if (now < expire)
1899 alarmLeft = expire - now;
1903 * Dynamic fcgi process management
1905 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1906 if (dwRet == MBOX_EVENT) {
1907 read_ready = 1;
1910 now = time(NULL);
1912 dynamic_read_msgs(read_ready);
1914 if(fcgi_dynamic_epoch == 0) {
1915 fcgi_dynamic_epoch = now;
1918 if ((now-fcgi_dynamic_epoch >= (int) dynamicKillInterval) ||
1919 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1920 dynamic_kill_idle_fs_procs();
1921 fcgi_dynamic_epoch = now;
1923 read_ready = 0;
1925 else if (dwRet == WAKE_EVENT) {
1926 continue;
1928 else if (dwRet == TERM_EVENT) {
1929 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1930 "FastCGI: Termination event received process manager shutting down");
1932 bTimeToDie = TRUE;
1933 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
1935 goto ProcessSigTerm;
1937 else {
1938 // Have an received an unknown event - should not happen
1939 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1940 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1942 bTimeToDie = TRUE;
1943 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
1945 goto ProcessSigTerm;
1948 #endif /* WIN32 */
1950 } /* for (;;), the whole shoot'n match */
1952 ProcessSigTerm:
1954 * Kill off the children, then exit.
1956 shutdown_all();
1958 #ifdef WIN32
1959 return;
1960 #else
1961 exit(0);
1962 #endif
1965 #ifdef WIN32
1966 int fcgi_pm_add_job(fcgi_pm_job *new_job)
1968 int rv = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
1970 if (rv != WAIT_OBJECT_0 && rv != WAIT_ABANDONED)
1972 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1973 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
1974 return -1;
1977 new_job->next = fcgi_dynamic_mbox;
1978 fcgi_dynamic_mbox = new_job;
1980 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
1982 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1983 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
1986 return 0;
1988 #endif