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