1 /* Copyright 2001-2006 The Apache Software Foundation or its licensors, as
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "apr_strings.h"
19 #include "apr_portable.h"
22 #define APR_WANT_STRFUNC
25 #if APR_HAVE_SYS_TYPES_H
26 #include <sys/types.h>
29 #include "ap_config.h"
32 #include "http_main.h"
33 #include "http_core.h"
34 #include "http_config.h"
38 #include "scoreboard.h"
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;
44 #if APR_HAS_SHARED_MEMORY
49 static /* but must be exported to mpm_winnt */
51 apr_shm_t *ap_scoreboard_shm = NULL;
56 APR_HOOK_LINK(pre_mpm)
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)
63 static APR_OPTIONAL_FN_TYPE(ap_proxy_lb_workers)
66 struct ap_sb_handle_t {
71 static int server_limit, thread_limit, lb_limit;
72 static apr_size_t scoreboard_size;
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
82 static apr_status_t ap_cleanup_shared_mem(void *d)
84 #if APR_HAS_SHARED_MEMORY
85 free(ap_scoreboard_image);
86 ap_scoreboard_image = NULL;
87 apr_shm_destroy(ap_scoreboard_shm);
92 AP_DECLARE(int) ap_calc_scoreboard_size(void)
94 ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
95 ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);
97 if (!proxy_lb_workers)
98 proxy_lb_workers = APR_RETRIEVE_OPTIONAL_FN(ap_proxy_lb_workers);
100 lb_limit = proxy_lb_workers();
104 scoreboard_size = sizeof(global_score);
105 scoreboard_size += sizeof(process_score) * server_limit;
106 scoreboard_size += sizeof(worker_score) * server_limit * thread_limit;
108 scoreboard_size += sizeof(lb_score) * lb_limit;
110 return scoreboard_size;
113 void ap_init_scoreboard(void *shared_score)
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);
134 ap_scoreboard_image->balancers = (lb_score *)more_storage;
135 more_storage += lb_limit * sizeof(lb_score);
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;
144 * Create a name-based scoreboard in the given pool using the
147 static apr_status_t create_namebased_scoreboard(apr_pool_t *pool,
150 #if APR_HAS_SHARED_MEMORY
153 /* The shared memory file must not exist before we create the
155 apr_shm_remove(fname, pool); /* ignore errors */
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);
164 #endif /* APR_HAS_SHARED_MEMORY */
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
172 static apr_status_t open_scoreboard(apr_pool_t *pconf)
174 #if APR_HAS_SHARED_MEMORY
177 apr_pool_t *global_pool;
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.
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");
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);
195 ap_log_error(APLOG_MARK, APLOG_CRIT, APR_EBADPATH, NULL,
196 "Fatal error: Invalid Scoreboard path %s",
197 ap_scoreboard_fname);
200 return create_namebased_scoreboard(global_pool, fname);
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)");
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);
217 return create_namebased_scoreboard(global_pool, fname);
220 #endif /* APR_HAS_SHARED_MEMORY */
224 /* If detach is non-zero, this is a seperate child process,
225 * if zero, it is a forked child.
227 apr_status_t ap_reopen_scoreboard(apr_pool_t *p, apr_shm_t **shm, int detached)
229 #if APR_HAS_SHARED_MEMORY
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;
240 /* everything will be cleared shortly */
242 *shm = ap_scoreboard_shm;
248 apr_status_t ap_cleanup_scoreboard(void *d)
250 if (ap_scoreboard_image == NULL) {
253 if (ap_scoreboard_image->global->sb_type == SB_SHARED) {
254 ap_cleanup_shared_mem(NULL);
257 free(ap_scoreboard_image->global);
258 free(ap_scoreboard_image);
259 ap_scoreboard_image = NULL;
264 /* Create or reinit an existing scoreboard. The MPM can control whether
265 * the scoreboard is shared across multiple processes or not
267 int ap_create_scoreboard(apr_pool_t *p, ap_scoreboard_e sb_type)
271 #if APR_HAS_SHARED_MEMORY
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);
284 /* Clean up the lb workers data */
286 memset(ap_scoreboard_image->balancers, 0,
287 sizeof(lb_score) * lb_limit);
292 ap_calc_scoreboard_size();
293 #if APR_HAS_SHARED_MEMORY
294 if (sb_type == 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;
300 memset(sb_shared, 0, scoreboard_size);
301 ap_init_scoreboard(sb_shared);
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;
314 ap_init_scoreboard(sb_mem);
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();
321 apr_pool_cleanup_register(p, NULL, ap_cleanup_scoreboard, apr_pool_cleanup_null);
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).
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
337 AP_DECLARE(int) ap_exists_scoreboard_image(void)
339 return (ap_scoreboard_image ? 1 : 0);
342 AP_DECLARE(void) ap_increment_counts(ap_sb_handle_t *sb, request_rec *r)
346 ws = &ap_scoreboard_image->servers[sb->child_num][sb->thread_num];
352 ws->my_access_count++;
354 ws->bytes_served += r->bytes_sent;
355 ws->my_bytes_served += r->bytes_sent;
356 ws->conn_bytes += r->bytes_sent;
359 int find_child_by_pid(apr_proc_t *pid)
362 int max_daemons_limit;
364 ap_mpm_query(AP_MPMQ_MAX_DAEMONS, &max_daemons_limit);
366 for (i = 0; i < max_daemons_limit; ++i) {
367 if (ap_scoreboard_image->parent[i].pid == pid->pid) {
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)
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;
383 AP_DECLARE(int) ap_update_child_status_from_indexes(int child_num,
396 ws = &ap_scoreboard_image->servers[child_num][thread_num];
397 old_status = ws->status;
400 ps = &ap_scoreboard_image->parent[child_num];
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;
408 if (ap_extended_status) {
409 ws->last_used = apr_time_now();
410 if (status == SERVER_READY || status == SERVER_DEAD) {
412 * Reset individual counters
414 if (status == SERVER_DEAD) {
415 ws->my_access_count = 0L;
416 ws->my_bytes_served = 0L;
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));
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));
437 apr_cpystrn(ws->vhost, r->server->server_hostname,
445 AP_DECLARE(int) ap_update_child_status(ap_sb_handle_t *sbh, int status,
448 return ap_update_child_status_from_indexes(sbh->child_num, sbh->thread_num,
452 void ap_time_process_request(ap_sb_handle_t *sbh, int status)
456 if (sbh->child_num < 0) {
460 ws = &ap_scoreboard_image->servers[sbh->child_num][sbh->thread_num];
462 if (status == START_PREQUEST) {
463 ws->start_time = apr_time_now();
465 else if (status == STOP_PREQUEST) {
466 ws->stop_time = apr_time_now();
470 AP_DECLARE(worker_score *) ap_get_scoreboard_worker(int x, int y)
472 if (((x < 0) || (server_limit < x)) ||
473 ((y < 0) || (thread_limit < y))) {
474 return(NULL); /* Out of range */
476 return &ap_scoreboard_image->servers[x][y];
479 AP_DECLARE(process_score *) ap_get_scoreboard_process(int x)
481 if ((x < 0) || (server_limit < x)) {
482 return(NULL); /* Out of range */
484 return &ap_scoreboard_image->parent[x];
487 AP_DECLARE(global_score *) ap_get_scoreboard_global()
489 return ap_scoreboard_image->global;
492 AP_DECLARE(lb_score *) ap_get_scoreboard_lb(int lb_num)
494 if (((lb_num < 0) || (lb_limit < lb_num))) {
495 return(NULL); /* Out of range */
497 return &ap_scoreboard_image->balancers[lb_num];