~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to server/mpm/netware/mpm_netware.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
#ifndef USE_WINSOCK
 
61
#include <sys/select.h>
 
62
#endif
 
63
 
 
64
#define CORE_PRIVATE
 
65
 
 
66
#include "ap_config.h"
 
67
#include "httpd.h"
 
68
#include "mpm_default.h"
 
69
#include "http_main.h"
 
70
#include "http_log.h"
 
71
#include "http_config.h"
 
72
#include "http_core.h"             /* for get_remote_host */
 
73
#include "http_connection.h"
 
74
#include "scoreboard.h"
 
75
#include "ap_mpm.h"
 
76
#include "mpm_common.h"
 
77
#include "ap_listen.h"
 
78
#include "ap_mmn.h"
 
79
 
 
80
#ifdef HAVE_TIME_H
 
81
#include <time.h>
 
82
#endif
 
83
 
 
84
#include <signal.h>
 
85
 
 
86
#include <netware.h>
 
87
#include <nks/netware.h>
 
88
#include <library.h>
 
89
#include <screen.h>
 
90
 
 
91
/* Limit on the total --- clients will be locked out if more servers than
 
92
 * this are needed.  It is intended solely to keep the server from crashing
 
93
 * when things get out of hand.
 
94
 *
 
95
 * We keep a hard maximum number of servers, for two reasons --- first off,
 
96
 * in case something goes seriously wrong, we want to stop the fork bomb
 
97
 * short of actually crashing the machine we're running on by filling some
 
98
 * kernel table.  Secondly, it keeps the size of the scoreboard file small
 
99
 * enough that we can read the whole thing without worrying too much about
 
100
 * the overhead.
 
101
 */
 
102
#ifndef HARD_SERVER_LIMIT
 
103
#define HARD_SERVER_LIMIT 1
 
104
#endif
 
105
 
 
106
#define WORKER_DEAD         SERVER_DEAD
 
107
#define WORKER_STARTING     SERVER_STARTING
 
108
#define WORKER_READY        SERVER_READY
 
109
#define WORKER_IDLE_KILL    SERVER_IDLE_KILL
 
110
 
 
111
/* config globals */
 
112
 
 
113
int ap_threads_per_child=0;         /* Worker threads per child */
 
114
static int ap_threads_to_start=0;
 
115
static int ap_threads_min_free=0;
 
116
static int ap_threads_max_free=0;
 
117
static int ap_threads_limit=0;
 
118
static int mpm_state = AP_MPMQ_STARTING;
 
119
 
 
120
/*
 
121
 * The max child slot ever assigned, preserved across restarts.  Necessary
 
122
 * to deal with MaxClients changes across SIGWINCH restarts.  We use this
 
123
 * value to optimize routines that have to scan the entire scoreboard.
 
124
 */
 
125
int ap_max_workers_limit = -1;
 
126
server_rec *ap_server_conf;
 
127
 
 
128
/* *Non*-shared http_main globals... */
 
129
 
 
130
int hold_screen_on_exit = 0; /* Indicates whether the screen should be held open */
 
131
 
 
132
static fd_set listenfds;
 
133
static int listenmaxfd;
 
134
 
 
135
static apr_pool_t *pconf;               /* Pool for config stuff */
 
136
static apr_pool_t *pmain;               /* Pool for httpd child stuff */
 
137
 
 
138
static pid_t ap_my_pid;  /* it seems silly to call getpid all the time */
 
139
static char *ap_my_addrspace = NULL;
 
140
 
 
141
static int die_now = 0;
 
142
 
 
143
/* Keep track of the number of worker threads currently active */
 
144
static unsigned long worker_thread_count;
 
145
static int request_count;
 
146
 
 
147
/*  Structure used to register/deregister a console handler with the OS */
 
148
static int InstallConsoleHandler(void);
 
149
static void RemoveConsoleHandler(void);
 
150
static int CommandLineInterpreter(scr_t screenID, const char *commandLine);
 
151
static  CommandParser_t ConsoleHandler = {0, NULL, 0};
 
152
#define HANDLEDCOMMAND  0
 
153
#define NOTMYCOMMAND    1
 
154
 
 
155
static int show_settings = 0;
 
156
 
 
157
//#define DBINFO_ON
 
158
//#define DBPRINT_ON
 
159
#ifdef DBPRINT_ON
 
160
#define DBPRINT0(s) printf(s)
 
161
#define DBPRINT1(s,v1) printf(s,v1)
 
162
#define DBPRINT2(s,v1,v2) printf(s,v1,v2)
 
163
#else
 
164
#define DBPRINT0(s)
 
165
#define DBPRINT1(s,v1)
 
166
#define DBPRINT2(s,v1,v2)
 
167
#endif
 
