1 /* ====================================================================
2 * The Apache Software License, Version 1.1
4 * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
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
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.
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.
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.
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
47 * ====================================================================
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/>.
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.
60 * httpd.c: simple http daemon for answering WWW file requests
63 * 03-21-93 Rob McCool wrote original code (up to NCSA HTTPd 1.3)
66 * changed server number for child-alone processes to 0 and changed name
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.
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.
79 * Extensive rework for Apache.
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"
91 #define APR_WANT_STDIO
92 #define APR_WANT_STRFUNC
98 #if APR_HAVE_SYS_TYPES_H
99 #include <sys/types.h>
104 #include "ap_config.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"
114 #include "mpm_common.h"
115 #include "ap_listen.h"
125 #include <nks/netware.h>
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.
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
140 #ifndef HARD_SERVER_LIMIT
141 #define HARD_SERVER_LIMIT 1
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
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;
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.
163 int ap_max_workers_limit = -1;
164 server_rec *ap_server_conf;
166 /* *Non*-shared http_main globals... */
168 int hold_screen_on_exit = 0; /* Indicates whether the screen should be held open */
170 static fd_set listenfds;
171 static int listenmaxfd;
173 static apr_pool_t *pconf; /* Pool for config stuff */
174 static apr_pool_t *pmain; /* Pool for httpd child stuff */
176 static pid_t ap_my_pid; /* it seems silly to call getpid all the time */
177 static char *ap_my_addrspace = NULL;
179 static int die_now = 0;
181 /* Keep track of the number of worker threads currently active */
182 static unsigned long worker_thread_count;
183 static int request_count;
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
193 static int show_settings = 0;
198 #define DBPRINT0(s) printf(s)
199 #define DBPRINT1(s,v1) printf(s,v1)
200 #define DBPRINT2(s,v1,v2) printf(s,v1,v2)
203 #define DBPRINT1(s,v1)
204 #define DBPRINT2(s,v1,v2)
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;
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)
220 apr_bucket_alloc_destroy(bucket_alloc);
221 if (!shutdown_pending) {
222 apr_pool_destroy(ptrans);
225 atomic_dec (&worker_thread_count);
227 ap_update_child_status_from_indexes(0, worker_num, WORKER_DEAD,
228 (request_rec *) NULL);
229 NXThreadExit((void*)&code);
232 AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
235 case AP_MPMQ_MAX_DAEMON_USED:
238 case AP_MPMQ_IS_THREADED:
239 *result = AP_MPMQ_DYNAMIC;
241 case AP_MPMQ_IS_FORKED:
242 *result = AP_MPMQ_NOT_SUPPORTED;
244 case AP_MPMQ_HARD_LIMIT_DAEMONS:
245 *result = HARD_SERVER_LIMIT;
247 case AP_MPMQ_HARD_LIMIT_THREADS:
248 *result = HARD_THREAD_LIMIT;
250 case AP_MPMQ_MAX_THREADS:
251 *result = ap_threads_limit;
253 case AP_MPMQ_MIN_SPARE_DAEMONS:
256 case AP_MPMQ_MIN_SPARE_THREADS:
257 *result = ap_threads_min_free;
259 case AP_MPMQ_MAX_SPARE_DAEMONS:
262 case AP_MPMQ_MAX_SPARE_THREADS:
263 *result = ap_threads_max_free;
265 case AP_MPMQ_MAX_REQUESTS_DAEMON:
266 *result = ap_max_requests_per_child;
268 case AP_MPMQ_MAX_DAEMONS:
276 /*****************************************************************
277 * Connection structures and accounting...
280 static void mpm_term(void)
282 RemoveConsoleHandler();
287 static void sig_term(int sig)
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.
296 shutdown_pending = 1;
298 DBPRINT0 ("waiting for threads\n");
299 while (wait_to_finish) {
302 DBPRINT0 ("goodbye\n");
305 /* restart() is the signal handler for SIGHUP and SIGWINCH
306 * in the parent process, unless running in ONE_PROCESS mode
308 static void restart(void)
310 if (restart_pending == 1) {
311 /* Probably not an error - don't bother reporting it */
318 static void set_signals(void)
320 apr_signal(SIGTERM, sig_term);
321 apr_signal(SIGABRT, sig_term);
324 int nlmUnloadSignaled(int wait)
326 shutdown_pending = 1;
329 while (wait_to_finish) {
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.
344 int ap_graceful_stop_signalled(void)
346 /* not ever called anymore... */
350 #define MAX_WB_RETRIES 3
352 static int would_block = 0;
353 static int retry_success = 0;
354 static int retry_fail = 0;
355 static int avg_retries = 0;
359 void worker_main(void *arg)
361 ap_listen_rec *lr, *first_lr, *last_lr = NULL;
364 apr_allocator_t *allocator;
365 apr_bucket_alloc_t *bucket_alloc;
366 conn_rec *current_conn;
367 apr_status_t stat = APR_EINIT;
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;
379 int wouldblock_retry;
384 apr_allocator_create(&allocator);
385 apr_allocator_max_free_set(allocator, ap_max_mem_free);
387 apr_pool_create_ex(&ptrans, pmain, NULL, allocator);
388 apr_allocator_owner_set(allocator, ptrans);
389 apr_pool_tag(ptrans, "transaction");
391 bucket_alloc = apr_bucket_alloc_create_ex(allocator);
393 atomic_inc (&worker_thread_count);
397 * (Re)initialize this child to a pre-connection state.
400 apr_pool_clear(ptrans);
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);
408 ap_update_child_status_from_indexes(0, my_worker_num, WORKER_READY,
409 (request_rec *) NULL);
412 * Wait for an acceptable connection to arrive.
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);
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);
427 ap_log_error(APLOG_MARK, APLOG_NOTICE, apr_get_netos_error(), ap_server_conf,
428 "select() failed on listen socket");
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) {
446 apr_os_sock_get(&sockdes, lr->sd);
447 if (FD_ISSET(sockdes, &main_fds))
452 } while (lr != first_lr);
453 /* if we get here, something unexpected happened. Go back
454 into the select state and try again.
461 wouldblock_retry = MAX_WB_RETRIES;
463 while (wouldblock_retry) {
464 if ((stat = apr_socket_accept(&csd, sd, ptrans)) == APR_SUCCESS) {
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
472 if (!APR_STATUS_IS_EAGAIN(stat)) {
476 if (wouldblock_retry--) {
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);
487 if (wouldblock_retry < MAX_WB_RETRIES) {
489 avg_retries += (MAX_WB_RETRIES-wouldblock_retry);
492 break; /* We have a socket ready for reading */
496 if (APR_STATUS_IS_EAGAIN(stat)) {
502 if (APR_STATUS_IS_EAGAIN(stat) ||
504 APR_STATUS_IS_ECONNRESET(stat) ||
505 APR_STATUS_IS_ETIMEDOUT(stat) ||
506 APR_STATUS_IS_EHOSTUNREACH(stat) ||
507 APR_STATUS_IS_ENETUNREACH(stat)) {
510 else if (APR_STATUS_IS_ENETDOWN(stat)) {
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.
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,
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);
538 ap_create_sb_handle(&sbh, ptrans, 0, my_worker_num);
540 * We now have a connection, so set it up with the appropriate
541 * socket options, file descriptors, and read/write buffers.
543 current_conn = ap_run_create_connection(ptrans, ap_server_conf, csd,
547 ap_process_connection(current_conn, csd);
548 ap_lingering_close(current_conn);
552 clean_child_exit(0, my_worker_num, ptrans, bucket_alloc);
556 static int make_child(server_rec *s, int slot)
562 if (slot + 1 > ap_max_workers_limit) {
563 ap_max_workers_limit = slot + 1;
566 ap_update_child_status_from_indexes(0, slot, WORKER_STARTING,
567 (request_rec *) NULL);
569 if (ctx = NXContextAlloc((void (*)(void *)) worker_main, (void*)slot, NX_PRIO_MED, ap_thread_stack_size, NX_CTX_NORMAL, &err)) {
572 sprintf (threadName, "Apache_Worker %d", slot);
573 NXContextSetName(ctx, threadName);
574 err = NXThreadCreate(ctx, NX_THR_BIND_CONTEXT, &tid);
581 /* create thread didn't succeed. Fix the scoreboard or else
582 * it will say SERVER_STARTING forever and ever
584 ap_update_child_status_from_indexes(0, slot, WORKER_DEAD,
585 (request_rec *) NULL);
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. */
595 ap_scoreboard_image->servers[0][slot].tid = tid;
601 /* start up a bunch of worker threads */
602 static void startup_workers(int number_to_start)
606 for (i = 0; number_to_start && i < ap_threads_limit; ++i) {
607 if (ap_scoreboard_image->servers[0][i].status != WORKER_DEAD) {
610 if (make_child(ap_server_conf, i) < 0) {
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.
624 static int idle_spawn_rate = 1;
625 #ifndef MAX_SPAWN_RATE
626 #define MAX_SPAWN_RATE (64)
628 static int hold_off_on_exponential_spawning;
630 static void perform_idle_server_maintenance(apr_pool_t *p)
637 int free_slots[MAX_SPAWN_RATE];
641 /* initialize the free_list */
649 for (i = 0; i < ap_threads_limit; ++i) {
652 if (i >= ap_max_workers_limit && free_length == idle_spawn_rate)
654 ws = &ap_scoreboard_image->servers[0][i];
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;
663 else if (status == WORKER_IDLE_KILL) {
664 /* If it is already marked to die, skip it */
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.
674 if (status <= WORKER_READY) {
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).
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
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);
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;
708 ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
709 "server reached MaxClients setting, consider"
710 " raising the MaxClients setting");
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);
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]);
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
732 if (hold_off_on_exponential_spawning) {
733 --hold_off_on_exponential_spawning;
735 else if (idle_spawn_rate < MAX_SPAWN_RATE) {
736 idle_spawn_rate *= 2;
745 static void display_settings ()
747 int status_array[SERVER_NUM_STATUS];
748 int i, status, total=0;
749 int reqs = request_count;
751 int wblock = would_block;
758 ClearScreen (getscreenhandle());
759 printf("%s \n", ap_get_server_version());
761 for (i=0;i<SERVER_NUM_STATUS;i++) {
765 for (i = 0; i < ap_threads_limit; ++i) {
766 status = (ap_scoreboard_image->servers[0][i]).status;
767 status_array[status]++;
770 for (i=0;i<SERVER_NUM_STATUS;i++) {
774 printf ("Available:\t%d\n", status_array[i]);
776 case SERVER_STARTING:
777 printf ("Starting:\t%d\n", status_array[i]);
780 printf ("Ready:\t\t%d\n", status_array[i]);
782 case SERVER_BUSY_READ:
783 printf ("Busy:\t\t%d\n", status_array[i]);
785 case SERVER_BUSY_WRITE:
786 printf ("Busy Write:\t%d\n", status_array[i]);
788 case SERVER_BUSY_KEEPALIVE:
789 printf ("Busy Keepalive:\t%d\n", status_array[i]);
791 case SERVER_BUSY_LOG:
792 printf ("Busy Log:\t%d\n", status_array[i]);
794 case SERVER_BUSY_DNS:
795 printf ("Busy DNS:\t%d\n", status_array[i]);
798 printf ("Closing:\t%d\n", status_array[i]);
800 case SERVER_GRACEFUL:
801 printf ("Restart:\t%d\n", status_array[i]);
803 case SERVER_IDLE_KILL:
804 printf ("Idle Kill:\t%d\n", status_array[i]);
807 printf ("Unknown Status:\t%d\n", status_array[i]);
810 if (i != SERVER_DEAD)
811 total+=status_array[i];
813 printf ("Total Running:\t%d\tout of: \t%d\n", total, ap_threads_limit);
814 printf ("Requests per interval:\t%d\n", reqs);
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);
824 static void show_server_data()
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);
834 /* Display listening ports */
835 printf(" Listening on port(s):");
838 printf(" %d", lr->bind_addr->port);
840 } while(lr && lr != ap_listeners);
842 /* Display dynamic modules loaded */
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);
852 static int setup_listeners(server_rec *s)
857 if (ap_setup_listeners(s) < 1 ) {
858 ap_log_error(APLOG_MARK, APLOG_ALERT, 0, s,
859 "no listening sockets available, shutting down");
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;
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);
878 static int shutdown_listeners()
882 for (lr = ap_listeners; lr; lr = lr->next) {
883 apr_socket_close(lr->sd);
889 /*****************************************************************
890 * Executive routines.
893 int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
895 apr_status_t status=0;
900 if (setup_listeners(s)) {
901 ap_log_error(APLOG_MARK, APLOG_ALERT, status, s,
902 "no listening sockets available, shutting down");
906 restart_pending = shutdown_pending = 0;
907 worker_thread_count = 0;
910 if (ap_run_pre_mpm(s->process->pool, SB_NOT_SHARED) != OK) {
915 /* Only set slot 0 since that is all NetWare will ever have. */
916 ap_scoreboard_image->parent[0].pid = getpid();
920 apr_pool_create(&pmain, pconf);
921 ap_run_child_init(pmain, ap_server_conf);
923 if (ap_threads_max_free < ap_threads_min_free + 1) /* Don't thrash... */
924 ap_threads_max_free = ap_threads_min_free + 1;
927 startup_workers(ap_threads_to_start);
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;
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());
949 while (!restart_pending && !shutdown_pending) {
950 perform_idle_server_maintenance(pconf);
954 apr_sleep(SCOREBOARD_MAINTENANCE_INTERVAL);
958 /* Shutdown the listen sockets so that we don't get stuck in a blocking call.
959 shutdown_listeners();*/
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");
965 while (worker_thread_count > 0) {
966 printf ("\rShutdown pending. Waiting for %d thread(s) to terminate...",
967 worker_thread_count);
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.
979 ap_scoreboard_image->global->running_generation = ap_my_generation;
981 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
982 "Graceful restart requested, doing restart");
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);
990 printf ("\nRestarting...\n");
996 static int netware_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
999 char *addrname = NULL;
1001 debug = ap_exists_config_define("DEBUG");
1004 ap_my_pid = getpid();
1005 addrname = getaddressspacename (NULL, NULL);
1007 ap_my_addrspace = apr_pstrdup (p, addrname);
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;
1025 static void netware_mpm_hooks(apr_pool_t *p)
1027 ap_hook_pre_config(netware_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
1030 void netware_rewrite_args(process_rec *process)
1032 char *def_server_root;
1034 const char *opt_arg;
1036 apr_array_header_t *mpm_new_argv;
1040 InstallConsoleHandler();
1042 /* Make sure to hold the Apache screen open if exit() is called */
1043 hold_screen_on_exit = 1;
1045 /* Rewrite process->argv[];
1047 * add default -d serverroot from the path of this executable
1049 * The end result will look like:
1050 * The -d serverroot default from the running executable
1052 if (process->argc > 0) {
1053 char *s = apr_pstrdup (process->pconf, process->argv[0]);
1055 int i, len = strlen(s);
1057 for (i=len; i; i--) {
1058 if (s[i] == '\\' || s[i] == '/') {
1060 apr_filepath_merge(&def_server_root, NULL, s,
1061 APR_FILEPATH_TRUENAME, process->pool);
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.
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;
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]) {
1083 renamescreen(opt_arg);
1087 /* Don't need to hold the screen open if the output is going to a file */
1088 hold_screen_on_exit = -1;
1090 *(const char **)apr_array_push(mpm_new_argv) =
1091 apr_pstrdup(process->pool, optbuf);
1094 *(const char **)apr_array_push(mpm_new_argv) = opt_arg;
1099 process->argc = mpm_new_argv->nelts;
1100 process->argv = (const char * const *) mpm_new_argv->elts;
1105 static int CommandLineInterpreter(scr_t screenID, const char *commandLine)
1107 char *szCommand = "APACHE2 ";
1108 int iCommandLen = 8;
1109 char szcommandLine[256];
1111 screenID = screenID;
1114 if (commandLine == NULL)
1115 return NOTMYCOMMAND;
1116 if (strlen(commandLine) <= strlen(szCommand))
1117 return NOTMYCOMMAND;
1119 strncpy (szcommandLine, commandLine, sizeof(szcommandLine)-1);
1121 /* All added commands begin with "APACHE2 " */
1123 if (!strnicmp(szCommand, szcommandLine, iCommandLen)) {
1124 ActivateScreen (getscreenhandle());
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;
1133 /* If we got an instance id but it doesn't match this
1134 instance of the nlm, pass it on. */
1137 while (*pID && (*pID == ' '))
1140 if (pID && ap_my_addrspace && strnicmp(pID, ap_my_addrspace, strlen(ap_my_addrspace)))
1141 return NOTMYCOMMAND;
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");
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());
1153 else if (!strnicmp("MODULES",&szcommandLine[iCommandLen],3)) {
1156 else if (!strnicmp("DIRECTIVES",&szcommandLine[iCommandLen],3)) {
1157 ap_show_directives();
1159 else if (!strnicmp("SHUTDOWN",&szcommandLine[iCommandLen],3)) {
1160 printf("Shutdown Requested...\n");
1161 shutdown_pending = 1;
1163 else if (!strnicmp("SETTINGS",&szcommandLine[iCommandLen],3)) {
1164 if (show_settings) {
1166 ClearScreen (getscreenhandle());
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");
1189 /* Tell NetWare we handled the command */
1190 return HANDLEDCOMMAND;
1193 /* Tell NetWare that the command isn't mine */
1194 return NOTMYCOMMAND;
1197 static int InstallConsoleHandler(void)
1199 /* Our command line handler interfaces the system operator
1202 NX_WRAP_INTERFACE(CommandLineInterpreter, 2, (void*)&(ConsoleHandler.parser));
1204 ConsoleHandler.rTag = AllocateResourceTag(getnlmhandle(), "Command Line Processor",
1205 ConsoleCommandSignature);
1206 if (!ConsoleHandler.rTag)
1208 printf("Error on allocate resource tag\n");
1212 RegisterConsoleCommand(&ConsoleHandler);
1214 /* The Remove procedure unregisters the console handler */
1219 static void RemoveConsoleHandler(void)
1221 UnRegisterConsoleCommand(&ConsoleHandler);
1222 NX_UNWRAP_INTERFACE(ConsoleHandler.parser);
1225 static const char *set_threads_to_start(cmd_parms *cmd, void *dummy, const char *arg)
1227 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1232 ap_threads_to_start = atoi(arg);
1236 static const char *set_min_free_threads(cmd_parms *cmd, void *dummy, const char *arg)
1238 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
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;
1257 static const char *set_max_free_threads(cmd_parms *cmd, void *dummy, const char *arg)
1259 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1264 ap_threads_max_free = atoi(arg);
1268 static const char *set_thread_limit (cmd_parms *cmd, void *dummy, const char *arg)
1270 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
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;
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;
1296 static const char *set_thread_stacksize(cmd_parms *cmd, void *dummy,
1299 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1304 ap_thread_stack_size = atoi(arg);
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."),
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"),
1323 module AP_MODULE_DECLARE_DATA mpm_netware_module = {
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 */