allow AP2 to setup fcgi_user/group_id
[mod_fastcgi.git] / fcgi_pm.c
blob35bdcc76262c309cf470332108003a0de632dd9d
1 /*
2 * $Id: fcgi_pm.c,v 1.74 2002/07/26 03:10:54 robs Exp $
3 */
6 #include "fcgi.h"
8 #if defined(APACHE2) && !defined(WIN32)
9 #include <pwd.h>
10 #endif
12 #ifdef _HPUX_SOURCE
13 #include <unistd.h>
14 #define seteuid(arg) setresuid(-1, (arg), -1)
15 #endif
17 int fcgi_dynamic_total_proc_count = 0; /* number of running apps */
18 time_t fcgi_dynamic_epoch = 0; /* last time kill_procs was
19 * invoked by process mgr */
20 time_t fcgi_dynamic_last_analyzed = 0; /* last time calculation was
21 * made for the dynamic procs */
23 static time_t now = 0;
25 #ifdef WIN32
26 #pragma warning ( disable : 4100 4102 )
27 static BOOL bTimeToDie = FALSE; /* process termination flag */
28 HANDLE fcgi_event_handles[3];
29 #ifndef SIGKILL
30 #define SIGKILL 9
31 #endif
32 #endif
35 #if !defined(WIN32) && !defined(APACHE2)
36 static int seteuid_root(void)
38 int rc = seteuid((uid_t)0);
39 if (rc == -1) {
40 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
41 "FastCGI: seteuid(0) failed");
43 return rc;
46 static int seteuid_user(void)
48 int rc = seteuid(ap_user_id);
49 if (rc == -1) {
50 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
51 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id);
53 return rc;
55 #endif
58 * Signal the process to exit. How (or if) the process responds
59 * depends on the FastCGI application library (esp. on Win32) and
60 * possibly application code (signal handlers and whether or not
61 * SA_RESTART is on). At any rate, we send the signal with the
62 * hopes that the process will exit on its own. Later, as we
63 * review the state of application processes, if we see one marked
64 * for death, but that hasn't died within a specified period of
65 * time, fcgi_kill() is called again with a KILL)
67 static void fcgi_kill(ServerProcess *process, int sig)
69 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process->pid, sig);
71 process->state = FCGI_VICTIM_STATE;
73 #ifdef WIN32
74 if (sig == SIGTERM)
76 SetEvent(process->terminationEvent);
78 else if (sig == SIGKILL)
80 TerminateProcess(process->handle, 1);
82 else
84 ap_assert(0);
86 #else
87 if (fcgi_wrapper)
89 seteuid_root();
92 kill(process->pid, sig);
94 if (fcgi_wrapper)
96 seteuid_user();
98 #endif
101 /*******************************************************************************
102 * Send SIGTERM to each process in the server class, remove socket
103 * file if appropriate. Currently this is only called when the PM is shutting
104 * down and thus memory isn't freed and sockets and files aren't closed.
106 static void shutdown_all()
108 fcgi_server *s = fcgi_servers;
110 while (s)
112 ServerProcess *proc = s->procs;
113 int i;
114 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
115 ? dynamicMaxClassProcs
116 : s->numProcesses;
118 #ifndef WIN32
119 if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL)
121 /* Remove the socket file */
122 if (unlink(s->socket_path) != 0 && errno != ENOENT) {
123 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
124 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
125 s->socket_path,
126 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path);
129 #endif
131 /* Send TERM to all processes */
132 for (i = 0; i < numChildren; i++, proc++)
134 if (proc->state == FCGI_RUNNING_STATE)
136 fcgi_kill(proc, SIGTERM);
140 s = s->next;
143 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
146 * WIN32 applications may not have support for the shutdown event
147 * depending on their application library version
150 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT);
151 s = fcgi_servers;
153 while (s)
155 ServerProcess *proc = s->procs;
156 int i;
157 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
158 ? dynamicMaxClassProcs
159 : s->numProcesses;
161 /* Send KILL to all processes */
162 for (i = 0; i < numChildren; i++, proc++)
164 if (proc->state == FCGI_RUNNING_STATE)
166 fcgi_kill(proc, SIGKILL);
170 s = s->next;
173 #endif /* WIN32 */
176 static int init_listen_sock(fcgi_server * fs)
178 ap_assert(fs->directive != APP_CLASS_EXTERNAL);
180 /* Create the socket */
181 if ((fs->listenFd = socket(fs->socket_addr->sa_family, SOCK_STREAM, 0)) < 0)
183 #ifdef WIN32
184 errno = WSAGetLastError(); // Not sure if this will work as expected
185 #endif
186 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
187 "FastCGI: can't create %sserver \"%s\": socket() failed",
188 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
189 fs->fs_path);
190 return -1;
193 #ifndef WIN32
194 if (fs->socket_addr->sa_family == AF_UNIX)
196 /* Remove any existing socket file.. just in case */
197 unlink(((struct sockaddr_un *)fs->socket_addr)->sun_path);
199 else
200 #endif
202 int flag = 1;
203 setsockopt(fs->listenFd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
206 /* Bind it to the socket_addr */
207 if (bind(fs->listenFd, fs->socket_addr, fs->socket_addr_len))
209 char port[11];
211 #ifdef WIN32
212 errno = WSAGetLastError();
213 #endif
214 ap_snprintf(port, sizeof(port), "port=%d",
215 ((struct sockaddr_in *)fs->socket_addr)->sin_port);
217 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
218 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
219 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
220 fs->fs_path,
221 #ifndef WIN32
222 (fs->socket_addr->sa_family == AF_UNIX) ?
223 ((struct sockaddr_un *)fs->socket_addr)->sun_path :
224 #endif
225 port);
228 #ifndef WIN32
229 /* Twiddle Unix socket permissions */
230 else if (fs->socket_addr->sa_family == AF_UNIX
231 && chmod(((struct sockaddr_un *)fs->socket_addr)->sun_path, S_IRUSR | S_IWUSR))
233 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
234 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
235 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
236 fs->fs_path);
238 #endif
240 /* Set to listen */
241 else if (listen(fs->listenFd, fs->listenQueueDepth))
243 #ifdef WIN32
244 errno = WSAGetLastError();
245 #endif
246 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
247 "FastCGI: can't create %sserver \"%s\": listen() failed",
248 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
249 fs->fs_path);
251 else
253 return 0;
256 close(fs->listenFd);
257 fs->listenFd = -1;
258 return -2;
262 *----------------------------------------------------------------------
264 * pm_main
266 * The FastCGI process manager, which runs as a separate
267 * process responsible for:
268 * - Starting all the FastCGI proceses.
269 * - Restarting any of these processes that die (indicated
270 * by SIGCHLD).
271 * - Catching SIGTERM and relaying it to all the FastCGI
272 * processes before exiting.
274 * Inputs:
275 * Uses global variable fcgi_servers.
277 * Results:
278 * Does not return.
280 * Side effects:
281 * Described above.
283 *----------------------------------------------------------------------
285 #ifndef WIN32
286 static int caughtSigTerm = FALSE;
287 static int caughtSigChld = FALSE;
288 static int caughtSigAlarm = FALSE;
290 static void signal_handler(int signo)
292 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
293 /* SIGUSR1 & SIGHUP are sent by apache to its process group
294 * when apache get 'em. Apache follows up (1.2.x) with attacks
295 * on each of its child processes, but we've got the KillMgr
296 * sitting between us so we never see the KILL. The main loop
297 * in ProcMgr also checks to see if the KillMgr has terminated,
298 * and if it has, we handl it as if we should shutdown too. */
299 caughtSigTerm = TRUE;
300 } else if(signo == SIGCHLD) {
301 caughtSigChld = TRUE;
302 } else if(signo == SIGALRM) {
303 caughtSigAlarm = TRUE;
306 #endif
309 *----------------------------------------------------------------------
311 * spawn_fs_process --
313 * Fork and exec the specified fcgi process.
315 * Results:
316 * 0 for successful fork, -1 for failed fork.
318 * In case the child fails before or in the exec, the child
319 * obtains the error log by calling getErrLog, logs
320 * the error, and exits with exit status = errno of
321 * the failed system call.
323 * Side effects:
324 * Child process created.
326 *----------------------------------------------------------------------
328 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
330 #ifndef WIN32
332 pid_t child_pid;
333 int i;
334 char *dirName;
335 char *dnEnd, *failedSysCall;
337 child_pid = fork();
338 if (child_pid) {
339 return child_pid;
342 /* We're the child. We're gonna exec() so pools don't matter. */
344 dnEnd = strrchr(fs->fs_path, '/');
345 if (dnEnd == NULL) {
346 dirName = "./";
347 } else {
348 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
349 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
351 if (chdir(dirName) < 0) {
352 failedSysCall = "chdir()";
353 goto FailedSystemCallExit;
356 #ifndef __EMX__
357 /* OS/2 dosen't support nice() */
358 if (fs->processPriority != 0) {
359 if (nice(fs->processPriority) == -1) {
360 failedSysCall = "nice()";
361 goto FailedSystemCallExit;
364 #endif
366 /* Open the listenFd on spec'd fd */
367 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
368 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
370 /* Close all other open fds, except stdout/stderr. Leave these two open so
371 * FastCGI applications don't have to find and fix ALL 3rd party libs that
372 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
373 * main server error_log - @@@ provide a directive control where this goes.
375 ap_error_log2stderr(fcgi_apache_main_server);
376 dup2(2, 1);
377 for (i = 0; i < FCGI_MAX_FD; i++) {
378 if (i != FCGI_LISTENSOCK_FILENO && i != 2 && i != 1) {
379 close(i);
383 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
384 * install its own handler. */
385 signal(SIGPIPE, SIG_IGN);
387 if (fcgi_wrapper && (fcgi_user_id != fs->uid || fcgi_group_id != fs->gid)) {
388 char *shortName = strrchr(fs->fs_path, '/') + 1;
390 #ifndef APACHE2
391 /* Relinquish our root real uid powers */
392 seteuid_root();
393 setuid(ap_user_id);
394 #endif
396 do {
397 execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group, shortName, NULL, fs->envp);
398 } while (errno == EINTR);
400 else {
401 do {
402 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
403 } while (errno == EINTR);
406 failedSysCall = "execle()";
408 FailedSystemCallExit:
409 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
410 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
411 exit(-1);
413 /* avoid an irrelevant compiler warning */
414 return(0);
416 #else /* WIN32 */
418 #ifdef APACHE2
420 /* based on mod_cgi.c:run_cgi_child() */
422 apr_pool_t * tp;
423 char * termination_env_string;
424 HANDLE listen_handle = INVALID_HANDLE_VALUE;
425 apr_procattr_t * procattr;
426 apr_proc_t proc = { 0 };
427 apr_file_t * file;
428 int i = 0;
430 if (apr_pool_create(&tp, fcgi_config_pool))
431 return 0;
433 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
434 if (process->terminationEvent == NULL)
435 goto CLEANUP;
437 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
439 termination_env_string = ap_psprintf(tp,
440 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
442 while (fs->envp[i]) i++;
443 fs->envp[i++] = termination_env_string;
444 fs->envp[i] = (char *) fs->mutex_env_string;
446 ap_assert(fs->envp[i + 1] == NULL);
448 if (fs->socket_path)
450 SECURITY_ATTRIBUTES sa = { 0 };
452 sa.bInheritHandle = TRUE;
453 sa.nLength = sizeof(sa);
455 listen_handle = CreateNamedPipe(fs->socket_path,
456 PIPE_ACCESS_DUPLEX,
457 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
458 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
460 if (listen_handle == INVALID_HANDLE_VALUE)
462 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
463 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
464 fs->fs_path);
465 goto CLEANUP;
468 else
470 listen_handle = (HANDLE) fs->listenFd;
473 if (apr_procattr_create(&procattr, tp))
474 goto CLEANUP;
476 if (apr_procattr_dir_set(procattr, ap_make_dirstr_parent(tp, fs->fs_path)))
477 goto CLEANUP;
479 if (apr_procattr_detach_set(procattr, 1))
480 goto CLEANUP;
482 if (apr_os_file_put(&file, &listen_handle, 0, tp))
483 goto CLEANUP;
485 /* procattr is opaque so we have to use this - unfortuantely it dups */
486 if (apr_procattr_child_in_set(procattr, file, NULL))
487 goto CLEANUP;
489 if (apr_proc_create(&proc, fs->fs_path, NULL, fs->envp, procattr, tp))
490 goto CLEANUP;
492 process->handle = proc.hproc;
495 CLEANUP:
497 if (i)
499 fs->envp[i - 1] = NULL;
502 ap_destroy_pool(tp);
504 return proc.pid;
506 #else /* WIN32 && !APACHE2 */
508 /* Adapted from Apache's util_script.c ap_call_exec() */
509 char *interpreter = NULL;
510 char *quoted_filename;
511 char *pCommand;
512 char *pEnvBlock, *pNext;
514 int i = 0;
515 int iEnvBlockLen = 1;
517 file_type_e fileType;
519 STARTUPINFO si;
520 PROCESS_INFORMATION pi;
522 request_rec r;
523 pid_t pid = -1;
525 pool * tp = ap_make_sub_pool(fcgi_config_pool);
527 HANDLE listen_handle;
528 char * termination_env_string = NULL;
530 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
531 if (process->terminationEvent == NULL)
533 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
534 "FastCGI: can't create termination event for server \"%s\", "
535 "CreateEvent() failed", fs->fs_path);
536 goto CLEANUP;
538 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
540 termination_env_string = ap_psprintf(tp,
541 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
543 if (fs->socket_path)
545 SECURITY_ATTRIBUTES sa;
547 sa.lpSecurityDescriptor = NULL;
548 sa.bInheritHandle = TRUE;
549 sa.nLength = sizeof(sa);
551 listen_handle = CreateNamedPipe(fs->socket_path,
552 PIPE_ACCESS_DUPLEX,
553 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
554 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
556 if (listen_handle == INVALID_HANDLE_VALUE)
558 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
559 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
560 goto CLEANUP;
563 else
565 listen_handle = (HANDLE) fs->listenFd;
568 memset(&si, 0, sizeof(si));
569 memset(&pi, 0, sizeof(pi));
570 memset(&r, 0, sizeof(r));
572 // Can up a fake request to pass to ap_get_win32_interpreter()
573 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
574 r.server = fcgi_apache_main_server;
575 r.filename = (char *) fs->fs_path;
576 r.pool = tp;
578 fileType = ap_get_win32_interpreter(&r, &interpreter);
580 if (fileType == eFileTypeUNKNOWN) {
581 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
582 "FastCGI: %s is not executable; ensure interpreted scripts have "
583 "\"#!\" as their first line",
584 fs->fs_path);
585 ap_destroy_pool(tp);
586 goto CLEANUP;
590 * We have the interpreter (if there is one) and we have
591 * the arguments (if there are any).
592 * Build the command string to pass to CreateProcess.
594 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
595 if (interpreter && *interpreter) {
596 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
598 else {
599 pCommand = quoted_filename;
603 * Make child process use hPipeOutputWrite as standard out,
604 * and make sure it does not show on screen.
606 si.cb = sizeof(si);
607 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
608 si.wShowWindow = SW_HIDE;
609 si.hStdInput = listen_handle;
611 // XXX These should be open to the error_log
612 si.hStdOutput = INVALID_HANDLE_VALUE;
613 si.hStdError = INVALID_HANDLE_VALUE;
616 * Win32's CreateProcess call requires that the environment
617 * be passed in an environment block, a null terminated block of
618 * null terminated strings.
619 * @todo we should store the env in this format for win32.
621 while (fs->envp[i])
623 iEnvBlockLen += strlen(fs->envp[i]) + 1;
624 i++;
627 iEnvBlockLen += strlen(termination_env_string) + 1;
628 iEnvBlockLen += strlen(fs->mutex_env_string) + 1;
630 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
632 i = 0;
633 pNext = pEnvBlock;
634 while (fs->envp[i])
636 strcpy(pNext, fs->envp[i]);
637 pNext += strlen(pNext) + 1;
638 i++;
641 strcpy(pNext, termination_env_string);
642 pNext += strlen(pNext) + 1;
643 strcpy(pNext, fs->mutex_env_string);
645 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
647 pEnvBlock,
648 ap_make_dirstr_parent(tp, fs->fs_path),
649 &si, &pi))
651 /* Hack to get 16-bit CGI's working. It works for all the
652 * standard modules shipped with Apache. pi.dwProcessId is 0
653 * for 16-bit CGIs and all the Unix specific code that calls
654 * ap_call_exec interprets this as a failure case. And we can't
655 * use -1 either because it is mapped to 0 by the caller.
657 pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId;
659 process->handle = pi.hProcess;
660 CloseHandle(pi.hThread);
663 if (fs->socket_path)
665 CloseHandle(listen_handle);
668 CLEANUP:
670 ap_destroy_pool(tp);
672 return pid;
674 #endif /* !APACHE2 */
675 #endif /* WIN32 */
678 #if !defined(WIN32) && !defined(APACHE2)
679 static void reduce_privileges(void)
681 char *name;
683 if (geteuid() != 0)
684 return;
686 #ifndef __EMX__
687 /* Get username if passed as a uid */
688 if (ap_user_name[0] == '#') {
689 uid_t uid = atoi(&ap_user_name[1]);
690 struct passwd *ent = getpwuid(uid);
692 if (ent == NULL) {
693 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
694 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
695 "you probably need to modify the User directive", (unsigned)uid);
696 exit(1);
698 name = ent->pw_name;
700 else
701 name = ap_user_name;
703 /* Change Group */
704 if (setgid(ap_group_id) == -1) {
705 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
706 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
707 exit(1);
710 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
712 /* Initialize supplementary groups */
713 if (initgroups(name, ap_group_id) == -1) {
714 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
715 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
716 name, (unsigned)ap_group_id);
717 exit(1);
719 #endif /* __EMX__ */
721 /* Change User */
722 if (fcgi_wrapper) {
723 if (seteuid_user() == -1) {
724 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
725 "FastCGI: process manager exiting, failed to reduce privileges");
726 exit(1);
729 else {
730 if (setuid(ap_user_id) == -1) {
731 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
732 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
733 exit(1);
738 /*************
739 * Change the name of this process - best we can easily.
741 static void change_process_name(const char * const name)
743 strncpy(ap_server_argv0, name, strlen(ap_server_argv0));
745 #endif
747 static void schedule_start(fcgi_server *s, int proc)
749 /* If we've started one recently, don't register another */
750 time_t time_passed = now - s->restartTime;
752 if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay))
753 || ((s->procs[proc].pid == 0) && (time_passed < s->initStartDelay)))
755 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);
756 return;
759 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
760 s->procs[proc].state = FCGI_START_STATE;
761 if (proc == dynamicMaxClassProcs - 1) {
762 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
763 "FastCGI: scheduled the %sstart of the last (dynamic) server "
764 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
765 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
770 *----------------------------------------------------------------------
772 * dynamic_read_msgs
774 * Removes the records written by request handlers and decodes them.
775 * We also update the data structures to reflect the changes.
777 *----------------------------------------------------------------------
780 static void dynamic_read_msgs(int read_ready)
782 fcgi_server *s;
783 int rc;
785 #ifndef WIN32
786 static int buflen = 0;
787 static char buf[FCGI_MSGS_BUFSIZE + 1];
788 char *ptr1, *ptr2, opcode;
789 char execName[FCGI_MAXPATH + 1];
790 char user[MAX_USER_NAME_LEN + 2];
791 char group[MAX_GID_CHAR_LEN + 1];
792 unsigned long q_usec = 0UL, req_usec = 0UL;
793 #else
794 fcgi_pm_job *joblist = NULL;
795 fcgi_pm_job *cjob = NULL;
796 #endif
798 pool *sp = NULL, *tp;
800 #ifndef WIN32
801 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
802 #endif
805 * To prevent the idle application from running indefinitely, we
806 * check the timer and if it is expired, we recompute the values
807 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
808 * message is received, only updates are made to the data structures.
810 if (fcgi_dynamic_last_analyzed == 0) {
811 fcgi_dynamic_last_analyzed = now;
813 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
814 for (s = fcgi_servers; s != NULL; s = s->next) {
815 if (s->directive != APP_CLASS_DYNAMIC)
816 break;
818 /* Advance the last analyzed timestamp by the elapsed time since
819 * it was last set. Round the increase down to the nearest
820 * multiple of dynamicUpdateInterval */
822 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
823 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
824 s->totalConnTime = 0UL;
825 s->totalQueueTime = 0UL;
829 if (read_ready <= 0) {
830 return;
833 #ifndef WIN32
834 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
835 if (rc <= 0) {
836 if (!caughtSigTerm) {
837 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
838 "FastCGI: read() from pipe failed (%d)", rc);
839 if (rc == 0) {
840 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
841 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
842 caughtSigTerm = TRUE;
845 return;
847 buflen += rc;
848 buf[buflen] = '\0';
850 #else
852 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
853 * request to do something) and/or when a timeout expires.
854 * There really should be no reason why this wait would get stuck
855 * but there's no point in waiting forever. */
857 rc = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
859 if (rc != WAIT_OBJECT_0 && rc != WAIT_ABANDONED)
861 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
862 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
863 return;
866 joblist = fcgi_dynamic_mbox;
867 fcgi_dynamic_mbox = NULL;
869 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
871 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
872 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
875 cjob = joblist;
876 #endif
878 #ifdef APACHE2
879 apr_pool_create(&tp, fcgi_config_pool);
880 #else
881 tp = ap_make_sub_pool(fcgi_config_pool);
882 #endif
884 #ifndef WIN32
885 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
886 int scan_failed = 0;
888 ptr2 = strchr(ptr1, '*');
889 if (ptr2) {
890 *ptr2++ = '\0';
892 else {
893 break;
896 opcode = *ptr1;
898 switch (opcode)
900 case FCGI_SERVER_START_JOB:
901 case FCGI_SERVER_RESTART_JOB:
903 if (sscanf(ptr1, "%c %s %16s %15s",
904 &opcode, execName, user, group) != 4)
906 scan_failed = 1;
908 break;
910 case FCGI_REQUEST_TIMEOUT_JOB:
912 if (sscanf(ptr1, "%c %s %16s %15s",
913 &opcode, execName, user, group) != 4)
915 scan_failed = 1;
917 break;
919 case FCGI_REQUEST_COMPLETE_JOB:
921 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
922 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
924 scan_failed = 1;
926 break;
928 default:
930 scan_failed = 1;
931 break;
934 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode, execName, user, group, q_usec, req_usec);
936 if (scan_failed) {
937 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
938 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
939 goto NextJob;
941 #else
942 /* Update data structures for processing */
943 while (cjob != NULL) {
944 joblist = cjob->next;
945 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
946 #endif
948 #ifndef WIN32
949 s = fcgi_util_fs_get(execName, user, group);
950 #else
951 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
952 #endif
954 #ifndef WIN32
955 if (s==NULL && opcode != FCGI_REQUEST_COMPLETE_JOB)
956 #else
957 if (s==NULL && cjob->id != FCGI_REQUEST_COMPLETE_JOB)
958 #endif
960 #ifdef WIN32
962 HANDLE mutex = CreateMutex(NULL, FALSE, cjob->fs_path);
964 if (mutex == NULL)
966 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
967 "FastCGI: can't create accept mutex "
968 "for (dynamic) server \"%s\"", cjob->fs_path);
969 goto BagNewServer;
972 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
973 #else
974 const char *err;
975 #endif
977 /* Create a perm subpool to hold the new server data,
978 * we can destroy it if something doesn't pan out */
979 #ifdef APACHE2
980 apr_pool_create(&sp, fcgi_config_pool);
981 #else
982 sp = ap_make_sub_pool(fcgi_config_pool);
983 #endif
985 /* Create a new "dynamic" server */
986 s = fcgi_util_fs_new(sp);
988 s->directive = APP_CLASS_DYNAMIC;
989 s->restartDelay = dynamicRestartDelay;
990 s->listenQueueDepth = dynamicListenQueueDepth;
991 s->initStartDelay = dynamicInitStartDelay;
992 s->envp = dynamicEnvp;
993 s->flush = dynamicFlush;
995 #ifdef WIN32
996 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
997 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
998 #else
999 s->fs_path = ap_pstrdup(sp, execName);
1000 #endif
1001 ap_getparents(s->fs_path);
1002 ap_no2slash(s->fs_path);
1003 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
1005 /* XXX the socket_path (both Unix and Win) *is* deducible and
1006 * thus can and will be used by other apache instances without
1007 * the use of shared data regarding the processes serving the
1008 * requests. This can result in slightly unintuitive process
1009 * counts and security implications. This is prevented
1010 * if suexec (Unix) is in use. This is both a feature and a flaw.
1011 * Changing it now would break existing installations. */
1013 #ifndef WIN32
1014 /* Create socket file's path */
1015 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
1016 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1018 /* Create sockaddr, prealloc it so it won't get created in tp */
1019 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
1020 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
1021 &s->socket_addr_len, s->socket_path);
1022 if (err) {
1023 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1024 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
1025 goto BagNewServer;
1028 if (init_listen_sock(s)) {
1029 goto BagNewServer;
1032 /* If a wrapper is being used, config user/group info */
1033 if (fcgi_wrapper) {
1034 if (user[0] == '~') {
1035 /* its a user dir uri, the rest is a username, not a uid */
1036 struct passwd *pw = getpwnam(&user[1]);
1038 if (!pw) {
1039 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1040 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1041 execName, &user[1]);
1042 goto BagNewServer;
1044 s->uid = pw->pw_uid;
1045 s->user = ap_pstrdup(sp, user);
1046 s->username = s->user;
1048 s->gid = pw->pw_gid;
1049 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
1051 else {
1052 struct passwd *pw;
1054 s->uid = (uid_t)atol(user);
1055 pw = getpwuid(s->uid);
1056 if (!pw) {
1057 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1058 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1059 execName, (long)s->uid);
1060 goto BagNewServer;
1062 s->user = ap_pstrdup(sp, user);
1063 s->username = ap_pstrdup(sp, pw->pw_name);
1065 s->gid = (gid_t)atol(group);
1066 s->group = ap_pstrdup(sp, group);
1069 #else
1070 /* Create socket file's path */
1071 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
1072 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1073 s->listenFd = 0;
1074 #endif
1076 fcgi_util_fs_add(s);
1078 else {
1079 #ifndef WIN32
1080 if (opcode == FCGI_SERVER_RESTART_JOB) {
1081 #else
1082 if (cjob->id==FCGI_SERVER_RESTART_JOB) {
1083 #endif
1084 /* Check to see if the binary has changed. If so,
1085 * kill the FCGI application processes, and
1086 * restart them.
1088 struct stat stbuf;
1089 int i;
1090 #ifdef WIN32
1091 char * app_path = cjob->fs_path;
1092 #else
1093 char * app_path = execName;
1094 #endif
1096 if (stat(app_path, &stbuf) == 0 && stbuf.st_mtime > s->startTime)
1098 int do_restart = 0;
1100 /* prevent addition restart requests */
1101 s->startTime = now;
1102 #ifndef WIN32
1103 utime(s->socket_path, NULL);
1104 #endif
1106 /* kill old server(s) */
1107 for (i = 0; i < dynamicMaxClassProcs; i++)
1109 if (s->procs[i].pid > 0
1110 && stbuf.st_mtime > s->procs[i].start_time)
1112 fcgi_kill(&s->procs[i], SIGTERM);
1113 do_restart++;
1117 if (do_restart)
1119 ap_log_error(FCGI_LOG_WARN_NOERRNO,
1120 fcgi_apache_main_server, "FastCGI: restarting "
1121 "old server \"%s\" processes, newer version "
1122 "found", app_path);
1126 /* If dynamicAutoRestart, don't mark any new processes
1127 * for starting because we probably got the
1128 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1129 * will be restarting all of those we just killed.
1131 if (dynamicAutoRestart)
1132 goto NextJob;
1134 #ifndef WIN32
1135 else if (opcode == FCGI_SERVER_START_JOB) {
1136 #else
1137 else if (cjob->id==FCGI_SERVER_START_JOB) {
1138 #endif
1139 /* we've been asked to start a process--only start
1140 * it if we're not already running at least one
1141 * instance.
1143 int i;
1145 for (i = 0; i < dynamicMaxClassProcs; i++) {
1146 if (s->procs[i].state == FCGI_RUNNING_STATE)
1147 break;
1149 /* if already running, don't start another one */
1150 if (i < dynamicMaxClassProcs) {
1151 goto NextJob;
1156 #ifndef WIN32
1157 switch (opcode)
1158 #else
1159 switch (cjob->id)
1160 #endif
1162 int i, start;
1164 case FCGI_SERVER_RESTART_JOB:
1166 start = FALSE;
1168 /* We just waxed 'em all. Try to find an idle slot. */
1170 for (i = 0; i < dynamicMaxClassProcs; ++i)
1172 if (s->procs[i].state == FCGI_START_STATE
1173 || s->procs[i].state == FCGI_RUNNING_STATE)
1175 break;
1177 else if (s->procs[i].state == FCGI_KILLED_STATE
1178 || s->procs[i].state == FCGI_READY_STATE)
1180 start = TRUE;
1181 break;
1185 /* Nope, just use the first slot */
1186 if (i == dynamicMaxClassProcs)
1188 start = TRUE;
1189 i = 0;
1192 if (start)
1194 schedule_start(s, i);
1197 break;
1199 case FCGI_SERVER_START_JOB:
1200 case FCGI_REQUEST_TIMEOUT_JOB:
1202 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1204 * Extra instances should have been
1205 * terminated beforehand, probably need
1206 * to increase ProcessSlack parameter
1208 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1209 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1210 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1211 goto NextJob;
1214 /* find next free slot */
1215 for (i = 0; i < dynamicMaxClassProcs; i++)
1217 if (s->procs[i].state == FCGI_START_STATE)
1219 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1220 break;
1222 else if (s->procs[i].state == FCGI_RUNNING_STATE)
1224 continue;
1227 schedule_start(s, i);
1228 break;
1231 #ifdef FCGI_DEBUG
1232 if (i >= dynamicMaxClassProcs) {
1233 FCGIDBG1("ignore_job: slots are max'd");
1235 #endif
1236 break;
1237 case FCGI_REQUEST_COMPLETE_JOB:
1238 /* only record stats if we have a structure */
1239 if (s) {
1240 #ifndef WIN32
1241 s->totalConnTime += req_usec;
1242 s->totalQueueTime += q_usec;
1243 #else
1244 s->totalConnTime += cjob->start_time;
1245 s->totalQueueTime += cjob->qsec;
1246 #endif
1248 break;
1251 NextJob:
1253 #ifdef WIN32
1254 /* Cleanup job data */
1255 free(cjob->fs_path);
1256 free(cjob->user);
1257 free(cjob->group);
1258 free(cjob);
1259 cjob = joblist;
1260 #endif
1262 continue;
1264 BagNewServer:
1265 if (sp) ap_destroy_pool(sp);
1267 #ifdef WIN32
1268 free(cjob->fs_path);
1269 free(cjob);
1270 cjob = joblist;
1271 #endif
1274 #ifndef WIN32
1275 if (ptr1 == buf) {
1276 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1277 "FastCGI: really bogus message: \"%s\"", ptr1);
1278 ptr1 += strlen(buf);
1281 buflen -= ptr1 - buf;
1282 if (buflen) {
1283 memmove(buf, ptr1, buflen);
1285 #endif
1287 ap_destroy_pool(tp);
1291 *----------------------------------------------------------------------
1293 * dynamic_kill_idle_fs_procs
1295 * Implement a kill policy for the dynamic FastCGI applications.
1296 * We also update the data structures to reflect the changes.
1298 * Side effects:
1299 * Processes are marked for deletion possibly killed.
1301 *----------------------------------------------------------------------
1303 static void dynamic_kill_idle_fs_procs(void)
1305 fcgi_server *s;
1306 int victims = 0;
1308 for (s = fcgi_servers; s != NULL; s = s->next)
1311 * server's smoothed running time, or if that's 0, the current total
1313 unsigned long connTime;
1316 * maximum number of microseconds that all of a server's running
1317 * processes together could have spent running since the last check
1319 unsigned long totalTime;
1322 * percentage, 0-100, of totalTime that the processes actually used
1324 int loadFactor;
1326 int i;
1327 int really_running = 0;
1329 if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1331 continue;
1334 /* s->numProcesses includes pending kills so get the "active" count */
1335 for (i = 0; i < dynamicMaxClassProcs; ++i)
1337 if (s->procs[i].state == FCGI_RUNNING_STATE) ++really_running;
1340 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1341 totalTime = really_running * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1343 loadFactor = 100 * connTime / totalTime;
1345 if (really_running == 1)
1347 if (loadFactor >= dynamicThreshold1)
1349 continue;
1352 else
1354 int load = really_running / ( really_running - 1) * loadFactor;
1356 if (load >= dynamicThresholdN)
1358 continue;
1363 * Run through the procs to see if we can get away w/o waxing one.
1365 for (i = 0; i < dynamicMaxClassProcs; ++i)
1367 if (s->procs[i].state == FCGI_START_STATE)
1369 s->procs[i].state = FCGI_READY_STATE;
1370 break;
1372 else if (s->procs[i].state == FCGI_VICTIM_STATE)
1374 break;
1378 if (i >= dynamicMaxClassProcs)
1380 ServerProcess * procs = s->procs;
1381 int youngest = -1;
1383 for (i = 0; i < dynamicMaxClassProcs; ++i)
1385 if (procs[i].state == FCGI_RUNNING_STATE)
1387 if (youngest == -1 || procs[i].start_time >= procs[youngest].start_time)
1389 youngest = i;
1394 if (youngest != -1)
1396 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1397 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1398 s->fs_path, (long) s->procs[youngest].pid);
1400 fcgi_kill(&s->procs[youngest], SIGTERM);
1402 victims++;
1406 * If the number of non-victims is less than or equal to
1407 * the minimum that may be running without being killed off,
1408 * don't select any more victims.
1410 if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1412 break;
1418 #ifdef WIN32
1420 // This is a little bogus, there's gotta be a better way to do this
1421 // Can we use WaitForMultipleObjects()
1422 #define FCGI_PROC_WAIT_TIME 100
1424 void child_wait_thread_main(void *dummy) {
1425 fcgi_server *s;
1426 DWORD dwRet = WAIT_TIMEOUT;
1427 int numChildren;
1428 int i;
1429 int waited;
1431 while (!bTimeToDie) {
1432 waited = 0;
1434 for (s = fcgi_servers; s != NULL; s = s->next) {
1435 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1436 continue;
1438 if (s->directive == APP_CLASS_DYNAMIC) {
1439 numChildren = dynamicMaxClassProcs;
1441 else {
1442 numChildren = s->numProcesses;
1445 for (i=0; i < numChildren; i++) {
1446 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1448 DWORD exitStatus = 0;
1450 /* timeout is currently set for 100 miliecond */
1451 /* it may need to be longer or user customizable */
1452 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1454 waited = 1;
1456 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1457 /* a child fs has died */
1458 /* mark the child as dead */
1460 if (s->directive == APP_CLASS_STANDARD) {
1461 /* restart static app */
1462 s->procs[i].state = FCGI_START_STATE;
1463 s->numFailures++;
1465 else {
1466 s->numProcesses--;
1467 fcgi_dynamic_total_proc_count--;
1468 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1470 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1471 s->procs[i].state = FCGI_KILLED_STATE;
1473 else {
1474 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1475 s->numFailures++;
1477 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1478 s->procs[i].state = FCGI_START_STATE;
1480 else {
1481 s->procs[i].state = FCGI_READY_STATE;
1486 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1488 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1489 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1490 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1491 s->fs_path, (long) s->procs[i].pid, exitStatus);
1493 CloseHandle(s->procs[i].handle);
1494 s->procs[i].handle = INVALID_HANDLE_VALUE;
1495 s->procs[i].pid = -1;
1497 /* wake up the main thread */
1498 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1503 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1506 #endif
1508 #ifndef WIN32
1509 static void setup_signals(void)
1511 struct sigaction sa;
1513 /* Setup handlers */
1515 sa.sa_handler = signal_handler;
1516 sigemptyset(&sa.sa_mask);
1517 sa.sa_flags = 0;
1519 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1520 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1521 "sigaction(SIGTERM) failed");
1523 /* httpd restart */
1524 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1525 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1526 "sigaction(SIGHUP) failed");
1528 /* httpd graceful restart */
1529 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1530 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1531 "sigaction(SIGUSR1) failed");
1533 /* read messages from request handlers - kill interval expired */
1534 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1535 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1536 "sigaction(SIGALRM) failed");
1538 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1539 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1540 "sigaction(SIGCHLD) failed");
1543 #endif
1545 #if !defined(WIN32) && !defined(APACHE2)
1546 int fcgi_pm_main(void *dummy, child_info *info)
1547 #else
1548 void fcgi_pm_main(void *dummy)
1549 #endif
1551 fcgi_server *s;
1552 unsigned int i;
1553 int read_ready = 0;
1554 int alarmLeft = 0;
1556 #ifdef WIN32
1557 DWORD dwRet;
1558 HANDLE child_wait_thread = INVALID_HANDLE_VALUE;
1559 #else
1560 int callWaitPid, callDynamicProcs;
1561 #endif
1563 #ifdef WIN32
1564 // Add SystemRoot to the dynamic environment
1565 char ** envp = dynamicEnvp;
1566 for (i = 0; *envp; ++i) {
1567 ++envp;
1569 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1571 #else
1572 reduce_privileges();
1574 close(fcgi_pm_pipe[1]);
1575 change_process_name("fcgi-pm");
1576 setup_signals();
1578 if (fcgi_wrapper) {
1579 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1580 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1582 #endif
1584 /* Initialize AppClass */
1585 for (s = fcgi_servers; s != NULL; s = s->next)
1587 if (s->directive != APP_CLASS_STANDARD)
1588 continue;
1590 #ifdef WIN32
1591 if (s->socket_path)
1592 s->listenFd = 0;
1593 #endif
1595 for (i = 0; i < s->numProcesses; ++i)
1596 s->procs[i].state = FCGI_START_STATE;
1599 #ifdef WIN32
1600 child_wait_thread = (HANDLE) _beginthread(child_wait_thread_main, 0, NULL);
1602 if (child_wait_thread == (HANDLE) -1)
1604 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1605 "FastCGI: failed to create process manager's wait thread!");
1608 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1609 "FastCGI: process manager initialized");
1610 #else
1611 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1612 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1613 #endif
1615 now = time(NULL);
1618 * Loop until SIGTERM
1620 for (;;) {
1621 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1622 #ifdef WIN32
1623 time_t expire;
1624 #else
1625 pid_t childPid;
1626 int waitStatus;
1627 #endif
1628 unsigned int numChildren;
1631 * If we came out of sigsuspend() for any reason other than
1632 * SIGALRM, pick up where we left off.
1634 if (alarmLeft)
1635 sleepSeconds = alarmLeft;
1638 * Examine each configured AppClass for a process that needs
1639 * starting. Compute the earliest time when the start should
1640 * be attempted, starting it now if the time has passed. Also,
1641 * remember that we do NOT need to restart externally managed
1642 * FastCGI applications.
1644 for (s = fcgi_servers; s != NULL; s = s->next)
1646 if (s->directive == APP_CLASS_EXTERNAL)
1647 continue;
1649 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1650 ? dynamicMaxClassProcs
1651 : s->numProcesses;
1653 for (i = 0; i < numChildren; ++i)
1655 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1657 int restart = (s->procs[i].pid < 0);
1658 time_t restartTime = s->restartTime;
1660 if (s->bad)
1662 /* we've gone to using the badDelay, the only thing that
1663 resets bad is when badDelay has expired. but numFailures
1664 is only just set below its threshold. the proc's
1665 start_times are all reset when the bad is. the numFailures
1666 is reset when we see an app run for a period */
1668 s->procs[i].start_time = 0;
1671 if (s->numFailures > MAX_FAILED_STARTS)
1673 time_t last_start_time = s->procs[i].start_time;
1675 if (last_start_time && now - last_start_time > RUNTIME_SUCCESS_INTERVAL)
1677 s->bad = 0;
1678 s->numFailures = 0;
1680 else
1682 unsigned int j;
1684 for (j = 0; j < numChildren; ++j)
1686 if (s->procs[j].pid <= 0) continue;
1687 if (s->procs[j].state != FCGI_RUNNING_STATE) continue;
1688 if (s->procs[j].start_time == 0) continue;
1689 if (now - s->procs[j].start_time > RUNTIME_SUCCESS_INTERVAL) break;
1692 if (j >= numChildren)
1694 s->bad = 1;
1696 else
1698 s->bad = 0;
1699 s->numFailures = 0;
1704 if (s->bad)
1706 restartTime += FAILED_STARTS_DELAY;
1708 else
1710 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1713 if (restartTime <= now)
1715 if (s->bad)
1717 s->bad = 0;
1718 s->numFailures = MAX_FAILED_STARTS;
1721 if (s->listenFd < 0 && init_listen_sock(s))
1723 if (sleepSeconds > s->initStartDelay)
1724 sleepSeconds = s->initStartDelay;
1725 break;
1727 #ifndef WIN32
1728 if (caughtSigTerm) {
1729 goto ProcessSigTerm;
1731 #endif
1732 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1733 if (s->procs[i].pid <= 0) {
1734 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1735 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1736 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1737 s->fs_path);
1739 sleepSeconds = min(sleepSeconds,
1740 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1742 ap_assert(s->procs[i].pid < 0);
1743 break;
1746 s->procs[i].start_time = now;
1747 s->restartTime = now;
1749 if (s->startTime == 0) {
1750 s->startTime = now;
1753 if (s->directive == APP_CLASS_DYNAMIC) {
1754 s->numProcesses++;
1755 fcgi_dynamic_total_proc_count++;
1756 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1759 s->procs[i].state = FCGI_RUNNING_STATE;
1761 if (fcgi_wrapper) {
1762 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1763 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1764 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1765 s->fs_path, (long) s->uid, (long) s->gid,
1766 restart ? "re" : "", (long) s->procs[i].pid);
1768 else {
1769 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1770 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1771 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1772 s->fs_path, restart ? "re" : "", (long) s->procs[i].pid);
1774 ap_assert(s->procs[i].pid > 0);
1775 } else {
1776 sleepSeconds = min(sleepSeconds, restartTime - now);
1782 #ifndef WIN32
1784 if(caughtSigTerm) {
1785 goto ProcessSigTerm;
1787 if((!caughtSigChld) && (!caughtSigAlarm)) {
1788 fd_set rfds;
1790 alarm(sleepSeconds);
1792 FD_ZERO(&rfds);
1793 FD_SET(fcgi_pm_pipe[0], &rfds);
1794 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1796 alarmLeft = alarm(0);
1798 callWaitPid = caughtSigChld;
1799 caughtSigChld = FALSE;
1800 callDynamicProcs = caughtSigAlarm;
1801 caughtSigAlarm = FALSE;
1803 now = time(NULL);
1806 * Dynamic fcgi process management
1808 if((callDynamicProcs) || (!callWaitPid)) {
1809 dynamic_read_msgs(read_ready);
1810 if(fcgi_dynamic_epoch == 0) {
1811 fcgi_dynamic_epoch = now;
1813 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1814 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1815 dynamic_kill_idle_fs_procs();
1816 fcgi_dynamic_epoch = now;
1820 if(!callWaitPid) {
1821 continue;
1824 /* We've caught SIGCHLD, so find out who it was using waitpid,
1825 * write a log message and update its data structure. */
1827 for (;;) {
1828 if (caughtSigTerm)
1829 goto ProcessSigTerm;
1831 childPid = waitpid(-1, &waitStatus, WNOHANG);
1833 if (childPid == -1 || childPid == 0)
1834 break;
1836 for (s = fcgi_servers; s != NULL; s = s->next) {
1837 if (s->directive == APP_CLASS_EXTERNAL)
1838 continue;
1840 if (s->directive == APP_CLASS_DYNAMIC)
1841 numChildren = dynamicMaxClassProcs;
1842 else
1843 numChildren = s->numProcesses;
1845 for (i = 0; i < numChildren; i++) {
1846 if (s->procs[i].pid == childPid)
1847 goto ChildFound;
1851 /* TODO: print something about this unknown child */
1852 continue;
1854 ChildFound:
1855 s->procs[i].pid = -1;
1857 if (s->directive == APP_CLASS_STANDARD) {
1858 /* Always restart static apps */
1859 s->procs[i].state = FCGI_START_STATE;
1860 s->numFailures++;
1862 else {
1863 s->numProcesses--;
1864 fcgi_dynamic_total_proc_count--;
1866 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1867 s->procs[i].state = FCGI_KILLED_STATE;
1869 else {
1870 /* A dynamic app died or exited without provocation from the PM */
1871 s->numFailures++;
1873 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1874 s->procs[i].state = FCGI_START_STATE;
1875 else
1876 s->procs[i].state = FCGI_READY_STATE;
1880 if (WIFEXITED(waitStatus)) {
1881 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1882 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1883 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1884 s->fs_path, (long) childPid, WEXITSTATUS(waitStatus));
1886 else if (WIFSIGNALED(waitStatus)) {
1887 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1888 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1889 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1890 s->fs_path, (long) childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)],
1891 #ifdef WCOREDUMP
1892 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1893 #else
1894 "");
1895 #endif
1897 else if (WIFSTOPPED(waitStatus)) {
1898 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1899 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1900 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1901 s->fs_path, (long) childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)]);
1903 } /* for (;;), waitpid() */
1905 #else /* WIN32 */
1907 /* wait for an event to occur or timer expires */
1908 expire = time(NULL) + sleepSeconds;
1909 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1911 if (dwRet == WAIT_FAILED) {
1912 /* There is something seriously wrong here */
1913 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1914 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
1915 bTimeToDie = TRUE;
1918 if (dwRet != WAIT_TIMEOUT) {
1919 now = time(NULL);
1921 if (now < expire)
1922 alarmLeft = expire - now;
1926 * Dynamic fcgi process management
1928 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1929 if (dwRet == MBOX_EVENT) {
1930 read_ready = 1;
1933 now = time(NULL);
1935 dynamic_read_msgs(read_ready);
1937 if(fcgi_dynamic_epoch == 0) {
1938 fcgi_dynamic_epoch = now;
1941 if ((now-fcgi_dynamic_epoch >= (int) dynamicKillInterval) ||
1942 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1943 dynamic_kill_idle_fs_procs();
1944 fcgi_dynamic_epoch = now;
1946 read_ready = 0;
1948 else if (dwRet == WAKE_EVENT) {
1949 continue;
1951 else if (dwRet == TERM_EVENT) {
1952 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1953 "FastCGI: Termination event received process manager shutting down");
1955 bTimeToDie = TRUE;
1956 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
1958 goto ProcessSigTerm;
1960 else {
1961 // Have an received an unknown event - should not happen
1962 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1963 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
1965 bTimeToDie = TRUE;
1966 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
1968 goto ProcessSigTerm;
1971 #endif /* WIN32 */
1973 } /* for (;;), the whole shoot'n match */
1975 ProcessSigTerm:
1977 * Kill off the children, then exit.
1979 shutdown_all();
1981 #ifdef WIN32
1982 return;
1983 #else
1984 exit(0);
1985 #endif
1988 #ifdef WIN32
1989 int fcgi_pm_add_job(fcgi_pm_job *new_job)
1991 int rv = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
1993 if (rv != WAIT_OBJECT_0 && rv != WAIT_ABANDONED)
1995 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1996 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
1997 return -1;
2000 new_job->next = fcgi_dynamic_mbox;
2001 fcgi_dynamic_mbox = new_job;
2003 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
2005 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2006 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
2009 return 0;
2011 #endif