168
 
 
169
/* volatile just in case */
 
170
static int volatile shutdown_pending;
 
171
static int volatile restart_pending;
 
172
static int volatile is_graceful;
 
173
static int volatile wait_to_finish=1;
 
174
ap_generation_t volatile ap_my_generation=0;
 
175
 
 
176
/* a clean exit from a child with proper cleanup */
 
177
static void clean_child_exit(int code, int worker_num, apr_pool_t *ptrans,
 
178
                             apr_bucket_alloc_t *bucket_alloc) __attribute__ ((noreturn));
 
179
static void clean_child_exit(int code, int worker_num, apr_pool_t *ptrans,
 
180
                             apr_bucket_alloc_t *bucket_alloc)
 
181
{
 
182
    apr_bucket_alloc_destroy(bucket_alloc);
 
183
    if (!shutdown_pending) {
 
184
        apr_pool_destroy(ptrans);
 
185
    }
 
186
 
 
187
    atomic_dec (&worker_thread_count);
 
188
    if (worker_num >=0)
 
189
        ap_update_child_status_from_indexes(0, worker_num, WORKER_DEAD,
 
190
                                            (request_rec *) NULL);
 
191
    NXThreadExit((void*)&code);
 
192
}
 
193
 
 
194
AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
 
195
{
 
196
    switch(query_code){
 
197
        case AP_MPMQ_MAX_DAEMON_USED:
 
198
            *result = 1;
 
199
            return APR_SUCCESS;
 
200
        case AP_MPMQ_IS_THREADED:
 
201
            *result = AP_MPMQ_DYNAMIC;
 
202
            return APR_SUCCESS;
 
203
        case AP_MPMQ_IS_FORKED:
 
204
            *result = AP_MPMQ_NOT_SUPPORTED;
 
205
            return APR_SUCCESS;
 
206
        case AP_MPMQ_HARD_LIMIT_DAEMONS:
 
207
            *result = HARD_SERVER_LIMIT;
 
208
            return APR_SUCCESS;
 
209
        case AP_MPMQ_HARD_LIMIT_THREADS:
 
210
            *result = HARD_THREAD_LIMIT;
 
211
            return APR_SUCCESS;
 
212
        case AP_MPMQ_MAX_THREADS:
 
213
            *result = ap_threads_limit;
 
214
            return APR_SUCCESS;
 
215
        case AP_MPMQ_MIN_SPARE_DAEMONS:
 
216
            *result = 0;
 
217
            return APR_SUCCESS;
 
218
        case AP_MPMQ_MIN_SPARE_THREADS:
 
219
            *result = ap_threads_min_free;
 
220
            return APR_SUCCESS;
 
221
        case AP_MPMQ_MAX_SPARE_DAEMONS:
 
222
            *result = 0;
 
223
            return APR_SUCCESS;
 
224
        case AP_MPMQ_MAX_SPARE_THREADS:
 
225
            *result = ap_threads_max_free;
 
226
            return APR_SUCCESS;
 
227
        case AP_MPMQ_MAX_REQUESTS_DAEMON:
 
228
            *result = ap_max_requests_per_child;
 
229
            return APR_SUCCESS;
 
230
        case AP_MPMQ_MAX_DAEMONS:
 
231
            *result = 1;
 
232
            return APR_SUCCESS;
 
233
        case AP_MPMQ_MPM_STATE:
 
234
            *result = mpm_state;
 
235
            return APR_SUCCESS;
 
236
    }
 
237
    return APR_ENOTIMPL;
 
238
}
 
239
 
 
240
 
 
241
/*****************************************************************
 
242
 * Connection structures and accounting...
 
243
 */
 
244
 
 
245
static void mpm_term(void)
 
246
{
 
247
    RemoveConsoleHandler();
 
248
    wait_to_finish = 0;
 
249
    NXThreadYield();
 
250
}
 
251
 
 
252
static void sig_term(int sig)
 
253
{
 
254
    if (shutdown_pending == 1) {
 
255
        /* Um, is this _probably_ not an error, if the user has
 
256
         * tried to do a shutdown twice quickly, so we won't
 
257
         * worry about reporting it.
 
258
         */
 
259
        return;
 
260
    }
 
261
    shutdown_pending = 1;
 
262
 
 
263
    DBPRINT0 ("waiting for threads\n");
 
264
    while (wait_to_finish) {
 
265
        apr_thread_yield();
 
266
    }
 
267
    DBPRINT0 ("goodbye\n");
 
268
}
 
269
 
 
270
/* restart() is the signal handler for SIGHUP and SIGWINCH
 
271
 * in the parent process, unless running in ONE_PROCESS mode
 
272
 */
 
273
static void restart(void)
 
