Main Page | Modules | Namespace List | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages | Examples

scoreboard.c

Go to the documentation of this file.
00001 /* Copyright 2001-2005 The Apache Software Foundation or its licensors, as
00002  * applicable.
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #include "apr.h"
00018 #include "apr_strings.h"
00019 #include "apr_portable.h"
00020 #include "apr_lib.h"
00021 
00022 #define APR_WANT_STRFUNC
00023 #include "apr_want.h"
00024 
00025 #if APR_HAVE_SYS_TYPES_H
00026 #include <sys/types.h>
00027 #endif
00028 
00029 #include "ap_config.h"
00030 #include "httpd.h"
00031 #include "http_log.h"
00032 #include "http_main.h"
00033 #include "http_core.h"
00034 #include "http_config.h"
00035 #include "ap_mpm.h"
00036 
00037 #include "mpm.h"
00038 #include "scoreboard.h"
00039 
00040 AP_DECLARE_DATA scoreboard *ap_scoreboard_image = NULL;
00041 AP_DECLARE_DATA const char *ap_scoreboard_fname = NULL;
00042 AP_DECLARE_DATA int ap_extended_status = 0;
00043 
00044 #if APR_HAS_SHARED_MEMORY
00045 
00046 #include "apr_shm.h"
00047 
00048 #ifndef WIN32
00049 static /* but must be exported to mpm_winnt */
00050 #endif
00051         apr_shm_t *ap_scoreboard_shm = NULL;
00052 
00053 #endif
00054 
00055 APR_HOOK_STRUCT(
00056     APR_HOOK_LINK(pre_mpm)
00057 )
00058  
00059 AP_IMPLEMENT_HOOK_RUN_ALL(int,pre_mpm,
00060                           (apr_pool_t *p, ap_scoreboard_e sb_type),
00061                           (p, sb_type),OK,DECLINED)
00062 
00063 struct ap_sb_handle_t {
00064     int child_num;
00065     int thread_num;
00066 };
00067 
00068 static int server_limit, thread_limit;
00069 static apr_size_t scoreboard_size;
00070 
00071 /*
00072  * ToDo:
00073  * This function should be renamed to cleanup_shared
00074  * and it should handle cleaning up a scoreboard shared
00075  * between processes using any form of IPC (file, shared memory
00076  * segment, etc.). Leave it as is now because it is being used
00077  * by various MPMs. 
00078  */
00079 static apr_status_t ap_cleanup_shared_mem(void *d)
00080 {
00081 #if APR_HAS_SHARED_MEMORY
00082     free(ap_scoreboard_image);
00083     ap_scoreboard_image = NULL;
00084     apr_shm_destroy(ap_scoreboard_shm);
00085 #endif
00086     return APR_SUCCESS;
00087 }
00088 
00089 AP_DECLARE(int) ap_calc_scoreboard_size(void)
00090 {
00091     ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
00092     ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);
00093     scoreboard_size = sizeof(global_score);
00094     scoreboard_size += sizeof(process_score) * server_limit;
00095     scoreboard_size += sizeof(worker_score) * server_limit * thread_limit;
00096     return scoreboard_size;
00097 }
00098 
00099 void ap_init_scoreboard(void *shared_score)
00100 {
00101     char *more_storage;
00102     int i;
00103     
00104     ap_calc_scoreboard_size();
00105     ap_scoreboard_image = 
00106         calloc(1, sizeof(scoreboard) + server_limit * sizeof(worker_score *));
00107     more_storage = shared_score;
00108     ap_scoreboard_image->global = (global_score *)more_storage;
00109     more_storage += sizeof(global_score);
00110     ap_scoreboard_image->parent = (process_score *)more_storage;
00111     more_storage += sizeof(process_score) * server_limit;
00112     ap_scoreboard_image->servers = 
00113         (worker_score **)((char*)ap_scoreboard_image + sizeof(scoreboard));
00114     for (i = 0; i < server_limit; i++) {
00115         ap_scoreboard_image->servers[i] = (worker_score *)more_storage;
00116         more_storage += thread_limit * sizeof(worker_score);
00117     }
00118     ap_assert(more_storage == (char*)shared_score + scoreboard_size);
00119     ap_scoreboard_image->global->server_limit = server_limit;
00120     ap_scoreboard_image->global->thread_limit = thread_limit;
00121 }
00122 
00123 /**
00124  * Create a name-based scoreboard in the given pool using the
00125  * given filename.
00126  */
00127 static apr_status_t create_namebased_scoreboard(apr_pool_t *pool,
00128                                                 const char *fname)
00129 {
00130 #if APR_HAS_SHARED_MEMORY
00131     apr_status_t rv;
00132 
00133     /* The shared memory file must not exist before we create the
00134      * segment. */
00135     apr_file_remove(fname, pool); /* ignore errors */
00136 
00137     rv = apr_shm_create(&ap_scoreboard_shm, scoreboard_size, fname, pool);
00138     if (rv != APR_SUCCESS) {
00139         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
00140                      "unable to create scoreboard \"%s\" "
00141                      "(name-based shared memory failure)", fname);
00142         return rv;
00143     }
00144 #endif /* APR_HAS_SHARED_MEMORY */
00145     return APR_SUCCESS;
00146 }
00147 
00148 /* ToDo: This function should be made to handle setting up 
00149  * a scoreboard shared between processes using any IPC technique, 
00150  * not just a shared memory segment
00151  */
00152 static apr_status_t open_scoreboard(apr_pool_t *pconf)
00153 {
00154 #if APR_HAS_SHARED_MEMORY
00155     apr_status_t rv;
00156     char *fname = NULL;
00157     apr_pool_t *global_pool;
00158 
00159     /* We don't want to have to recreate the scoreboard after
00160      * restarts, so we'll create a global pool and never clean it.
00161      */
00162     rv = apr_pool_create(&global_pool, NULL);
00163     if (rv != APR_SUCCESS) {
00164         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
00165                      "Fatal error: unable to create global pool "
00166                      "for use with by the scoreboard");
00167         return rv;
00168     }
00169 
00170     /* The config says to create a name-based shmem */
00171     if (ap_scoreboard_fname) {
00172         /* make sure it's an absolute pathname */
00173         fname = ap_server_root_relative(pconf, ap_scoreboard_fname);
00174         if (!fname) {
00175             ap_log_error(APLOG_MARK, APLOG_CRIT, APR_EBADPATH, NULL,
00176                          "Fatal error: Invalid Scoreboard path %s",
00177                          ap_scoreboard_fname);
00178             return APR_EBADPATH;
00179         }
00180         return create_namebased_scoreboard(global_pool, fname);
00181     }
00182     else { /* config didn't specify, we get to choose shmem type */
00183         rv = apr_shm_create(&ap_scoreboard_shm, scoreboard_size, NULL,
00184                             global_pool); /* anonymous shared memory */
00185         if ((rv != APR_SUCCESS) && (rv != APR_ENOTIMPL)) {
00186             ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
00187                          "Unable to create scoreboard "
00188                          "(anonymous shared memory failure)");
00189             return rv;
00190         }
00191         /* Make up a filename and do name-based shmem */
00192         else if (rv == APR_ENOTIMPL) {
00193             /* Make sure it's an absolute pathname */
00194             ap_scoreboard_fname = DEFAULT_SCOREBOARD;
00195             fname = ap_server_root_relative(pconf, ap_scoreboard_fname);
00196 
00197             return create_namebased_scoreboard(global_pool, fname);
00198         }
00199     }
00200 #endif /* APR_HAS_SHARED_MEMORY */
00201     return APR_SUCCESS;
00202 }
00203 
00204 /* If detach is non-zero, this is a seperate child process,
00205  * if zero, it is a forked child.
00206  */
00207 apr_status_t ap_reopen_scoreboard(apr_pool_t *p, apr_shm_t **shm, int detached)
00208 {
00209 #if APR_HAS_SHARED_MEMORY
00210     if (!detached) {
00211         return APR_SUCCESS;
00212     }
00213     if (apr_shm_size_get(ap_scoreboard_shm) < scoreboard_size) {
00214         ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL,
00215                      "Fatal error: shared scoreboard too small for child!");
00216         apr_shm_detach(ap_scoreboard_shm);
00217         ap_scoreboard_shm = NULL;
00218         return APR_EINVAL;
00219     }
00220     /* everything will be cleared shortly */
00221     if (*shm) {
00222         *shm = ap_scoreboard_shm;
00223     }
00224 #endif
00225     return APR_SUCCESS;
00226 }
00227 
00228 apr_status_t ap_cleanup_scoreboard(void *d)
00229 {
00230     if (ap_scoreboard_image == NULL) {
00231         return APR_SUCCESS;
00232     }
00233     if (ap_scoreboard_image->global->sb_type == SB_SHARED) {
00234         ap_cleanup_shared_mem(NULL);
00235     }
00236     else {
00237         free(ap_scoreboard_image->global);
00238         free(ap_scoreboard_image);
00239         ap_scoreboard_image = NULL;
00240     }
00241     return APR_SUCCESS;
00242 }
00243 
00244 /* Create or reinit an existing scoreboard. The MPM can control whether
00245  * the scoreboard is shared across multiple processes or not
00246  */
00247 int ap_create_scoreboard(apr_pool_t *p, ap_scoreboard_e sb_type)
00248 {
00249     int running_gen = 0;
00250     int i;
00251 #if APR_HAS_SHARED_MEMORY
00252     apr_status_t rv;
00253 #endif
00254 
00255     if (ap_scoreboard_image) {
00256         running_gen = ap_scoreboard_image->global->running_generation;
00257         ap_scoreboard_image->global->restart_time = apr_time_now();
00258         memset(ap_scoreboard_image->parent, 0, 
00259                sizeof(process_score) * server_limit);
00260         for (i = 0; i < server_limit; i++) {
00261             memset(ap_scoreboard_image->servers[i], 0,
00262                    sizeof(worker_score) * thread_limit);
00263         }
00264         return OK;
00265     }
00266 
00267     ap_calc_scoreboard_size();
00268 #if APR_HAS_SHARED_MEMORY
00269     if (sb_type == SB_SHARED) {
00270         void *sb_shared;
00271         rv = open_scoreboard(p);
00272         if (rv || !(sb_shared = apr_shm_baseaddr_get(ap_scoreboard_shm))) {
00273             return HTTP_INTERNAL_SERVER_ERROR;
00274         }
00275         memset(sb_shared, 0, scoreboard_size);
00276         ap_init_scoreboard(sb_shared);
00277     }
00278     else 
00279 #endif
00280     {
00281         /* A simple malloc will suffice */
00282         void *sb_mem = calloc(1, scoreboard_size);
00283         if (sb_mem == NULL) {
00284             ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL,
00285                          "(%d)%s: cannot allocate scoreboard",
00286                          errno, strerror(errno));
00287             return HTTP_INTERNAL_SERVER_ERROR;
00288         }
00289         ap_init_scoreboard(sb_mem);
00290     }
00291 
00292     ap_scoreboard_image->global->sb_type = sb_type;
00293     ap_scoreboard_image->global->running_generation = running_gen;
00294     ap_scoreboard_image->global->restart_time = apr_time_now();
00295 
00296     apr_pool_cleanup_register(p, NULL, ap_cleanup_scoreboard, apr_pool_cleanup_null);
00297 
00298     return OK;
00299 }
00300 
00301 /* Routines called to deal with the scoreboard image
00302  * --- note that we do *not* need write locks, since update_child_status
00303  * only updates a *single* record in place, and only one process writes to
00304  * a given scoreboard slot at a time (either the child process owning that
00305  * slot, or the parent, noting that the child has died).
00306  *
00307  * As a final note --- setting the score entry to getpid() is always safe,
00308  * since when the parent is writing an entry, it's only noting SERVER_DEAD
00309  * anyway.
00310  */
00311 
00312 AP_DECLARE(int) ap_exists_scoreboard_image(void)
00313 {
00314     return (ap_scoreboard_image ? 1 : 0);
00315 }
00316 
00317 AP_DECLARE(void) ap_increment_counts(ap_sb_handle_t *sb, request_rec *r)
00318 {
00319     worker_score *ws;
00320 
00321     ws = &ap_scoreboard_image->servers[sb->child_num][sb->thread_num];
00322 
00323 #ifdef HAVE_TIMES
00324     times(&ws->times);
00325 #endif
00326     ws->access_count++;
00327     ws->my_access_count++;
00328     ws->conn_count++;
00329     ws->bytes_served += r->bytes_sent;
00330     ws->my_bytes_served += r->bytes_sent;
00331     ws->conn_bytes += r->bytes_sent;
00332 }
00333 
00334 AP_DECLARE(int) find_child_by_pid(apr_proc_t *pid)
00335 {
00336     int i;
00337     int max_daemons_limit;
00338 
00339     ap_mpm_query(AP_MPMQ_MAX_DAEMONS, &max_daemons_limit);
00340 
00341     for (i = 0; i < max_daemons_limit; ++i) {
00342         if (ap_scoreboard_image->parent[i].pid == pid->pid) {
00343             return i;
00344         }
00345     }
00346 
00347     return -1;
00348 }
00349 
00350 AP_DECLARE(void) ap_create_sb_handle(ap_sb_handle_t **new_sbh, apr_pool_t *p,
00351                                      int child_num, int thread_num)
00352 {
00353     *new_sbh = (ap_sb_handle_t *)apr_palloc(p, sizeof(ap_sb_handle_t));
00354     (*new_sbh)->child_num = child_num;
00355     (*new_sbh)->thread_num = thread_num;
00356 }
00357 
00358 AP_DECLARE(int) ap_update_child_status_from_indexes(int child_num,
00359                                                     int thread_num,
00360                                                     int status,
00361                                                     request_rec *r)
00362 {
00363     int old_status;
00364     worker_score *ws;
00365     process_score *ps;
00366 
00367     if (child_num < 0) {
00368         return -1;
00369     }
00370 
00371     ws = &ap_scoreboard_image->servers[child_num][thread_num];
00372     old_status = ws->status;
00373     ws->status = status;
00374 
00375     ps = &ap_scoreboard_image->parent[child_num];
00376     
00377     if (status == SERVER_READY
00378         && old_status == SERVER_STARTING) {
00379         ws->thread_num = child_num * thread_limit + thread_num;
00380         ps->generation = ap_my_generation;
00381     }
00382 
00383     if (ap_extended_status) {
00384         ws->last_used = apr_time_now();
00385         if (status == SERVER_READY || status == SERVER_DEAD) {
00386             /*
00387              * Reset individual counters
00388              */
00389             if (status == SERVER_DEAD) {
00390                 ws->my_access_count = 0L;
00391                 ws->my_bytes_served = 0L;
00392             }
00393             ws->conn_count = 0;
00394             ws->conn_bytes = 0;
00395         }
00396         if (r) {
00397             conn_rec *c = r->connection;
00398             apr_cpystrn(ws->client, ap_get_remote_host(c, r->per_dir_config,
00399                         REMOTE_NOLOOKUP, NULL), sizeof(ws->client));
00400             if (r->the_request == NULL) {
00401                 apr_cpystrn(ws->request, "NULL", sizeof(ws->request));
00402             } else if (r->parsed_uri.password == NULL) {
00403                 apr_cpystrn(ws->request, r->the_request, sizeof(ws->request));
00404             } else {
00405                 /* Don't reveal the password in the server-status view */
00406                 apr_cpystrn(ws->request, apr_pstrcat(r->pool, r->method, " ",
00407                             apr_uri_unparse(r->pool, &r->parsed_uri,
00408                             APR_URI_UNP_OMITPASSWORD),
00409                             r->assbackwards ? NULL : " ", r->protocol, NULL),
00410                             sizeof(ws->request));
00411             }
00412             apr_cpystrn(ws->vhost, r->server->server_hostname,
00413                         sizeof(ws->vhost));
00414         }
00415     }
00416     
00417     return old_status;
00418 }
00419 
00420 AP_DECLARE(int) ap_update_child_status(ap_sb_handle_t *sbh, int status,
00421                                       request_rec *r)
00422 {
00423     return ap_update_child_status_from_indexes(sbh->child_num, sbh->thread_num,
00424                                                status, r);
00425 }
00426 
00427 void ap_time_process_request(ap_sb_handle_t *sbh, int status)
00428 {
00429     worker_score *ws;
00430 
00431     if (sbh->child_num < 0) {
00432         return;
00433     }
00434 
00435     ws = &ap_scoreboard_image->servers[sbh->child_num][sbh->thread_num];
00436 
00437     if (status == START_PREQUEST) {
00438         ws->start_time = apr_time_now(); 
00439     }
00440     else if (status == STOP_PREQUEST) {
00441         ws->stop_time = apr_time_now(); 
00442     }
00443 }
00444 
00445 AP_DECLARE(worker_score *) ap_get_scoreboard_worker(int x, int y)
00446 {
00447     if (((x < 0) || (server_limit < x)) ||
00448         ((y < 0) || (thread_limit < y))) {
00449         return(NULL); /* Out of range */
00450     }
00451     return &ap_scoreboard_image->servers[x][y];
00452 }
00453 
00454 AP_DECLARE(process_score *) ap_get_scoreboard_process(int x)
00455 {
00456     if ((x < 0) || (server_limit < x)) {
00457         return(NULL); /* Out of range */
00458     }
00459     return &ap_scoreboard_image->parent[x];
00460 }
00461 
00462 AP_DECLARE(global_score *) ap_get_scoreboard_global()
00463 {
00464     return ap_scoreboard_image->global;
00465 }