switch to a 60 bit hash
[httpd-crcsyncproxy.git] / server / mpm / netware / mpm_netware.c
blob421e631dea469256c4239aa64cb58bde95d430c6
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * httpd.c: simple http daemon for answering WWW file requests
21 * 03-21-93 Rob McCool wrote original code (up to NCSA HTTPd 1.3)
23 * 03-06-95 blong
24 * changed server number for child-alone processes to 0 and changed name
25 * of processes
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.
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.
36 * April-July '95 rst
37 * Extensive rework for Apache.
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"
49 #define APR_WANT_STDIO
50 #define APR_WANT_STRFUNC
51 #include "apr_want.h"
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
60 #ifndef USE_WINSOCK
61 #include <sys/select.h>
62 #endif
64 #include "ap_config.h"
65 #include "httpd.h"
66 #include "mpm_default.h"
67 #include "http_main.h"
68 #include "http_log.h"
69 #include "http_config.h"
70 #include "http_core.h" /* for get_remote_host */
71 #include "http_connection.h"
72 #include "scoreboard.h"
73 #include "ap_mpm.h"
74 #include "mpm_common.h"
75 #include "ap_listen.h"
76 #include "ap_mmn.h"
78 #ifdef HAVE_TIME_H
79 #include <time.h>
80 #endif
82 #include <signal.h>
84 #include <netware.h>
85 #include <nks/netware.h>
86 #include <library.h>
87 #include <screen.h>
89 /* Limit on the total --- clients will be locked out if more servers than
90 * this are needed. It is intended solely to keep the server from crashing
91 * when things get out of hand.
93 * We keep a hard maximum number of servers, for two reasons --- first off,
94 * in case something goes seriously wrong, we want to stop the fork bomb
95 * short of actually crashing the machine we're running on by filling some
96 * kernel table. Secondly, it keeps the size of the scoreboard file small
97 * enough that we can read the whole thing without worrying too much about
98 * the overhead.
100 #ifndef HARD_SERVER_LIMIT
101 #define HARD_SERVER_LIMIT 1
102 #endif
104 #define WORKER_DEAD SERVER_DEAD
105 #define WORKER_STARTING SERVER_STARTING
106 #define WORKER_READY SERVER_READY
107 #define WORKER_IDLE_KILL SERVER_IDLE_KILL
109 /* config globals */
111 int ap_threads_per_child=0; /* Worker threads per child */
112 static int ap_threads_to_start=0;
113 static int ap_threads_min_free=0;
114 static int ap_threads_max_free=0;
115 static int ap_threads_limit=0;
116 static int mpm_state = AP_MPMQ_STARTING;
119 * The max child slot ever assigned, preserved across restarts. Necessary
120 * to deal with MaxClients changes across SIGWINCH restarts. We use this
121 * value to optimize routines that have to scan the entire scoreboard.
123 int ap_max_workers_limit = -1;
124 server_rec *ap_server_conf;
126 /* *Non*-shared http_main globals... */
128 int hold_screen_on_exit = 0; /* Indicates whether the screen should be held open */
130 static fd_set listenfds;
131 static int listenmaxfd;
133 static apr_pool_t *pconf; /* Pool for config stuff */
134 static apr_pool_t *pmain; /* Pool for httpd child stuff */
136 static pid_t ap_my_pid; /* it seems silly to call getpid all the time */
137 static char *ap_my_addrspace = NULL;
139 static int die_now = 0;
141 /* Keep track of the number of worker threads currently active */
142 static unsigned long worker_thread_count;
143 static int request_count;
145 /* Structure used to register/deregister a console handler with the OS */
146 static int InstallConsoleHandler(void);
147 static void RemoveConsoleHandler(void);
148 static int CommandLineInterpreter(scr_t screenID, const char *commandLine);
149 static CommandParser_t ConsoleHandler = {0, NULL, 0};
150 #define HANDLEDCOMMAND 0
151 #define NOTMYCOMMAND 1
153 static int show_settings = 0;
155 //#define DBINFO_ON
156 //#define DBPRINT_ON
157 #ifdef DBPRINT_ON
158 #define DBPRINT0(s) printf(s)
159 #define DBPRINT1(s,v1) printf(s,v1)
160 #define DBPRINT2(s,v1,v2) printf(s,v1,v2)
161 #else
162 #define DBPRINT0(s)
163 #define DBPRINT1(s,v1)
164 #define DBPRINT2(s,v1,v2)
165 #endif
167 /* volatile just in case */
168 static int volatile shutdown_pending;
169 static int volatile restart_pending;
170 static int volatile is_graceful;
171 static int volatile wait_to_finish=1;
172 ap_generation_t volatile ap_my_generation=0;
174 /* a clean exit from a child with proper cleanup */
175 static void clean_child_exit(int code, int worker_num, apr_pool_t *ptrans,
176 apr_bucket_alloc_t *bucket_alloc) __attribute__ ((noreturn));
177 static void clean_child_exit(int code, int worker_num, apr_pool_t *ptrans,
178 apr_bucket_alloc_t *bucket_alloc)
180 apr_bucket_alloc_destroy(bucket_alloc);
181 if (!shutdown_pending) {
182 apr_pool_destroy(ptrans);
185 atomic_dec (&worker_thread_count);
186 if (worker_num >=0)
187 ap_update_child_status_from_indexes(0, worker_num, WORKER_DEAD,
188 (request_rec *) NULL);
189 NXThreadExit((void*)&code);
192 /* proper cleanup when returning from ap_mpm_run() */
193 static void mpm_main_cleanup(void)
195 if (pmain) {
196 apr_pool_destroy(pmain);
200 AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
202 switch(query_code){
203 case AP_MPMQ_MAX_DAEMON_USED:
204 *result = 1;
205 return APR_SUCCESS;
206 case AP_MPMQ_IS_THREADED:
207 *result = AP_MPMQ_DYNAMIC;
208 return APR_SUCCESS;
209 case AP_MPMQ_IS_FORKED:
210 *result = AP_MPMQ_NOT_SUPPORTED;
211 return APR_SUCCESS;
212 case AP_MPMQ_HARD_LIMIT_DAEMONS:
213 *result = HARD_SERVER_LIMIT;
214 return APR_SUCCESS;
215 case AP_MPMQ_HARD_LIMIT_THREADS:
216 *result = HARD_THREAD_LIMIT;
217 return APR_SUCCESS;
218 case AP_MPMQ_MAX_THREADS:
219 *result = ap_threads_limit;
220 return APR_SUCCESS;
221 case AP_MPMQ_MIN_SPARE_DAEMONS:
222 *result = 0;
223 return APR_SUCCESS;
224 case AP_MPMQ_MIN_SPARE_THREADS:
225 *result = ap_threads_min_free;
226 return APR_SUCCESS;
227 case AP_MPMQ_MAX_SPARE_DAEMONS:
228 *result = 0;
229 return APR_SUCCESS;
230 case AP_MPMQ_MAX_SPARE_THREADS:
231 *result = ap_threads_max_free;
232 return APR_SUCCESS;
233 case AP_MPMQ_MAX_REQUESTS_DAEMON:
234 *result = ap_max_requests_per_child;
235 return APR_SUCCESS;
236 case AP_MPMQ_MAX_DAEMONS:
237 *result = 1;
238 return APR_SUCCESS;
239 case AP_MPMQ_MPM_STATE:
240 *result = mpm_state;
241 return APR_SUCCESS;
243 return APR_ENOTIMPL;
247 /*****************************************************************
248 * Connection structures and accounting...
251 static void mpm_term(void)
253 RemoveConsoleHandler();
254 wait_to_finish = 0;
255 NXThreadYield();
258 static void sig_term(int sig)
260 if (shutdown_pending == 1) {
261 /* Um, is this _probably_ not an error, if the user has
262 * tried to do a shutdown twice quickly, so we won't
263 * worry about reporting it.
265 return;
267 shutdown_pending = 1;
269 DBPRINT0 ("waiting for threads\n");
270 while (wait_to_finish) {
271 apr_thread_yield();
273 DBPRINT0 ("goodbye\n");
276 /* restart() is the signal handler for SIGHUP and SIGWINCH
277 * in the parent process, unless running in ONE_PROCESS mode
279 static void restart(void)
281 if (restart_pending == 1) {
282 /* Probably not an error - don't bother reporting it */
283 return;
285 restart_pending = 1;
286 is_graceful = 1;
289 static void set_signals(void)
291 apr_signal(SIGTERM, sig_term);
292 apr_signal(SIGABRT, sig_term);
295 int nlmUnloadSignaled(int wait)
297 shutdown_pending = 1;
299 if (wait) {
300 while (wait_to_finish) {
301 NXThreadYield();
305 return 0;
308 /*****************************************************************
309 * Child process main loop.
310 * The following vars are static to avoid getting clobbered by longjmp();
311 * they are really private to child_main.
315 #define MAX_WB_RETRIES 3
316 #ifdef DBINFO_ON
317 static int would_block = 0;
318 static int retry_success = 0;
319 static int retry_fail = 0;
320 static int avg_retries = 0;
321 #endif
323 /*static */
324 void worker_main(void *arg)
326 ap_listen_rec *lr, *first_lr, *last_lr = NULL;
327 apr_pool_t *ptrans;
328 apr_pool_t *pbucket;
329 apr_allocator_t *allocator;
330 apr_bucket_alloc_t *bucket_alloc;
331 conn_rec *current_conn;
332 apr_status_t stat = APR_EINIT;
333 ap_sb_handle_t *sbh;
335 int my_worker_num = (int)arg;
336 apr_socket_t *csd = NULL;
337 int requests_this_child = 0;
338 apr_socket_t *sd = NULL;
339 fd_set main_fds;
341 int sockdes;
342 int srv;
343 struct timeval tv;
344 int wouldblock_retry;
346 tv.tv_sec = 1;
347 tv.tv_usec = 0;
349 apr_allocator_create(&allocator);
350 apr_allocator_max_free_set(allocator, ap_max_mem_free);
352 apr_pool_create_ex(&ptrans, pmain, NULL, allocator);
353 apr_allocator_owner_set(allocator, ptrans);
354 apr_pool_tag(ptrans, "transaction");
356 bucket_alloc = apr_bucket_alloc_create_ex(allocator);
358 atomic_inc (&worker_thread_count);
360 while (!die_now) {
362 * (Re)initialize this child to a pre-connection state.
364 current_conn = NULL;
365 apr_pool_clear(ptrans);
367 if ((ap_max_requests_per_child > 0
368 && requests_this_child++ >= ap_max_requests_per_child)) {
369 DBPRINT1 ("\n**Thread slot %d is shutting down", my_worker_num);
370 clean_child_exit(0, my_worker_num, ptrans, bucket_alloc);
373 ap_update_child_status_from_indexes(0, my_worker_num, WORKER_READY,
374 (request_rec *) NULL);
377 * Wait for an acceptable connection to arrive.
380 for (;;) {
381 if (shutdown_pending || restart_pending || (ap_scoreboard_image->servers[0][my_worker_num].status == WORKER_IDLE_KILL)) {
382 DBPRINT1 ("\nThread slot %d is shutting down\n", my_worker_num);
383 clean_child_exit(0, my_worker_num, ptrans, bucket_alloc);
386 /* Check the listen queue on all sockets for requests */
387 memcpy(&main_fds, &listenfds, sizeof(fd_set));
388 srv = select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv);
390 if (srv <= 0) {
391 if (srv < 0) {
392 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
393 "select() failed on listen socket");
394 apr_thread_yield();
396 continue;
399 /* remember the last_lr we searched last time around so that
400 we don't end up starving any particular listening socket */
401 if (last_lr == NULL) {
402 lr = ap_listeners;
404 else {
405 lr = last_lr->next;
406 if (!lr)
407 lr = ap_listeners;
409 first_lr = lr;
410 do {
411 apr_os_sock_get(&sockdes, lr->sd);
412 if (FD_ISSET(sockdes, &main_fds))
413 goto got_listener;
414 lr = lr->next;
415 if (!lr)
416 lr = ap_listeners;
417 } while (lr != first_lr);
418 /* if we get here, something unexpected happened. Go back
419 into the select state and try again.
421 continue;
422 got_listener:
423 last_lr = lr;
424 sd = lr->sd;
426 wouldblock_retry = MAX_WB_RETRIES;
428 while (wouldblock_retry) {
429 if ((stat = apr_socket_accept(&csd, sd, ptrans)) == APR_SUCCESS) {
430 break;
432 else {
433 /* if the error is a wouldblock then maybe we were too
434 quick try to pull the next request from the listen
435 queue. Try a few more times then return to our idle
436 listen state. */
437 if (!APR_STATUS_IS_EAGAIN(stat)) {
438 break;
441 if (wouldblock_retry--) {
442 apr_thread_yield();
447 /* If we got a new socket, set it to non-blocking mode and process
448 it. Otherwise handle the error. */
449 if (stat == APR_SUCCESS) {
450 apr_socket_opt_set(csd, APR_SO_NONBLOCK, 0);
451 #ifdef DBINFO_ON
452 if (wouldblock_retry < MAX_WB_RETRIES) {
453 retry_success++;
454 avg_retries += (MAX_WB_RETRIES-wouldblock_retry);
456 #endif
457 break; /* We have a socket ready for reading */
459 else {
460 #ifdef DBINFO_ON
461 if (APR_STATUS_IS_EAGAIN(stat)) {
462 would_block++;
463 retry_fail++;
465 else if (
466 #else
467 if (APR_STATUS_IS_EAGAIN(stat) ||
468 #endif
469 APR_STATUS_IS_ECONNRESET(stat) ||
470 APR_STATUS_IS_ETIMEDOUT(stat) ||
471 APR_STATUS_IS_EHOSTUNREACH(stat) ||
472 APR_STATUS_IS_ENETUNREACH(stat)) {
475 #ifdef USE_WINSOCK
476 else if (APR_STATUS_IS_ENETDOWN(stat)) {
478 * When the network layer has been shut down, there
479 * is not much use in simply exiting: the parent
480 * would simply re-create us (and we'd fail again).
481 * Use the CHILDFATAL code to tear the server down.
482 * @@@ Martin's idea for possible improvement:
483 * A different approach would be to define
484 * a new APEXIT_NETDOWN exit code, the reception
485 * of which would make the parent shutdown all
486 * children, then idle-loop until it detected that
487 * the network is up again, and restart the children.
488 * Ben Hyde noted that temporary ENETDOWN situations
489 * occur in mobile IP.
491 ap_log_error(APLOG_MARK, APLOG_EMERG, stat, ap_server_conf,
492 "apr_socket_accept: giving up.");
493 clean_child_exit(APEXIT_CHILDFATAL, my_worker_num, ptrans,
494 bucket_alloc);
496 #endif
497 else {
498 ap_log_error(APLOG_MARK, APLOG_ERR, stat, ap_server_conf,
499 "apr_socket_accept: (client socket)");
500 clean_child_exit(1, my_worker_num, ptrans, bucket_alloc);
505 ap_create_sb_handle(&sbh, ptrans, 0, my_worker_num);
507 * We now have a connection, so set it up with the appropriate
508 * socket options, file descriptors, and read/write buffers.
510 current_conn = ap_run_create_connection(ptrans, ap_server_conf, csd,
511 my_worker_num, sbh,
512 bucket_alloc);
513 if (current_conn) {
514 ap_process_connection(current_conn, csd);
515 ap_lingering_close(current_conn);
517 request_count++;
519 clean_child_exit(0, my_worker_num, ptrans, bucket_alloc);
523 static int make_child(server_rec *s, int slot)
525 int tid;
526 int err=0;
527 NXContext_t ctx;
529 if (slot + 1 > ap_max_workers_limit) {
530 ap_max_workers_limit = slot + 1;
533 ap_update_child_status_from_indexes(0, slot, WORKER_STARTING,
534 (request_rec *) NULL);
536 if (ctx = NXContextAlloc((void (*)(void *)) worker_main, (void*)slot, NX_PRIO_MED, ap_thread_stacksize, NX_CTX_NORMAL, &err)) {
537 char threadName[32];
539 sprintf (threadName, "Apache_Worker %d", slot);
540 NXContextSetName(ctx, threadName);
541 err = NXThreadCreate(ctx, NX_THR_BIND_CONTEXT, &tid);
542 if (err) {
543 NXContextFree (ctx);
547 if (err) {
548 /* create thread didn't succeed. Fix the scoreboard or else
549 * it will say SERVER_STARTING forever and ever
551 ap_update_child_status_from_indexes(0, slot, WORKER_DEAD,
552 (request_rec *) NULL);
554 /* In case system resources are maxxed out, we don't want
555 Apache running away with the CPU trying to fork over and
556 over and over again. */
557 apr_thread_yield();
559 return -1;
562 ap_scoreboard_image->servers[0][slot].tid = tid;
564 return 0;
568 /* start up a bunch of worker threads */
569 static void startup_workers(int number_to_start)
571 int i;
573 for (i = 0; number_to_start && i < ap_threads_limit; ++i) {
574 if (ap_scoreboard_image->servers[0][i].status != WORKER_DEAD) {
575 continue;
577 if (make_child(ap_server_conf, i) < 0) {
578 break;
580 --number_to_start;
586 * idle_spawn_rate is the number of children that will be spawned on the
587 * next maintenance cycle if there aren't enough idle servers. It is
588 * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by
589 * without the need to spawn.
591 static int idle_spawn_rate = 1;
592 #ifndef MAX_SPAWN_RATE
593 #define MAX_SPAWN_RATE (64)
594 #endif
595 static int hold_off_on_exponential_spawning;
597 static void perform_idle_server_maintenance(apr_pool_t *p)
599 int i;
600 int to_kill;
601 int idle_count;
602 worker_score *ws;
603 int free_length;
604 int free_slots[MAX_SPAWN_RATE];
605 int last_non_dead;
606 int total_non_dead;
608 /* initialize the free_list */
609 free_length = 0;
611 to_kill = -1;
612 idle_count = 0;
613 last_non_dead = -1;
614 total_non_dead = 0;
616 for (i = 0; i < ap_threads_limit; ++i) {
617 int status;
619 if (i >= ap_max_workers_limit && free_length == idle_spawn_rate)
620 break;
621 ws = &ap_scoreboard_image->servers[0][i];
622 status = ws->status;
623 if (status == WORKER_DEAD) {
624 /* try to keep children numbers as low as possible */
625 if (free_length < idle_spawn_rate) {
626 free_slots[free_length] = i;
627 ++free_length;
630 else if (status == WORKER_IDLE_KILL) {
631 /* If it is already marked to die, skip it */
632 continue;
634 else {
635 /* We consider a starting server as idle because we started it
636 * at least a cycle ago, and if it still hasn't finished starting
637 * then we're just going to swamp things worse by forking more.
638 * So we hopefully won't need to fork more if we count it.
639 * This depends on the ordering of SERVER_READY and SERVER_STARTING.
641 if (status <= WORKER_READY) {
642 ++ idle_count;
643 /* always kill the highest numbered child if we have to...
644 * no really well thought out reason ... other than observing
645 * the server behaviour under linux where lower numbered children
646 * tend to service more hits (and hence are more likely to have
647 * their data in cpu caches).
649 to_kill = i;
652 ++total_non_dead;
653 last_non_dead = i;
656 DBPRINT2("Total: %d Idle Count: %d \r", total_non_dead, idle_count);
657 ap_max_workers_limit = last_non_dead + 1;
658 if (idle_count > ap_threads_max_free) {
659 /* kill off one child... we use the pod because that'll cause it to
660 * shut down gracefully, in case it happened to pick up a request
661 * while we were counting
663 idle_spawn_rate = 1;
664 ap_update_child_status_from_indexes(0, last_non_dead, WORKER_IDLE_KILL,
665 (request_rec *) NULL);
666 DBPRINT1("\nKilling idle thread: %d\n", last_non_dead);
668 else if (idle_count < ap_threads_min_free) {
669 /* terminate the free list */
670 if (free_length == 0) {
671 /* only report this condition once */
672 static int reported = 0;
674 if (!reported) {
675 ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
676 "server reached MaxClients setting, consider"
677 " raising the MaxClients setting");
678 reported = 1;
680 idle_spawn_rate = 1;
682 else {
683 if (idle_spawn_rate >= 8) {
684 ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
685 "server seems busy, (you may need "
686 "to increase StartServers, or Min/MaxSpareServers), "
687 "spawning %d children, there are %d idle, and "
688 "%d total children", idle_spawn_rate,
689 idle_count, total_non_dead);
691 DBPRINT0("\n");
692 for (i = 0; i < free_length; ++i) {
693 DBPRINT1("Spawning additional thread slot: %d\n", free_slots[i]);
694 make_child(ap_server_conf, free_slots[i]);
696 /* the next time around we want to spawn twice as many if this
697 * wasn't good enough, but not if we've just done a graceful
699 if (hold_off_on_exponential_spawning) {
700 --hold_off_on_exponential_spawning;
702 else if (idle_spawn_rate < MAX_SPAWN_RATE) {
703 idle_spawn_rate *= 2;
707 else {
708 idle_spawn_rate = 1;
712 static void display_settings ()
714 int status_array[SERVER_NUM_STATUS];
715 int i, status, total=0;
716 int reqs = request_count;
717 #ifdef DBINFO_ON
718 int wblock = would_block;
720 would_block = 0;
721 #endif
723 request_count = 0;
725 ClearScreen (getscreenhandle());
726 printf("%s \n", ap_get_server_description());
728 for (i=0;i<SERVER_NUM_STATUS;i++) {
729 status_array[i] = 0;
732 for (i = 0; i < ap_threads_limit; ++i) {
733 status = (ap_scoreboard_image->servers[0][i]).status;
734 status_array[status]++;
737 for (i=0;i<SERVER_NUM_STATUS;i++) {
738 switch(i)
740 case SERVER_DEAD:
741 printf ("Available:\t%d\n", status_array[i]);
742 break;
743 case SERVER_STARTING:
744 printf ("Starting:\t%d\n", status_array[i]);
745 break;
746 case SERVER_READY:
747 printf ("Ready:\t\t%d\n", status_array[i]);
748 break;
749 case SERVER_BUSY_READ:
750 printf ("Busy:\t\t%d\n", status_array[i]);
751 break;
752 case SERVER_BUSY_WRITE:
753 printf ("Busy Write:\t%d\n", status_array[i]);
754 break;
755 case SERVER_BUSY_KEEPALIVE:
756 printf ("Busy Keepalive:\t%d\n", status_array[i]);
757 break;
758 case SERVER_BUSY_LOG:
759 printf ("Busy Log:\t%d\n", status_array[i]);
760 break;
761 case SERVER_BUSY_DNS:
762 printf ("Busy DNS:\t%d\n", status_array[i]);
763 break;
764 case SERVER_CLOSING:
765 printf ("Closing:\t%d\n", status_array[i]);
766 break;
767 case SERVER_GRACEFUL:
768 printf ("Restart:\t%d\n", status_array[i]);
769 break;
770 case SERVER_IDLE_KILL:
771 printf ("Idle Kill:\t%d\n", status_array[i]);
772 break;
773 default:
774 printf ("Unknown Status:\t%d\n", status_array[i]);
775 break;
777 if (i != SERVER_DEAD)
778 total+=status_array[i];
780 printf ("Total Running:\t%d\tout of: \t%d\n", total, ap_threads_limit);
781 printf ("Requests per interval:\t%d\n", reqs);
783 #ifdef DBINFO_ON
784 printf ("Would blocks:\t%d\n", wblock);
785 printf ("Successful retries:\t%d\n", retry_success);
786 printf ("Failed retries:\t%d\n", retry_fail);
787 printf ("Avg retries:\t%d\n", retry_success == 0 ? 0 : avg_retries / retry_success);
788 #endif
791 static void show_server_data()
793 ap_listen_rec *lr;
794 module **m;
796 printf("%s\n", ap_get_server_description());
797 if (ap_my_addrspace && (ap_my_addrspace[0] != 'O') && (ap_my_addrspace[1] != 'S'))
798 printf(" Running in address space %s\n", ap_my_addrspace);
801 /* Display listening ports */
802 printf(" Listening on port(s):");
803 lr = ap_listeners;
804 do {
805 printf(" %d", lr->bind_addr->port);
806 lr = lr->next;
807 } while(lr && lr != ap_listeners);
809 /* Display dynamic modules loaded */
810 printf("\n");
811 for (m = ap_loaded_modules; *m != NULL; m++) {
812 if (((module*)*m)->dynamic_load_handle) {
813 printf(" Loaded dynamic module %s\n", ((module*)*m)->name);
819 static int setup_listeners(server_rec *s)
821 ap_listen_rec *lr;
822 int sockdes;
824 if (ap_setup_listeners(s) < 1 ) {
825 ap_log_error(APLOG_MARK, APLOG_ALERT, 0, s,
826 "no listening sockets available, shutting down");
827 return -1;
830 listenmaxfd = -1;
831 FD_ZERO(&listenfds);
832 for (lr = ap_listeners; lr; lr = lr->next) {
833 apr_os_sock_get(&sockdes, lr->sd);
834 FD_SET(sockdes, &listenfds);
835 if (sockdes > listenmaxfd) {
836 listenmaxfd = sockdes;
839 return 0;
842 static int shutdown_listeners()
844 ap_listen_rec *lr;
846 for (lr = ap_listeners; lr; lr = lr->next) {
847 apr_socket_close(lr->sd);
849 ap_listeners = NULL;
850 return 0;
853 /*****************************************************************
854 * Executive routines.
857 int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
859 apr_status_t status=0;
861 pconf = _pconf;
862 ap_server_conf = s;
864 if (setup_listeners(s)) {
865 ap_log_error(APLOG_MARK, APLOG_ALERT, status, s,
866 "no listening sockets available, shutting down");
867 return -1;
870 restart_pending = shutdown_pending = 0;
871 worker_thread_count = 0;
873 if (!is_graceful) {
874 if (ap_run_pre_mpm(s->process->pool, SB_NOT_SHARED) != OK) {
875 return 1;
879 /* Only set slot 0 since that is all NetWare will ever have. */
880 ap_scoreboard_image->parent[0].pid = getpid();
882 set_signals();
884 apr_pool_create(&pmain, pconf);
885 ap_run_child_init(pmain, ap_server_conf);
887 if (ap_threads_max_free < ap_threads_min_free + 1) /* Don't thrash... */
888 ap_threads_max_free = ap_threads_min_free + 1;
889 request_count = 0;
891 startup_workers(ap_threads_to_start);
893 /* Allow the Apache screen to be closed normally on exit() only if it
894 has not been explicitly forced to close on exit(). (ie. the -E flag
895 was specified at startup) */
896 if (hold_screen_on_exit > 0) {
897 hold_screen_on_exit = 0;
900 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
901 "%s configured -- resuming normal operations",
902 ap_get_server_description());
903 ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
904 "Server built: %s", ap_get_server_built());
905 #ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH
906 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
907 "AcceptMutex: %s (default: %s)",
908 apr_proc_mutex_name(accept_mutex),
909 apr_proc_mutex_defname());
910 #endif
911 show_server_data();
913 mpm_state = AP_MPMQ_RUNNING;
914 while (!restart_pending && !shutdown_pending) {
915 perform_idle_server_maintenance(pconf);
916 if (show_settings)
917 display_settings();
918 apr_thread_yield();
919 apr_sleep(SCOREBOARD_MAINTENANCE_INTERVAL);
921 mpm_state = AP_MPMQ_STOPPING;
924 /* Shutdown the listen sockets so that we don't get stuck in a blocking call.
925 shutdown_listeners();*/
927 if (shutdown_pending) { /* Got an unload from the console */
928 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
929 "caught SIGTERM, shutting down");
931 while (worker_thread_count > 0) {
932 printf ("\rShutdown pending. Waiting for %d thread(s) to terminate...",
933 worker_thread_count);
934 apr_thread_yield();
937 mpm_main_cleanup();
938 return 1;
940 else { /* the only other way out is a restart */
941 /* advance to the next generation */
942 /* XXX: we really need to make sure this new generation number isn't in
943 * use by any of the children.
945 ++ap_my_generation;
946 ap_scoreboard_image->global->running_generation = ap_my_generation;
948 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
949 "Graceful restart requested, doing restart");
951 /* Wait for all of the threads to terminate before initiating the restart */
952 while (worker_thread_count > 0) {
953 printf ("\rRestart pending. Waiting for %d thread(s) to terminate...",
954 worker_thread_count);
955 apr_thread_yield();
957 printf ("\nRestarting...\n");
960 mpm_main_cleanup();
961 return 0;
964 static int netware_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
966 int debug;
967 char *addrname = NULL;
969 mpm_state = AP_MPMQ_STARTING;
971 debug = ap_exists_config_define("DEBUG");
973 is_graceful = 0;
974 ap_my_pid = getpid();
975 addrname = getaddressspacename (NULL, NULL);
976 if (addrname) {
977 ap_my_addrspace = apr_pstrdup (p, addrname);
978 free (addrname);
981 #ifndef USE_WINSOCK
982 /* The following call has been moved to the mod_nw_ssl pre-config handler */
983 ap_listen_pre_config();
984 #endif
986 ap_threads_to_start = DEFAULT_START_THREADS;
987 ap_threads_min_free = DEFAULT_MIN_FREE_THREADS;
988 ap_threads_max_free = DEFAULT_MAX_FREE_THREADS;
989 ap_threads_limit = HARD_THREAD_LIMIT;
990 ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
991 ap_extended_status = 0;
992 ap_thread_stacksize = DEFAULT_THREAD_STACKSIZE;
993 #ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
994 ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
995 #endif
997 return OK;
1000 static int netware_check_config(apr_pool_t *p, apr_pool_t *plog,
1001 apr_pool_t *ptemp, server_rec *s)
1003 static int restart_num = 0;
1004 int startup = 0;
1006 /* we want this only the first time around */
1007 if (restart_num++ == 0) {
1008 startup = 1;
1011 if (ap_threads_limit > HARD_THREAD_LIMIT) {
1012 if (startup) {
1013 ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
1014 "WARNING: MaxThreads of %d exceeds compile-time "
1015 "limit of", ap_threads_limit);
1016 ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
1017 " %d threads, decreasing to %d.",
1018 HARD_THREAD_LIMIT, HARD_THREAD_LIMIT);
1019 ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
1020 " To increase, please see the HARD_THREAD_LIMIT"
1021 "define in");
1022 ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
1023 " server/mpm/netware%s.", AP_MPM_HARD_LIMITS_FILE);
1024 } else {
1025 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
1026 "MaxThreads of %d exceeds compile-time limit "
1027 "of %d, decreasing to match",
1028 ap_threads_limit, HARD_THREAD_LIMIT);
1030 ap_threads_limit = HARD_THREAD_LIMIT;
1032 else if (ap_threads_limit < 1) {
1033 if (startup) {
1034 ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
1035 "WARNING: MaxThreads of %d not allowed, "
1036 "increasing to 1.", ap_threads_limit);
1037 } else {
1038 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
1039 "MaxThreads of %d not allowed, increasing to 1",
1040 ap_threads_limit);
1042 ap_threads_limit = 1;
1045 /* ap_threads_to_start > ap_threads_limit effectively checked in
1046 * call to startup_workers(ap_threads_to_start) in ap_mpm_run()
1048 if (ap_threads_to_start < 0) {
1049 if (startup) {
1050 ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
1051 "WARNING: StartThreads of %d not allowed, "
1052 "increasing to 1.", ap_threads_to_start);
1053 } else {
1054 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
1055 "StartThreads of %d not allowed, increasing to 1",
1056 ap_threads_to_start);
1058 ap_threads_to_start = 1;
1061 if (ap_threads_min_free < 1) {
1062 if (startup) {
1063 ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
1064 "WARNING: MinSpareThreads of %d not allowed, "
1065 "increasing to 1", ap_threads_min_free);
1066 ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
1067 " to avoid almost certain server failure.");
1068 ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
1069 " Please read the documentation.");
1070 } else {
1071 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
1072 "MinSpareThreads of %d not allowed, increasing to 1",
1073 ap_threads_min_free);
1075 ap_threads_min_free = 1;
1078 /* ap_threads_max_free < ap_threads_min_free + 1 checked in ap_mpm_run() */
1080 return OK;
1083 static void netware_mpm_hooks(apr_pool_t *p)
1085 ap_hook_pre_config(netware_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
1086 ap_hook_check_config(netware_check_config, NULL, NULL, APR_HOOK_MIDDLE);
1089 void netware_rewrite_args(process_rec *process)
1091 char *def_server_root;
1092 char optbuf[3];
1093 const char *opt_arg;
1094 apr_getopt_t *opt;
1095 apr_array_header_t *mpm_new_argv;
1098 atexit (mpm_term);
1099 InstallConsoleHandler();
1101 /* Make sure to hold the Apache screen open if exit() is called */
1102 hold_screen_on_exit = 1;
1104 /* Rewrite process->argv[];
1106 * add default -d serverroot from the path of this executable
1108 * The end result will look like:
1109 * The -d serverroot default from the running executable
1111 if (process->argc > 0) {
1112 char *s = apr_pstrdup (process->pconf, process->argv[0]);
1113 if (s) {
1114 int i, len = strlen(s);
1116 for (i=len; i; i--) {
1117 if (s[i] == '\\' || s[i] == '/') {
1118 s[i] = '\0';
1119 apr_filepath_merge(&def_server_root, NULL, s,
1120 APR_FILEPATH_TRUENAME, process->pool);
1121 break;
1124 /* Use process->pool so that the rewritten argv
1125 * lasts for the lifetime of the server process,
1126 * because pconf will be destroyed after the
1127 * initial pre-flight of the config parser.
1129 mpm_new_argv = apr_array_make(process->pool, process->argc + 2,
1130 sizeof(const char *));
1131 *(const char **)apr_array_push(mpm_new_argv) = process->argv[0];
1132 *(const char **)apr_array_push(mpm_new_argv) = "-d";
1133 *(const char **)apr_array_push(mpm_new_argv) = def_server_root;
1135 optbuf[0] = '-';
1136 optbuf[2] = '\0';
1137 apr_getopt_init(&opt, process->pool, process->argc, (char**) process->argv);
1138 while (apr_getopt(opt, AP_SERVER_BASEARGS"n:", optbuf + 1, &opt_arg) == APR_SUCCESS) {
1139 switch (optbuf[1]) {
1140 case 'n':
1141 if (opt_arg) {
1142 renamescreen(opt_arg);
1144 break;
1145 case 'E':
1146 /* Don't need to hold the screen open if the output is going to a file */
1147 hold_screen_on_exit = -1;
1148 default:
1149 *(const char **)apr_array_push(mpm_new_argv) =
1150 apr_pstrdup(process->pool, optbuf);
1152 if (opt_arg) {
1153 *(const char **)apr_array_push(mpm_new_argv) = opt_arg;
1155 break;
1158 process->argc = mpm_new_argv->nelts;
1159 process->argv = (const char * const *) mpm_new_argv->elts;
1164 static int CommandLineInterpreter(scr_t screenID, const char *commandLine)
1166 char *szCommand = "APACHE2 ";
1167 int iCommandLen = 8;
1168 char szcommandLine[256];
1169 char *pID;
1170 screenID = screenID;
1173 if (commandLine == NULL)
1174 return NOTMYCOMMAND;
1175 if (strlen(commandLine) <= strlen(szCommand))
1176 return NOTMYCOMMAND;
1178 strncpy (szcommandLine, commandLine, sizeof(szcommandLine)-1);
1180 /* All added commands begin with "APACHE2 " */
1182 if (!strnicmp(szCommand, szcommandLine, iCommandLen)) {
1183 ActivateScreen (getscreenhandle());
1185 /* If an instance id was not given but the nlm is loaded in
1186 protected space, then the the command belongs to the
1187 OS address space instance to pass it on. */
1188 pID = strstr (szcommandLine, "-p");
1189 if ((pID == NULL) && nlmisloadedprotected())
1190 return NOTMYCOMMAND;
1192 /* If we got an instance id but it doesn't match this
1193 instance of the nlm, pass it on. */
1194 if (pID) {
1195 pID = &pID[2];
1196 while (*pID && (*pID == ' '))
1197 pID++;
1199 if (pID && ap_my_addrspace && strnicmp(pID, ap_my_addrspace, strlen(ap_my_addrspace)))
1200 return NOTMYCOMMAND;
1202 /* If we have determined that this command belongs to this
1203 instance of the nlm, then handle it. */
1204 if (!strnicmp("RESTART",&szcommandLine[iCommandLen],3)) {
1205 printf("Restart Requested...\n");
1206 restart();
1208 else if (!strnicmp("VERSION",&szcommandLine[iCommandLen],3)) {
1209 printf("Server version: %s\n", ap_get_server_description());
1210 printf("Server built: %s\n", ap_get_server_built());
1212 else if (!strnicmp("MODULES",&szcommandLine[iCommandLen],3)) {
1213 ap_show_modules();
1215 else if (!strnicmp("DIRECTIVES",&szcommandLine[iCommandLen],3)) {
1216 ap_show_directives();
1218 else if (!strnicmp("SHUTDOWN",&szcommandLine[iCommandLen],3)) {
1219 printf("Shutdown Requested...\n");
1220 shutdown_pending = 1;
1222 else if (!strnicmp("SETTINGS",&szcommandLine[iCommandLen],3)) {
1223 if (show_settings) {
1224 show_settings = 0;
1225 ClearScreen (getscreenhandle());
1226 show_server_data();
1228 else {
1229 show_settings = 1;
1230 display_settings();
1233 else {
1234 show_settings = 0;
1235 if (strnicmp("HELP",&szcommandLine[iCommandLen],3))
1236 printf("Unknown APACHE2 command %s\n", &szcommandLine[iCommandLen]);
1237 printf("Usage: APACHE2 [command] [-p <instance ID>]\n");
1238 printf("Commands:\n");
1239 printf("\tDIRECTIVES - Show directives\n");
1240 printf("\tHELP - Display this help information\n");
1241 printf("\tMODULES - Show a list of the loaded modules\n");
1242 printf("\tRESTART - Reread the configuration file and restart Apache\n");
1243 printf("\tSETTINGS - Show current thread status\n");
1244 printf("\tSHUTDOWN - Shutdown Apache\n");
1245 printf("\tVERSION - Display the server version information\n");
1248 /* Tell NetWare we handled the command */
1249 return HANDLEDCOMMAND;
1252 /* Tell NetWare that the command isn't mine */
1253 return NOTMYCOMMAND;
1256 static int InstallConsoleHandler(void)
1258 /* Our command line handler interfaces the system operator
1259 with this NLM */
1261 NX_WRAP_INTERFACE(CommandLineInterpreter, 2, (void*)&(ConsoleHandler.parser));
1263 ConsoleHandler.rTag = AllocateResourceTag(getnlmhandle(), "Command Line Processor",
1264 ConsoleCommandSignature);
1265 if (!ConsoleHandler.rTag)
1267 printf("Error on allocate resource tag\n");
1268 return 1;
1271 RegisterConsoleCommand(&ConsoleHandler);
1273 /* The Remove procedure unregisters the console handler */
1275 return 0;
1278 static void RemoveConsoleHandler(void)
1280 UnRegisterConsoleCommand(&ConsoleHandler);
1281 NX_UNWRAP_INTERFACE(ConsoleHandler.parser);
1284 static const char *set_threads_to_start(cmd_parms *cmd, void *dummy, const char *arg)
1286 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1287 if (err != NULL) {
1288 return err;
1291 ap_threads_to_start = atoi(arg);
1292 return NULL;
1295 static const char *set_min_free_threads(cmd_parms *cmd, void *dummy, const char *arg)
1297 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1298 if (err != NULL) {
1299 return err;
1302 ap_threads_min_free = atoi(arg);
1303 return NULL;
1306 static const char *set_max_free_threads(cmd_parms *cmd, void *dummy, const char *arg)
1308 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1309 if (err != NULL) {
1310 return err;
1313 ap_threads_max_free = atoi(arg);
1314 return NULL;
1317 static const char *set_thread_limit (cmd_parms *cmd, void *dummy, const char *arg)
1319 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1320 if (err != NULL) {
1321 return err;
1324 ap_threads_limit = atoi(arg);
1325 return NULL;
1328 static const command_rec netware_mpm_cmds[] = {
1329 LISTEN_COMMANDS,
1330 AP_INIT_TAKE1("StartThreads", set_threads_to_start, NULL, RSRC_CONF,
1331 "Number of worker threads launched at server startup"),
1332 AP_INIT_TAKE1("MinSpareThreads", set_min_free_threads, NULL, RSRC_CONF,
1333 "Minimum number of idle threads, to handle request spikes"),
1334 AP_INIT_TAKE1("MaxSpareThreads", set_max_free_threads, NULL, RSRC_CONF,
1335 "Maximum number of idle threads"),
1336 AP_INIT_TAKE1("MaxThreads", set_thread_limit, NULL, RSRC_CONF,
1337 "Maximum number of worker threads alive at the same time"),
1338 { NULL }
1341 module AP_MODULE_DECLARE_DATA mpm_netware_module = {
1342 MPM20_MODULE_STUFF,
1343 netware_rewrite_args, /* hook to run before apache parses args */
1344 NULL, /* create per-directory config structure */
1345 NULL, /* merge per-directory config structures */
1346 NULL, /* create per-server config structure */
1347 NULL, /* merge per-server config structures */
1348 netware_mpm_cmds, /* command apr_table_t */
1349 netware_mpm_hooks, /* register hooks */