274
{
 
275
    if (restart_pending == 1) {
 
276
        /* Probably not an error - don't bother reporting it */
 
277
        return;
 
278
    }
 
279
    restart_pending = 1;
 
280
    is_graceful = 1;
 
281
}
 
282
 
 
283
static void set_signals(void)
 
284
{
 
285
    apr_signal(SIGTERM, sig_term);
 
286
    apr_signal(SIGABRT, sig_term);
 
287
}
 
288
 
 
289
int nlmUnloadSignaled(int wait)
 
290
{
 
291
    shutdown_pending = 1;
 
292
 
 
293
    if (wait) {
 
294
        while (wait_to_finish) {
 
295
            NXThreadYield();
 
296
        }
 
297
    }
 
298
 
 
299
    return 0;
 
300
}
 
301
 
 
302
/*****************************************************************
 
303
 * Child process main loop.
 
304
 * The following vars are static to avoid getting clobbered by longjmp();
 
305
 * they are really private to child_main.
 
306
 */
 
307
 
 
308
 
 
309
int ap_graceful_stop_signalled(void)
 
310
{
 
311
    /* not ever called anymore... */
 
312
    return 0;
 
313
}
 
314
 
 
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
 
322
 
 
323
/*static */
 
324
void worker_main(void *arg)
 
325
{
 
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;
 
334
 
 
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;
 
340
 
 
341
    int sockdes;
 
342
    int srv;
 
343
    struct timeval tv;
 
344
    int wouldblock_retry;
 
345
 
 
346
    tv.tv_sec = 1;
 
347
    tv.tv_usec = 0;
 
348
 
 
349
    apr_allocator_create(&allocator);
 
350
    apr_allocator_max_free_set(allocator, ap_max_mem_free);
 
351
 
 
352
    apr_pool_create_ex(&ptrans, pmain, NULL, allocator);
 
353
    apr_allocator_owner_set(allocator, ptrans);
 
354
    apr_pool_tag(ptrans, "transaction");
 
355
 
 
356
    bucket_alloc = apr_bucket_alloc_create_ex(allocator);
 
357
 
 
358
    atomic_inc (&worker_thread_count);
 
359
 
 
360
    while (!die_now) {
 
361
        /*
 
362
        * (Re)initialize this child to a pre-connection state.
 
363
        */
 
364
        current_conn = NULL;
 
365
        apr_pool_clear(ptrans);
 
366
 
 
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);
 
371
        }
 
372
 
 
373
        ap_update_child_status_from_indexes(0, my_worker_num, WORKER_READY,
 
374
                                            (request_rec *) NULL);
 
375
 
 
376
        /*
 
377
        * Wait for an acceptable connection to arrive.
 
378
        */
 
379
 
 
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);
 
384
            }
 
385
 
 
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);
 
389
 
 
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();
 
395
                }
 
396
                continue;
 
397
            }
 
398
 
 
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;
 
403
            }
 
404
            else {
 
405
                lr = last_lr->next;
 
406
                if (!lr)
 
407
                    lr = ap_listeners;
 
408
            }
 
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.
 
420
            */
 
421
            continue;
 
422
        got_listener:
 
423
            last_lr = lr;
 
424
            sd = lr->sd;
 
425
 
 
426
            wouldblock_retry = MAX_WB_RETRIES;
 
427
 
 
428
            while (wouldblock_retry) {
 
429
                if ((stat = apr_socket_accept(&csd, sd, ptrans)) == APR_SUCCESS) {
 
430
                    break;
 
431
                }
 
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;
 
439
                    }
 
440
 
 
441
                    if (wouldblock_retry--) {
 
442
                        apr_thread_yield();
 
443
                    }
 
444
                }
 
445
            }
 
446
 
 
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);
 
455
                }
 
456
#endif
 
457
                break;       /* We have a socket ready for reading */
 
458
            }
 
459
            else {
 
460
#ifdef DBINFO_ON
 
461
                if (APR_STATUS_IS_EAGAIN(stat)) {
 
462
                        would_block++;
 
463
                        retry_fail++;
 
464
                }
 
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)) {
 
473
                        ;
 
474
                }
 
475
#ifdef USE_WINSOCK
 
476
                else if (APR_STATUS_IS_ENETDOWN(stat)) {
 
477
                       /*
 
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.
 
490
                        */
 
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);
 
495
                }
 
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);
 
501
                }
 
502
            }
 
503
        }
 
504
 
 
505
        ap_create_sb_handle(&sbh, ptrans, 0, my_worker_num);
 
506
        /*
 
507
        * We now have a connection, so set it up with the appropriate
 
508
        * socket options, file descriptors, and read/write buffers.
 
509
        */
 
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);
 
516
        }
 
517
        request_count++;
 
518
    }
 
