]> granicus.if.org Git - apache/blob - server/mpm/netware/mpm_netware.c
Fix the display of the terminate and restarting console messages
[apache] / server / mpm / netware / mpm_netware.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2003 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 /*
60  * httpd.c: simple http daemon for answering WWW file requests
61  *
62  * 
63  * 03-21-93  Rob McCool wrote original code (up to NCSA HTTPd 1.3)
64  * 
65  * 03-06-95  blong
66  *  changed server number for child-alone processes to 0 and changed name
67  *   of processes
68  *
69  * 03-10-95  blong
70  *      Added numerous speed hacks proposed by Robert S. Thau (rst@ai.mit.edu) 
71  *      including set group before fork, and call gettime before to fork
72  *      to set up libraries.
73  *
74  * 04-14-95  rst / rh
75  *      Brandon's code snarfed from NCSA 1.4, but tinkered to work with the
76  *      Apache server, and also to have child processes do accept() directly.
77  *
78  * April-July '95 rst
79  *      Extensive rework for Apache.
80  */
81
82 #include "apr.h"
83 #include "apr_portable.h"
84 #include "apr_strings.h"
85 #include "apr_thread_proc.h"
86 #include "apr_signal.h"
87 #include "apr_tables.h"
88 #include "apr_getopt.h"
89 #include "apr_thread_mutex.h"
90
91 #define APR_WANT_STDIO
92 #define APR_WANT_STRFUNC
93 #include "apr_want.h"
94
95 #if APR_HAVE_UNISTD_H
96 #include <unistd.h>
97 #endif
98 #if APR_HAVE_SYS_TYPES_H
99 #include <sys/types.h>
100 #endif
101
102 #define CORE_PRIVATE
103
104 #include "ap_config.h"
105 #include "httpd.h"
106 #include "mpm_default.h"
107 #include "http_main.h"
108 #include "http_log.h"
109 #include "http_config.h"
110 #include "http_core.h"          /* for get_remote_host */
111 #include "http_connection.h"
112 #include "scoreboard.h"
113 #include "ap_mpm.h"
114 #include "mpm_common.h"
115 #include "ap_listen.h"
116 #include "ap_mmn.h"
117
118 #ifdef HAVE_TIME_H
119 #include <time.h>
120 #endif
121
122 #include <signal.h>
123
124 #include <netware.h>
125 #include <nks/netware.h>
126 #include <library.h>
127 #include <screen.h>
128
129 /* Limit on the total --- clients will be locked out if more servers than
130  * this are needed.  It is intended solely to keep the server from crashing
131  * when things get out of hand.
132  *
133  * We keep a hard maximum number of servers, for two reasons --- first off,
134  * in case something goes seriously wrong, we want to stop the fork bomb
135  * short of actually crashing the machine we're running on by filling some
136  * kernel table.  Secondly, it keeps the size of the scoreboard file small
137  * enough that we can read the whole thing without worrying too much about
138  * the overhead.
139  */
140 #ifndef HARD_SERVER_LIMIT
141 #define HARD_SERVER_LIMIT 1
142 #endif
143
144 #define WORKER_DEAD         SERVER_DEAD
145 #define WORKER_STARTING     SERVER_STARTING
146 #define WORKER_READY        SERVER_READY
147 #define WORKER_IDLE_KILL    SERVER_IDLE_KILL
148
149 /* config globals */
150
151 int ap_threads_per_child=0;         /* Worker threads per child */
152 int ap_thread_stack_size=65536;
153 static int ap_threads_to_start=0;
154 static int ap_threads_min_free=0;
155 static int ap_threads_max_free=0;
156 static int ap_threads_limit=0;
157
158 /*
159  * The max child slot ever assigned, preserved across restarts.  Necessary
160  * to deal with MaxClients changes across SIGWINCH restarts.  We use this
161  * value to optimize routines that have to scan the entire scoreboard.
162  */
163 int ap_max_workers_limit = -1;
164 server_rec *ap_server_conf;
165
166 /* *Non*-shared http_main globals... */
167
168 int hold_screen_on_exit = 0; /* Indicates whether the screen should be held open */
169
170 static fd_set listenfds;
171 static int listenmaxfd;
172
173 static apr_pool_t *pconf;               /* Pool for config stuff */
174 static apr_pool_t *pmain;               /* Pool for httpd child stuff */
175
176 static pid_t ap_my_pid; /* it seems silly to call getpid all the time */
177 static char *ap_my_addrspace = NULL;
178
179 static int die_now = 0;
180
181 /* Keep track of the number of worker threads currently active */
182 static unsigned long worker_thread_count;
183 static int request_count;
184
185 /*  Structure used to register/deregister a console handler with the OS */
186 static int InstallConsoleHandler(void);
187 static void RemoveConsoleHandler(void);
188 static int CommandLineInterpreter(scr_t screenID, const char *commandLine);
189 static  CommandParser_t ConsoleHandler = {0, NULL, 0};
190 #define HANDLEDCOMMAND  0
191 #define NOTMYCOMMAND    1
192
193 static int show_settings = 0;
194
195 //#define DBINFO_ON
196 //#define DBPRINT_ON
197 #ifdef DBPRINT_ON
198 #define DBPRINT0(s) printf(s)
199 #define DBPRINT1(s,v1) printf(s,v1)
200 #define DBPRINT2(s,v1,v2) printf(s,v1,v2)
201 #else
202 #define DBPRINT0(s)
203 #define DBPRINT1(s,v1)
204 #define DBPRINT2(s,v1,v2)
205 #endif
206
207 /* volatile just in case */
208 static int volatile shutdown_pending;
209 static int volatile restart_pending;
210 static int volatile is_graceful;
211 static int volatile wait_to_finish=1;
212 ap_generation_t volatile ap_my_generation=0;
213
214 /* a clean exit from a child with proper cleanup */
215 static void clean_child_exit(int code, int worker_num, apr_pool_t *ptrans, 
216                              apr_bucket_alloc_t *bucket_alloc) __attribute__ ((noreturn));
217 static void clean_child_exit(int code, int worker_num, apr_pool_t *ptrans, 
218                              apr_bucket_alloc_t *bucket_alloc)
219 {
220     apr_bucket_alloc_destroy(bucket_alloc);
221     if (!shutdown_pending) {
222         apr_pool_destroy(ptrans);
223     }
224
225     atomic_dec (&worker_thread_count);
226     if (worker_num >=0)
227         ap_update_child_status_from_indexes(0, worker_num, WORKER_DEAD, 
228                                             (request_rec *) NULL);
229     NXThreadExit((void*)&code);
230 }
231
232 AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
233 {
234     switch(query_code){
235         case AP_MPMQ_MAX_DAEMON_USED:
236             *result = 1;
237             return APR_SUCCESS;
238         case AP_MPMQ_IS_THREADED:
239             *result = AP_MPMQ_DYNAMIC;
240             return APR_SUCCESS;
241         case AP_MPMQ_IS_FORKED:
242             *result = AP_MPMQ_NOT_SUPPORTED;
243             return APR_SUCCESS;
244         case AP_MPMQ_HARD_LIMIT_DAEMONS:
245             *result = HARD_SERVER_LIMIT;
246             return APR_SUCCESS;
247         case AP_MPMQ_HARD_LIMIT_THREADS:
248             *result = HARD_THREAD_LIMIT;
249             return APR_SUCCESS;
250         case AP_MPMQ_MAX_THREADS:
251             *result = ap_threads_limit;
252             return APR_SUCCESS;
253         case AP_MPMQ_MIN_SPARE_DAEMONS:
254             *result = 0;
255             return APR_SUCCESS;
256         case AP_MPMQ_MIN_SPARE_THREADS:
257             *result = ap_threads_min_free;
258             return APR_SUCCESS;
259         case AP_MPMQ_MAX_SPARE_DAEMONS:
260             *result = 0;
261             return APR_SUCCESS;
262         case AP_MPMQ_MAX_SPARE_THREADS:
263             *result = ap_threads_max_free;
264             return APR_SUCCESS;
265         case AP_MPMQ_MAX_REQUESTS_DAEMON:
266             *result = ap_max_requests_per_child;
267             return APR_SUCCESS;
268         case AP_MPMQ_MAX_DAEMONS:
269             *result = 1;
270             return APR_SUCCESS;
271     }
272     return APR_ENOTIMPL;
273 }
274
275
276 /*****************************************************************
277  * Connection structures and accounting...
278  */
279
280 static void mpm_term(void)
281 {
282     RemoveConsoleHandler();
283     wait_to_finish = 0;
284     NXThreadYield();
285 }
286
287 static void sig_term(int sig)
288 {
289     if (shutdown_pending == 1) {
290             /* Um, is this _probably_ not an error, if the user has
291              * tried to do a shutdown twice quickly, so we won't
292              * worry about reporting it.
293              */
294             return;
295     }
296     shutdown_pending = 1;
297
298     DBPRINT0 ("waiting for threads\n");
299     while (wait_to_finish) {
300         apr_thread_yield();
301     }
302     DBPRINT0 ("goodbye\n");
303 }
304
305 /* restart() is the signal handler for SIGHUP and SIGWINCH
306  * in the parent process, unless running in ONE_PROCESS mode
307  */
308 static void restart(void)
309 {
310     if (restart_pending == 1) {
311         /* Probably not an error - don't bother reporting it */
312         return;
313     }
314     restart_pending = 1;
315     is_graceful = 1;
316 }
317
318 static void set_signals(void)
319 {
320     apr_signal(SIGTERM, sig_term);
321     apr_signal(SIGABRT, sig_term);
322 }
323
324 int nlmUnloadSignaled(int wait)
325 {
326     shutdown_pending = 1;
327
328     if (wait) {
329         while (wait_to_finish) {
330             NXThreadYield();
331         }
332     }
333
334     return 0;
335 }
336
337 /*****************************************************************
338  * Child process main loop.
339  * The following vars are static to avoid getting clobbered by longjmp();
340  * they are really private to child_main.
341  */
342
343
344 int ap_graceful_stop_signalled(void)
345 {
346     /* not ever called anymore... */
347     return 0;
348 }
349
350 #define MAX_WB_RETRIES  3
351 #ifdef DBINFO_ON
352 static int would_block = 0;
353 static int retry_success = 0;
354 static int retry_fail = 0;
355 static int avg_retries = 0;
356 #endif
357
358 /*static */
359 void worker_main(void *arg)
360 {
361     ap_listen_rec *lr, *first_lr, *last_lr = NULL;
362     apr_pool_t *ptrans;
363     apr_pool_t *pbucket;
364     apr_allocator_t *allocator;
365     apr_bucket_alloc_t *bucket_alloc;
366     conn_rec *current_conn;
367     apr_status_t stat = APR_EINIT;
368     ap_sb_handle_t *sbh;
369
370     int my_worker_num = (int)arg;
371     apr_socket_t *csd = NULL;
372     int requests_this_child = 0;
373     apr_socket_t *sd = NULL;
374     fd_set main_fds;
375
376     int sockdes;
377     int srv;
378     struct timeval tv;
379     int wouldblock_retry;
380
381     tv.tv_sec = 1;
382     tv.tv_usec = 0;
383
384     apr_allocator_create(&allocator);
385     apr_allocator_max_free_set(allocator, ap_max_mem_free);
386
387     apr_pool_create_ex(&ptrans, pmain, NULL, allocator);
388     apr_allocator_owner_set(allocator, ptrans);
389     apr_pool_tag(ptrans, "transaction");
390
391     bucket_alloc = apr_bucket_alloc_create_ex(allocator);
392
393     atomic_inc (&worker_thread_count);
394
395     while (!die_now) {
396         /*
397         * (Re)initialize this child to a pre-connection state.
398         */
399         current_conn = NULL;
400         apr_pool_clear(ptrans);
401
402         if ((ap_max_requests_per_child > 0
403             && requests_this_child++ >= ap_max_requests_per_child)) {
404             DBPRINT1 ("\n**Thread slot %d is shutting down", my_worker_num);
405             clean_child_exit(0, my_worker_num, ptrans, bucket_alloc);
406         }
407
408         ap_update_child_status_from_indexes(0, my_worker_num, WORKER_READY, 
409                                             (request_rec *) NULL);
410
411         /*
412         * Wait for an acceptable connection to arrive.
413         */
414
415         for (;;) {
416             if (shutdown_pending || restart_pending || (ap_scoreboard_image->servers[0][my_worker_num].status == WORKER_IDLE_KILL)) {
417                 DBPRINT1 ("\nThread slot %d is shutting down\n", my_worker_num);
418                  clean_child_exit(0, my_worker_num, ptrans, bucket_alloc);
419             }
420
421             /* Check the listen queue on all sockets for requests */
422             memcpy(&main_fds, &listenfds, sizeof(fd_set));
423             srv = select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv);
424
425             if (srv <= 0) {
426                 if (srv < 0) {
427                     ap_log_error(APLOG_MARK, APLOG_NOTICE, apr_get_netos_error(), ap_server_conf,
428                         "select() failed on listen socket");
429                     apr_thread_yield();
430                 }
431                 continue;
432             }
433
434             /* remember the last_lr we searched last time around so that
435             we don't end up starving any particular listening socket */
436             if (last_lr == NULL) {
437                 lr = ap_listeners;
438             }
439             else {
440                 lr = last_lr->next;
441                 if (!lr)
442                     lr = ap_listeners;
443             }
444             first_lr = lr;
445             do {
446                 apr_os_sock_get(&sockdes, lr->sd);
447                 if (FD_ISSET(sockdes, &main_fds))
448                     goto got_listener;
449                 lr = lr->next;
450                 if (!lr)
451                     lr = ap_listeners;
452             } while (lr != first_lr);
453             /* if we get here, something unexpected happened. Go back
454             into the select state and try again.
455             */
456             continue;
457         got_listener:
458             last_lr = lr;
459             sd = lr->sd;
460
461             wouldblock_retry = MAX_WB_RETRIES;
462
463             while (wouldblock_retry) {
464                 if ((stat = apr_socket_accept(&csd, sd, ptrans)) == APR_SUCCESS) {
465                     break;
466                 }
467                 else {
468                     /* if the error is a wouldblock then maybe we were too
469                         quick try to pull the next request from the listen 
470                         queue.  Try a few more times then return to our idle
471                         listen state. */
472                     if (!APR_STATUS_IS_EAGAIN(stat)) {
473                         break;
474                     }
475
476                     if (wouldblock_retry--) {
477                         apr_thread_yield();
478                     }
479                 }
480             }
481
482             /* If we got a new socket, set it to non-blocking mode and process
483                 it.  Otherwise handle the error. */
484             if (stat == APR_SUCCESS) {
485                 apr_socket_opt_set(csd, APR_SO_NONBLOCK, 0);
486 #ifdef DBINFO_ON
487                 if (wouldblock_retry < MAX_WB_RETRIES) {
488                     retry_success++;
489                     avg_retries += (MAX_WB_RETRIES-wouldblock_retry);
490                 }
491 #endif
492                 break;          /* We have a socket ready for reading */
493             }
494             else {
495 #ifdef DBINFO_ON
496                 if (APR_STATUS_IS_EAGAIN(stat)) {
497                         would_block++;
498                         retry_fail++;
499                 }
500                 else
501 #else
502                 if (APR_STATUS_IS_EAGAIN(stat) ||
503 #endif
504                     APR_STATUS_IS_ECONNRESET(stat) ||
505                     APR_STATUS_IS_ETIMEDOUT(stat) ||
506                     APR_STATUS_IS_EHOSTUNREACH(stat) ||
507                     APR_STATUS_IS_ENETUNREACH(stat)) {
508                         ;
509                 }
510                 else if (APR_STATUS_IS_ENETDOWN(stat)) {
511                        /*
512                         * When the network layer has been shut down, there
513                         * is not much use in simply exiting: the parent
514                         * would simply re-create us (and we'd fail again).
515                         * Use the CHILDFATAL code to tear the server down.
516                         * @@@ Martin's idea for possible improvement:
517                         * A different approach would be to define
518                         * a new APEXIT_NETDOWN exit code, the reception
519                         * of which would make the parent shutdown all
520                         * children, then idle-loop until it detected that
521                         * the network is up again, and restart the children.
522                         * Ben Hyde noted that temporary ENETDOWN situations
523                         * occur in mobile IP.
524                         */
525                         ap_log_error(APLOG_MARK, APLOG_EMERG, stat, ap_server_conf,
526                             "apr_socket_accept: giving up.");
527                         clean_child_exit(APEXIT_CHILDFATAL, my_worker_num, ptrans, 
528                                          bucket_alloc);
529                 }
530                 else {
531                         ap_log_error(APLOG_MARK, APLOG_ERR, stat, ap_server_conf,
532                             "apr_socket_accept: (client socket)");
533                         clean_child_exit(1, my_worker_num, ptrans, bucket_alloc);
534                 }
535             }
536         }
537
538         ap_create_sb_handle(&sbh, ptrans, 0, my_worker_num);
539         /*
540         * We now have a connection, so set it up with the appropriate
541         * socket options, file descriptors, and read/write buffers.
542         */
543         current_conn = ap_run_create_connection(ptrans, ap_server_conf, csd, 
544                                                 my_worker_num, sbh,
545                                                 bucket_alloc);
546         if (current_conn) {
547             ap_process_connection(current_conn, csd);
548             ap_lingering_close(current_conn);
549         }
550         request_count++;
551     }
552     clean_child_exit(0, my_worker_num, ptrans, bucket_alloc);
553 }
554
555
556 static int make_child(server_rec *s, int slot)
557 {
558     int tid;
559     int err=0;
560     NXContext_t ctx;
561
562     if (slot + 1 > ap_max_workers_limit) {
563         ap_max_workers_limit = slot + 1;
564     }
565
566     ap_update_child_status_from_indexes(0, slot, WORKER_STARTING, 
567                                         (request_rec *) NULL);
568
569     if (ctx = NXContextAlloc((void (*)(void *)) worker_main, (void*)slot, NX_PRIO_MED, ap_thread_stack_size, NX_CTX_NORMAL, &err)) {
570         char threadName[32];
571
572         sprintf (threadName, "Apache_Worker %d", slot);
573         NXContextSetName(ctx, threadName);
574         err = NXThreadCreate(ctx, NX_THR_BIND_CONTEXT, &tid);
575         if (err) {
576             NXContextFree (ctx);
577         }
578     }
579
580     if (err) {
581         /* create thread didn't succeed. Fix the scoreboard or else
582         * it will say SERVER_STARTING forever and ever
583         */
584         ap_update_child_status_from_indexes(0, slot, WORKER_DEAD, 
585                                             (request_rec *) NULL);
586
587         /* In case system resources are maxxed out, we don't want
588         Apache running away with the CPU trying to fork over and
589         over and over again. */
590         apr_thread_yield();
591
592         return -1;
593     }
594
595     ap_scoreboard_image->servers[0][slot].tid = tid;
596
597     return 0;
598 }
599
600
601 /* start up a bunch of worker threads */
602 static void startup_workers(int number_to_start)
603 {
604     int i;
605
606     for (i = 0; number_to_start && i < ap_threads_limit; ++i) {
607         if (ap_scoreboard_image->servers[0][i].status != WORKER_DEAD) {
608             continue;
609         }
610         if (make_child(ap_server_conf, i) < 0) {
611             break;
612         }
613         --number_to_start;
614     }
615 }
616
617
618 /*
619  * idle_spawn_rate is the number of children that will be spawned on the
620  * next maintenance cycle if there aren't enough idle servers.  It is
621  * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by
622  * without the need to spawn.
623  */
624 static int idle_spawn_rate = 1;
625 #ifndef MAX_SPAWN_RATE
626 #define MAX_SPAWN_RATE  (64)
627 #endif
628 static int hold_off_on_exponential_spawning;
629
630 static void perform_idle_server_maintenance(apr_pool_t *p)
631 {
632     int i;
633     int to_kill;
634     int idle_count;
635     worker_score *ws;
636     int free_length;
637     int free_slots[MAX_SPAWN_RATE];
638     int last_non_dead;
639     int total_non_dead;
640
641     /* initialize the free_list */
642     free_length = 0;
643
644     to_kill = -1;
645     idle_count = 0;
646     last_non_dead = -1;
647     total_non_dead = 0;
648
649     for (i = 0; i < ap_threads_limit; ++i) {
650         int status;
651
652         if (i >= ap_max_workers_limit && free_length == idle_spawn_rate)
653             break;
654         ws = &ap_scoreboard_image->servers[0][i];
655         status = ws->status;
656         if (status == WORKER_DEAD) {
657             /* try to keep children numbers as low as possible */
658             if (free_length < idle_spawn_rate) {
659                 free_slots[free_length] = i;
660                 ++free_length;
661             }
662         }
663         else if (status == WORKER_IDLE_KILL) {
664             /* If it is already marked to die, skip it */
665             continue;
666         }
667         else {
668             /* We consider a starting server as idle because we started it
669             * at least a cycle ago, and if it still hasn't finished starting
670             * then we're just going to swamp things worse by forking more.
671             * So we hopefully won't need to fork more if we count it.
672             * This depends on the ordering of SERVER_READY and SERVER_STARTING.
673             */
674             if (status <= WORKER_READY) {
675                 ++ idle_count;
676                 /* always kill the highest numbered child if we have to...
677                 * no really well thought out reason ... other than observing
678                 * the server behaviour under linux where lower numbered children
679                 * tend to service more hits (and hence are more likely to have
680                 * their data in cpu caches).
681                 */
682                 to_kill = i;
683             }
684
685             ++total_non_dead;
686             last_non_dead = i;
687         }
688     }
689     DBPRINT2("Total: %d Idle Count: %d  \r", total_non_dead, idle_count);
690     ap_max_workers_limit = last_non_dead + 1;
691     if (idle_count > ap_threads_max_free) {
692         /* kill off one child... we use the pod because that'll cause it to
693         * shut down gracefully, in case it happened to pick up a request
694         * while we were counting
695         */
696         idle_spawn_rate = 1;
697         ap_update_child_status_from_indexes(0, last_non_dead, WORKER_IDLE_KILL, 
698                                             (request_rec *) NULL);
699         DBPRINT1("\nKilling idle thread: %d\n", last_non_dead);
700     }
701     else if (idle_count < ap_threads_min_free) {
702         /* terminate the free list */
703         if (free_length == 0) {
704             /* only report this condition once */
705             static int reported = 0;
706
707             if (!reported) {
708                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
709                     "server reached MaxClients setting, consider"
710                     " raising the MaxClients setting");
711                 reported = 1;
712             }
713             idle_spawn_rate = 1;
714         }
715         else {
716             if (idle_spawn_rate >= 8) {
717                 ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
718                     "server seems busy, (you may need "
719                     "to increase StartServers, or Min/MaxSpareServers), "
720                     "spawning %d children, there are %d idle, and "
721                     "%d total children", idle_spawn_rate,
722                     idle_count, total_non_dead);
723             }
724             DBPRINT0("\n");
725             for (i = 0; i < free_length; ++i) {
726                 DBPRINT1("Spawning additional thread slot: %d\n", free_slots[i]);
727                 make_child(ap_server_conf, free_slots[i]);
728             }
729             /* the next time around we want to spawn twice as many if this
730             * wasn't good enough, but not if we've just done a graceful
731             */
732             if (hold_off_on_exponential_spawning) {
733                 --hold_off_on_exponential_spawning;
734             }
735             else if (idle_spawn_rate < MAX_SPAWN_RATE) {
736                 idle_spawn_rate *= 2;
737             }
738         }
739     }
740     else {
741         idle_spawn_rate = 1;
742     }
743 }
744
745 static void display_settings ()
746 {
747     int status_array[SERVER_NUM_STATUS];
748     int i, status, total=0;
749     int reqs = request_count;
750 #ifdef DBINFO_ON
751     int wblock = would_block;
752     
753     would_block = 0;
754 #endif    
755
756     request_count = 0;
757
758     ClearScreen (getscreenhandle());
759     printf("%s \n", ap_get_server_version());
760
761     for (i=0;i<SERVER_NUM_STATUS;i++) {
762         status_array[i] = 0;
763     }
764
765     for (i = 0; i < ap_threads_limit; ++i) {
766         status = (ap_scoreboard_image->servers[0][i]).status;
767         status_array[status]++;
768     }
769
770     for (i=0;i<SERVER_NUM_STATUS;i++) {
771         switch(i)
772         {
773         case SERVER_DEAD:
774             printf ("Available:\t%d\n", status_array[i]);
775             break;
776         case SERVER_STARTING:
777             printf ("Starting:\t%d\n", status_array[i]);
778             break;
779         case SERVER_READY:
780             printf ("Ready:\t\t%d\n", status_array[i]);
781             break;
782         case SERVER_BUSY_READ:
783             printf ("Busy:\t\t%d\n", status_array[i]);
784             break;
785         case SERVER_BUSY_WRITE:
786             printf ("Busy Write:\t%d\n", status_array[i]);
787             break;
788         case SERVER_BUSY_KEEPALIVE:
789             printf ("Busy Keepalive:\t%d\n", status_array[i]);
790             break;
791         case SERVER_BUSY_LOG:
792             printf ("Busy Log:\t%d\n", status_array[i]);
793             break;
794         case SERVER_BUSY_DNS:
795             printf ("Busy DNS:\t%d\n", status_array[i]);
796             break;
797         case SERVER_CLOSING:
798             printf ("Closing:\t%d\n", status_array[i]);
799             break;
800         case SERVER_GRACEFUL:
801             printf ("Restart:\t%d\n", status_array[i]);
802             break;
803         case SERVER_IDLE_KILL:
804             printf ("Idle Kill:\t%d\n", status_array[i]);
805             break;
806         default:
807             printf ("Unknown Status:\t%d\n", status_array[i]);
808             break;
809         }
810         if (i != SERVER_DEAD)
811             total+=status_array[i];
812     }
813     printf ("Total Running:\t%d\tout of: \t%d\n", total, ap_threads_limit);
814     printf ("Requests per interval:\t%d\n", reqs);
815     
816 #ifdef DBINFO_ON
817     printf ("Would blocks:\t%d\n", wblock);
818     printf ("Successful retries:\t%d\n", retry_success);
819     printf ("Failed retries:\t%d\n", retry_fail);
820     printf ("Avg retries:\t%d\n", retry_success == 0 ? 0 : avg_retries / retry_success);
821 #endif
822 }
823
824 static void show_server_data()
825 {
826     ap_listen_rec *lr;
827     module **m;
828
829     printf("%s\n", ap_get_server_version());
830     if (ap_my_addrspace && (ap_my_addrspace[0] != 'O') && (ap_my_addrspace[1] != 'S'))
831         printf("   Running in address space %s\n", ap_my_addrspace);
832
833
834     /* Display listening ports */
835     printf("   Listening on port(s):");
836     lr = ap_listeners;
837     do {
838        printf(" %d", lr->bind_addr->port);
839        lr = lr->next;
840     } while(lr && lr != ap_listeners);
841     
842     /* Display dynamic modules loaded */
843     printf("\n");    
844     for (m = ap_loaded_modules; *m != NULL; m++) {
845         if (((module*)*m)->dynamic_load_handle) {
846             printf("   Loaded dynamic module %s\n", ((module*)*m)->name);
847         }
848     }
849 }
850
851
852 static int setup_listeners(server_rec *s)
853 {
854     ap_listen_rec *lr;
855     int sockdes;
856
857     if (ap_setup_listeners(s) < 1 ) {
858         ap_log_error(APLOG_MARK, APLOG_ALERT, 0, s,
859             "no listening sockets available, shutting down");
860         return -1;
861     }
862
863     listenmaxfd = -1;
864     FD_ZERO(&listenfds);
865     for (lr = ap_listeners; lr; lr = lr->next) {
866         apr_os_sock_get(&sockdes, lr->sd);
867         FD_SET(sockdes, &listenfds);
868         if (sockdes > listenmaxfd) {
869             listenmaxfd = sockdes;
870         }
871         /* Use non-blocking listen sockets so that we
872            never get hung up. */
873         apr_socket_opt_set(lr->sd, APR_SO_NONBLOCK, 1);
874     }
875     return 0;
876 }
877
878 static int shutdown_listeners()
879 {
880     ap_listen_rec *lr;
881
882     for (lr = ap_listeners; lr; lr = lr->next) {
883         apr_socket_close(lr->sd);
884     }
885     ap_listeners = NULL;
886     return 0;
887 }
888
889 /*****************************************************************
890  * Executive routines.
891  */
892
893 int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
894 {
895     apr_status_t status=0;
896
897     pconf = _pconf;
898     ap_server_conf = s;
899
900     if (setup_listeners(s)) {
901         ap_log_error(APLOG_MARK, APLOG_ALERT, status, s,
902             "no listening sockets available, shutting down");
903         return -1;
904     }
905
906     restart_pending = shutdown_pending = 0;
907     worker_thread_count = 0;
908
909     if (!is_graceful) {
910         if (ap_run_pre_mpm(s->process->pool, SB_NOT_SHARED) != OK) {
911             return 1;
912         }
913     }
914
915     /* Only set slot 0 since that is all NetWare will ever have. */
916     ap_scoreboard_image->parent[0].pid = getpid();
917
918     set_signals();
919
920     apr_pool_create(&pmain, pconf);
921     ap_run_child_init(pmain, ap_server_conf);
922
923     if (ap_threads_max_free < ap_threads_min_free + 1)  /* Don't thrash... */
924         ap_threads_max_free = ap_threads_min_free + 1;
925     request_count = 0;
926
927     startup_workers(ap_threads_to_start);
928
929      /* Allow the Apache screen to be closed normally on exit() only if it
930         has not been explicitly forced to close on exit(). (ie. the -E flag
931         was specified at startup) */
932     if (hold_screen_on_exit > 0) {
933         hold_screen_on_exit = 0;
934     }
935
936     ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
937                 "%s configured -- resuming normal operations",
938                 ap_get_server_version());
939     ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
940                 "Server built: %s", ap_get_server_built());
941 #ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH
942     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
943                 "AcceptMutex: %s (default: %s)",
944                 apr_proc_mutex_name(accept_mutex),
945                 apr_proc_mutex_defname());
946 #endif
947     show_server_data();
948
949     while (!restart_pending && !shutdown_pending) {
950         perform_idle_server_maintenance(pconf);
951         if (show_settings)
952             display_settings();
953         apr_thread_yield();
954         apr_sleep(SCOREBOARD_MAINTENANCE_INTERVAL);
955     }
956
957
958     /* Shutdown the listen sockets so that we don't get stuck in a blocking call. 
959     shutdown_listeners();*/
960
961     if (shutdown_pending) { /* Got an unload from the console */
962         ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
963             "caught SIGTERM, shutting down");
964
965         while (worker_thread_count > 0) {
966             printf ("\rShutdown pending. Waiting for %d thread(s) to terminate...", 
967                     worker_thread_count);
968             apr_thread_yield();
969         }
970
971         return 1;
972     }
973     else {  /* the only other way out is a restart */
974         /* advance to the next generation */
975         /* XXX: we really need to make sure this new generation number isn't in
976          * use by any of the children.
977          */
978         ++ap_my_generation;
979         ap_scoreboard_image->global->running_generation = ap_my_generation;
980
981         ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
982                     "Graceful restart requested, doing restart");
983
984         /* Wait for all of the threads to terminate before initiating the restart */
985         while (worker_thread_count > 0) {
986             printf ("\rRestart pending. Waiting for %d thread(s) to terminate...",
987                     worker_thread_count);
988             apr_thread_yield();
989         }
990         printf ("\nRestarting...\n");
991     }
992
993     return 0;
994 }
995
996 static int netware_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
997 {
998     int debug;
999     char *addrname = NULL;
1000
1001     debug = ap_exists_config_define("DEBUG");
1002
1003     is_graceful = 0;
1004     ap_my_pid = getpid();
1005     addrname = getaddressspacename (NULL, NULL);
1006     if (addrname) {
1007         ap_my_addrspace = apr_pstrdup (p, addrname);
1008         free (addrname);
1009     }
1010
1011     ap_listen_pre_config();
1012     ap_threads_to_start = DEFAULT_START_THREADS;
1013     ap_threads_min_free = DEFAULT_MIN_FREE_THREADS;
1014     ap_threads_max_free = DEFAULT_MAX_FREE_THREADS;
1015     ap_threads_limit = HARD_THREAD_LIMIT;
1016     ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
1017     ap_extended_status = 0;
1018 #ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
1019         ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
1020 #endif
1021
1022     return OK;
1023 }
1024
1025 static void netware_mpm_hooks(apr_pool_t *p)
1026 {
1027     ap_hook_pre_config(netware_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
1028 }
1029
1030 void netware_rewrite_args(process_rec *process) 
1031 {
1032     char *def_server_root;
1033     char optbuf[3];
1034     const char *opt_arg;
1035     apr_getopt_t *opt;
1036     apr_array_header_t *mpm_new_argv;
1037
1038
1039     atexit (mpm_term);
1040     InstallConsoleHandler();
1041
1042     /* Make sure to hold the Apache screen open if exit() is called */
1043     hold_screen_on_exit = 1;
1044
1045     /* Rewrite process->argv[]; 
1046      *
1047      * add default -d serverroot from the path of this executable
1048      * 
1049      * The end result will look like:
1050      *     The -d serverroot default from the running executable
1051      */
1052     if (process->argc > 0) {
1053         char *s = apr_pstrdup (process->pconf, process->argv[0]);
1054         if (s) {
1055             int i, len = strlen(s);
1056
1057             for (i=len; i; i--) {
1058                 if (s[i] == '\\' || s[i] == '/') {
1059                     s[i] = NULL;
1060                     apr_filepath_merge(&def_server_root, NULL, s, 
1061                         APR_FILEPATH_TRUENAME, process->pool);
1062                     break;
1063                 }
1064             }
1065             /* Use process->pool so that the rewritten argv
1066             * lasts for the lifetime of the server process,
1067             * because pconf will be destroyed after the 
1068             * initial pre-flight of the config parser.
1069             */
1070             mpm_new_argv = apr_array_make(process->pool, process->argc + 2,
1071                                   sizeof(const char *));
1072             *(const char **)apr_array_push(mpm_new_argv) = process->argv[0];
1073             *(const char **)apr_array_push(mpm_new_argv) = "-d";
1074             *(const char **)apr_array_push(mpm_new_argv) = def_server_root;
1075
1076             optbuf[0] = '-';
1077             optbuf[2] = '\0';
1078             apr_getopt_init(&opt, process->pool, process->argc, (char**) process->argv);
1079             while (apr_getopt(opt, AP_SERVER_BASEARGS"n:", optbuf + 1, &opt_arg) == APR_SUCCESS) {
1080                 switch (optbuf[1]) {
1081                 case 'n':
1082                     if (opt_arg) {
1083                         renamescreen(opt_arg);
1084                     }
1085                     break;
1086                                 case 'E':
1087                                         /* Don't need to hold the screen open if the output is going to a file */
1088                                         hold_screen_on_exit = -1;
1089                 default:
1090                     *(const char **)apr_array_push(mpm_new_argv) =
1091                         apr_pstrdup(process->pool, optbuf);
1092
1093                     if (opt_arg) {
1094                         *(const char **)apr_array_push(mpm_new_argv) = opt_arg;
1095                     }
1096                     break;
1097                 }
1098             }
1099             process->argc = mpm_new_argv->nelts; 
1100             process->argv = (const char * const *) mpm_new_argv->elts;
1101         }
1102     }
1103 }
1104
1105 static int CommandLineInterpreter(scr_t screenID, const char *commandLine)
1106 {
1107     char *szCommand = "APACHE2 ";
1108     int iCommandLen = 8;
1109     char szcommandLine[256];
1110     char *pID;
1111     screenID = screenID;
1112
1113
1114     if (commandLine == NULL)
1115         return NOTMYCOMMAND;
1116     if (strlen(commandLine) <= strlen(szCommand))
1117         return NOTMYCOMMAND;
1118
1119     strncpy (szcommandLine, commandLine, sizeof(szcommandLine)-1);
1120
1121     /*  All added commands begin with "APACHE2 " */
1122
1123     if (!strnicmp(szCommand, szcommandLine, iCommandLen)) {
1124         ActivateScreen (getscreenhandle());
1125
1126         /* If an instance id was not given but the nlm is loaded in 
1127             protected space, then the the command belongs to the
1128             OS address space instance to pass it on. */
1129         pID = strstr (szcommandLine, "-p");
1130         if ((pID == NULL) && nlmisloadedprotected())
1131             return NOTMYCOMMAND;
1132
1133         /* If we got an instance id but it doesn't match this 
1134             instance of the nlm, pass it on. */
1135         if (pID) {
1136             pID = &pID[2];
1137             while (*pID && (*pID == ' '))
1138                 pID++;
1139         }
1140         if (pID && ap_my_addrspace && strnicmp(pID, ap_my_addrspace, strlen(ap_my_addrspace)))
1141             return NOTMYCOMMAND;
1142
1143         /* If we have determined that this command belongs to this
1144             instance of the nlm, then handle it. */
1145         if (!strnicmp("RESTART",&szcommandLine[iCommandLen],3)) {
1146             printf("Restart Requested...\n");
1147             restart();
1148         }
1149         else if (!strnicmp("VERSION",&szcommandLine[iCommandLen],3)) {
1150             printf("Server version: %s\n", ap_get_server_version());
1151             printf("Server built:   %s\n", ap_get_server_built());
1152         }
1153         else if (!strnicmp("MODULES",&szcommandLine[iCommandLen],3)) {
1154             ap_show_modules();
1155         }
1156         else if (!strnicmp("DIRECTIVES",&szcommandLine[iCommandLen],3)) {
1157                 ap_show_directives();
1158         }
1159         else if (!strnicmp("SHUTDOWN",&szcommandLine[iCommandLen],3)) {
1160             printf("Shutdown Requested...\n");
1161             shutdown_pending = 1;
1162         }
1163         else if (!strnicmp("SETTINGS",&szcommandLine[iCommandLen],3)) {
1164             if (show_settings) {
1165                 show_settings = 0;
1166                 ClearScreen (getscreenhandle());
1167                 show_server_data();
1168             }
1169             else {
1170                 show_settings = 1;
1171                 display_settings();
1172             }
1173         }
1174         else {
1175             show_settings = 0;
1176             if (strnicmp("HELP",&szcommandLine[iCommandLen],3))
1177                 printf("Unknown APACHE2 command %s\n", &szcommandLine[iCommandLen]);
1178             printf("Usage: APACHE2 [command] [-p <instance ID>]\n");
1179             printf("Commands:\n");
1180             printf("\tDIRECTIVES - Show directives\n");
1181             printf("\tHELP       - Display this help information\n");
1182             printf("\tMODULES    - Show a list of the loaded modules\n");
1183             printf("\tRESTART    - Reread the configurtion file and restart Apache\n");
1184             printf("\tSETTINGS   - Show current thread status\n");
1185             printf("\tSHUTDOWN   - Shutdown Apache\n");
1186             printf("\tVERSION    - Display the server version information\n");
1187         }
1188
1189         /*  Tell NetWare we handled the command */
1190         return HANDLEDCOMMAND;
1191     }
1192
1193     /*  Tell NetWare that the command isn't mine */
1194     return NOTMYCOMMAND;
1195 }
1196
1197 static int InstallConsoleHandler(void)
1198 {
1199     /*  Our command line handler interfaces the system operator
1200     with this NLM */
1201
1202     NX_WRAP_INTERFACE(CommandLineInterpreter, 2, (void*)&(ConsoleHandler.parser));
1203
1204     ConsoleHandler.rTag = AllocateResourceTag(getnlmhandle(), "Command Line Processor",
1205         ConsoleCommandSignature);
1206     if (!ConsoleHandler.rTag)
1207     {
1208         printf("Error on allocate resource tag\n");
1209         return 1;
1210     }
1211
1212     RegisterConsoleCommand(&ConsoleHandler);
1213
1214     /*  The Remove procedure unregisters the console handler */
1215
1216     return 0;
1217 }
1218
1219 static void RemoveConsoleHandler(void)
1220 {
1221     UnRegisterConsoleCommand(&ConsoleHandler);
1222     NX_UNWRAP_INTERFACE(ConsoleHandler.parser);
1223 }
1224
1225 static const char *set_threads_to_start(cmd_parms *cmd, void *dummy, const char *arg) 
1226 {
1227     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1228     if (err != NULL) {
1229         return err;
1230     }
1231
1232     ap_threads_to_start = atoi(arg);
1233     return NULL;
1234 }
1235
1236 static const char *set_min_free_threads(cmd_parms *cmd, void *dummy, const char *arg)
1237 {
1238     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1239     if (err != NULL) {
1240         return err;
1241     }
1242
1243     ap_threads_min_free = atoi(arg);
1244     if (ap_threads_min_free <= 0) {
1245        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
1246                     "WARNING: detected MinSpareServers set to non-positive.");
1247        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
1248                     "Resetting to 1 to avoid almost certain Apache failure.");
1249        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
1250                     "Please read the documentation.");
1251        ap_threads_min_free = 1;
1252     }
1253        
1254     return NULL;
1255 }
1256
1257 static const char *set_max_free_threads(cmd_parms *cmd, void *dummy, const char *arg)
1258 {
1259     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1260     if (err != NULL) {
1261         return err;
1262     }
1263
1264     ap_threads_max_free = atoi(arg);
1265     return NULL;
1266 }
1267
1268 static const char *set_thread_limit (cmd_parms *cmd, void *dummy, const char *arg) 
1269 {
1270     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1271     if (err != NULL) {
1272         return err;
1273     }
1274
1275     ap_threads_limit = atoi(arg);
1276     if (ap_threads_limit > HARD_THREAD_LIMIT) {
1277        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
1278                     "WARNING: MaxThreads of %d exceeds compile time limit "
1279                     "of %d threads,", ap_threads_limit, HARD_THREAD_LIMIT);
1280        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
1281                     " lowering MaxThreads to %d.  To increase, please "
1282                     "see the", HARD_THREAD_LIMIT);
1283        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1284                     " HARD_THREAD_LIMIT define in %s.",
1285                     AP_MPM_HARD_LIMITS_FILE);
1286        ap_threads_limit = HARD_THREAD_LIMIT;
1287     } 
1288     else if (ap_threads_limit < 1) {
1289         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
1290             "WARNING: Require MaxThreads > 0, setting to 1");
1291         ap_threads_limit = 1;
1292     }
1293     return NULL;
1294 }
1295
1296 static const char *set_thread_stacksize(cmd_parms *cmd, void *dummy, 
1297                                         const char *arg)
1298 {
1299     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1300     if (err != NULL) {
1301         return err;
1302     }
1303     
1304     ap_thread_stack_size = atoi(arg);
1305     return NULL;
1306 }
1307
1308 static const command_rec netware_mpm_cmds[] = {
1309 AP_INIT_TAKE1("ThreadStackSize", set_thread_stacksize, NULL, RSRC_CONF,
1310               "Stack size each created thread will use."),
1311 LISTEN_COMMANDS,
1312 AP_INIT_TAKE1("StartThreads", set_threads_to_start, NULL, RSRC_CONF,
1313               "Number of worker threads launched at server startup"),
1314 AP_INIT_TAKE1("MinSpareThreads", set_min_free_threads, NULL, RSRC_CONF,
1315               "Minimum number of idle threads, to handle request spikes"),
1316 AP_INIT_TAKE1("MaxSpareThreads", set_max_free_threads, NULL, RSRC_CONF,
1317               "Maximum number of idle threads"),
1318 AP_INIT_TAKE1("MaxThreads", set_thread_limit, NULL, RSRC_CONF,
1319               "Maximum number of worker threads alive at the same time"),
1320 { NULL }
1321 };
1322
1323 module AP_MODULE_DECLARE_DATA mpm_netware_module = {
1324     MPM20_MODULE_STUFF,
1325     netware_rewrite_args,   /* hook to run before apache parses args */
1326     NULL,                               /* create per-directory config structure */
1327     NULL,                               /* merge per-directory config structures */
1328     NULL,                               /* create per-server config structure */
1329     NULL,                               /* merge per-server config structures */
1330     netware_mpm_cmds,       /* command apr_table_t */
1331     netware_mpm_hooks,      /* register hooks */
1332 };