seteuid() tweak for HP-UX 11. Milton L. Hankins [mlh@swl.msd.ray.com]
[mod_fastcgi.git] / fcgi_pm.c
blob26f3adf3d9c3e30071e1f565327b87325019fca7
1 /*
2 * $Id: fcgi_pm.c,v 1.34 2000/07/20 19:41:13 robs Exp $
3 */
6 #include "fcgi.h"
8 #ifdef _HPUX_SOURCE
9 #include <unistd.h>
10 #define seteuid(arg) setresuid(-1, (arg), -1)
11 #endif
13 int fcgi_dynamic_total_proc_count = 0; /* number of running apps */
14 time_t fcgi_dynamic_epoch = 0; /* last time kill_procs was
15 * invoked by process mgr */
16 time_t fcgi_dynamic_last_analyzed = 0; /* last time calculation was
17 * made for the dynamic procs */
19 static time_t now = 0;
21 /* Information about a process we are doing a blocking kill of. */
22 struct FuncData {
23 #ifndef WIN32
24 const char *lockFileName; /* name of the lock file to lock */
25 #else
26 FcgiRWLock *lock; /* reader/writer lock for dynamic app */
27 #endif
28 ServerProcess *process; /* process to issue SIGTERM to */
31 #ifdef WIN32
32 static BOOL bTimeToDie = FALSE; /* process termination flag */
33 HANDLE fcgi_event_handles[3];
34 #endif
37 #ifndef WIN32
38 static int seteuid_root(void)
40 int rc = seteuid((uid_t)0);
41 if (rc == -1) {
42 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
43 "FastCGI: seteuid(0) failed");
45 return rc;
48 static int seteuid_user(void)
50 int rc = seteuid(ap_user_id);
51 if (rc == -1) {
52 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
53 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id);
55 return rc;
57 #endif
59 static int fcgi_kill(ServerProcess *process, int sig)
61 int rc;
62 FCGIDBG2("fcgi_kill(%ld)", process->pid);
64 #ifndef WIN32
65 if (fcgi_suexec) {
66 seteuid_root();
69 rc = kill(process->pid, sig);
71 if (fcgi_suexec) {
72 seteuid_user();
75 #else
76 rc = TerminateProcess(process->handle, sig);
77 #endif
79 return rc;
82 /*******************************************************************************
83 * Send SIGTERM to each process in the server class, remove socket and lock
84 * file if appropriate. Currently this is only called when the PM is shutting
85 * down and thus memory isn't freed and sockets and files aren't closed.
87 static void kill_fs_procs(pool *p, fcgi_server *s)
89 ServerProcess *proc = s->procs;
90 int i, numChildren;
92 if (s->directive == APP_CLASS_DYNAMIC)
93 numChildren = dynamicMaxClassProcs;
94 else
95 numChildren = s->numProcesses;
97 for (i = 0; i < numChildren; i++, proc++) {
98 #ifndef WIN32
99 if (proc->pid > 0) {
100 fcgi_kill(proc, SIGTERM);
101 proc->pid = -1;
103 #else
104 if (proc->handle != INVALID_HANDLE_VALUE) {
105 fcgi_kill(proc, 1);
106 CloseHandle(proc->handle);
107 proc->handle = INVALID_HANDLE_VALUE;
108 proc->pid = -1;
110 #endif
113 /* Remove the dead lock file */
114 if (s->directive == APP_CLASS_DYNAMIC) {
115 #ifndef WIN32
116 const char *lockFileName = fcgi_util_socket_get_lock_filename(p, s->socket_path);
118 if (unlink(lockFileName) != 0) {
119 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
120 "FastCGI: unlink() failed to remove lock file \"%s\" for (dynamic) server \"%s\"",
121 lockFileName, s->fs_path);
123 #else
124 fcgi_rdwr_destroy(s->dynamic_lock);
125 #endif
128 /* Remove the socket file */
129 if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL) {
130 #ifndef WIN32
131 if (unlink(s->socket_path) != 0) {
132 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
133 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
134 s->socket_path,
135 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path);
137 #else
138 CloseHandle((HANDLE)s->listenFd);
139 #endif
141 fcgi_servers = s->next;
144 /*******************************************************************************
145 * Bind an address to a socket and set it to listen for incoming connects.
146 * The error messages are allocated from the pool p, use temp storage.
147 * Don't forget to close the socket, if an error occurs.
149 static const char *bind_n_listen(pool *p, struct sockaddr *socket_addr,
150 int socket_addr_len, int backlog, int sock)
152 #ifndef WIN32
153 if (socket_addr->sa_family == AF_UNIX) {
154 /* Remove any existing socket file.. just in case */
155 unlink(((struct sockaddr_un *)socket_addr)->sun_path);
157 else
158 #endif
160 int flag = 1;
161 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
164 /* Bind it to the socket_addr */
165 if (bind(sock, socket_addr, socket_addr_len) != 0)
166 return "bind() failed";
168 #ifndef WIN32
169 /* Twiddle permissions */
170 if (socket_addr->sa_family == AF_UNIX) {
171 if (chmod(((struct sockaddr_un *)socket_addr)->sun_path, S_IRUSR | S_IWUSR))
172 return "chmod() of socket failed";
174 #endif
176 /* Set to listen */
177 if (listen(sock, backlog) != 0)
178 return "listen() failed";
180 return NULL;
184 *----------------------------------------------------------------------
186 * dynamic_blocking_kill
188 * Block on the lock file until it is available, and then
189 * issue a kill signal to the corresponding application.
190 * Since this function is executed in the child process,
191 * _exit() is called upon completion.
193 * Inputs
194 * Pointer to the data structure containing a process id to
195 * issue a signal to and the full pathname to the lockfile
196 * that needs to be locked before the issue of the signal.
198 * Notes
199 * Memory is allocated by the caller, but is freed by this
200 * function.
202 *----------------------------------------------------------------------
204 static void dynamic_blocking_kill(void *data)
206 struct FuncData *funcData = (struct FuncData *)data;
208 #ifndef WIN32
209 int lockFd;
211 ap_assert(funcData->lockFileName);
212 if ((lockFd = open(funcData->lockFileName, O_RDWR)) < 0) {
213 /* There is something terribly wrong here */
214 } else {
215 if (fcgi_wait_for_shared_write_lock(lockFd) < 0) {
216 /* This is a major problem */
217 } else {
218 fcgi_kill(funcData->process, SIGTERM);
221 /* exit() may flush stdio buffers inherited from the parent. */
222 _exit(0);
224 #else
225 FCGIDBG1("dynamic_blocking_kill()");
226 if (fcgi_wait_for_shared_write_lock(funcData->lock) < 0) {
227 // This is a major problem
228 FCGIDBG1("fcgi_wait_for_shared_write_lock() failed >> MAJOR PROBLEM");
230 else {
231 fcgi_kill(funcData->process, 1);
232 fcgi_rdwr_unlock(funcData->lock, WRITER);
234 free(data);
235 return;
236 #endif
240 *----------------------------------------------------------------------
242 * pm_main
244 * The FastCGI process manager, which runs as a separate
245 * process responsible for:
246 * - Starting all the FastCGI proceses.
247 * - Restarting any of these processes that die (indicated
248 * by SIGCHLD).
249 * - Catching SIGTERM and relaying it to all the FastCGI
250 * processes before exiting.
252 * Inputs:
253 * Uses global variable fcgi_servers.
255 * Results:
256 * Does not return.
258 * Side effects:
259 * Described above.
261 *----------------------------------------------------------------------
263 #ifndef WIN32
264 static int caughtSigTerm = FALSE;
265 static int caughtSigChld = FALSE;
266 static int caughtSigUsr2 = FALSE;
268 static void signal_handler(int signo)
270 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
271 /* SIGUSR1 & SIGHUP are sent by apache to its process group
272 * when apache get 'em. Apache follows up (1.2.x) with attacks
273 * on each of its child processes, but we've got the KillMgr
274 * sitting between us so we never see the KILL. The main loop
275 * in ProcMgr also checks to see if the KillMgr has terminated,
276 * and if it has, we handl it as if we should shutdown too. */
277 caughtSigTerm = TRUE;
278 } else if(signo == SIGCHLD) {
279 caughtSigChld = TRUE;
280 } else if(signo == SIGALRM) {
281 caughtSigUsr2 = TRUE;
284 #endif
287 *----------------------------------------------------------------------
289 * spawn_fs_process --
291 * Fork and exec the specified fcgi process.
293 * Results:
294 * 0 for successful fork, -1 for failed fork.
296 * In case the child fails before or in the exec, the child
297 * obtains the error log by calling getErrLog, logs
298 * the error, and exits with exit status = errno of
299 * the failed system call.
301 * Side effects:
302 * Child process created.
304 *----------------------------------------------------------------------
307 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
309 #ifndef WIN32
310 pid_t child_pid;
311 int i;
312 char *dirName;
313 char *dnEnd, *failedSysCall;
315 child_pid = fork();
316 if (child_pid) {
317 return child_pid;
320 /* We're the child. We're gonna exec() so pools don't matter. */
322 dnEnd = strrchr(fs->fs_path, '/');
323 if (dnEnd == NULL) {
324 dirName = "./";
325 } else {
326 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
327 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
329 if (chdir(dirName) < 0) {
330 failedSysCall = "chdir()";
331 goto FailedSystemCallExit;
334 #ifndef __EMX__
335 /* OS/2 dosen't support nice() */
336 if (fs->processPriority != 0) {
337 if (nice(fs->processPriority) == -1) {
338 failedSysCall = "nice()";
339 goto FailedSystemCallExit;
342 #endif
344 /* Open the listenFd on spec'd fd */
345 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
346 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
348 /* Close all other open fds, except stdout/stderr. Leave these two open so
349 * FastCGI applications don't have to find and fix ALL 3rd party libs that
350 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
351 * main server error_log - @@@ provide a directive control where this goes.
353 ap_error_log2stderr(fcgi_apache_main_server);
354 dup2(STDERR_FILENO, STDOUT_FILENO);
355 for (i = 0; i < FCGI_MAX_FD; i++) {
356 if (i != FCGI_LISTENSOCK_FILENO && i != STDERR_FILENO && i != STDOUT_FILENO) {
357 close(i);
361 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
362 * install its own handler. */
363 signal(SIGPIPE, SIG_IGN);
365 if (fcgi_suexec != NULL) {
366 char *shortName = strrchr(fs->fs_path, '/') + 1;
368 /* Relinquish our root real uid powers */
369 seteuid_root();
370 setuid(ap_user_id);
372 do {
373 execle(fcgi_suexec, fcgi_suexec, fs->username, fs->group, shortName, NULL, fs->envp);
374 } while (errno == EINTR);
376 else {
377 do {
378 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
379 } while (errno == EINTR);
382 failedSysCall = "execle()";
384 FailedSystemCallExit:
385 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
386 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
387 exit(-1);
389 /* avoid an irrelevant compiler warning */
390 return(0);
392 #else
394 /* Adapted from Apache's util_script.c ap_call_exec() */
395 char *interpreter = NULL;
396 char *ext = NULL;
397 char *exename = NULL;
398 char *s = NULL;
399 char *quoted_filename;
400 char *pCommand;
401 char *pEnvBlock, *pNext;
403 int i;
404 int iEnvBlockLen;
406 file_type_e fileType;
408 STARTUPINFO si;
409 PROCESS_INFORMATION pi;
411 request_rec r;
412 pid_t pid = -1;
414 HANDLE listen_handle, mutex;
415 char * mutex_string = NULL;
417 pool * tp = ap_make_sub_pool(fcgi_config_pool);
419 if (fs->socket_path)
421 SECURITY_ATTRIBUTES sa;
423 sa.nLength = sizeof(sa);
424 sa.lpSecurityDescriptor = NULL;
425 sa.bInheritHandle = TRUE;
427 listen_handle = CreateNamedPipe(fs->socket_path, PIPE_ACCESS_DUPLEX,
428 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
429 PIPE_UNLIMITED_INSTANCES, 4096,4096,0, &sa);
430 if (listen_handle == INVALID_HANDLE_VALUE)
432 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
433 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
434 exit(0);
437 // This mutex is not really necessary, but for compatibility with
438 // the existing library, we need it.
440 mutex = ap_create_mutex(NULL);
441 if (mutex == NULL)
443 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
444 "FastCGI: can't exec server \"%s\", ap_create_mutex() failed", fs->fs_path);
445 CloseHandle(listen_handle);
446 exit(0);
449 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
451 mutex_string = ap_psprintf(tp, "_FCGI_MUTEX_=%ld", mutex);
453 else
455 listen_handle = (HANDLE) fs->listenFd;
458 memset(&si, 0, sizeof(si));
459 memset(&pi, 0, sizeof(pi));
460 memset(&r, 0, sizeof(r));
462 // Can up a fake request to pass to ap_get_win32_interpreter()
463 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
464 r.server = fcgi_apache_main_server;
465 r.filename = (char *) fs->fs_path;
466 r.pool = tp;
468 fileType = ap_get_win32_interpreter(&r, &interpreter);
470 if (fileType == eFileTypeUNKNOWN) {
471 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, fcgi_apache_main_server,
472 "FastCGI: %s is not executable; ensure interpreted scripts have "
473 "\"#!\" as their first line",
474 fs->fs_path);
475 ap_destroy_pool(tp);
476 return (pid);
480 * We have the interpreter (if there is one) and we have
481 * the arguments (if there are any).
482 * Build the command string to pass to CreateProcess.
484 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
485 if (interpreter && *interpreter) {
486 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
488 else {
489 pCommand = quoted_filename;
493 * Make child process use hPipeOutputWrite as standard out,
494 * and make sure it does not show on screen.
496 si.cb = sizeof(si);
497 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
498 si.wShowWindow = SW_HIDE;
499 si.hStdInput = listen_handle;
501 // XXX These should be open to the error_log
502 si.hStdOutput = INVALID_HANDLE_VALUE;
503 si.hStdError = INVALID_HANDLE_VALUE;
506 * Win32's CreateProcess call requires that the environment
507 * be passed in an environment block, a null terminated block of
508 * null terminated strings.
510 i = 0;
511 iEnvBlockLen = 1;
512 while (fs->envp[i]) {
513 iEnvBlockLen += strlen(fs->envp[i]) + 1;
514 i++;
517 if (fs->socket_path)
519 iEnvBlockLen += strlen(mutex_string) + 1;
522 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
524 i = 0;
525 pNext = pEnvBlock;
526 while (fs->envp[i]) {
527 strcpy(pNext, fs->envp[i]);
528 pNext = pNext + strlen(pNext) + 1;
529 i++;
532 if (fs->socket_path)
534 strcpy(pNext, mutex_string);
537 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
539 pEnvBlock,
540 ap_make_dirstr_parent(tp, fs->fs_path),
541 &si, &pi)) {
542 if (fileType == eFileTypeEXE16) {
543 /* Hack to get 16-bit CGI's working. It works for all the
544 * standard modules shipped with Apache. pi.dwProcessId is 0
545 * for 16-bit CGIs and all the Unix specific code that calls
546 * ap_call_exec interprets this as a failure case. And we can't
547 * use -1 either because it is mapped to 0 by the caller.
549 pid = -2;
551 else {
552 pid = pi.dwProcessId;
553 process->handle = pi.hProcess;
554 CloseHandle(pi.hThread);
558 // We don't need these anymore..
559 if (fs->socket_path)
561 CloseHandle(listen_handle);
562 CloseHandle(mutex);
565 ap_destroy_pool(tp);
567 return pid;
569 #endif
572 #ifndef WIN32
573 static void reduce_priveleges(void)
575 char *name;
577 if (geteuid() != 0)
578 return;
580 #ifndef __EMX__
581 /* Get username if passed as a uid */
582 if (ap_user_name[0] == '#') {
583 uid_t uid = atoi(&ap_user_name[1]);
584 struct passwd *ent = getpwuid(uid);
586 if (ent == NULL) {
587 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
588 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
589 "you probably need to modify the User directive", (unsigned)uid);
590 exit(1);
592 name = ent->pw_name;
594 else
595 name = ap_user_name;
597 /* Change Group */
598 if (setgid(ap_group_id) == -1) {
599 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
600 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
601 exit(1);
604 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
606 /* Initialize supplementary groups */
607 if (initgroups(name, ap_group_id) == -1) {
608 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
609 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
610 name, (unsigned)ap_group_id);
611 exit(1);
613 #endif /* __EMX__ */
615 /* Change User */
616 if (fcgi_suexec) {
617 if (seteuid_user() == -1) {
618 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
619 "FastCGI: process manager exiting, failed to reduce priveleges");
620 exit(1);
623 else {
624 if (setuid(ap_user_id) == -1) {
625 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
626 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
627 exit(1);
632 /*************
633 * Change the name of this process - best we can easily.
635 static void change_process_name(const char * const name)
637 strncpy(ap_server_argv0, name, strlen(ap_server_argv0));
639 #endif
641 static void schedule_start(fcgi_server *s, int proc)
643 /* If we've started one recently, don't register another */
644 time_t time_passed = now - s->restartTime;
646 if ((s->procs[proc].pid && time_passed < (int) s->restartDelay)
647 || (s->procs[proc].pid == 0 && time_passed < (int) s->initStartDelay))
649 FCGIDBG6("ignore_job: slot=%d, pid=%ld, time_passed=%ld, initStartDelay=%ld, restartDelay=%ld", proc, s->procs[proc].pid, time_passed, s->initStartDelay, s->restartDelay);
650 return;
653 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
654 s->procs[proc].state = STATE_NEEDS_STARTING;
655 if (proc == (int)dynamicMaxClassProcs - 1) {
656 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
657 "FastCGI: scheduled the %sstart of the last (dynamic) server "
658 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
659 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
664 *----------------------------------------------------------------------
666 * dynamic_read_msgs
668 * Removes the records written by request handlers and decodes them.
669 * We also update the data structures to reflect the changes.
671 *----------------------------------------------------------------------
674 static void dynamic_read_msgs(int read_ready)
676 fcgi_server *s;
678 #ifndef WIN32
679 int rc;
680 static int buflen = 0;
681 static char buf[FCGI_MSGS_BUFSIZE + 1];
682 char *ptr1, *ptr2, opcode;
683 char execName[FCGI_MAXPATH + 1];
684 char user[MAX_USER_NAME_LEN + 2];
685 char group[MAX_GID_CHAR_LEN + 1];
686 unsigned long q_usec = 0UL, req_usec = 0UL;
687 #else
688 fcgi_pm_job *joblist = NULL;
689 fcgi_pm_job *cjob = NULL;
690 #endif
692 pool *sp, *tp;
694 #ifndef WIN32
695 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
696 #endif
699 * To prevent the idle application from running indefinitely, we
700 * check the timer and if it is expired, we recompute the values
701 * for each running application class. Then, when REQ_COMPLETE
702 * message is recieved, only updates are made to the data structures.
704 if (fcgi_dynamic_last_analyzed == 0) {
705 fcgi_dynamic_last_analyzed = now;
707 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
708 for (s = fcgi_servers; s != NULL; s = s->next) {
709 if (s->directive != APP_CLASS_DYNAMIC)
710 break;
711 /* XXX what does this adjustment do? */
712 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
713 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
714 s->totalConnTime = 0UL;
715 s->totalQueueTime = 0UL;
719 if (read_ready <= 0) {
720 return;
723 #ifndef WIN32
724 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
725 if (rc <= 0) {
726 if (!caughtSigTerm) {
727 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
728 "FastCGI: read() from pipe failed (%d)", rc);
730 return;
732 buflen += rc;
733 buf[buflen] = '\0';
734 #else
735 if (ap_acquire_mutex(fcgi_dynamic_mbox_mutex) != MULTI_OK) {
736 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
737 "FastCGI: Failed to aquire the dynamic mbox mutex!");
740 joblist = fcgi_dynamic_mbox;
741 fcgi_dynamic_mbox = NULL;
743 if (! ap_release_mutex(fcgi_dynamic_mbox_mutex)) {
744 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
745 "FastCGI: Failed to release the dynamic mbox mutex!");
748 cjob = joblist;
749 #endif
751 tp = ap_make_sub_pool(fcgi_config_pool);
753 #ifndef WIN32
754 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
755 int scan_failed = 0;
757 ptr2 = strchr(ptr1, '*');
758 if (ptr2) {
759 *ptr2++ = '\0';
761 else {
762 break;
765 opcode = *ptr1;
767 switch (opcode) {
768 case PLEASE_START:
769 if (sscanf(ptr1, "%c %s %16s %15s",
770 &opcode, execName, user, group) != 4)
772 scan_failed = 1;
774 break;
775 case CONN_TIMEOUT:
776 if (sscanf(ptr1, "%c %s %16s %15s",
777 &opcode, execName, user, group) != 4)
779 scan_failed = 1;
781 break;
782 case REQ_COMPLETE:
783 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
784 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
786 scan_failed = 1;
788 break;
789 default:
790 scan_failed = 1;
791 break;
794 if (scan_failed) {
795 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
796 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
797 goto NextJob;
799 #else
800 /* Update data structures for processing */
801 while (cjob != NULL) {
802 joblist = cjob->next;
803 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
804 #endif
806 #ifndef WIN32
807 s = fcgi_util_fs_get(execName, user, group);
808 #else
809 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
810 #endif
812 #ifndef WIN32
813 if (s==NULL && opcode != REQ_COMPLETE)
814 #else
815 if (s==NULL && cjob->id != REQ_COMPLETE)
816 #endif
818 #ifndef WIN32
819 int fd;
820 const char *err, *lockPath;
821 #endif
823 /* Create a perm subpool to hold the new server data,
824 * we can destroy it if something doesn't pan out */
825 sp = ap_make_sub_pool(fcgi_config_pool);
827 /* Create a new "dynamic" server */
828 s = fcgi_util_fs_new(sp);
829 s->directive = APP_CLASS_DYNAMIC;
830 s->restartDelay = dynamicRestartDelay;
831 s->listenQueueDepth = dynamicListenQueueDepth;
832 s->initStartDelay = dynamicInitStartDelay;
833 s->envp = dynamicEnvp;
834 #ifdef WIN32
835 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
836 #else
837 s->fs_path = ap_pstrdup(sp, execName);
838 #endif
839 ap_getparents(s->fs_path);
840 ap_no2slash(s->fs_path);
841 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
843 #ifndef WIN32
844 /* Create socket file's path */
845 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
846 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
848 /* Create sockaddr, prealloc it so it won't get created in tp */
849 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
850 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
851 &s->socket_addr_len, s->socket_path);
852 if (err) {
853 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
854 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
855 goto BagNewServer;
858 /* Create the socket */
859 if ((s->listenFd = ap_psocket(sp, s->socket_addr->sa_family, SOCK_STREAM, 0)) < 0) {
860 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
861 "FastCGI: can't create (dynamic) server \"%s\": socket() failed", execName);
862 goto BagNewServer;
865 /* bind() and listen() */
866 err = bind_n_listen(tp, s->socket_addr, s->socket_addr_len,
867 s->listenQueueDepth, s->listenFd);
868 if (err) {
869 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
870 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
871 goto BagNewServer;
874 /* Create the lock file */
875 lockPath = fcgi_util_socket_get_lock_filename(tp, s->socket_path);
876 fd = ap_popenf(tp, lockPath,
877 O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
878 if (fd < 0) {
879 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
880 "FastCGI: can't create (dynamic) server \"%s\": can't open lock file \"%s\": popenf() failed",
881 execName, lockPath);
882 goto BagNewServer;
884 ap_pclosef(tp, fd);
886 /* If suexec is being used, config user/group info */
887 if (fcgi_suexec) {
888 if (user[0] == '~') {
889 /* its a user dir uri, the rest is a username, not a uid */
890 struct passwd *pw = getpwnam(&user[1]);
892 if (!pw) {
893 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
894 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for suexec: getpwnam(%s) failed",
895 execName, &user[1]);
896 goto BagNewServer;
898 s->uid = pw->pw_uid;
899 s->user = ap_pstrdup(sp, user);
900 s->username = s->user;
902 s->gid = pw->pw_gid;
903 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
905 else {
906 struct passwd *pw;
908 s->uid = (uid_t)atol(user);
909 pw = getpwuid(s->uid);
910 if (!pw) {
911 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
912 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for suexec: getwpuid(%ld) failed",
913 execName, (long)s->uid);
914 goto BagNewServer;
916 s->user = ap_pstrdup(sp, user);
917 s->username = ap_pstrdup(sp, pw->pw_name);
919 s->gid = (gid_t)atol(group);
920 s->group = ap_pstrdup(sp, group);
923 #else
924 /* Create socket file's path */
925 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
926 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
927 s->listenFd = 0;
929 /* Create the application lock */
930 if ((s->dynamic_lock = fcgi_rdwr_create()) == NULL) {
931 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
932 "FastCGI: can;t create (dynamic) server \"%s\": fcgi_rdwr_create() failed",
933 cjob->fs_path);
934 goto BagNewServer;
937 // Lock it until the first process is started
938 fcgi_rdwr_lock(s->dynamic_lock, WRITER);
939 #endif
941 fcgi_util_fs_add(s);
943 else {
944 #ifndef WIN32
945 if (opcode == PLEASE_START) {
946 #else
947 if(cjob->id==PLEASE_START) {
948 #endif
949 if (dynamicAutoUpdate) {
950 /* Check to see if the binary has changed. If so,
951 * kill the FCGI application processes, and
952 * restart them.
954 struct stat stbuf;
955 unsigned int i;
957 #ifndef WIN32
958 if ((stat(execName, &stbuf)==0) &&
959 #else
960 if ((stat(cjob->fs_path, &stbuf)==0) &&
961 #endif
962 (stbuf.st_mtime > s->restartTime)) {
963 /* kill old server(s) */
964 for (i = 0; i < dynamicMaxClassProcs; i++) {
965 if (s->procs[i].pid > 0) {
966 fcgi_kill(&s->procs[i], SIGTERM);
970 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
971 "FastCGI: restarting server \"%s\" processes, newer version found",
972 #ifndef WIN32
973 execName);
974 #else
975 cjob->fs_path);
976 #endif
979 /* If dynamicAutoRestart, don't mark any new processes
980 * for starting because we probably got the
981 * PLEASE_START due to dynamicAutoUpdate and the ProcMgr
982 * will be restarting all of those we just killed.
984 if (dynamicAutoRestart)
985 goto NextJob;
986 } else {
987 /* we've been asked to start a process--only start
988 * it if we're not already running at least one
989 * instance.
991 unsigned int i;
993 for (i = 0; i < dynamicMaxClassProcs; i++) {
994 if (s->procs[i].state == STATE_STARTED)
995 break;
997 /* if already running, don't start another one */
998 if (i < dynamicMaxClassProcs) {
999 goto NextJob;
1004 #ifndef WIN32
1005 switch (opcode)
1006 #else
1007 switch (cjob->id)
1008 #endif
1010 unsigned int i;
1012 case PLEASE_START:
1013 case CONN_TIMEOUT:
1015 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1017 * Extra instances should have been
1018 * terminated beforehand, probably need
1019 * to increase ProcessSlack parameter
1021 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1022 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1023 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1024 goto NextJob;
1027 /* find next free slot */
1028 for (i = 0; i < dynamicMaxClassProcs; i++)
1030 if (s->procs[i].state == STATE_NEEDS_STARTING)
1032 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1033 break;
1035 else if (s->procs[i].state == STATE_STARTED)
1037 continue;
1040 schedule_start(s, i);
1041 break;
1044 #ifdef FCGI_DEBUG
1045 if (i >= dynamicMaxClassProcs) {
1046 FCGIDBG1("ignore_job: slots are max'd");
1048 #endif
1049 break;
1050 case REQ_COMPLETE:
1051 /* only record stats if we have a structure */
1052 if (s) {
1053 #ifndef WIN32
1054 s->totalConnTime += req_usec;
1055 s->totalQueueTime += q_usec;
1056 #else
1057 s->totalConnTime += cjob->start_time;
1058 s->totalQueueTime += cjob->qsec;
1059 #endif
1061 break;
1064 NextJob:
1066 #ifdef WIN32
1067 /* Cleanup job data */
1068 free(cjob->fs_path);
1069 free(cjob->user);
1070 free(cjob->group);
1071 free(cjob);
1072 cjob = joblist;
1073 #endif
1075 continue;
1077 BagNewServer:
1078 ap_destroy_pool(sp);
1080 #ifdef WIN32
1081 free(cjob->fs_path);
1082 free(cjob);
1083 cjob = joblist;
1084 #endif
1087 #ifndef WIN32
1088 if (ptr1 == buf) {
1089 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1090 "FastCGI: really bogus message: \"%s\"", ptr1);
1091 ptr1 += strlen(buf);
1094 buflen -= ptr1 - buf;
1095 if (buflen) {
1096 memmove(buf, ptr1, buflen);
1098 #endif
1100 ap_destroy_pool(tp);
1104 *----------------------------------------------------------------------
1106 * dynamic_kill_idle_fs_procs
1108 * Implement a kill policy for the dynamic FastCGI applications.
1109 * We also update the data structures to reflect the changes.
1111 * Side effects:
1112 * Processes are marked for deletion possibly killed.
1114 *----------------------------------------------------------------------
1116 static void dynamic_kill_idle_fs_procs(void)
1118 fcgi_server *s;
1119 struct FuncData *funcData = NULL;
1120 unsigned long connTime; /* server's smoothed running time, or
1121 * if that's 0, the current total */
1122 unsigned long totalTime; /* maximum number of microseconds that all
1123 * of a server's running processes together
1124 * could have spent running since the
1125 * last check */
1126 double loadFactor; /* percentage, 0-100, of totalTime that
1127 * the processes actually used */
1128 unsigned int i, victims = 0;
1129 #ifndef WIN32
1130 const char *lockFileName;
1131 int lockFd;
1132 pid_t pid;
1133 #endif
1134 pool *tp = ap_make_sub_pool(fcgi_config_pool);
1136 /* pass 1 - locate and mark all victims */
1137 for(s=fcgi_servers; s!=NULL; s=s->next) {
1138 /* Only kill dynamic apps */
1139 if (s->directive != APP_CLASS_DYNAMIC)
1140 continue;
1142 /* If the number of non-victims is less than or equal to
1143 the minimum that may be running without being killed off,
1144 don't select any more victims. */
1145 if ((fcgi_dynamic_total_proc_count - victims) <= (int) dynamicMinProcs) {
1146 break;
1149 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1150 totalTime = (s->numProcesses)*(now - fcgi_dynamic_epoch)*1000000 + 1;
1152 /* XXX producing a heavy load with one client, I haven't been
1153 able to achieve a loadFactor greater than 0.5. Perhaps this
1154 should be scaled up by another order of magnitude or two. */
1155 loadFactor = connTime/totalTime*100.0;
1157 if ((s->numProcesses > 1
1158 && s->numProcesses/(s->numProcesses - 1)*loadFactor < dynamicThreshholdN)
1159 || (s->numProcesses == 1 && loadFactor < dynamicThreshhold1))
1161 int got_one = 0;
1163 for (i = 0; !got_one && i < dynamicMaxClassProcs; ++i) {
1164 if (s->procs[i].state == STATE_NEEDS_STARTING) {
1165 s->procs[i].state = STATE_READY;
1166 got_one = 1;
1168 else if (s->procs[i].state == STATE_VICTIM || s->procs[i].state == STATE_KILL) {
1169 got_one = 1;
1173 for (i = 0; !got_one && i < dynamicMaxClassProcs; ++i) {
1174 if (s->procs[i].state == STATE_STARTED) {
1175 s->procs[i].state = STATE_KILL;
1176 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1177 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination scheduled",
1178 s->fs_path, s->procs[i].pid);
1179 victims++;
1180 got_one = 1;
1186 /* pass 2 - kill procs off */
1187 for(s=fcgi_servers; s!=NULL; s=s->next) {
1188 /* Only kill dynamic apps */
1189 if (s->directive != APP_CLASS_DYNAMIC)
1190 continue;
1192 for(i = 0; i < dynamicMaxClassProcs; i++) {
1193 if (s->procs[i].state == STATE_KILL) {
1194 #ifndef WIN32
1195 lockFileName = fcgi_util_socket_get_lock_filename(tp, s->socket_path);
1196 if ((lockFd = ap_popenf(tp, lockFileName, O_RDWR, 0))<0) {
1198 * If we need to kill an application and the
1199 * corresponding lock file does not exist, then
1200 * that means we are in big trouble here
1202 /*@@@ this should be logged, but since all the lock
1203 * file stuff will be tossed, I'll leave it now */
1204 ap_pclosef(tp, lockFd);
1205 continue;
1208 if (fcgi_get_exclusive_write_lock_no_wait(lockFd) < 0) {
1209 #else
1210 if (fcgi_get_exclusive_write_lock_no_wait(s->dynamic_lock) < 0) {
1211 #endif
1212 FCGIDBG2("fcgi_get_exclusive_write_lock_no_wait() failed (%ld)", GetLastError());
1214 * Unable to lock the lockfile, indicative
1215 * of WS performing operation with the given
1216 * application class. The simplest solution
1217 * is to spawn off another process and block
1218 * on lock to kill it. This is under assumptions
1219 * that fork() is not very costly and this
1220 * situation occurs very rarely, which it should
1222 #ifndef WIN32
1223 funcData = ap_pcalloc(tp, sizeof(struct FuncData));
1224 funcData->lockFileName = lockFileName;
1225 #else
1226 funcData = malloc(sizeof(struct FuncData));
1227 funcData->lock = s->dynamic_lock;
1228 #endif
1229 funcData->process = &s->procs[i];
1231 #ifndef WIN32
1232 if((pid=fork())<0) {
1233 /*@@@ this should be logged, but since all the lock
1234 * file stuff will be tossed, I'll leave it now */
1235 ap_pclosef(tp, lockFd);
1236 continue;
1237 } else if(pid==0) {
1238 /* child */
1240 /* rename the process for ps - best we can easily */
1241 change_process_name("fcgiBlkKill");
1243 dynamic_blocking_kill(funcData);
1244 } else {
1245 /* parent */
1246 s->procs[i].state = STATE_VICTIM;
1247 ap_pclosef(tp, lockFd);
1249 #else
1250 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) dynamic_blocking_kill, (LPVOID) funcData, 0, NULL);
1251 s->procs[i].state = STATE_VICTIM;
1252 #endif
1254 else {
1255 FCGIDBG1("fcgi_get_exclusive_write_lock_no_wait() succeeded");
1256 s->procs[i].state = STATE_VICTIM;
1257 #ifndef WIN32
1258 fcgi_kill(&s->procs[i], SIGTERM);
1259 ap_pclosef(tp, lockFd);
1260 #else
1261 fcgi_kill(&s->procs[i], 1);
1262 fcgi_rdwr_unlock(s->dynamic_lock, WRITER);
1263 #endif
1268 ap_destroy_pool(tp);
1271 #ifdef WIN32
1273 // This is a little bogus, there's gotta be a better way to do this
1274 // Can we use WaitForMultipleObjects()
1275 #define FCGI_PROC_WAIT_TIME 100
1277 void child_wait_thread(void *dummy) {
1278 fcgi_server *s;
1279 DWORD dwRet = WAIT_TIMEOUT;
1280 int numChildren;
1281 int i;
1282 int waited;
1284 while (!bTimeToDie) {
1285 waited = 0;
1287 for (s = fcgi_servers; s != NULL; s = s->next) {
1288 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1289 continue;
1291 if (s->directive == APP_CLASS_DYNAMIC) {
1292 numChildren = dynamicMaxClassProcs;
1294 else {
1295 numChildren = s->numProcesses;
1298 for (i=0; i < numChildren; i++) {
1299 if (s->procs[i].handle != INVALID_HANDLE_VALUE) {
1300 /* timeout is currently set for 100 miliecond */
1301 /* it may need t longer or user customizable */
1302 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1304 waited = 1;
1306 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1307 /* a child fs has died */
1308 /* mark the child as dead */
1310 if (s->directive == APP_CLASS_STANDARD) {
1311 /* restart static app */
1312 s->procs[i].state = STATE_NEEDS_STARTING;
1313 s->numFailures++;
1315 else {
1316 s->numProcesses--;
1317 fcgi_dynamic_total_proc_count--;
1318 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1320 if (s->procs[i].state == STATE_VICTIM) {
1321 s->procs[i].state = STATE_KILLED;
1323 else {
1324 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1325 s->numFailures++;
1327 if (dynamicAutoRestart) {
1328 s->procs[i].state = STATE_NEEDS_STARTING;
1330 else {
1331 s->procs[i].state = STATE_READY;
1336 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1337 "FastCGI:%s server \"%s\" (pid %d) terminated",
1338 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1339 s->fs_path, s->procs[i].pid);
1341 CloseHandle(s->procs[i].handle);
1342 s->procs[i].handle = INVALID_HANDLE_VALUE;
1343 s->procs[i].pid = -1;
1345 /* wake up the main thread */
1346 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1351 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1354 #endif
1356 #ifndef WIN32
1357 static void setup_signals(void)
1359 sigset_t mask;
1360 struct sigaction sa;
1362 /* Ignore USR2 */
1363 sigemptyset(&mask);
1364 sigaddset(&mask, SIGUSR2);
1365 sigprocmask(SIG_BLOCK, &mask, NULL);
1367 /* Setup handlers */
1369 sa.sa_handler = signal_handler;
1370 sigemptyset(&sa.sa_mask);
1371 sa.sa_flags = 0;
1373 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1374 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1375 "sigaction(SIGTERM) failed");
1377 /* httpd restart */
1378 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1379 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1380 "sigaction(SIGHUP) failed");
1382 /* httpd graceful restart */
1383 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1384 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1385 "sigaction(SIGUSR1) failed");
1387 /* read messages from request handlers - kill interval expired */
1388 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1389 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1390 "sigaction(SIGALRM) failed");
1392 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1393 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1394 "sigaction(SIGCHLD) failed");
1397 #endif
1399 #ifndef WIN32
1400 int fcgi_pm_main(void *dummy, child_info *info)
1401 #else
1402 void fcgi_pm_main(void *dummy)
1403 #endif
1405 fcgi_server *s;
1406 unsigned int i;
1407 int read_ready = 0;
1408 int alarmLeft = 0;
1409 pool *tp;
1410 const char *err;
1412 #ifdef WIN32
1413 DWORD dwRet;
1414 int first_time = 1;
1415 HANDLE wait_thread = INVALID_HANDLE_VALUE;
1416 #else
1417 int callWaitPid, callDynamicProcs;
1418 #endif
1420 #ifdef WIN32
1421 // Add SystemRoot to the dyanmic environment
1422 char ** envp = dynamicEnvp;
1423 for (i = 0; *envp; ++i) {
1424 ++envp;
1426 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1428 #else
1429 reduce_priveleges();
1431 close(fcgi_pm_pipe[1]);
1432 change_process_name("fcgi-pm");
1433 setup_signals();
1435 if (fcgi_suexec) {
1436 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1437 "FastCGI: suEXEC mechanism enabled (wrapper: %s)", fcgi_suexec);
1439 #endif
1441 /* Initialize AppClass */
1442 tp = ap_make_sub_pool(fcgi_config_pool);
1443 for(s = fcgi_servers; s != NULL; s = s->next) {
1444 if (s->directive == APP_CLASS_EXTERNAL)
1445 continue;
1447 #ifdef WIN32
1448 if (s->socket_path)
1450 s->listenFd = 0;
1452 else
1453 #endif
1455 /* Create the socket */
1456 s->listenFd = ap_psocket(fcgi_config_pool, s->socket_addr->sa_family, SOCK_STREAM, 0);
1457 if (s->listenFd < 0) {
1458 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1459 "FastCGI: server \"%s\" disabled, socket() failed", s->fs_path);
1460 continue;
1463 /* bind() and listen() */
1464 err = bind_n_listen(tp, s->socket_addr, s->socket_addr_len,
1465 s->listenQueueDepth, s->listenFd);
1466 if (err) {
1467 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1468 "FastCGI: server \"%s\" disabled: %s",
1469 s->fs_path, err);
1470 ap_pclosesocket(fcgi_config_pool, s->listenFd);
1471 s->listenFd = -1;
1472 continue;
1476 for (i = 0; i < s->numProcesses; i++)
1477 s->procs[i].state = STATE_NEEDS_STARTING;
1480 ap_destroy_pool(tp);
1482 #ifdef WIN32
1483 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1484 "FastCGI: process manager initialized");
1485 #else
1486 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1487 "FastCGI: process manager initialized (pid %ld)", (long)getpid());
1488 #endif
1490 now = time(NULL);
1493 * Loop until SIGTERM
1495 for (;;) {
1496 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1497 #ifdef WIN32
1498 time_t expire;
1499 #else
1500 pid_t childPid;
1501 int waitStatus;
1502 #endif
1503 unsigned int numChildren;
1506 * If we came out of sigsuspend() for any reason other than
1507 * SIGALRM, pick up where we left off.
1509 if (alarmLeft)
1510 sleepSeconds = alarmLeft;
1513 * Examine each configured AppClass for a process that needs
1514 * starting. Compute the earliest time when the start should
1515 * be attempted, starting it now if the time has passed. Also,
1516 * remember that we do NOT need to restart externally managed
1517 * FastCGI applications.
1519 for (s = fcgi_servers; s != NULL; s = s->next) {
1520 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1521 continue;
1523 if (s->directive == APP_CLASS_DYNAMIC) {
1524 numChildren = dynamicMaxClassProcs;
1525 } else {
1526 numChildren = s->numProcesses;
1529 for (i = 0; i < numChildren; i++) {
1530 if ((s->procs[i].pid <= 0) &&
1531 (s->procs[i].state == STATE_NEEDS_STARTING))
1533 time_t restartTime;
1535 if (s->procs[i].pid == 0) {
1536 restartTime = s->restartTime + s->initStartDelay;
1537 } else {
1538 restartTime = s->restartTime + s->restartDelay;
1541 if (restartTime <= now) {
1542 int restart = (s->procs[i].pid < 0);
1544 s->restartTime = now;
1546 #ifndef WIN32
1547 if (caughtSigTerm) {
1548 goto ProcessSigTerm;
1550 #endif
1551 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1552 if (s->procs[i].pid <= 0) {
1553 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1554 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1555 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1556 s->fs_path);
1558 sleepSeconds = min(sleepSeconds,
1559 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1561 ap_assert(s->procs[i].pid < 0);
1562 break;
1565 if (s->directive == APP_CLASS_DYNAMIC) {
1566 s->numProcesses++;
1567 fcgi_dynamic_total_proc_count++;
1568 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1569 #ifdef WIN32
1570 if (i == 0 && !restart) {
1571 fcgi_rdwr_unlock(s->dynamic_lock, WRITER);
1573 #endif
1576 s->procs[i].state = STATE_STARTED;
1578 if (restart)
1579 s->numRestarts++;
1581 if (fcgi_suexec != NULL) {
1582 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1583 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1584 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1585 s->fs_path, (long)s->uid, (long)s->gid,
1586 restart ? "re" : "", (long)s->procs[i].pid);
1588 else {
1589 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1590 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1591 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1592 s->fs_path, restart ? "re" : "", (long)s->procs[i].pid);
1594 ap_assert(s->procs[i].pid > 0);
1595 } else {
1596 sleepSeconds = min(sleepSeconds, restartTime - now);
1602 #ifndef WIN32
1603 if(caughtSigTerm) {
1604 goto ProcessSigTerm;
1606 if((!caughtSigChld) && (!caughtSigUsr2)) {
1607 fd_set rfds;
1609 alarm(sleepSeconds);
1611 FD_ZERO(&rfds);
1612 FD_SET(fcgi_pm_pipe[0], &rfds);
1613 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1615 alarmLeft = alarm(0);
1617 callWaitPid = caughtSigChld;
1618 caughtSigChld = FALSE;
1619 callDynamicProcs = caughtSigUsr2;
1620 caughtSigUsr2 = FALSE;
1622 now = time(NULL);
1625 * Dynamic fcgi process management
1627 if((callDynamicProcs) || (!callWaitPid)) {
1628 dynamic_read_msgs(read_ready);
1629 if(fcgi_dynamic_epoch == 0) {
1630 fcgi_dynamic_epoch = now;
1632 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1633 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1634 dynamic_kill_idle_fs_procs();
1635 fcgi_dynamic_epoch = now;
1639 if(!callWaitPid) {
1640 continue;
1643 /* We've caught SIGCHLD, so find out who it was using waitpid,
1644 * write a log message and update its data structure. */
1646 for (;;) {
1647 if (caughtSigTerm)
1648 goto ProcessSigTerm;
1650 childPid = waitpid(-1, &waitStatus, WNOHANG);
1652 if (childPid == -1 || childPid == 0)
1653 break;
1655 for (s = fcgi_servers; s != NULL; s = s->next) {
1656 if (s->directive == APP_CLASS_EXTERNAL)
1657 continue;
1659 if (s->directive == APP_CLASS_DYNAMIC)
1660 numChildren = dynamicMaxClassProcs;
1661 else
1662 numChildren = s->numProcesses;
1664 for (i = 0; i < numChildren; i++) {
1665 if (s->procs[i].pid == childPid)
1666 goto ChildFound;
1670 /* @@@ This (comment) needs to go away when dynamic gets cleaned up.
1671 * If we get to this point, we have detected the
1672 * termination of the process that was spawned off by
1673 * the process manager to do a blocking kill above. */
1674 continue;
1676 ChildFound:
1677 s->procs[i].pid = -1;
1679 if (s->directive == APP_CLASS_STANDARD) {
1680 /* Always restart static apps */
1681 s->procs[i].state = STATE_NEEDS_STARTING;
1682 s->numFailures++;
1684 else {
1685 s->numProcesses--;
1686 fcgi_dynamic_total_proc_count--;
1688 if (s->procs[i].state == STATE_VICTIM) {
1689 s->procs[i].state = STATE_KILLED;
1691 else {
1692 /* A dynamic app died or exited without provacation from the PM */
1693 s->numFailures++;
1695 if (dynamicAutoRestart || s->numProcesses <= 0)
1696 s->procs[i].state = STATE_NEEDS_STARTING;
1697 else
1698 s->procs[i].state = STATE_READY;
1702 if (WIFEXITED(waitStatus)) {
1703 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1704 "FastCGI:%s server \"%s\" (pid %d) terminated by calling exit with status '%d'",
1705 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1706 s->fs_path, (int)childPid, WEXITSTATUS(waitStatus));
1708 else if (WIFSIGNALED(waitStatus)) {
1709 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1710 "FastCGI:%s server \"%s\" (pid %d) terminated due to uncaught signal '%d' (%s)%s",
1711 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1712 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)],
1713 #ifdef WCOREDUMP
1714 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
1715 #else
1716 "");
1717 #endif
1719 else if (WIFSTOPPED(waitStatus)) {
1720 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1721 "FastCGI:%s server \"%s\" (pid %d) stopped due to uncaught signal '%d' (%s)",
1722 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1723 s->fs_path, (int)childPid, WTERMSIG(waitStatus), SYS_SIGLIST[WTERMSIG(waitStatus)]);
1725 } /* for (;;), waitpid() */
1726 #else
1727 if (first_time) {
1728 /* Start the child wait thread */
1729 wait_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)child_wait_thread, NULL, 0, NULL);
1730 first_time = 0;
1733 /* wait for an event to occur or timer expires */
1734 expire = time(NULL) + sleepSeconds;
1735 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
1737 if (dwRet == WAIT_FAILED) {
1738 /* There is something seriously wrong here */
1739 ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, fcgi_apache_main_server,
1740 "FastDGI: WaitForMultipleObjects on event handles -- pm is shuting down");
1741 bTimeToDie = TRUE;
1744 if (dwRet != WAIT_TIMEOUT) {
1745 now = time(NULL);
1747 if (now < expire)
1748 alarmLeft = expire - now;
1752 * Dynamic fcgi process management
1754 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
1755 if (dwRet == MBOX_EVENT) {
1756 read_ready = 1;
1759 now = time(NULL);
1761 dynamic_read_msgs(read_ready);
1763 if(fcgi_dynamic_epoch == 0) {
1764 fcgi_dynamic_epoch = now;
1767 if (((long)(now-fcgi_dynamic_epoch) >= (int)dynamicKillInterval) ||
1768 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
1769 dynamic_kill_idle_fs_procs();
1770 fcgi_dynamic_epoch = now;
1772 read_ready = 0;
1774 else if (dwRet == WAKE_EVENT) {
1775 continue;
1777 else if (dwRet == TERM_EVENT) {
1778 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
1779 "FastCGI: Termination event received process manager shutting down");
1780 bTimeToDie = TRUE;
1782 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1783 goto ProcessSigTerm;
1785 else {
1786 // Have an received an unknown event - should not happen
1787 ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, fcgi_apache_main_server,
1788 "FastCGI: WaitForMultipleobjects return an unrecognized event");
1789 bTimeToDie = TRUE;
1790 dwRet = WaitForSingleObject(wait_thread, INFINITE);
1791 goto ProcessSigTerm;
1793 #endif
1794 } /* for (;;), the whole shoot'n match */
1796 ProcessSigTerm:
1798 * Kill off the children, then exit.
1800 while (fcgi_servers != NULL) {
1801 kill_fs_procs(fcgi_config_pool, fcgi_servers);
1804 #ifdef WIN32
1805 return;
1806 #else
1807 exit(0);
1808 #endif
1811 #ifdef WIN32
1812 int fcgi_pm_add_job(fcgi_pm_job *new_job) {
1814 if (new_job == NULL)
1815 return 0;
1817 if (ap_acquire_mutex(fcgi_dynamic_mbox_mutex) != MULTI_OK) {
1818 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1819 "FastCGI: Failed to aquire the dynamic mbox mutex!");
1822 new_job->next = fcgi_dynamic_mbox;
1823 fcgi_dynamic_mbox = new_job;
1825 if (! ap_release_mutex(fcgi_dynamic_mbox_mutex)) {
1826 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1827 "FastCGI: Failed to release the dynamic mbox mutex!");
1830 return 1;
1832 #endif