]> granicus.if.org Git - apache/blob - server/scoreboard.c
394824c5027a0b6dcb2accb5fc5023d0a3088e44
[apache] / server / scoreboard.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2002 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * originally written at the National Center for Supercomputing Applications,
56  * University of Illinois, Urbana-Champaign.
57  */
58
59 #include "apr.h"
60 #include "apr_strings.h"
61 #include "apr_portable.h"
62 #include "apr_lib.h"
63
64 #define APR_WANT_STRFUNC
65 #include "apr_want.h"
66
67 #if APR_HAVE_SYS_TYPES_H
68 #include <sys/types.h>
69 #endif
70
71 #include "ap_config.h"
72 #include "httpd.h"
73 #include "http_log.h"
74 #include "http_main.h"
75 #include "http_core.h"
76 #include "http_config.h"
77 #include "ap_mpm.h"
78
79 #include "mpm.h"
80 #include "scoreboard.h"
81
82 AP_DECLARE_DATA scoreboard *ap_scoreboard_image = NULL;
83 AP_DECLARE_DATA const char *ap_scoreboard_fname = NULL;
84 AP_DECLARE_DATA int ap_extended_status = 0;
85
86 #if APR_HAS_SHARED_MEMORY
87
88 #include "apr_shm.h"
89
90 #ifndef WIN32
91 static /* but must be exported to mpm_winnt */
92 #endif
93         apr_shm_t *ap_scoreboard_shm = NULL;
94
95 #endif
96
97 APR_HOOK_STRUCT(
98     APR_HOOK_LINK(pre_mpm)
99 )
100  
101 AP_IMPLEMENT_HOOK_RUN_ALL(int,pre_mpm,
102                           (apr_pool_t *p, ap_scoreboard_e sb_type),
103                           (p, sb_type),OK,DECLINED)
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     scoreboard_size = sizeof(global_score);
136     scoreboard_size += sizeof(process_score) * server_limit;
137     scoreboard_size += sizeof(worker_score) * server_limit * thread_limit;
138     return scoreboard_size;
139 }
140
141 void ap_init_scoreboard(void *shared_score)
142 {
143     char *more_storage;
144     int i;
145     
146     ap_calc_scoreboard_size();
147     ap_scoreboard_image = 
148         calloc(1, sizeof(scoreboard) + server_limit * sizeof(worker_score *));
149     more_storage = shared_score;
150     ap_scoreboard_image->global = (global_score *)more_storage;
151     more_storage += sizeof(global_score);
152     ap_scoreboard_image->parent = (process_score *)more_storage;
153     more_storage += sizeof(process_score) * server_limit;
154     ap_scoreboard_image->servers = 
155         (worker_score **)((char*)ap_scoreboard_image + sizeof(scoreboard));
156     for (i = 0; i < server_limit; i++) {
157         ap_scoreboard_image->servers[i] = (worker_score *)more_storage;
158         more_storage += thread_limit * sizeof(worker_score);
159     }
160     ap_assert(more_storage == (char*)shared_score + scoreboard_size);
161     ap_scoreboard_image->global->server_limit = server_limit;
162     ap_scoreboard_image->global->thread_limit = thread_limit;
163 }
164
165 /**
166  * Create a name-based scoreboard in the given pool using the
167  * given filename.
168  */
169 static apr_status_t create_namebased_scoreboard(apr_pool_t *pool,
170                                                 const char *fname)
171 {
172 #if APR_HAS_SHARED_MEMORY
173     apr_status_t rv;
174
175     /* The shared memory file must not exist before we create the
176      * segment. */
177     apr_file_remove(fname, pool); /* ignore errors */
178
179     rv = apr_shm_create(&ap_scoreboard_shm, scoreboard_size, fname, pool);
180     if (rv != APR_SUCCESS) {
181         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
182                      "unable to create scoreboard "
183                      "(name-based shared memory failure)");
184         return rv;
185     }
186 #endif /* APR_HAS_SHARED_MEMORY */
187     return APR_SUCCESS;
188 }
189
190 /* ToDo: This function should be made to handle setting up 
191  * a scoreboard shared between processes using any IPC technique, 
192  * not just a shared memory segment
193  */
194 static apr_status_t open_scoreboard(apr_pool_t *pconf)
195 {
196 #if APR_HAS_SHARED_MEMORY
197     apr_status_t rv;
198     char *fname = NULL;
199     apr_pool_t *global_pool;
200
201     /* We don't want to have to recreate the scoreboard after
202      * restarts, so we'll create a global pool and never clean it.
203      */
204     rv = apr_pool_create(&global_pool, NULL);
205     if (rv != APR_SUCCESS) {
206         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
207                      "Fatal error: unable to create global pool "
208                      "for use with by the scoreboard");
209         return rv;
210     }
211
212     /* The config says to create a name-based shmem */
213     if (ap_scoreboard_fname) {
214         /* make sure it's an absolute pathname */
215         fname = ap_server_root_relative(pconf, ap_scoreboard_fname);
216         if (!fname) {
217             ap_log_error(APLOG_MARK, APLOG_CRIT, APR_EBADPATH, NULL,
218                          "Fatal error: Invalid Scoreboard path %s",
219                          ap_scoreboard_fname);
220             return APR_EBADPATH;
221         }
222         return create_namebased_scoreboard(global_pool, fname);
223     }
224     else { /* config didn't specify, we get to choose shmem type */
225         rv = apr_shm_create(&ap_scoreboard_shm, scoreboard_size, NULL,
226                             global_pool); /* anonymous shared memory */
227         if ((rv != APR_SUCCESS) && (rv != APR_ENOTIMPL)) {
228             ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
229                          "Unable to create scoreboard "
230                          "(anonymous shared memory failure)");
231             return rv;
232         }
233         /* Make up a filename and do name-based shmem */
234         else if (rv == APR_ENOTIMPL) {
235             /* Make sure it's an absolute pathname */
236             ap_scoreboard_fname = DEFAULT_SCOREBOARD;
237             fname = ap_server_root_relative(pconf, ap_scoreboard_fname);
238
239             return create_namebased_scoreboard(global_pool, fname);
240         }
241     }
242 #endif /* APR_HAS_SHARED_MEMORY */
243     return APR_SUCCESS;
244 }
245
246 /* If detach is non-zero, this is a seperate child process,
247  * if zero, it is a forked child.
248  */
249 apr_status_t ap_reopen_scoreboard(apr_pool_t *p, apr_shm_t **shm, int detached)
250 {
251 #if APR_HAS_SHARED_MEMORY
252     if (!detached) {
253         return APR_SUCCESS;
254     }
255     if (apr_shm_size_get(ap_scoreboard_shm) < scoreboard_size) {
256         ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL,
257                      "Fatal error: shared scoreboard too small for child!");
258         apr_shm_detach(ap_scoreboard_shm);
259         ap_scoreboard_shm = NULL;
260         return APR_EINVAL;
261     }
262     /* everything will be cleared shortly */
263     if (*shm) {
264         *shm = ap_scoreboard_shm;
265     }
266 #endif
267     return APR_SUCCESS;
268 }
269
270 apr_status_t ap_cleanup_scoreboard(void *d)
271 {
272     if (ap_scoreboard_image == NULL) {
273         return APR_SUCCESS;
274     }
275     if (ap_scoreboard_image->global->sb_type == SB_SHARED) {
276         ap_cleanup_shared_mem(NULL);
277     }
278     else {
279         free(ap_scoreboard_image->global);
280         free(ap_scoreboard_image);
281         ap_scoreboard_image = NULL;
282     }
283     return APR_SUCCESS;
284 }
285
286 /* Create or reinit an existing scoreboard. The MPM can control whether
287  * the scoreboard is shared across multiple processes or not
288  */
289 int ap_create_scoreboard(apr_pool_t *p, ap_scoreboard_e sb_type)
290 {
291     int running_gen = 0;
292     int i;
293 #if APR_HAS_SHARED_MEMORY
294     apr_status_t rv;
295 #endif
296
297     if (ap_scoreboard_image) {
298         running_gen = ap_scoreboard_image->global->running_generation;
299         ap_scoreboard_image->global->restart_time = apr_time_now();
300         memset(ap_scoreboard_image->parent, 0, 
301                sizeof(process_score) * server_limit);
302         for (i = 0; i < server_limit; i++) {
303             memset(ap_scoreboard_image->servers[i], 0,
304                    sizeof(worker_score) * thread_limit);
305         }
306         return OK;
307     }
308
309     ap_calc_scoreboard_size();
310 #if APR_HAS_SHARED_MEMORY
311     if (sb_type == SB_SHARED) {
312         void *sb_shared;
313         rv = open_scoreboard(p);
314         if (rv || !(sb_shared = apr_shm_baseaddr_get(ap_scoreboard_shm))) {
315             return HTTP_INTERNAL_SERVER_ERROR;
316         }
317         memset(sb_shared, 0, scoreboard_size);
318         ap_init_scoreboard(sb_shared);
319     }
320     else 
321 #endif
322     {
323         /* A simple malloc will suffice */
324         void *sb_mem = calloc(1, scoreboard_size);
325         if (sb_mem == NULL) {
326             ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL,
327                          "(%d)%s: cannot allocate scoreboard",
328                          errno, strerror(errno));
329             return HTTP_INTERNAL_SERVER_ERROR;
330         }
331         ap_init_scoreboard(sb_mem);
332     }
333
334     ap_scoreboard_image->global->sb_type = sb_type;
335     ap_scoreboard_image->global->running_generation = running_gen;
336     ap_scoreboard_image->global->restart_time = apr_time_now();
337
338     apr_pool_cleanup_register(p, NULL, ap_cleanup_scoreboard, apr_pool_cleanup_null);
339
340     return OK;
341 }
342
343 /* Routines called to deal with the scoreboard image
344  * --- note that we do *not* need write locks, since update_child_status
345  * only updates a *single* record in place, and only one process writes to
346  * a given scoreboard slot at a time (either the child process owning that
347  * slot, or the parent, noting that the child has died).
348  *
349  * As a final note --- setting the score entry to getpid() is always safe,
350  * since when the parent is writing an entry, it's only noting SERVER_DEAD
351  * anyway.
352  */
353
354 AP_DECLARE(int) ap_exists_scoreboard_image(void)
355 {
356     return (ap_scoreboard_image ? 1 : 0);
357 }
358
359 AP_DECLARE(void) ap_increment_counts(ap_sb_handle_t *sb, request_rec *r)
360 {
361     worker_score *ws;
362
363     ws = &ap_scoreboard_image->servers[sb->child_num][sb->thread_num];
364
365 #ifdef HAVE_TIMES
366     times(&ws->times);
367 #endif
368     ws->access_count++;
369     ws->my_access_count++;
370     ws->conn_count++;
371     ws->bytes_served += r->bytes_sent;
372     ws->my_bytes_served += r->bytes_sent;
373     ws->conn_bytes += r->bytes_sent;
374 }
375
376 AP_DECLARE(int) find_child_by_pid(apr_proc_t *pid)
377 {
378     int i;
379     int max_daemons_limit;
380
381     ap_mpm_query(AP_MPMQ_MAX_DAEMONS, &max_daemons_limit);
382
383     for (i = 0; i < max_daemons_limit; ++i) {
384         if (ap_scoreboard_image->parent[i].pid == pid->pid) {
385             return i;
386         }
387     }
388
389     return -1;
390 }
391
392 AP_DECLARE(void) ap_create_sb_handle(ap_sb_handle_t **new_sbh, apr_pool_t *p,
393                                      int child_num, int thread_num)
394 {
395     *new_sbh = (ap_sb_handle_t *)apr_palloc(p, sizeof(ap_sb_handle_t));
396     (*new_sbh)->child_num = child_num;
397     (*new_sbh)->thread_num = thread_num;
398 }
399
400 AP_DECLARE(int) ap_update_child_status_from_indexes(int child_num,
401                                                     int thread_num,
402                                                     int status,
403                                                     request_rec *r)
404 {
405     int old_status;
406     worker_score *ws;
407     process_score *ps;
408
409     if (child_num < 0) {
410         return -1;
411     }
412
413     ws = &ap_scoreboard_image->servers[child_num][thread_num];
414     old_status = ws->status;
415     ws->status = status;
416
417     ps = &ap_scoreboard_image->parent[child_num];
418     
419     if (status == SERVER_READY
420         && old_status == SERVER_STARTING) {
421         ws->thread_num = child_num * thread_limit + thread_num;
422         ps->generation = ap_my_generation;
423     }
424
425     if (ap_extended_status) {
426         ws->last_used = apr_time_now();
427         if (status == SERVER_READY || status == SERVER_DEAD) {
428             /*
429              * Reset individual counters
430              */
431             if (status == SERVER_DEAD) {
432                 ws->my_access_count = 0L;
433                 ws->my_bytes_served = 0L;
434             }
435             ws->conn_count = 0;
436             ws->conn_bytes = 0;
437         }
438         if (r) {
439             conn_rec *c = r->connection;
440             apr_cpystrn(ws->client, ap_get_remote_host(c, r->per_dir_config,
441                         REMOTE_NOLOOKUP, NULL), sizeof(ws->client));
442             if (r->the_request == NULL) {
443                 apr_cpystrn(ws->request, "NULL", sizeof(ws->request));
444             } else if (r->parsed_uri.password == NULL) {
445                 apr_cpystrn(ws->request, r->the_request, sizeof(ws->request));
446             } else {
447                 /* Don't reveal the password in the server-status view */
448                 apr_cpystrn(ws->request, apr_pstrcat(r->pool, r->method, " ",
449                             apr_uri_unparse(r->pool, &r->parsed_uri,
450                             APR_URI_UNP_OMITPASSWORD),
451                             r->assbackwards ? NULL : " ", r->protocol, NULL),
452                             sizeof(ws->request));
453             }
454             apr_cpystrn(ws->vhost, r->server->server_hostname,
455                         sizeof(ws->vhost));
456         }
457     }
458     
459     return old_status;
460 }
461
462 AP_DECLARE(int) ap_update_child_status(ap_sb_handle_t *sbh, int status,
463                                       request_rec *r)
464 {
465     return ap_update_child_status_from_indexes(sbh->child_num, sbh->thread_num,
466                                                status, r);
467 }
468
469 void ap_time_process_request(int child_num, int thread_num, int status)
470 {
471     worker_score *ws;
472
473     if (child_num < 0) {
474         return;
475     }
476
477     ws = &ap_scoreboard_image->servers[child_num][thread_num];
478
479     if (status == START_PREQUEST) {
480         ws->start_time = apr_time_now(); 
481     }
482     else if (status == STOP_PREQUEST) {
483         ws->stop_time = apr_time_now(); 
484     }
485 }
486
487 AP_DECLARE(worker_score *) ap_get_scoreboard_worker(int x, int y)
488 {
489     if (((x < 0) || (server_limit < x)) ||
490         ((y < 0) || (thread_limit < y))) {
491         return(NULL); /* Out of range */
492     }
493     return &ap_scoreboard_image->servers[x][y];
494 }
495
496 AP_DECLARE(process_score *) ap_get_scoreboard_process(int x)
497 {
498     if ((x < 0) || (server_limit < x)) {
499         return(NULL); /* Out of range */
500     }
501     return &ap_scoreboard_image->parent[x];
502 }
503
504 AP_DECLARE(global_score *) ap_get_scoreboard_global()
505 {
506     return ap_scoreboard_image->global;
507 }