]> granicus.if.org Git - apache/blob - server/mpm/mpmt_os2/mpmt_os2_child.c
Add lots of unique tags to error log messages
[apache] / server / mpm / mpmt_os2 / mpmt_os2_child.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 #define INCL_NOPMAPI
18 #define INCL_DOS
19 #define INCL_DOSERRORS
20
21 #include "ap_config.h"
22 #include "httpd.h"
23 #include "mpm_default.h"
24 #include "http_main.h"
25 #include "http_log.h"
26 #include "http_config.h"
27 #include "http_core.h"  /* for get_remote_host */
28 #include "http_connection.h"
29 #include "scoreboard.h"
30 #include "ap_mpm.h"
31 #include "ap_listen.h"
32 #include "apr_portable.h"
33 #include "apr_poll.h"
34 #include "mpm_common.h"
35 #include "apr_strings.h"
36 #include <os2.h>
37 #include <process.h>
38
39 APLOG_USE_MODULE(mpm_mpmt_os2);
40
41 /* XXXXXX move these to header file private to this MPM */
42
43 /* We don't need many processes,
44  * they're only for redundancy in the event of a crash
45  */
46 #define HARD_SERVER_LIMIT 10
47
48 /* Limit on the total number of threads per process
49  */
50 #ifndef HARD_THREAD_LIMIT
51 #define HARD_THREAD_LIMIT 256
52 #endif
53
54 #define ID_FROM_CHILD_THREAD(c, t)    ((c * HARD_THREAD_LIMIT) + t)
55
56 typedef struct {
57     apr_pool_t *pconn;
58     apr_socket_t *conn_sd;
59 } worker_args_t;
60
61 #define WORKTYPE_CONN 0
62 #define WORKTYPE_EXIT 1
63
64 static apr_pool_t *pchild = NULL;
65 static int child_slot;
66 static int shutdown_pending = 0;
67 extern int ap_my_generation;
68 static int volatile is_graceful = 1;
69 HEV shutdown_event; /* signaled when this child is shutting down */
70
71 /* grab some MPM globals */
72 extern int ap_min_spare_threads;
73 extern int ap_max_spare_threads;
74 extern HMTX ap_mpm_accept_mutex;
75
76 static void worker_main(void *vpArg);
77 static void clean_child_exit(int code);
78 static void set_signals();
79 static void server_maintenance(void *vpArg);
80
81
82 static void clean_child_exit(int code)
83 {
84     if (pchild) {
85         apr_pool_destroy(pchild);
86     }
87
88     exit(code);
89 }
90
91
92
93 void ap_mpm_child_main(apr_pool_t *pconf)
94 {
95     ap_listen_rec *lr = NULL;
96     int requests_this_child = 0;
97     int rv = 0;
98     unsigned long ulTimes;
99     int my_pid = getpid();
100     ULONG rc, c;
101     HQUEUE workq;
102     apr_pollset_t *pollset;
103     int num_listeners;
104     TID server_maint_tid;
105     void *sb_mem;
106
107     /* Stop Ctrl-C/Ctrl-Break signals going to child processes */
108     DosSetSignalExceptionFocus(0, &ulTimes);
109     set_signals();
110
111     /* Create pool for child */
112     apr_pool_create(&pchild, pconf);
113
114     ap_run_child_init(pchild, ap_server_conf);
115
116     /* Create an event semaphore used to trigger other threads to shutdown */
117     rc = DosCreateEventSem(NULL, &shutdown_event, 0, FALSE);
118
119     if (rc) {
120         ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00189)
121                      "unable to create shutdown semaphore, exiting");
122         clean_child_exit(APEXIT_CHILDFATAL);
123     }
124
125     /* Gain access to the scoreboard. */
126     rc = DosGetNamedSharedMem(&sb_mem, ap_scoreboard_fname,
127                               PAG_READ|PAG_WRITE);
128
129     if (rc) {
130         ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00190)
131                      "scoreboard not readable in child, exiting");
132         clean_child_exit(APEXIT_CHILDFATAL);
133     }
134
135     ap_calc_scoreboard_size();
136     ap_init_scoreboard(sb_mem);
137
138     /* Gain access to the accpet mutex */
139     rc = DosOpenMutexSem(NULL, &ap_mpm_accept_mutex);
140
141     if (rc) {
142         ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00191)
143                      "accept mutex couldn't be accessed in child, exiting");
144         clean_child_exit(APEXIT_CHILDFATAL);
145     }
146
147     /* Find our pid in the scoreboard so we know what slot our parent allocated us */
148     for (child_slot = 0; ap_scoreboard_image->parent[child_slot].pid != my_pid && child_slot < HARD_SERVER_LIMIT; child_slot++);
149
150     if (child_slot == HARD_SERVER_LIMIT) {
151         ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00192)
152                      "child pid not found in scoreboard, exiting");
153         clean_child_exit(APEXIT_CHILDFATAL);
154     }
155
156     ap_my_generation = ap_scoreboard_image->parent[child_slot].generation;
157     memset(ap_scoreboard_image->servers[child_slot], 0, sizeof(worker_score) * HARD_THREAD_LIMIT);
158
159     /* Set up an OS/2 queue for passing connections & termination requests
160      * to worker threads
161      */
162     rc = DosCreateQueue(&workq, QUE_FIFO, apr_psprintf(pchild, "/queues/httpd/work.%d", my_pid));
163
164     if (rc) {
165         ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00193)
166                      "unable to create work queue, exiting");
167         clean_child_exit(APEXIT_CHILDFATAL);
168     }
169
170     /* Create initial pool of worker threads */
171     for (c = 0; c < ap_min_spare_threads; c++) {
172 //        ap_scoreboard_image->servers[child_slot][c].tid = _beginthread(worker_main, NULL, 128*1024, (void *)c);
173     }
174
175     /* Start maintenance thread */
176     server_maint_tid = _beginthread(server_maintenance, NULL, 32768, NULL);
177
178     /* Set up poll */
179     for (num_listeners = 0, lr = ap_listeners; lr; lr = lr->next) {
180         num_listeners++;
181     }
182
183     apr_pollset_create(&pollset, num_listeners, pchild, 0);
184
185     for (lr = ap_listeners; lr != NULL; lr = lr->next) {
186         apr_pollfd_t pfd = { 0 };
187
188         pfd.desc_type = APR_POLL_SOCKET;
189         pfd.desc.s = lr->sd;
190         pfd.reqevents = APR_POLLIN;
191         pfd.client_data = lr;
192         apr_pollset_add(pollset, &pfd);
193     }
194
195     /* Main connection accept loop */
196     do {
197         apr_pool_t *pconn;
198         worker_args_t *worker_args;
199         int last_poll_idx = 0;
200
201         apr_pool_create(&pconn, pchild);
202         worker_args = apr_palloc(pconn, sizeof(worker_args_t));
203         worker_args->pconn = pconn;
204
205         if (num_listeners == 1) {
206             rv = apr_socket_accept(&worker_args->conn_sd, ap_listeners->sd, pconn);
207         } else {
208             const apr_pollfd_t *poll_results;
209             apr_int32_t num_poll_results;
210
211             rc = DosRequestMutexSem(ap_mpm_accept_mutex, SEM_INDEFINITE_WAIT);
212
213             if (shutdown_pending) {
214                 DosReleaseMutexSem(ap_mpm_accept_mutex);
215                 break;
216             }
217
218             rv = APR_FROM_OS_ERROR(rc);
219
220             if (rv == APR_SUCCESS) {
221                 rv = apr_pollset_poll(pollset, -1, &num_poll_results, &poll_results);
222                 DosReleaseMutexSem(ap_mpm_accept_mutex);
223             }
224
225             if (rv == APR_SUCCESS) {
226                 if (last_poll_idx >= num_listeners) {
227                     last_poll_idx = 0;
228                 }
229
230                 lr = poll_results[last_poll_idx++].client_data;
231                 rv = apr_socket_accept(&worker_args->conn_sd, lr->sd, pconn);
232                 last_poll_idx++;
233             }
234         }
235
236         if (rv != APR_SUCCESS) {
237             if (!APR_STATUS_IS_EINTR(rv)) {
238                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(00194)
239                              "apr_socket_accept");
240                 clean_child_exit(APEXIT_CHILDFATAL);
241             }
242         } else {
243             DosWriteQueue(workq, WORKTYPE_CONN, sizeof(worker_args_t), worker_args, 0);
244             requests_this_child++;
245         }
246
247         if (ap_max_requests_per_child != 0 && requests_this_child >= ap_max_requests_per_child)
248             break;
249     } while (!shutdown_pending && ap_my_generation == ap_scoreboard_image->global->running_generation);
250
251     ap_scoreboard_image->parent[child_slot].quiescing = 1;
252     DosPostEventSem(shutdown_event);
253     DosWaitThread(&server_maint_tid, DCWW_WAIT);
254
255     if (is_graceful) {
256         char someleft;
257
258         /* tell our worker threads to exit */
259         for (c=0; c<HARD_THREAD_LIMIT; c++) {
260             if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
261                 DosWriteQueue(workq, WORKTYPE_EXIT, 0, NULL, 0);
262             }
263         }
264
265         do {
266             someleft = 0;
267
268             for (c=0; c<HARD_THREAD_LIMIT; c++) {
269                 if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
270                     someleft = 1;
271                     DosSleep(1000);
272                     break;
273                 }
274             }
275         } while (someleft);
276     } else {
277         DosPurgeQueue(workq);
278
279         for (c=0; c<HARD_THREAD_LIMIT; c++) {
280             if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
281                 DosKillThread(ap_scoreboard_image->servers[child_slot][c].tid);
282             }
283         }
284     }
285
286     apr_pool_destroy(pchild);
287 }
288
289
290
291 void add_worker()
292 {
293     int thread_slot;
294     int stacksize = ap_thread_stacksize == 0 ? 128*1024 : ap_thread_stacksize;
295
296     /* Find a free thread slot */
297     for (thread_slot=0; thread_slot < HARD_THREAD_LIMIT; thread_slot++) {
298         if (ap_scoreboard_image->servers[child_slot][thread_slot].status == SERVER_DEAD) {
299             ap_scoreboard_image->servers[child_slot][thread_slot].status = SERVER_STARTING;
300             ap_scoreboard_image->servers[child_slot][thread_slot].tid =
301                 _beginthread(worker_main, NULL, stacksize, (void *)thread_slot);
302             break;
303         }
304     }
305 }
306
307
308
309 ULONG APIENTRY thread_exception_handler(EXCEPTIONREPORTRECORD *pReportRec,
310                                         EXCEPTIONREGISTRATIONRECORD *pRegRec,
311                                         CONTEXTRECORD *pContext,
312                                         PVOID p)
313 {
314     int c;
315
316     if (pReportRec->fHandlerFlags & EH_NESTED_CALL) {
317         return XCPT_CONTINUE_SEARCH;
318     }
319
320     if (pReportRec->ExceptionNum == XCPT_ACCESS_VIOLATION ||
321         pReportRec->ExceptionNum == XCPT_INTEGER_DIVIDE_BY_ZERO) {
322         ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00195)
323                      "caught exception in worker thread, initiating child shutdown pid=%d", getpid());
324         for (c=0; c<HARD_THREAD_LIMIT; c++) {
325             if (ap_scoreboard_image->servers[child_slot][c].tid == _gettid()) {
326                 ap_scoreboard_image->servers[child_slot][c].status = SERVER_DEAD;
327                 break;
328             }
329         }
330
331         /* Shut down process ASAP, it could be quite unhealthy & leaking resources */
332         shutdown_pending = 1;
333         ap_scoreboard_image->parent[child_slot].quiescing = 1;
334         kill(getpid(), SIGHUP);
335         DosUnwindException(UNWIND_ALL, 0, 0);
336     }
337
338     return XCPT_CONTINUE_SEARCH;
339 }
340
341
342
343 static void worker_main(void *vpArg)
344 {
345     apr_thread_t *thd = NULL;
346     apr_os_thread_t osthd;
347     long conn_id;
348     conn_rec *current_conn;
349     apr_pool_t *pconn;
350     apr_allocator_t *allocator;
351     apr_bucket_alloc_t *bucket_alloc;
352     worker_args_t *worker_args;
353     HQUEUE workq;
354     PID owner;
355     int rc;
356     REQUESTDATA rd;
357     ULONG len;
358     BYTE priority;
359     int thread_slot = (int)vpArg;
360     EXCEPTIONREGISTRATIONRECORD reg_rec = { NULL, thread_exception_handler };
361     ap_sb_handle_t *sbh;
362
363     /* Trap exceptions in this thread so we don't take down the whole process */
364     DosSetExceptionHandler( &reg_rec );
365
366     osthd = apr_os_thread_current();
367     apr_os_thread_put(&thd, &osthd, pchild);
368
369     rc = DosOpenQueue(&owner, &workq,
370                       apr_psprintf(pchild, "/queues/httpd/work.%d", getpid()));
371
372     if (rc) {
373         ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00196)
374                      "unable to open work queue, exiting");
375         ap_scoreboard_image->servers[child_slot][thread_slot].tid = 0;
376     }
377
378     conn_id = ID_FROM_CHILD_THREAD(child_slot, thread_slot);
379     ap_update_child_status_from_indexes(child_slot, thread_slot, SERVER_READY,
380                                         NULL);
381
382     apr_allocator_create(&allocator);
383     apr_allocator_max_free_set(allocator, ap_max_mem_free);
384     bucket_alloc = apr_bucket_alloc_create_ex(allocator);
385
386     while (rc = DosReadQueue(workq, &rd, &len, (PPVOID)&worker_args, 0, DCWW_WAIT, &priority, NULLHANDLE),
387            rc == 0 && rd.ulData != WORKTYPE_EXIT) {
388         pconn = worker_args->pconn;
389         ap_create_sb_handle(&sbh, pconn, child_slot, thread_slot);
390         current_conn = ap_run_create_connection(pconn, ap_server_conf,
391                                                 worker_args->conn_sd, conn_id,
392                                                 sbh, bucket_alloc);
393
394         if (current_conn) {
395             current_conn->current_thread = thd;
396             ap_process_connection(current_conn, worker_args->conn_sd);
397             ap_lingering_close(current_conn);
398         }
399
400         apr_pool_destroy(pconn);
401         ap_update_child_status_from_indexes(child_slot, thread_slot,
402                                             SERVER_READY, NULL);
403     }
404
405     ap_update_child_status_from_indexes(child_slot, thread_slot, SERVER_DEAD,
406                                         NULL);
407
408     apr_bucket_alloc_destroy(bucket_alloc);
409     apr_allocator_destroy(allocator);
410 }
411
412
413
414 static void server_maintenance(void *vpArg)
415 {
416     int num_idle, num_needed;
417     ULONG num_pending = 0;
418     int threadnum;
419     HQUEUE workq;
420     ULONG rc;
421     PID owner;
422
423     rc = DosOpenQueue(&owner, &workq,
424                       apr_psprintf(pchild, "/queues/httpd/work.%d", getpid()));
425
426     if (rc) {
427         ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00197)
428                      "unable to open work queue in maintenance thread");
429         return;
430     }
431
432     do {
433         for (num_idle=0, threadnum=0; threadnum < HARD_THREAD_LIMIT; threadnum++) {
434             num_idle += ap_scoreboard_image->servers[child_slot][threadnum].status == SERVER_READY;
435         }
436
437         DosQueryQueue(workq, &num_pending);
438         num_needed = ap_min_spare_threads - num_idle + num_pending;
439
440         if (num_needed > 0) {
441             for (threadnum=0; threadnum < num_needed; threadnum++) {
442                 add_worker();
443             }
444         }
445
446         if (num_idle - num_pending > ap_max_spare_threads) {
447             DosWriteQueue(workq, WORKTYPE_EXIT, 0, NULL, 0);
448         }
449     } while (DosWaitEventSem(shutdown_event, 500) == ERROR_TIMEOUT);
450 }
451
452
453
454 /* Signal handling routines */
455
456 static void sig_term(int sig)
457 {
458     shutdown_pending = 1;
459     is_graceful = 0;
460     signal(SIGTERM, SIG_DFL);
461 }
462
463
464
465 static void sig_hup(int sig)
466 {
467     shutdown_pending = 1;
468     is_graceful = 1;
469 }
470
471
472
473 static void set_signals()
474 {
475     struct sigaction sa;
476
477     sigemptyset(&sa.sa_mask);
478     sa.sa_flags = 0;
479     sa.sa_handler = sig_term;
480
481     if (sigaction(SIGTERM, &sa, NULL) < 0)
482         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00198) "sigaction(SIGTERM)");
483
484     sa.sa_handler = sig_hup;
485
486     if (sigaction(SIGHUP, &sa, NULL) < 0)
487         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00199) "sigaction(SIGHUP)");
488 }