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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "apr_strings.h"
19 #include "apr_portable.h"
22 #define APR_WANT_STRFUNC
25 #if APR_HAVE_SYS_TYPES_H
26 #include <sys/types.h>
29 #include "ap_config.h"
32 #include "http_main.h"
33 #include "http_core.h"
34 #include "http_config.h"
38 #include "scoreboard.h"
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;
45 #if APR_HAS_SHARED_MEMORY
50 static /* but must be exported to mpm_winnt */
52 apr_shm_t
*ap_scoreboard_shm
= NULL
;
57 APR_HOOK_LINK(pre_mpm
)
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
)
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
;
69 struct ap_sb_handle_t
{
74 static int server_limit
, thread_limit
, lb_limit
, lb_size
;
75 static apr_size_t scoreboard_size
;
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
85 static apr_status_t
ap_cleanup_shared_mem(void *d
)
87 #if APR_HAS_SHARED_MEMORY
88 free(ap_scoreboard_image
);
89 ap_scoreboard_image
= NULL
;
90 apr_shm_destroy(ap_scoreboard_shm
);
95 AP_DECLARE(int) ap_calc_scoreboard_size(void)
97 ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS
, &thread_limit
);
98 ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS
, &server_limit
);
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();
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();
112 lb_size
= sizeof(lb_score
);
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
;
120 return scoreboard_size
;
123 void ap_init_scoreboard(void *shared_score
)
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
);
142 if (lb_limit
&& lb_size
) {
143 ap_scoreboard_image
->balancers
= (void *)more_storage
;
144 more_storage
+= lb_limit
* lb_size
;
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
;
153 * Create a name-based scoreboard in the given pool using the
156 static apr_status_t
create_namebased_scoreboard(apr_pool_t
*pool
,
159 #if APR_HAS_SHARED_MEMORY
162 /* The shared memory file must not exist before we create the
164 apr_shm_remove(fname
, pool
); /* ignore errors */
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
);
173 #endif /* APR_HAS_SHARED_MEMORY */
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
181 static apr_status_t
open_scoreboard(apr_pool_t
*pconf
)
183 #if APR_HAS_SHARED_MEMORY
186 apr_pool_t
*global_pool
;
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.
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");
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
);
204 ap_log_error(APLOG_MARK
, APLOG_CRIT
, APR_EBADPATH
, NULL
,
205 "Fatal error: Invalid Scoreboard path %s",
206 ap_scoreboard_fname
);
209 return create_namebased_scoreboard(global_pool
, fname
);
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)");
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
);
226 return create_namebased_scoreboard(global_pool
, fname
);
229 #endif /* APR_HAS_SHARED_MEMORY */
233 /* If detach is non-zero, this is a seperate child process,
234 * if zero, it is a forked child.
236 apr_status_t
ap_reopen_scoreboard(apr_pool_t
*p
, apr_shm_t
**shm
, int detached
)
238 #if APR_HAS_SHARED_MEMORY
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
;
249 /* everything will be cleared shortly */
251 *shm
= ap_scoreboard_shm
;
257 apr_status_t
ap_cleanup_scoreboard(void *d
)
259 if (ap_scoreboard_image
== NULL
) {
262 if (ap_scoreboard_image
->global
->sb_type
== SB_SHARED
) {
263 ap_cleanup_shared_mem(NULL
);
266 free(ap_scoreboard_image
->global
);
267 free(ap_scoreboard_image
);
268 ap_scoreboard_image
= NULL
;
273 /* Create or reinit an existing scoreboard. The MPM can control whether
274 * the scoreboard is shared across multiple processes or not
276 int ap_create_scoreboard(apr_pool_t
*p
, ap_scoreboard_e sb_type
)
280 #if APR_HAS_SHARED_MEMORY
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
);
293 /* Clean up the lb workers data */
294 if (lb_limit
&& lb_size
) {
295 memset(ap_scoreboard_image
->balancers
, 0,
301 ap_calc_scoreboard_size();
302 #if APR_HAS_SHARED_MEMORY
303 if (sb_type
== 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
;
309 memset(sb_shared
, 0, scoreboard_size
);
310 ap_init_scoreboard(sb_shared
);
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
;
323 ap_init_scoreboard(sb_mem
);
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();
330 apr_pool_cleanup_register(p
, NULL
, ap_cleanup_scoreboard
, apr_pool_cleanup_null
);
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).
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
346 AP_DECLARE(int) ap_exists_scoreboard_image(void)
348 return (ap_scoreboard_image
? 1 : 0);
351 AP_DECLARE(void) ap_increment_counts(ap_sb_handle_t
*sb
, request_rec
*r
)
358 ws
= &ap_scoreboard_image
->servers
[sb
->child_num
][sb
->thread_num
];
364 ws
->my_access_count
++;
366 ws
->bytes_served
+= r
->bytes_sent
;
367 ws
->my_bytes_served
+= r
->bytes_sent
;
368 ws
->conn_bytes
+= r
->bytes_sent
;
371 AP_DECLARE(int) ap_find_child_by_pid(apr_proc_t
*pid
)
374 int max_daemons_limit
;
376 ap_mpm_query(AP_MPMQ_MAX_DAEMONS
, &max_daemons_limit
);
378 for (i
= 0; i
< max_daemons_limit
; ++i
) {
379 if (ap_scoreboard_image
->parent
[i
].pid
== pid
->pid
) {
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
)
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
;
395 static void copy_request(char *rbuf
, apr_size_t rbuflen
, request_rec
*r
)
399 if (r
->the_request
== NULL
) {
400 apr_cpystrn(rbuf
, "NULL", rbuflen
);
401 return; /* short circuit below */
404 if (r
->parsed_uri
.password
== NULL
) {
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
);
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
);
420 apr_size_t slen
= strlen(p
);
421 if (slen
< rbuflen
) {
422 /* it all fits anyway */
423 apr_cpystrn(rbuf
, p
, rbuflen
);
426 apr_cpystrn(rbuf
, p
+(slen
-rbuflen
+1), rbuflen
);
431 AP_DECLARE(int) ap_update_child_status_from_indexes(int child_num
,
444 ws
= &ap_scoreboard_image
->servers
[child_num
][thread_num
];
445 old_status
= ws
->status
;
448 ps
= &ap_scoreboard_image
->parent
[child_num
];
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
;
456 if (ap_extended_status
) {
457 ws
->last_used
= apr_time_now();
458 if (status
== SERVER_READY
|| status
== SERVER_DEAD
) {
460 * Reset individual counters
462 if (status
== SERVER_DEAD
) {
463 ws
->my_access_count
= 0L;
464 ws
->my_bytes_served
= 0L;
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
,
482 AP_DECLARE(int) ap_update_child_status(ap_sb_handle_t
*sbh
, int status
,
488 return ap_update_child_status_from_indexes(sbh
->child_num
, sbh
->thread_num
,
492 AP_DECLARE(void) ap_time_process_request(ap_sb_handle_t
*sbh
, int status
)
499 if (sbh
->child_num
< 0) {
503 ws
= &ap_scoreboard_image
->servers
[sbh
->child_num
][sbh
->thread_num
];
505 if (status
== START_PREQUEST
) {
506 ws
->start_time
= apr_time_now();
508 else if (status
== STOP_PREQUEST
) {
509 ws
->stop_time
= apr_time_now();
513 AP_DECLARE(worker_score
*) ap_get_scoreboard_worker_from_indexes(int x
, int y
)
515 if (((x
< 0) || (x
>= server_limit
)) ||
516 ((y
< 0) || (y
>= thread_limit
))) {
517 return(NULL
); /* Out of range */
519 return &ap_scoreboard_image
->servers
[x
][y
];
522 AP_DECLARE(worker_score
*) ap_get_scoreboard_worker(ap_sb_handle_t
*sbh
)
527 return ap_get_scoreboard_worker_from_indexes(sbh
->child_num
,
531 AP_DECLARE(process_score
*) ap_get_scoreboard_process(int x
)
533 if ((x
< 0) || (x
>= server_limit
)) {
534 return(NULL
); /* Out of range */
536 return &ap_scoreboard_image
->parent
[x
];
539 AP_DECLARE(global_score
*) ap_get_scoreboard_global()
541 return ap_scoreboard_image
->global
;
544 AP_DECLARE(lb_score
*) ap_get_scoreboard_lb(int lb_num
)
546 if ( (lb_num
< 0) || (lb_limit
< lb_num
) || (lb_size
==0) ) {
547 return(NULL
); /* Out of range */
549 return (lb_score
*) ( ((char *) ap_scoreboard_image
->balancers
) +