]> granicus.if.org Git - apache/blob - server/scoreboard.c
This fixes a bug in mod_status on Windows where restart time was not
[apache] / server / scoreboard.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2001 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 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 }
162
163 /**
164  * Create a name-based scoreboard in the given pool using the
165  * given filename.
166  */
167 static apr_status_t create_namebased_scoreboard(apr_pool_t *pool,
168                                                 const char *fname)
169 {
170 #if APR_HAS_SHARED_MEMORY
171     apr_status_t rv;
172
173     /* The shared memory file must not exist before we create the
174      * segment. */
175     apr_file_remove(fname, pool); /* ignore errors */
176
177     rv = apr_shm_create(&ap_scoreboard_shm, scoreboard_size, fname, pool);
178     if (rv != APR_SUCCESS) {
179         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
180                      "unable to create scoreboard "
181                      "(name-based shared memory failure)");
182         return rv;
183     }
184 #endif /* APR_HAS_SHARED_MEMORY */
185     return APR_SUCCESS;
186 }
187
188 /* ToDo: This function should be made to handle setting up 
189  * a scoreboard shared between processes using any IPC technique, 
190  * not just a shared memory segment
191  */
192 static apr_status_t open_scoreboard(apr_pool_t *pconf)
193 {
194 #if APR_HAS_SHARED_MEMORY
195     apr_status_t rv;
196     char *fname = NULL;
197     apr_pool_t *global_pool;
198
199     /* We don't want to have to recreate the scoreboard after
200      * restarts, so we'll create a global pool and never clean it.
201      */
202     rv = apr_pool_create(&global_pool, NULL);
203     if (rv != APR_SUCCESS) {
204         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
205                      "Fatal error: unable to create global pool "
206                      "for use with by the scoreboard");
207         return rv;
208     }
209
210     /* The config says to create a name-based shmem */
211     if (ap_scoreboard_fname) {
212         /* make sure it's an absolute pathname */
213         fname = ap_server_root_relative(pconf, ap_scoreboard_fname);
214
215         return create_namebased_scoreboard(global_pool, fname);
216     }
217     else { /* config didn't specify, we get to choose shmem type */
218         rv = apr_shm_create(&ap_scoreboard_shm, scoreboard_size, NULL,
219                             global_pool); /* anonymous shared memory */
220         if ((rv != APR_SUCCESS) && (rv != APR_ENOTIMPL)) {
221             ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
222                          "Unable to create scoreboard "
223                          "(anonymous shared memory failure)");
224             return rv;
225         }
226         /* Make up a filename and do name-based shmem */
227         else if (rv == APR_ENOTIMPL) {
228             /* Make sure it's an absolute pathname */
229             ap_scoreboard_fname = DEFAULT_SCOREBOARD;
230             fname = ap_server_root_relative(pconf, ap_scoreboard_fname);
231
232             return create_namebased_scoreboard(global_pool, fname);
233         }
234     }
235 #endif /* APR_HAS_SHARED_MEMORY */
236     return APR_SUCCESS;
237 }
238
239 /* If detach is non-zero, this is a seperate child process,
240  * if zero, it is a forked child.
241  */
242 apr_status_t ap_reopen_scoreboard(apr_pool_t *p, apr_shm_t **shm, int detached)
243 {
244 #if APR_HAS_SHARED_MEMORY
245     if (!detached) {
246         return APR_SUCCESS;
247     }
248     if (apr_shm_size_get(ap_scoreboard_shm) < scoreboard_size) {
249         ap_log_error(APLOG_MARK, APLOG_CRIT | APLOG_NOERRNO, 0, NULL,
250                      "Fatal error: shared scoreboard too small for child!");
251         apr_shm_detach(ap_scoreboard_shm);
252         ap_scoreboard_shm = NULL;
253         return APR_EINVAL;
254     }
255     /* everything will be cleared shortly */
256     if (*shm) {
257         *shm = ap_scoreboard_shm;
258     }
259 #endif
260     return APR_SUCCESS;
261 }
262
263 apr_status_t ap_cleanup_scoreboard(void *d)
264 {
265     if (ap_scoreboard_image == NULL) {
266         return APR_SUCCESS;
267     }
268     if (ap_scoreboard_image->global->sb_type == SB_SHARED) {
269         ap_cleanup_shared_mem(NULL);
270     }
271     else {
272         free(ap_scoreboard_image->global);
273         free(ap_scoreboard_image);
274         ap_scoreboard_image = NULL;
275     }
276     return APR_SUCCESS;
277 }
278
279 /* Create or reinit an existing scoreboard. The MPM can control whether
280  * the scoreboard is shared across multiple processes or not
281  */
282 int ap_create_scoreboard(apr_pool_t *p, ap_scoreboard_e sb_type)
283 {
284     int running_gen = 0;
285 #if APR_HAS_SHARED_MEMORY
286     apr_status_t rv;
287 #endif
288
289     if (ap_scoreboard_image) {
290         running_gen = ap_scoreboard_image->global->running_generation;
291     }
292
293     if (ap_scoreboard_image == NULL) {
294         ap_calc_scoreboard_size();
295 #if APR_HAS_SHARED_MEMORY
296         if (sb_type == SB_SHARED) {
297             void *sb_shared;
298             rv = open_scoreboard(p);
299             if (rv || !(sb_shared = apr_shm_baseaddr_get(ap_scoreboard_shm))) {
300                 return HTTP_INTERNAL_SERVER_ERROR;
301             }
302             memset(sb_shared, 0, scoreboard_size);
303             ap_init_scoreboard(sb_shared);
304         }
305         else 
306 #endif
307         {
308             /* A simple malloc will suffice */
309             void *sb_mem = calloc(1, scoreboard_size);
310             if (sb_mem == NULL) {
311                 ap_log_error(APLOG_MARK, APLOG_CRIT | APLOG_NOERRNO, 0, NULL,
312                              "(%d)%s: cannot allocate scoreboard",
313                              errno, strerror(errno));
314                 return HTTP_INTERNAL_SERVER_ERROR;
315             }
316             ap_init_scoreboard(sb_mem);
317         }
318     }
319     ap_scoreboard_image->global->sb_type = sb_type;
320     ap_scoreboard_image->global->running_generation = running_gen;
321     ap_scoreboard_image->global->restart_time = apr_time_now();
322     apr_pool_cleanup_register(p, NULL, ap_cleanup_scoreboard, apr_pool_cleanup_null);
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 void ap_sync_scoreboard_image(void)
338 {
339 }
340
341 AP_DECLARE(int) ap_exists_scoreboard_image(void)
342 {
343     return (ap_scoreboard_image ? 1 : 0);
344 }
345
346 static APR_INLINE void put_scoreboard_info(int child_num, int thread_num, 
347                                            worker_score *new_score_rec)
348 {
349     /* XXX - needs to be fixed to account for threads */
350 #ifdef SCOREBOARD_FILE
351     lseek(scoreboard_fd, sizeof(global_score) 
352                          + (long)child_num * sizeof(worker_score), 0);
353     force_write(scoreboard_fd, new_score_rec, sizeof(worker_score));
354 #endif
355 }
356
357 void update_scoreboard_global(void)
358 {
359 #ifdef SCOREBOARD_FILE
360     lseek(scoreboard_fd, 0, 0);
361     force_write(scoreboard_fd, &ap_scoreboard_image->global,
362                 sizeof ap_scoreboard_image->global);
363 #endif
364 }
365
366 AP_DECLARE(void) ap_increment_counts(ap_sb_handle_t *sb, request_rec *r)
367 {
368     worker_score *ws;
369
370     ws = &ap_scoreboard_image->servers[sb->child_num][sb->thread_num];
371
372 #ifdef HAVE_TIMES
373     times(&ws->times);
374 #endif
375     ws->access_count++;
376     ws->my_access_count++;
377     ws->conn_count++;
378     ws->bytes_served += r->bytes_sent;
379     ws->my_bytes_served += r->bytes_sent;
380     ws->conn_bytes += r->bytes_sent;
381
382     put_scoreboard_info(sb->child_num, sb->thread_num, ws);
383 }
384
385 AP_DECLARE(int) find_child_by_pid(apr_proc_t *pid)
386 {
387     int i;
388     int max_daemons_limit;
389
390     ap_mpm_query(AP_MPMQ_MAX_DAEMONS, &max_daemons_limit);
391
392     for (i = 0; i < max_daemons_limit; ++i) {
393         if (ap_scoreboard_image->parent[i].pid == pid->pid) {
394             return i;
395         }
396     }
397
398     return -1;
399 }
400
401 AP_DECLARE(void) ap_create_sb_handle(ap_sb_handle_t **new_sbh, apr_pool_t *p,
402                                      int child_num, int thread_num)
403 {
404     *new_sbh = (ap_sb_handle_t *)apr_palloc(p, sizeof(ap_sb_handle_t));
405     (*new_sbh)->child_num = child_num;
406     (*new_sbh)->thread_num = thread_num;
407 }
408
409 AP_DECLARE(int) ap_update_child_status_from_indexes(int child_num,
410                                                     int thread_num,
411                                                     int status,
412                                                     request_rec *r)
413 {
414     int old_status;
415     worker_score *ws;
416     process_score *ps;
417
418     if (child_num < 0) {
419         return -1;
420     }
421
422     ws = &ap_scoreboard_image->servers[child_num][thread_num];
423     old_status = ws->status;
424     ws->status = status;
425
426     ps = &ap_scoreboard_image->parent[child_num];
427     
428     if (status == SERVER_READY
429         && old_status == SERVER_STARTING) {
430         ws->thread_num = child_num * server_limit + thread_num;
431         ps->generation = ap_my_generation;
432     }
433
434     if (ap_extended_status) {
435         ws->last_used = apr_time_now();
436         if (status == SERVER_READY || status == SERVER_DEAD) {
437             /*
438              * Reset individual counters
439              */
440             if (status == SERVER_DEAD) {
441                 ws->my_access_count = 0L;
442                 ws->my_bytes_served = 0L;
443             }
444             ws->conn_count = (unsigned short)0;
445             ws->conn_bytes = (unsigned long)0;
446         }
447         if (r) {
448             conn_rec *c = r->connection;
449             apr_cpystrn(ws->client, ap_get_remote_host(c, r->per_dir_config,
450                         REMOTE_NOLOOKUP, NULL), sizeof(ws->client));
451             if (r->the_request == NULL) {
452                 apr_cpystrn(ws->request, "NULL", sizeof(ws->request));
453             } else if (r->parsed_uri.password == NULL) {
454                 apr_cpystrn(ws->request, r->the_request, sizeof(ws->request));
455             } else {
456                 /* Don't reveal the password in the server-status view */
457                 apr_cpystrn(ws->request, apr_pstrcat(r->pool, r->method, " ",
458                             apr_uri_unparse(r->pool, &r->parsed_uri,
459                             APR_URI_UNP_OMITPASSWORD),
460                             r->assbackwards ? NULL : " ", r->protocol, NULL),
461                             sizeof(ws->request));
462             }
463             apr_cpystrn(ws->vhost, r->server->server_hostname,
464                         sizeof(ws->vhost));
465         }
466     }
467     
468     put_scoreboard_info(child_num, thread_num, ws);
469     return old_status;
470 }
471
472 AP_DECLARE(int) ap_update_child_status(ap_sb_handle_t *sbh, int status,
473                                       request_rec *r)
474 {
475     return ap_update_child_status_from_indexes(sbh->child_num, sbh->thread_num,
476                                                status, r);
477 }
478
479 void ap_time_process_request(int child_num, int thread_num, int status)
480 {
481     worker_score *ws;
482
483     if (child_num < 0) {
484         return;
485     }
486
487     ws = &ap_scoreboard_image->servers[child_num][thread_num];
488
489     if (status == START_PREQUEST) {
490         ws->start_time = apr_time_now(); 
491     }
492     else if (status == STOP_PREQUEST) {
493         ws->stop_time = apr_time_now(); 
494     }
495     put_scoreboard_info(child_num, thread_num, ws);
496 }
497
498 AP_DECLARE(worker_score *) ap_get_scoreboard_worker(int x, int y)
499 {
500     if (((x < 0) || (server_limit < x)) ||
501         ((y < 0) || (thread_limit < y))) {
502         return(NULL); /* Out of range */
503     }
504     return &ap_scoreboard_image->servers[x][y];
505 }
506
507 AP_DECLARE(process_score *) ap_get_scoreboard_process(int x)
508 {
509     if ((x < 0) || (server_limit < x)) {
510         return(NULL); /* Out of range */
511     }
512     return &ap_scoreboard_image->parent[x];
513 }
514
515 AP_DECLARE(global_score *) ap_get_scoreboard_global()
516 {
517     return ap_scoreboard_image->global;
518 }