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