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