[WIN] Fix starting of scripts under AP2.
[mod_fastcgi.git] / fcgi_pm.c
blob8ca32314a9e851762e3a9ddd30ff358a4cdad674
1 /*
2 * $Id: fcgi_pm.c,v 1.87 2003/06/19 02:18:00 robs Exp $
3 */
6 #include "fcgi.h"
8 #if defined(APACHE2) && !defined(WIN32)
9 #include <pwd.h>
10 #include <unistd.h>
11 #include "unixd.h"
12 #include "apr_signal.h"
13 #endif
15 #ifndef WIN32
16 #include <utime.h>
17 #endif
19 #ifdef _HPUX_SOURCE
20 #include <unistd.h>
21 #define seteuid(arg) setresuid(-1, (arg), -1)
22 #endif
24 int fcgi_dynamic_total_proc_count = 0; /* number of running apps */
25 time_t fcgi_dynamic_epoch = 0; /* last time kill_procs was
26 * invoked by process mgr */
27 time_t fcgi_dynamic_last_analyzed = 0; /* last time calculation was
28 * made for the dynamic procs */
30 static time_t now = 0;
32 #ifdef WIN32
33 #ifdef APACHE2
34 #include "mod_cgi.h"
35 #endif
36 #pragma warning ( disable : 4100 4102 )
37 static BOOL bTimeToDie = FALSE; /* process termination flag */
38 HANDLE fcgi_event_handles[3];
39 #ifndef SIGKILL
40 #define SIGKILL 9
41 #endif
42 #endif
45 #ifndef WIN32
46 static int seteuid_root(void)
48 int rc = seteuid(getuid());
49 if (rc) {
50 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
51 "FastCGI: seteuid(0) failed");
53 return rc;
56 static int seteuid_user(void)
58 int rc = seteuid(ap_user_id);
59 if (rc) {
60 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
61 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id);
63 return rc;
65 #endif
68 * Signal the process to exit. How (or if) the process responds
69 * depends on the FastCGI application library (esp. on Win32) and
70 * possibly application code (signal handlers and whether or not
71 * SA_RESTART is on). At any rate, we send the signal with the
72 * hopes that the process will exit on its own. Later, as we
73 * review the state of application processes, if we see one marked
74 * for death, but that hasn't died within a specified period of
75 * time, fcgi_kill() is called again with a KILL)
77 static void fcgi_kill(ServerProcess *process, int sig)
79 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process->pid, sig);
81 process->state = FCGI_VICTIM_STATE;
83 #ifdef WIN32
85 if (sig == SIGTERM)
87 SetEvent(process->terminationEvent);
89 else if (sig == SIGKILL)
91 TerminateProcess(process->handle, 1);
93 else
95 ap_assert(0);
98 #else /* !WIN32 */
100 if (fcgi_wrapper)
102 seteuid_root();
105 kill(process->pid, sig);
107 if (fcgi_wrapper)
109 seteuid_user();
112 #endif /* !WIN32 */
115 /*******************************************************************************
116 * Send SIGTERM to each process in the server class, remove socket
117 * file if appropriate. Currently this is only called when the PM is shutting
118 * down and thus memory isn't freed and sockets and files aren't closed.
120 static void shutdown_all()
122 fcgi_server *s = fcgi_servers;
124 while (s)
126 ServerProcess *proc = s->procs;
127 int i;
128 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
129 ? dynamicMaxClassProcs
130 : s->numProcesses;
132 #ifndef WIN32
133 if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL)
135 /* Remove the socket file */
136 if (unlink(s->socket_path) != 0 && errno != ENOENT) {
137 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
138 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
139 s->socket_path,
140 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path);
143 #endif
145 /* Send TERM to all processes */
146 for (i = 0; i < numChildren; i++, proc++)
148 if (proc->state == FCGI_RUNNING_STATE)
150 fcgi_kill(proc, SIGTERM);
154 s = s->next;
157 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
160 * WIN32 applications may not have support for the shutdown event
161 * depending on their application library version
164 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT);
165 s = fcgi_servers;
167 while (s)
169 ServerProcess *proc = s->procs;
170 int i;
171 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
172 ? dynamicMaxClassProcs
173 : s->numProcesses;
175 /* Send KILL to all processes */
176 for (i = 0; i < numChildren; i++, proc++)
178 if (proc->state == FCGI_RUNNING_STATE)
180 fcgi_kill(proc, SIGKILL);
184 s = s->next;
187 #endif /* WIN32 */
190 static int init_listen_sock(fcgi_server * fs)
192 ap_assert(fs->directive != APP_CLASS_EXTERNAL);
194 /* Create the socket */
195 if ((fs->listenFd = socket(fs->socket_addr->sa_family, SOCK_STREAM, 0)) < 0)
197 #ifdef WIN32
198 errno = WSAGetLastError(); // Not sure if this will work as expected
199 #endif
200 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
201 "FastCGI: can't create %sserver \"%s\": socket() failed",
202 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
203 fs->fs_path);
204 return -1;
207 #ifndef WIN32
208 if (fs->socket_addr->sa_family == AF_UNIX)
210 /* Remove any existing socket file.. just in case */
211 unlink(((struct sockaddr_un *)fs->socket_addr)->sun_path);
213 else
214 #endif
216 int flag = 1;
217 setsockopt(fs->listenFd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
220 /* Bind it to the socket_addr */
221 if (bind(fs->listenFd, fs->socket_addr, fs->socket_addr_len))
223 char port[11];
225 #ifdef WIN32
226 errno = WSAGetLastError();
227 #endif
228 ap_snprintf(port, sizeof(port), "port=%d",
229 ((struct sockaddr_in *)fs->socket_addr)->sin_port);
231 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
232 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
233 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
234 fs->fs_path,
235 #ifndef WIN32
236 (fs->socket_addr->sa_family == AF_UNIX) ?
237 ((struct sockaddr_un *)fs->socket_addr)->sun_path :
238 #endif
239 port);
242 #ifndef WIN32
243 /* Twiddle Unix socket permissions */
244 else if (fs->socket_addr->sa_family == AF_UNIX
245 && chmod(((struct sockaddr_un *)fs->socket_addr)->sun_path, S_IRUSR | S_IWUSR))
247 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
248 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
249 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
250 fs->fs_path);
252 #endif
254 /* Set to listen */
255 else if (listen(fs->listenFd, fs->listenQueueDepth))
257 #ifdef WIN32
258 errno = WSAGetLastError();
259 #endif
260 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
261 "FastCGI: can't create %sserver \"%s\": listen() failed",
262 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
263 fs->fs_path);
265 else
267 return 0;
270 #ifdef WIN32
271 closesocket(fs->listenFd);
272 #else
273 close(fs->listenFd);
274 #endif
276 fs->listenFd = -1;
278 return -2;
282 *----------------------------------------------------------------------
284 * pm_main
286 * The FastCGI process manager, which runs as a separate
287 * process responsible for:
288 * - Starting all the FastCGI proceses.
289 * - Restarting any of these processes that die (indicated
290 * by SIGCHLD).
291 * - Catching SIGTERM and relaying it to all the FastCGI
292 * processes before exiting.
294 * Inputs:
295 * Uses global variable fcgi_servers.
297 * Results:
298 * Does not return.
300 * Side effects:
301 * Described above.
303 *----------------------------------------------------------------------
305 #ifndef WIN32
306 static int caughtSigTerm = FALSE;
307 static int caughtSigChld = FALSE;
308 static int caughtSigAlarm = FALSE;
310 static void signal_handler(int signo)
312 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
313 /* SIGUSR1 & SIGHUP are sent by apache to its process group
314 * when apache get 'em. Apache follows up (1.2.x) with attacks
315 * on each of its child processes, but we've got the KillMgr
316 * sitting between us so we never see the KILL. The main loop
317 * in ProcMgr also checks to see if the KillMgr has terminated,
318 * and if it has, we handl it as if we should shutdown too. */
319 caughtSigTerm = TRUE;
320 } else if(signo == SIGCHLD) {
321 caughtSigChld = TRUE;
322 } else if(signo == SIGALRM) {
323 caughtSigAlarm = TRUE;
326 #endif
329 *----------------------------------------------------------------------
331 * spawn_fs_process --
333 * Fork and exec the specified fcgi process.
335 * Results:
336 * 0 for successful fork, -1 for failed fork.
338 * In case the child fails before or in the exec, the child
339 * obtains the error log by calling getErrLog, logs
340 * the error, and exits with exit status = errno of
341 * the failed system call.
343 * Side effects:
344 * Child process created.
346 *----------------------------------------------------------------------
348 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
350 #ifndef WIN32
352 pid_t child_pid;
353 int i;
354 char *dirName;
355 char *dnEnd, *failedSysCall;
357 child_pid = fork();
358 if (child_pid) {
359 return child_pid;
362 /* We're the child. We're gonna exec() so pools don't matter. */
364 dnEnd = strrchr(fs->fs_path, '/');
365 if (dnEnd == NULL) {
366 dirName = "./";
367 } else {
368 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
369 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
371 if (chdir(dirName) < 0) {
372 failedSysCall = "chdir()";
373 goto FailedSystemCallExit;
376 #ifndef __EMX__
377 /* OS/2 dosen't support nice() */
378 if (fs->processPriority != 0) {
379 if (nice(fs->processPriority) == -1) {
380 failedSysCall = "nice()";
381 goto FailedSystemCallExit;
384 #endif
386 /* Open the listenFd on spec'd fd */
387 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
388 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
390 /* Close all other open fds, except stdout/stderr. Leave these two open so
391 * FastCGI applications don't have to find and fix ALL 3rd party libs that
392 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
393 * main server error_log - @@@ provide a directive control where this goes.
395 ap_error_log2stderr(fcgi_apache_main_server);
396 dup2(2, 1);
397 for (i = 0; i < FCGI_MAX_FD; i++) {
398 if (i != FCGI_LISTENSOCK_FILENO && i != 2 && i != 1) {
399 close(i);
403 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
404 * install its own handler. */
405 signal(SIGPIPE, SIG_IGN);
407 if (fcgi_wrapper)
409 char *shortName;
411 /* Relinquish our root real uid powers */
412 seteuid_root();
413 setuid(ap_user_id);
415 /* AP13 does not use suexec if the target uid/gid is the same as the
416 * server's - AP20 does. I (now) consider the AP2 approach better
417 * (fcgi_pm.c v1.42 incorporated the 1.3 behaviour, v1.84 reverted it,
418 * v1.85 added the compile time option to use the old behaviour). */
420 #ifdef NO_SUEXEC_FOR_AP_USER_N_GROUP
422 if (fcgi_user_id == fs->uid && fcgi_group_id == fs->gid)
424 goto NO_SUEXEC;
427 #endif
428 shortName = strrchr(fs->fs_path, '/') + 1;
430 do {
431 execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group,
432 shortName, NULL, fs->envp);
433 } while (errno == EINTR);
435 else
438 NO_SUEXEC:
439 do {
440 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
441 } while (errno == EINTR);
444 failedSysCall = "execle()";
446 FailedSystemCallExit:
447 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
448 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
449 exit(-1);
451 /* avoid an irrelevant compiler warning */
452 return(0);
454 #else /* WIN32 */
456 #ifdef APACHE2
458 /* based on mod_cgi.c:run_cgi_child() */
460 apr_pool_t * tp;
461 char * termination_env_string;
462 HANDLE listen_handle = INVALID_HANDLE_VALUE;
463 apr_procattr_t * procattr;
464 apr_proc_t proc = { 0 };
465 apr_file_t * file;
466 int i = 0;
467 cgi_exec_info_t e_info = { 0 };
468 request_rec r = { 0 };
469 const char *command;
470 const char **argv;
471 int rv;
472 APR_OPTIONAL_FN_TYPE(ap_cgi_build_command) *cgi_build_command;
474 cgi_build_command = APR_RETRIEVE_OPTIONAL_FN(ap_cgi_build_command);
475 if (cgi_build_command == NULL)
477 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
478 "FastCGI: can't exec server \"%s\", mod_cgi isn't loaded",
479 fs->fs_path);
480 return 0;
483 if (apr_pool_create(&tp, fcgi_config_pool))
484 return 0;
486 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
487 if (process->terminationEvent == NULL)
488 goto CLEANUP;
490 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
492 termination_env_string = ap_psprintf(tp,
493 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
495 while (fs->envp[i]) i++;
496 fs->envp[i++] = termination_env_string;
497 fs->envp[i] = (char *) fs->mutex_env_string;
499 ap_assert(fs->envp[i + 1] == NULL);
501 if (fs->socket_path)
503 SECURITY_ATTRIBUTES sa = { 0 };
505 sa.bInheritHandle = TRUE;
506 sa.nLength = sizeof(sa);
508 listen_handle = CreateNamedPipe(fs->socket_path,
509 PIPE_ACCESS_DUPLEX,
510 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
511 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
513 if (listen_handle == INVALID_HANDLE_VALUE)
515 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
516 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
517 fs->fs_path);
518 goto CLEANUP;
521 else
523 listen_handle = (HANDLE) fs->listenFd;
526 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
527 r.server = fcgi_apache_main_server;
528 r.filename = (char *) fs->fs_path;
529 r.pool = tp;
530 r.subprocess_env = apr_table_make(tp, 0);
532 e_info.cmd_type = APR_PROGRAM;
534 rv = cgi_build_command(&command, &argv, &r, tp, &e_info);
535 if (rv != APR_SUCCESS)
537 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
538 "FastCGI: don't know how to spawn cmd child process: %s",
539 fs->fs_path);
540 goto CLEANUP;
543 if (apr_procattr_create(&procattr, tp))
544 goto CLEANUP;
546 if (apr_procattr_dir_set(procattr, ap_make_dirstr_parent(tp, fs->fs_path)))
547 goto CLEANUP;
549 if (apr_procattr_cmdtype_set(procattr, e_info.cmd_type))
550 goto CLEANUP;
552 if (apr_procattr_detach_set(procattr, 1))
553 goto CLEANUP;
555 if (apr_os_file_put(&file, &listen_handle, 0, tp))
556 goto CLEANUP;
558 /* procattr is opaque so we have to use this - unfortuantely it dups */
559 if (apr_procattr_child_in_set(procattr, file, NULL))
560 goto CLEANUP;
562 if (apr_proc_create(&proc, command, argv, fs->envp, procattr, tp))
563 goto CLEANUP;
565 process->handle = proc.hproc;
568 CLEANUP:
570 if (i)
572 fs->envp[i - 1] = NULL;
575 ap_destroy_pool(tp);
577 return proc.pid;
579 #else /* WIN32 && !APACHE2 */
581 /* Adapted from Apache's util_script.c ap_call_exec() */
582 char *interpreter = NULL;
583 char *quoted_filename;
584 char *pCommand;
585 char *pEnvBlock, *pNext;
587 int i = 0;
588 int iEnvBlockLen = 1;
590 file_type_e fileType;
592 STARTUPINFO si;
593 PROCESS_INFORMATION pi;
595 request_rec r;
596 pid_t pid = -1;
598 pool * tp = ap_make_sub_pool(fcgi_config_pool);
600 HANDLE listen_handle;
601 char * termination_env_string = NULL;
603 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
604 if (process->terminationEvent == NULL)
606 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
607 "FastCGI: can't create termination event for server \"%s\", "
608 "CreateEvent() failed", fs->fs_path);
609 goto CLEANUP;
611 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
613 termination_env_string = ap_psprintf(tp,
614 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
616 if (fs->socket_path)
618 SECURITY_ATTRIBUTES sa;
620 sa.lpSecurityDescriptor = NULL;
621 sa.bInheritHandle = TRUE;
622 sa.nLength = sizeof(sa);
624 listen_handle = CreateNamedPipe(fs->socket_path,
625 PIPE_ACCESS_DUPLEX,
626 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
627 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
629 if (listen_handle == INVALID_HANDLE_VALUE)
631 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
632 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
633 goto CLEANUP;
636 else
638 listen_handle = (HANDLE) fs->listenFd;
641 memset(&si, 0, sizeof(si));
642 memset(&pi, 0, sizeof(pi));
643 memset(&r, 0, sizeof(r));
645 // Can up a fake request to pass to ap_get_win32_interpreter()
646 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
647 r.server = fcgi_apache_main_server;
648 r.filename = (char *) fs->fs_path;
649 r.pool = tp;
651 fileType = ap_get_win32_interpreter(&r, &interpreter);
653 if (fileType == eFileTypeUNKNOWN) {
654 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
655 "FastCGI: %s is not executable; ensure interpreted scripts have "
656 "\"#!\" as their first line",
657 fs->fs_path);
658 ap_destroy_pool(tp);
659 goto CLEANUP;
663 * We have the interpreter (if there is one) and we have
664 * the arguments (if there are any).
665 * Build the command string to pass to CreateProcess.
667 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
668 if (interpreter && *interpreter) {
669 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
671 else {
672 pCommand = quoted_filename;
676 * Make child process use hPipeOutputWrite as standard out,
677 * and make sure it does not show on screen.
679 si.cb = sizeof(si);
680 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
681 si.wShowWindow = SW_HIDE;
682 si.hStdInput = listen_handle;
684 // XXX These should be open to the error_log
685 si.hStdOutput = INVALID_HANDLE_VALUE;
686 si.hStdError = INVALID_HANDLE_VALUE;
689 * Win32's CreateProcess call requires that the environment
690 * be passed in an environment block, a null terminated block of
691 * null terminated strings.
692 * @todo we should store the env in this format for win32.
694 while (fs->envp[i])
696 iEnvBlockLen += strlen(fs->envp[i]) + 1;
697 i++;
700 iEnvBlockLen += strlen(termination_env_string) + 1;
701 iEnvBlockLen += strlen(fs->mutex_env_string) + 1;
703 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
705 i = 0;
706 pNext = pEnvBlock;
707 while (fs->envp[i])
709 strcpy(pNext, fs->envp[i]);
710 pNext += strlen(pNext) + 1;
711 i++;
714 strcpy(pNext, termination_env_string);
715 pNext += strlen(pNext) + 1;
716 strcpy(pNext, fs->mutex_env_string);
718 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
720 pEnvBlock,
721 ap_make_dirstr_parent(tp, fs->fs_path),
722 &si, &pi))
724 /* Hack to get 16-bit CGI's working. It works for all the
725 * standard modules shipped with Apache. pi.dwProcessId is 0
726 * for 16-bit CGIs and all the Unix specific code that calls
727 * ap_call_exec interprets this as a failure case. And we can't
728 * use -1 either because it is mapped to 0 by the caller.
730 pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId;
732 process->handle = pi.hProcess;
733 CloseHandle(pi.hThread);
736 if (fs->socket_path)
738 CloseHandle(listen_handle);
741 CLEANUP:
743 ap_destroy_pool(tp);
745 return pid;
747 #endif /* !APACHE2 */
748 #endif /* WIN32 */
751 #ifndef WIN32
752 static void reduce_privileges(void)
754 const char *name;
756 if (geteuid() != 0)
757 return;
759 #ifndef __EMX__
760 /* Get username if passed as a uid */
761 if (ap_user_name[0] == '#') {
762 uid_t uid = atoi(&ap_user_name[1]);
763 struct passwd *ent = getpwuid(uid);
765 if (ent == NULL) {
766 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
767 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
768 "you probably need to modify the User directive", (unsigned)uid);
769 exit(1);
771 name = ent->pw_name;
773 else
774 name = ap_user_name;
776 /* Change Group */
777 if (setgid(ap_group_id) == -1) {
778 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
779 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
780 exit(1);
783 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
785 /* Initialize supplementary groups */
786 if (initgroups(name, ap_group_id) == -1) {
787 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
788 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
789 name, (unsigned)ap_group_id);
790 exit(1);
792 #endif /* __EMX__ */
794 /* Change User */
795 if (fcgi_wrapper) {
796 if (seteuid_user() == -1) {
797 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
798 "FastCGI: process manager exiting, failed to reduce privileges");
799 exit(1);
802 else {
803 if (setuid(ap_user_id) == -1) {
804 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
805 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
806 exit(1);
811 /*************
812 * Change the name of this process - best we can easily.
814 static void change_process_name(const char * const name)
816 /* under Apache2, ap_server_argv0 is const */
817 strncpy((char *) ap_server_argv0, name, strlen(ap_server_argv0));
819 #endif /* !WIN32 */
821 static void schedule_start(fcgi_server *s, int proc)
823 /* If we've started one recently, don't register another */
824 time_t time_passed = now - s->restartTime;
826 if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay))
827 || ((s->procs[proc].pid == 0) && (time_passed < s->initStartDelay)))
829 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);
830 return;
833 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
834 s->procs[proc].state = FCGI_START_STATE;
835 if (proc == dynamicMaxClassProcs - 1) {
836 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
837 "FastCGI: scheduled the %sstart of the last (dynamic) server "
838 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
839 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
844 *----------------------------------------------------------------------
846 * dynamic_read_msgs
848 * Removes the records written by request handlers and decodes them.
849 * We also update the data structures to reflect the changes.
851 *----------------------------------------------------------------------
854 static void dynamic_read_msgs(int read_ready)
856 fcgi_server *s;
857 int rc;
859 #ifndef WIN32
860 static int buflen = 0;
861 static char buf[FCGI_MSGS_BUFSIZE + 1];
862 char *ptr1, *ptr2, opcode;
863 char execName[FCGI_MAXPATH + 1];
864 char user[MAX_USER_NAME_LEN + 2];
865 char group[MAX_GID_CHAR_LEN + 1];
866 unsigned long q_usec = 0UL, req_usec = 0UL;
867 #else
868 fcgi_pm_job *joblist = NULL;
869 fcgi_pm_job *cjob = NULL;
870 #endif
872 pool *sp = NULL, *tp;
874 #ifndef WIN32
875 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
876 #endif
879 * To prevent the idle application from running indefinitely, we
880 * check the timer and if it is expired, we recompute the values
881 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
882 * message is received, only updates are made to the data structures.
884 if (fcgi_dynamic_last_analyzed == 0) {
885 fcgi_dynamic_last_analyzed = now;
887 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
888 for (s = fcgi_servers; s != NULL; s = s->next) {
889 if (s->directive != APP_CLASS_DYNAMIC)
890 break;
892 /* Advance the last analyzed timestamp by the elapsed time since
893 * it was last set. Round the increase down to the nearest
894 * multiple of dynamicUpdateInterval */
896 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
897 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
898 s->totalConnTime = 0UL;
899 s->totalQueueTime = 0UL;
903 if (read_ready <= 0) {
904 return;
907 #ifndef WIN32
908 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
909 if (rc <= 0) {
910 if (!caughtSigTerm) {
911 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
912 "FastCGI: read() from pipe failed (%d)", rc);
913 if (rc == 0) {
914 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
915 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
916 caughtSigTerm = TRUE;
919 return;
921 buflen += rc;
922 buf[buflen] = '\0';
924 #else
926 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
927 * request to do something) and/or when a timeout expires.
928 * There really should be no reason why this wait would get stuck
929 * but there's no point in waiting forever. */
931 rc = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
933 if (rc != WAIT_OBJECT_0 && rc != WAIT_ABANDONED)
935 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
936 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
937 return;
940 joblist = fcgi_dynamic_mbox;
941 fcgi_dynamic_mbox = NULL;
943 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
945 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
946 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
949 cjob = joblist;
950 #endif
952 #ifdef APACHE2
953 apr_pool_create(&tp, fcgi_config_pool);
954 #else
955 tp = ap_make_sub_pool(fcgi_config_pool);
956 #endif
958 #ifndef WIN32
959 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
960 int scan_failed = 0;
962 ptr2 = strchr(ptr1, '*');
963 if (ptr2) {
964 *ptr2++ = '\0';
966 else {
967 break;
970 opcode = *ptr1;
972 switch (opcode)
974 case FCGI_SERVER_START_JOB:
975 case FCGI_SERVER_RESTART_JOB:
977 if (sscanf(ptr1, "%c %s %16s %15s",
978 &opcode, execName, user, group) != 4)
980 scan_failed = 1;
982 break;
984 case FCGI_REQUEST_TIMEOUT_JOB:
986 if (sscanf(ptr1, "%c %s %16s %15s",
987 &opcode, execName, user, group) != 4)
989 scan_failed = 1;
991 break;
993 case FCGI_REQUEST_COMPLETE_JOB:
995 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
996 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
998 scan_failed = 1;
1000 break;
1002 default:
1004 scan_failed = 1;
1005 break;
1008 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode, execName, user, group, q_usec, req_usec);
1010 if (scan_failed) {
1011 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1012 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
1013 goto NextJob;
1015 #else
1016 /* Update data structures for processing */
1017 while (cjob != NULL) {
1018 joblist = cjob->next;
1019 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
1020 #endif
1022 #ifndef WIN32
1023 s = fcgi_util_fs_get(execName, user, group);
1024 #else
1025 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
1026 #endif
1028 #ifndef WIN32
1029 if (s==NULL && opcode != FCGI_REQUEST_COMPLETE_JOB)
1030 #else
1031 if (s==NULL && cjob->id != FCGI_REQUEST_COMPLETE_JOB)
1032 #endif
1034 #ifdef WIN32
1036 HANDLE mutex = CreateMutex(NULL, FALSE, cjob->fs_path);
1038 if (mutex == NULL)
1040 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1041 "FastCGI: can't create accept mutex "
1042 "for (dynamic) server \"%s\"", cjob->fs_path);
1043 goto BagNewServer;
1046 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
1047 #else
1048 const char *err;
1049 #endif
1051 /* Create a perm subpool to hold the new server data,
1052 * we can destroy it if something doesn't pan out */
1053 #ifdef APACHE2
1054 apr_pool_create(&sp, fcgi_config_pool);
1055 #else
1056 sp = ap_make_sub_pool(fcgi_config_pool);
1057 #endif
1059 /* Create a new "dynamic" server */
1060 s = fcgi_util_fs_new(sp);
1062 s->directive = APP_CLASS_DYNAMIC;
1063 s->restartDelay = dynamicRestartDelay;
1064 s->listenQueueDepth = dynamicListenQueueDepth;
1065 s->initStartDelay = dynamicInitStartDelay;
1066 s->envp = dynamicEnvp;
1067 s->flush = dynamicFlush;
1069 #ifdef WIN32
1070 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
1071 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
1072 #else
1073 s->fs_path = ap_pstrdup(sp, execName);
1074 #endif
1075 ap_getparents(s->fs_path);
1076 ap_no2slash(s->fs_path);
1077 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
1079 /* XXX the socket_path (both Unix and Win) *is* deducible and
1080 * thus can and will be used by other apache instances without
1081 * the use of shared data regarding the processes serving the
1082 * requests. This can result in slightly unintuitive process
1083 * counts and security implications. This is prevented
1084 * if suexec (Unix) is in use. This is both a feature and a flaw.
1085 * Changing it now would break existing installations. */
1087 #ifndef WIN32
1088 /* Create socket file's path */
1089 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
1090 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1092 /* Create sockaddr, prealloc it so it won't get created in tp */
1093 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
1094 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
1095 &s->socket_addr_len, s->socket_path);
1096 if (err) {
1097 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1098 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
1099 goto BagNewServer;
1102 if (init_listen_sock(s)) {
1103 goto BagNewServer;
1106 /* If a wrapper is being used, config user/group info */
1107 if (fcgi_wrapper) {
1108 if (user[0] == '~') {
1109 /* its a user dir uri, the rest is a username, not a uid */
1110 struct passwd *pw = getpwnam(&user[1]);
1112 if (!pw) {
1113 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1114 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1115 execName, &user[1]);
1116 goto BagNewServer;
1118 s->uid = pw->pw_uid;
1119 s->user = ap_pstrdup(sp, user);
1120 s->username = s->user;
1122 s->gid = pw->pw_gid;
1123 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
1125 else {
1126 struct passwd *pw;
1128 s->uid = (uid_t)atol(user);
1129 pw = getpwuid(s->uid);
1130 if (!pw) {
1131 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1132 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1133 execName, (long)s->uid);
1134 goto BagNewServer;
1136 s->user = ap_pstrdup(sp, user);
1137 s->username = ap_pstrdup(sp, pw->pw_name);
1139 s->gid = (gid_t)atol(group);
1140 s->group = ap_pstrdup(sp, group);
1143 #else
1144 /* Create socket file's path */
1145 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
1146 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1147 s->listenFd = 0;
1148 #endif
1150 fcgi_util_fs_add(s);
1152 else {
1153 #ifndef WIN32
1154 if (opcode == FCGI_SERVER_RESTART_JOB) {
1155 #else
1156 if (cjob->id==FCGI_SERVER_RESTART_JOB) {
1157 #endif
1158 /* Check to see if the binary has changed. If so,
1159 * kill the FCGI application processes, and
1160 * restart them.
1162 struct stat stbuf;
1163 int i;
1164 #ifdef WIN32
1165 char * app_path = cjob->fs_path;
1166 #else
1167 char * app_path = execName;
1168 #endif
1170 if (stat(app_path, &stbuf) == 0 && stbuf.st_mtime > s->startTime)
1172 int do_restart = 0;
1174 /* prevent addition restart requests */
1175 s->startTime = now;
1176 #ifndef WIN32
1177 utime(s->socket_path, NULL);
1178 #endif
1180 /* kill old server(s) */
1181 for (i = 0; i < dynamicMaxClassProcs; i++)
1183 if (s->procs[i].pid > 0
1184 && stbuf.st_mtime > s->procs[i].start_time)
1186 fcgi_kill(&s->procs[i], SIGTERM);
1187 do_restart++;
1191 if (do_restart)
1193 ap_log_error(FCGI_LOG_WARN_NOERRNO,
1194 fcgi_apache_main_server, "FastCGI: restarting "
1195 "old server \"%s\" processes, newer version "
1196 "found", app_path);
1200 /* If dynamicAutoRestart, don't mark any new processes
1201 * for starting because we probably got the
1202 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1203 * will be restarting all of those we just killed.
1205 if (dynamicAutoRestart)
1206 goto NextJob;
1208 #ifndef WIN32
1209 else if (opcode == FCGI_SERVER_START_JOB) {
1210 #else
1211 else if (cjob->id==FCGI_SERVER_START_JOB) {
1212 #endif
1213 /* we've been asked to start a process--only start
1214 * it if we're not already running at least one
1215 * instance.
1217 int i;
1219 for (i = 0; i < dynamicMaxClassProcs; i++) {
1220 if (s->procs[i].state == FCGI_RUNNING_STATE)
1221 break;
1223 /* if already running, don't start another one */
1224 if (i < dynamicMaxClassProcs) {
1225 goto NextJob;
1230 #ifndef WIN32
1231 switch (opcode)
1232 #else
1233 switch (cjob->id)
1234 #endif
1236 int i, start;
1238 case FCGI_SERVER_RESTART_JOB:
1240 start = FALSE;
1242 /* We just waxed 'em all. Try to find an idle slot. */
1244 for (i = 0; i < dynamicMaxClassProcs; ++i)
1246 if (s->procs[i].state == FCGI_START_STATE
1247 || s->procs[i].state == FCGI_RUNNING_STATE)
1249 break;
1251 else if (s->procs[i].state == FCGI_KILLED_STATE
1252 || s->procs[i].state == FCGI_READY_STATE)
1254 start = TRUE;
1255 break;
1259 /* Nope, just use the first slot */
1260 if (i == dynamicMaxClassProcs)
1262 start = TRUE;
1263 i = 0;
1266 if (start)
1268 schedule_start(s, i);
1271 break;
1273 case FCGI_SERVER_START_JOB:
1274 case FCGI_REQUEST_TIMEOUT_JOB:
1276 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1278 * Extra instances should have been
1279 * terminated beforehand, probably need
1280 * to increase ProcessSlack parameter
1282 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1283 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1284 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1285 goto NextJob;
1288 /* find next free slot */
1289 for (i = 0; i < dynamicMaxClassProcs; i++)
1291 if (s->procs[i].state == FCGI_START_STATE)
1293 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1294 break;
1296 else if (s->procs[i].state == FCGI_RUNNING_STATE)
1298 continue;
1301 schedule_start(s, i);
1302 break;
1305 #ifdef FCGI_DEBUG
1306 if (i >= dynamicMaxClassProcs) {
1307 FCGIDBG1("ignore_job: slots are max'd");
1309 #endif
1310 break;
1311 case FCGI_REQUEST_COMPLETE_JOB:
1312 /* only record stats if we have a structure */
1313 if (s) {
1314 #ifndef WIN32
1315 s->totalConnTime += req_usec;
1316 s->totalQueueTime += q_usec;
1317 #else
1318 s->totalConnTime += cjob->start_time;
1319 s->totalQueueTime += cjob->qsec;
1320 #endif
1322 break;
1325 NextJob:
1327 #ifdef WIN32
1328 /* Cleanup job data */
1329 free(cjob->fs_path);
1330 free(cjob->user);
1331 free(cjob->group);
1332 free(cjob);
1333 cjob = joblist;
1334 #endif
1336 continue;
1338 BagNewServer:
1339 if (sp) ap_destroy_pool(sp);
1341 #ifdef WIN32
1342 free(cjob->fs_path);
1343 free(cjob);
1344 cjob = joblist;
1345 #endif
1348 #ifndef WIN32
1349 if (ptr1 == buf) {
1350 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1351 "FastCGI: really bogus message: \"%s\"", ptr1);
1352 ptr1 += strlen(buf);
1355 buflen -= ptr1 - buf;
1356 if (buflen) {
1357 memmove(buf, ptr1, buflen);
1359 #endif
1361 ap_destroy_pool(tp);
1365 *----------------------------------------------------------------------
1367 * dynamic_kill_idle_fs_procs
1369 * Implement a kill policy for the dynamic FastCGI applications.
1370 * We also update the data structures to reflect the changes.
1372 * Side effects:
1373 * Processes are marked for deletion possibly killed.
1375 *----------------------------------------------------------------------
1377 static void dynamic_kill_idle_fs_procs(void)
1379 fcgi_server *s;
1380 int victims = 0;
1382 for (s = fcgi_servers; s != NULL; s = s->next)
1385 * server's smoothed running time, or if that's 0, the current total
1387 unsigned long connTime;
1390 * maximum number of microseconds that all of a server's running
1391 * processes together could have spent running since the last check
1393 unsigned long totalTime;
1396 * percentage, 0-100, of totalTime that the processes actually used
1398 int loadFactor;
1400 int i;
1401 int really_running = 0;
1403 if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1405 continue;
1408 /* s->numProcesses includes pending kills so get the "active" count */
1409 for (i = 0; i < dynamicMaxClassProcs; ++i)
1411 if (s->procs[i].state == FCGI_RUNNING_STATE) ++really_running;
1414 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1415 totalTime = really_running * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1417 loadFactor = 100 * connTime / totalTime;
1419 if (really_running == 1)
1421 if (loadFactor >= dynamicThreshold1)
1423 continue;
1426 else
1428 int load = really_running / ( really_running - 1) * loadFactor;
1430 if (load >= dynamicThresholdN)
1432 continue;
1437 * Run through the procs to see if we can get away w/o waxing one.
1439 for (i = 0; i < dynamicMaxClassProcs; ++i)
1441 if (s->procs[i].state == FCGI_START_STATE)
1443 s->procs[i].state = FCGI_READY_STATE;
1444 break;
1446 else if (s->procs[i].state == FCGI_VICTIM_STATE)
1448 break;
1452 if (i >= dynamicMaxClassProcs)
1454 ServerProcess * procs = s->procs;
1455 int youngest = -1;
1457 for (i = 0; i < dynamicMaxClassProcs; ++i)
1459 if (procs[i].state == FCGI_RUNNING_STATE)
1461 if (youngest == -1 || procs[i].start_time >= procs[youngest].start_time)
1463 youngest = i;
1468 if (youngest != -1)
1470 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1471 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1472 s->fs_path, (long) s->procs[youngest].pid);
1474 fcgi_kill(&s->procs[youngest], SIGTERM);
1476 victims++;
1480 * If the number of non-victims is less than or equal to
1481 * the minimum that may be running without being killed off,
1482 * don't select any more victims.
1484 if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1486 break;
1492 #ifdef WIN32
1494 // This is a little bogus, there's gotta be a better way to do this
1495 // Can we use WaitForMultipleObjects()
1496 #define FCGI_PROC_WAIT_TIME 100
1498 void child_wait_thread_main(void *dummy) {
1499 fcgi_server *s;
1500 DWORD dwRet = WAIT_TIMEOUT;
1501 int numChildren;
1502 int i;
1503 int waited;
1505 while (!bTimeToDie) {
1506 waited = 0;
1508 for (s = fcgi_servers; s != NULL; s = s->next) {
1509 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1510 continue;
1512 if (s->directive == APP_CLASS_DYNAMIC) {
1513 numChildren = dynamicMaxClassProcs;
1515 else {
1516 numChildren = s->numProcesses;
1519 for (i=0; i < numChildren; i++) {
1520 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1522 DWORD exitStatus = 0;
1524 /* timeout is currently set for 100 miliecond */
1525 /* it may need to be longer or user customizable */
1526 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1528 waited = 1;
1530 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1531 /* a child fs has died */
1532 /* mark the child as dead */
1534 if (s->directive == APP_CLASS_STANDARD) {
1535 /* restart static app */
1536 s->procs[i].state = FCGI_START_STATE;
1537 s->numFailures++;
1539 else {
1540 s->numProcesses--;
1541 fcgi_dynamic_total_proc_count--;
1542 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1544 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1545 s->procs[i].state = FCGI_KILLED_STATE;
1547 else {
1548 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1549 s->numFailures++;
1551 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1552 s->procs[i].state = FCGI_START_STATE;
1554 else {
1555 s->procs[i].state = FCGI_READY_STATE;
1560 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1562 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1563 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1564 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1565 s->fs_path, (long) s->procs[i].pid, exitStatus);
1567 CloseHandle(s->procs[i].handle);
1568 s->procs[i].handle = INVALID_HANDLE_VALUE;
1569 s->procs[i].pid = -1;
1571 /* wake up the main thread */
1572 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1577 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1580 #endif
1582 #ifndef WIN32
1583 static void setup_signals(void)
1585 struct sigaction sa;
1587 /* Setup handlers */
1589 sa.sa_handler = signal_handler;
1590 sigemptyset(&sa.sa_mask);
1591 sa.sa_flags = 0;
1593 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1594 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1595 "sigaction(SIGTERM) failed");
1597 /* httpd restart */
1598 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1599 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1600 "sigaction(SIGHUP) failed");
1602 /* httpd graceful restart */
1603 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1604 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1605 "sigaction(SIGUSR1) failed");
1607 /* read messages from request handlers - kill interval expired */
1608 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1609 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1610 "sigaction(SIGALRM) failed");
1612 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1613 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1614 "sigaction(SIGCHLD) failed");
1617 #endif
1619 #if !defined(WIN32) && !defined(APACHE2)
1620 int fcgi_pm_main(void *dummy, child_info *info)
1621 #else
1622 void fcgi_pm_main(void *dummy)
1623 #endif
1625 fcgi_server *s;
1626 unsigned int i;
1627 int read_ready = 0;
1628 int alarmLeft = 0;
1630 #ifdef WIN32
1631 DWORD dwRet;
1632 HANDLE child_wait_thread = INVALID_HANDLE_VALUE;
1633 #else
1634 int callWaitPid, callDynamicProcs;
1635 #endif
1637 #ifdef WIN32
1638 // Add SystemRoot to the dynamic environment
1639 char ** envp = dynamicEnvp;
1640 for (i = 0; *envp; ++i) {
1641 ++envp;
1643 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1645 #else
1647 reduce_privileges();
1648 change_process_name("fcgi-pm");
1650 close(fcgi_pm_pipe[1]);
1651 setup_signals();
1653 if (fcgi_wrapper) {
1654 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1655 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1657 #endif
1659 /* Initialize AppClass */
1660 for (s = fcgi_servers; s != NULL; s = s->next)
1662 if (s->directive != APP_CLASS_STANDARD)
1663 continue;
1665 #ifdef WIN32
1666 if (s->socket_path)
1667 s->listenFd = 0;
1668 #endif
1670 for (i = 0; i < s->numProcesses; ++i)
1671 s->procs[i].state = FCGI_START_STATE;
1674 #ifdef WIN32
1675 child_wait_thread = (HANDLE) _beginthread(child_wait_thread_main, 0, NULL);
1677 if (child_wait_thread == (HANDLE) -1)
1679 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1680 "FastCGI: failed to create process manager's wait thread!");
1683 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1684 "FastCGI: process manager initialized");
1685 #else
1686 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1687 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1688 #endif
1690 now = time(NULL);
1693 * Loop until SIGTERM
1695 for (;;) {
1696 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1697 #ifdef WIN32
1698 time_t expire;
1699 #else
1700 pid_t childPid;
1701 int waitStatus;
1702 #endif
1703 unsigned int numChildren;
1706 * If we came out of sigsuspend() for any reason other than
1707 * SIGALRM, pick up where we left off.
1709 if (alarmLeft)
1710 sleepSeconds = alarmLeft;
1713 * Examine each configured AppClass for a process that needs
1714 * starting. Compute the earliest time when the start should
1715 * be attempted, starting it now if the time has passed. Also,
1716 * remember that we do NOT need to restart externally managed
1717 * FastCGI applications.
1719 for (s = fcgi_servers; s != NULL; s = s->next)
1721 if (s->directive == APP_CLASS_EXTERNAL)
1722 continue;
1724 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1725 ? dynamicMaxClassProcs
1726 : s->numProcesses;
1728 for (i = 0; i < numChildren; ++i)
1730 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1732 int restart = (s->procs[i].pid < 0);
1733 time_t restartTime = s->restartTime;
1735 if (s->bad)
1737 /* we've gone to using the badDelay, the only thing that
1738 resets bad is when badDelay has expired. but numFailures
1739 is only just set below its threshold. the proc's
1740 start_times are all reset when the bad is. the numFailures
1741 is reset when we see an app run for a period */
1743 s->procs[i].start_time = 0;
1746 if (s->numFailures > MAX_FAILED_STARTS)
1748 time_t last_start_time = s->procs[i].start_time;
1750 if (last_start_time && now - last_start_time > RUNTIME_SUCCESS_INTERVAL)
1752 s->bad = 0;
1753 s->numFailures = 0;
1754 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1755 "FastCGI:%s server \"%s\" has remained"
1756 " running for more than %d seconds, its restart"
1757 " interval has been restored to %d seconds",
1758 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1759 s->fs_path, RUNTIME_SUCCESS_INTERVAL, s->restartDelay);
1761 else
1763 unsigned int j;
1765 for (j = 0; j < numChildren; ++j)
1767 if (s->procs[j].pid <= 0) continue;
1768 if (s->procs[j].state != FCGI_RUNNING_STATE) continue;
1769 if (s->procs[j].start_time == 0) continue;
1770 if (now - s->procs[j].start_time > RUNTIME_SUCCESS_INTERVAL) break;
1773 if (j >= numChildren)
1775 s->bad = 1;
1776 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1777 "FastCGI:%s server \"%s\" has failed to remain"
1778 " running for %d seconds given %d attempts, its restart"
1779 " interval has been backed off to %d seconds",
1780 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1781 s->fs_path, RUNTIME_SUCCESS_INTERVAL, MAX_FAILED_STARTS,
1782 FAILED_STARTS_DELAY);
1784 else
1786 s->bad = 0;
1787 s->numFailures = 0;
1788 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1789 "FastCGI:%s server \"%s\" has remained"
1790 " running for more than %d seconds, its restart"
1791 " interval has been restored to %d seconds",
1792 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1793 s->fs_path, RUNTIME_SUCCESS_INTERVAL, s->restartDelay);
1798 if (s->bad)
1800 restartTime += FAILED_STARTS_DELAY;
1802 else
1804 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1807 if (restartTime <= now)
1809 if (s->bad)
1811 s->bad = 0;
1812 s->numFailures = MAX_FAILED_STARTS;
1815 if (s->listenFd < 0 && init_listen_sock(s))
1817 if (sleepSeconds > s->initStartDelay)
1818 sleepSeconds = s->initStartDelay;
1819 break;
1821 #ifndef WIN32
1822 if (caughtSigTerm) {
1823 goto ProcessSigTerm;
1825 #endif
1826 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1827 if (s->procs[i].pid <= 0) {
1828 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1829 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1830 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1831 s->fs_path);
1833 sleepSeconds = min(sleepSeconds,
1834 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1836 s->procs[i].pid = -1;
1837 break;
1840 s->procs[i].start_time = now;
1841 s->restartTime = now;
1843 if (s->startTime == 0) {
1844 s->startTime = now;
1847 if (s->directive == APP_CLASS_DYNAMIC) {
1848 s->numProcesses++;
1849 fcgi_dynamic_total_proc_count++;
1850 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1853 s->procs[i].state = FCGI_RUNNING_STATE;
1855 if (fcgi_wrapper) {
1856 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1857 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1858 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1859 s->fs_path, (long) s->uid, (long) s->gid,
1860 restart ? "re" : "", (long) s->procs[i].pid);
1862 else {
1863 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1864 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1865 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1866 s->fs_path, restart ? "re" : "", (long) s->procs[i].pid);
1868 ap_assert(s->procs[i].pid > 0);
1869 } else {
1870 sleepSeconds = min(sleepSeconds, restartTime - now);
1876 #ifndef WIN32
1878 if(caughtSigTerm) {
1879 goto ProcessSigTerm;
1881 if((!caughtSigChld) && (!caughtSigAlarm)) {
1882 fd_set rfds;
1884 alarm(sleepSeconds);
1886 FD_ZERO(&rfds);
1887 FD_SET(fcgi_pm_pipe[0], &rfds);
1888 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1890 alarmLeft = alarm(0);
1892 callWaitPid = caughtSigChld;
1893 caughtSigChld = FALSE;
1894 callDynamicProcs = caughtSigAlarm;
1895 caughtSigAlarm = FALSE;
1897 now = time(NULL);
1900 * Dynamic fcgi process management
1902 if((callDynamicProcs) || (!callWaitPid)) {
1903 dynamic_read_msgs(read_ready);
1904 if(fcgi_dynamic_epoch == 0) {
1905 fcgi_dynamic_epoch = now;
1907 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1908 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1909 dynamic_kill_idle_fs_procs();
1910 fcgi_dynamic_epoch = now;
1914 if(!callWaitPid) {
1915 continue;
1918 /* We've caught SIGCHLD, so find out who it was using waitpid,
1919 * write a log message and update its data structure. */
1921 for (;;) {
1922 if (caughtSigTerm)
1923 goto ProcessSigTerm;
1925 childPid = waitpid(-1, &waitStatus, WNOHANG);
1927 if (childPid == -1 || childPid == 0)
1928 break;
1930 for (s = fcgi_servers; s != NULL; s = s->next) {
1931 if (s->directive == APP_CLASS_EXTERNAL)
1932 continue;
1934 if (s->directive == APP_CLASS_DYNAMIC)
1935 numChildren = dynamicMaxClassProcs;
1936 else
1937 numChildren = s->numProcesses;
1939 for (i = 0; i < numChildren; i++) {
1940 if (s->procs[i].pid == childPid)
1941 goto ChildFound;
1945 /* TODO: print something about this unknown child */
1946 continue;
1948 ChildFound:
1949 s->procs[i].pid = -1;
1951 if (s->directive == APP_CLASS_STANDARD) {
1952 /* Always restart static apps */
1953 s->procs[i].state = FCGI_START_STATE;
1954 s->numFailures++;
1956 else {
1957 s->numProcesses--;
1958 fcgi_dynamic_total_proc_count--;
1960 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1961 s->procs[i].state = FCGI_KILLED_STATE;
1963 else {
1964 /* A dynamic app died or exited without provocation from the PM */
1965 s->numFailures++;
1967 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
1968 s->procs[i].state = FCGI_START_STATE;
1969 else
1970 s->procs[i].state = FCGI_READY_STATE;
1974 if (WIFEXITED(waitStatus)) {
1975 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1976 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
1977 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1978 s->fs_path, (long) childPid, WEXITSTATUS(waitStatus));
1980 else if (WIFSIGNALED(waitStatus)) {
1981 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1982 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
1983 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1984 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus),
1985 #ifdef WCOREDUMP
1986 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1987 #else
1988 "");
1989 #endif
1991 else if (WIFSTOPPED(waitStatus)) {
1992 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1993 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
1994 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1995 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus));
1997 } /* for (;;), waitpid() */
1999 #else /* WIN32 */
2001 /* wait for an event to occur or timer expires */
2002 expire = time(NULL) + sleepSeconds;
2003 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
2005 if (dwRet == WAIT_FAILED) {
2006 /* There is something seriously wrong here */
2007 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
2008 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
2009 bTimeToDie = TRUE;
2012 if (dwRet != WAIT_TIMEOUT) {
2013 now = time(NULL);
2015 if (now < expire)
2016 alarmLeft = expire - now;
2020 * Dynamic fcgi process management
2022 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
2023 if (dwRet == MBOX_EVENT) {
2024 read_ready = 1;
2027 now = time(NULL);
2029 dynamic_read_msgs(read_ready);
2031 if(fcgi_dynamic_epoch == 0) {
2032 fcgi_dynamic_epoch = now;
2035 if ((now-fcgi_dynamic_epoch >= (int) dynamicKillInterval) ||
2036 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
2037 dynamic_kill_idle_fs_procs();
2038 fcgi_dynamic_epoch = now;
2040 read_ready = 0;
2042 else if (dwRet == WAKE_EVENT) {
2043 continue;
2045 else if (dwRet == TERM_EVENT) {
2046 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
2047 "FastCGI: Termination event received process manager shutting down");
2049 bTimeToDie = TRUE;
2050 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2052 goto ProcessSigTerm;
2054 else {
2055 // Have an received an unknown event - should not happen
2056 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
2057 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
2059 bTimeToDie = TRUE;
2060 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2062 goto ProcessSigTerm;
2065 #endif /* WIN32 */
2067 } /* for (;;), the whole shoot'n match */
2069 ProcessSigTerm:
2071 * Kill off the children, then exit.
2073 shutdown_all();
2075 #ifdef WIN32
2076 return;
2077 #else
2078 exit(0);
2079 #endif
2082 #ifdef WIN32
2083 int fcgi_pm_add_job(fcgi_pm_job *new_job)
2085 int rv = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
2087 if (rv != WAIT_OBJECT_0 && rv != WAIT_ABANDONED)
2089 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2090 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2091 return -1;
2094 new_job->next = fcgi_dynamic_mbox;
2095 fcgi_dynamic_mbox = new_job;
2097 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
2099 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2100 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
2103 return 0;
2105 #endif