]> granicus.if.org Git - apache/blob - server/scoreboard.c
mod_session_cookie: Add a session implementation capable of storing
[apache] / server / scoreboard.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include "apr.h"
18 #include "apr_strings.h"
19 #include "apr_portable.h"
20 #include "apr_lib.h"
21
22 #define APR_WANT_STRFUNC
23 #include "apr_want.h"
24
25 #if APR_HAVE_SYS_TYPES_H
26 #include <sys/types.h>
27 #endif
28
29 #include "ap_config.h"
30 #include "httpd.h"
31 #include "http_log.h"
32 #include "http_main.h"
33 #include "http_core.h"
34 #include "http_config.h"
35 #include "ap_mpm.h"
36
37 #include "mpm.h"
38 #include "scoreboard.h"
39
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;
43 AP_DECLARE_DATA int ap_mod_status_reqtail = 0;
44
45 #if APR_HAS_SHARED_MEMORY
46
47 #include "apr_shm.h"
48
49 #ifndef WIN32
50 static /* but must be exported to mpm_winnt */
51 #endif
52         apr_shm_t *ap_scoreboard_shm = NULL;
53
54 #endif
55
56 APR_HOOK_STRUCT(
57     APR_HOOK_LINK(pre_mpm)
58 )
59
60 AP_IMPLEMENT_HOOK_RUN_ALL(int,pre_mpm,
61                           (apr_pool_t *p, ap_scoreboard_e sb_type),
62                           (p, sb_type),OK,DECLINED)
63
64 static APR_OPTIONAL_FN_TYPE(ap_proxy_lb_workers)
65                                 *pfn_proxy_lb_workers;
66 static APR_OPTIONAL_FN_TYPE(ap_proxy_lb_worker_size)
67                                 *pfn_proxy_lb_worker_size;
68
69 struct ap_sb_handle_t {
70     int child_num;
71     int thread_num;
72 };
73
74 static int server_limit, thread_limit, lb_limit, lb_size;
75 static apr_size_t scoreboard_size;
76
77 /*
78  * ToDo:
79  * This function should be renamed to cleanup_shared
80  * and it should handle cleaning up a scoreboard shared
81  * between processes using any form of IPC (file, shared memory
82  * segment, etc.). Leave it as is now because it is being used
83  * by various MPMs.
84  */
85 static apr_status_t ap_cleanup_shared_mem(void *d)
86 {
87 #if APR_HAS_SHARED_MEMORY
88     free(ap_scoreboard_image);
89     ap_scoreboard_image = NULL;
90     apr_shm_destroy(ap_scoreboard_shm);
91 #endif
92     return APR_SUCCESS;
93 }
94
95 AP_DECLARE(int) ap_calc_scoreboard_size(void)
96 {
97     ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
98     ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);
99
100     if (!pfn_proxy_lb_workers)
101         pfn_proxy_lb_workers = APR_RETRIEVE_OPTIONAL_FN(ap_proxy_lb_workers);
102     if (pfn_proxy_lb_workers)
103         lb_limit = pfn_proxy_lb_workers();
104     else
105         lb_limit = 0;
106
107     if (!pfn_proxy_lb_worker_size)
108         pfn_proxy_lb_worker_size = APR_RETRIEVE_OPTIONAL_FN(ap_proxy_lb_worker_size);
109     if (pfn_proxy_lb_worker_size)
110         lb_size = pfn_proxy_lb_worker_size();
111     else
112         lb_size = sizeof(lb_score);
113
114     scoreboard_size = sizeof(global_score);
115     scoreboard_size += sizeof(process_score) * server_limit;
116     scoreboard_size += sizeof(worker_score) * server_limit * thread_limit;
117     if (lb_limit && lb_size)
118         scoreboard_size += lb_size * lb_limit;
119
120     return scoreboard_size;
121 }
122
123 void ap_init_scoreboard(void *shared_score)
124 {
125     char *more_storage;
126     int i;
127
128     ap_calc_scoreboard_size();
129     ap_scoreboard_image =
130         calloc(1, sizeof(scoreboard) + server_limit * sizeof(worker_score *));
131     more_storage = shared_score;
132     ap_scoreboard_image->global = (global_score *)more_storage;
133     more_storage += sizeof(global_score);
134     ap_scoreboard_image->parent = (process_score *)more_storage;
135     more_storage += sizeof(process_score) * server_limit;
136     ap_scoreboard_image->servers =
137         (worker_score **)((char*)ap_scoreboard_image + sizeof(scoreboard));
138     for (i = 0; i < server_limit; i++) {
139         ap_scoreboard_image->servers[i] = (worker_score *)more_storage;
140         more_storage += thread_limit * sizeof(worker_score);
141     }
142     if (lb_limit && lb_size) {
143         ap_scoreboard_image->balancers = (void *)more_storage;
144         more_storage += lb_limit * lb_size;
145     }
146     ap_assert(more_storage == (char*)shared_score + scoreboard_size);
147     ap_scoreboard_image->global->server_limit = server_limit;
148     ap_scoreboard_image->global->thread_limit = thread_limit;
149     ap_scoreboard_image->global->lb_limit     = lb_limit;
150 }
151
152 /**
153  * Create a name-based scoreboard in the given pool using the
154  * given filename.
155  */
156 static apr_status_t create_namebased_scoreboard(apr_pool_t *pool,
157                                                 const char *fname)
158 {
159 #if APR_HAS_SHARED_MEMORY
160     apr_status_t rv;
161
162     /* The shared memory file must not exist before we create the
163      * segment. */
164     apr_shm_remove(fname, pool); /* ignore errors */
165
166     rv = apr_shm_create(&ap_scoreboard_shm, scoreboard_size, fname, pool);
167     if (rv != APR_SUCCESS) {
168         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
169                      "unable to create or access scoreboard \"%s\" "
170                      "(name-based shared memory failure)", fname);
171         return rv;
172     }
173 #endif /* APR_HAS_SHARED_MEMORY */
174     return APR_SUCCESS;
175 }
176
177 /* ToDo: This function should be made to handle setting up
178  * a scoreboard shared between processes using any IPC technique,
179  * not just a shared memory segment
180  */
181 static apr_status_t open_scoreboard(apr_pool_t *pconf)
182 {
183 #if APR_HAS_SHARED_MEMORY
184     apr_status_t rv;
185     char *fname = NULL;
186     apr_pool_t *global_pool;
187
188     /* We don't want to have to recreate the scoreboard after
189      * restarts, so we'll create a global pool and never clean it.
190      */
191     rv = apr_pool_create(&global_pool, NULL);
192     if (rv != APR_SUCCESS) {
193         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
194                      "Fatal error: unable to create global pool "
195                      "for use by the scoreboard");
196         return rv;
197     }
198
199     /* The config says to create a name-based shmem */
200     if (ap_scoreboard_fname) {
201         /* make sure it's an absolute pathname */
202         fname = ap_server_root_relative(pconf, ap_scoreboard_fname);
203         if (!fname) {
204             ap_log_error(APLOG_MARK, APLOG_CRIT, APR_EBADPATH, NULL,
205                          "Fatal error: Invalid Scoreboard path %s",
206                          ap_scoreboard_fname);
207             return APR_EBADPATH;
208         }
209         return create_namebased_scoreboard(global_pool, fname);
210     }
211     else { /* config didn't specify, we get to choose shmem type */
212         rv = apr_shm_create(&ap_scoreboard_shm, scoreboard_size, NULL,
213                             global_pool); /* anonymous shared memory */
214         if ((rv != APR_SUCCESS) && (rv != APR_ENOTIMPL)) {
215             ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
216                          "Unable to create or access scoreboard "
217                          "(anonymous shared memory failure)");
218             return rv;
219         }
220         /* Make up a filename and do name-based shmem */
221         else if (rv == APR_ENOTIMPL) {
222             /* Make sure it's an absolute pathname */
223             ap_scoreboard_fname = DEFAULT_SCOREBOARD;
224             fname = ap_server_root_relative(pconf, ap_scoreboard_fname);
225
226             return create_namebased_scoreboard(global_pool, fname);
227         }
228     }
229 #endif /* APR_HAS_SHARED_MEMORY */
230     return APR_SUCCESS;
231 }
232
233 /* If detach is non-zero, this is a seperate child process,
234  * if zero, it is a forked child.
235  */
236 apr_status_t ap_reopen_scoreboard(apr_pool_t *p, apr_shm_t **shm, int detached)
237 {
238 #if APR_HAS_SHARED_MEMORY
239     if (!detached) {
240         return APR_SUCCESS;
241     }
242     if (apr_shm_size_get(ap_scoreboard_shm) < scoreboard_size) {
243         ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL,
244                      "Fatal error: shared scoreboard too small for child!");
245         apr_shm_detach(ap_scoreboard_shm);
246         ap_scoreboard_shm = NULL;
247         return APR_EINVAL;
248     }
249     /* everything will be cleared shortly */
250     if (*shm) {
251         *shm = ap_scoreboard_shm;
252     }
253 #endif
254     return APR_SUCCESS;
255 }
256
257 apr_status_t ap_cleanup_scoreboard(void *d)
258 {
259     if (ap_scoreboard_image == NULL) {
260         return APR_SUCCESS;
261     }
262     if (ap_scoreboard_image->global->sb_type == SB_SHARED) {
263         ap_cleanup_shared_mem(NULL);
264     }
265     else {
266         free(ap_scoreboard_image->global);
267         free(ap_scoreboard_image);
268         ap_scoreboard_image = NULL;
269     }
270     return APR_SUCCESS;
271 }
272
273 /* Create or reinit an existing scoreboard. The MPM can control whether
274  * the scoreboard is shared across multiple processes or not
275  */
276 int ap_create_scoreboard(apr_pool_t *p, ap_scoreboard_e sb_type)
277 {
278     int running_gen = 0;
279     int i;
280 #if APR_HAS_SHARED_MEMORY
281     apr_status_t rv;
282 #endif
283
284     if (ap_scoreboard_image) {
285         running_gen = ap_scoreboard_image->global->running_generation;
286         ap_scoreboard_image->global->restart_time = apr_time_now();
287         memset(ap_scoreboard_image->parent, 0,
288                sizeof(process_score) * server_limit);
289         for (i = 0; i < server_limit; i++) {
290             memset(ap_scoreboard_image->servers[i], 0,
291                    sizeof(worker_score) * thread_limit);
292         }
293         /* Clean up the lb workers data */
294         if (lb_limit && lb_size) {
295             memset(ap_scoreboard_image->balancers, 0,
296                    lb_size * lb_limit);
297         }
298         return OK;
299     }
300
301     ap_calc_scoreboard_size();
302 #if APR_HAS_SHARED_MEMORY
303     if (sb_type == SB_SHARED) {
304         void *sb_shared;
305         rv = open_scoreboard(p);
306         if (rv || !(sb_shared = apr_shm_baseaddr_get(ap_scoreboard_shm))) {
307             return HTTP_INTERNAL_SERVER_ERROR;
308         }
309         memset(sb_shared, 0, scoreboard_size);
310         ap_init_scoreboard(sb_shared);
311     }
312     else
313 #endif
314     {
315         /* A simple malloc will suffice */
316         void *sb_mem = calloc(1, scoreboard_size);
317         if (sb_mem == NULL) {
318             ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL,
319                          "(%d)%s: cannot allocate scoreboard",
320                          errno, strerror(errno));
321             return HTTP_INTERNAL_SERVER_ERROR;
322         }
323         ap_init_scoreboard(sb_mem);
324     }
325
326     ap_scoreboard_image->global->sb_type = sb_type;
327     ap_scoreboard_image->global->running_generation = running_gen;
328     ap_scoreboard_image->global->restart_time = apr_time_now();
329
330     apr_pool_cleanup_register(p, NULL, ap_cleanup_scoreboard, apr_pool_cleanup_null);
331
332     return OK;
333 }
334
335 /* Routines called to deal with the scoreboard image
336  * --- note that we do *not* need write locks, since update_child_status
337  * only updates a *single* record in place, and only one process writes to
338  * a given scoreboard slot at a time (either the child process owning that
339  * slot, or the parent, noting that the child has died).
340  *
341  * As a final note --- setting the score entry to getpid() is always safe,
342  * since when the parent is writing an entry, it's only noting SERVER_DEAD
343  * anyway.
344  */
345
346 AP_DECLARE(int) ap_exists_scoreboard_image(void)
347 {
348     return (ap_scoreboard_image ? 1 : 0);
349 }
350
351 AP_DECLARE(void) ap_increment_counts(ap_sb_handle_t *sb, request_rec *r)
352 {
353     worker_score *ws;
354
355     if (!sb)
356         return;
357
358     ws = &ap_scoreboard_image->servers[sb->child_num][sb->thread_num];
359
360 #ifdef HAVE_TIMES
361     times(&ws->times);
362 #endif
363     ws->access_count++;
364     ws->my_access_count++;
365     ws->conn_count++;
366     ws->bytes_served += r->bytes_sent;
367     ws->my_bytes_served += r->bytes_sent;
368     ws->conn_bytes += r->bytes_sent;
369 }
370
371 int find_child_by_pid(apr_proc_t *pid)
372 {
373     int i;
374     int max_daemons_limit;
375
376     ap_mpm_query(AP_MPMQ_MAX_DAEMONS, &max_daemons_limit);
377
378     for (i = 0; i < max_daemons_limit; ++i) {
379         if (ap_scoreboard_image->parent[i].pid == pid->pid) {
380             return i;
381         }
382     }
383
384     return -1;
385 }
386
387 AP_DECLARE(void) ap_create_sb_handle(ap_sb_handle_t **new_sbh, apr_pool_t *p,
388                                      int child_num, int thread_num)
389 {
390     *new_sbh = (ap_sb_handle_t *)apr_palloc(p, sizeof(ap_sb_handle_t));
391     (*new_sbh)->child_num = child_num;
392     (*new_sbh)->thread_num = thread_num;
393 }
394
395 static void copy_request(char *rbuf, apr_size_t rbuflen, request_rec *r)
396 {
397     char *p;
398
399     if (r->the_request == NULL) {
400         apr_cpystrn(rbuf, "NULL", rbuflen);
401         return; /* short circuit below */
402     }
403
404     if (r->parsed_uri.password == NULL) {
405         p = r->the_request;
406     }
407     else {
408         /* Don't reveal the password in the server-status view */
409         p = apr_pstrcat(r->pool, r->method, " ",
410                         apr_uri_unparse(r->pool, &r->parsed_uri,
411                         APR_URI_UNP_OMITPASSWORD),
412                         r->assbackwards ? NULL : " ", r->protocol, NULL);
413     }
414
415     /* now figure out if we copy over the 1st rbuflen chars or the last */
416     if (!ap_mod_status_reqtail) {
417         apr_cpystrn(rbuf, p, rbuflen);
418     }
419     else {
420         apr_size_t slen = strlen(p);
421         if (slen < rbuflen) {
422             /* it all fits anyway */
423             apr_cpystrn(rbuf, p, rbuflen);
424         }
425         else {
426             apr_cpystrn(rbuf, p+(slen-rbuflen+1), rbuflen);
427         }
428     }
429 }
430
431 AP_DECLARE(int) ap_update_child_status_from_indexes(int child_num,
432                                                     int thread_num,
433                                                     int status,
434                                                     request_rec *r)
435 {
436     int old_status;
437     worker_score *ws;
438     process_score *ps;
439
440     if (child_num < 0) {
441         return -1;
442     }
443
444     ws = &ap_scoreboard_image->servers[child_num][thread_num];
445     old_status = ws->status;
446     ws->status = status;
447
448     ps = &ap_scoreboard_image->parent[child_num];
449
450     if (status == SERVER_READY
451         && old_status == SERVER_STARTING) {
452         ws->thread_num = child_num * thread_limit + thread_num;
453         ps->generation = ap_my_generation;
454     }
455
456     if (ap_extended_status) {
457         ws->last_used = apr_time_now();
458         if (status == SERVER_READY || status == SERVER_DEAD) {
459             /*
460              * Reset individual counters
461              */
462             if (status == SERVER_DEAD) {
463                 ws->my_access_count = 0L;
464                 ws->my_bytes_served = 0L;
465             }
466             ws->conn_count = 0;
467             ws->conn_bytes = 0;
468         }
469         if (r) {
470             conn_rec *c = r->connection;
471             apr_cpystrn(ws->client, ap_get_remote_host(c, r->per_dir_config,
472                         REMOTE_NOLOOKUP, NULL), sizeof(ws->client));
473             copy_request(ws->request, sizeof(ws->request), r);
474             apr_cpystrn(ws->vhost, r->server->server_hostname,
475                         sizeof(ws->vhost));
476         }
477     }
478
479     return old_status;
480 }
481
482 AP_DECLARE(int) ap_update_child_status(ap_sb_handle_t *sbh, int status,
483                                       request_rec *r)
484 {
485     if (!sbh)
486         return -1;
487
488     return ap_update_child_status_from_indexes(sbh->child_num, sbh->thread_num,
489                                                status, r);
490 }
491
492 AP_DECLARE(void) ap_time_process_request(ap_sb_handle_t *sbh, int status)
493 {
494     worker_score *ws;
495
496     if (!sbh)
497         return;
498
499     if (sbh->child_num < 0) {
500         return;
501     }
502
503     ws = &ap_scoreboard_image->servers[sbh->child_num][sbh->thread_num];
504
505     if (status == START_PREQUEST) {
506         ws->start_time = apr_time_now();
507     }
508     else if (status == STOP_PREQUEST) {
509         ws->stop_time = apr_time_now();
510     }
511 }
512
513 AP_DECLARE(worker_score *) ap_get_scoreboard_worker_from_indexes(int x, int y)
514 {
515     if (((x < 0) || (x >= server_limit)) ||
516         ((y < 0) || (y >= thread_limit))) {
517         return(NULL); /* Out of range */
518     }
519     return &ap_scoreboard_image->servers[x][y];
520 }
521
522 AP_DECLARE(worker_score *) ap_get_scoreboard_worker(ap_sb_handle_t *sbh)
523 {
524     if (!sbh)
525         return NULL;
526
527     return ap_get_scoreboard_worker_from_indexes(sbh->child_num,
528                                                  sbh->thread_num);
529 }
530
531 AP_DECLARE(process_score *) ap_get_scoreboard_process(int x)
532 {
533     if ((x < 0) || (x >= server_limit)) {
534         return(NULL); /* Out of range */
535     }
536     return &ap_scoreboard_image->parent[x];
537 }
538
539 AP_DECLARE(global_score *) ap_get_scoreboard_global()
540 {
541     return ap_scoreboard_image->global;
542 }
543
544 AP_DECLARE(lb_score *) ap_get_scoreboard_lb(int lb_num)
545 {
546     if ( (lb_num < 0) || (lb_limit < lb_num) || (lb_size==0) ) {
547         return(NULL); /* Out of range */
548     }
549     return (lb_score *) ( ((char *) ap_scoreboard_image->balancers) +
550                           (lb_num*lb_size) );
551 }