519
    clean_child_exit(0, my_worker_num, ptrans, bucket_alloc);
 
520
}
 
521
 
 
522
 
 
523
static int make_child(server_rec *s, int slot)
 
524
{
 
525
    int tid;
 
526
    int err=0;
 
527
    NXContext_t ctx;
 
528
 
 
529
    if (slot + 1 > ap_max_workers_limit) {
 
530
        ap_max_workers_limit = slot + 1;
 
531
    }
 
532
 
 
533
    ap_update_child_status_from_indexes(0, slot, WORKER_STARTING,
 
534
                                        (request_rec *) NULL);
 
535
 
 
536
    if (ctx = NXContextAlloc((void (*)(void *)) worker_main, (void*)slot, NX_PRIO_MED, ap_thread_stacksize, NX_CTX_NORMAL, &err)) {
 
537
        char threadName[32];
 
538
 
 
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);
 
544
        }
 
545
    }
 
546
 
 
547
    if (err) {
 
548
        /* create thread didn't succeed. Fix the scoreboard or else
 
549
        * it will say SERVER_STARTING forever and ever
 
550
        */
 
551
        ap_update_child_status_from_indexes(0, slot, WORKER_DEAD,
 
552
                                            (request_rec *) NULL);
 
553
 
 
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();
 
558
 
 
559
        return -1;
 
560
    }
 
561
 
 
562
    ap_scoreboard_image->servers[0][slot].tid = tid;
 
563
 
 
564
    return 0;
 
565
}
 
566
 
 
567
 
 
568
/* start up a bunch of worker threads */
 
569
static void startup_workers(int number_to_start)
 
570
{
 
571
    int i;
 
572
 
 
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;
 
576
        }
 
577
        if (make_child(ap_server_conf, i) < 0) {
 
578
            break;
 
579
        }
 
580
        --number_to_start;
 
581
    }
 
582
}
 
583
 
 
584
 
 
585
/*
 
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.
 
590
 */
 
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;
 
596
 
 
597
static void perform_idle_server_maintenance(apr_pool_t *p)
 
598
{
 
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;
 
607
 
 
608
    /* initialize the free_list */
 
609
    free_length = 0;
 
610
 
 
611
    to_kill = -1;
 
612
    idle_count = 0;
 
613
    last_non_dead = -1;
 
614
    total_non_dead = 0;
 
615
 
 
616
    for (i = 0; i < ap_threads_limit; ++i) {
 
617
        int status;
 
618
 
 
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;
 
628
            }
 
629
        }
 
630
        else if (status == WORKER_IDLE_KILL) {
 
631
            /* If it is already marked to die, skip it */
 
632
            continue;
 
633
        }
 
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.
 
640
            */
 
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).
 
648
                */
 
649
                to_kill = i;
 
650
            }
 
651
 
 
652
            ++total_non_dead;
 
653
            last_non_dead = i;
 
654
        }
 
655
    }
 
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
 
662
        */
 
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);
 
667
    }
 
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;
 
673
 
 
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;
 
679
            }
 
680
            idle_spawn_rate = 1;
 
681
        }
 
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);
 
690
            }
 
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]);
 
695
            }
 
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
 
698
            */
 
699
            if (hold_off_on_exponential_spawning) {
 
700
                --hold_off_on_exponential_spawning;
 
701
            }
 
702
            else if (idle_spawn_rate < MAX_SPAWN_RATE) {
 
703
                idle_spawn_rate *= 2;
 
704
            }
 
705
        }
 
706
    }
 
707
    else {
 
708
        idle_spawn_rate = 1;
 
709
    }
 
710
}
 
711
 
 
712
static void display_settings ()
 
713
{
 
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;
 
719
 
 
720
    would_block = 0;
 
721
#endif
 
722
 
 
723
    request_count = 0;
 
724
 
 
725
    ClearScreen (getscreenhandle());
 
726
    printf("%s \n", ap_get_server_version());
 
727
 
 
728
    for (i=0;i<SERVER_NUM_STATUS;i++) {
 
729
        status_array[i] = 0;
 
730
    }
 
731
 
 
732
    for (i = 0; i < ap_threads_limit; ++i) {
 
733
        status = (ap_scoreboard_image->servers[0][i]).status;
 
734
        status_array[status]++;
 
735
    }
 
736
 
 
737
    for (i=0;i<SERVER_NUM_STATUS;i++) {
 
738
        switch(i)
 
739
        {
 
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;
 
776
        }
 
777
        if (i != SERVER_DEAD)
 
778
            total+=status_array[i];
 
779
    }
 
780
    printf ("Total Running:\t%d\tout of: \t%d\n", total, ap_threads_limit);
 
781
    printf ("Requests per interval:\t%d\n", reqs);
 
782
 
 
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
 
789
}
 
