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