]> granicus.if.org Git - apache/blob - server/mpm/mpmt_beos/mpmt_beos.c
Cleanup the ZZZ comments. Basically these used to mark places where APR
[apache] / server / mpm / mpmt_beos / mpmt_beos.c
1 /* ====================================================================
2  * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
3  * 
4  * Redistribution and use in source and binary forms, with or without 
5  * modification, are permitted provided that the following conditions 
6  * are met: 
7  * 
8  * 1. Redistributions of source code must retain the above copyright 
9  *    notice, this list of conditions and the following disclaimer.  
10  * 
11  * 2. Redistributions in binary form must reproduce the above copyright 
12  *    notice, this list of conditions and the following disclaimer in 
13  *    the documentation and/or other materials provided with the 
14  *    distribution. 
15  * 
16  * 3. All advertising materials mentioning features or use of this 
17  *    software must display the following acknowledgment: 
18  *    "This product includes software developed by the Apache Group 
19  *    for use in the Apache HTTP server project (http://www.apache.org/)." 
20  * 
21  * 4. The names "Apache Server" and "Apache Group" must not be used to 
22  *    endorse or promote products derived from this software without 
23  *    prior written permission. For written permission, please contact 
24  *    apache@apache.org. 
25  * 
26  * 5. Products derived from this software may not be called "Apache" 
27  *    nor may "Apache" appear in their names without prior written 
28  *    permission of the Apache Group. 
29  * 
30  * 6. Redistributions of any form whatsoever must retain the following 
31  *    acknowledgment: 
32  *    "This product includes software developed by the Apache Group 
33  *    for use in the Apache HTTP server project (http://www.apache.org/)." 
34  * 
35  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY 
36  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
37  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
38  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR 
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
41  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
42  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
43  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
44  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
45  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
46  * OF THE POSSIBILITY OF SUCH DAMAGE. 
47  * ==================================================================== 
48  * 
49  * This software consists of voluntary contributions made by many 
50  * individuals on behalf of the Apache Group and was originally based 
51  * on public domain software written at the National Center for 
52  * Supercomputing Applications, University of Illinois, Urbana-Champaign. 
53  * For more information on the Apache Group and the Apache HTTP server 
54  * project, please see <http://www.apache.org/>. 
55  * 
56  */ 
57
58 /* This module is effectivly mpmt_pthread much modified to
59  * allow it work on BeOS.  It's stable and works OK.
60  */
61  
62 #define CORE_PRIVATE 
63  
64 #include "apr_portable.h"
65 #include "httpd.h" 
66 #include "http_main.h" 
67 #include "http_log.h" 
68 #include "http_config.h"        /* for read_config */ 
69 #include "http_core.h"          /* for get_remote_host */ 
70 #include "http_connection.h"
71 #include "ap_mpm.h"
72 #include "beosd.h"
73 #include "iol_socket.h"
74 #include "ap_listen.h"
75 #include "scoreboard.h" 
76 #include "acceptlock.h"
77 #include "mutex.h"
78 #include "poll.h"
79
80 /*
81  * Actual definitions of config globals
82  */
83
84 int ap_threads_per_child=0;         /* Worker threads per child */
85 int ap_max_requests_per_child=0;
86 static char *ap_pid_fname=NULL;
87 static char *ap_scoreboard_fname=NULL;
88 static int ap_daemons_to_start=0;
89 static int min_spare_threads=0;
90 static int max_spare_threads=0;
91 static int ap_daemons_limit=0;
92 static time_t ap_restart_time=0;
93 API_VAR_EXPORT int ap_extended_status = 0;
94 static int workers_may_exit = 0;
95 static int requests_this_child;
96 static int num_listenfds = 0;
97 static struct pollfd *listenfds;
98
99 /* The structure used to pass unique initialization info to each thread */
100 typedef struct {
101     int pid;
102     thread_id tid;
103     int sd;
104     ap_context_t *tpool; /* "pthread" would be confusing */
105 } proc_info;
106
107 #define SERVER_DEAD 0
108 #define SERVER_DYING 1
109 #define SERVER_ALIVE 2
110
111 static struct {
112     pid_t pid;
113     unsigned char status;
114 } child_table[HARD_SERVER_LIMIT];
115
116 #if 0
117 #define SAFE_ACCEPT(stmt) do {if (ap_listeners->next != NULL) {stmt;}} while (0)
118 #else
119 #define SAFE_ACCEPT(stmt) do {stmt;} while (0)
120 #endif
121
122 /*
123  * The max child slot ever assigned, preserved across restarts.  Necessary
124  * to deal with MaxClients changes across SIGWINCH restarts.  We use this
125  * value to optimize routines that have to scan the entire scoreboard.
126  */
127 int max_daemons_limit = -1;
128 static char ap_coredump_dir[MAX_STRING_LEN];
129 port_id port_of_death;
130
131 /* *Non*-shared http_main globals... */
132
133 static server_rec *server_conf;
134
135 /* one_process --- debugging mode variable; can be set from the command line
136  * with the -X flag.  If set, this gets you the child_main loop running
137  * in the process which originally started up (no detach, no make_child),
138  * which is a pretty nice debugging environment.  (You'll get a SIGHUP
139  * early in standalone_main; just continue through.  This is the server
140  * trying to kill off any child processes which it might have lying
141  * around --- Apache doesn't keep track of their pids, it just sends
142  * SIGHUP to the process group, ignoring it in the root process.
143  * Continue through and you'll be fine.).
144  */
145
146 static int one_process = 0;
147
148 #ifdef DEBUG_SIGSTOP
149 int raise_sigstop_flags;
150 #endif
151
152 #ifdef HAS_OTHER_CHILD
153 /* used to maintain list of children which aren't part of the scoreboard */
154 typedef struct other_child_rec other_child_rec;
155 struct other_child_rec {
156     other_child_rec *next;
157     int pid;
158     void (*maintenance) (int, void *, ap_wait_t);
159     void *data;
160     int write_fd;
161 };
162 static other_child_rec *other_children;
163 #endif
164
165 static ap_context_t *pconf;             /* Pool for config stuff */
166 static ap_context_t *pchild;            /* Pool for httpd child stuff */
167
168 static int my_pid; /* Linux getpid() doesn't work except in main thread. Use
169                       this instead */
170 /* Keep track of the number of worker threads currently active */
171 static int worker_thread_count;
172 static be_mutex_t worker_thread_count_mutex;
173
174 /* Global, alas, so http_core can talk to us */
175 enum server_token_type ap_server_tokens = SrvTk_FULL;
176
177 API_EXPORT(const server_rec *) ap_get_server_conf(void)
178 {
179     return (server_conf);
180 }
181
182 API_EXPORT(int) ap_get_max_daemons(void)
183 {
184     return max_daemons_limit;
185 }
186
187 /* a clean exit from a child with proper cleanup 
188    static void clean_child_exit(int code) __attribute__ ((noreturn)); */
189 void clean_child_exit(int code)
190 {
191     if (pchild) {
192         ap_destroy_pool(pchild);
193     }
194     exit(code);
195 }
196
197 /*****************************************************************
198  * dealing with other children
199  */
200
201 #ifdef HAS_OTHER_CHILD
202 API_EXPORT(void) ap_register_other_child(int pid,
203                        void (*maintenance) (int reason, void *, ap_wait_t status),
204                           void *data, int write_fd)
205 {
206     other_child_rec *ocr;
207
208     ocr = ap_palloc(pconf, sizeof(*ocr));
209     ocr->pid = pid;
210     ocr->maintenance = maintenance;
211     ocr->data = data;
212     ocr->write_fd = write_fd;
213     ocr->next = other_children;
214     other_children = ocr;
215 }
216
217 /* note that since this can be called by a maintenance function while we're
218  * scanning the other_children list, all scanners should protect themself
219  * by loading ocr->next before calling any maintenance function.
220  */
221 API_EXPORT(void) ap_unregister_other_child(void *data)
222 {
223     other_child_rec **pocr, *nocr;
224
225     for (pocr = &other_children; *pocr; pocr = &(*pocr)->next) {
226         if ((*pocr)->data == data) {
227             nocr = (*pocr)->next;
228             (*(*pocr)->maintenance) (OC_REASON_UNREGISTER, (*pocr)->data, -1);
229             *pocr = nocr;
230             /* XXX: um, well we've just wasted some space in pconf ? */
231             return;
232         }
233     }
234 }
235
236 /* test to ensure that the write_fds are all still writable, otherwise
237  * invoke the maintenance functions as appropriate */
238 static void probe_writable_fds(void)
239 {
240     return;
241 #if 0
242     fd_set writable_fds;
243     int fd_max;
244     other_child_rec *ocr, *nocr;
245     struct timeval tv;
246     int rc;
247
248     if (other_children == NULL)
249         return;
250
251     fd_max = 0;
252     FD_ZERO(&writable_fds);
253     do {
254         for (ocr = other_children; ocr; ocr = ocr->next) {
255             if (ocr->write_fd == -1)
256                 continue;
257             FD_SET(ocr->write_fd, &writable_fds);
258             if (ocr->write_fd > fd_max) {
259                 fd_max = ocr->write_fd;
260             }
261         }
262         if (fd_max == 0)
263             return;
264
265         tv.tv_sec = 0;
266         tv.tv_usec = 0;
267         rc = ap_select(fd_max + 1, NULL, &writable_fds, NULL, &tv);
268     } while (rc == -1 && errno == EINTR);
269
270     if (rc == -1) {
271         /* XXX: uhh this could be really bad, we could have a bad file
272          * descriptor due to a bug in one of the maintenance routines */
273         ap_log_unixerr("probe_writable_fds", "select",
274                     "could not probe writable fds", server_conf);
275         return;
276     }
277     if (rc == 0)
278         return;
279
280     for (ocr = other_children; ocr; ocr = nocr) {
281         nocr = ocr->next;
282         if (ocr->write_fd == -1)
283             continue;
284         if (FD_ISSET(ocr->write_fd, &writable_fds))
285             continue;
286         (*ocr->maintenance) (OC_REASON_UNWRITABLE, ocr->data, -1);
287     }
288 #endif
289 }
290
291 /* possibly reap an other_child, return 0 if yes, -1 if not */
292 static int reap_other_child(int pid, ap_wait_t status)
293 {
294     other_child_rec *ocr, *nocr;
295
296     for (ocr = other_children; ocr; ocr = nocr) {
297         nocr = ocr->next;
298         if (ocr->pid != pid)
299             continue;
300         ocr->pid = -1;
301         (*ocr->maintenance) (OC_REASON_DEATH, ocr->data, status);
302         return 0;
303     }
304     return -1;
305 }
306 #endif
307
308 static void reclaim_child_processes(int terminate)
309 {
310     int i, status;
311     long int waittime = 1024 * 16;      /* in usecs */
312     struct timeval tv;
313     int waitret, tries;
314     int not_dead_yet;
315 #ifdef HAS_OTHER_CHILD
316     other_child_rec *ocr, *nocr;
317 #endif
318
319     for (tries = terminate ? 4 : 1; tries <= 9; ++tries) {
320         /* don't want to hold up progress any more than 
321          * necessary, but we need to allow children a few moments to exit.
322          * Set delay with an exponential backoff.
323          */
324         tv.tv_sec = waittime / 1000000;
325         tv.tv_usec = waittime % 1000000;
326         waittime = waittime * 4;
327         ap_select(0, NULL, NULL, NULL, &tv);
328
329         /* now see who is done */
330         not_dead_yet = 0;
331         for (i = 0; i < max_daemons_limit; ++i) {
332         int pid;
333         if (child_table[i].status == SERVER_DEAD)
334             continue;
335
336             pid = child_table[i].pid;
337
338             waitret = waitpid(pid, &status, WNOHANG);
339             if (waitret == pid || waitret == -1) {
340                     child_table[i].status = SERVER_DEAD;
341                     continue;
342             }
343             ++not_dead_yet;
344             switch (tries) {
345             case 1:     /*  16ms */
346             case 2:     /*  82ms */
347                 break;
348             case 3:     /* 344ms */
349             case 4:     /*  16ms */
350             case 5:     /*  82ms */
351             case 6:     /* 344ms */
352             case 7:     /* 1.4sec */
353                 /* ok, now it's being annoying */
354                 ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING,
355                             errno, server_conf,
356                    "child process %d still did not exit, sending a SIGTERM",
357                             pid);
358                 kill(pid, SIGTERM);
359                 break;
360             case 8:     /*  6 sec */
361                 /* die child scum */
362                 ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, errno, server_conf,
363                    "child process %d still did not exit, sending a SIGKILL",
364                             pid);
365                 kill(pid, SIGKILL);
366                 break;
367             case 9:     /* 14 sec */
368                 /* gave it our best shot, but alas...  If this really 
369                  * is a child we are trying to kill and it really hasn't
370                  * exited, we will likely fail to bind to the port
371                  * after the restart.
372                  */
373                 ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, errno, server_conf,
374                             "could not make child process %d exit, "
375                             "attempting to continue anyway", pid);
376                 break;
377             }
378         }
379 #ifdef HAS_OTHER_CHILD
380         for (ocr = other_children; ocr; ocr = nocr) {
381             nocr = ocr->next;
382             if (ocr->pid == -1)
383                 continue;
384
385             waitret = waitpid(ocr->pid, &status, WNOHANG);
386             if (waitret == ocr->pid) {
387                 ocr->pid = -1;
388                 (*ocr->maintenance) (OC_REASON_DEATH, ocr->data, status);
389             }
390             else if (waitret == 0) {
391                 (*ocr->maintenance) (OC_REASON_RESTART, ocr->data, -1);
392                 ++not_dead_yet;
393             }
394             else if (waitret == -1) {
395                 /* uh what the heck? they didn't call unregister? */
396                 ocr->pid = -1;
397                 (*ocr->maintenance) (OC_REASON_LOST, ocr->data, -1);
398             }
399         }
400 #endif
401         if (!not_dead_yet) {
402             /* nothing left to wait for */
403             break;
404         }
405     }
406 }
407
408 /* Finally, this routine is used by the caretaker process to wait for
409  * a while...
410  */
411
412 /* number of calls to wait_or_timeout between writable probes */
413 #ifndef INTERVAL_OF_WRITABLE_PROBES
414 #define INTERVAL_OF_WRITABLE_PROBES 10
415 #endif
416 static int wait_or_timeout_counter;
417
418 static int wait_or_timeout(ap_wait_t *status)
419 {
420     struct timeval tv;
421     int ret;
422
423     ++wait_or_timeout_counter;
424     if (wait_or_timeout_counter == INTERVAL_OF_WRITABLE_PROBES) {
425         wait_or_timeout_counter = 0;
426 #ifdef HAS_OTHER_CHILD
427         probe_writable_fds();
428 #endif
429     }
430     ret = waitpid(-1, status, WNOHANG);
431     if (ret == -1 && errno == EINTR) {
432         return -1;
433     }
434     if (ret > 0) {
435         return ret;
436     }
437     tv.tv_sec = SCOREBOARD_MAINTENANCE_INTERVAL / 1000000;
438     tv.tv_usec = SCOREBOARD_MAINTENANCE_INTERVAL % 1000000;
439     ap_select(0, NULL, NULL, NULL, &tv);
440     return -1;
441 }
442
443 /* handle all varieties of core dumping signals */
444 static void sig_coredump(int sig)
445 {
446     chdir(ap_coredump_dir);
447     signal(sig, SIG_DFL);
448     kill(my_pid, sig);
449     /* At this point we've got sig blocked, because we're still inside
450      * the signal handler.  When we leave the signal handler it will
451      * be unblocked, and we'll take the signal... and coredump or whatever
452      * is appropriate for this particular Unix.  In addition the parent
453      * will see the real signal we received -- whereas if we called
454      * abort() here, the parent would only see SIGABRT.
455      */
456 }
457
458 static void just_die(int sig)
459 {
460     clean_child_exit(0);
461 }
462
463 /*****************************************************************
464  * Connection structures and accounting...
465  */
466
467 /* volatile just in case */
468 static int volatile shutdown_pending;
469 static int volatile restart_pending;
470 static int volatile is_graceful;
471
472 /*
473  * ap_start_shutdown() and ap_start_restart(), below, are a first stab at
474  * functions to initiate shutdown or restart without relying on signals. 
475  * Previously this was initiated in sig_term() and restart() signal handlers, 
476  * but we want to be able to start a shutdown/restart from other sources --
477  * e.g. on Win32, from the service manager. Now the service manager can
478  * call ap_start_shutdown() or ap_start_restart() as appropiate.  Note that
479  * these functions can also be called by the child processes, since global
480  * variables are no longer used to pass on the required action to the parent.
481  *
482  * These should only be called from the parent process itself, since the
483  * parent process will use the shutdown_pending and restart_pending variables
484  * to determine whether to shutdown or restart. The child process should
485  * call signal_parent() directly to tell the parent to die -- this will
486  * cause neither of those variable to be set, which the parent will
487  * assume means something serious is wrong (which it will be, for the
488  * child to force an exit) and so do an exit anyway.
489  */
490
491 void ap_start_shutdown(void)
492 {
493     if (shutdown_pending == 1) {
494         /* Um, is this _probably_ not an error, if the user has
495          * tried to do a shutdown twice quickly, so we won't
496          * worry about reporting it.
497          */
498         return;
499     }
500     shutdown_pending = 1;
501 }
502
503 /* do a graceful restart if graceful == 1 */
504 void ap_start_restart(int graceful)
505 {
506
507     if (restart_pending == 1) {
508         /* Probably not an error - don't bother reporting it */
509         return;
510     }
511     restart_pending = 1;
512     is_graceful = graceful;
513 }
514
515 static void sig_term(int sig)
516 {
517     ap_start_shutdown();
518 }
519
520 static void restart(int sig)
521 {
522     ap_start_restart(sig == SIGWINCH);
523 }
524
525 static void set_signals(void)
526 {
527     struct sigaction sa;
528
529     sigemptyset(&sa.sa_mask);
530     sa.sa_flags = 0;
531
532     if (!one_process) {
533         sa.sa_handler = sig_coredump;
534
535         if (sigaction(SIGSEGV, &sa, NULL) < 0)
536             ap_log_error(APLOG_MARK, APLOG_WARNING, errno, server_conf, "sigaction(SIGSEGV)");
537         if (sigaction(SIGBUS, &sa, NULL) < 0)
538             ap_log_error(APLOG_MARK, APLOG_WARNING, errno, server_conf, "sigaction(SIGBUS)");
539         if (sigaction(SIGABRT, &sa, NULL) < 0)
540             ap_log_error(APLOG_MARK, APLOG_WARNING, errno, server_conf, "sigaction(SIGABRT)");
541         if (sigaction(SIGILL, &sa, NULL) < 0)
542             ap_log_error(APLOG_MARK, APLOG_WARNING, errno, server_conf, "sigaction(SIGILL)");
543         sa.sa_flags = 0;
544     }
545     sa.sa_handler = sig_term;
546     if (sigaction(SIGTERM, &sa, NULL) < 0)
547             ap_log_error(APLOG_MARK, APLOG_WARNING, errno, server_conf, "sigaction(SIGTERM)");
548     if (sigaction(SIGINT, &sa, NULL) < 0)
549         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, server_conf, "sigaction(SIGINT)");
550     
551     sa.sa_handler = SIG_IGN;
552     if (sigaction(SIGPIPE, &sa, NULL) < 0)
553         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, server_conf, "sigaction(SIGPIPE)");
554
555     /* we want to ignore HUPs and WINCH while we're busy processing one */
556     sigaddset(&sa.sa_mask, SIGHUP);
557     sigaddset(&sa.sa_mask, SIGWINCH);
558     sa.sa_handler = restart;
559     if (sigaction(SIGHUP, &sa, NULL) < 0)
560         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, server_conf, "sigaction(SIGHUP)");
561     if (sigaction(SIGWINCH, &sa, NULL) < 0)
562             ap_log_error(APLOG_MARK, APLOG_WARNING, errno, server_conf, "sigaction(SIGWINCH)");
563 }
564
565 static void process_child_status(int pid, ap_wait_t status)
566 {
567     /* Child died... if it died due to a fatal error,
568         * we should simply bail out.
569         */
570     if ((WIFEXITED(status)) &&
571         WEXITSTATUS(status) == APEXIT_CHILDFATAL) {
572         ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, errno, server_conf,
573                         "Child %d returned a Fatal error... \n"
574                         "Apache is exiting!",
575                         pid);
576         exit(APEXIT_CHILDFATAL);
577     }
578     if (WIFSIGNALED(status)) {
579         switch (WTERMSIG(status)) {
580         case SIGTERM:
581         case SIGHUP:
582         case SIGUSR1:
583         case SIGKILL:
584             break;
585         default:
586 #ifdef SYS_SIGLIST
587 #ifdef WCOREDUMP
588             if (WCOREDUMP(status)) {
589                 ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,
590                              errno, server_conf,
591                              "child pid %d exit signal %s (%d), "
592                              "possible coredump in %s",
593                              pid, (WTERMSIG(status) >= NumSIG) ? "" : 
594                              SYS_SIGLIST[WTERMSIG(status)], WTERMSIG(status),
595                              ap_coredump_dir);
596             }
597             else {
598 #endif
599                 ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,
600                              errno, server_conf,
601                              "child pid %d exit signal %s (%d)", pid,
602                              SYS_SIGLIST[WTERMSIG(status)], WTERMSIG(status));
603 #ifdef WCOREDUMP
604             }
605 #endif
606 #else
607             ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,
608                          errno, server_conf,
609                          "child pid %d exit signal %d",
610                          pid, WTERMSIG(status));
611 #endif
612         }
613     }
614 }
615
616 static int setup_listeners(server_rec *s)
617 {
618     ap_listen_rec *lr;
619     int num_listeners = 0;
620     if (ap_listen_open(s->process, s->port)) {
621        return 0;
622     }
623     for (lr = ap_listeners; lr; lr = lr->next) {
624         num_listeners++;
625     }
626     return num_listeners;
627 }
628
629 /*****************************************************************
630  * Here follows a long bunch of generic server bookkeeping stuff...
631  */
632
633 #define sock_disable_nagle(s)   /* NOOP */
634
635 int ap_graceful_stop_signalled(void)
636 {
637     /* XXX - Does this really work? - Manoj */
638     return is_graceful;
639 }
640
641 /*****************************************************************
642  * Child process main loop.
643  */
644
645 static void process_socket(ap_context_t *p, ap_socket_t *sock, int my_child_num, int my_thread_num)
646 {
647     BUFF *conn_io;
648     conn_rec *current_conn;
649     ap_iol *iol;
650     long conn_id = my_child_num * HARD_THREAD_LIMIT + my_thread_num;
651     int csd;
652
653     iol = beos_attach_socket(sock);
654     if (iol == NULL) {
655         if (errno == EBADF) {
656             ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, errno, NULL,
657                 "filedescriptor (%u) larger than FD_SETSIZE (%u) "
658                 "found, you probably need to rebuild Apache with a "
659                 "larger FD_SETSIZE", csd, FD_SETSIZE);
660         }
661         else {
662             ap_log_error(APLOG_MARK, APLOG_WARNING, errno, NULL,
663                 "error attaching to socket");
664         }
665         ap_close_socket(sock);
666         return;
667     }
668
669     conn_io = ap_bcreate(p, B_RDWR);
670     ap_bpush_iol(conn_io, iol);
671
672     current_conn = ap_new_apr_connection(p, server_conf, conn_io, sock, 
673                                          conn_id);
674
675     ap_process_connection(current_conn);
676 }
677
678 static int32 worker_thread(void * dummy)
679 {
680     proc_info * ti = dummy;
681     int process_slot = ti->pid;
682     int thread_slot = ti->tid;
683     ap_context_t *tpool = ti->tpool;
684     struct sockaddr sa_client;
685     ap_socket_t *csd = NULL;
686     ap_context_t *ptrans;               /* Pool for per-transaction stuff */
687     ap_socket_t *sd = NULL;
688     int srv;
689     int curr_pollfd, last_pollfd = 0;
690     int thesock;
691     sigset_t sig_mask;
692     
693     free(ti);
694
695     /* block the signals for this thread */
696     sigfillset(&sig_mask);
697     sigprocmask(SIG_BLOCK, &sig_mask, NULL);
698
699     ap_create_context(&ptrans, tpool);
700
701     be_mutex_lock(&worker_thread_count_mutex);
702     worker_thread_count++;
703     be_mutex_unlock(&worker_thread_count_mutex);
704
705     /* TODO: Switch to a system where threads reuse the results from earlier
706        poll calls - manoj */
707     while (!workers_may_exit) {
708         workers_may_exit |= (ap_max_requests_per_child != 0) && (requests_this_child <= 0);
709         if (workers_may_exit) break;
710
711         SAFE_ACCEPT(intra_mutex_on(0));
712         if (workers_may_exit) {
713             SAFE_ACCEPT(intra_mutex_off(0));
714             break;
715         }
716         SAFE_ACCEPT(accept_mutex_on(0));
717         while (!workers_may_exit) {
718             srv = poll(listenfds, num_listenfds + 1, -1);
719             if (srv < 0) {
720                 if (errno == EINTR) {
721                     continue;
722                 }
723
724                 /* poll() will only return errors in catastrophic
725                  * circumstances. Let's try exiting gracefully, for now. */
726                 ap_log_error(APLOG_MARK, APLOG_ERR,errno,  (const server_rec *)
727                              ap_get_server_conf(), "poll: (listen)");
728                 workers_may_exit = 1;
729             }
730
731             if (workers_may_exit) break;
732
733             if (num_listenfds == 1) {
734                 sd = ap_listeners->sd;
735                 goto got_fd;
736             }
737             else {
738                 /* find a listener */
739                 curr_pollfd = last_pollfd;
740                 do {
741                     curr_pollfd++;
742                     if (curr_pollfd > num_listenfds) {
743                         curr_pollfd = 1;
744                     }
745                     /* XXX: Should we check for POLLERR? */
746                     if (listenfds[curr_pollfd].revents & POLLIN) {
747                         last_pollfd = curr_pollfd;
748                         ap_put_os_sock(&sd, &listenfds[curr_pollfd].fd, tpool); 
749                         goto got_fd;
750                     }
751                 } while (curr_pollfd != last_pollfd);
752             }
753         }
754     got_fd:
755         if (!workers_may_exit) {
756             ap_accept(&csd, sd, ptrans);
757             SAFE_ACCEPT(accept_mutex_off(0));
758             SAFE_ACCEPT(intra_mutex_off(0));
759             process_socket(ptrans, csd, process_slot,
760                        thread_slot);
761             requests_this_child--;
762         }
763         else {
764             SAFE_ACCEPT(accept_mutex_off(0));
765             SAFE_ACCEPT(intra_mutex_off(0));
766             break;
767         }
768         ap_clear_pool(ptrans);
769     }
770
771     ap_destroy_pool(tpool);
772     be_mutex_lock(&worker_thread_count_mutex);
773     worker_thread_count--;
774     if (worker_thread_count == 0) {
775         /* All the threads have exited, now finish the shutdown process
776          * by signalling the sigwait thread */
777         kill(my_pid, SIGTERM);
778     }
779     be_mutex_unlock(&worker_thread_count_mutex);
780
781     return (0);
782 }
783
784
785 static int32 child_main(void * data)
786 {
787     int child_num_arg = (int) data;
788     sigset_t sig_mask;
789     thread_id thread;
790     int i;
791     int my_child_num = child_num_arg;
792     proc_info *my_info = NULL;
793     ap_listen_rec *lr;
794     struct sigaction sa;
795     int32 msg;
796     char buf;
797     
798     my_pid = getpid();
799     ap_create_context(&pchild, pconf);
800
801     /*stuff to do before we switch id's, so we have permissions.*/
802     SAFE_ACCEPT(intra_mutex_init(pchild, 1));
803     SAFE_ACCEPT(accept_mutex_child_init(pchild));
804
805     if (beosd_setup_child()) {
806         clean_child_exit(APEXIT_CHILDFATAL);
807     }
808
809     ap_child_init_hook(pchild, server_conf);
810
811     /*done with init critical section */
812
813     /* All threads should mask signals out, accoring to sigwait(2) man page */
814     sigfillset(&sig_mask);
815     sigprocmask(SIG_BLOCK, &sig_mask, NULL);
816     
817     requests_this_child = ap_max_requests_per_child;
818     
819     /* Set up the pollfd array */
820     listenfds = ap_palloc(pchild, sizeof(struct pollfd) * (num_listenfds));
821     for (lr = ap_listeners, i = 0; i < num_listenfds; lr = lr->next, ++i) {
822         ap_get_os_sock(&listenfds[i].fd , lr->sd);
823         listenfds[i].events = POLLIN; /* should we add POLLPRI ?*/
824         listenfds[i].revents = 0;
825     }
826
827     /* Setup worker threads */
828
829     worker_thread_count = 0;
830     be_mutex_init(&worker_thread_count_mutex, NULL);
831
832     for (i=0; i < ap_threads_per_child; i++) {
833
834         my_info = (proc_info *)malloc(sizeof(proc_info));
835         if (my_info == NULL) {
836             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, server_conf,
837                          "malloc: out of memory");
838             clean_child_exit(APEXIT_CHILDFATAL);
839         }
840         my_info->pid = my_child_num;
841         my_info->tid = i;
842         my_info->sd = 0;
843         ap_create_context(&my_info->tpool, pchild);
844         
845         /* We are creating threads right now */
846
847         if ((thread = spawn_thread(worker_thread, "httpd_worker_thread",
848               B_NORMAL_PRIORITY, my_info)) < B_NO_ERROR) {
849             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, server_conf,
850                          "spawn_thread: unable to create worker thread");
851             /* In case system resources are maxxed out, we don't want
852                Apache running away with the CPU trying to fork over and
853                over and over again if we exit. */
854         sleep(10);
855             clean_child_exit(APEXIT_CHILDFATAL);
856         }
857         resume_thread(thread);
858
859         /* We let each thread update it's own scoreboard entry.  This is done
860          * because it let's us deal with tid better.
861          */
862     }
863
864     sigemptyset(&sa.sa_mask);
865     sa.sa_flags = 0;
866     sa.sa_handler = just_die;
867     sigaction(SIGTERM, &sa, NULL);
868     sigaction(SIGINT, &sa, NULL);
869     /* this blocks until it gets a message... */
870     read_port(port_of_death, &msg, &buf, 1);
871
872     return (0);
873 }
874
875 static int make_child(server_rec *s, int slot, time_t now)
876 {
877     thread_id tid;
878     
879     if (slot + 1 > max_daemons_limit) {
880         max_daemons_limit = slot + 1;
881     }
882
883     if (one_process) {
884         set_signals();
885         child_table[slot].pid = getpid();
886         child_table[slot].status = SERVER_ALIVE;
887             //child_main(slot);
888     }
889
890     tid = spawn_thread(child_main, "httpd_child", B_NORMAL_PRIORITY,
891         (void*)slot);
892     if (tid < B_NO_ERROR) {
893         ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, 
894             "spawn_thread: Unable to fork new process");
895         /* In case system resources are maxxed out, we don't want
896            Apache running away with the CPU trying to fork over and
897            over and over again. */
898         sleep(10);
899
900         return -1;
901     }
902     resume_thread(tid);
903     
904     child_table[slot].pid = getpid();
905     child_table[slot].status = SERVER_ALIVE;
906     return 0;
907 }
908
909 /* start up a bunch of children */
910 static void startup_children(int number_to_start)
911 {
912     int i;
913
914     for (i = 0; number_to_start && i < ap_daemons_limit; ++i) {
915         if (child_table[i].status  != SERVER_DEAD) {
916             continue;
917         }
918         if (make_child(server_conf, i, 0) < 0) {
919             break;
920         }
921         --number_to_start;
922     }
923 }
924
925
926 /*
927  * spawn_rate is the number of children that will be spawned on the
928  * next maintenance cycle if there aren't enough idle servers.  It is
929  * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by
930  * without the need to spawn.
931  */
932 static int spawn_rate = 1;
933 #ifndef MAX_SPAWN_RATE
934 #define MAX_SPAWN_RATE  (32)
935 #endif
936 static int hold_off_on_exponential_spawning;
937
938 static void perform_idle_server_maintenance(void)
939 {
940     int i, j;
941     int idle_thread_count;
942     time_t now = 0;
943     int free_length;
944     int free_slots[MAX_SPAWN_RATE];
945     int last_non_dead;
946     int total_non_dead;
947
948     /* initialize the free_list */
949     free_length = 0;
950
951     ap_check_signals();
952     
953     for (i = 0; i < ap_daemons_limit; ++i) {
954         if (child_table[i].status == SERVER_DEAD) {
955             if (free_length < spawn_rate) {
956                 free_slots[free_length] = i;
957                 ++free_length;
958             }
959         }
960         else {
961             last_non_dead = i;
962         }
963
964         if (i >= max_daemons_limit && free_length >= spawn_rate) {
965                  break;
966             }
967     }
968     max_daemons_limit = last_non_dead + 1;
969
970     if (free_length > 0) {
971         for (i = 0; i < free_length; ++i) {
972                 make_child(server_conf, free_slots[i], now);
973             }
974             /* the next time around we want to spawn twice as many if this
975              * wasn't good enough, but not if we've just done a graceful
976              */
977             if (hold_off_on_exponential_spawning) {
978                 --hold_off_on_exponential_spawning;
979             } else if (spawn_rate < MAX_SPAWN_RATE) {
980                 spawn_rate *= 2;
981             }
982     } else {
983         spawn_rate = 1;
984     }
985 }
986
987 static void server_main_loop(int remaining_children_to_start)
988 {
989     int child_slot;
990     ap_wait_t status;
991     int pid;
992     int i;
993
994     while (!restart_pending && !shutdown_pending) {
995         pid = wait_or_timeout(&status);
996         
997         if (pid >= 0) {
998             process_child_status(pid, status);
999             /* non-fatal death... note that it's gone in the scoreboard. */
1000             child_slot = -1;
1001             for (i = 0; i < max_daemons_limit; ++i) {
1002                 if (child_table[i].pid == pid) {
1003                     int j;
1004
1005                     child_slot = i;
1006                     for (j = 0; j < HARD_THREAD_LIMIT; j++) {
1007                         ap_mpmt_beos_force_reset_connection_status(i * HARD_THREAD_LIMIT + j);
1008                     }
1009                     break;
1010                 }
1011             }
1012             if (child_slot >= 0) {
1013                 child_table[child_slot].status = SERVER_DEAD;
1014                 
1015                 if (remaining_children_to_start
1016                     && child_slot < ap_daemons_limit) {
1017                     /* we're still doing a 1-for-1 replacement of dead
1018                      * children with new children
1019                      */
1020                     make_child(server_conf, child_slot, time(NULL));
1021                     --remaining_children_to_start;
1022                 }
1023 #ifdef HAS_OTHER_CHILD
1024             }
1025             else if (reap_other_child(pid, status) == 0) {
1026                 /* handled */
1027 #endif
1028             }
1029             else if (is_graceful) {
1030                 /* Great, we've probably just lost a slot in the
1031                     * scoreboard.  Somehow we don't know about this
1032                     * child.
1033                     */
1034                 ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, errno, server_conf,
1035                             "long lost child came home! (pid %d)", pid);
1036             }
1037             /* Don't perform idle maintenance when a child dies,
1038              * only do it when there's a timeout.  Remember only a
1039              * finite number of children can die, and it's pretty
1040              * pathological for a lot to die suddenly.
1041              */
1042             continue;
1043         }
1044         else if (remaining_children_to_start) {
1045             /* we hit a 1 second timeout in which none of the previous
1046              * generation of children needed to be reaped... so assume
1047              * they're all done, and pick up the slack if any is left.
1048              */
1049             startup_children(remaining_children_to_start);
1050             remaining_children_to_start = 0;
1051             /* In any event we really shouldn't do the code below because
1052              * few of the servers we just started are in the IDLE state
1053              * yet, so we'd mistakenly create an extra server.
1054              */
1055             continue;
1056         }
1057
1058         perform_idle_server_maintenance();
1059     }
1060 }
1061
1062 int ap_mpm_run(ap_context_t *_pconf, ap_context_t *plog, server_rec *s)
1063 {
1064     int remaining_children_to_start;
1065
1066     pconf = _pconf;
1067     server_conf = s;
1068     port_of_death = create_port(1, "httpd_port_of_death");
1069
1070     if ((num_listenfds = setup_listeners(server_conf)) < 1) {
1071         /* XXX: hey, what's the right way for the mpm to indicate a fatal error? */
1072         ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, errno, s,
1073             "no listening sockets available, shutting down");
1074         return 1;
1075     }
1076     ap_log_pid(pconf, ap_pid_fname);
1077     SAFE_ACCEPT(accept_mutex_init(pconf, 1));
1078     if (!is_graceful) {
1079         reinit_scoreboard(pconf);
1080     }
1081
1082     set_signals();
1083     /* Don't thrash... */
1084     if (max_spare_threads < min_spare_threads + ap_threads_per_child)
1085         max_spare_threads = min_spare_threads + ap_threads_per_child;
1086
1087     /* If we're doing a graceful_restart then we're going to see a lot
1088      * of children exiting immediately when we get into the main loop
1089      * below (because we just sent them SIGWINCH).  This happens pretty
1090      * rapidly... and for each one that exits we'll start a new one until
1091      * we reach at least daemons_min_free.  But we may be permitted to
1092      * start more than that, so we'll just keep track of how many we're
1093      * supposed to start up without the 1 second penalty between each fork.
1094      */
1095     remaining_children_to_start = ap_daemons_to_start;
1096     if (remaining_children_to_start > ap_daemons_limit) {
1097         remaining_children_to_start = ap_daemons_limit;
1098     }
1099     if (!is_graceful) {
1100         startup_children(remaining_children_to_start);
1101         remaining_children_to_start = 0;
1102     }
1103     else {
1104         /* give the system some time to recover before kicking into
1105             * exponential mode */
1106         hold_off_on_exponential_spawning = 10;
1107     }
1108
1109     ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, errno, server_conf,
1110                 "%s configured -- resuming normal operations",
1111                 ap_get_server_version());
1112     ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, errno, server_conf,
1113                 "Server built: %s", ap_get_server_built());
1114     restart_pending = shutdown_pending = 0;
1115
1116     server_main_loop(remaining_children_to_start);
1117
1118     if (shutdown_pending) {
1119         /* Time to gracefully shut down:
1120          * Kill child processes, tell them to call child_exit, etc...
1121          */
1122         if (ap_killpg(getpgrp(), SIGTERM) < 0) {
1123             ap_log_error(APLOG_MARK, APLOG_WARNING, errno, server_conf,
1124              "killpg SIGTERM");
1125         }
1126         reclaim_child_processes(1);             /* Start with SIGTERM */
1127     
1128         /* cleanup pid file on normal shutdown */
1129         {
1130             const char *pidfile = NULL;
1131             pidfile = ap_server_root_relative (pconf, ap_pid_fname);
1132             if ( pidfile != NULL && unlink(pidfile) == 0)
1133                 ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO,
1134                          errno, server_conf,
1135                          "removed PID file %s (pid=%ld)",
1136                          pidfile, (long)getpid());
1137         }
1138     
1139         ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, errno, server_conf,
1140             "caught SIGTERM, shutting down");
1141     
1142         return 1;
1143     }
1144
1145     /* we've been told to restart */
1146     signal(SIGHUP, SIG_IGN);
1147
1148     if (one_process) {
1149         /* not worth thinking about */
1150         return 1;
1151     }
1152
1153     if (is_graceful) {
1154         int i, j;
1155         char char_of_death = '!';
1156
1157         ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, errno, server_conf,
1158                     "SIGWINCH received.  Doing graceful restart");
1159
1160         /* give the children the signal to die */
1161         for (i = 0; i < ap_daemons_limit;) {
1162             if(child_table[i].status != SERVER_DEAD) {
1163                 if (write_port(port_of_death, 99, &char_of_death, 1) != B_OK) {
1164                     if (errno == EINTR) continue;
1165                     ap_log_error(APLOG_MARK, APLOG_WARNING, errno, server_conf,
1166                        "write port_of_death");
1167                 }
1168             }
1169             i++;
1170         }
1171
1172     }
1173     else {
1174       /* Kill 'em all.  Since the child acts the same on the parents SIGTERM 
1175        * and a SIGHUP, we may as well use the same signal, because some user
1176        * pthreads are stealing signals from us left and right.
1177        */
1178         if (ap_killpg(getpgrp(), SIGTERM) < 0) {
1179             ap_log_error(APLOG_MARK, APLOG_WARNING, errno, server_conf,
1180                "killpg SIGTERM");
1181         }
1182         reclaim_child_processes(1);             /* Start with SIGTERM */
1183         ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, errno, server_conf,
1184                     "SIGHUP received.  Attempting to restart");
1185     }
1186     if (!is_graceful) {
1187         ap_restart_time = time(NULL); 
1188     }
1189     delete_port(port_of_death);
1190     return 0;
1191 }
1192
1193 static void mpmt_beos_pre_config(ap_context_t *pconf, ap_context_t *plog, ap_context_t *ptemp)
1194 {
1195     static int restart_num = 0;
1196
1197     one_process = !!getenv("ONE_PROCESS");
1198
1199     /* sigh, want this only the second time around */
1200     if (restart_num++ == 1) {
1201         is_graceful = 0;
1202
1203         if (!one_process) {
1204             beosd_detach();
1205         }
1206
1207         my_pid = getpid();
1208     }
1209
1210     beosd_pre_config();
1211     ap_listen_pre_config();
1212     ap_daemons_to_start = DEFAULT_START_DAEMON;
1213     min_spare_threads = DEFAULT_MIN_FREE_DAEMON * DEFAULT_THREADS_PER_CHILD;
1214     max_spare_threads = DEFAULT_MAX_FREE_DAEMON * DEFAULT_THREADS_PER_CHILD;
1215     ap_daemons_limit = HARD_SERVER_LIMIT;
1216     ap_threads_per_child = DEFAULT_THREADS_PER_CHILD;
1217     ap_pid_fname = DEFAULT_PIDLOG;
1218     ap_scoreboard_fname = DEFAULT_SCOREBOARD;
1219     ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
1220     ap_mpmt_beos_set_maintain_connection_status(1);
1221
1222     ap_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
1223 }
1224
1225 static void mpmt_beos_hooks(void)
1226 {
1227     ap_hook_pre_config(mpmt_beos_pre_config,NULL,NULL,HOOK_MIDDLE);
1228     INIT_SIGLIST()
1229     one_process = 0;
1230 }
1231
1232
1233 static const char *set_pidfile(cmd_parms *cmd, void *dummy, char *arg) 
1234 {
1235     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1236     if (err != NULL) {
1237         return err;
1238     }
1239
1240     if (cmd->server->is_virtual) {
1241         return "PidFile directive not allowed in <VirtualHost>";
1242     }
1243     ap_pid_fname = arg;
1244     return NULL;
1245 }
1246
1247 static const char *set_scoreboard(cmd_parms *cmd, void *dummy, char *arg) 
1248 {
1249     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1250     if (err != NULL) {
1251         return err;
1252     }
1253
1254     ap_scoreboard_fname = arg;
1255     return NULL;
1256 }
1257
1258 static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy, char *arg) 
1259 {
1260     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1261     if (err != NULL) {
1262         return err;
1263     }
1264
1265     ap_daemons_to_start = atoi(arg);
1266     return NULL;
1267 }
1268
1269 static const char *set_min_spare_threads(cmd_parms *cmd, void *dummy, char *arg)
1270 {
1271     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1272     if (err != NULL) {
1273         return err;
1274     }
1275
1276     min_spare_threads = atoi(arg);
1277     if (min_spare_threads <= 0) {
1278        fprintf(stderr, "WARNING: detected MinSpareThreads set to non-positive.\n");
1279        fprintf(stderr, "Resetting to 1 to avoid almost certain Apache failure.\n");
1280        fprintf(stderr, "Please read the documentation.\n");
1281        min_spare_threads = 1;
1282     }
1283        
1284     return NULL;
1285 }
1286
1287 static const char *set_max_spare_threads(cmd_parms *cmd, void *dummy, char *arg)
1288 {
1289     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1290     if (err != NULL) {
1291         return err;
1292     }
1293
1294     max_spare_threads = atoi(arg);
1295     return NULL;
1296 }
1297
1298 static const char *set_server_limit (cmd_parms *cmd, void *dummy, char *arg) 
1299 {
1300     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1301     if (err != NULL) {
1302         return err;
1303     }
1304
1305     ap_daemons_limit = atoi(arg);
1306     if (ap_daemons_limit > HARD_SERVER_LIMIT) {
1307        fprintf(stderr, "WARNING: MaxClients of %d exceeds compile time limit "
1308            "of %d servers,\n", ap_daemons_limit, HARD_SERVER_LIMIT);
1309        fprintf(stderr, " lowering MaxClients to %d.  To increase, please "
1310            "see the\n", HARD_SERVER_LIMIT);
1311        fprintf(stderr, " HARD_SERVER_LIMIT define in src/include/httpd.h.\n");
1312        ap_daemons_limit = HARD_SERVER_LIMIT;
1313     } 
1314     else if (ap_daemons_limit < 1) {
1315         fprintf(stderr, "WARNING: Require MaxClients > 0, setting to 1\n");
1316         ap_daemons_limit = 1;
1317     }
1318     return NULL;
1319 }
1320
1321 static const char *set_threads_per_child (cmd_parms *cmd, void *dummy, char *arg) 
1322 {
1323     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1324     if (err != NULL) {
1325         return err;
1326     }
1327
1328     ap_threads_per_child = atoi(arg);
1329     if (ap_threads_per_child > HARD_THREAD_LIMIT) {
1330         fprintf(stderr, "WARNING: ThreadsPerChild of %d exceeds compile time"
1331                 "limit of %d threads,\n", ap_threads_per_child,
1332                 HARD_THREAD_LIMIT);
1333         fprintf(stderr, " lowering ThreadsPerChild to %d. To increase, please"
1334                 "see the\n", HARD_THREAD_LIMIT);
1335         fprintf(stderr, " HARD_THREAD_LIMIT define in src/include/httpd.h.\n");
1336     }
1337     else if (ap_threads_per_child < 1) {
1338         fprintf(stderr, "WARNING: Require ThreadsPerChild > 0, setting to 1\n");
1339         ap_threads_per_child = 1;
1340     }
1341     return NULL;
1342 }
1343
1344 static const char *set_max_requests(cmd_parms *cmd, void *dummy, char *arg) 
1345 {
1346     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1347     if (err != NULL) {
1348         return err;
1349     }
1350
1351     ap_max_requests_per_child = atoi(arg);
1352
1353     return NULL;
1354 }
1355
1356 static const char *set_maintain_connection_status(cmd_parms *cmd,
1357                                                   core_dir_config *d, int arg) 
1358 {
1359     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1360     if (err != NULL) {
1361         return err;
1362     }
1363
1364     ap_mpmt_beos_set_maintain_connection_status(arg != 0);
1365     return NULL;
1366 }
1367
1368 static const char *set_coredumpdir (cmd_parms *cmd, void *dummy, char *arg) 
1369 {
1370     struct stat finfo;
1371     const char *fname;
1372     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1373     if (err != NULL) {
1374         return err;
1375     }
1376
1377     fname = ap_server_root_relative(cmd->pool, arg);
1378     if ((stat(fname, &finfo) == -1) || !S_ISDIR(finfo.st_mode)) {
1379         return ap_pstrcat(cmd->pool, "CoreDumpDirectory ", fname, 
1380                           " does not exist or is not a directory", NULL);
1381     }
1382     ap_cpystrn(ap_coredump_dir, fname, sizeof(ap_coredump_dir));
1383     return NULL;
1384 }
1385
1386 struct ap_thread_mutex {
1387     be_mutex_t mutex;
1388 };
1389
1390 API_EXPORT(ap_thread_mutex *) ap_thread_mutex_new(void)
1391 {
1392     ap_thread_mutex *mtx;
1393
1394     mtx = malloc(sizeof(ap_thread_mutex));
1395     be_mutex_init(&(mtx->mutex), NULL);
1396     return mtx;
1397 }
1398
1399 API_EXPORT(void) ap_thread_mutex_lock(ap_thread_mutex *mtx)
1400 {
1401     /* Ignoring error conditions here. :( */
1402     be_mutex_lock(&(mtx->mutex));
1403 }
1404
1405 API_EXPORT(void) ap_thread_mutex_unlock(ap_thread_mutex *mtx)
1406 {
1407     /* Here too. */
1408     be_mutex_unlock(&(mtx->mutex));
1409 }
1410
1411 API_EXPORT(void) ap_thread_mutex_destroy(ap_thread_mutex *mtx)
1412 {
1413     /* Here too. */
1414     be_mutex_destroy(&(mtx->mutex));
1415     free(mtx);
1416 }
1417
1418
1419 static const command_rec mpmt_beos_cmds[] = {
1420 UNIX_DAEMON_COMMANDS
1421 LISTEN_COMMANDS
1422 { "PidFile", set_pidfile, NULL, RSRC_CONF, TAKE1,
1423     "A file for logging the server process ID"},
1424 { "ScoreBoardFile", set_scoreboard, NULL, RSRC_CONF, TAKE1,
1425     "A file for Apache to maintain runtime process management information"},
1426 { "StartServers", set_daemons_to_start, NULL, RSRC_CONF, TAKE1,
1427   "Number of child processes launched at server startup" },
1428 { "MinSpareThreads", set_min_spare_threads, NULL, RSRC_CONF, TAKE1,
1429   "Minimum number of idle children, to handle request spikes" },
1430 { "MaxSpareThreads", set_max_spare_threads, NULL, RSRC_CONF, TAKE1,
1431   "Maximum number of idle children" },
1432 { "MaxClients", set_server_limit, NULL, RSRC_CONF, TAKE1,
1433   "Maximum number of children alive at the same time" },
1434 { "ThreadsPerChild", set_threads_per_child, NULL, RSRC_CONF, TAKE1,
1435   "Number of threads each child creates" },
1436 { "MaxRequestsPerChild", set_max_requests, NULL, RSRC_CONF, TAKE1,
1437   "Maximum number of requests a particular child serves before dying." },
1438 { "ConnectionStatus", set_maintain_connection_status, NULL, RSRC_CONF, FLAG,
1439   "Whether or not to maintain status information on current connections"},
1440 { "CoreDumpDirectory", set_coredumpdir, NULL, RSRC_CONF, TAKE1,
1441   "The location of the directory Apache changes to before dumping core" },
1442 { NULL }
1443 };
1444
1445 module MODULE_VAR_EXPORT mpm_mpmt_beos_module = {
1446     STANDARD20_MODULE_STUFF,
1447     NULL,                       /* create per-directory config structure */
1448     NULL,                       /* merge per-directory config structures */
1449     NULL,                       /* create per-server config structure */
1450     NULL,                       /* merge per-server config structures */
1451     mpmt_beos_cmds,             /* command ap_table_t */
1452     NULL,                       /* handlers */
1453     mpmt_beos_hooks             /* register_hooks */
1454 };
1455