790
 
 
791
static void show_server_data()
 
792
{
 
793
    ap_listen_rec *lr;
 
794
    module **m;
 
795
 
 
796
    printf("%s\n", ap_get_server_version());
 
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);
 
799
 
 
800
 
 
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);
 
808
 
 
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);
 
814
        }
 
815
    }
 
816
}
 
817
 
 
818
 
 
819
static int setup_listeners(server_rec *s)
 
820
{
 
821
    ap_listen_rec *lr;
 
822
    int sockdes;
 
823
 
 
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;
 
828
    }
 
829
 
 
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;
 
837
        }
 
838
    }
 
839
    return 0;
 
840
}
 
841
 
 
842
static int shutdown_listeners()
 
843
{
 
844
    ap_listen_rec *lr;
 
845
 
 
846
    for (lr = ap_listeners; lr; lr = lr->next) {
 
847
        apr_socket_close(lr->sd);
 
848
    }
 
849
    ap_listeners = NULL;
 
850
    return 0;
 
851
}
 
852
 
 
853
/*****************************************************************
 
854
 * Executive routines.
 
855
 */
 
856
 
 
857
int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
 
858
{
 
859
    apr_status_t status=0;
 
860
 
 
861
    pconf = _pconf;
 
862
    ap_server_conf = s;
 
863
 
 
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;
 
868
    }
 
869
 
 
870
    restart_pending = shutdown_pending = 0;
 
871
    worker_thread_count = 0;
 
872
 
 
873
    if (!is_graceful) {
 
874
        if (ap_run_pre_mpm(s->process->pool, SB_NOT_SHARED) != OK) {
 
875
            return 1;
 
876
        }
 
877
    }
 
878
 
 
879
    /* Only set slot 0 since that is all NetWare will ever have. */
 
880
    ap_scoreboard_image->parent[0].pid = getpid();
 
881
 
 
882
    set_signals();
 
883
 
 
884
    apr_pool_create(&pmain, pconf);
 
885
    ap_run_child_init(pmain, ap_server_conf);
 
886
 
 
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;
 
890
 
 
891
    startup_workers(ap_threads_to_start);
 
892
 
 
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;
 
898
    }
 
899
 
 
900
    ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
 
901
            "%s configured -- resuming normal operations",
 
902
            ap_get_server_version());
 
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();
 
912
 
 
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);
 
920
    }
 
921
    mpm_state = AP_MPMQ_STOPPING;
 
922
 
 
923
 
 
924
    /* Shutdown the listen sockets so that we don't get stuck in a blocking call.
 
925
    shutdown_listeners();*/
 
926
 
 
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");
 
930
 
 
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();
 
935
        }
 
936
 
 
937
        return 1;
 
938
    }
 
939
    else {  /* the only other way out is a restart */
 
940
        /* advance to the next generation */
 
941
        /* XXX: we really need to make sure this new generation number isn't in
 
942
         * use by any of the children.
 
943
         */
 
944
        ++ap_my_generation;
 
945
        ap_scoreboard_image->global->running_generation = ap_my_generation;
 
946
 
 
947
        ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
 
948
                "Graceful restart requested, doing restart");
 
949
 
 
950
        /* Wait for all of the threads to terminate before initiating the restart */
 
951
        while (worker_thread_count > 0) {
 
952
            printf ("\rRestart pending. Waiting for %d thread(s) to terminate...",
 
953
                    worker_thread_count);
 
954
            apr_thread_yield();
 
955
        }
 
956
        printf ("\nRestarting...\n");
 
957
    }
 
958
 
 
959
    return 0;
 
960
}
 
961
 
 
962
static int netware_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
 
963
{
 
964
    int debug;
 
965
    char *addrname = NULL;
 
966
 
 
967
    mpm_state = AP_MPMQ_STARTING;
 
968
 
 
969
    debug = ap_exists_config_define("DEBUG");
 
970
 
 
971
    is_graceful = 0;
 
972
    ap_my_pid = getpid();
 
973
    addrname = getaddressspacename (NULL, NULL);
 
974
    if (addrname) {
 
975
        ap_my_addrspace = apr_pstrdup (p, addrname);
 
976
        free (addrname);
 
977
    }
 
978
 
 
979
#ifndef USE_WINSOCK
 
980
    /* The following call has been moved to the mod_nw_ssl pre-config handler */
 
981
    ap_listen_pre_config();
 
982
#endif
 
983
 
 
984
    ap_threads_to_start = DEFAULT_START_THREADS;
 
985
    ap_threads_min_free = DEFAULT_MIN_FREE_THREADS;
 
986
    ap_threads_max_free = DEFAULT_MAX_FREE_THREADS;
 
987
    ap_threads_limit = HARD_THREAD_LIMIT;
 
988
    ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
 
989
    ap_extended_status = 0;
 
990
    ap_thread_stacksize = DEFAULT_THREAD_STACKSIZE;
 
991
#ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
 
992
    ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
 
993
#endif
 
994
 
 
995
    return OK;
 
996
}
 
