]> granicus.if.org Git - apache/blob - server/mpm/beos/beos.c
c58a5786f4c9b2b39edc4c15aff9515988685df2
[apache] / server / mpm / beos / beos.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * originally written at the National Center for Supercomputing Applications,
56  * University of Illinois, Urbana-Champaign.
57  */
58
59 /* The new BeOS MPM!
60  *
61  * This one basically is a single process multi threaded model, but 
62  * I couldn't be bothered adding the spmt_ to the front of the name!
63  * Anyway, this is still under development so it isn't yet the default
64  * choice.
65  */
66  
67 #define CORE_PRIVATE 
68  
69 #include "apr_strings.h"
70 #include "apr_portable.h"
71 #include "httpd.h" 
72 #include "http_main.h" 
73 #include "http_log.h" 
74 #include "http_config.h"        /* for read_config */ 
75 #include "http_core.h"          /* for get_remote_host */ 
76 #include "http_connection.h"
77 #include "ap_mpm.h"
78 #include "beosd.h"
79 #include "ap_listen.h"
80 #include "scoreboard.h" 
81 #include <kernel/OS.h>
82 #include "mpm_common.h"
83 #include "mpm.h"
84 #include "mpm_default.h"
85 #include <unistd.h>
86 #include <sys/socket.h>
87 #include <signal.h>
88
89 extern int _kset_fd_limit_(int num);
90
91 /* Limit on the total --- clients will be locked out if more servers than
92  * this are needed.  It is intended solely to keep the server from crashing
93  * when things get out of hand.
94  *
95  * We keep a hard maximum number of servers, for two reasons:
96  * 1) in case something goes seriously wrong, we want to stop the server starting
97  *    threads ad infinitum and crashing the server (remember that BeOS has a 192
98  *    thread per team limit).
99  * 2) it keeps the size of the scoreboard file small
100  *    enough that we can read the whole thing without worrying too much about
101  *    the overhead.
102  */
103
104 /* we only ever have 1 main process running... */ 
105 #define HARD_SERVER_LIMIT 1
106
107 /* Limit on the threads per process.  Clients will be locked out if more than
108  * this  * HARD_SERVER_LIMIT are needed.
109  *
110  * We keep this for one reason it keeps the size of the scoreboard file small
111  * enough that we can read the whole thing without worrying too much about
112  * the overhead.
113  */
114 #ifdef NO_THREADS
115 #define HARD_THREAD_LIMIT 1
116 #endif
117 #ifndef HARD_THREAD_LIMIT
118 #define HARD_THREAD_LIMIT 50 
119 #endif
120
121 /*
122  * Actual definitions of config globals
123  */
124
125 static int ap_threads_to_start=0;
126 static int ap_max_requests_per_thread = 0;
127 static int min_spare_threads=0;
128 static int max_spare_threads=0;
129 static int ap_thread_limit=0;
130 static int num_listening_sockets = 0;
131 static apr_socket_t ** listening_sockets;
132 apr_lock_t *accept_mutex = NULL;
133
134 static apr_pool_t *pconf;               /* Pool for config stuff */
135 static apr_pool_t *pchild;              /* Pool for httpd child stuff */
136
137 static int server_pid; 
138
139 /* Keep track of the number of worker threads currently active */
140 static int worker_thread_count;
141 apr_lock_t *worker_thread_count_mutex;
142
143 /* The structure used to pass unique initialization info to each thread */
144 typedef struct {
145     int slot;
146     apr_pool_t *tpool;
147 } proc_info;
148
149 static void check_restart(void *data);
150
151 /*
152  * The max child slot ever assigned, preserved across restarts.  Necessary
153  * to deal with MaxClients changes across AP_SIG_GRACEFUL restarts.  We use 
154  * this value to optimize routines that have to scan the entire scoreboard.
155  */
156 int ap_max_child_assigned = -1;
157 int ap_max_threads_limit = -1;
158
159 static apr_socket_t *udp_sock;
160 static apr_sockaddr_t *udp_sa;
161
162 /* shared http_main globals... */
163
164 server_rec *ap_server_conf;
165
166 /* one_process */
167 static int one_process = 0;
168
169 #ifdef DEBUG_SIGSTOP
170 int raise_sigstop_flags;
171 #endif
172
173 /* a clean exit from a child with proper cleanup 
174    static void clean_child_exit(int code) __attribute__ ((noreturn)); */
175 static void clean_child_exit(int code)
176 {
177     if (pchild)
178         apr_pool_destroy(pchild);
179     exit(code);
180 }
181
182 /* handle all varieties of core dumping signals */
183 static void sig_coredump(int sig)
184 {
185     chdir(ap_coredump_dir);
186     signal(sig, SIG_DFL);
187     kill(server_pid, sig);
188     /* At this point we've got sig blocked, because we're still inside
189      * the signal handler.  When we leave the signal handler it will
190      * be unblocked, and we'll take the signal... and coredump or whatever
191      * is appropriate for this particular Unix.  In addition the parent
192      * will see the real signal we received -- whereas if we called
193      * abort() here, the parent would only see SIGABRT.
194      */
195 }
196
197 /*****************************************************************
198  * Connection structures and accounting...
199  */
200
201 /* volatile just in case */
202 static int volatile shutdown_pending;
203 static int volatile restart_pending;
204 static int volatile is_graceful;
205 static int volatile child_fatal;
206 ap_generation_t volatile ap_my_generation = 0;
207
208 /*
209  * ap_start_shutdown() and ap_start_restart(), below, are a first stab at
210  * functions to initiate shutdown or restart without relying on signals. 
211  * Previously this was initiated in sig_term() and restart() signal handlers, 
212  * but we want to be able to start a shutdown/restart from other sources --
213  * e.g. on Win32, from the service manager. Now the service manager can
214  * call ap_start_shutdown() or ap_start_restart() as appropiate.  Note that
215  * these functions can also be called by the child processes, since global
216  * variables are no longer used to pass on the required action to the parent.
217  *
218  * These should only be called from the parent process itself, since the
219  * parent process will use the shutdown_pending and restart_pending variables
220  * to determine whether to shutdown or restart. The child process should
221  * call signal_parent() directly to tell the parent to die -- this will
222  * cause neither of those variable to be set, which the parent will
223  * assume means something serious is wrong (which it will be, for the
224  * child to force an exit) and so do an exit anyway.
225  */
226
227 static void ap_start_shutdown(void)
228 {
229     if (shutdown_pending == 1) {
230         /* Um, is this _probably_ not an error, if the user has
231          * tried to do a shutdown twice quickly, so we won't
232          * worry about reporting it.
233          */
234         return;
235     }
236     shutdown_pending = 1;
237 }
238
239 /* do a graceful restart if graceful == 1 */
240 static void ap_start_restart(int graceful)
241 {
242
243     if (restart_pending == 1) {
244         /* Probably not an error - don't bother reporting it */
245         return;
246     }
247     restart_pending = 1;
248     is_graceful = graceful;
249     if (is_graceful){
250         apr_pool_cleanup_kill(pconf, NULL, ap_cleanup_scoreboard);
251     }
252 }
253
254 static void sig_term(int sig)
255 {
256     ap_start_shutdown();
257 }
258
259 static void restart(int sig)
260 {
261     ap_start_restart(sig == AP_SIG_GRACEFUL);
262 }
263
264 static void tell_workers_to_exit(void)
265 {
266     apr_size_t len;
267     int i = 0;
268     for (i = 0 ; i < ap_max_child_assigned; i++){
269         len = 4;
270         if (apr_sendto(udp_sock, udp_sa, 0, "die!", &len) != APR_SUCCESS)
271             break;
272     }   
273 }
274
275 static void set_signals(void)
276 {
277     struct sigaction sa;
278
279     sigemptyset(&sa.sa_mask);
280     sa.sa_flags = 0;
281
282     if (!one_process) {
283         sa.sa_handler = sig_coredump;
284
285         if (sigaction(SIGSEGV, &sa, NULL) < 0)
286             ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGSEGV)");
287         if (sigaction(SIGBUS, &sa, NULL) < 0)
288             ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGBUS)");
289         if (sigaction(SIGABRT, &sa, NULL) < 0)
290             ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGABRT)");
291         if (sigaction(SIGILL, &sa, NULL) < 0)
292             ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGILL)");
293         sa.sa_flags = 0;
294     }
295     sa.sa_handler = sig_term;
296     if (sigaction(SIGTERM, &sa, NULL) < 0)
297             ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)");
298     if (sigaction(SIGINT, &sa, NULL) < 0)
299         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGINT)");
300     
301     sa.sa_handler = SIG_IGN;
302     if (sigaction(SIGPIPE, &sa, NULL) < 0)
303         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGPIPE)");
304
305     /* we want to ignore HUPs and AP_SIG_GRACEFUL while we're busy 
306      * processing one */
307     sigaddset(&sa.sa_mask, SIGHUP);
308     sigaddset(&sa.sa_mask, AP_SIG_GRACEFUL);
309     sa.sa_handler = restart;
310     if (sigaction(SIGHUP, &sa, NULL) < 0)
311         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");
312     if (sigaction(AP_SIG_GRACEFUL, &sa, NULL) < 0)
313             ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(" AP_SIG_GRACEFUL_STRING ")");
314 }
315
316 /*****************************************************************
317  * Here follows a long bunch of generic server bookkeeping stuff...
318  */
319
320 int ap_graceful_stop_signalled(void)
321 {
322     /* XXX - Does this really work? - Manoj */
323     return is_graceful;
324 }
325
326 /*****************************************************************
327  * Child process main loop.
328  */
329
330 static void process_socket(apr_pool_t *p, apr_socket_t *sock, int my_child_num)
331 {
332     conn_rec *current_conn;
333     long conn_id = my_child_num;
334     int csd;
335     ap_sb_handle_t *sbh;
336
337     (void)apr_os_sock_get(&csd, sock);
338     
339     if (csd >= FD_SETSIZE) {
340         ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, NULL,
341                      "filedescriptor (%u) larger than FD_SETSIZE (%u) "
342                      "found, you probably need to rebuild Apache with a "
343                      "larger FD_SETSIZE", csd, FD_SETSIZE);
344         apr_socket_close(sock);
345         return;
346     }
347
348     ap_create_sb_handle(&sbh, p, 0, my_child_num);
349     current_conn = ap_run_create_connection(p, ap_server_conf, sock, conn_id, sbh);
350
351     if (current_conn) {
352         ap_process_connection(current_conn, sock);
353         ap_lingering_close(current_conn);
354     }
355 }
356
357 static int32 worker_thread(void * dummy)
358 {
359     proc_info * ti = dummy;
360     int child_slot = ti->slot;
361     apr_pool_t *tpool = ti->tpool;
362     apr_socket_t *csd = NULL;
363     apr_pool_t *ptrans;         /* Pool for per-transaction stuff */
364     apr_socket_t *sd = NULL;
365     apr_status_t rv = APR_EINIT;
366     int srv , n;
367     int curr_pollfd = 0, last_pollfd = 0;
368     sigset_t sig_mask;
369     int requests_this_child = ap_max_requests_per_thread;
370     apr_pollfd_t *pollset;
371     /* each worker thread is in control of its own destiny...*/
372     int this_worker_should_exit = 0; 
373     free(ti);
374
375     on_exit_thread(check_restart, (void*)child_slot);
376           
377     /* block the signals for this thread */
378     sigfillset(&sig_mask);
379     sigprocmask(SIG_BLOCK, &sig_mask, NULL);
380
381     apr_pool_create_ex(&ptrans, tpool, NULL, APR_POOL_FNEW_ALLOCATOR);
382     apr_pool_tag(ptrans, "transaction");
383
384     apr_lock_acquire(worker_thread_count_mutex);
385     worker_thread_count++;
386     apr_lock_release(worker_thread_count_mutex);
387
388     (void) ap_update_child_status_from_indexes(0, child_slot, SERVER_STARTING,
389                                                (request_rec*)NULL);
390                                   
391     apr_poll_setup(&pollset, num_listening_sockets, tpool);
392     for(n=0 ; n <= num_listening_sockets ; n++)
393         apr_poll_socket_add(pollset, listening_sockets[n], APR_POLLIN);
394
395     while (1) {
396         /* If we're here, then chances are (unless we're the first thread created) 
397          * we're going to be held up in the accept mutex, so doing this here
398          * shouldn't hurt performance.
399          */
400
401         this_worker_should_exit |= (ap_max_requests_per_thread != 0) && (requests_this_child <= 0);
402         
403         if (this_worker_should_exit) break;
404
405         (void) ap_update_child_status_from_indexes(0, child_slot, SERVER_READY,
406                                                    (request_rec*)NULL);
407
408         apr_lock_acquire(accept_mutex);
409
410         while (!this_worker_should_exit) {
411             apr_int16_t event;
412             apr_status_t ret;
413
414             ret = apr_poll(pollset, &srv, -1);
415
416             if (ret != APR_SUCCESS) {
417                 if (APR_STATUS_IS_EINTR(ret)) {
418                     continue;
419                 }
420                 /* poll() will only return errors in catastrophic
421                  * circumstances. Let's try exiting gracefully, for now. */
422                 ap_log_error(APLOG_MARK, APLOG_ERR, ret, (const server_rec *)
423                              ap_server_conf, "apr_poll: (listen)");
424                 this_worker_should_exit = 1;
425             } else {
426                 /* if we've bailed in apr_poll what's the point of trying to use the data? */
427                 apr_poll_revents_get(&event, listening_sockets[0], pollset);
428
429                 if (event & APR_POLLIN){
430                     apr_sockaddr_t *rec_sa;
431                     apr_size_t len = 5;
432                     char *tmpbuf = apr_palloc(ptrans, sizeof(char) * 5);
433                     apr_sockaddr_info_get(&rec_sa, "127.0.0.1", APR_UNSPEC, 7772, 0, ptrans);
434                     
435                     if ((ret = apr_recvfrom(rec_sa, listening_sockets[0], 0, tmpbuf, &len))
436                         != APR_SUCCESS){
437                         ap_log_error(APLOG_MARK, APLOG_ERR, ret, NULL, 
438                             "error getting data from UDP!!");
439                     }else {
440                         /* add checking??? */              
441                     }
442                     this_worker_should_exit = 1;
443                 }
444             }
445           
446             if (this_worker_should_exit) break;
447
448             if (num_listening_sockets == 1) {
449                 sd = ap_listeners->sd;
450                 goto got_fd;
451             }
452             else {
453                 /* find a listener */
454                 curr_pollfd = last_pollfd;
455                 do {
456                     curr_pollfd++;
457
458                     if (curr_pollfd > num_listening_sockets)
459                         curr_pollfd = 1;
460                     
461                     /* Get the revent... */
462                     apr_poll_revents_get(&event, listening_sockets[curr_pollfd], pollset);
463                     
464                     if (event & APR_POLLIN) {
465                         last_pollfd = curr_pollfd;
466                         sd = listening_sockets[curr_pollfd];
467                         goto got_fd;
468                     }
469                 } while (curr_pollfd != last_pollfd);
470             }
471         }
472     got_fd:
473
474         if (!this_worker_should_exit) {
475             rv = apr_accept(&csd, sd, ptrans);
476
477             apr_lock_release(accept_mutex);
478             if (rv != APR_SUCCESS) {
479                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
480                   "apr_accept");
481             } else {
482                 process_socket(ptrans, csd, child_slot);
483                 requests_this_child--;
484             }
485         }
486         else {
487             apr_lock_release(accept_mutex);
488             break;
489         }
490         apr_pool_clear(ptrans);
491     }
492
493     ap_update_child_status_from_indexes(0, child_slot, SERVER_DEAD, (request_rec*)NULL);
494
495 ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, NULL,
496              "worker_thread %ld exiting", find_thread(NULL));
497     
498     apr_lock_acquire(worker_thread_count_mutex);
499     worker_thread_count--;
500     apr_lock_release(worker_thread_count_mutex);
501
502     return (0);
503 }
504
505 static int make_worker(int slot)
506 {
507     thread_id tid;
508     proc_info *my_info = (proc_info *)malloc(sizeof(proc_info)); /* freed by thread... */
509
510     if (my_info == NULL) {
511         ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf,
512             "malloc: out of memory");
513         clean_child_exit(APEXIT_CHILDFATAL);
514     }
515     
516     my_info->slot = slot;
517     apr_pool_create(&my_info->tpool, pchild);
518     
519     if (slot + 1 > ap_max_child_assigned)
520             ap_max_child_assigned = slot + 1;
521
522     if (one_process) {
523         set_signals();
524         ap_scoreboard_image->parent[0].pid = getpid();
525         return 0;
526     }
527
528     (void) ap_update_child_status_from_indexes(0, slot, SERVER_STARTING, (request_rec*)NULL);
529     tid = spawn_thread(worker_thread, "apache_worker", B_NORMAL_PRIORITY,
530         my_info);
531     if (tid < B_NO_ERROR) {
532         ap_log_error(APLOG_MARK, APLOG_ERR, errno, NULL, 
533             "spawn_thread: Unable to start a new thread");
534         /* In case system resources are maxxed out, we don't want
535          * Apache running away with the CPU trying to fork over and
536          * over and over again. 
537          */
538         (void) ap_update_child_status_from_indexes(0, slot, SERVER_DEAD, 
539                                                    (request_rec*)NULL);
540         
541         sleep(10);
542         free(my_info);
543         
544         return -1;
545     }
546     resume_thread(tid);
547
548     ap_scoreboard_image->servers[0][slot].tid = tid;
549     return 0;
550 }
551
552 static void check_restart(void *data)
553 {
554     if (!restart_pending && !shutdown_pending) {
555         int slot = (int)data;
556         make_worker(slot);
557         ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, NULL, 
558             "spawning a new worker thread in slot %d", slot);
559     }
560 }
561
562 /* start up a bunch of children */
563 static void startup_threads(int number_to_start)
564 {
565     int i;
566
567     for (i = 0; number_to_start && i < ap_thread_limit; ++i) {
568         if (ap_scoreboard_image->servers[0][i].tid) {
569             continue;
570         }
571         if (make_worker(i) < 0) {
572             break;
573         }
574         --number_to_start;
575     }
576 }
577
578
579 /*
580  * spawn_rate is the number of children that will be spawned on the
581  * next maintenance cycle if there aren't enough idle servers.  It is
582  * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by
583  * without the need to spawn.
584  */
585 static int spawn_rate = 1;
586 #ifndef MAX_SPAWN_RATE
587 #define MAX_SPAWN_RATE  (32)
588 #endif
589 static int hold_off_on_exponential_spawning;
590
591 static void perform_idle_server_maintenance(void)
592 {
593     int i;
594     int free_length;
595     int free_slots[MAX_SPAWN_RATE];
596     int last_non_dead  = -1;
597
598     /* initialize the free_list */
599     free_length = 0;
600
601     for (i = 0; i < ap_thread_limit; ++i) {
602         if (ap_scoreboard_image->servers[0][i].tid == 0) {
603             if (free_length < spawn_rate) {
604                 free_slots[free_length] = i;
605                 ++free_length;
606             }
607         }
608         else {
609             last_non_dead = i;
610         }
611
612         if (i >= ap_max_child_assigned && free_length >= spawn_rate) {
613                  break;
614             }
615     }
616     ap_max_child_assigned = last_non_dead + 1;
617
618     if (free_length > 0) {
619         for (i = 0; i < free_length; ++i) {
620                 make_worker(free_slots[i]);
621             }
622             /* the next time around we want to spawn twice as many if this
623              * wasn't good enough, but not if we've just done a graceful
624              */
625             if (hold_off_on_exponential_spawning) {
626                 --hold_off_on_exponential_spawning;
627             } else if (spawn_rate < MAX_SPAWN_RATE) {
628                 spawn_rate *= 2;
629             }
630     } else {
631         spawn_rate = 1;
632     }
633 }
634
635 static void server_main_loop(int remaining_threads_to_start)
636 {
637     int child_slot;
638     apr_exit_why_e exitwhy;
639     int status;
640     apr_proc_t pid;
641     int i;
642
643     while (!restart_pending && !shutdown_pending) {
644
645         ap_wait_or_timeout(&exitwhy, &status, &pid, pconf);
646          
647         if (pid.pid >= 0) {
648             if (ap_process_child_status(&pid, exitwhy, status) == APEXIT_CHILDFATAL) {
649                 shutdown_pending = 1;
650                 child_fatal = 1;
651                 return;
652             }
653             /* non-fatal death... note that it's gone in the scoreboard. */
654             child_slot = -1;
655             for (i = 0; i < ap_max_child_assigned; ++i) {
656                 if (ap_scoreboard_image->servers[0][i].tid == pid.pid) {
657                     child_slot = i;
658                     break;
659                 }
660             }
661             if (child_slot >= 0) {
662                 ap_scoreboard_image->servers[0][child_slot].tid = 0;
663                 (void) ap_update_child_status_from_indexes(0, child_slot, 
664                                                            SERVER_DEAD, 
665                                                            (request_rec*)NULL);
666                 
667                 if (remaining_threads_to_start
668                             && child_slot < ap_thread_limit) {
669                     /* we're still doing a 1-for-1 replacement of dead
670                      * children with new children
671                      */
672                     make_worker(child_slot);
673                     --remaining_threads_to_start;
674                         }
675 #if APR_HAS_OTHER_CHILD
676             }
677             else if (apr_proc_other_child_read(&pid, status) == 0) {
678                 /* handled */
679 #endif
680             }
681             else if (is_graceful) {
682                 /* Great, we've probably just lost a slot in the
683                  * scoreboard.  Somehow we don't know about this
684                  * child.
685                  */
686                  ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, ap_server_conf,
687                                           "long lost child came home! (pid %ld)", pid.pid);
688             }
689             
690             /* Don't perform idle maintenance when a child dies,
691              * only do it when there's a timeout.  Remember only a
692              * finite number of children can die, and it's pretty
693              * pathological for a lot to die suddenly.
694              */
695              continue;
696          }
697              else if (remaining_threads_to_start) {
698              /* we hit a 1 second timeout in which none of the previous
699               * generation of children needed to be reaped... so assume
700               * they're all done, and pick up the slack if any is left.
701               */
702               startup_threads(remaining_threads_to_start);
703               remaining_threads_to_start = 0;
704               /* In any event we really shouldn't do the code below because
705                * few of the servers we just started are in the IDLE state
706                * yet, so we'd mistakenly create an extra server.
707                */
708               continue;
709          }
710          perform_idle_server_maintenance();
711     }
712 }
713
714 AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
715 {
716     switch(query_code){
717         case AP_MPMQ_MAX_DAEMON_USED:
718             *result = ap_max_child_assigned;
719             return APR_SUCCESS;
720         case AP_MPMQ_IS_THREADED:
721             *result = AP_MPMQ_DYNAMIC;
722             return APR_SUCCESS;
723         case AP_MPMQ_IS_FORKED:
724             *result = AP_MPMQ_NOT_SUPPORTED;
725             return APR_SUCCESS;
726         case AP_MPMQ_HARD_LIMIT_DAEMONS:
727             *result = HARD_SERVER_LIMIT;
728             return APR_SUCCESS;
729         case AP_MPMQ_HARD_LIMIT_THREADS:
730             *result = HARD_THREAD_LIMIT;
731             return APR_SUCCESS;
732         case AP_MPMQ_MAX_THREADS:
733             *result = HARD_THREAD_LIMIT;
734             return APR_SUCCESS;
735         case AP_MPMQ_MIN_SPARE_DAEMONS:
736             *result = 0;
737             return APR_SUCCESS;
738         case AP_MPMQ_MIN_SPARE_THREADS:    
739             *result = max_spare_threads;
740             return APR_SUCCESS;
741         case AP_MPMQ_MAX_SPARE_DAEMONS:
742             *result = 0;
743             return APR_SUCCESS;
744         case AP_MPMQ_MAX_SPARE_THREADS:
745             *result = min_spare_threads;
746             return APR_SUCCESS;
747         case AP_MPMQ_MAX_REQUESTS_DAEMON:
748             *result = ap_max_requests_per_thread;
749             return APR_SUCCESS;
750         case AP_MPMQ_MAX_DAEMONS:
751             *result = HARD_SERVER_LIMIT;
752             return APR_SUCCESS;
753     }
754     return APR_ENOTIMPL;
755 }
756
757 int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
758 {
759     int remaining_threads_to_start, i,j;
760     apr_status_t rv;
761     ap_listen_rec *lr;    
762     pconf = _pconf;
763     ap_server_conf = s;
764
765     /* Increase the available pool of fd's.  This code from
766      * Joe Kloss <joek@be.com>
767      */
768     if( FD_SETSIZE > 128 && (i = _kset_fd_limit_( 128 )) < 0 ){
769         ap_log_error(APLOG_MARK, APLOG_ERR, i, s,
770             "could not set FD_SETSIZE (_kset_fd_limit_ failed)");
771     }
772
773     /* BeOS R5 doesn't support pipes on select() calls, so we use a 
774        UDP socket as these are supported in both R5 and BONE.  If we only cared
775        about BONE we'd use a pipe, but there it is.
776        As we have UDP support in APR, now use the APR functions and check all the
777        return values...
778       */
779     if (apr_sockaddr_info_get(&udp_sa, "127.0.0.1", APR_UNSPEC, 7772, 0, _pconf)
780         != APR_SUCCESS){
781         ap_log_error(APLOG_MARK, APLOG_ALERT, errno, s,
782             "couldn't create control socket information, shutting down");
783         return 1;
784     }
785     if (apr_socket_create(&udp_sock, udp_sa->sa.sin.sin_family, SOCK_DGRAM,
786                       _pconf) != APR_SUCCESS){
787         ap_log_error(APLOG_MARK, APLOG_ALERT, errno, s,
788             "couldn't create control socket, shutting down");
789         return 1;
790     }
791     if (apr_bind(udp_sock, udp_sa) != APR_SUCCESS){
792         ap_log_error(APLOG_MARK, APLOG_ALERT, errno, s,
793             "couldn't bind UDP socket!");
794         return 1;
795     }
796  
797     if ((num_listening_sockets = ap_setup_listeners(ap_server_conf)) < 1) {
798         ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, 0, s,
799             "no listening sockets available, shutting down");
800         return 1;
801     }
802
803     ap_log_pid(pconf, ap_pid_fname);
804
805     /*
806      * Create our locks... 
807      */
808     
809     /* accept_mutex
810      * used to lock around select so we only have one thread
811      * in select at a time
812      */
813     if ((rv = apr_lock_create(&accept_mutex, APR_MUTEX, APR_CROSS_PROCESS,
814                               APR_LOCK_DEFAULT, NULL, pconf)) != APR_SUCCESS) {
815         /* tsch tsch, can't have more than one thread in the accept loop
816            at a time so we need to fall on our sword... */
817         ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
818                      "Couldn't create accept lock");
819         return 1;
820     }
821
822     /* worker_thread_count_mutex
823      * locks the worker_thread_count so we have ana ccurate count...
824      */
825     if ((rv = apr_lock_create(&worker_thread_count_mutex, APR_MUTEX, APR_CROSS_PROCESS,
826                               APR_LOCK_DEFAULT, NULL, pconf)) != APR_SUCCESS) {
827         ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
828                      "Couldn't create worker thread count lock");
829         return 1;
830     }
831
832     /*
833      * Startup/shutdown... 
834      */
835     
836     if (!is_graceful) {
837         /* setup the scoreboard shared memory */
838         if (ap_run_pre_mpm(pconf, SB_SHARED) != OK) {
839             return 1;
840         }
841
842         for (i = 0; i < HARD_SERVER_LIMIT; i++) {
843             ap_scoreboard_image->parent[i].pid = 0;
844             for (j = 0;j < HARD_THREAD_LIMIT; j++)
845                 ap_scoreboard_image->servers[i][j].tid = 0;
846         }
847     }
848
849     if (HARD_SERVER_LIMIT == 1)
850         ap_scoreboard_image->parent[0].pid = getpid();
851
852     set_signals();
853
854     /* Sanity checks to avoid thrashing... */
855     if (max_spare_threads < min_spare_threads )
856         max_spare_threads = min_spare_threads;
857
858     /* If we're doing a graceful_restart then we're going to see a lot
859      * of threads exiting immediately when we get into the main loop
860      * below (because we just sent them AP_SIG_GRACEFUL).  This happens 
861      * pretty rapidly... and for each one that exits we'll start a new one 
862      * until we reach at least threads_min_free.  But we may be permitted to
863      * start more than that, so we'll just keep track of how many we're
864      * supposed to start up without the 1 second penalty between each fork.
865      */
866     remaining_threads_to_start = ap_threads_to_start;
867     /* sanity check on the number to start... */
868     if (remaining_threads_to_start > ap_thread_limit) {
869             remaining_threads_to_start = ap_thread_limit;
870     }
871
872     /* setup the child pool to use for the workers.  Each worker creates
873      * a seperate pool of its own to use.
874      */
875     apr_pool_create(&pchild, pconf);
876
877     /* Now that we have the child pool (pchild) we can allocate
878      * the listenfds and creat the pollset...
879      */
880     listening_sockets = apr_palloc(pchild,
881        sizeof(*listening_sockets) * (num_listening_sockets + 1));
882
883     listening_sockets[0] = udp_sock;
884     for (lr = ap_listeners, i = 1; i <= num_listening_sockets; lr = lr->next, ++i)
885             listening_sockets[i]=lr->sd;
886
887     /* we assume all goes OK...hmm might want to check that! */
888     /* if we're in one_process mode we don't want to start threads
889      * do we??
890      */
891     if (!is_graceful && !one_process) {
892             startup_threads(remaining_threads_to_start);
893             remaining_threads_to_start = 0;
894     }
895     else {
896             /* give the system some time to recover before kicking into
897              * exponential mode */
898         hold_off_on_exponential_spawning = 10;
899     }
900
901     /*
902      * record that we've entered the world !
903      */
904     ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf,
905                 "%s configured -- resuming normal operations",
906                 ap_get_server_version());
907
908     ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, ap_server_conf,
909                 "Server built: %s", ap_get_server_built());
910
911     restart_pending = shutdown_pending = 0;
912
913     /*
914      * main_loop until it's all over
915      */
916     if (!one_process) {
917         server_main_loop(remaining_threads_to_start);
918     
919         tell_workers_to_exit(); /* if we get here we're exiting... */
920         sleep(1); /* give them a brief chance to exit */
921     } else {
922         proc_info *my_info = (proc_info *)malloc(sizeof(proc_info));
923         my_info->slot = 0;
924         apr_pool_create(&my_info->tpool, pchild);
925         worker_thread(my_info);
926     }
927         
928     /* close the UDP socket we've been using... */
929     apr_socket_close(listening_sockets[0]);
930
931     if ((one_process || shutdown_pending) && !child_fatal) {
932         const char *pidfile = NULL;
933         pidfile = ap_server_root_relative (pconf, ap_pid_fname);
934         if ( pidfile != NULL && unlink(pidfile) == 0)
935             ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, ap_server_conf,
936                          "removed PID file %s (pid=%ld)", pidfile, 
937                          (long)getpid());
938     }
939
940     if (one_process) {
941         return 1;
942     }
943         
944     /*
945      * If we get here we're shutting down...
946      */
947     if (shutdown_pending) {
948         /* Time to gracefully shut down:
949          * Kill child processes, tell them to call child_exit, etc...
950          */
951         if (beosd_killpg(getpgrp(), SIGTERM) < 0)
952             ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
953              "killpg SIGTERM");
954       
955         /* use ap_reclaim_child_processes starting with SIGTERM */
956         ap_reclaim_child_processes(1);
957
958         if (!child_fatal) {         /* already recorded */
959             /* record the shutdown in the log */
960             ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf,
961                          "caught SIGTERM, shutting down");
962         }
963     
964         return 1;
965     }
966
967     /* we've been told to restart */
968     signal(SIGHUP, SIG_IGN);
969
970     if (is_graceful) {
971         ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf,
972                     AP_SIG_GRACEFUL_STRING " received.  Doing graceful restart");
973     }
974     else {
975         /* Kill 'em all.  Since the child acts the same on the parents SIGTERM 
976          * and a SIGHUP, we may as well use the same signal, because some user
977          * pthreads are stealing signals from us left and right.
978          */
979             
980         ap_reclaim_child_processes(1);          /* Start with SIGTERM */
981             ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf,
982                     "SIGHUP received.  Attempting to restart");
983     }
984     
985     /* just before we go, tidy up the locks we've created to prevent a 
986      * potential leak of semaphores... */
987     apr_lock_destroy(worker_thread_count_mutex);
988     apr_lock_destroy(accept_mutex);
989     
990     return 0;
991 }
992
993 static int beos_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
994 {
995     static int restart_num = 0;
996     int no_detach, debug;
997     apr_status_t rv;
998
999     debug = ap_exists_config_define("DEBUG");
1000
1001     if (debug)
1002         no_detach = one_process = 1;
1003     else
1004     {
1005         one_process = ap_exists_config_define("ONE_PROCESS");
1006         no_detach = ap_exists_config_define("NO_DETACH");
1007     }
1008
1009     /* sigh, want this only the second time around */
1010     if (restart_num++ == 1) {
1011         is_graceful = 0;
1012         
1013         if (!one_process && !no_detach) {
1014                 rv = apr_proc_detach();
1015             if (rv != APR_SUCCESS) {
1016                 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
1017                              "apr_proc_detach failed");
1018                 return HTTP_INTERNAL_SERVER_ERROR;
1019             }                  
1020         }
1021
1022         server_pid = getpid();
1023     }
1024
1025     beosd_pre_config();
1026     ap_listen_pre_config();
1027     ap_threads_to_start = DEFAULT_START_THREADS;
1028     min_spare_threads = DEFAULT_MIN_FREE_THREADS;
1029     max_spare_threads = DEFAULT_MAX_FREE_THREADS;
1030     ap_thread_limit = HARD_THREAD_LIMIT;
1031     ap_pid_fname = DEFAULT_PIDLOG;
1032     ap_max_requests_per_thread = DEFAULT_MAX_REQUESTS_PER_THREAD;
1033
1034     apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
1035
1036     return OK;
1037 }
1038
1039 static void beos_hooks(apr_pool_t *p)
1040 {
1041     one_process = 0;
1042     
1043     ap_hook_pre_config(beos_pre_config, NULL, NULL, APR_HOOK_MIDDLE); 
1044 }
1045
1046 static const char *set_threads_to_start(cmd_parms *cmd, void *dummy, const char *arg) 
1047 {
1048     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1049     if (err != NULL) {
1050         return err;
1051     }
1052
1053     ap_threads_to_start = atoi(arg);
1054     if (ap_threads_to_start < 0) {
1055         ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
1056                      "StartThreads set to a value less than 0, reset to 1");
1057         ap_threads_to_start = 1;
1058     }
1059     return NULL;
1060 }
1061
1062 static const char *set_min_spare_threads(cmd_parms *cmd, void *dummy, const char *arg)
1063 {
1064     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1065     if (err != NULL) {
1066         return err;
1067     }
1068
1069     min_spare_threads = atoi(arg);
1070     if (min_spare_threads <= 0) {
1071        ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
1072                     "WARNING: detected MinSpareThreads set to non-positive.");
1073        ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
1074                     "Resetting to 1 to avoid almost certain Apache failure.");
1075        ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
1076                     "Please read the documentation.");
1077        min_spare_threads = 1;
1078     }
1079        
1080     return NULL;
1081 }
1082
1083 static const char *set_max_spare_threads(cmd_parms *cmd, void *dummy, const char *arg)
1084 {
1085     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1086     if (err != NULL) {
1087         return err;
1088     }
1089
1090     max_spare_threads = atoi(arg);
1091     return NULL;
1092 }
1093
1094 static const char *set_threads_limit (cmd_parms *cmd, void *dummy, const char *arg) 
1095 {
1096     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1097     if (err != NULL) {
1098         return err;
1099     }
1100
1101     ap_thread_limit = atoi(arg);
1102     if (ap_thread_limit > HARD_THREAD_LIMIT) {
1103        ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
1104                     "WARNING: MaxClients of %d exceeds compile time limit "
1105                     "of %d servers,", ap_thread_limit, HARD_THREAD_LIMIT);
1106        ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
1107                     " lowering MaxClients to %d.  To increase, please "
1108                     "see the", HARD_THREAD_LIMIT);
1109        ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
1110                     " HARD_THREAD_LIMIT define in server/mpm/beos/mpm_default.h.");
1111        ap_thread_limit = HARD_THREAD_LIMIT;
1112     } 
1113     else if (ap_thread_limit < 1) {
1114         ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
1115                      "WARNING: Require MaxClients > 0, setting to %d", HARD_THREAD_LIMIT);
1116         ap_thread_limit = HARD_THREAD_LIMIT;
1117     }
1118     return NULL;
1119 }
1120
1121 static const char *set_max_requests_per_thread (cmd_parms *cmd, void *dummy, const char *arg)
1122 {
1123     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1124     if (err != NULL) {
1125         return err;
1126     }
1127
1128     ap_max_requests_per_thread = atoi(arg);
1129     if (ap_max_requests_per_thread < 0) {
1130         ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
1131                      "WARNING: MaxRequestsPerThread was set below 0"
1132                      "reset to 0, but this may not be what you want.");
1133         ap_max_requests_per_thread = 0;
1134     }
1135
1136     return NULL;
1137 }
1138
1139 static const command_rec beos_cmds[] = {
1140 BEOS_DAEMON_COMMANDS,
1141 LISTEN_COMMANDS,
1142 AP_INIT_TAKE1( "StartThreads", set_threads_to_start, NULL, RSRC_CONF,
1143   "Number of threads to launch at server startup"),
1144 AP_INIT_TAKE1( "MinSpareThreads", set_min_spare_threads, NULL, RSRC_CONF,
1145   "Minimum number of idle children, to handle request spikes"),
1146 AP_INIT_TAKE1( "MaxSpareThreads", set_max_spare_threads, NULL, RSRC_CONF,
1147   "Maximum number of idle children" ),
1148 AP_INIT_TAKE1( "MaxClients", set_threads_limit, NULL, RSRC_CONF, 
1149   "Maximum number of children alive at the same time (max threads)" ),
1150 AP_INIT_TAKE1( "MaxRequestsPerThread", set_max_requests_per_thread, NULL, RSRC_CONF,
1151   "Maximum number of requests served by a thread" ),
1152 { NULL }
1153 };
1154
1155 module AP_MODULE_DECLARE_DATA mpm_beos_module = {
1156     MPM20_MODULE_STUFF,
1157     NULL,                       /* hook to run before apache parses args */
1158     NULL,                       /* create per-directory config structure */
1159     NULL,                       /* merge per-directory config structures */
1160     NULL,                       /* create per-server config structure */
1161     NULL,                       /* merge per-server config structures */
1162     beos_cmds,          /* command apr_table_t */
1163     beos_hooks          /* register_hooks */
1164 };
1165