]> granicus.if.org Git - apache/blob - server/scoreboard.c
enabled building gen_test_char for running on build when cross-compiling;
[apache] / server / scoreboard.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 #include "apr.h"
18 #include "apr_strings.h"
19 #include "apr_portable.h"
20 #include "apr_lib.h"
21
22 #define APR_WANT_STRFUNC
23 #include "apr_want.h"
24
25 #if APR_HAVE_SYS_TYPES_H
26 #include <sys/types.h>
27 #endif
28
29 #include "ap_config.h"
30 #include "httpd.h"
31 #include "http_log.h"
32 #include "http_main.h"
33 #include "http_core.h"
34 #include "http_config.h"
35 #include "ap_mpm.h"
36
37 #include "scoreboard.h"
38
39 AP_DECLARE_DATA scoreboard *ap_scoreboard_image = NULL;
40 AP_DECLARE_DATA const char *ap_scoreboard_fname = NULL;
41 AP_DECLARE_DATA int ap_extended_status = 0;
42 AP_DECLARE_DATA int ap_mod_status_reqtail = 0;
43
44 #if APR_HAS_SHARED_MEMORY
45
46 #include "apr_shm.h"
47
48 #ifndef WIN32
49 static /* but must be exported to mpm_winnt */
50 #endif
51         apr_shm_t *ap_scoreboard_shm = NULL;
52
53 #endif
54
55 APR_HOOK_STRUCT(
56     APR_HOOK_LINK(pre_mpm)
57 )
58
59 AP_IMPLEMENT_HOOK_RUN_ALL(int,pre_mpm,
60                           (apr_pool_t *p, ap_scoreboard_e sb_type),
61                           (p, sb_type),OK,DECLINED)
62
63 static APR_OPTIONAL_FN_TYPE(ap_proxy_lb_workers)
64                                 *pfn_proxy_lb_workers;
65 static APR_OPTIONAL_FN_TYPE(ap_proxy_lb_worker_size)
66                                 *pfn_proxy_lb_worker_size;
67
68 struct ap_sb_handle_t {
69     int child_num;
70     int thread_num;
71 };
72
73 static int server_limit, thread_limit, lb_limit, lb_size;
74 static apr_size_t scoreboard_size;
75
76 /*
77  * ToDo:
78  * This function should be renamed to cleanup_shared
79  * and it should handle cleaning up a scoreboard shared
80  * between processes using any form of IPC (file, shared memory
81  * segment, etc.). Leave it as is now because it is being used
82  * by various MPMs.
83  */
84 static apr_status_t ap_cleanup_shared_mem(void *d)
85 {
86 #if APR_HAS_SHARED_MEMORY
87     free(ap_scoreboard_image);
88     ap_scoreboard_image = NULL;
89     apr_shm_destroy(ap_scoreboard_shm);
90 #endif
91     return APR_SUCCESS;
92 }
93
94 AP_DECLARE(int) ap_calc_scoreboard_size(void)
95 {
96     ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
97     ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);
98
99     if (!pfn_proxy_lb_workers)
100         pfn_proxy_lb_workers = APR_RETRIEVE_OPTIONAL_FN(ap_proxy_lb_workers);
101     if (pfn_proxy_lb_workers)
102         lb_limit = pfn_proxy_lb_workers();
103     else
104         lb_limit = 0;
105
106     if (!pfn_proxy_lb_worker_size)
107         pfn_proxy_lb_worker_size = APR_RETRIEVE_OPTIONAL_FN(ap_proxy_lb_worker_size);
108     if (pfn_proxy_lb_worker_size)
109         lb_size = pfn_proxy_lb_worker_size();
110     else
111         lb_size = sizeof(lb_score);
112
113     scoreboard_size = sizeof(global_score);
114     scoreboard_size += sizeof(process_score) * server_limit;
115     scoreboard_size += sizeof(worker_score) * server_limit * thread_limit;
116     if (lb_limit && lb_size)
117         scoreboard_size += lb_size * lb_limit;
118
119     return scoreboard_size;
120 }
121
122 void ap_init_scoreboard(void *shared_score)
123 {
124     char *more_storage;
125     int i;
126
127     ap_calc_scoreboard_size();
128     ap_scoreboard_image =
129         calloc(1, sizeof(scoreboard) + server_limit * sizeof(worker_score *));
130     more_storage = shared_score;
131     ap_scoreboard_image->global = (global_score *)more_storage;
132     more_storage += sizeof(global_score);
133     ap_scoreboard_image->parent = (process_score *)more_storage;
134     more_storage += sizeof(process_score) * server_limit;
135     ap_scoreboard_image->servers =
136         (worker_score **)((char*)ap_scoreboard_image + sizeof(scoreboard));
137     for (i = 0; i < server_limit; i++) {
138         ap_scoreboard_image->servers[i] = (worker_score *)more_storage;
139         more_storage += thread_limit * sizeof(worker_score);
140     }
141     if (lb_limit && lb_size) {
142         ap_scoreboard_image->balancers = (void *)more_storage;
143         more_storage += lb_limit * lb_size;
144     }
145     ap_assert(more_storage == (char*)shared_score + scoreboard_size);
146     ap_scoreboard_image->global->server_limit = server_limit;
147     ap_scoreboard_image->global->thread_limit = thread_limit;
148     ap_scoreboard_image->global->lb_limit     = lb_limit;
149 }
150
151 /**
152  * Create a name-based scoreboard in the given pool using the
153  * given filename.
154  */
155 static apr_status_t create_namebased_scoreboard(apr_pool_t *pool,
156                                                 const char *fname)
157 {
158 #if APR_HAS_SHARED_MEMORY
159     apr_status_t rv;
160
161     /* The shared memory file must not exist before we create the
162      * segment. */
163     apr_shm_remove(fname, pool); /* ignore errors */
164
165     rv = apr_shm_create(&ap_scoreboard_shm, scoreboard_size, fname, pool);
166     if (rv != APR_SUCCESS) {
167         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
168                      "unable to create or access scoreboard \"%s\" "
169                      "(name-based shared memory failure)", fname);
170         return rv;
171     }
172 #endif /* APR_HAS_SHARED_MEMORY */
173     return APR_SUCCESS;
174 }
175
176 /* ToDo: This function should be made to handle setting up
177  * a scoreboard shared between processes using any IPC technique,
178  * not just a shared memory segment
179  */
180 static apr_status_t open_scoreboard(apr_pool_t *pconf)
181 {
182 #if APR_HAS_SHARED_MEMORY
183     apr_status_t rv;
184     char *fname = NULL;
185     apr_pool_t *global_pool;
186
187     /* We don't want to have to recreate the scoreboard after
188      * restarts, so we'll create a global pool and never clean it.
189      */
190     rv = apr_pool_create(&global_pool, NULL);
191     if (rv != APR_SUCCESS) {
192         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
193                      "Fatal error: unable to create global pool "
194                      "for use by the scoreboard");
195         return rv;
196     }
197
198     /* The config says to create a name-based shmem */
199     if (ap_scoreboard_fname) {
200         /* make sure it's an absolute pathname */
201         fname = ap_server_root_relative(pconf, ap_scoreboard_fname);
202         if (!fname) {
203             ap_log_error(APLOG_MARK, APLOG_CRIT, APR_EBADPATH, NULL,
204                          "Fatal error: Invalid Scoreboard path %s",
205                          ap_scoreboard_fname);
206             return APR_EBADPATH;
207         }
208         return create_namebased_scoreboard(global_pool, fname);
209     }
210     else { /* config didn't specify, we get to choose shmem type */
211         rv = apr_shm_create(&ap_scoreboard_shm, scoreboard_size, NULL,
212                             global_pool); /* anonymous shared memory */
213         if ((rv != APR_SUCCESS) && (rv != APR_ENOTIMPL)) {
214             ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
215                          "Unable to create or access scoreboard "
216                          "(anonymous shared memory failure)");
217             return rv;
218         }
219         /* Make up a filename and do name-based shmem */
220         else if (rv == APR_ENOTIMPL) {
221             /* Make sure it's an absolute pathname */
222             ap_scoreboard_fname = DEFAULT_SCOREBOARD;
223             fname = ap_server_root_relative(pconf, ap_scoreboard_fname);
224
225             return create_namebased_scoreboard(global_pool, fname);
226         }
227     }
228 #endif /* APR_HAS_SHARED_MEMORY */
229     return APR_SUCCESS;
230 }
231
232 /* If detach is non-zero, this is a seperate child process,
233  * if zero, it is a forked child.
234  */
235 apr_status_t ap_reopen_scoreboard(apr_pool_t *p, apr_shm_t **shm, int detached)
236 {
237 #if APR_HAS_SHARED_MEMORY
238     if (!detached) {
239         return APR_SUCCESS;
240     }
241     if (apr_shm_size_get(ap_scoreboard_shm) < scoreboard_size) {
242         ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL,
243                      "Fatal error: shared scoreboard too small for child!");
244         apr_shm_detach(ap_scoreboard_shm);
245         ap_scoreboard_shm = NULL;
246         return APR_EINVAL;
247     }
248     /* everything will be cleared shortly */
249     if (*shm) {
250         *shm = ap_scoreboard_shm;
251     }
252 #endif
253     return APR_SUCCESS;
254 }
255
256 apr_status_t ap_cleanup_scoreboard(void *d)
257 {
258     if (ap_scoreboard_image == NULL) {
259         return APR_SUCCESS;
260     }
261     if (ap_scoreboard_image->global->sb_type == SB_SHARED) {
262         ap_cleanup_shared_mem(NULL);
263     }
264     else {
265         free(ap_scoreboard_image->global);
266         free(ap_scoreboard_image);
267         ap_scoreboard_image = NULL;
268     }
269     return APR_SUCCESS;
270 }
271
272 /* Create or reinit an existing scoreboard. The MPM can control whether
273  * the scoreboard is shared across multiple processes or not
274  */
275 int ap_create_scoreboard(apr_pool_t *p, ap_scoreboard_e sb_type)
276 {
277     int running_gen = 0;
278     int i;
279 #if APR_HAS_SHARED_MEMORY
280     apr_status_t rv;
281 #endif
282
283     if (ap_scoreboard_image) {
284         running_gen = ap_scoreboard_image->global->running_generation;
285         ap_scoreboard_image->global->restart_time = apr_time_now();
286         memset(ap_scoreboard_image->parent, 0,
287                sizeof(process_score) * server_limit);
288         for (i = 0; i < server_limit; i++) {
289             memset(ap_scoreboard_image->servers[i], 0,
290                    sizeof(worker_score) * thread_limit);
291         }
292         /* Clean up the lb workers data */
293         if (lb_limit && lb_size) {
294             memset(ap_scoreboard_image->balancers, 0,
295                    lb_size * lb_limit);
296         }
297         return OK;
298     }
299
300     ap_calc_scoreboard_size();
301 #if APR_HAS_SHARED_MEMORY
302     if (sb_type == SB_SHARED) {
303         void *sb_shared;
304         rv = open_scoreboard(p);
305         if (rv || !(sb_shared = apr_shm_baseaddr_get(ap_scoreboard_shm))) {
306             return HTTP_INTERNAL_SERVER_ERROR;
307         }
308         memset(sb_shared, 0, scoreboard_size);
309         ap_init_scoreboard(sb_shared);
310     }
311     else
312 #endif
313     {
314         /* A simple malloc will suffice */
315         void *sb_mem = calloc(1, scoreboard_size);
316         if (sb_mem == NULL) {
317             ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL,
318                          "(%d)%s: cannot allocate scoreboard",
319                          errno, strerror(errno));
320             return HTTP_INTERNAL_SERVER_ERROR;
321         }
322         ap_init_scoreboard(sb_mem);
323     }
324
325     ap_scoreboard_image->global->sb_type = sb_type;
326     ap_scoreboard_image->global->running_generation = running_gen;
327     ap_scoreboard_image->global->restart_time = apr_time_now();
328
329     apr_pool_cleanup_register(p, NULL, ap_cleanup_scoreboard, apr_pool_cleanup_null);
330
331     return OK;
332 }
333
334 /* Routines called to deal with the scoreboard image
335  * --- note that we do *not* need write locks, since update_child_status
336  * only updates a *single* record in place, and only one process writes to
337  * a given scoreboard slot at a time (either the child process owning that
338  * slot, or the parent, noting that the child has died).
339  *
340  * As a final note --- setting the score entry to getpid() is always safe,
341  * since when the parent is writing an entry, it's only noting SERVER_DEAD
342  * anyway.
343  */
344
345 AP_DECLARE(int) ap_exists_scoreboard_image(void)
346 {
347     return (ap_scoreboard_image ? 1 : 0);
348 }
349
350 AP_DECLARE(void) ap_increment_counts(ap_sb_handle_t *sb, request_rec *r)
351 {
352     worker_score *ws;
353
354     if (!sb)
355         return;
356
357     ws = &ap_scoreboard_image->servers[sb->child_num][sb->thread_num];
358
359 #ifdef HAVE_TIMES
360     times(&ws->times);
361 #endif
362     ws->access_count++;
363     ws->my_access_count++;
364     ws->conn_count++;
365     ws->bytes_served += r->bytes_sent;
366     ws->my_bytes_served += r->bytes_sent;
367     ws->conn_bytes += r->bytes_sent;
368 }
369
370 AP_DECLARE(int) ap_find_child_by_pid(apr_proc_t *pid)
371 {
372     int i;
373     int max_daemons_limit;
374
375     ap_mpm_query(AP_MPMQ_MAX_DAEMONS, &max_daemons_limit);
376
377     for (i = 0; i < max_daemons_limit; ++i) {
378         if (ap_scoreboard_image->parent[i].pid == pid->pid) {
379             return i;
380         }
381     }
382
383     return -1;
384 }
385
386 AP_DECLARE(void) ap_create_sb_handle(ap_sb_handle_t **new_sbh, apr_pool_t *p,
387                                      int child_num, int thread_num)
388 {
389     *new_sbh = (ap_sb_handle_t *)apr_palloc(p, sizeof(ap_sb_handle_t));
390     (*new_sbh)->child_num = child_num;
391     (*new_sbh)->thread_num = thread_num;
392 }
393
394 static void copy_request(char *rbuf, apr_size_t rbuflen, request_rec *r)
395 {
396     char *p;
397
398     if (r->the_request == NULL) {
399         apr_cpystrn(rbuf, "NULL", rbuflen);
400         return; /* short circuit below */
401     }
402
403     if (r->parsed_uri.password == NULL) {
404         p = r->the_request;
405     }
406     else {
407         /* Don't reveal the password in the server-status view */
408         p = apr_pstrcat(r->pool, r->method, " ",
409                         apr_uri_unparse(r->pool, &r->parsed_uri,
410                         APR_URI_UNP_OMITPASSWORD),
411                         r->assbackwards ? NULL : " ", r->protocol, NULL);
412     }
413
414     /* now figure out if we copy over the 1st rbuflen chars or the last */
415     if (!ap_mod_status_reqtail) {
416         apr_cpystrn(rbuf, p, rbuflen);
417     }
418     else {
419         apr_size_t slen = strlen(p);
420         if (slen < rbuflen) {
421             /* it all fits anyway */
422             apr_cpystrn(rbuf, p, rbuflen);
423         }
424         else {
425             apr_cpystrn(rbuf, p+(slen-rbuflen+1), rbuflen);
426         }
427     }
428 }
429
430 AP_DECLARE(int) ap_update_child_status_from_indexes(int child_num,
431                                                     int thread_num,
432                                                     int status,
433                                                     request_rec *r)
434 {
435     int old_status;
436     worker_score *ws;
437     process_score *ps;
438     int mpm_generation;
439
440     if (child_num < 0) {
441         return -1;
442     }
443
444     ws = &ap_scoreboard_image->servers[child_num][thread_num];
445     old_status = ws->status;
446     ws->status = status;
447
448     ps = &ap_scoreboard_image->parent[child_num];
449
450     if (status == SERVER_READY
451         && old_status == SERVER_STARTING) {
452         ws->thread_num = child_num * thread_limit + thread_num;
453         ap_mpm_query(AP_MPMQ_GENERATION, &mpm_generation);
454         ps->generation = mpm_generation;
455     }
456
457     if (ap_extended_status) {
458         ws->last_used = apr_time_now();
459         if (status == SERVER_READY || status == SERVER_DEAD) {
460             /*
461              * Reset individual counters
462              */
463             if (status == SERVER_DEAD) {
464                 ws->my_access_count = 0L;
465                 ws->my_bytes_served = 0L;
466             }
467             ws->conn_count = 0;
468             ws->conn_bytes = 0;
469         }
470         if (r) {
471             conn_rec *c = r->connection;
472             apr_cpystrn(ws->client, ap_get_remote_host(c, r->per_dir_config,
473                         REMOTE_NOLOOKUP, NULL), sizeof(ws->client));
474             copy_request(ws->request, sizeof(ws->request), r);
475             apr_cpystrn(ws->vhost, r->server->server_hostname,
476                         sizeof(ws->vhost));
477         }
478     }
479
480     return old_status;
481 }
482
483 AP_DECLARE(int) ap_update_child_status(ap_sb_handle_t *sbh, int status,
484                                       request_rec *r)
485 {
486     if (!sbh)
487         return -1;
488
489     return ap_update_child_status_from_indexes(sbh->child_num, sbh->thread_num,
490                                                status, r);
491 }
492
493 AP_DECLARE(void) ap_time_process_request(ap_sb_handle_t *sbh, int status)
494 {
495     worker_score *ws;
496
497     if (!sbh)
498         return;
499
500     if (sbh->child_num < 0) {
501         return;
502     }
503
504     ws = &ap_scoreboard_image->servers[sbh->child_num][sbh->thread_num];
505
506     if (status == START_PREQUEST) {
507         ws->start_time = apr_time_now();
508     }
509     else if (status == STOP_PREQUEST) {
510         ws->stop_time = apr_time_now();
511     }
512 }
513
514 AP_DECLARE(worker_score *) ap_get_scoreboard_worker_from_indexes(int x, int y)
515 {
516     if (((x < 0) || (x >= server_limit)) ||
517         ((y < 0) || (y >= thread_limit))) {
518         return(NULL); /* Out of range */
519     }
520     return &ap_scoreboard_image->servers[x][y];
521 }
522
523 AP_DECLARE(worker_score *) ap_get_scoreboard_worker(ap_sb_handle_t *sbh)
524 {
525     if (!sbh)
526         return NULL;
527
528     return ap_get_scoreboard_worker_from_indexes(sbh->child_num,
529                                                  sbh->thread_num);
530 }
531
532 AP_DECLARE(process_score *) ap_get_scoreboard_process(int x)
533 {
534     if ((x < 0) || (x >= server_limit)) {
535         return(NULL); /* Out of range */
536     }
537     return &ap_scoreboard_image->parent[x];
538 }
539
540 AP_DECLARE(global_score *) ap_get_scoreboard_global()
541 {
542     return ap_scoreboard_image->global;
543 }
544
545 AP_DECLARE(lb_score *) ap_get_scoreboard_lb(int lb_num)
546 {
547     if ( (lb_num < 0) || (lb_limit < lb_num) || (lb_size==0) ) {
548         return(NULL); /* Out of range */
549     }
550     return (lb_score *) ( ((char *) ap_scoreboard_image->balancers) +
551                           (lb_num*lb_size) );
552 }