997
 
 
998
static void netware_mpm_hooks(apr_pool_t *p)
 
999
{
 
1000
    ap_hook_pre_config(netware_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
 
1001
}
 
1002
 
 
1003
void netware_rewrite_args(process_rec *process)
 
1004
{
 
1005
    char *def_server_root;
 
1006
    char optbuf[3];
 
1007
    const char *opt_arg;
 
1008
    apr_getopt_t *opt;
 
1009
    apr_array_header_t *mpm_new_argv;
 
1010
 
 
1011
 
 
1012
    atexit (mpm_term);
 
1013
    InstallConsoleHandler();
 
1014
 
 
1015
    /* Make sure to hold the Apache screen open if exit() is called */
 
1016
    hold_screen_on_exit = 1;
 
1017
 
 
1018
    /* Rewrite process->argv[];
 
1019
     *
 
1020
     * add default -d serverroot from the path of this executable
 
1021
     *
 
1022
     * The end result will look like:
 
1023
     *     The -d serverroot default from the running executable
 
1024
     */
 
1025
    if (process->argc > 0) {
 
1026
        char *s = apr_pstrdup (process->pconf, process->argv[0]);
 
1027
        if (s) {
 
1028
            int i, len = strlen(s);
 
1029
 
 
1030
            for (i=len; i; i--) {
 
1031
                if (s[i] == '\\' || s[i] == '/') {
 
1032
                    s[i] = '\0';
 
1033
                    apr_filepath_merge(&def_server_root, NULL, s,
 
1034
                        APR_FILEPATH_TRUENAME, process->pool);
 
1035
                    break;
 
1036
                }
 
1037
            }
 
1038
            /* Use process->pool so that the rewritten argv
 
1039
            * lasts for the lifetime of the server process,
 
1040
            * because pconf will be destroyed after the
 
1041
            * initial pre-flight of the config parser.
 
1042
            */
 
1043
            mpm_new_argv = apr_array_make(process->pool, process->argc + 2,
 
1044
                                  sizeof(const char *));
 
1045
            *(const char **)apr_array_push(mpm_new_argv) = process->argv[0];
 
1046
            *(const char **)apr_array_push(mpm_new_argv) = "-d";
 
1047
            *(const char **)apr_array_push(mpm_new_argv) = def_server_root;
 
1048
 
 
1049
            optbuf[0] = '-';
 
1050
            optbuf[2] = '\0';
 
1051
            apr_getopt_init(&opt, process->pool, process->argc, (char**) process->argv);
 
1052
            while (apr_getopt(opt, AP_SERVER_BASEARGS"n:", optbuf + 1, &opt_arg) == APR_SUCCESS) {
 
1053
                switch (optbuf[1]) {
 
1054
                case 'n':
 
1055
                    if (opt_arg) {
 
1056
                        renamescreen(opt_arg);
 
1057
                    }
 
1058
                    break;
 
1059
                case 'E':
 
1060
                    /* Don't need to hold the screen open if the output is going to a file */
 
1061
                    hold_screen_on_exit = -1;
 
1062
                default:
 
1063
                    *(const char **)apr_array_push(mpm_new_argv) =
 
1064
                        apr_pstrdup(process->pool, optbuf);
 
1065
 
 
1066
                    if (opt_arg) {
 
1067
                        *(const char **)apr_array_push(mpm_new_argv) = opt_arg;
 
1068
                    }
 
1069
                    break;
 
1070
                }
 
1071
            }
 
1072
            process->argc = mpm_new_argv->nelts;
 
1073
            process->argv = (const char * const *) mpm_new_argv->elts;
 
1074
        }
 
1075
    }
 
1076
}
 
1077
 
 
1078
static int CommandLineInterpreter(scr_t screenID, const char *commandLine)
 
1079
{
 
1080
    char *szCommand = "APACHE2 ";
 
1081
    int iCommandLen = 8;
 
1082
    char szcommandLine[256];
 
1083
    char *pID;
 
1084
    screenID = screenID;
 
1085
 
 
1086
 
 
1087
    if (commandLine == NULL)
 
1088
        return NOTMYCOMMAND;
 
1089
    if (strlen(commandLine) <= strlen(szCommand))
 
1090
        return NOTMYCOMMAND;
 
1091
 
 
1092
    strncpy (szcommandLine, commandLine, sizeof(szcommandLine)-1);
 
1093
 
 
1094
    /*  All added commands begin with "APACHE2 " */
 
1095
 
 
1096
    if (!strnicmp(szCommand, szcommandLine, iCommandLen)) {
 
1097
        ActivateScreen (getscreenhandle());
 
1098
 
 
1099
        /* If an instance id was not given but the nlm is loaded in
 
1100
            protected space, then the the command belongs to the
 
1101
            OS address space instance to pass it on. */
 
1102
        pID = strstr (szcommandLine, "-p");
 
1103
        if ((pID == NULL) && nlmisloadedprotected())
 
1104
            return NOTMYCOMMAND;
 
1105
 
 
1106
        /* If we got an instance id but it doesn't match this
 
1107
            instance of the nlm, pass it on. */
 
1108
        if (pID) {
 
1109
            pID = &pID[2];
 
1110
            while (*pID && (*pID == ' '))
 
1111
                pID++;
 
1112
        }
 
1113
        if (pID && ap_my_addrspace && strnicmp(pID, ap_my_addrspace, strlen(ap_my_addrspace)))
 
1114
            return NOTMYCOMMAND;
 
1115
 
 
1116
        /* If we have determined that this command belongs to this
 
1117
            instance of the nlm, then handle it. */
 
1118
        if (!strnicmp("RESTART",&szcommandLine[iCommandLen],3)) {
 
1119
            printf("Restart Requested...\n");
 
1120
            restart();
 
1121
        }
 
1122
        else if (!strnicmp("VERSION",&szcommandLine[iCommandLen],3)) {
 
1123
            printf("Server version: %s\n", ap_get_server_version());
 
1124
            printf("Server built:   %s\n", ap_get_server_built());
 
1125
        }
 
1126
        else if (!strnicmp("MODULES",&szcommandLine[iCommandLen],3)) {
 
1127
            ap_show_modules();
 
1128
        }
 
1129
        else if (!strnicmp("DIRECTIVES",&szcommandLine[iCommandLen],3)) {
 
1130
                ap_show_directives();
 
1131
        }
 
1132
        else if (!strnicmp("SHUTDOWN",&szcommandLine[iCommandLen],3)) {
 
1133
            printf("Shutdown Requested...\n");
 
1134
            shutdown_pending = 1;
 
1135
        }
 
1136
        else if (!strnicmp("SETTINGS",&szcommandLine[iCommandLen],3)) {
 
1137
            if (show_settings) {
 
1138
                show_settings = 0;
 
1139
                ClearScreen (getscreenhandle());
 
1140
                show_server_data();
 
1141
            }
 
1142
            else {
 
1143
                show_settings = 1;
 
1144
                display_settings();
 
1145
            }
 
1146
        }
 
1147
        else {
 
1148
            show_settings = 0;
 
1149
            if (strnicmp("HELP",&szcommandLine[iCommandLen],3))
 
1150
                printf("Unknown APACHE2 command %s\n", &szcommandLine[iCommandLen]);
 
1151
            printf("Usage: APACHE2 [command] [-p <instance ID>]\n");
 
1152
            printf("Commands:\n");
 
1153
            printf("\tDIRECTIVES - Show directives\n");
 
1154
            printf("\tHELP       - Display this help information\n");
 
1155
            printf("\tMODULES    - Show a list of the loaded modules\n");
 
1156
            printf("\tRESTART    - Reread the configuration file and restart Apache\n");
 
1157
            printf("\tSETTINGS   - Show current thread status\n");
 
1158
            printf("\tSHUTDOWN   - Shutdown Apache\n");
 
1159
            printf("\tVERSION    - Display the server version information\n");
 
1160
        }
 
1161
 
 
1162
        /*  Tell NetWare we handled the command */
 
1163
        return HANDLEDCOMMAND;
 
1164
    }
 
1165
 
 
1166
    /*  Tell NetWare that the command isn't mine */
 
1167
    return NOTMYCOMMAND;
 
1168
}
 
1169
 
 
1170
static int InstallConsoleHandler(void)
 
1171
{
 
1172
    /*  Our command line handler interfaces the system operator
 
1173
    with this NLM */
 
1174
 
 
1175
    NX_WRAP_INTERFACE(CommandLineInterpreter, 2, (void*)&(ConsoleHandler.parser));
 
1176
 
 
1177
    ConsoleHandler.rTag = AllocateResourceTag(getnlmhandle(), "Command Line Processor",
 
1178
        ConsoleCommandSignature);
 
1179
    if (!ConsoleHandler.rTag)
 
1180
    {
 
1181
        printf("Error on allocate resource tag\n");
 
1182
        return 1;
 
1183
    }
 
1184
 
 
1185
    RegisterConsoleCommand(&ConsoleHandler);
 
1186
 
 
1187
    /*  The Remove procedure unregisters the console handler */
 
1188
 
 
1189
    return 0;
 
1190
}
 
1191
 
 
1192
static void RemoveConsoleHandler(void)
 
1193
{
 
1194
    UnRegisterConsoleCommand(&ConsoleHandler);
 
1195
    NX_UNWRAP_INTERFACE(ConsoleHandler.parser);
 
1196
}
 
1197
 
 
1198
static const char *set_threads_to_start(cmd_parms *cmd, void *dummy, const char *arg)
 
1199
{
 
1200
    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
 
1201
    if (err != NULL) {
 
1202
        return err;
 
1203
    }
 
1204
 
 
1205
    ap_threads_to_start = atoi(arg);
 
1206
    return NULL;
 
1207
}
 
1208
 
 
1209
static const char *set_min_free_threads(cmd_parms *cmd, void *dummy, const char *arg)
 
1210
{
 
1211
    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
 
1212
    if (err != NULL) {
 
1213
        return err;
 
1214
    }
 
1215
 
 
1216
    ap_threads_min_free = atoi(arg);
 
1217
    if (ap_threads_min_free <= 0) {
 
1218
       ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
 
1219
                    "WARNING: detected MinSpareServers set to non-positive.");
 
1220
       ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
 
1221
                    "Resetting to 1 to avoid almost certain Apache failure.");
 
1222
       ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
 
1223
                    "Please read the documentation.");
 
