upload http
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / server / mpm / mpmt_os2 / mpmt_os2.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /* Multi-process, multi-threaded MPM for OS/2
18  *
19  * Server consists of
20  * - a main, parent process
21  * - a small, static number of child processes
22  *
23  * The parent process's job is to manage the child processes. This involves
24  * spawning children as required to ensure there are always ap_daemons_to_start
25  * processes accepting connections.
26  *
27  * Each child process consists of a a pool of worker threads and a
28  * main thread that accepts connections & passes them to the workers via
29  * a work queue. The worker thread pool is dynamic, managed by a maintanence
30  * thread so that the number of idle threads is kept between
31  * min_spare_threads & max_spare_threads.
32  *
33  */
34
35 /*
36  Todo list
37  - Enforce MaxClients somehow
38 */
39 #define CORE_PRIVATE
40 #define INCL_NOPMAPI
41 #define INCL_DOS
42 #define INCL_DOSERRORS
43
44 #include "ap_config.h"
45 #include "httpd.h"
46 #include "mpm_default.h"
47 #include "http_main.h"
48 #include "http_log.h"
49 #include "http_config.h"
50 #include "http_core.h"          /* for get_remote_host */
51 #include "http_connection.h"
52 #include "mpm.h"
53 #include "ap_mpm.h"
54 #include "ap_listen.h"
55 #include "apr_portable.h"
56 #include "mpm_common.h"
57 #include "apr_strings.h"
58 #include <os2.h>
59 #include <process.h>
60
61 /* We don't need many processes, 
62  * they're only for redundancy in the event of a crash 
63  */
64 #define HARD_SERVER_LIMIT 10
65
66 /* Limit on the total number of threads per process
67  */
68 #ifndef HARD_THREAD_LIMIT
69 #define HARD_THREAD_LIMIT 256
70 #endif
71
72 server_rec *ap_server_conf;
73 static apr_pool_t *pconf = NULL;                /* Pool for config stuff */
74 static const char *ap_pid_fname=NULL;
75
76 /* Config globals */
77 static int one_process = 0;
78 static int ap_daemons_to_start = 0;
79 static int ap_thread_limit = 0;
80 static int ap_max_requests_per_child = 0;
81 int ap_min_spare_threads = 0;
82 int ap_max_spare_threads = 0;
83
84 /* Keep track of a few interesting statistics */
85 int ap_max_daemons_limit = -1;
86
87 /* volatile just in case */
88 static int volatile shutdown_pending;
89 static int volatile restart_pending;
90 static int volatile is_graceful = 0;
91 ap_generation_t volatile ap_my_generation=0; /* Used by the scoreboard */
92 static int is_parent_process=TRUE;
93 HMTX ap_mpm_accept_mutex = 0;
94
95 /* An array of these is stored in a shared memory area for passing
96  * sockets from the parent to child processes
97  */
98 typedef struct {
99     struct sockaddr_in name;
100     apr_os_sock_t listen_fd;
101 } listen_socket_t;
102
103 typedef struct {
104     HMTX accept_mutex;
105     listen_socket_t listeners[1];
106 } parent_info_t;
107
108 static char master_main();
109 static void spawn_child(int slot);
110 void ap_mpm_child_main(apr_pool_t *pconf);
111 static void set_signals();
112
113
114 int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s )
115 {
116     char *listener_shm_name;
117     parent_info_t *parent_info;
118     ULONG rc;
119     pconf = _pconf;
120     ap_server_conf = s;
121     restart_pending = 0;
122
123     DosSetMaxFH(ap_thread_limit * 2);
124     listener_shm_name = apr_psprintf(pconf, "/sharemem/httpd/parent_info.%d", getppid());
125     rc = DosGetNamedSharedMem((PPVOID)&parent_info, listener_shm_name, PAG_READ);
126     is_parent_process = rc != 0;
127     ap_scoreboard_fname = apr_psprintf(pconf, "/sharemem/httpd/scoreboard.%d", is_parent_process ? getpid() : getppid());
128
129     if (rc == 0) {
130         /* Child process */
131         ap_listen_rec *lr;
132         int num_listeners = 0;
133
134         ap_mpm_accept_mutex = parent_info->accept_mutex;
135
136         /* Set up a default listener if necessary */
137         if (ap_listeners == NULL) {
138             ap_listen_rec *lr = apr_pcalloc(s->process->pool, sizeof(ap_listen_rec));
139             ap_listeners = lr;
140             apr_sockaddr_info_get(&lr->bind_addr, "0.0.0.0", APR_UNSPEC,
141                                   DEFAULT_HTTP_PORT, 0, s->process->pool);
142             apr_socket_create(&lr->sd, lr->bind_addr->family,
143                               SOCK_STREAM, s->process->pool);
144         }
145
146         for (lr = ap_listeners; lr; lr = lr->next) {
147             apr_sockaddr_t *sa;
148             apr_os_sock_put(&lr->sd, &parent_info->listeners[num_listeners].listen_fd, pconf);
149             apr_socket_addr_get(&sa, APR_LOCAL, lr->sd);
150             num_listeners++;
151         }
152
153         DosFreeMem(parent_info);
154
155         /* Do the work */
156         ap_mpm_child_main(pconf);
157
158         /* Outta here */
159         return 1;
160     }
161     else {
162         /* Parent process */
163         char restart;
164         is_parent_process = TRUE;
165
166         if (ap_setup_listeners(ap_server_conf) < 1) {
167             ap_log_error(APLOG_MARK, APLOG_ALERT, 0, s,
168                          "no listening sockets available, shutting down");
169             return 1;
170         }
171
172         ap_log_pid(pconf, ap_pid_fname);
173
174         restart = master_main();
175         ++ap_my_generation;
176         ap_scoreboard_image->global->running_generation = ap_my_generation;
177
178         if (!restart) {
179             const char *pidfile = ap_server_root_relative(pconf, ap_pid_fname);
180
181             if (pidfile != NULL && remove(pidfile) == 0) {
182                 ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS,
183                              ap_server_conf, "removed PID file %s (pid=%d)",
184                              pidfile, getpid());
185             }
186
187             ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
188                          "caught SIGTERM, shutting down");
189             return 1;
190         }
191     }  /* Parent process */
192
193     return 0; /* Restart */
194 }
195
196
197
198 /* Main processing of the parent process
199  * returns TRUE if restarting
200  */
201 static char master_main()
202 {
203     server_rec *s = ap_server_conf;
204     ap_listen_rec *lr;
205     parent_info_t *parent_info;
206     char *listener_shm_name;
207     int listener_num, num_listeners, slot;
208     ULONG rc;
209
210     printf("%s \n", ap_get_server_version());
211     set_signals();
212
213     if (ap_setup_listeners(ap_server_conf) < 1) {
214         ap_log_error(APLOG_MARK, APLOG_ALERT, 0, s,
215                      "no listening sockets available, shutting down");
216         return FALSE;
217     }
218
219     /* Allocate a shared memory block for the array of listeners */
220     for (num_listeners = 0, lr = ap_listeners; lr; lr = lr->next) {
221         num_listeners++;
222     }
223
224     listener_shm_name = apr_psprintf(pconf, "/sharemem/httpd/parent_info.%d", getpid());
225     rc = DosAllocSharedMem((PPVOID)&parent_info, listener_shm_name,
226                            sizeof(parent_info_t) + num_listeners * sizeof(listen_socket_t),
227                            PAG_READ|PAG_WRITE|PAG_COMMIT);
228
229     if (rc) {
230         ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s,
231                      "failure allocating shared memory, shutting down");
232         return FALSE;
233     }
234
235     /* Store the listener sockets in the shared memory area for our children to see */
236     for (listener_num = 0, lr = ap_listeners; lr; lr = lr->next, listener_num++) {
237         apr_os_sock_get(&parent_info->listeners[listener_num].listen_fd, lr->sd);
238     }
239
240     /* Create mutex to prevent multiple child processes from detecting
241      * a connection with apr_poll()
242      */
243
244     rc = DosCreateMutexSem(NULL, &ap_mpm_accept_mutex, DC_SEM_SHARED, FALSE);
245
246     if (rc) {
247         ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s,
248                      "failure creating accept mutex, shutting down");
249         return FALSE;
250     }
251
252     parent_info->accept_mutex = ap_mpm_accept_mutex;
253
254     /* Allocate shared memory for scoreboard */
255     if (ap_scoreboard_image == NULL) {
256         void *sb_mem;
257         rc = DosAllocSharedMem(&sb_mem, ap_scoreboard_fname,
258                                ap_calc_scoreboard_size(),
259                                PAG_COMMIT|PAG_READ|PAG_WRITE);
260
261         if (rc) {
262             ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
263                          "unable to allocate shared memory for scoreboard , exiting");
264             return FALSE;
265         }
266
267         ap_init_scoreboard(sb_mem);
268     }
269
270     ap_scoreboard_image->global->restart_time = apr_time_now();
271     ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
272                 "%s configured -- resuming normal operations",
273                 ap_get_server_version());
274     ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
275                 "Server built: %s", ap_get_server_built());
276 #ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH
277     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
278                 "AcceptMutex: %s (default: %s)",
279                 apr_proc_mutex_name(accept_mutex),
280                 apr_proc_mutex_defname());
281 #endif
282     if (one_process) {
283         ap_scoreboard_image->parent[0].pid = getpid();
284         ap_mpm_child_main(pconf);
285         return FALSE;
286     }
287
288     while (!restart_pending && !shutdown_pending) {
289         RESULTCODES proc_rc;
290         PID child_pid;
291         int active_children = 0;
292
293         /* Count number of active children */
294         for (slot=0; slot < HARD_SERVER_LIMIT; slot++) {
295             active_children += ap_scoreboard_image->parent[slot].pid != 0 &&
296                 !ap_scoreboard_image->parent[slot].quiescing;
297         }
298
299         /* Spawn children if needed */
300         for (slot=0; slot < HARD_SERVER_LIMIT && active_children < ap_daemons_to_start; slot++) {
301             if (ap_scoreboard_image->parent[slot].pid == 0) {
302                 spawn_child(slot);
303                 active_children++;
304             }
305         }
306
307         rc = DosWaitChild(DCWA_PROCESSTREE, DCWW_NOWAIT, &proc_rc, &child_pid, 0);
308
309         if (rc == 0) {
310             /* A child has terminated, remove its scoreboard entry & terminate if necessary */
311             for (slot=0; ap_scoreboard_image->parent[slot].pid != child_pid && slot < HARD_SERVER_LIMIT; slot++);
312
313             if (slot < HARD_SERVER_LIMIT) {
314                 ap_scoreboard_image->parent[slot].pid = 0;
315                 ap_scoreboard_image->parent[slot].quiescing = 0;
316
317                 if (proc_rc.codeTerminate == TC_EXIT) {
318                     /* Child terminated normally, check its exit code and
319                      * terminate server if child indicates a fatal error
320                      */
321                     if (proc_rc.codeResult == APEXIT_CHILDFATAL)
322                         break;
323                 }
324             }
325         } else if (rc == ERROR_CHILD_NOT_COMPLETE) {
326             /* No child exited, lets sleep for a while.... */
327             apr_sleep(SCOREBOARD_MAINTENANCE_INTERVAL);
328         }
329     }
330
331     /* Signal children to shut down, either gracefully or immediately */
332     for (slot=0; slot<HARD_SERVER_LIMIT; slot++) {
333       kill(ap_scoreboard_image->parent[slot].pid, is_graceful ? SIGHUP : SIGTERM);
334     }
335
336     DosFreeMem(parent_info);
337     return restart_pending;
338 }
339
340
341
342 static void spawn_child(int slot)
343 {
344     PPIB ppib;
345     PTIB ptib;
346     char fail_module[100];
347     char progname[CCHMAXPATH];
348     RESULTCODES proc_rc;
349     ULONG rc;
350
351     ap_scoreboard_image->parent[slot].generation = ap_my_generation;
352     DosGetInfoBlocks(&ptib, &ppib);
353     DosQueryModuleName(ppib->pib_hmte, sizeof(progname), progname);
354     rc = DosExecPgm(fail_module, sizeof(fail_module), EXEC_ASYNCRESULT,
355                     ppib->pib_pchcmd, NULL, &proc_rc, progname);
356
357     if (rc) {
358         ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
359                      "error spawning child, slot %d", slot);
360     }
361
362     if (ap_max_daemons_limit < slot) {
363         ap_max_daemons_limit = slot;
364     }
365
366     ap_scoreboard_image->parent[slot].pid = proc_rc.codeTerminate;
367 }
368
369
370
371 /* Signal handling routines */
372
373 static void sig_term(int sig)
374 {
375     shutdown_pending = 1;
376     signal(SIGTERM, SIG_DFL);
377 }
378
379
380
381 static void sig_restart(int sig)
382 {
383     if (sig == SIGUSR1) {
384         is_graceful = 1;
385     }
386
387     restart_pending = 1;
388 }
389
390
391
392 static void set_signals()
393 {
394     struct sigaction sa;
395
396     sigemptyset(&sa.sa_mask);
397     sa.sa_flags = 0;
398     sa.sa_handler = sig_term;
399
400     if (sigaction(SIGTERM, &sa, NULL) < 0)
401         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)");
402
403     if (sigaction(SIGINT, &sa, NULL) < 0)
404         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGINT)");
405
406     sa.sa_handler = sig_restart;
407
408     if (sigaction(SIGHUP, &sa, NULL) < 0)
409         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");
410     if (sigaction(SIGUSR1, &sa, NULL) < 0)
411         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGUSR1)");
412 }
413
414
415
416 /* Enquiry functions used get MPM status info */
417
418 AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
419 {
420     switch (query_code) {
421         case AP_MPMQ_MAX_DAEMON_USED:
422             *result = ap_max_daemons_limit;
423             return APR_SUCCESS;
424         case AP_MPMQ_IS_THREADED:
425             *result = AP_MPMQ_DYNAMIC;
426             return APR_SUCCESS;
427         case AP_MPMQ_IS_FORKED:
428             *result = AP_MPMQ_NOT_SUPPORTED;
429             return APR_SUCCESS;
430         case AP_MPMQ_HARD_LIMIT_DAEMONS:
431             *result = HARD_SERVER_LIMIT;
432             return APR_SUCCESS;
433         case AP_MPMQ_HARD_LIMIT_THREADS:
434             *result = HARD_THREAD_LIMIT;
435             return APR_SUCCESS;
436         case AP_MPMQ_MIN_SPARE_DAEMONS:
437             *result = 0;
438             return APR_SUCCESS;
439         case AP_MPMQ_MAX_SPARE_DAEMONS:
440             *result = 0;
441             return APR_SUCCESS;
442         case AP_MPMQ_MAX_REQUESTS_DAEMON:
443             *result = ap_max_requests_per_child;
444             return APR_SUCCESS; 
445     }
446     return APR_ENOTIMPL;
447
448
449
450
451 int ap_graceful_stop_signalled(void)
452 {
453     return is_graceful;
454 }
455
456
457
458 /* Configuration handling stuff */
459
460 static int mpmt_os2_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
461 {
462     one_process = ap_exists_config_define("ONE_PROCESS") || 
463                   ap_exists_config_define("DEBUG");
464     is_graceful = 0;
465     ap_listen_pre_config();
466     ap_daemons_to_start = DEFAULT_START_DAEMON;
467     ap_thread_limit = HARD_THREAD_LIMIT;
468     ap_pid_fname = DEFAULT_PIDLOG;
469     ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
470     ap_extended_status = 0;
471     ap_min_spare_threads = DEFAULT_MIN_SPARE_THREAD;
472     ap_max_spare_threads = DEFAULT_MAX_SPARE_THREAD;
473 #ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
474         ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
475 #endif
476
477     return OK;
478 }
479
480
481
482 static void mpmt_os2_hooks(apr_pool_t *p)
483 {
484     ap_hook_pre_config(mpmt_os2_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
485 }
486
487
488
489 static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy, const char *arg)
490 {
491     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
492
493     if (err != NULL) {
494         return err;
495     }
496
497     ap_daemons_to_start = atoi(arg);
498     return NULL;
499 }
500
501
502
503 static const char *set_min_spare_threads(cmd_parms *cmd, void *dummy,
504                                          const char *arg)
505 {
506     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
507
508     if (err != NULL) {
509         return err;
510     }
511
512     ap_min_spare_threads = atoi(arg);
513
514     if (ap_min_spare_threads <= 0) {
515        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
516                     "WARNING: detected MinSpareThreads set to non-positive.");
517        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
518                     "Resetting to 1 to avoid almost certain Apache failure.");
519        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
520                     "Please read the documentation.");
521        ap_min_spare_threads = 1;
522     }
523        
524     return NULL;
525 }
526
527
528
529 static const char *set_max_spare_threads(cmd_parms *cmd, void *dummy,
530                                          const char *arg)
531 {
532     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
533
534     if (err != NULL) {
535         return err;
536     }
537
538     ap_max_spare_threads = atoi(arg);
539     return NULL;
540 }
541
542
543
544 static const char *ignore_cmd(cmd_parms *cmd, void *dummy, const char *arg)
545 {
546     return NULL;
547 }
548
549
550
551 static const command_rec mpmt_os2_cmds[] = {
552 LISTEN_COMMANDS,
553 AP_INIT_TAKE1( "StartServers", set_daemons_to_start, NULL, RSRC_CONF, 
554   "Number of child processes launched at server startup" ),
555 AP_INIT_TAKE1("MinSpareThreads", set_min_spare_threads, NULL, RSRC_CONF,
556   "Minimum number of idle children, to handle request spikes"),
557 AP_INIT_TAKE1("MaxSpareThreads", set_max_spare_threads, NULL, RSRC_CONF,
558   "Maximum number of idle children"),
559 AP_INIT_TAKE1("User", ignore_cmd, NULL, RSRC_CONF,
560   "Not applicable on this platform"),
561 AP_INIT_TAKE1("Group", ignore_cmd, NULL, RSRC_CONF,
562   "Not applicable on this platform"),
563 AP_INIT_TAKE1("ScoreBoardFile", ignore_cmd, NULL, RSRC_CONF, \
564   "Not applicable on this platform"),
565 { NULL }
566 };
567
568 module AP_MODULE_DECLARE_DATA mpm_mpmt_os2_module = {
569     MPM20_MODULE_STUFF,
570     NULL,                       /* hook to run before apache parses args */
571     NULL,                       /* create per-directory config structure */
572     NULL,                       /* merge per-directory config structures */
573     NULL,                       /* create per-server config structure */
574     NULL,                       /* merge per-server config structures */
575     mpmt_os2_cmds,              /* command apr_table_t */
576     mpmt_os2_hooks,             /* register_hooks */
577 };