]> granicus.if.org Git - apache/blob - server/scoreboard.c
Placing the thread-limit and server-limit values, those which define the
[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     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
217         return create_namebased_scoreboard(global_pool, fname);
218     }
219     else { /* config didn't specify, we get to choose shmem type */
220         rv = apr_shm_create(&ap_scoreboard_shm, scoreboard_size, NULL,
221                             global_pool); /* anonymous shared memory */
222         if ((rv != APR_SUCCESS) && (rv != APR_ENOTIMPL)) {
223             ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
224                          "Unable to create scoreboard "
225                          "(anonymous shared memory failure)");
226             return rv;
227         }
228         /* Make up a filename and do name-based shmem */
229         else if (rv == APR_ENOTIMPL) {
230             /* Make sure it's an absolute pathname */
231             ap_scoreboard_fname = DEFAULT_SCOREBOARD;
232             fname = ap_server_root_relative(pconf, ap_scoreboard_fname);
233
234             return create_namebased_scoreboard(global_pool, fname);
235         }
236     }
237 #endif /* APR_HAS_SHARED_MEMORY */
238     return APR_SUCCESS;
239 }
240
241 /* If detach is non-zero, this is a seperate child process,
242  * if zero, it is a forked child.
243  */
244 apr_status_t ap_reopen_scoreboard(apr_pool_t *p, apr_shm_t **shm, int detached)
245 {
246 #if APR_HAS_SHARED_MEMORY
247     if (!detached) {
248         return APR_SUCCESS;
249     }
250     if (apr_shm_size_get(ap_scoreboard_shm) < scoreboard_size) {
251         ap_log_error(APLOG_MARK, APLOG_CRIT | APLOG_NOERRNO, 0, NULL,
252                      "Fatal error: shared scoreboard too small for child!");
253         apr_shm_detach(ap_scoreboard_shm);
254         ap_scoreboard_shm = NULL;
255         return APR_EINVAL;
256     }
257     /* everything will be cleared shortly */
258     if (*shm) {
259         *shm = ap_scoreboard_shm;
260     }
261 #endif
262     return APR_SUCCESS;
263 }
264
265 apr_status_t ap_cleanup_scoreboard(void *d)
266 {
267     if (ap_scoreboard_image == NULL) {
268         return APR_SUCCESS;
269     }
270     if (ap_scoreboard_image->global->sb_type == SB_SHARED) {
271         ap_cleanup_shared_mem(NULL);
272     }
273     else {
274         free(ap_scoreboard_image->global);
275         free(ap_scoreboard_image);
276         ap_scoreboard_image = NULL;
277     }
278     return APR_SUCCESS;
279 }
280
281 /* Create or reinit an existing scoreboard. The MPM can control whether
282  * the scoreboard is shared across multiple processes or not
283  */
284 int ap_create_scoreboard(apr_pool_t *p, ap_scoreboard_e sb_type)
285 {
286     int running_gen = 0;
287 #if APR_HAS_SHARED_MEMORY
288     apr_status_t rv;
289 #endif
290
291     if (ap_scoreboard_image) {
292         running_gen = ap_scoreboard_image->global->running_generation;
293     }
294
295     if (ap_scoreboard_image == NULL) {
296         ap_calc_scoreboard_size();
297 #if APR_HAS_SHARED_MEMORY
298         if (sb_type == SB_SHARED) {
299             void *sb_shared;
300             rv = open_scoreboard(p);
301             if (rv || !(sb_shared = apr_shm_baseaddr_get(ap_scoreboard_shm))) {
302                 return HTTP_INTERNAL_SERVER_ERROR;
303             }
304             memset(sb_shared, 0, scoreboard_size);
305             ap_init_scoreboard(sb_shared);
306         }
307         else 
308 #endif
309         {
310             /* A simple malloc will suffice */
311             void *sb_mem = calloc(1, scoreboard_size);
312             if (sb_mem == NULL) {
313                 ap_log_error(APLOG_MARK, APLOG_CRIT | APLOG_NOERRNO, 0, NULL,
314                              "(%d)%s: cannot allocate scoreboard",
315                              errno, strerror(errno));
316                 return HTTP_INTERNAL_SERVER_ERROR;
317             }
318             ap_init_scoreboard(sb_mem);
319         }
320     }
321     ap_scoreboard_image->global->sb_type = sb_type;
322     ap_scoreboard_image->global->running_generation = running_gen;
323     ap_scoreboard_image->global->restart_time = apr_time_now();
324     apr_pool_cleanup_register(p, NULL, ap_cleanup_scoreboard, apr_pool_cleanup_null);
325     return OK;
326 }
327
328 /* Routines called to deal with the scoreboard image
329  * --- note that we do *not* need write locks, since update_child_status
330  * only updates a *single* record in place, and only one process writes to
331  * a given scoreboard slot at a time (either the child process owning that
332  * slot, or the parent, noting that the child has died).
333  *
334  * As a final note --- setting the score entry to getpid() is always safe,
335  * since when the parent is writing an entry, it's only noting SERVER_DEAD
336  * anyway.
337  */
338
339 void ap_sync_scoreboard_image(void)
340 {
341 }
342
343 AP_DECLARE(int) ap_exists_scoreboard_image(void)
344 {
345     return (ap_scoreboard_image ? 1 : 0);
346 }
347
348 static APR_INLINE void put_scoreboard_info(int child_num, int thread_num, 
349                                            worker_score *new_score_rec)
350 {
351     /* XXX - needs to be fixed to account for threads */
352 #ifdef SCOREBOARD_FILE
353     lseek(scoreboard_fd, sizeof(global_score) 
354                          + (long)child_num * sizeof(worker_score), 0);
355     force_write(scoreboard_fd, new_score_rec, sizeof(worker_score));
356 #endif
357 }
358
359 void update_scoreboard_global(void)
360 {
361 #ifdef SCOREBOARD_FILE
362     lseek(scoreboard_fd, 0, 0);
363     force_write(scoreboard_fd, &ap_scoreboard_image->global,
364                 sizeof ap_scoreboard_image->global);
365 #endif
366 }
367
368 AP_DECLARE(void) ap_increment_counts(ap_sb_handle_t *sb, request_rec *r)
369 {
370     worker_score *ws;
371
372     ws = &ap_scoreboard_image->servers[sb->child_num][sb->thread_num];
373
374 #ifdef HAVE_TIMES
375     times(&ws->times);
376 #endif
377     ws->access_count++;
378     ws->my_access_count++;
379     ws->conn_count++;
380     ws->bytes_served += r->bytes_sent;
381     ws->my_bytes_served += r->bytes_sent;
382     ws->conn_bytes += r->bytes_sent;
383
384     put_scoreboard_info(sb->child_num, sb->thread_num, ws);
385 }
386
387 AP_DECLARE(int) find_child_by_pid(apr_proc_t *pid)
388 {
389     int i;
390     int max_daemons_limit;
391
392     ap_mpm_query(AP_MPMQ_MAX_DAEMONS, &max_daemons_limit);
393
394     for (i = 0; i < max_daemons_limit; ++i) {
395         if (ap_scoreboard_image->parent[i].pid == pid->pid) {
396             return i;
397         }
398     }
399
400     return -1;
401 }
402
403 AP_DECLARE(void) ap_create_sb_handle(ap_sb_handle_t **new_sbh, apr_pool_t *p,
404                                      int child_num, int thread_num)
405 {
406     *new_sbh = (ap_sb_handle_t *)apr_palloc(p, sizeof(ap_sb_handle_t));
407     (*new_sbh)->child_num = child_num;
408     (*new_sbh)->thread_num = thread_num;
409 }
410
411 AP_DECLARE(int) ap_update_child_status_from_indexes(int child_num,
412                                                     int thread_num,
413                                                     int status,
414                                                     request_rec *r)
415 {
416     int old_status;
417     worker_score *ws;
418     process_score *ps;
419
420     if (child_num < 0) {
421         return -1;
422     }
423
424     ws = &ap_scoreboard_image->servers[child_num][thread_num];
425     old_status = ws->status;
426     ws->status = status;
427
428     ps = &ap_scoreboard_image->parent[child_num];
429     
430     if (status == SERVER_READY
431         && old_status == SERVER_STARTING) {
432         ws->thread_num = child_num * server_limit + thread_num;
433         ps->generation = ap_my_generation;
434     }
435
436     if (ap_extended_status) {
437         ws->last_used = apr_time_now();
438         if (status == SERVER_READY || status == SERVER_DEAD) {
439             /*
440              * Reset individual counters
441              */
442             if (status == SERVER_DEAD) {
443                 ws->my_access_count = 0L;
444                 ws->my_bytes_served = 0L;
445             }
446             ws->conn_count = (unsigned short)0;
447             ws->conn_bytes = (unsigned long)0;
448         }
449         if (r) {
450             conn_rec *c = r->connection;
451             apr_cpystrn(ws->client, ap_get_remote_host(c, r->per_dir_config,
452                         REMOTE_NOLOOKUP, NULL), sizeof(ws->client));
453             if (r->the_request == NULL) {
454                 apr_cpystrn(ws->request, "NULL", sizeof(ws->request));
455             } else if (r->parsed_uri.password == NULL) {
456                 apr_cpystrn(ws->request, r->the_request, sizeof(ws->request));
457             } else {
458                 /* Don't reveal the password in the server-status view */
459                 apr_cpystrn(ws->request, apr_pstrcat(r->pool, r->method, " ",
460                             apr_uri_unparse(r->pool, &r->parsed_uri,
461                             APR_URI_UNP_OMITPASSWORD),
462                             r->assbackwards ? NULL : " ", r->protocol, NULL),
463                             sizeof(ws->request));
464             }
465             apr_cpystrn(ws->vhost, r->server->server_hostname,
466                         sizeof(ws->vhost));
467         }
468     }
469     
470     put_scoreboard_info(child_num, thread_num, ws);
471     return old_status;
472 }
473
474 AP_DECLARE(int) ap_update_child_status(ap_sb_handle_t *sbh, int status,
475                                       request_rec *r)
476 {
477     return ap_update_child_status_from_indexes(sbh->child_num, sbh->thread_num,
478                                                status, r);
479 }
480
481 void ap_time_process_request(int child_num, int thread_num, int status)
482 {
483     worker_score *ws;
484
485     if (child_num < 0) {
486         return;
487     }
488
489     ws = &ap_scoreboard_image->servers[child_num][thread_num];
490
491     if (status == START_PREQUEST) {
492         ws->start_time = apr_time_now(); 
493     }
494     else if (status == STOP_PREQUEST) {
495         ws->stop_time = apr_time_now(); 
496     }
497     put_scoreboard_info(child_num, thread_num, ws);
498 }
499
500 AP_DECLARE(worker_score *) ap_get_scoreboard_worker(int x, int y)
501 {
502     if (((x < 0) || (server_limit < x)) ||
503         ((y < 0) || (thread_limit < y))) {
504         return(NULL); /* Out of range */
505     }
506     return &ap_scoreboard_image->servers[x][y];
507 }
508
509 AP_DECLARE(process_score *) ap_get_scoreboard_process(int x)
510 {
511     if ((x < 0) || (server_limit < x)) {
512         return(NULL); /* Out of range */
513     }
514     return &ap_scoreboard_image->parent[x];
515 }
516
517 AP_DECLARE(global_score *) ap_get_scoreboard_global()
518 {
519     return ap_scoreboard_image->global;
520 }