1224
       ap_threads_min_free = 1;
 
1225
    }
 
1226
 
 
1227
    return NULL;
 
1228
}
 
1229
 
 
1230
static const char *set_max_free_threads(cmd_parms *cmd, void *dummy, const char *arg)
 
1231
{
 
1232
    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
 
1233
    if (err != NULL) {
 
1234
        return err;
 
1235
    }
 
1236
 
 
1237
    ap_threads_max_free = atoi(arg);
 
1238
    return NULL;
 
1239
}
 
1240
 
 
1241
static const char *set_thread_limit (cmd_parms *cmd, void *dummy, const char *arg)
 
1242
{
 
1243
    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
 
1244
    if (err != NULL) {
 
1245
        return err;
 
1246
    }
 
1247
 
 
1248
    ap_threads_limit = atoi(arg);
 
1249
    if (ap_threads_limit > HARD_THREAD_LIMIT) {
 
1250
       ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
 
1251
                    "WARNING: MaxThreads of %d exceeds compile time limit "
 
1252
                    "of %d threads,", ap_threads_limit, HARD_THREAD_LIMIT);
 
1253
       ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
 
1254
                    " lowering MaxThreads to %d.  To increase, please "
 
1255
                    "see the", HARD_THREAD_LIMIT);
 
1256
       ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
 
