"Fossies" - the Fresh Open Source Software Archive 
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
See also the latest
Fossies "Diffs" side-by-side code changes report for "fcgi_pm.c":
2.4.6_vs_2.4.7-0910052141.
1 /*
2 * $Id: fcgi_pm.c,v 1.96 2009/09/29 00:34:10 robs Exp $
3 */
4
5
6 #include "fcgi.h"
7
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
14
15 #ifndef WIN32
16 #include <utime.h>
17 #endif
18
19 #ifdef _HPUX_SOURCE
20 #include <unistd.h>
21 #define seteuid(arg) setresuid(-1, (arg), -1)
22 #endif
23
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 */
29
30 static time_t now = 0;
31
32 #ifdef WIN32
33 #ifdef APACHE2
34 #include "mod_cgi.h"
35 #include "apr_version.h"
36 #endif
37 #pragma warning ( disable : 4100 4102 )
38 static BOOL bTimeToDie = FALSE; /* process termination flag */
39 HANDLE fcgi_event_handles[3];
40 #ifndef SIGKILL
41 #define SIGKILL 9
42 #endif
43 #endif
44
45
46 #ifndef WIN32
47 static int seteuid_root(void)
48 {
49 int rc = seteuid(getuid());
50 if (rc) {
51 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
52 "FastCGI: seteuid(0) failed");
53 }
54 return rc;
55 }
56
57 static int seteuid_user(void)
58 {
59 int rc = seteuid(ap_user_id);
60 if (rc) {
61 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
62 "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id);
63 }
64 return rc;
65 }
66 #endif
67
68 /*
69 * Signal the process to exit. How (or if) the process responds
70 * depends on the FastCGI application library (esp. on Win32) and
71 * possibly application code (signal handlers and whether or not
72 * SA_RESTART is on). At any rate, we send the signal with the
73 * hopes that the process will exit on its own. Later, as we
74 * review the state of application processes, if we see one marked
75 * for death, but that hasn't died within a specified period of
76 * time, fcgi_kill() is called again with a KILL)
77 */
78 static void fcgi_kill(ServerProcess *process, int sig)
79 {
80 FCGIDBG3("fcgi_kill(%ld, %d)", (long) process->pid, sig);
81
82 process->state = FCGI_VICTIM_STATE;
83
84 #ifdef WIN32
85
86 if (sig == SIGTERM)
87 {
88 SetEvent(process->terminationEvent);
89 }
90 else if (sig == SIGKILL)
91 {
92 TerminateProcess(process->handle, 1);
93 }
94 else
95 {
96 ap_assert(0);
97 }
98
99 #else /* !WIN32 */
100
101 if (fcgi_wrapper)
102 {
103 seteuid_root();
104 }
105
106 kill(process->pid, sig);
107
108 if (fcgi_wrapper)
109 {
110 seteuid_user();
111 }
112
113 #endif /* !WIN32 */
114 }
115
116 /*******************************************************************************
117 * Send SIGTERM to each process in the server class, remove socket
118 * file if appropriate. Currently this is only called when the PM is shutting
119 * down and thus memory isn't freed and sockets and files aren't closed.
120 */
121 static void shutdown_all()
122 {
123 fcgi_server *s = fcgi_servers;
124
125 while (s)
126 {
127 ServerProcess *proc = s->procs;
128 int i;
129 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
130 ? dynamicMaxClassProcs
131 : s->numProcesses;
132
133 /* Send TERM to all processes */
134 for (i = 0; i < numChildren; i++, proc++)
135 {
136 if (proc->state == FCGI_RUNNING_STATE)
137 {
138 fcgi_kill(proc, SIGTERM);
139 }
140 }
141
142 s = s->next;
143 }
144
145 #ifndef WIN32
146
147 s = fcgi_servers;
148 while (s)
149 {
150 if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL)
151 {
152 struct timeval tv;
153
154 /* sleep two seconds to let the children terminate themselves */
155 tv.tv_sec = 2;
156 tv.tv_usec = 0;
157 ap_select(0, NULL, NULL, NULL, &tv);
158
159 while (s)
160 {
161 if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL)
162 {
163 /* Remove the socket file */
164 if (unlink(s->socket_path) != 0 && errno != ENOENT) {
165 ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server,
166 "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"",
167 s->socket_path,
168 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path);
169 }
170 }
171
172 s = s->next;
173 }
174
175 break;
176 }
177
178 s = s->next;
179 }
180
181 #endif
182
183 #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0)
184
185 /*
186 * WIN32 applications may not have support for the shutdown event
187 * depending on their application library version
188 */
189
190 Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT);
191 s = fcgi_servers;
192
193 while (s)
194 {
195 ServerProcess *proc = s->procs;
196 int i;
197 int numChildren = (s->directive == APP_CLASS_DYNAMIC)
198 ? dynamicMaxClassProcs
199 : s->numProcesses;
200
201 /* Send KILL to all processes */
202 for (i = 0; i < numChildren; i++, proc++)
203 {
204 if (proc->state == FCGI_RUNNING_STATE)
205 {
206 fcgi_kill(proc, SIGKILL);
207 }
208 }
209
210 s = s->next;
211 }
212
213 #endif /* WIN32 */
214 }
215
216 static int init_listen_sock(fcgi_server * fs)
217 {
218 ap_assert(fs->directive != APP_CLASS_EXTERNAL);
219
220 /* Create the socket */
221 if ((fs->listenFd = socket(fs->socket_addr->sa_family, SOCK_STREAM, 0)) < 0)
222 {
223 #ifdef WIN32
224 errno = WSAGetLastError(); /* Not sure if this will work as expected */
225 #endif
226 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
227 "FastCGI: can't create %sserver \"%s\": socket() failed",
228 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
229 fs->fs_path);
230 return -1;
231 }
232
233 #ifndef WIN32
234 if (fs->socket_addr->sa_family == AF_UNIX)
235 {
236 /* Remove any existing socket file.. just in case */
237 unlink(((struct sockaddr_un *)fs->socket_addr)->sun_path);
238 }
239 else
240 #endif
241 {
242 int flag = 1;
243 setsockopt(fs->listenFd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
244 }
245
246 /* Bind it to the socket_addr */
247 if (bind(fs->listenFd, fs->socket_addr, fs->socket_addr_len))
248 {
249 char port[11];
250
251 #ifdef WIN32
252 errno = WSAGetLastError();
253 #endif
254 ap_snprintf(port, sizeof(port), "port=%d",
255 ((struct sockaddr_in *)fs->socket_addr)->sin_port);
256
257 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
258 "FastCGI: can't create %sserver \"%s\": bind() failed [%s]",
259 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
260 fs->fs_path,
261 #ifndef WIN32
262 (fs->socket_addr->sa_family == AF_UNIX) ?
263 ((struct sockaddr_un *)fs->socket_addr)->sun_path :
264 #endif
265 port);
266 }
267
268 #ifndef WIN32
269 /* Twiddle Unix socket permissions */
270 else if (fs->socket_addr->sa_family == AF_UNIX
271 && chmod(((struct sockaddr_un *)fs->socket_addr)->sun_path, S_IRUSR | S_IWUSR))
272 {
273 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
274 "FastCGI: can't create %sserver \"%s\": chmod() of socket failed",
275 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
276 fs->fs_path);
277 }
278 #endif
279
280 /* Set to listen */
281 else if (listen(fs->listenFd, fs->listenQueueDepth))
282 {
283 #ifdef WIN32
284 errno = WSAGetLastError();
285 #endif
286 ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server,
287 "FastCGI: can't create %sserver \"%s\": listen() failed",
288 (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "",
289 fs->fs_path);
290 }
291 else
292 {
293 return 0;
294 }
295
296 #ifdef WIN32
297 closesocket(fs->listenFd);
298 #else
299 close(fs->listenFd);
300 #endif
301
302 fs->listenFd = -1;
303
304 return -2;
305 }
306
307 /*
308 *----------------------------------------------------------------------
309 *
310 * pm_main
311 *
312 * The FastCGI process manager, which runs as a separate
313 * process responsible for:
314 * - Starting all the FastCGI proceses.
315 * - Restarting any of these processes that die (indicated
316 * by SIGCHLD).
317 * - Catching SIGTERM and relaying it to all the FastCGI
318 * processes before exiting.
319 *
320 * Inputs:
321 * Uses global variable fcgi_servers.
322 *
323 * Results:
324 * Does not return.
325 *
326 * Side effects:
327 * Described above.
328 *
329 *----------------------------------------------------------------------
330 */
331 #ifndef WIN32
332 static int caughtSigTerm = FALSE;
333 static int caughtSigChld = FALSE;
334 static int caughtSigAlarm = FALSE;
335
336 static void signal_handler(int signo)
337 {
338 if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) {
339 /* SIGUSR1 & SIGHUP are sent by apache to its process group
340 * when apache get 'em. Apache follows up (1.2.x) with attacks
341 * on each of its child processes, but we've got the KillMgr
342 * sitting between us so we never see the KILL. The main loop
343 * in ProcMgr also checks to see if the KillMgr has terminated,
344 * and if it has, we handl it as if we should shutdown too. */
345 caughtSigTerm = TRUE;
346 } else if(signo == SIGCHLD) {
347 caughtSigChld = TRUE;
348 } else if(signo == SIGALRM) {
349 caughtSigAlarm = TRUE;
350 }
351 }
352 #endif
353
354 /*
355 *----------------------------------------------------------------------
356 *
357 * spawn_fs_process --
358 *
359 * Fork and exec the specified fcgi process.
360 *
361 * Results:
362 * 0 for successful fork, -1 for failed fork.
363 *
364 * In case the child fails before or in the exec, the child
365 * obtains the error log by calling getErrLog, logs
366 * the error, and exits with exit status = errno of
367 * the failed system call.
368 *
369 * Side effects:
370 * Child process created.
371 *
372 *----------------------------------------------------------------------
373 */
374 static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process)
375 {
376 #ifndef WIN32
377
378 pid_t child_pid;
379 int i;
380 char *dirName;
381 char *dnEnd, *failedSysCall;
382
383 child_pid = fork();
384 if (child_pid) {
385 return child_pid;
386 }
387
388 /* We're the child. We're gonna exec() so pools don't matter. */
389
390 dnEnd = strrchr(fs->fs_path, '/');
391 if (dnEnd == NULL) {
392 dirName = "./";
393 } else {
394 dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1);
395 dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path);
396 }
397 if (chdir(dirName) < 0) {
398 failedSysCall = "chdir()";
399 goto FailedSystemCallExit;
400 }
401
402 #ifndef __EMX__
403 /* OS/2 dosen't support nice() */
404 if (fs->processPriority != 0) {
405 if (nice(fs->processPriority) == -1) {
406 failedSysCall = "nice()";
407 goto FailedSystemCallExit;
408 }
409 }
410 #endif
411
412 /* Open the listenFd on spec'd fd */
413 if (fs->listenFd != FCGI_LISTENSOCK_FILENO)
414 dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO);
415
416 /* Close all other open fds, except stdout/stderr. Leave these two open so
417 * FastCGI applications don't have to find and fix ALL 3rd party libs that
418 * write to stdout/stderr inadvertantly. For now, just leave 'em open to the
419 * main server error_log - @@@ provide a directive control where this goes.
420 */
421 ap_error_log2stderr(fcgi_apache_main_server);
422 dup2(2, 1);
423 for (i = 0; i < FCGI_MAX_FD; i++) {
424 if (i != FCGI_LISTENSOCK_FILENO && i != 2 && i != 1) {
425 close(i);
426 }
427 }
428
429 /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD
430 * install its own handler. */
431 signal(SIGPIPE, SIG_IGN);
432
433 if (fcgi_wrapper)
434 {
435 char *shortName;
436
437 /* Relinquish our root real uid powers */
438 seteuid_root();
439 setuid(ap_user_id);
440
441 /* Apache (2 anyway) doesn't use suexec if there is no user/group in
442 * effect - this translates to a uid/gid of 0/0 (which should never
443 * be a valid uid/gid for an suexec invocation so it should be safe */
444 if (fs->uid == 0 && fs->gid == 0) {
445 goto NO_SUEXEC;
446 }
447
448 #ifdef NO_SUEXEC_FOR_AP_USER_N_GROUP
449
450 /* AP13 does not use suexec if the target uid/gid is the same as the
451 * server's - AP20 does. I (now) consider the AP2 approach better
452 * (fcgi_pm.c v1.42 incorporated the 1.3 behaviour, v1.84 reverted it,
453 * v1.85 added the compile time option to use the old behaviour). */
454 if (fcgi_user_id == fs->uid && fcgi_group_id == fs->gid) {
455 goto NO_SUEXEC;
456 }
457
458 #endif
459 shortName = strrchr(fs->fs_path, '/') + 1;
460
461 do {
462 execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group,
463 shortName, NULL, fs->envp);
464 } while (errno == EINTR);
465 }
466 else
467 {
468 NO_SUEXEC:
469 do {
470 execle(fs->fs_path, fs->fs_path, NULL, fs->envp);
471 } while (errno == EINTR);
472 }
473
474 failedSysCall = "execle()";
475
476 FailedSystemCallExit:
477 fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n",
478 fs->fs_path, (long) getpid(), failedSysCall, strerror(errno));
479 exit(-1);
480
481 /* avoid an irrelevant compiler warning */
482 return(0);
483
484 #else /* WIN32 */
485
486 #ifdef APACHE2
487
488 /* based on mod_cgi.c:run_cgi_child() */
489
490 apr_pool_t * tp;
491 char * termination_env_string;
492 HANDLE listen_handle = INVALID_HANDLE_VALUE;
493 apr_procattr_t * procattr;
494 apr_proc_t proc = { 0 };
495 apr_file_t * file;
496 int i = 0;
497 cgi_exec_info_t e_info = { 0 };
498 request_rec r = { 0 };
499 const char *command;
500 const char **argv;
501 int rv;
502 APR_OPTIONAL_FN_TYPE(ap_cgi_build_command) *cgi_build_command;
503
504 cgi_build_command = APR_RETRIEVE_OPTIONAL_FN(ap_cgi_build_command);
505 if (cgi_build_command == NULL)
506 {
507 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
508 "FastCGI: can't exec server \"%s\", mod_cgi isn't loaded",
509 fs->fs_path);
510 return 0;
511 }
512
513 if (apr_pool_create(&tp, fcgi_config_pool))
514 return 0;
515
516 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
517 if (process->terminationEvent == NULL)
518 goto CLEANUP;
519
520 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
521
522 termination_env_string = ap_psprintf(tp,
523 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
524
525 while (fs->envp[i]) i++;
526 fs->envp[i++] = termination_env_string;
527 fs->envp[i] = (char *) fs->mutex_env_string;
528
529 ap_assert(fs->envp[i + 1] == NULL);
530
531 if (fs->socket_path)
532 {
533 SECURITY_ATTRIBUTES sa = { 0 };
534
535 sa.bInheritHandle = TRUE;
536 sa.nLength = sizeof(sa);
537
538 listen_handle = CreateNamedPipe(fs->socket_path,
539 PIPE_ACCESS_DUPLEX,
540 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
541 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
542
543 if (listen_handle == INVALID_HANDLE_VALUE)
544 {
545 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
546 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed",
547 fs->fs_path);
548 goto CLEANUP;
549 }
550 }
551 else
552 {
553 listen_handle = (HANDLE) fs->listenFd;
554 }
555
556 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
557 r.server = fcgi_apache_main_server;
558 r.filename = (char *) fs->fs_path;
559 r.pool = tp;
560 r.subprocess_env = apr_table_make(tp, 0);
561
562 e_info.cmd_type = APR_PROGRAM;
563
564 rv = cgi_build_command(&command, &argv, &r, tp, &e_info);
565 if (rv != APR_SUCCESS)
566 {
567 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
568 "FastCGI: don't know how to spawn cmd child process: %s",
569 fs->fs_path);
570 goto CLEANUP;
571 }
572
573 if (apr_procattr_create(&procattr, tp))
574 goto CLEANUP;
575
576 if (apr_procattr_dir_set(procattr, ap_make_dirstr_parent(tp, fs->fs_path)))
577 goto CLEANUP;
578
579 if (apr_procattr_cmdtype_set(procattr, e_info.cmd_type))
580 goto CLEANUP;
581
582 if (apr_procattr_detach_set(procattr, 1))
583 goto CLEANUP;
584
585 if (apr_os_file_put(&file, &listen_handle, 0, tp))
586 goto CLEANUP;
587
588 #if (APR_MAJOR_VERSION >= 1) && (APR_MINOR_VERSION >= 3)
589 if (apr_procattr_io_set(procattr, APR_FULL_BLOCK, APR_NO_FILE, APR_NO_FILE))
590 goto CLEANUP;
591 #endif
592
593 /* procattr is opaque so we have to use this - unfortuantely it dups */
594 if (apr_procattr_child_in_set(procattr, file, NULL))
595 goto CLEANUP;
596
597 if (apr_proc_create(&proc, command, argv, fs->envp, procattr, tp))
598 goto CLEANUP;
599
600 process->handle = proc.hproc;
601
602 CLEANUP:
603
604 if (fs->socket_path && listen_handle != INVALID_HANDLE_VALUE)
605 {
606 CloseHandle(listen_handle);
607 }
608
609 if (i)
610 {
611 fs->envp[i - 1] = NULL;
612 }
613
614 ap_destroy_pool(tp);
615
616 return proc.pid;
617
618 #else /* WIN32 && !APACHE2 */
619
620 /* Adapted from Apache's util_script.c ap_call_exec() */
621 char *interpreter = NULL;
622 char *quoted_filename;
623 char *pCommand;
624 char *pEnvBlock, *pNext;
625
626 int i = 0;
627 int iEnvBlockLen = 1;
628
629 file_type_e fileType;
630
631 STARTUPINFO si;
632 PROCESS_INFORMATION pi;
633
634 request_rec r;
635 pid_t pid = -1;
636
637 pool * tp = ap_make_sub_pool(fcgi_config_pool);
638
639 HANDLE listen_handle = INVALID_HANDLE_VALUE;
640 char * termination_env_string = NULL;
641
642 process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
643 if (process->terminationEvent == NULL)
644 {
645 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
646 "FastCGI: can't create termination event for server \"%s\", "
647 "CreateEvent() failed", fs->fs_path);
648 goto CLEANUP;
649 }
650 SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE);
651
652 termination_env_string = ap_psprintf(tp,
653 "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent);
654
655 if (fs->socket_path)
656 {
657 SECURITY_ATTRIBUTES sa;
658
659 sa.lpSecurityDescriptor = NULL;
660 sa.bInheritHandle = TRUE;
661 sa.nLength = sizeof(sa);
662
663 listen_handle = CreateNamedPipe(fs->socket_path,
664 PIPE_ACCESS_DUPLEX,
665 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
666 PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa);
667
668 if (listen_handle == INVALID_HANDLE_VALUE)
669 {
670 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
671 "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path);
672 goto CLEANUP;
673 }
674 }
675 else
676 {
677 listen_handle = (HANDLE) fs->listenFd;
678 }
679
680 memset(&si, 0, sizeof(si));
681 memset(&pi, 0, sizeof(pi));
682 memset(&r, 0, sizeof(r));
683
684 /* Can up a fake request to pass to ap_get_win32_interpreter() */
685 r.per_dir_config = fcgi_apache_main_server->lookup_defaults;
686 r.server = fcgi_apache_main_server;
687 r.filename = (char *) fs->fs_path;
688 r.pool = tp;
689
690 fileType = ap_get_win32_interpreter(&r, &interpreter);
691
692 if (fileType == eFileTypeUNKNOWN) {
693 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
694 "FastCGI: %s is not executable; ensure interpreted scripts have "
695 "\"#!\" as their first line",
696 fs->fs_path);
697 ap_destroy_pool(tp);
698 goto CLEANUP;
699 }
700
701 /*
702 * We have the interpreter (if there is one) and we have
703 * the arguments (if there are any).
704 * Build the command string to pass to CreateProcess.
705 */
706 quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL);
707 if (interpreter && *interpreter) {
708 pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL);
709 }
710 else {
711 pCommand = quoted_filename;
712 }
713
714 /*
715 * Make child process use hPipeOutputWrite as standard out,
716 * and make sure it does not show on screen.
717 */
718 si.cb = sizeof(si);
719 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
720 si.wShowWindow = SW_HIDE;
721 si.hStdInput = listen_handle;
722
723 /* XXX These should be open to the error_log */
724 si.hStdOutput = INVALID_HANDLE_VALUE;
725 si.hStdError = INVALID_HANDLE_VALUE;
726
727 /*
728 * Win32's CreateProcess call requires that the environment
729 * be passed in an environment block, a null terminated block of
730 * null terminated strings.
731 * @todo we should store the env in this format for win32.
732 */
733 while (fs->envp[i])
734 {
735 iEnvBlockLen += strlen(fs->envp[i]) + 1;
736 i++;
737 }
738
739 iEnvBlockLen += strlen(termination_env_string) + 1;
740 iEnvBlockLen += strlen(fs->mutex_env_string) + 1;
741
742 pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen);
743
744 i = 0;
745 pNext = pEnvBlock;
746 while (fs->envp[i])
747 {
748 strcpy(pNext, fs->envp[i]);
749 pNext += strlen(pNext) + 1;
750 i++;
751 }
752
753 strcpy(pNext, termination_env_string);
754 pNext += strlen(pNext) + 1;
755 strcpy(pNext, fs->mutex_env_string);
756
757 if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE,
758 0,
759 pEnvBlock,
760 ap_make_dirstr_parent(tp, fs->fs_path),
761 &si, &pi))
762 {
763 /* Hack to get 16-bit CGI's working. It works for all the
764 * standard modules shipped with Apache. pi.dwProcessId is 0
765 * for 16-bit CGIs and all the Unix specific code that calls
766 * ap_call_exec interprets this as a failure case. And we can't
767 * use -1 either because it is mapped to 0 by the caller.
768 */
769 pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId;
770
771 process->handle = pi.hProcess;
772 CloseHandle(pi.hThread);
773 }
774
775 CLEANUP:
776
777 if (fs->socket_path && listen_handle != INVALID_HANDLE_VALUE)
778 {
779 CloseHandle(listen_handle);
780 }
781
782 ap_destroy_pool(tp);
783
784 return pid;
785
786 #endif /* !APACHE2 */
787 #endif /* WIN32 */
788 }
789
790 #ifndef WIN32
791 static void reduce_privileges(void)
792 {
793 const char *name;
794
795 if (geteuid() != 0)
796 return;
797
798 #ifndef __EMX__
799 /* Get username if passed as a uid */
800 if (ap_user_name[0] == '#') {
801 uid_t uid = atoi(&ap_user_name[1]);
802 struct passwd *ent = getpwuid(uid);
803
804 if (ent == NULL) {
805 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
806 "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, "
807 "you probably need to modify the User directive", (unsigned)uid);
808 exit(1);
809 }
810 name = ent->pw_name;
811 }
812 else
813 name = ap_user_name;
814
815 /* Change Group */
816 if (setgid(ap_group_id) == -1) {
817 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
818 "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id);
819 exit(1);
820 }
821
822 /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */
823
824 /* Initialize supplementary groups */
825 if (initgroups(name, ap_group_id) == -1) {
826 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
827 "FastCGI: process manager exiting, initgroups(%s,%u) failed",
828 name, (unsigned)ap_group_id);
829 exit(1);
830 }
831 #endif /* __EMX__ */
832
833 /* Change User */
834 if (fcgi_wrapper) {
835 if (seteuid_user() == -1) {
836 ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server,
837 "FastCGI: process manager exiting, failed to reduce privileges");
838 exit(1);
839 }
840 }
841 else {
842 if (setuid(ap_user_id) == -1) {
843 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
844 "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id);
845 exit(1);
846 }
847 }
848 }
849
850 /*************
851 * Change the name of this process - best we can easily.
852 */
853 static void change_process_name(const char * const name)
854 {
855 /* under Apache2, ap_server_argv0 is const */
856 strncpy((char *) ap_server_argv0, name, strlen(ap_server_argv0));
857 }
858 #endif /* !WIN32 */
859
860 static void schedule_start(fcgi_server *s, int proc)
861 {
862 /* If we've started one recently, don't register another */
863 time_t time_passed = now - s->restartTime;
864
865 if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay))
866 || ((s->procs[proc].pid == 0) && (time_passed < s->initStartDelay)))
867 {
868 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);
869 return;
870 }
871
872 FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc);
873 s->procs[proc].state = FCGI_START_STATE;
874 if (proc == dynamicMaxClassProcs - 1) {
875 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
876 "FastCGI: scheduled the %sstart of the last (dynamic) server "
877 "\"%s\" process: reached dynamicMaxClassProcs (%d)",
878 s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs);
879 }
880 }
881
882 /*
883 *----------------------------------------------------------------------
884 *
885 * dynamic_read_msgs
886 *
887 * Removes the records written by request handlers and decodes them.
888 * We also update the data structures to reflect the changes.
889 *
890 *----------------------------------------------------------------------
891 */
892
893 static void dynamic_read_msgs(int read_ready)
894 {
895 fcgi_server *s;
896 int rc;
897
898 #ifndef WIN32
899 static int buflen = 0;
900 static char buf[FCGI_MSGS_BUFSIZE + 1];
901 char *ptr1, *ptr2, opcode;
902 char execName[FCGI_MAXPATH + 1];
903 char user[MAX_USER_NAME_LEN + 2];
904 char group[MAX_GID_CHAR_LEN + 1];
905 unsigned long q_usec = 0UL, req_usec = 0UL;
906 #else
907 fcgi_pm_job *joblist = NULL;
908 fcgi_pm_job *cjob = NULL;
909 #endif
910
911 pool *sp = NULL, *tp;
912
913 #ifndef WIN32
914 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0';
915 #endif
916
917 /*
918 * To prevent the idle application from running indefinitely, we
919 * check the timer and if it is expired, we recompute the values
920 * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB
921 * message is received, only updates are made to the data structures.
922 */
923 if (fcgi_dynamic_last_analyzed == 0) {
924 fcgi_dynamic_last_analyzed = now;
925 }
926 if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) {
927 for (s = fcgi_servers; s != NULL; s = s->next) {
928 if (s->directive != APP_CLASS_DYNAMIC)
929 break;
930
931 /* Advance the last analyzed timestamp by the elapsed time since
932 * it was last set. Round the increase down to the nearest
933 * multiple of dynamicUpdateInterval */
934
935 fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval);
936 s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime);
937 s->totalConnTime = 0UL;
938 s->totalQueueTime = 0UL;
939 }
940 }
941
942 if (read_ready <= 0) {
943 return;
944 }
945
946 #ifndef WIN32
947 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen);
948 if (rc <= 0) {
949 if (!caughtSigTerm) {
950 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
951 "FastCGI: read() from pipe failed (%d)", rc);
952 if (rc == 0) {
953 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
954 "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye");
955 caughtSigTerm = TRUE;
956 }
957 }
958 return;
959 }
960 buflen += rc;
961 buf[buflen] = '\0';
962
963 #else
964
965 /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a
966 * request to do something) and/or when a timeout expires.
967 * There really should be no reason why this wait would get stuck
968 * but there's no point in waiting forever. */
969
970 rc = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
971
972 if (rc != WAIT_OBJECT_0 && rc != WAIT_ABANDONED)
973 {
974 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
975 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
976 return;
977 }
978
979 joblist = fcgi_dynamic_mbox;
980 fcgi_dynamic_mbox = NULL;
981
982 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
983 {
984 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
985 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
986 }
987
988 cjob = joblist;
989 #endif
990
991 #ifdef APACHE2
992 apr_pool_create(&tp, fcgi_config_pool);
993 #else
994 tp = ap_make_sub_pool(fcgi_config_pool);
995 #endif
996
997 #ifndef WIN32
998 for (ptr1 = buf; ptr1; ptr1 = ptr2) {
999 int scan_failed = 0;
1000
1001 ptr2 = strchr(ptr1, '*');
1002 if (ptr2) {
1003 *ptr2++ = '\0';
1004 }
1005 else {
1006 break;
1007 }
1008
1009 opcode = *ptr1;
1010
1011 switch (opcode)
1012 {
1013 case FCGI_SERVER_START_JOB:
1014 case FCGI_SERVER_RESTART_JOB:
1015
1016 if (sscanf(ptr1, "%c %s %16s %15s",
1017 &opcode, execName, user, group) != 4)
1018 {
1019 scan_failed = 1;
1020 }
1021 break;
1022
1023 case FCGI_REQUEST_TIMEOUT_JOB:
1024
1025 if (sscanf(ptr1, "%c %s %16s %15s",
1026 &opcode, execName, user, group) != 4)
1027 {
1028 scan_failed = 1;
1029 }
1030 break;
1031
1032 case FCGI_REQUEST_COMPLETE_JOB:
1033
1034 if (sscanf(ptr1, "%c %s %16s %15s %lu %lu",
1035 &opcode, execName, user, group, &q_usec, &req_usec) != 6)
1036 {
1037 scan_failed = 1;
1038 }
1039 break;
1040
1041 default:
1042
1043 scan_failed = 1;
1044 break;
1045 }
1046
1047 FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode, execName, user, group, q_usec, req_usec);
1048
1049 if (scan_failed) {
1050 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1051 "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1);
1052 goto NextJob;
1053 }
1054 #else
1055 /* Update data structures for processing */
1056 while (cjob != NULL) {
1057 joblist = cjob->next;
1058 FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time);
1059 #endif
1060
1061 #ifndef WIN32
1062 s = fcgi_util_fs_get(execName, user, group);
1063 #else
1064 s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group);
1065 #endif
1066
1067 #ifndef WIN32
1068 if (s==NULL && opcode != FCGI_REQUEST_COMPLETE_JOB)
1069 #else
1070 if (s==NULL && cjob->id != FCGI_REQUEST_COMPLETE_JOB)
1071 #endif
1072 {
1073 #ifdef WIN32
1074
1075 HANDLE mutex = CreateMutex(NULL, FALSE, cjob->fs_path);
1076
1077 if (mutex == NULL)
1078 {
1079 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1080 "FastCGI: can't create accept mutex "
1081 "for (dynamic) server \"%s\"", cjob->fs_path);
1082 goto BagNewServer;
1083 }
1084
1085 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
1086 #else
1087 const char *err;
1088 #endif
1089
1090 /* Create a perm subpool to hold the new server data,
1091 * we can destroy it if something doesn't pan out */
1092 #ifdef APACHE2
1093 apr_pool_create(&sp, fcgi_config_pool);
1094 #else
1095 sp = ap_make_sub_pool(fcgi_config_pool);
1096 #endif
1097
1098 /* Create a new "dynamic" server */
1099 s = fcgi_util_fs_new(sp);
1100
1101 s->directive = APP_CLASS_DYNAMIC;
1102 s->restartDelay = dynamicRestartDelay;
1103 s->listenQueueDepth = dynamicListenQueueDepth;
1104 s->initStartDelay = dynamicInitStartDelay;
1105 s->envp = dynamicEnvp;
1106 s->flush = dynamicFlush;
1107
1108 #ifdef WIN32
1109 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex);
1110 s->fs_path = ap_pstrdup(sp, cjob->fs_path);
1111 #else
1112 s->fs_path = ap_pstrdup(sp, execName);
1113 #endif
1114 ap_getparents(s->fs_path);
1115 ap_no2slash(s->fs_path);
1116 s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs);
1117
1118 /* XXX the socket_path (both Unix and Win) *is* deducible and
1119 * thus can and will be used by other apache instances without
1120 * the use of shared data regarding the processes serving the
1121 * requests. This can result in slightly unintuitive process
1122 * counts and security implications. This is prevented
1123 * if suexec (Unix) is in use. This is both a feature and a flaw.
1124 * Changing it now would break existing installations. */
1125
1126 #ifndef WIN32
1127 /* Create socket file's path */
1128 s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group);
1129 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1130
1131 /* Create sockaddr, prealloc it so it won't get created in tp */
1132 s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un));
1133 err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr,
1134 &s->socket_addr_len, s->socket_path);
1135 if (err) {
1136 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1137 "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err);
1138 goto BagNewServer;
1139 }
1140
1141 if (init_listen_sock(s)) {
1142 goto BagNewServer;
1143 }
1144
1145 /* If a wrapper is being used, config user/group info */
1146 if (fcgi_wrapper) {
1147 if (user[0] == '~') {
1148 /* its a user dir uri, the rest is a username, not a uid */
1149 struct passwd *pw = getpwnam(&user[1]);
1150
1151 if (!pw) {
1152 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1153 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed",
1154 execName, &user[1]);
1155 goto BagNewServer;
1156 }
1157 s->uid = pw->pw_uid;
1158 s->user = ap_pstrdup(sp, user);
1159 s->username = s->user;
1160
1161 s->gid = pw->pw_gid;
1162 s->group = ap_psprintf(sp, "%ld", (long)s->gid);
1163 }
1164 else {
1165 struct passwd *pw;
1166
1167 s->uid = (uid_t)atol(user);
1168 pw = getpwuid(s->uid);
1169 if (!pw) {
1170 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1171 "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed",
1172 execName, (long)s->uid);
1173 goto BagNewServer;
1174 }
1175 s->user = ap_pstrdup(sp, user);
1176 s->username = ap_pstrdup(sp, pw->pw_name);
1177
1178 s->gid = (gid_t)atol(group);
1179 s->group = ap_pstrdup(sp, group);
1180 }
1181 }
1182 #else
1183 /* Create socket file's path */
1184 s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group);
1185 s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1);
1186 s->listenFd = 0;
1187 #endif
1188
1189 fcgi_util_fs_add(s);
1190 }
1191 else {
1192 #ifndef WIN32
1193 if (opcode == FCGI_SERVER_RESTART_JOB) {
1194 #else
1195 if (cjob->id==FCGI_SERVER_RESTART_JOB) {
1196 #endif
1197 /* Check to see if the binary has changed. If so,
1198 * kill the FCGI application processes, and
1199 * restart them.
1200 */
1201 struct stat stbuf;
1202 int i;
1203 #ifdef WIN32
1204 char * app_path = cjob->fs_path;
1205 #else
1206 char * app_path = execName;
1207 #endif
1208
1209 if (stat(app_path, &stbuf) == 0 && stbuf.st_mtime > s->startTime)
1210 {
1211 int do_restart = 0;
1212
1213 /* prevent addition restart requests */
1214 s->startTime = now;
1215 #ifndef WIN32
1216 utime(s->socket_path, NULL);
1217 #endif
1218
1219 /* kill old server(s) */
1220 for (i = 0; i < dynamicMaxClassProcs; i++)
1221 {
1222 if (s->procs[i].pid > 0
1223 && stbuf.st_mtime > s->procs[i].start_time)
1224 {
1225 fcgi_kill(&s->procs[i], SIGTERM);
1226 do_restart++;
1227 }
1228 }
1229
1230 if (do_restart)
1231 {
1232 ap_log_error(FCGI_LOG_WARN_NOERRNO,
1233 fcgi_apache_main_server, "FastCGI: restarting "
1234 "old server \"%s\" processes, newer version "
1235 "found", app_path);
1236 }
1237 }
1238
1239 /* If dynamicAutoRestart, don't mark any new processes
1240 * for starting because we probably got the
1241 * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr
1242 * will be restarting all of those we just killed.
1243 */
1244 if (dynamicAutoRestart)
1245 goto NextJob;
1246 }
1247 #ifndef WIN32
1248 else if (opcode == FCGI_SERVER_START_JOB) {
1249 #else
1250 else if (cjob->id==FCGI_SERVER_START_JOB) {
1251 #endif
1252 /* we've been asked to start a process--only start
1253 * it if we're not already running at least one
1254 * instance.
1255 */
1256 int i;
1257
1258 for (i = 0; i < dynamicMaxClassProcs; i++) {
1259 if (s->procs[i].state == FCGI_RUNNING_STATE)
1260 break;
1261 }
1262 /* if already running, don't start another one */
1263 if (i < dynamicMaxClassProcs) {
1264 goto NextJob;
1265 }
1266 }
1267 }
1268
1269 #ifndef WIN32
1270 switch (opcode)
1271 #else
1272 switch (cjob->id)
1273 #endif
1274 {
1275 int i, start;
1276
1277 case FCGI_SERVER_RESTART_JOB:
1278
1279 start = FALSE;
1280
1281 /* We just waxed 'em all. Try to find an idle slot. */
1282
1283 for (i = 0; i < dynamicMaxClassProcs; ++i)
1284 {
1285 if (s->procs[i].state == FCGI_START_STATE
1286 || s->procs[i].state == FCGI_RUNNING_STATE)
1287 {
1288 break;
1289 }
1290 else if (s->procs[i].state == FCGI_KILLED_STATE
1291 || s->procs[i].state == FCGI_READY_STATE)
1292 {
1293 start = TRUE;
1294 break;
1295 }
1296 }
1297
1298 /* Nope, just use the first slot */
1299 if (i == dynamicMaxClassProcs)
1300 {
1301 start = TRUE;
1302 i = 0;
1303 }
1304
1305 if (start)
1306 {
1307 schedule_start(s, i);
1308 }
1309
1310 break;
1311
1312 case FCGI_SERVER_START_JOB:
1313 case FCGI_REQUEST_TIMEOUT_JOB:
1314
1315 if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) {
1316 /*
1317 * Extra instances should have been
1318 * terminated beforehand, probably need
1319 * to increase ProcessSlack parameter
1320 */
1321 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1322 "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: "
1323 "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs);
1324 goto NextJob;
1325 }
1326
1327 /* find next free slot */
1328 for (i = 0; i < dynamicMaxClassProcs; i++)
1329 {
1330 if (s->procs[i].state == FCGI_START_STATE)
1331 {
1332 FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i);
1333 break;
1334 }
1335 else if (s->procs[i].state == FCGI_RUNNING_STATE)
1336 {
1337 continue;
1338 }
1339
1340 schedule_start(s, i);
1341 break;
1342 }
1343
1344 #ifdef FCGI_DEBUG
1345 if (i >= dynamicMaxClassProcs) {
1346 FCGIDBG1("ignore_job: slots are max'd");
1347 }
1348 #endif
1349 break;
1350 case FCGI_REQUEST_COMPLETE_JOB:
1351 /* only record stats if we have a structure */
1352 if (s) {
1353 #ifndef WIN32
1354 s->totalConnTime += req_usec;
1355 s->totalQueueTime += q_usec;
1356 #else
1357 s->totalConnTime += cjob->start_time;
1358 s->totalQueueTime += cjob->qsec;
1359 #endif
1360 }
1361 break;
1362 }
1363
1364 NextJob:
1365
1366 #ifdef WIN32
1367 /* Cleanup job data */
1368 free(cjob->fs_path);
1369 free(cjob->user);
1370 free(cjob->group);
1371 free(cjob);
1372 cjob = joblist;
1373 #endif
1374
1375 continue;
1376
1377 BagNewServer:
1378 if (sp) ap_destroy_pool(sp);
1379
1380 #ifdef WIN32
1381 free(cjob->fs_path);
1382 free(cjob);
1383 cjob = joblist;
1384 #endif
1385 }
1386
1387 #ifndef WIN32
1388 if (ptr1 == buf) {
1389 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
1390 "FastCGI: really bogus message: \"%s\"", ptr1);
1391 ptr1 += strlen(buf);
1392 }
1393
1394 buflen -= ptr1 - buf;
1395 if (buflen) {
1396 memmove(buf, ptr1, buflen);
1397 }
1398 #endif
1399
1400 ap_destroy_pool(tp);
1401 }
1402
1403 /*
1404 *----------------------------------------------------------------------
1405 *
1406 * dynamic_kill_idle_fs_procs
1407 *
1408 * Implement a kill policy for the dynamic FastCGI applications.
1409 * We also update the data structures to reflect the changes.
1410 *
1411 * Side effects:
1412 * Processes are marked for deletion possibly killed.
1413 *
1414 *----------------------------------------------------------------------
1415 */
1416 static void dynamic_kill_idle_fs_procs(void)
1417 {
1418 fcgi_server *s;
1419 int victims = 0;
1420
1421 for (s = fcgi_servers; s != NULL; s = s->next)
1422 {
1423 /*
1424 * server's smoothed running time, or if that's 0, the current total
1425 */
1426 unsigned long connTime;
1427
1428 /*
1429 * maximum number of microseconds that all of a server's running
1430 * processes together could have spent running since the last check
1431 */
1432 unsigned long totalTime;
1433
1434 /*
1435 * percentage, 0-100, of totalTime that the processes actually used
1436 */
1437 int loadFactor;
1438
1439 int i;
1440 int really_running = 0;
1441
1442 if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0)
1443 {
1444 continue;
1445 }
1446
1447 /* s->numProcesses includes pending kills so get the "active" count */
1448 for (i = 0; i < dynamicMaxClassProcs; ++i)
1449 {
1450 if (s->procs[i].state == FCGI_RUNNING_STATE) ++really_running;
1451 }
1452
1453 connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime;
1454 totalTime = really_running * (now - fcgi_dynamic_epoch) * 1000000 + 1;
1455
1456 loadFactor = 100 * connTime / totalTime;
1457
1458 if (really_running == 1)
1459 {
1460 if (loadFactor >= dynamicThreshold1)
1461 {
1462 continue;
1463 }
1464 }
1465 else
1466 {
1467 int load = really_running / ( really_running - 1) * loadFactor;
1468
1469 if (load >= dynamicThresholdN)
1470 {
1471 continue;
1472 }
1473 }
1474
1475 /*
1476 * Run through the procs to see if we can get away w/o waxing one.
1477 */
1478 for (i = 0; i < dynamicMaxClassProcs; ++i)
1479 {
1480 if (s->procs[i].state == FCGI_START_STATE)
1481 {
1482 s->procs[i].state = FCGI_READY_STATE;
1483 break;
1484 }
1485 else if (s->procs[i].state == FCGI_VICTIM_STATE)
1486 {
1487 break;
1488 }
1489 }
1490
1491 if (i >= dynamicMaxClassProcs)
1492 {
1493 ServerProcess * procs = s->procs;
1494 int youngest = -1;
1495
1496 for (i = 0; i < dynamicMaxClassProcs; ++i)
1497 {
1498 if (procs[i].state == FCGI_RUNNING_STATE)
1499 {
1500 if (youngest == -1 || procs[i].start_time >= procs[youngest].start_time)
1501 {
1502 youngest = i;
1503 }
1504 }
1505 }
1506
1507 if (youngest != -1)
1508 {
1509 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1510 "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled",
1511 s->fs_path, (long) s->procs[youngest].pid);
1512
1513 fcgi_kill(&s->procs[youngest], SIGTERM);
1514
1515 victims++;
1516 }
1517
1518 /*
1519 * If the number of non-victims is less than or equal to
1520 * the minimum that may be running without being killed off,
1521 * don't select any more victims.
1522 */
1523 if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs)
1524 {
1525 break;
1526 }
1527 }
1528 }
1529 }
1530
1531 #ifdef WIN32
1532
1533 /* This is a little bogus, there's gotta be a better way to do this
1534 * Can we use WaitForMultipleObjects() */
1535 #define FCGI_PROC_WAIT_TIME 100
1536
1537 void child_wait_thread_main(void *dummy) {
1538 fcgi_server *s;
1539 DWORD dwRet = WAIT_TIMEOUT;
1540 int numChildren;
1541 int i;
1542 int waited;
1543
1544 while (!bTimeToDie) {
1545 waited = 0;
1546
1547 for (s = fcgi_servers; s != NULL; s = s->next) {
1548 if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) {
1549 continue;
1550 }
1551 if (s->directive == APP_CLASS_DYNAMIC) {
1552 numChildren = dynamicMaxClassProcs;
1553 }
1554 else {
1555 numChildren = s->numProcesses;
1556 }
1557
1558 for (i=0; i < numChildren; i++) {
1559 if (s->procs[i].handle != INVALID_HANDLE_VALUE)
1560 {
1561 DWORD exitStatus = 0;
1562
1563 /* timeout is currently set for 100 miliecond */
1564 /* it may need to be longer or user customizable */
1565 dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME);
1566
1567 waited = 1;
1568
1569 if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) {
1570 /* a child fs has died */
1571 /* mark the child as dead */
1572
1573 GetExitCodeProcess(s->procs[i].handle, &exitStatus);
1574
1575 if (s->directive == APP_CLASS_STANDARD) {
1576 /* restart static app */
1577 s->procs[i].state = FCGI_START_STATE;
1578 if (exitStatus != 0) {
1579 /* don't bump failure count on exit 0 */
1580 s->numFailures++;
1581 }
1582 }
1583 else {
1584 s->numProcesses--;
1585 fcgi_dynamic_total_proc_count--;
1586 FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1587
1588 if (s->procs[i].state == FCGI_VICTIM_STATE) {
1589 s->procs[i].state = FCGI_KILLED_STATE;
1590 }
1591 else {
1592 /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/
1593 if (exitStatus != 0) {
1594 /* don't bump failure count on exit 0 */
1595 s->numFailures++;
1596 }
1597
1598 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) {
1599 s->procs[i].state = FCGI_START_STATE;
1600 }
1601 else {
1602 s->procs[i].state = FCGI_READY_STATE;
1603 }
1604 }
1605 }
1606
1607 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1608 "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'",
1609 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1610 s->fs_path, (long) s->procs[i].pid, exitStatus);
1611
1612 CloseHandle(s->procs[i].handle);
1613 CloseHandle(s->procs[i].terminationEvent);
1614 s->procs[i].handle = INVALID_HANDLE_VALUE;
1615 s->procs[i].terminationEvent = INVALID_HANDLE_VALUE;
1616 s->procs[i].pid = -1;
1617
1618 /* wake up the main thread */
1619 SetEvent(fcgi_event_handles[WAKE_EVENT]);
1620 }
1621 }
1622 }
1623 }
1624 Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME);
1625 }
1626 }
1627 #endif
1628
1629 #ifndef WIN32
1630 static void setup_signals(void)
1631 {
1632 struct sigaction sa;
1633
1634 /* Setup handlers */
1635
1636 sa.sa_handler = signal_handler;
1637 sigemptyset(&sa.sa_mask);
1638 sa.sa_flags = 0;
1639
1640 if (sigaction(SIGTERM, &sa, NULL) < 0) {
1641 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1642 "sigaction(SIGTERM) failed");
1643 }
1644 /* httpd restart */
1645 if (sigaction(SIGHUP, &sa, NULL) < 0) {
1646 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1647 "sigaction(SIGHUP) failed");
1648 }
1649 /* httpd graceful restart */
1650 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
1651 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1652 "sigaction(SIGUSR1) failed");
1653 }
1654 /* read messages from request handlers - kill interval expired */
1655 if (sigaction(SIGALRM, &sa, NULL) < 0) {
1656 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1657 "sigaction(SIGALRM) failed");
1658 }
1659 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1660 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1661 "sigaction(SIGCHLD) failed");
1662 }
1663 }
1664 #endif
1665
1666 #if !defined(WIN32) && !defined(APACHE2)
1667 int fcgi_pm_main(void *dummy, child_info *info)
1668 #else
1669 void fcgi_pm_main(void *dummy)
1670 #endif
1671 {
1672 fcgi_server *s;
1673 unsigned int i;
1674 int read_ready = 0;
1675 int alarmLeft = 0;
1676
1677 #ifdef WIN32
1678 DWORD dwRet;
1679 HANDLE child_wait_thread = INVALID_HANDLE_VALUE;
1680 #else
1681 int callWaitPid, callDynamicProcs;
1682 #endif
1683
1684 #ifdef WIN32
1685 /* Add SystemRoot to the dynamic environment */
1686 char ** envp = dynamicEnvp;
1687 for (i = 0; *envp; ++i) {
1688 ++envp;
1689 }
1690 fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot");
1691
1692 #else
1693
1694 reduce_privileges();
1695 change_process_name("fcgi-pm");
1696
1697 close(fcgi_pm_pipe[1]);
1698 setup_signals();
1699
1700 if (fcgi_wrapper) {
1701 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1702 "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper);
1703 }
1704 #endif
1705
1706 /* Initialize AppClass */
1707 for (s = fcgi_servers; s != NULL; s = s->next)
1708 {
1709 if (s->directive != APP_CLASS_STANDARD)
1710 continue;
1711
1712 #ifdef WIN32
1713 if (s->socket_path)
1714 s->listenFd = 0;
1715 #endif
1716
1717 for (i = 0; i < s->numProcesses; ++i)
1718 s->procs[i].state = FCGI_START_STATE;
1719 }
1720
1721 #ifdef WIN32
1722 child_wait_thread = (HANDLE) _beginthread(child_wait_thread_main, 0, NULL);
1723
1724 if (child_wait_thread == (HANDLE) -1)
1725 {
1726 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
1727 "FastCGI: failed to create process manager's wait thread!");
1728 }
1729
1730 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1731 "FastCGI: process manager initialized");
1732 #else
1733 ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server,
1734 "FastCGI: process manager initialized (pid %ld)", (long) getpid());
1735 #endif
1736
1737 now = time(NULL);
1738
1739 /*
1740 * Loop until SIGTERM
1741 */
1742 for (;;) {
1743 int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval);
1744 #ifdef WIN32
1745 time_t expire;
1746 #else
1747 pid_t childPid;
1748 int waitStatus;
1749 #endif
1750 unsigned int numChildren;
1751 unsigned int minServerLife;
1752
1753 /*
1754 * If we came out of sigsuspend() for any reason other than
1755 * SIGALRM, pick up where we left off.
1756 */
1757 if (alarmLeft)
1758 sleepSeconds = alarmLeft;
1759
1760 /*
1761 * Examine each configured AppClass for a process that needs
1762 * starting. Compute the earliest time when the start should
1763 * be attempted, starting it now if the time has passed. Also,
1764 * remember that we do NOT need to restart externally managed
1765 * FastCGI applications.
1766 */
1767 for (s = fcgi_servers; s != NULL; s = s->next)
1768 {
1769 if (s->directive == APP_CLASS_EXTERNAL)
1770 continue;
1771
1772 numChildren = (s->directive == APP_CLASS_DYNAMIC)
1773 ? dynamicMaxClassProcs
1774 : s->numProcesses;
1775
1776 minServerLife = (s->directive == APP_CLASS_DYNAMIC)
1777 ? dynamicMinServerLife
1778 : s->minServerLife;
1779
1780 for (i = 0; i < numChildren; ++i)
1781 {
1782 if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE)
1783 {
1784 int restart = (s->procs[i].pid < 0);
1785 time_t restartTime = s->restartTime;
1786
1787 if (s->bad)
1788 {
1789 /* we've gone to using the badDelay, the only thing that
1790 resets bad is when badDelay has expired. but numFailures
1791 is only just set below its threshold. the proc's
1792 start_times are all reset when the bad is. the numFailures
1793 is reset when we see an app run for a period */
1794
1795 s->procs[i].start_time = 0;
1796 }
1797
1798 if (s->numFailures > MAX_FAILED_STARTS)
1799 {
1800 time_t last_start_time = s->procs[i].start_time;
1801
1802 if (last_start_time && now - last_start_time > minServerLife)
1803 {
1804 s->bad = 0;
1805 s->numFailures = 0;
1806 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1807 "FastCGI:%s server \"%s\" has remained"
1808 " running for more than %d seconds, its restart"
1809 " interval has been restored to %d seconds",
1810 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1811 s->fs_path, minServerLife, s->restartDelay);
1812 }
1813 else
1814 {
1815 unsigned int j;
1816
1817 for (j = 0; j < numChildren; ++j)
1818 {
1819 if (s->procs[j].pid <= 0) continue;
1820 if (s->procs[j].state != FCGI_RUNNING_STATE) continue;
1821 if (s->procs[j].start_time == 0) continue;
1822 if (now - s->procs[j].start_time > minServerLife) break;
1823 }
1824
1825 if (j >= numChildren)
1826 {
1827 s->bad = 1;
1828 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1829 "FastCGI:%s server \"%s\" has failed to remain"
1830 " running for %d seconds given %d attempts, its restart"
1831 " interval has been backed off to %d seconds",
1832 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1833 s->fs_path, minServerLife, MAX_FAILED_STARTS,
1834 FAILED_STARTS_DELAY);
1835 }
1836 else
1837 {
1838 s->bad = 0;
1839 s->numFailures = 0;
1840 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1841 "FastCGI:%s server \"%s\" has remained"
1842 " running for more than %d seconds, its restart"
1843 " interval has been restored to %d seconds",
1844 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1845 s->fs_path, minServerLife, s->restartDelay);
1846 }
1847 }
1848 }
1849
1850 if (s->bad)
1851 {
1852 restartTime += FAILED_STARTS_DELAY;
1853 }
1854 else
1855 {
1856 restartTime += (restart) ? s->restartDelay : s->initStartDelay;
1857 }
1858
1859 if (restartTime <= now)
1860 {
1861 if (s->bad)
1862 {
1863 s->bad = 0;
1864 s->numFailures = MAX_FAILED_STARTS;
1865 }
1866
1867 if (s->listenFd < 0 && init_listen_sock(s))
1868 {
1869 if (sleepSeconds > s->initStartDelay)
1870 sleepSeconds = s->initStartDelay;
1871 break;
1872 }
1873 #ifndef WIN32
1874 if (caughtSigTerm) {
1875 goto ProcessSigTerm;
1876 }
1877 #endif
1878 s->procs[i].pid = spawn_fs_process(s, &s->procs[i]);
1879 if (s->procs[i].pid <= 0) {
1880 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
1881 "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed",
1882 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1883 s->fs_path);
1884
1885 sleepSeconds = min(sleepSeconds,
1886 max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY));
1887
1888 s->procs[i].pid = -1;
1889 break;
1890 }
1891
1892 s->procs[i].start_time = now;
1893 s->restartTime = now;
1894
1895 if (s->startTime == 0) {
1896 s->startTime = now;
1897 }
1898
1899 if (s->directive == APP_CLASS_DYNAMIC) {
1900 s->numProcesses++;
1901 fcgi_dynamic_total_proc_count++;
1902 FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count);
1903 }
1904
1905 s->procs[i].state = FCGI_RUNNING_STATE;
1906
1907 if (fcgi_wrapper) {
1908 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1909 "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)",
1910 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1911 s->fs_path, (long) s->uid, (long) s->gid,
1912 restart ? "re" : "", (long) s->procs[i].pid);
1913 }
1914 else {
1915 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
1916 "FastCGI:%s server \"%s\" %sstarted (pid %ld)",
1917 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
1918 s->fs_path, restart ? "re" : "", (long) s->procs[i].pid);
1919 }
1920 ap_assert(s->procs[i].pid > 0);
1921 } else {
1922 sleepSeconds = min(sleepSeconds, restartTime - now);
1923 }
1924 }
1925 }
1926 }
1927
1928 #ifndef WIN32
1929
1930 if(caughtSigTerm) {
1931 goto ProcessSigTerm;
1932 }
1933 if((!caughtSigChld) && (!caughtSigAlarm)) {
1934 fd_set rfds;
1935
1936 alarm(sleepSeconds);
1937
1938 FD_ZERO(&rfds);
1939 FD_SET(fcgi_pm_pipe[0], &rfds);
1940 read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL);
1941
1942 alarmLeft = alarm(0);
1943 }
1944 callWaitPid = caughtSigChld;
1945 caughtSigChld = FALSE;
1946 callDynamicProcs = caughtSigAlarm;
1947 caughtSigAlarm = FALSE;
1948
1949 now = time(NULL);
1950
1951 /*
1952 * Dynamic fcgi process management
1953 */
1954 if((callDynamicProcs) || (!callWaitPid)) {
1955 dynamic_read_msgs(read_ready);
1956 if(fcgi_dynamic_epoch == 0) {
1957 fcgi_dynamic_epoch = now;
1958 }
1959 if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) ||
1960 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) {
1961 dynamic_kill_idle_fs_procs();
1962 fcgi_dynamic_epoch = now;
1963 }
1964 }
1965
1966 if(!callWaitPid) {
1967 continue;
1968 }
1969
1970 /* We've caught SIGCHLD, so find out who it was using waitpid,
1971 * write a log message and update its data structure. */
1972
1973 for (;;) {
1974 if (caughtSigTerm)
1975 goto ProcessSigTerm;
1976
1977 childPid = waitpid(-1, &waitStatus, WNOHANG);
1978
1979 if (childPid == -1 || childPid == 0)
1980 break;
1981
1982 for (s = fcgi_servers; s != NULL; s = s->next) {
1983 if (s->directive == APP_CLASS_EXTERNAL)
1984 continue;
1985
1986 if (s->directive == APP_CLASS_DYNAMIC)
1987 numChildren = dynamicMaxClassProcs;
1988 else
1989 numChildren = s->numProcesses;
1990
1991 for (i = 0; i < numChildren; i++) {
1992 if (s->procs[i].pid == childPid)
1993 goto ChildFound;
1994 }
1995 }
1996
1997 /* TODO: print something about this unknown child */
1998 continue;
1999
2000 ChildFound:
2001 s->procs[i].pid = -1;
2002
2003 if (s->directive == APP_CLASS_STANDARD) {
2004 /* Always restart static apps */
2005 s->procs[i].state = FCGI_START_STATE;
2006 if (! (WIFEXITED(waitStatus) && (WEXITSTATUS(waitStatus) == 0))) {
2007 /* don't bump the failure count if the app exited with 0 */
2008 s->numFailures++;
2009 }
2010 }
2011 else {
2012 s->numProcesses--;
2013 fcgi_dynamic_total_proc_count--;
2014
2015 if (s->procs[i].state == FCGI_VICTIM_STATE) {
2016 s->procs[i].state = FCGI_KILLED_STATE;
2017 }
2018 else {
2019 /* A dynamic app died or exited without provocation from the PM */
2020
2021 if (! (WIFEXITED(waitStatus) && (WEXITSTATUS(waitStatus) == 0))) {
2022 /* don't bump the failure count if the app exited with 0 */
2023 s->numFailures++;
2024 }
2025
2026 if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0))
2027 s->procs[i].state = FCGI_START_STATE;
2028 else
2029 s->procs[i].state = FCGI_READY_STATE;
2030 }
2031 }
2032
2033 if (WIFEXITED(waitStatus)) {
2034 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
2035 "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'",
2036 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
2037 s->fs_path, (long) childPid, WEXITSTATUS(waitStatus));
2038 }
2039 else if (WIFSIGNALED(waitStatus)) {
2040 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
2041 "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s",
2042 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
2043 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus),
2044 #ifdef WCOREDUMP
2045 WCOREDUMP(waitStatus) ? ", a core file may have been generated" : "");
2046 #else
2047 "");
2048 #endif
2049 }
2050 else if (WIFSTOPPED(waitStatus)) {
2051 ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server,
2052 "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)",
2053 (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "",
2054 s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus));
2055 }
2056 } /* for (;;), waitpid() */
2057
2058 #else /* WIN32 */
2059
2060 /* wait for an event to occur or timer expires */
2061 expire = time(NULL) + sleepSeconds;
2062 dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000);
2063
2064 if (dwRet == WAIT_FAILED) {
2065 /* There is something seriously wrong here */
2066 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
2067 "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down");
2068 bTimeToDie = TRUE;
2069 }
2070
2071 if (dwRet != WAIT_TIMEOUT) {
2072 now = time(NULL);
2073
2074 if (now < expire)
2075 alarmLeft = expire - now;
2076 }
2077
2078 /*
2079 * Dynamic fcgi process management
2080 */
2081 if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) {
2082 if (dwRet == MBOX_EVENT) {
2083 read_ready = 1;
2084 }
2085
2086 now = time(NULL);
2087
2088 dynamic_read_msgs(read_ready);
2089
2090 if(fcgi_dynamic_epoch == 0) {
2091 fcgi_dynamic_epoch = now;
2092 }
2093
2094 if ((now-fcgi_dynamic_epoch >= (int) dynamicKillInterval) ||
2095 ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) {
2096 dynamic_kill_idle_fs_procs();
2097 fcgi_dynamic_epoch = now;
2098 }
2099 read_ready = 0;
2100 }
2101 else if (dwRet == WAKE_EVENT) {
2102 continue;
2103 }
2104 else if (dwRet == TERM_EVENT) {
2105 ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server,
2106 "FastCGI: Termination event received process manager shutting down");
2107
2108 bTimeToDie = TRUE;
2109 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2110
2111 goto ProcessSigTerm;
2112 }
2113 else {
2114 /* Have an received an unknown event - should not happen */
2115 ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server,
2116 "FastCGI: WaitForMultipleobjects() return an unrecognized event");
2117
2118 bTimeToDie = TRUE;
2119 dwRet = WaitForSingleObject(child_wait_thread, INFINITE);
2120
2121 goto ProcessSigTerm;
2122 }
2123
2124 #endif /* WIN32 */
2125
2126 } /* for (;;), the whole shoot'n match */
2127
2128 ProcessSigTerm:
2129 /*
2130 * Kill off the children, then exit.
2131 */
2132 shutdown_all();
2133
2134 #ifdef WIN32
2135 return;
2136 #else
2137 exit(0);
2138 #endif
2139 }
2140
2141 #ifdef WIN32
2142 int fcgi_pm_add_job(fcgi_pm_job *new_job)
2143 {
2144 int rv = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT);
2145
2146 if (rv != WAIT_OBJECT_0 && rv != WAIT_ABANDONED)
2147 {
2148 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2149 "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!");
2150 return -1;
2151 }
2152
2153 new_job->next = fcgi_dynamic_mbox;
2154 fcgi_dynamic_mbox = new_job;
2155
2156 if (! ReleaseMutex(fcgi_dynamic_mbox_mutex))
2157 {
2158 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
2159 "FastCGI: failed to release the dynamic mbox mutex - something is broke?!");
2160 }
2161
2162 return 0;
2163 }
2164 #endif