]> granicus.if.org Git - apache/blob - server/scoreboard.c
Merge r1741310, r1741461 from trunk:
[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 "http_protocol.h"
36 #include "ap_mpm.h"
37
38 #include "scoreboard.h"
39
40 /* we know core's module_index is 0 */
41 #undef APLOG_MODULE_INDEX
42 #define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
43
44 AP_DECLARE_DATA scoreboard *ap_scoreboard_image = NULL;
45 AP_DECLARE_DATA const char *ap_scoreboard_fname = NULL;
46 static ap_scoreboard_e scoreboard_type;
47
48 const char * ap_set_scoreboard(cmd_parms *cmd, void *dummy,
49                                const char *arg)
50 {
51     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
52     if (err != NULL) {
53         return err;
54     }
55
56     ap_scoreboard_fname = arg;
57     return NULL;
58 }
59
60 /* Default to false when mod_status is not loaded */
61 AP_DECLARE_DATA int ap_extended_status = 0;
62
63 const char *ap_set_extended_status(cmd_parms *cmd, void *dummy, int arg)
64 {
65     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
66     if (err != NULL) {
67         return err;
68     }
69     ap_extended_status = arg;
70     return NULL;
71 }
72
73 AP_DECLARE_DATA int ap_mod_status_reqtail = 0;
74
75 const char *ap_set_reqtail(cmd_parms *cmd, void *dummy, int arg)
76 {
77     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
78     if (err != NULL) {
79         return err;
80     }
81     ap_mod_status_reqtail = arg;
82     return NULL;
83 }
84
85 #if APR_HAS_SHARED_MEMORY
86
87 #include "apr_shm.h"
88
89 #ifndef WIN32
90 static /* but must be exported to mpm_winnt */
91 #endif
92         apr_shm_t *ap_scoreboard_shm = NULL;
93
94 #endif
95
96 APR_HOOK_STRUCT(
97     APR_HOOK_LINK(pre_mpm)
98 )
99
100 AP_IMPLEMENT_HOOK_RUN_ALL(int,pre_mpm,
101                           (apr_pool_t *p, ap_scoreboard_e sb_type),
102                           (p, sb_type),OK,DECLINED)
103
104 static APR_OPTIONAL_FN_TYPE(ap_logio_get_last_bytes)
105                                 *pfn_ap_logio_get_last_bytes;
106
107 struct ap_sb_handle_t {
108     int child_num;
109     int thread_num;
110 };
111
112 static int server_limit, thread_limit;
113 static apr_size_t scoreboard_size;
114
115 /*
116  * ToDo:
117  * This function should be renamed to cleanup_shared
118  * and it should handle cleaning up a scoreboard shared
119  * between processes using any form of IPC (file, shared memory
120  * segment, etc.). Leave it as is now because it is being used
121  * by various MPMs.
122  */
123 static apr_status_t ap_cleanup_shared_mem(void *d)
124 {
125 #if APR_HAS_SHARED_MEMORY
126     free(ap_scoreboard_image);
127     ap_scoreboard_image = NULL;
128     apr_shm_destroy(ap_scoreboard_shm);
129 #endif
130     return APR_SUCCESS;
131 }
132
133 #define SIZE_OF_scoreboard    APR_ALIGN_DEFAULT(sizeof(scoreboard))
134 #define SIZE_OF_global_score  APR_ALIGN_DEFAULT(sizeof(global_score))
135 #define SIZE_OF_process_score APR_ALIGN_DEFAULT(sizeof(process_score))
136 #define SIZE_OF_worker_score  APR_ALIGN_DEFAULT(sizeof(worker_score))
137
138 AP_DECLARE(int) ap_calc_scoreboard_size(void)
139 {
140     ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
141     ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);
142
143     scoreboard_size  = SIZE_OF_global_score;
144     scoreboard_size += SIZE_OF_process_score * server_limit;
145     scoreboard_size += SIZE_OF_worker_score * server_limit * thread_limit;
146
147     return scoreboard_size;
148 }
149
150 AP_DECLARE(void) ap_init_scoreboard(void *shared_score)
151 {
152     char *more_storage;
153     int i;
154
155     pfn_ap_logio_get_last_bytes = APR_RETRIEVE_OPTIONAL_FN(ap_logio_get_last_bytes);
156     if (!shared_score) {
157         return;
158     }
159     
160     ap_calc_scoreboard_size();
161     ap_scoreboard_image =
162         ap_calloc(1, SIZE_OF_scoreboard + server_limit * sizeof(worker_score *));
163     more_storage = shared_score;
164     ap_scoreboard_image->global = (global_score *)more_storage;
165     more_storage += SIZE_OF_global_score;
166     ap_scoreboard_image->parent = (process_score *)more_storage;
167     more_storage += SIZE_OF_process_score * server_limit;
168     ap_scoreboard_image->servers =
169         (worker_score **)((char*)ap_scoreboard_image + SIZE_OF_scoreboard);
170     for (i = 0; i < server_limit; i++) {
171         ap_scoreboard_image->servers[i] = (worker_score *)more_storage;
172         more_storage += thread_limit * SIZE_OF_worker_score;
173     }
174     ap_assert(more_storage == (char*)shared_score + scoreboard_size);
175     ap_scoreboard_image->global->server_limit = server_limit;
176     ap_scoreboard_image->global->thread_limit = thread_limit;
177 }
178
179 /**
180  * Create a name-based scoreboard in the given pool using the
181  * given filename.
182  */
183 static apr_status_t create_namebased_scoreboard(apr_pool_t *pool,
184                                                 const char *fname)
185 {
186 #if APR_HAS_SHARED_MEMORY
187     apr_status_t rv;
188
189     /* The shared memory file must not exist before we create the
190      * segment. */
191     apr_shm_remove(fname, pool); /* ignore errors */
192
193     rv = apr_shm_create(&ap_scoreboard_shm, scoreboard_size, fname, pool);
194     if (rv != APR_SUCCESS) {
195         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00001)
196                      "unable to create or access scoreboard \"%s\" "
197                      "(name-based shared memory failure)", fname);
198         return rv;
199     }
200 #endif /* APR_HAS_SHARED_MEMORY */
201     return APR_SUCCESS;
202 }
203
204 /* ToDo: This function should be made to handle setting up
205  * a scoreboard shared between processes using any IPC technique,
206  * not just a shared memory segment
207  */
208 static apr_status_t open_scoreboard(apr_pool_t *pconf)
209 {
210 #if APR_HAS_SHARED_MEMORY
211     apr_status_t rv;
212     char *fname = NULL;
213     apr_pool_t *global_pool;
214
215     /* We don't want to have to recreate the scoreboard after
216      * restarts, so we'll create a global pool and never clean it.
217      */
218     rv = apr_pool_create(&global_pool, NULL);
219     if (rv != APR_SUCCESS) {
220         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00002)
221                      "Fatal error: unable to create global pool "
222                      "for use by the scoreboard");
223         return rv;
224     }
225
226     /* The config says to create a name-based shmem */
227     if (ap_scoreboard_fname) {
228         /* make sure it's an absolute pathname */
229         fname = ap_server_root_relative(pconf, ap_scoreboard_fname);
230         if (!fname) {
231             ap_log_error(APLOG_MARK, APLOG_CRIT, APR_EBADPATH, ap_server_conf, APLOGNO(00003)
232                          "Fatal error: Invalid Scoreboard path %s",
233                          ap_scoreboard_fname);
234             return APR_EBADPATH;
235         }
236         return create_namebased_scoreboard(global_pool, fname);
237     }
238     else { /* config didn't specify, we get to choose shmem type */
239         rv = apr_shm_create(&ap_scoreboard_shm, scoreboard_size, NULL,
240                             global_pool); /* anonymous shared memory */
241         if ((rv != APR_SUCCESS) && (rv != APR_ENOTIMPL)) {
242             ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00004)
243                          "Unable to create or access scoreboard "
244                          "(anonymous shared memory failure)");
245             return rv;
246         }
247         /* Make up a filename and do name-based shmem */
248         else if (rv == APR_ENOTIMPL) {
249             /* Make sure it's an absolute pathname */
250             ap_scoreboard_fname = DEFAULT_SCOREBOARD;
251             fname = ap_server_root_relative(pconf, ap_scoreboard_fname);
252
253             return create_namebased_scoreboard(global_pool, fname);
254         }
255     }
256 #endif /* APR_HAS_SHARED_MEMORY */
257     return APR_SUCCESS;
258 }
259
260 /* If detach is non-zero, this is a separate child process,
261  * if zero, it is a forked child.
262  */
263 AP_DECLARE(apr_status_t) ap_reopen_scoreboard(apr_pool_t *p, apr_shm_t **shm,
264                                               int detached)
265 {
266 #if APR_HAS_SHARED_MEMORY
267     if (!detached) {
268         return APR_SUCCESS;
269     }
270     if (apr_shm_size_get(ap_scoreboard_shm) < scoreboard_size) {
271         ap_log_error(APLOG_MARK, APLOG_CRIT, 0, ap_server_conf, APLOGNO(00005)
272                      "Fatal error: shared scoreboard too small for child!");
273         apr_shm_detach(ap_scoreboard_shm);
274         ap_scoreboard_shm = NULL;
275         return APR_EINVAL;
276     }
277     /* everything will be cleared shortly */
278     if (*shm) {
279         *shm = ap_scoreboard_shm;
280     }
281 #endif
282     return APR_SUCCESS;
283 }
284
285 apr_status_t ap_cleanup_scoreboard(void *d)
286 {
287     if (ap_scoreboard_image == NULL) {
288         return APR_SUCCESS;
289     }
290     if (scoreboard_type == SB_SHARED) {
291         ap_cleanup_shared_mem(NULL);
292     }
293     else {
294         free(ap_scoreboard_image->global);
295         free(ap_scoreboard_image);
296         ap_scoreboard_image = NULL;
297     }
298     return APR_SUCCESS;
299 }
300
301 /* Create or reinit an existing scoreboard. The MPM can control whether
302  * the scoreboard is shared across multiple processes or not
303  */
304 int ap_create_scoreboard(apr_pool_t *p, ap_scoreboard_e sb_type)
305 {
306     int i;
307 #if APR_HAS_SHARED_MEMORY
308     apr_status_t rv;
309 #endif
310
311     if (ap_scoreboard_image) {
312         ap_scoreboard_image->global->restart_time = apr_time_now();
313         memset(ap_scoreboard_image->parent, 0,
314                SIZE_OF_process_score * server_limit);
315         for (i = 0; i < server_limit; i++) {
316             memset(ap_scoreboard_image->servers[i], 0,
317                    SIZE_OF_worker_score * thread_limit);
318         }
319         ap_init_scoreboard(NULL);
320         return OK;
321     }
322
323     ap_calc_scoreboard_size();
324 #if APR_HAS_SHARED_MEMORY
325     if (sb_type == SB_SHARED) {
326         void *sb_shared;
327         rv = open_scoreboard(p);
328         if (rv || !(sb_shared = apr_shm_baseaddr_get(ap_scoreboard_shm))) {
329             return HTTP_INTERNAL_SERVER_ERROR;
330         }
331         memset(sb_shared, 0, scoreboard_size);
332         ap_init_scoreboard(sb_shared);
333     }
334     else
335 #endif
336     {
337         /* A simple malloc will suffice */
338         void *sb_mem = ap_calloc(1, scoreboard_size);
339         ap_init_scoreboard(sb_mem);
340     }
341
342     scoreboard_type = sb_type;
343     ap_scoreboard_image->global->running_generation = 0;
344     ap_scoreboard_image->global->restart_time = apr_time_now();
345
346     apr_pool_cleanup_register(p, NULL, ap_cleanup_scoreboard, apr_pool_cleanup_null);
347
348     return OK;
349 }
350
351 /* Routines called to deal with the scoreboard image
352  * --- note that we do *not* need write locks, since update_child_status
353  * only updates a *single* record in place, and only one process writes to
354  * a given scoreboard slot at a time (either the child process owning that
355  * slot, or the parent, noting that the child has died).
356  *
357  * As a final note --- setting the score entry to getpid() is always safe,
358  * since when the parent is writing an entry, it's only noting SERVER_DEAD
359  * anyway.
360  */
361
362 AP_DECLARE(int) ap_exists_scoreboard_image(void)
363 {
364     return (ap_scoreboard_image ? 1 : 0);
365 }
366
367 AP_DECLARE(void) ap_increment_counts(ap_sb_handle_t *sb, request_rec *r)
368 {
369     worker_score *ws;
370     apr_off_t bytes;
371
372     if (!sb)
373         return;
374
375     ws = &ap_scoreboard_image->servers[sb->child_num][sb->thread_num];
376     if (pfn_ap_logio_get_last_bytes != NULL) {
377         bytes = pfn_ap_logio_get_last_bytes(r->connection);
378     }
379     else if (r->method_number == M_GET && r->method[0] == 'H') {
380         bytes = 0;
381     }
382     else {
383         bytes = r->bytes_sent;
384     }
385
386 #ifdef HAVE_TIMES
387     times(&ws->times);
388 #endif
389     ws->access_count++;
390     ws->my_access_count++;
391     ws->conn_count++;
392     ws->bytes_served += bytes;
393     ws->my_bytes_served += bytes;
394     ws->conn_bytes += bytes;
395 }
396
397 AP_DECLARE(int) ap_find_child_by_pid(apr_proc_t *pid)
398 {
399     int i;
400     int max_daemons_limit = 0;
401
402     ap_mpm_query(AP_MPMQ_MAX_DAEMONS, &max_daemons_limit);
403
404     for (i = 0; i < max_daemons_limit; ++i) {
405         if (ap_scoreboard_image->parent[i].pid == pid->pid) {
406             return i;
407         }
408     }
409
410     return -1;
411 }
412
413 AP_DECLARE(void) ap_create_sb_handle(ap_sb_handle_t **new_sbh, apr_pool_t *p,
414                                      int child_num, int thread_num)
415 {
416     *new_sbh = (ap_sb_handle_t *)apr_palloc(p, sizeof(ap_sb_handle_t));
417     (*new_sbh)->child_num = child_num;
418     (*new_sbh)->thread_num = thread_num;
419 }
420
421 static void copy_request(char *rbuf, apr_size_t rbuflen, request_rec *r)
422 {
423     char *p;
424
425     if (r->the_request == NULL) {
426         apr_cpystrn(rbuf, "NULL", rbuflen);
427         return; /* short circuit below */
428     }
429
430     if (r->parsed_uri.password == NULL) {
431         p = r->the_request;
432     }
433     else {
434         /* Don't reveal the password in the server-status view */
435         p = apr_pstrcat(r->pool, r->method, " ",
436                         apr_uri_unparse(r->pool, &r->parsed_uri,
437                         APR_URI_UNP_OMITPASSWORD),
438                         r->assbackwards ? NULL : " ", r->protocol, NULL);
439     }
440
441     /* now figure out if we copy over the 1st rbuflen chars or the last */
442     if (!ap_mod_status_reqtail) {
443         apr_cpystrn(rbuf, p, rbuflen);
444     }
445     else {
446         apr_size_t slen = strlen(p);
447         if (slen < rbuflen) {
448             /* it all fits anyway */
449             apr_cpystrn(rbuf, p, rbuflen);
450         }
451         else {
452             apr_cpystrn(rbuf, p+(slen-rbuflen+1), rbuflen);
453         }
454     }
455 }
456
457 static int update_child_status_internal(int child_num,
458                                         int thread_num,
459                                         int status,
460                                         conn_rec *c,
461                                         server_rec *s,
462                                         request_rec *r,
463                                         const char *descr)
464 {
465     int old_status;
466     worker_score *ws;
467     int mpm_generation;
468
469     ws = &ap_scoreboard_image->servers[child_num][thread_num];
470     old_status = ws->status;
471     ws->status = status;
472     
473     if (status == SERVER_READY
474         && old_status == SERVER_STARTING) {
475         process_score *ps = &ap_scoreboard_image->parent[child_num];
476         ws->thread_num = child_num * thread_limit + thread_num;
477         ap_mpm_query(AP_MPMQ_GENERATION, &mpm_generation);
478         ps->generation = mpm_generation;
479     }
480
481     if (ap_extended_status) {
482         const char *val;
483         
484         if (status == SERVER_READY || status == SERVER_DEAD) {
485             /*
486              * Reset individual counters
487              */
488             if (status == SERVER_DEAD) {
489                 ws->my_access_count = 0L;
490                 ws->my_bytes_served = 0L;
491             }
492             ws->conn_count = 0;
493             ws->conn_bytes = 0;
494             ws->last_used = apr_time_now();
495         }
496
497         if (descr) {
498             apr_cpystrn(ws->request, descr, sizeof(ws->request));
499         }
500         else if (r) {
501             copy_request(ws->request, sizeof(ws->request), r);
502         }
503         else if (c) {
504             ws->request[0]='\0';
505         }
506
507         if (r && r->useragent_ip) {
508             if (!(val = ap_get_useragent_host(r, REMOTE_NOLOOKUP, NULL)))
509                 apr_cpystrn(ws->client, r->useragent_ip, sizeof(ws->client));
510             else
511                 apr_cpystrn(ws->client, val, sizeof(ws->client));
512         }
513         else if (c) {
514             if (!(val = ap_get_remote_host(c, c->base_server->lookup_defaults,
515                                            REMOTE_NOLOOKUP, NULL)))
516                 apr_cpystrn(ws->client, c->client_ip, sizeof(ws->client));
517             else
518                 apr_cpystrn(ws->client, val, sizeof(ws->client));
519         }
520
521         if (s) {
522             if (c) {
523                 apr_snprintf(ws->vhost, sizeof(ws->vhost), "%s:%d",
524                              s->server_hostname, c->local_addr->port);
525             }
526             else {
527                 apr_cpystrn(ws->vhost, s->server_hostname, sizeof(ws->vhost));
528             }
529         }
530         else if (c) {
531             ws->vhost[0]='\0';
532         }
533
534         if (c) {
535             val = ap_get_protocol(c);
536             apr_cpystrn(ws->protocol, val, sizeof(ws->protocol));
537         }
538     }
539
540     return old_status;
541 }
542
543 AP_DECLARE(int) ap_update_child_status_from_indexes(int child_num,
544                                                     int thread_num,
545                                                     int status,
546                                                     request_rec *r)
547 {
548     if (child_num < 0) {
549         return -1;
550     }
551
552     return update_child_status_internal(child_num, thread_num, status,
553                                         r ? r->connection : NULL,
554                                         r ? r->server : NULL,
555                                         r, NULL);
556 }
557
558 AP_DECLARE(int) ap_update_child_status(ap_sb_handle_t *sbh, int status,
559                                       request_rec *r)
560 {
561     if (!sbh || (sbh->child_num < 0))
562         return -1;
563
564     return update_child_status_internal(sbh->child_num, sbh->thread_num,
565                                         status,
566                                         r ? r->connection : NULL,
567                                         r ? r->server : NULL,
568                                         r, NULL);
569 }
570
571 AP_DECLARE(int) ap_update_child_status_from_conn(ap_sb_handle_t *sbh, int status,
572                                                  conn_rec *c)
573 {
574     if (!sbh || (sbh->child_num < 0))
575         return -1;
576
577     return update_child_status_internal(sbh->child_num, sbh->thread_num,
578                                         status, c, NULL, NULL, NULL);
579 }
580
581 AP_DECLARE(int) ap_update_child_status_from_server(ap_sb_handle_t *sbh, int status, 
582                                                    conn_rec *c, server_rec *s)
583 {
584     if (!sbh || (sbh->child_num < 0))
585         return -1;
586
587     return update_child_status_internal(sbh->child_num, sbh->thread_num,
588                                         status, c, s, NULL, NULL);
589 }
590
591 AP_DECLARE(int) ap_update_child_status_descr(ap_sb_handle_t *sbh, int status, const char *descr)
592 {
593     if (!sbh || (sbh->child_num < 0))
594         return -1;
595
596     return update_child_status_internal(sbh->child_num, sbh->thread_num,
597                                         status, NULL, NULL, NULL, descr);
598 }
599
600 AP_DECLARE(void) ap_time_process_request(ap_sb_handle_t *sbh, int status)
601 {
602     worker_score *ws;
603
604     if (!sbh)
605         return;
606
607     if (sbh->child_num < 0) {
608         return;
609     }
610
611     ws = &ap_scoreboard_image->servers[sbh->child_num][sbh->thread_num];
612
613     if (status == START_PREQUEST) {
614         ws->start_time = ws->last_used = apr_time_now();
615     }
616     else if (status == STOP_PREQUEST) {
617         ws->stop_time = ws->last_used = apr_time_now();
618     }
619 }
620
621 AP_DECLARE(worker_score *) ap_get_scoreboard_worker_from_indexes(int x, int y)
622 {
623     if (((x < 0) || (x >= server_limit)) ||
624         ((y < 0) || (y >= thread_limit))) {
625         return(NULL); /* Out of range */
626     }
627     return &ap_scoreboard_image->servers[x][y];
628 }
629
630 AP_DECLARE(worker_score *) ap_get_scoreboard_worker(ap_sb_handle_t *sbh)
631 {
632     if (!sbh)
633         return NULL;
634
635     return ap_get_scoreboard_worker_from_indexes(sbh->child_num,
636                                                  sbh->thread_num);
637 }
638
639 AP_DECLARE(void) ap_copy_scoreboard_worker(worker_score *dest, 
640                                            int child_num,
641                                            int thread_num)
642 {
643     worker_score *ws = ap_get_scoreboard_worker_from_indexes(child_num, thread_num);
644
645     memcpy(dest, ws, sizeof *ws);
646
647     /* For extra safety, NUL-terminate the strings returned, though it
648      * should be true those last bytes are always zero anyway. */
649     dest->client[sizeof(dest->client) - 1] = '\0';
650     dest->request[sizeof(dest->request) - 1] = '\0';
651     dest->vhost[sizeof(dest->vhost) - 1] = '\0';
652     dest->protocol[sizeof(dest->protocol) - 1] = '\0';
653 }
654
655 AP_DECLARE(process_score *) ap_get_scoreboard_process(int x)
656 {
657     if ((x < 0) || (x >= server_limit)) {
658         return(NULL); /* Out of range */
659     }
660     return &ap_scoreboard_image->parent[x];
661 }
662
663 AP_DECLARE(global_score *) ap_get_scoreboard_global()
664 {
665     return ap_scoreboard_image->global;
666 }