1257
                    " HARD_THREAD_LIMIT define in %s.",
 
1258
                    AP_MPM_HARD_LIMITS_FILE);
 
1259
       ap_threads_limit = HARD_THREAD_LIMIT;
 
1260
    }
 
1261
    else if (ap_threads_limit < 1) {
 
1262
        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
 
1263
            "WARNING: Require MaxThreads > 0, setting to 1");
 
1264
        ap_threads_limit = 1;
 
1265
    }
 
1266
    return NULL;
 
1267
}
 
1268
 
 
1269
static const command_rec netware_mpm_cmds[] = {
 
1270
LISTEN_COMMANDS,
 
1271
AP_INIT_TAKE1("StartThreads", set_threads_to_start, NULL, RSRC_CONF,
 
1272
              "Number of worker threads launched at server startup"),
 
1273
AP_INIT_TAKE1("MinSpareThreads", set_min_free_threads, NULL, RSRC_CONF,
 
1274
              "Minimum number of idle threads, to handle request spikes"),
 
1275
AP_INIT_TAKE1("MaxSpareThreads", set_max_free_threads, NULL, RSRC_CONF,
 
1276
              "Maximum number of idle threads"),
 
1277
AP_INIT_TAKE1("MaxThreads", set_thread_limit, NULL, RSRC_CONF,
 
1278
              "Maximum number of worker threads alive at the same time"),
 
1279
{ NULL }
 
1280
};
 
1281
 
 
1282
module AP_MODULE_DECLARE_DATA mpm_netware_module = {
 
1283
    MPM20_MODULE_STUFF,
 
1284
    netware_rewrite_args,   /* hook to run before apache parses args */
 
1285
    NULL,                   /* create per-directory config structure */
 
1286
    NULL,                   /* merge per-directory config structures */
 
1287
    NULL,                   /* create per-server config structure */
 
1288
    NULL,                   /* merge per-server config structures */
 
1289
    netware_mpm_cmds,       /* command apr_table_t */
 
1290
    netware_mpm_hooks,      /* register hooks */
 
1291
};