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