Parent Directory
|
Revision Log
use Greg's cleaner fix for CAN-2005-2970
| 1 | jerenkrantz | 151408 | /* Copyright 2001-2005 The Apache Software Foundation or its licensors, as |
| 2 | * applicable. | ||
| 3 | rbb | 89781 | * |
| 4 | nd | 102525 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | ||
| 6 | * You may obtain a copy of the License at | ||
| 7 | rbb | 89781 | * |
| 8 | nd | 102525 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | rbb | 89781 | * |
| 10 | nd | 102525 | * 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 | rbb | 89781 | */ |
| 16 | |||
| 17 | rbb | 89938 | /* The purpose of this MPM is to fix the design flaws in the threaded |
| 18 | * model. Because of the way that pthreads and mutex locks interact, | ||
| 19 | * it is basically impossible to cleanly gracefully shutdown a child | ||
| 20 | * process if multiple threads are all blocked in accept. This model | ||
| 21 | * fixes those problems. | ||
| 22 | */ | ||
| 23 | |||
| 24 | rbb | 89781 | #include "apr.h" |
| 25 | #include "apr_portable.h" | ||
| 26 | #include "apr_strings.h" | ||
| 27 | #include "apr_file_io.h" | ||
| 28 | #include "apr_thread_proc.h" | ||
| 29 | #include "apr_signal.h" | ||
| 30 | aaron | 91518 | #include "apr_thread_mutex.h" |
| 31 | aaron | 91582 | #include "apr_proc_mutex.h" |
| 32 | rbb | 96005 | #include "apr_poll.h" |
| 33 | rbb | 89781 | #define APR_WANT_STRFUNC |
| 34 | #include "apr_want.h" | ||
| 35 | |||
| 36 | #if APR_HAVE_UNISTD_H | ||
| 37 | #include <unistd.h> | ||
| 38 | #endif | ||
| 39 | #if APR_HAVE_SYS_SOCKET_H | ||
| 40 | #include <sys/socket.h> | ||
| 41 | #endif | ||
| 42 | #if APR_HAVE_SYS_WAIT_H | ||
| 43 | #include <sys/wait.h> | ||
| 44 | #endif | ||
| 45 | #ifdef HAVE_SYS_PROCESSOR_H | ||
| 46 | #include <sys/processor.h> /* for bindprocessor() */ | ||
| 47 | #endif | ||
| 48 | |||
| 49 | #if !APR_HAS_THREADS | ||
| 50 | #error The Worker MPM requires APR threads, but they are unavailable. | ||
| 51 | #endif | ||
| 52 | |||
| 53 | #define CORE_PRIVATE | ||
| 54 | |||
| 55 | #include "ap_config.h" | ||
| 56 | #include "httpd.h" | ||
| 57 | #include "http_main.h" | ||
| 58 | #include "http_log.h" | ||
| 59 | jerenkrantz | 92473 | #include "http_config.h" /* for read_config */ |
| 60 | #include "http_core.h" /* for get_remote_host */ | ||
| 61 | rbb | 89781 | #include "http_connection.h" |
| 62 | #include "ap_mpm.h" | ||
| 63 | rbb | 93358 | #include "pod.h" |
| 64 | rbb | 89781 | #include "mpm_common.h" |
| 65 | #include "ap_listen.h" | ||
| 66 | #include "scoreboard.h" | ||
| 67 | #include "fdqueue.h" | ||
| 68 | trawick | 92512 | #include "mpm_default.h" |
| 69 | rbb | 89781 | |
| 70 | #include <signal.h> | ||
| 71 | #include <limits.h> /* for INT_MAX */ | ||
| 72 | |||
| 73 | trawick | 92512 | /* Limit on the total --- clients will be locked out if more servers than |
| 74 | * this are needed. It is intended solely to keep the server from crashing | ||
| 75 | * when things get out of hand. | ||
| 76 | * | ||
| 77 | * We keep a hard maximum number of servers, for two reasons --- first off, | ||
| 78 | * in case something goes seriously wrong, we want to stop the fork bomb | ||
| 79 | * short of actually crashing the machine we're running on by filling some | ||
| 80 | * kernel table. Secondly, it keeps the size of the scoreboard file small | ||
| 81 | * enough that we can read the whole thing without worrying too much about | ||
| 82 | * the overhead. | ||
| 83 | */ | ||
| 84 | trawick | 92530 | #ifndef DEFAULT_SERVER_LIMIT |
| 85 | #define DEFAULT_SERVER_LIMIT 16 | ||
| 86 | trawick | 92512 | #endif |
| 87 | |||
| 88 | trawick | 92530 | /* Admin can't tune ServerLimit beyond MAX_SERVER_LIMIT. We want |
| 89 | * some sort of compile-time limit to help catch typos. | ||
| 90 | */ | ||
| 91 | #ifndef MAX_SERVER_LIMIT | ||
| 92 | #define MAX_SERVER_LIMIT 20000 | ||
| 93 | #endif | ||
| 94 | |||
| 95 | trawick | 92512 | /* Limit on the threads per process. Clients will be locked out if more than |
| 96 | trawick | 92530 | * this * server_limit are needed. |
| 97 | trawick | 92512 | * |
| 98 | * We keep this for one reason it keeps the size of the scoreboard file small | ||
| 99 | * enough that we can read the whole thing without worrying too much about | ||
| 100 | * the overhead. | ||
| 101 | */ | ||
| 102 | trawick | 92530 | #ifndef DEFAULT_THREAD_LIMIT |
| 103 | #define DEFAULT_THREAD_LIMIT 64 | ||
| 104 | trawick | 92512 | #endif |
| 105 | |||
| 106 | trawick | 92530 | /* Admin can't tune ThreadLimit beyond MAX_THREAD_LIMIT. We want |
| 107 | * some sort of compile-time limit to help catch typos. | ||
| 108 | */ | ||
| 109 | #ifndef MAX_THREAD_LIMIT | ||
| 110 | #define MAX_THREAD_LIMIT 20000 | ||
| 111 | #endif | ||
| 112 | |||
| 113 | rbb | 89781 | /* |
| 114 | * Actual definitions of config globals | ||
| 115 | */ | ||
| 116 | |||
| 117 | jerenkrantz | 92473 | int ap_threads_per_child = 0; /* Worker threads per child */ |
| 118 | static int ap_daemons_to_start = 0; | ||
| 119 | static int min_spare_threads = 0; | ||
| 120 | static int max_spare_threads = 0; | ||
| 121 | trawick | 93528 | static int ap_daemons_limit = 0; |
| 122 | trawick | 92530 | static int server_limit = DEFAULT_SERVER_LIMIT; |
| 123 | colm | 293164 | static int first_server_limit = 0; |
| 124 | trawick | 92530 | static int thread_limit = DEFAULT_THREAD_LIMIT; |
| 125 | colm | 293164 | static int first_thread_limit = 0; |
| 126 | trawick | 92530 | static int changed_limit_at_restart; |
| 127 | rbb | 89781 | static int dying = 0; |
| 128 | static int workers_may_exit = 0; | ||
| 129 | trawick | 94095 | static int start_thread_may_exit = 0; |
| 130 | static int listener_may_exit = 0; | ||
| 131 | rbb | 89781 | static int requests_this_child; |
| 132 | static int num_listensocks = 0; | ||
| 133 | gregames | 93366 | static int resource_shortage = 0; |
| 134 | rbb | 90635 | static fd_queue_t *worker_queue; |
| 135 | aaron | 94824 | static fd_queue_info_t *worker_queue_info; |
| 136 | trawick | 102045 | static int mpm_state = AP_MPMQ_STARTING; |
| 137 | gregames | 168182 | static int sick_child_detected; |
| 138 | rbb | 89781 | |
| 139 | /* The structure used to pass unique initialization info to each thread */ | ||
| 140 | typedef struct { | ||
| 141 | int pid; | ||
| 142 | int tid; | ||
| 143 | int sd; | ||
| 144 | } proc_info; | ||
| 145 | |||
| 146 | /* Structure used to pass information to the thread responsible for | ||
| 147 | * creating the rest of the threads. | ||
| 148 | */ | ||
| 149 | typedef struct { | ||
| 150 | apr_thread_t **threads; | ||
| 151 | trawick | 94031 | apr_thread_t *listener; |
| 152 | rbb | 89781 | int child_num_arg; |
| 153 | apr_threadattr_t *threadattr; | ||
| 154 | } thread_starter; | ||
| 155 | |||
| 156 | trawick | 92530 | #define ID_FROM_CHILD_THREAD(c, t) ((c * thread_limit) + t) |
| 157 | trawick | 92512 | |
| 158 | rbb | 89781 | /* |
| 159 | * The max child slot ever assigned, preserved across restarts. Necessary | ||
| 160 | jerenkrantz | 91076 | * to deal with MaxClients changes across AP_SIG_GRACEFUL restarts. We |
| 161 | * use this value to optimize routines that have to scan the entire | ||
| 162 | * scoreboard. | ||
| 163 | rbb | 89781 | */ |
| 164 | int ap_max_daemons_limit = -1; | ||
| 165 | |||
| 166 | brianp | 93096 | static ap_pod_t *pod; |
| 167 | rbb | 89781 | |
| 168 | /* *Non*-shared http_main globals... */ | ||
| 169 | |||
| 170 | server_rec *ap_server_conf; | ||
| 171 | |||
| 172 | jwoolley | 90790 | /* The worker MPM respects a couple of runtime flags that can aid |
| 173 | * in debugging. Setting the -DNO_DETACH flag will prevent the root process | ||
| 174 | * from detaching from its controlling terminal. Additionally, setting | ||
| 175 | * the -DONE_PROCESS flag (which implies -DNO_DETACH) will get you the | ||
| 176 | * child_main loop running in the process which originally started up. | ||
| 177 | * This gives you a pretty nice debugging environment. (You'll get a SIGHUP | ||
| 178 | rbb | 89781 | * early in standalone_main; just continue through. This is the server |
| 179 | * trying to kill off any child processes which it might have lying | ||
| 180 | * around --- Apache doesn't keep track of their pids, it just sends | ||
| 181 | * SIGHUP to the process group, ignoring it in the root process. | ||
| 182 | * Continue through and you'll be fine.). | ||
| 183 | */ | ||
| 184 | |||
| 185 | static int one_process = 0; | ||
| 186 | |||
| 187 | #ifdef DEBUG_SIGSTOP | ||
| 188 | int raise_sigstop_flags; | ||
| 189 | #endif | ||
| 190 | |||
| 191 | jerenkrantz | 92473 | static apr_pool_t *pconf; /* Pool for config stuff */ |
| 192 | static apr_pool_t *pchild; /* Pool for httpd child stuff */ | ||
| 193 | rbb | 89781 | |
| 194 | static pid_t ap_my_pid; /* Linux getpid() doesn't work except in main | ||
| 195 | thread. Use this instead */ | ||
| 196 | gregames | 90064 | static pid_t parent_pid; |
| 197 | trawick | 94031 | static apr_os_thread_t *listener_os_thread; |
| 198 | rbb | 89781 | |
| 199 | /* Locks for accept serialization */ | ||
| 200 | aaron | 91580 | static apr_proc_mutex_t *accept_mutex; |
| 201 | rbb | 89781 | |
| 202 | brianp | 93096 | #ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT |
| 203 | #define SAFE_ACCEPT(stmt) (ap_listeners->next ? (stmt) : APR_SUCCESS) | ||
| 204 | rbb | 89781 | #else |
| 205 | #define SAFE_ACCEPT(stmt) (stmt) | ||
| 206 | #endif | ||
| 207 | |||
| 208 | trawick | 94031 | /* The LISTENER_SIGNAL signal will be sent from the main thread to the |
| 209 | * listener thread to wake it up for graceful termination (what a child | ||
| 210 | * process from an old generation does when the admin does "apachectl | ||
| 211 | * graceful"). This signal will be blocked in all threads of a child | ||
| 212 | * process except for the listener thread. | ||
| 213 | */ | ||
| 214 | #define LISTENER_SIGNAL SIGHUP | ||
| 215 | |||
| 216 | jerenkrantz | 94886 | /* An array of socket descriptors in use by each thread used to |
| 217 | * perform a non-graceful (forced) shutdown of the server. */ | ||
| 218 | static apr_socket_t **worker_sockets; | ||
| 219 | |||
| 220 | static void close_worker_sockets(void) | ||
| 221 | { | ||
| 222 | int i; | ||
| 223 | for (i = 0; i < ap_threads_per_child; i++) { | ||
| 224 | if (worker_sockets[i]) { | ||
| 225 | apr_socket_close(worker_sockets[i]); | ||
| 226 | worker_sockets[i] = NULL; | ||
| 227 | } | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | trawick | 94031 | static void wakeup_listener(void) |
| 232 | { | ||
| 233 | trawick | 94095 | listener_may_exit = 1; |
| 234 | trawick | 94420 | if (!listener_os_thread) { |
| 235 | /* XXX there is an obscure path that this doesn't handle perfectly: | ||
| 236 | * right after listener thread is created but before | ||
| 237 | * listener_os_thread is set, the first worker thread hits an | ||
| 238 | * error and starts graceful termination | ||
| 239 | */ | ||
| 240 | return; | ||
| 241 | } | ||
| 242 | trawick | 94031 | /* |
| 243 | trawick | 94417 | * we should just be able to "kill(ap_my_pid, LISTENER_SIGNAL)" on all |
| 244 | * platforms and wake up the listener thread since it is the only thread | ||
| 245 | * with SIGHUP unblocked, but that doesn't work on Linux | ||
| 246 | trawick | 94031 | */ |
| 247 | trawick | 94417 | #ifdef HAVE_PTHREAD_KILL |
| 248 | trawick | 94031 | pthread_kill(*listener_os_thread, LISTENER_SIGNAL); |
| 249 | trawick | 94417 | #else |
| 250 | kill(ap_my_pid, LISTENER_SIGNAL); | ||
| 251 | #endif | ||
| 252 | trawick | 94031 | } |
| 253 | |||
| 254 | trawick | 94232 | #define ST_INIT 0 |
| 255 | trawick | 94095 | #define ST_GRACEFUL 1 |
| 256 | #define ST_UNGRACEFUL 2 | ||
| 257 | |||
| 258 | trawick | 94232 | static int terminate_mode = ST_INIT; |
| 259 | |||
| 260 | trawick | 94095 | static void signal_threads(int mode) |
| 261 | rbb | 89827 | { |
| 262 | trawick | 94232 | if (terminate_mode == mode) { |
| 263 | trawick | 94106 | return; |
| 264 | } | ||
| 265 | trawick | 94232 | terminate_mode = mode; |
| 266 | trawick | 102045 | mpm_state = AP_MPMQ_STOPPING; |
| 267 | trawick | 94106 | |
| 268 | trawick | 94031 | /* in case we weren't called from the listener thread, wake up the |
| 269 | * listener thread | ||
| 270 | */ | ||
| 271 | wakeup_listener(); | ||
| 272 | |||
| 273 | trawick | 94106 | /* for ungraceful termination, let the workers exit now; |
| 274 | * for graceful termination, the listener thread will notify the | ||
| 275 | * workers to exit once it has stopped accepting new connections | ||
| 276 | */ | ||
| 277 | if (mode == ST_UNGRACEFUL) { | ||
| 278 | workers_may_exit = 1; | ||
| 279 | ap_queue_interrupt_all(worker_queue); | ||
| 280 | aaron | 94824 | ap_queue_info_term(worker_queue_info); |
| 281 | jerenkrantz | 94886 | close_worker_sockets(); /* forcefully kill all current connections */ |
| 282 | trawick | 94106 | } |
| 283 | rbb | 89827 | } |
| 284 | |||
| 285 | rbb | 89781 | AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result) |
| 286 | { | ||
| 287 | switch(query_code){ | ||
| 288 | case AP_MPMQ_MAX_DAEMON_USED: | ||
| 289 | *result = ap_max_daemons_limit; | ||
| 290 | return APR_SUCCESS; | ||
| 291 | case AP_MPMQ_IS_THREADED: | ||
| 292 | *result = AP_MPMQ_STATIC; | ||
| 293 | return APR_SUCCESS; | ||
| 294 | case AP_MPMQ_IS_FORKED: | ||
| 295 | *result = AP_MPMQ_DYNAMIC; | ||
| 296 | return APR_SUCCESS; | ||
| 297 | case AP_MPMQ_HARD_LIMIT_DAEMONS: | ||
| 298 | trawick | 92530 | *result = server_limit; |
| 299 | rbb | 89781 | return APR_SUCCESS; |
| 300 | case AP_MPMQ_HARD_LIMIT_THREADS: | ||
| 301 | trawick | 92530 | *result = thread_limit; |
| 302 | rbb | 89781 | return APR_SUCCESS; |
| 303 | case AP_MPMQ_MAX_THREADS: | ||
| 304 | *result = ap_threads_per_child; | ||
| 305 | return APR_SUCCESS; | ||
| 306 | jwoolley | 91777 | case AP_MPMQ_MIN_SPARE_DAEMONS: |
| 307 | rbb | 89781 | *result = 0; |
| 308 | return APR_SUCCESS; | ||
| 309 | case AP_MPMQ_MIN_SPARE_THREADS: | ||
| 310 | *result = min_spare_threads; | ||
| 311 | return APR_SUCCESS; | ||
| 312 | case AP_MPMQ_MAX_SPARE_DAEMONS: | ||
| 313 | *result = 0; | ||
| 314 | return APR_SUCCESS; | ||
| 315 | case AP_MPMQ_MAX_SPARE_THREADS: | ||
| 316 | *result = max_spare_threads; | ||
| 317 | return APR_SUCCESS; | ||
| 318 | jwoolley | 91777 | case AP_MPMQ_MAX_REQUESTS_DAEMON: |
| 319 | rbb | 89781 | *result = ap_max_requests_per_child; |
| 320 | return APR_SUCCESS; | ||
| 321 | case AP_MPMQ_MAX_DAEMONS: | ||
| 322 | *result = ap_daemons_limit; | ||
| 323 | return APR_SUCCESS; | ||
| 324 | trawick | 102045 | case AP_MPMQ_MPM_STATE: |
| 325 | *result = mpm_state; | ||
| 326 | return APR_SUCCESS; | ||
| 327 | rbb | 89781 | } |
| 328 | return APR_ENOTIMPL; | ||
| 329 | } | ||
| 330 | |||
| 331 | /* a clean exit from a child with proper cleanup */ | ||
| 332 | static void clean_child_exit(int code) __attribute__ ((noreturn)); | ||
| 333 | static void clean_child_exit(int code) | ||
| 334 | { | ||
| 335 | trawick | 102045 | mpm_state = AP_MPMQ_STOPPING; |
| 336 | rbb | 89781 | if (pchild) { |
| 337 | jerenkrantz | 92473 | apr_pool_destroy(pchild); |
| 338 | rbb | 89781 | } |
| 339 | exit(code); | ||
| 340 | } | ||
| 341 | |||
| 342 | static void just_die(int sig) | ||
| 343 | { | ||
| 344 | clean_child_exit(0); | ||
| 345 | } | ||
| 346 | |||
| 347 | /***************************************************************** | ||
| 348 | * Connection structures and accounting... | ||
| 349 | */ | ||
| 350 | |||
| 351 | /* volatile just in case */ | ||
| 352 | static int volatile shutdown_pending; | ||
| 353 | static int volatile restart_pending; | ||
| 354 | static int volatile is_graceful; | ||
| 355 | trawick | 92019 | static volatile int child_fatal; |
| 356 | rbb | 89781 | ap_generation_t volatile ap_my_generation; |
| 357 | |||
| 358 | /* | ||
| 359 | * ap_start_shutdown() and ap_start_restart(), below, are a first stab at | ||
| 360 | * functions to initiate shutdown or restart without relying on signals. | ||
| 361 | * Previously this was initiated in sig_term() and restart() signal handlers, | ||
| 362 | * but we want to be able to start a shutdown/restart from other sources -- | ||
| 363 | * e.g. on Win32, from the service manager. Now the service manager can | ||
| 364 | * call ap_start_shutdown() or ap_start_restart() as appropiate. Note that | ||
| 365 | * these functions can also be called by the child processes, since global | ||
| 366 | * variables are no longer used to pass on the required action to the parent. | ||
| 367 | * | ||
| 368 | * These should only be called from the parent process itself, since the | ||
| 369 | * parent process will use the shutdown_pending and restart_pending variables | ||
| 370 | * to determine whether to shutdown or restart. The child process should | ||
| 371 | * call signal_parent() directly to tell the parent to die -- this will | ||
| 372 | * cause neither of those variable to be set, which the parent will | ||
| 373 | * assume means something serious is wrong (which it will be, for the | ||
| 374 | * child to force an exit) and so do an exit anyway. | ||
| 375 | */ | ||
| 376 | |||
| 377 | colm | 290189 | static void ap_start_shutdown(int graceful) |
| 378 | rbb | 89781 | { |
| 379 | trawick | 102045 | mpm_state = AP_MPMQ_STOPPING; |
| 380 | rbb | 89781 | if (shutdown_pending == 1) { |
| 381 | jerenkrantz | 92473 | /* Um, is this _probably_ not an error, if the user has |
| 382 | * tried to do a shutdown twice quickly, so we won't | ||
| 383 | * worry about reporting it. | ||
| 384 | */ | ||
| 385 | return; | ||
| 386 | rbb | 89781 | } |
| 387 | shutdown_pending = 1; | ||
| 388 | colm | 290189 | is_graceful = graceful; |
| 389 | rbb | 89781 | } |
| 390 | |||
| 391 | /* do a graceful restart if graceful == 1 */ | ||
| 392 | static void ap_start_restart(int graceful) | ||
| 393 | { | ||
| 394 | trawick | 102045 | mpm_state = AP_MPMQ_STOPPING; |
| 395 | rbb | 89781 | if (restart_pending == 1) { |
| 396 | jerenkrantz | 92473 | /* Probably not an error - don't bother reporting it */ |
| 397 | return; | ||
| 398 | rbb | 89781 | } |
| 399 | restart_pending = 1; | ||
| 400 | is_graceful = graceful; | ||
| 401 | } | ||
| 402 | |||
| 403 | static void sig_term(int sig) | ||
| 404 | { | ||
| 405 | colm | 290189 | ap_start_shutdown(sig == AP_SIG_GRACEFUL_STOP); |
| 406 | rbb | 89781 | } |
| 407 | |||
| 408 | static void restart(int sig) | ||
| 409 | { | ||
| 410 | jerenkrantz | 91076 | ap_start_restart(sig == AP_SIG_GRACEFUL); |
| 411 | rbb | 89781 | } |
| 412 | |||
| 413 | static void set_signals(void) | ||
| 414 | { | ||
| 415 | #ifndef NO_USE_SIGACTION | ||
| 416 | struct sigaction sa; | ||
| 417 | trawick | 99312 | #endif |
| 418 | rbb | 89781 | |
| 419 | trawick | 99312 | if (!one_process) { |
| 420 | ap_fatal_signal_setup(ap_server_conf, pconf); | ||
| 421 | } | ||
| 422 | |||
| 423 | #ifndef NO_USE_SIGACTION | ||
| 424 | rbb | 89781 | sigemptyset(&sa.sa_mask); |
| 425 | sa.sa_flags = 0; | ||
| 426 | |||
| 427 | sa.sa_handler = sig_term; | ||
| 428 | if (sigaction(SIGTERM, &sa, NULL) < 0) | ||
| 429 | jerenkrantz | 92473 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, |
| 430 | "sigaction(SIGTERM)"); | ||
| 431 | colm | 290189 | #ifdef AP_SIG_GRACEFUL_STOP |
| 432 | if (sigaction(AP_SIG_GRACEFUL_STOP, &sa, NULL) < 0) | ||
| 433 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, | ||
| 434 | "sigaction(" AP_SIG_GRACEFUL_STOP_STRING ")"); | ||
| 435 | #endif | ||
| 436 | rbb | 89781 | #ifdef SIGINT |
| 437 | if (sigaction(SIGINT, &sa, NULL) < 0) | ||
| 438 | jerenkrantz | 92473 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, |
| 439 | "sigaction(SIGINT)"); | ||
| 440 | rbb | 89781 | #endif |
| 441 | #ifdef SIGXCPU | ||
| 442 | sa.sa_handler = SIG_DFL; | ||
| 443 | if (sigaction(SIGXCPU, &sa, NULL) < 0) | ||
| 444 | jerenkrantz | 92473 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, |
| 445 | "sigaction(SIGXCPU)"); | ||
| 446 | rbb | 89781 | #endif |
| 447 | #ifdef SIGXFSZ | ||
| 448 | sa.sa_handler = SIG_DFL; | ||
| 449 | if (sigaction(SIGXFSZ, &sa, NULL) < 0) | ||
| 450 | jerenkrantz | 92473 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, |
| 451 | "sigaction(SIGXFSZ)"); | ||
| 452 | rbb | 89781 | #endif |
| 453 | #ifdef SIGPIPE | ||
| 454 | sa.sa_handler = SIG_IGN; | ||
| 455 | if (sigaction(SIGPIPE, &sa, NULL) < 0) | ||
| 456 | jerenkrantz | 92473 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, |
| 457 | "sigaction(SIGPIPE)"); | ||
| 458 | rbb | 89781 | #endif |
| 459 | |||
| 460 | jerenkrantz | 91076 | /* we want to ignore HUPs and AP_SIG_GRACEFUL while we're busy |
| 461 | * processing one */ | ||
| 462 | rbb | 89781 | sigaddset(&sa.sa_mask, SIGHUP); |
| 463 | jerenkrantz | 91076 | sigaddset(&sa.sa_mask, AP_SIG_GRACEFUL); |
| 464 | rbb | 89781 | sa.sa_handler = restart; |
| 465 | if (sigaction(SIGHUP, &sa, NULL) < 0) | ||
| 466 | jerenkrantz | 92473 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, |
| 467 | "sigaction(SIGHUP)"); | ||
| 468 | jerenkrantz | 91076 | if (sigaction(AP_SIG_GRACEFUL, &sa, NULL) < 0) |
| 469 | jerenkrantz | 92473 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, |
| 470 | "sigaction(" AP_SIG_GRACEFUL_STRING ")"); | ||
| 471 | rbb | 89781 | #else |
| 472 | if (!one_process) { | ||
| 473 | #ifdef SIGXCPU | ||
| 474 | jerenkrantz | 92473 | apr_signal(SIGXCPU, SIG_DFL); |
| 475 | rbb | 89781 | #endif /* SIGXCPU */ |
| 476 | #ifdef SIGXFSZ | ||
| 477 | jerenkrantz | 92473 | apr_signal(SIGXFSZ, SIG_DFL); |
| 478 | rbb | 89781 | #endif /* SIGXFSZ */ |
| 479 | } | ||
| 480 | |||
| 481 | apr_signal(SIGTERM, sig_term); | ||
| 482 | #ifdef SIGHUP | ||
| 483 | apr_signal(SIGHUP, restart); | ||
| 484 | #endif /* SIGHUP */ | ||
| 485 | jerenkrantz | 91076 | #ifdef AP_SIG_GRACEFUL |
| 486 | apr_signal(AP_SIG_GRACEFUL, restart); | ||
| 487 | #endif /* AP_SIG_GRACEFUL */ | ||
| 488 | colm | 290189 | #ifdef AP_SIG_GRACEFUL_STOP |
| 489 | apr_signal(AP_SIG_GRACEFUL_STOP, sig_term); | ||
| 490 | #endif /* AP_SIG_GRACEFUL_STOP */ | ||
| 491 | rbb | 89781 | #ifdef SIGPIPE |
| 492 | apr_signal(SIGPIPE, SIG_IGN); | ||
| 493 | #endif /* SIGPIPE */ | ||
| 494 | |||
| 495 | #endif | ||
| 496 | } | ||
| 497 | |||
| 498 | /***************************************************************** | ||
| 499 | * Here follows a long bunch of generic server bookkeeping stuff... | ||
| 500 | */ | ||
| 501 | |||
| 502 | int ap_graceful_stop_signalled(void) | ||
| 503 | gregames | 89881 | /* XXX this is really a bad confusing obsolete name |
| 504 | * maybe it should be ap_mpm_process_exiting? | ||
| 505 | */ | ||
| 506 | rbb | 89781 | { |
| 507 | trawick | 94095 | /* note: for a graceful termination, listener_may_exit will be set before |
| 508 | * workers_may_exit, so check listener_may_exit | ||
| 509 | */ | ||
| 510 | return listener_may_exit; | ||
| 511 | rbb | 89781 | } |
| 512 | |||
| 513 | /***************************************************************** | ||
| 514 | * Child process main loop. | ||
| 515 | */ | ||
| 516 | |||
| 517 | jerenkrantz | 92473 | static void process_socket(apr_pool_t *p, apr_socket_t *sock, int my_child_num, |
| 518 | jwoolley | 94304 | int my_thread_num, apr_bucket_alloc_t *bucket_alloc) |
| 519 | rbb | 89781 | { |
| 520 | conn_rec *current_conn; | ||
| 521 | trawick | 92512 | long conn_id = ID_FROM_CHILD_THREAD(my_child_num, my_thread_num); |
| 522 | rbb | 89781 | int csd; |
| 523 | wrowe | 92791 | ap_sb_handle_t *sbh; |
| 524 | rbb | 89781 | |
| 525 | trawick | 92512 | ap_create_sb_handle(&sbh, p, my_child_num, my_thread_num); |
| 526 | jwoolley | 91082 | apr_os_sock_get(&csd, sock); |
| 527 | rbb | 89781 | |
| 528 | jwoolley | 94304 | current_conn = ap_run_create_connection(p, ap_server_conf, sock, |
| 529 | conn_id, sbh, bucket_alloc); | ||
| 530 | rbb | 89781 | if (current_conn) { |
| 531 | stoddard | 93087 | ap_process_connection(current_conn, sock); |
| 532 | rbb | 91968 | ap_lingering_close(current_conn); |
| 533 | rbb | 89781 | } |
| 534 | } | ||
| 535 | |||
| 536 | /* requests_this_child has gone to zero or below. See if the admin coded | ||
| 537 | "MaxRequestsPerChild 0", and keep going in that case. Doing it this way | ||
| 538 | simplifies the hot path in worker_thread */ | ||
| 539 | static void check_infinite_requests(void) | ||
| 540 | { | ||
| 541 | if (ap_max_requests_per_child) { | ||
| 542 | trawick | 94095 | signal_threads(ST_GRACEFUL); |
| 543 | rbb | 89781 | } |
| 544 | else { | ||
| 545 | /* wow! if you're executing this code, you may have set a record. | ||
| 546 | * either this child process has served over 2 billion requests, or | ||
| 547 | * you're running a threaded 2.0 on a 16 bit machine. | ||
| 548 | * | ||
| 549 | * I'll buy pizza and beers at Apachecon for the first person to do | ||
| 550 | * the former without cheating (dorking with INT_MAX, or running with | ||
| 551 | * uncommitted performance patches, for example). | ||
| 552 | * | ||
| 553 | * for the latter case, you probably deserve a beer too. Greg Ames | ||
| 554 | */ | ||
| 555 | |||
| 556 | requests_this_child = INT_MAX; /* keep going */ | ||
| 557 | } | ||
| 558 | } | ||
| 559 | |||
| 560 | trawick | 94232 | static void unblock_signal(int sig) |
| 561 | trawick | 94031 | { |
| 562 | trawick | 94232 | sigset_t sig_mask; |
| 563 | |||
| 564 | sigemptyset(&sig_mask); | ||
| 565 | sigaddset(&sig_mask, sig); | ||
| 566 | #if defined(SIGPROCMASK_SETS_THREAD_MASK) | ||
| 567 | sigprocmask(SIG_UNBLOCK, &sig_mask, NULL); | ||
| 568 | #else | ||
| 569 | pthread_sigmask(SIG_UNBLOCK, &sig_mask, NULL); | ||
| 570 | #endif | ||
| 571 | } | ||
| 572 | |||
| 573 | static void dummy_signal_handler(int sig) | ||
| 574 | { | ||
| 575 | trawick | 94031 | /* XXX If specifying SIG_IGN is guaranteed to unblock a syscall, |
| 576 | * then we don't need this goofy function. | ||
| 577 | */ | ||
| 578 | } | ||
| 579 | |||
| 580 | rbb | 89781 | static void *listener_thread(apr_thread_t *thd, void * dummy) |
| 581 | { | ||
| 582 | proc_info * ti = dummy; | ||
| 583 | int process_slot = ti->pid; | ||
| 584 | rbb | 90635 | apr_pool_t *tpool = apr_thread_pool_get(thd); |
| 585 | rbb | 91955 | void *csd = NULL; |
| 586 | trawick | 307222 | apr_pool_t *ptrans = NULL; /* Pool for per-transaction stuff */ |
| 587 | gstein | 101801 | apr_pollset_t *pollset; |
| 588 | rbb | 89781 | apr_status_t rv; |
| 589 | gstein | 101801 | ap_listen_rec *lr; |
| 590 | brianp | 95270 | int have_idle_worker = 0; |
| 591 | gstein | 101801 | int last_poll_idx = 0; |
| 592 | rbb | 89781 | |
| 593 | free(ti); | ||
| 594 | |||
| 595 | gstein | 101801 | /* ### check the status */ |
| 596 | (void) apr_pollset_create(&pollset, num_listensocks, tpool, 0); | ||
| 597 | rbb | 89781 | |
| 598 | gstein | 101801 | for (lr = ap_listeners; lr != NULL; lr = lr->next) { |
| 599 | apr_pollfd_t pfd = { 0 }; | ||
| 600 | |||
| 601 | pfd.desc_type = APR_POLL_SOCKET; | ||
| 602 | pfd.desc.s = lr->sd; | ||
| 603 | pfd.reqevents = APR_POLLIN; | ||
| 604 | pfd.client_data = lr; | ||
| 605 | |||
| 606 | /* ### check the status */ | ||
| 607 | (void) apr_pollset_add(pollset, &pfd); | ||
| 608 | } | ||
| 609 | |||
| 610 | trawick | 94031 | /* Unblock the signal used to wake this thread up, and set a handler for |
| 611 | * it. | ||
| 612 | */ | ||
| 613 | trawick | 94232 | unblock_signal(LISTENER_SIGNAL); |
| 614 | apr_signal(LISTENER_SIGNAL, dummy_signal_handler); | ||
| 615 | trawick | 94031 | |
| 616 | rbb | 89781 | /* TODO: Switch to a system where threads reuse the results from earlier |
| 617 | poll calls - manoj */ | ||
| 618 | while (1) { | ||
| 619 | rbb | 90635 | /* TODO: requests_this_child should be synchronized - aaron */ |
| 620 | rbb | 89781 | if (requests_this_child <= 0) { |
| 621 | check_infinite_requests(); | ||
| 622 | } | ||
| 623 | trawick | 94095 | if (listener_may_exit) break; |
| 624 | rbb | 89781 | |
| 625 | brianp | 95270 | if (!have_idle_worker) { |
| 626 | trawick | 307222 | /* the following pops a recycled ptrans pool off a stack |
| 627 | * if there is one, in addition to reserving a worker thread | ||
| 628 | */ | ||
| 629 | brianp | 95270 | rv = ap_queue_info_wait_for_idler(worker_queue_info, |
| 630 | trawick | 307222 | &ptrans); |
| 631 | brianp | 95270 | if (APR_STATUS_IS_EOF(rv)) { |
| 632 | break; /* we've been signaled to die now */ | ||
| 633 | } | ||
| 634 | else if (rv != APR_SUCCESS) { | ||
| 635 | ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, | ||
| 636 | "apr_queue_info_wait failed. Attempting to " | ||
| 637 | " shutdown process gracefully."); | ||
| 638 | signal_threads(ST_GRACEFUL); | ||
| 639 | break; | ||
| 640 | } | ||
| 641 | have_idle_worker = 1; | ||
| 642 | aaron | 94824 | } |
| 643 | brianp | 95270 | |
| 644 | aaron | 94824 | /* We've already decremented the idle worker count inside |
| 645 | * ap_queue_info_wait_for_idler. */ | ||
| 646 | |||
| 647 | aaron | 91580 | if ((rv = SAFE_ACCEPT(apr_proc_mutex_lock(accept_mutex))) |
| 648 | rbb | 89781 | != APR_SUCCESS) { |
| 649 | trawick | 93720 | int level = APLOG_EMERG; |
| 650 | |||
| 651 | trawick | 94095 | if (listener_may_exit) { |
| 652 | trawick | 94031 | break; |
| 653 | } | ||
| 654 | trawick | 93720 | if (ap_scoreboard_image->parent[process_slot].generation != |
| 655 | ap_scoreboard_image->global->running_generation) { | ||
| 656 | level = APLOG_DEBUG; /* common to get these at restart time */ | ||
| 657 | } | ||
| 658 | ap_log_error(APLOG_MARK, level, rv, ap_server_conf, | ||
| 659 | aaron | 91582 | "apr_proc_mutex_lock failed. Attempting to shutdown " |
| 660 | rbb | 89781 | "process gracefully."); |
| 661 | trawick | 94095 | signal_threads(ST_GRACEFUL); |
| 662 | trawick | 93718 | break; /* skip the lock release */ |
| 663 | rbb | 89781 | } |
| 664 | |||
| 665 | brianp | 93096 | if (!ap_listeners->next) { |
| 666 | /* Only one listener, so skip the poll */ | ||
| 667 | lr = ap_listeners; | ||
| 668 | } | ||
| 669 | else { | ||
| 670 | trawick | 94095 | while (!listener_may_exit) { |
| 671 | gstein | 101801 | apr_int32_t numdesc; |
| 672 | const apr_pollfd_t *pdesc; | ||
| 673 | rbb | 89781 | |
| 674 | gstein | 101801 | rv = apr_pollset_poll(pollset, -1, &numdesc, &pdesc); |
| 675 | if (rv != APR_SUCCESS) { | ||
| 676 | if (APR_STATUS_IS_EINTR(rv)) { | ||
| 677 | brianp | 93096 | continue; |
| 678 | } | ||
| 679 | |||
| 680 | trawick | 102045 | /* apr_pollset_poll() will only return errors in catastrophic |
| 681 | brianp | 93096 | * circumstances. Let's try exiting gracefully, for now. */ |
| 682 | gstein | 101801 | ap_log_error(APLOG_MARK, APLOG_ERR, rv, |
| 683 | (const server_rec *) ap_server_conf, | ||
| 684 | "apr_pollset_poll: (listen)"); | ||
| 685 | trawick | 94095 | signal_threads(ST_GRACEFUL); |
| 686 | rbb | 89781 | } |
| 687 | |||
| 688 | trawick | 94095 | if (listener_may_exit) break; |
| 689 | rbb | 89781 | |
| 690 | gstein | 101801 | /* We can always use pdesc[0], but sockets at position N |
| 691 | * could end up completely starved of attention in a very | ||
| 692 | * busy server. Therefore, we round-robin across the | ||
| 693 | * returned set of descriptors. While it is possible that | ||
| 694 | * the returned set of descriptors might flip around and | ||
| 695 | * continue to starve some sockets, we happen to know the | ||
| 696 | * internal pollset implementation retains ordering | ||
| 697 | * stability of the sockets. Thus, the round-robin should | ||
| 698 | * ensure that a socket will eventually be serviced. | ||
| 699 | */ | ||
| 700 | if (last_poll_idx >= numdesc) | ||
| 701 | last_poll_idx = 0; | ||
| 702 | |||
| 703 | /* Grab a listener record from the client_data of the poll | ||
| 704 | * descriptor, and advance our saved index to round-robin | ||
| 705 | * the next fetch. | ||
| 706 | * | ||
| 707 | * ### hmm... this descriptor might have POLLERR rather | ||
| 708 | * ### than POLLIN | ||
| 709 | */ | ||
| 710 | lr = pdesc[last_poll_idx++].client_data; | ||
| 711 | break; | ||
| 712 | |||
| 713 | } /* while */ | ||
| 714 | |||
| 715 | } /* if/else */ | ||
| 716 | |||
| 717 | trawick | 94095 | if (!listener_may_exit) { |
| 718 | trawick | 307222 | if (ptrans == NULL) { |
| 719 | /* we can't use a recycled transaction pool this time. | ||
| 720 | * create a new transaction pool */ | ||
| 721 | striker | 93943 | apr_allocator_t *allocator; |
| 722 | |||
| 723 | apr_allocator_create(&allocator); | ||
| 724 | striker | 95954 | apr_allocator_max_free_set(allocator, ap_max_mem_free); |
| 725 | jorton | 170896 | apr_pool_create_ex(&ptrans, pconf, NULL, allocator); |
| 726 | striker | 95373 | apr_allocator_owner_set(allocator, ptrans); |
| 727 | brianp | 93386 | } |
| 728 | brianp | 92482 | apr_pool_tag(ptrans, "transaction"); |
| 729 | rbb | 91960 | rv = lr->accept_func(&csd, lr, ptrans); |
| 730 | trawick | 94625 | /* later we trash rv and rely on csd to indicate success/failure */ |
| 731 | AP_DEBUG_ASSERT(rv == APR_SUCCESS || !csd); | ||
| 732 | rbb | 91955 | |
| 733 | if (rv == APR_EGENERAL) { | ||
| 734 | gregames | 93366 | /* E[NM]FILE, ENOMEM, etc */ |
| 735 | resource_shortage = 1; | ||
| 736 | trawick | 94095 | signal_threads(ST_GRACEFUL); |
| 737 | rbb | 89781 | } |
| 738 | aaron | 91580 | if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(accept_mutex))) |
| 739 | rbb | 89781 | != APR_SUCCESS) { |
| 740 | trawick | 93720 | int level = APLOG_EMERG; |
| 741 | |||
| 742 | trawick | 94095 | if (listener_may_exit) { |
| 743 | trawick | 94031 | break; |
| 744 | } | ||
| 745 | trawick | 93720 | if (ap_scoreboard_image->parent[process_slot].generation != |
| 746 | ap_scoreboard_image->global->running_generation) { | ||
| 747 | level = APLOG_DEBUG; /* common to get these at restart time */ | ||
| 748 | } | ||
| 749 | ap_log_error(APLOG_MARK, level, rv, ap_server_conf, | ||
| 750 | trawick | 93719 | "apr_proc_mutex_unlock failed. Attempting to " |
| 751 | aaron | 91582 | "shutdown process gracefully."); |
| 752 | trawick | 94095 | signal_threads(ST_GRACEFUL); |
| 753 | rbb | 89781 | } |
| 754 | if (csd != NULL) { | ||
| 755 | brianp | 94830 | rv = ap_queue_push(worker_queue, csd, ptrans); |
| 756 | trawick | 91089 | if (rv) { |
| 757 | /* trash the connection; we couldn't queue the connected | ||
| 758 | * socket to a worker | ||
| 759 | */ | ||
| 760 | apr_socket_close(csd); | ||
| 761 | trawick | 93530 | ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, |
| 762 | "ap_queue_push failed"); | ||
| 763 | trawick | 91089 | } |
| 764 | brianp | 95270 | else { |
| 765 | have_idle_worker = 0; | ||
| 766 | } | ||
| 767 | rbb | 89781 | } |
| 768 | } | ||
| 769 | else { | ||
| 770 | aaron | 91580 | if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(accept_mutex))) |
| 771 | rbb | 89781 | != APR_SUCCESS) { |
| 772 | trawick | 179317 | int level = APLOG_EMERG; |
| 773 | |||
| 774 | if (ap_scoreboard_image->parent[process_slot].generation != | ||
| 775 | ap_scoreboard_image->global->running_generation) { | ||
| 776 | level = APLOG_DEBUG; /* common to get these at restart time */ | ||
| 777 | } | ||
| 778 | ap_log_error(APLOG_MARK, level, rv, ap_server_conf, | ||
| 779 | aaron | 91582 | "apr_proc_mutex_unlock failed. Attempting to " |
| 780 | "shutdown process gracefully."); | ||
| 781 | trawick | 94095 | signal_threads(ST_GRACEFUL); |
| 782 | rbb | 89781 | } |
| 783 | break; | ||
| 784 | } | ||
| 785 | } | ||
| 786 | |||
| 787 | colm | 290179 | ap_close_listeners(); |
| 788 | trawick | 94106 | ap_queue_term(worker_queue); |
| 789 | rbb | 89781 | dying = 1; |
| 790 | rbb | 90065 | ap_scoreboard_image->parent[process_slot].quiescing = 1; |
| 791 | trawick | 94112 | |
| 792 | trawick | 94232 | /* wake up the main thread */ |
| 793 | rbb | 90065 | kill(ap_my_pid, SIGTERM); |
| 794 | rbb | 89781 | |
| 795 | rbb | 90635 | apr_thread_exit(thd, APR_SUCCESS); |
| 796 | rbb | 89781 | return NULL; |
| 797 | } | ||
| 798 | |||
| 799 | trawick | 94068 | /* XXX For ungraceful termination/restart, we definitely don't want to |
| 800 | * wait for active connections to finish but we may want to wait | ||
| 801 | * for idle workers to get out of the queue code and release mutexes, | ||
| 802 | * since those mutexes are cleaned up pretty soon and some systems | ||
| 803 | * may not react favorably (i.e., segfault) if operations are attempted | ||
| 804 | * on cleaned-up mutexes. | ||
| 805 | */ | ||
| 806 | wrowe | 93264 | static void * APR_THREAD_FUNC worker_thread(apr_thread_t *thd, void * dummy) |
| 807 | rbb | 89781 | { |
| 808 | proc_info * ti = dummy; | ||
| 809 | int process_slot = ti->pid; | ||
| 810 | int thread_slot = ti->tid; | ||
| 811 | apr_socket_t *csd = NULL; | ||
| 812 | jwoolley | 94304 | apr_bucket_alloc_t *bucket_alloc; |
| 813 | brianp | 93386 | apr_pool_t *last_ptrans = NULL; |
| 814 | jerenkrantz | 92473 | apr_pool_t *ptrans; /* Pool for per-transaction stuff */ |
| 815 | rbb | 90635 | apr_status_t rv; |
| 816 | brianp | 95270 | int is_idle = 0; |
| 817 | rbb | 89781 | |
| 818 | free(ti); | ||
| 819 | |||
| 820 | trawick | 156274 | ap_scoreboard_image->servers[process_slot][thread_slot].pid = ap_my_pid; |
| 821 | ap_scoreboard_image->servers[process_slot][thread_slot].generation = ap_my_generation; | ||
| 822 | trawick | 92512 | ap_update_child_status_from_indexes(process_slot, thread_slot, SERVER_STARTING, NULL); |
| 823 | jwoolley | 94304 | |
| 824 | rbb | 89781 | while (!workers_may_exit) { |
| 825 | brianp | 95270 | if (!is_idle) { |
| 826 | rv = ap_queue_info_set_idle(worker_queue_info, last_ptrans); | ||
| 827 | last_ptrans = NULL; | ||
| 828 | if (rv != APR_SUCCESS) { | ||
| 829 | ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, | ||
| 830 | "ap_queue_info_set_idle failed. Attempting to " | ||
| 831 | "shutdown process gracefully."); | ||
| 832 | signal_threads(ST_GRACEFUL); | ||
| 833 | break; | ||
| 834 | } | ||
| 835 | is_idle = 1; | ||
| 836 | aaron | 94824 | } |
| 837 | |||
| 838 | trawick | 92512 | ap_update_child_status_from_indexes(process_slot, thread_slot, SERVER_READY, NULL); |
| 839 | aaron | 94840 | worker_pop: |
| 840 | if (workers_may_exit) { | ||
| 841 | break; | ||
| 842 | } | ||
| 843 | brianp | 94830 | rv = ap_queue_pop(worker_queue, &csd, &ptrans); |
| 844 | brianp | 93386 | |
| 845 | trawick | 94067 | if (rv != APR_SUCCESS) { |
| 846 | trawick | 94106 | /* We get APR_EOF during a graceful shutdown once all the connections |
| 847 | * accepted by this server process have been handled. | ||
| 848 | */ | ||
| 849 | aaron | 94840 | if (APR_STATUS_IS_EOF(rv)) { |
| 850 | trawick | 94106 | break; |
| 851 | } | ||
| 852 | trawick | 94067 | /* We get APR_EINTR whenever ap_queue_pop() has been interrupted |
| 853 | * from an explicit call to ap_queue_interrupt_all(). This allows | ||
| 854 | * us to unblock threads stuck in ap_queue_pop() when a shutdown | ||
| 855 | * is pending. | ||
| 856 | trawick | 94068 | * |
| 857 | * If workers_may_exit is set and this is ungraceful termination/ | ||
| 858 | * restart, we are bound to get an error on some systems (e.g., | ||
| 859 | * AIX, which sanity-checks mutex operations) since the queue | ||
| 860 | * may have already been cleaned up. Don't log the "error" if | ||
| 861 | * workers_may_exit is set. | ||
| 862 | trawick | 94067 | */ |
| 863 | aaron | 94840 | else if (APR_STATUS_IS_EINTR(rv)) { |
| 864 | goto worker_pop; | ||
| 865 | } | ||
| 866 | /* We got some other error. */ | ||
| 867 | else if (!workers_may_exit) { | ||
| 868 | trawick | 94067 | ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, |
| 869 | "ap_queue_pop failed"); | ||
| 870 | } | ||
| 871 | rbb | 90065 | continue; |
| 872 | } | ||
| 873 | brianp | 95270 | is_idle = 0; |
| 874 | jerenkrantz | 94886 | worker_sockets[thread_slot] = csd; |
| 875 | jwoolley | 101122 | bucket_alloc = apr_bucket_alloc_create(ptrans); |
| 876 | jwoolley | 94304 | process_socket(ptrans, csd, process_slot, thread_slot, bucket_alloc); |
| 877 | jerenkrantz | 94886 | worker_sockets[thread_slot] = NULL; |
| 878 | rbb | 90635 | requests_this_child--; /* FIXME: should be synchronized - aaron */ |
| 879 | brianp | 93386 | apr_pool_clear(ptrans); |
| 880 | last_ptrans = ptrans; | ||
| 881 | rbb | 89781 | } |
| 882 | |||
| 883 | trawick | 92512 | ap_update_child_status_from_indexes(process_slot, thread_slot, |
| 884 | rbb | 90771 | (dying) ? SERVER_DEAD : SERVER_GRACEFUL, (request_rec *) NULL); |
| 885 | rbb | 89781 | |
| 886 | aaron | 92622 | apr_thread_exit(thd, APR_SUCCESS); |
| 887 | rbb | 89781 | return NULL; |
| 888 | } | ||
| 889 | |||
| 890 | aaron | 93403 | static int check_signal(int signum) |
| 891 | { | ||
| 892 | switch (signum) { | ||
| 893 | case SIGTERM: | ||
| 894 | case SIGINT: | ||
| 895 | return 1; | ||
| 896 | } | ||
| 897 | return 0; | ||
| 898 | } | ||
| 899 | |||
| 900 | trawick | 94420 | static void create_listener_thread(thread_starter *ts) |
| 901 | { | ||
| 902 | int my_child_num = ts->child_num_arg; | ||
| 903 | apr_threadattr_t *thread_attr = ts->threadattr; | ||
| 904 | proc_info *my_info; | ||
| 905 | apr_status_t rv; | ||
| 906 | |||
| 907 | my_info = (proc_info *)malloc(sizeof(proc_info)); | ||
| 908 | my_info->pid = my_child_num; | ||
| 909 | my_info->tid = -1; /* listener thread doesn't have a thread slot */ | ||
| 910 | my_info->sd = 0; | ||
| 911 | rv = apr_thread_create(&ts->listener, thread_attr, listener_thread, | ||
| 912 | trawick | 94700 | my_info, pchild); |
| 913 | trawick | 94420 | if (rv != APR_SUCCESS) { |
| 914 | ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, | ||
| 915 | "apr_thread_create: unable to create listener thread"); | ||
| 916 | gregames | 168182 | /* let the parent decide how bad this really is */ |
| 917 | clean_child_exit(APEXIT_CHILDSICK); | ||
| 918 | trawick | 94420 | } |
| 919 | apr_os_thread_get(&listener_os_thread, ts->listener); | ||
| 920 | } | ||
| 921 | |||
| 922 | trawick | 94059 | /* XXX under some circumstances not understood, children can get stuck |
| 923 | * in start_threads forever trying to take over slots which will | ||
| 924 | * never be cleaned up; for now there is an APLOG_DEBUG message issued | ||
| 925 | * every so often when this condition occurs | ||
| 926 | trawick | 94030 | */ |
| 927 | wrowe | 93264 | static void * APR_THREAD_FUNC start_threads(apr_thread_t *thd, void *dummy) |
| 928 | rbb | 89781 | { |
| 929 | thread_starter *ts = dummy; | ||
| 930 | apr_thread_t **threads = ts->threads; | ||
| 931 | apr_threadattr_t *thread_attr = ts->threadattr; | ||
| 932 | int child_num_arg = ts->child_num_arg; | ||
| 933 | int my_child_num = child_num_arg; | ||
| 934 | trawick | 94420 | proc_info *my_info; |
| 935 | rbb | 89781 | apr_status_t rv; |
| 936 | trawick | 94420 | int i; |
| 937 | rbb | 89781 | int threads_created = 0; |
| 938 | stoddard | 94889 | int listener_started = 0; |
| 939 | trawick | 94059 | int loops; |
| 940 | int prev_threads_created; | ||
| 941 | rbb | 89781 | |
| 942 | rbb | 90771 | /* We must create the fd queues before we start up the listener |
| 943 | rbb | 90635 | * and worker threads. */ |
| 944 | rbb | 91075 | worker_queue = apr_pcalloc(pchild, sizeof(*worker_queue)); |
| 945 | trawick | 93530 | rv = ap_queue_init(worker_queue, ap_threads_per_child, pchild); |
| 946 | if (rv != APR_SUCCESS) { | ||
| 947 | ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, | ||
| 948 | "ap_queue_init() failed"); | ||
| 949 | clean_child_exit(APEXIT_CHILDFATAL); | ||
| 950 | } | ||
| 951 | rbb | 90635 | |
| 952 | brianp | 94830 | rv = ap_queue_info_create(&worker_queue_info, pchild, |
| 953 | ap_threads_per_child); | ||
| 954 | aaron | 94824 | if (rv != APR_SUCCESS) { |
| 955 | ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, | ||
| 956 | "ap_queue_info_create() failed"); | ||
| 957 | clean_child_exit(APEXIT_CHILDFATAL); | ||
| 958 | } | ||
| 959 | |||
| 960 | jerenkrantz | 94886 | worker_sockets = apr_pcalloc(pchild, ap_threads_per_child |
| 961 | * sizeof(apr_socket_t *)); | ||
| 962 | |||
| 963 | trawick | 94059 | loops = prev_threads_created = 0; |
| 964 | rbb | 89781 | while (1) { |
| 965 | dougm | 90673 | /* ap_threads_per_child does not include the listener thread */ |
| 966 | dougm | 90670 | for (i = 0; i < ap_threads_per_child; i++) { |
| 967 | rbb | 89781 | int status = ap_scoreboard_image->servers[child_num_arg][i].status; |
| 968 | |||
| 969 | if (status != SERVER_GRACEFUL && status != SERVER_DEAD) { | ||
| 970 | continue; | ||
| 971 | } | ||
| 972 | |||
| 973 | aaron | 92632 | my_info = (proc_info *)malloc(sizeof(proc_info)); |
| 974 | rbb | 89781 | if (my_info == NULL) { |
| 975 | ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf, | ||
| 976 | jerenkrantz | 92473 | "malloc: out of memory"); |
| 977 | rbb | 89781 | clean_child_exit(APEXIT_CHILDFATAL); |
| 978 | } | ||
| 979 | jerenkrantz | 92473 | my_info->pid = my_child_num; |
| 980 | rbb | 89781 | my_info->tid = i; |
| 981 | jerenkrantz | 92473 | my_info->sd = 0; |
| 982 | |||
| 983 | aaron | 92632 | /* We are creating threads right now */ |
| 984 | ap_update_child_status_from_indexes(my_child_num, i, | ||
| 985 | SERVER_STARTING, NULL); | ||
| 986 | rbb | 89781 | /* We let each thread update its own scoreboard entry. This is |
| 987 | * done because it lets us deal with tid better. | ||
| 988 | jerenkrantz | 92473 | */ |
| 989 | aaron | 92632 | rv = apr_thread_create(&threads[i], thread_attr, |
| 990 | trawick | 94700 | worker_thread, my_info, pchild); |
| 991 | aaron | 92632 | if (rv != APR_SUCCESS) { |
| 992 | jerenkrantz | 92473 | ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, |
| 993 | aaron | 92632 | "apr_thread_create: unable to create worker thread"); |
| 994 | gregames | 168182 | /* let the parent decide how bad this really is */ |
| 995 | clean_child_exit(APEXIT_CHILDSICK); | ||
| 996 | jerenkrantz | 92473 | } |
| 997 | rbb | 89781 | threads_created++; |
| 998 | } | ||
| 999 | stoddard | 94889 | /* Start the listener only when there are workers available */ |
| 1000 | if (!listener_started && threads_created) { | ||
| 1001 | create_listener_thread(ts); | ||
| 1002 | listener_started = 1; | ||
| 1003 | } | ||
| 1004 | trawick | 94095 | if (start_thread_may_exit || threads_created == ap_threads_per_child) { |
| 1005 | rbb | 89781 | break; |
| 1006 | } | ||
| 1007 | wrowe | 93264 | /* wait for previous generation to clean up an entry */ |
| 1008 | brianp | 95959 | apr_sleep(apr_time_from_sec(1)); |
| 1009 | trawick | 94059 | ++loops; |
| 1010 | if (loops % 120 == 0) { /* every couple of minutes */ | ||
| 1011 | if (prev_threads_created == threads_created) { | ||
| 1012 | ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, | ||
| 1013 | "child %" APR_PID_T_FMT " isn't taking over " | ||
| 1014 | "slots very quickly (%d of %d)", | ||
| 1015 | ap_my_pid, threads_created, ap_threads_per_child); | ||
| 1016 | } | ||
| 1017 | prev_threads_created = threads_created; | ||
| 1018 | } | ||
| 1019 | rbb | 89781 | } |
| 1020 | |||
| 1021 | jerenkrantz | 92473 | /* What state should this child_main process be listed as in the |
| 1022 | * scoreboard...? | ||
| 1023 | trawick | 92512 | * ap_update_child_status_from_indexes(my_child_num, i, SERVER_STARTING, |
| 1024 | * (request_rec *) NULL); | ||
| 1025 | rbb | 89781 | * |
| 1026 | * This state should be listed separately in the scoreboard, in some kind | ||
| 1027 | * of process_status, not mixed in with the worker threads' status. | ||
| 1028 | * "life_status" is almost right, but it's in the worker's structure, and | ||
| 1029 | * the name could be clearer. gla | ||
| 1030 | */ | ||
| 1031 | aaron | 92622 | apr_thread_exit(thd, APR_SUCCESS); |
| 1032 | rbb | 89781 | return NULL; |
| 1033 | } | ||
| 1034 | |||
| 1035 | trawick | 94031 | static void join_workers(apr_thread_t *listener, apr_thread_t **threads) |
| 1036 | trawick | 93561 | { |
| 1037 | int i; | ||
| 1038 | apr_status_t rv, thread_rv; | ||
| 1039 | |||
| 1040 | trawick | 94031 | if (listener) { |
| 1041 | int iter; | ||
| 1042 | |||
| 1043 | /* deal with a rare timing window which affects waking up the | ||
| 1044 | * listener thread... if the signal sent to the listener thread | ||
| 1045 | * is delivered between the time it verifies that the | ||
| 1046 | trawick | 94095 | * listener_may_exit flag is clear and the time it enters a |
| 1047 | trawick | 94031 | * blocking syscall, the signal didn't do any good... work around |
| 1048 | * that by sleeping briefly and sending it again | ||
| 1049 | */ | ||
| 1050 | |||
| 1051 | iter = 0; | ||
| 1052 | trawick | 94417 | while (iter < 10 && |
| 1053 | #ifdef HAVE_PTHREAD_KILL | ||
| 1054 | pthread_kill(*listener_os_thread, 0) | ||
| 1055 | #else | ||
| 1056 | kill(ap_my_pid, 0) | ||
| 1057 | #endif | ||
| 1058 | == 0) { | ||
| 1059 | trawick | 94031 | /* listener not dead yet */ |
| 1060 | brianp | 95959 | apr_sleep(apr_time_make(0, 500000)); |
| 1061 | trawick | 94031 | wakeup_listener(); |
| 1062 | ++iter; | ||
| 1063 | } | ||
| 1064 | if (iter >= 10) { | ||
| 1065 | trawick | 101165 | ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, |
| 1066 | trawick | 94031 | "the listener thread didn't exit"); |
| 1067 | } | ||
| 1068 | else { | ||
| 1069 | rv = apr_thread_join(&thread_rv, listener); | ||
| 1070 | if (rv != APR_SUCCESS) { | ||
| 1071 | ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, | ||
| 1072 | "apr_thread_join: unable to join listener thread"); | ||
| 1073 | } | ||
| 1074 | } | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | trawick | 93561 | for (i = 0; i < ap_threads_per_child; i++) { |
| 1078 | if (threads[i]) { /* if we ever created this thread */ | ||
| 1079 | rv = apr_thread_join(&thread_rv, threads[i]); | ||
| 1080 | if (rv != APR_SUCCESS) { | ||
| 1081 | ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, | ||
| 1082 | "apr_thread_join: unable to join worker " | ||
| 1083 | "thread %d", | ||
| 1084 | i); | ||
| 1085 | } | ||
| 1086 | } | ||
| 1087 | } | ||
| 1088 | } | ||
| 1089 | |||
| 1090 | static void join_start_thread(apr_thread_t *start_thread_id) | ||
| 1091 | { | ||
| 1092 | apr_status_t rv, thread_rv; | ||
| 1093 | |||
| 1094 | trawick | 94095 | start_thread_may_exit = 1; /* tell it to give up in case it is still |
| 1095 | * trying to take over slots from a | ||
| 1096 | * previous generation | ||
| 1097 | */ | ||
| 1098 | trawick | 93561 | rv = apr_thread_join(&thread_rv, start_thread_id); |
| 1099 | if (rv != APR_SUCCESS) { | ||
| 1100 | ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, | ||
| 1101 | "apr_thread_join: unable to join the start " | ||
| 1102 | "thread"); | ||
| 1103 | } | ||
| 1104 | } | ||
| 1105 | |||
| 1106 | rbb | 89781 | static void child_main(int child_num_arg) |
| 1107 | { | ||
| 1108 | apr_thread_t **threads; | ||
| 1109 | apr_status_t rv; | ||
| 1110 | thread_starter *ts; | ||
| 1111 | apr_threadattr_t *thread_attr; | ||
| 1112 | apr_thread_t *start_thread_id; | ||
| 1113 | |||
| 1114 | trawick | 102045 | mpm_state = AP_MPMQ_STARTING; /* for benefit of any hooks that run as this |
| 1115 | * child initializes | ||
| 1116 | */ | ||
| 1117 | rbb | 89781 | ap_my_pid = getpid(); |
| 1118 | trawick | 99312 | ap_fatal_signal_child_setup(ap_server_conf); |
| 1119 | rbb | 89781 | apr_pool_create(&pchild, pconf); |
| 1120 | |||
| 1121 | /*stuff to do before we switch id's, so we have permissions.*/ | ||
| 1122 | rbb | 93119 | ap_reopen_scoreboard(pchild, NULL, 0); |
| 1123 | rbb | 89781 | |
| 1124 | trawick | 92412 | rv = SAFE_ACCEPT(apr_proc_mutex_child_init(&accept_mutex, ap_lock_fname, |
| 1125 | aaron | 91580 | pchild)); |
| 1126 | rbb | 89781 | if (rv != APR_SUCCESS) { |
| 1127 | ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, | ||
| 1128 | "Couldn't initialize cross-process lock in child"); | ||
| 1129 | clean_child_exit(APEXIT_CHILDFATAL); | ||
| 1130 | } | ||
| 1131 | |||
| 1132 | if (unixd_setup_child()) { | ||
| 1133 | jerenkrantz | 92473 | clean_child_exit(APEXIT_CHILDFATAL); |
| 1134 | rbb | 89781 | } |
| 1135 | |||
| 1136 | ap_run_child_init(pchild, ap_server_conf); | ||
| 1137 | |||
| 1138 | rbb | 90771 | /* done with init critical section */ |
| 1139 | rbb | 89781 | |
| 1140 | rbb | 93358 | /* Just use the standard apr_setup_signal_thread to block all signals |
| 1141 | * from being received. The child processes no longer use signals for | ||
| 1142 | * any communication with the parent process. | ||
| 1143 | */ | ||
| 1144 | rbb | 89781 | rv = apr_setup_signal_thread(); |
| 1145 | if (rv != APR_SUCCESS) { | ||
| 1146 | ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, | ||
| 1147 | "Couldn't initialize signal thread"); | ||
| 1148 | clean_child_exit(APEXIT_CHILDFATAL); | ||
| 1149 | } | ||
| 1150 | |||
| 1151 | if (ap_max_requests_per_child) { | ||
| 1152 | requests_this_child = ap_max_requests_per_child; | ||
| 1153 | } | ||
| 1154 | else { | ||
| 1155 | /* coding a value of zero means infinity */ | ||
| 1156 | requests_this_child = INT_MAX; | ||
| 1157 | } | ||
| 1158 | |||
| 1159 | /* Setup worker threads */ | ||
| 1160 | |||
| 1161 | jerenkrantz | 92473 | /* clear the storage; we may not create all our threads immediately, |
| 1162 | * and we want a 0 entry to indicate a thread which was not created | ||
| 1163 | rbb | 89781 | */ |
| 1164 | jerenkrantz | 92473 | threads = (apr_thread_t **)calloc(1, |
| 1165 | sizeof(apr_thread_t *) * ap_threads_per_child); | ||
| 1166 | rbb | 89781 | if (threads == NULL) { |
| 1167 | ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf, | ||
| 1168 | "malloc: out of memory"); | ||
| 1169 | clean_child_exit(APEXIT_CHILDFATAL); | ||
| 1170 | } | ||
| 1171 | |||
| 1172 | rbb | 90635 | ts = (thread_starter *)apr_palloc(pchild, sizeof(*ts)); |
| 1173 | rbb | 89781 | |
| 1174 | apr_threadattr_create(&thread_attr, pchild); | ||
| 1175 | jerenkrantz | 92473 | /* 0 means PTHREAD_CREATE_JOINABLE */ |
| 1176 | apr_threadattr_detach_set(thread_attr, 0); | ||
| 1177 | rbb | 89781 | |
| 1178 | trawick | 102975 | if (ap_thread_stacksize != 0) { |
| 1179 | apr_threadattr_stacksize_set(thread_attr, ap_thread_stacksize); | ||
| 1180 | trawick | 102931 | } |
| 1181 | |||
| 1182 | rbb | 89781 | ts->threads = threads; |
| 1183 | trawick | 94031 | ts->listener = NULL; |
| 1184 | rbb | 89781 | ts->child_num_arg = child_num_arg; |
| 1185 | ts->threadattr = thread_attr; | ||
| 1186 | |||
| 1187 | aaron | 92632 | rv = apr_thread_create(&start_thread_id, thread_attr, start_threads, |
| 1188 | trawick | 94700 | ts, pchild); |
| 1189 | aaron | 92632 | if (rv != APR_SUCCESS) { |
| 1190 | rbb | 89781 | ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, |
| 1191 | "apr_thread_create: unable to create worker thread"); | ||
| 1192 | gregames | 168649 | /* let the parent decide how bad this really is */ |
| 1193 | clean_child_exit(APEXIT_CHILDSICK); | ||
| 1194 | rbb | 89781 | } |
| 1195 | |||
| 1196 | trawick | 102045 | mpm_state = AP_MPMQ_RUNNING; |
| 1197 | |||
| 1198 | aaron | 93403 | /* If we are only running in one_process mode, we will want to |
| 1199 | * still handle signals. */ | ||
| 1200 | if (one_process) { | ||
| 1201 | trawick | 94091 | /* Block until we get a terminating signal. */ |
| 1202 | aaron | 93403 | apr_signal_thread(check_signal); |
| 1203 | trawick | 94095 | /* make sure the start thread has finished; signal_threads() |
| 1204 | trawick | 93561 | * and join_workers() depend on that |
| 1205 | */ | ||
| 1206 | trawick | 94094 | /* XXX join_start_thread() won't be awakened if one of our |
| 1207 | * threads encounters a critical error and attempts to | ||
| 1208 | * shutdown this child | ||
| 1209 | */ | ||
| 1210 | trawick | 93561 | join_start_thread(start_thread_id); |
| 1211 | trawick | 94095 | signal_threads(ST_UNGRACEFUL); /* helps us terminate a little more |
| 1212 | * quickly than the dispatch of the signal thread | ||
| 1213 | aaron | 93403 | * beats the Pipe of Death and the browsers |
| 1214 | */ | ||
| 1215 | /* A terminating signal was received. Now join each of the | ||
| 1216 | * workers to clean them up. | ||
| 1217 | * If the worker already exited, then the join frees | ||
| 1218 | * their resources and returns. | ||
| 1219 | * If the worker hasn't exited, then this blocks until | ||
| 1220 | * they have (then cleans up). | ||
| 1221 | rbb | 93358 | */ |
| 1222 | trawick | 94031 | join_workers(ts->listener, threads); |
| 1223 | rbb | 89781 | } |
| 1224 | aaron | 93403 | else { /* !one_process */ |
| 1225 | trawick | 94232 | /* remove SIGTERM from the set of blocked signals... if one of |
| 1226 | * the other threads in the process needs to take us down | ||
| 1227 | * (e.g., for MaxRequestsPerChild) it will send us SIGTERM | ||
| 1228 | */ | ||
| 1229 | unblock_signal(SIGTERM); | ||
| 1230 | apr_signal(SIGTERM, dummy_signal_handler); | ||
| 1231 | aaron | 93403 | /* Watch for any messages from the parent over the POD */ |
| 1232 | while (1) { | ||
| 1233 | rv = ap_mpm_pod_check(pod); | ||
| 1234 | trawick | 94232 | if (rv == AP_NORESTART) { |
| 1235 | /* see if termination was triggered while we slept */ | ||
| 1236 | switch(terminate_mode) { | ||
| 1237 | case ST_GRACEFUL: | ||
| 1238 | rv = AP_GRACEFUL; | ||
| 1239 | break; | ||
| 1240 | case ST_UNGRACEFUL: | ||
| 1241 | rv = AP_RESTART; | ||
| 1242 | break; | ||
| 1243 | } | ||
| 1244 | } | ||
| 1245 | aaron | 93403 | if (rv == AP_GRACEFUL || rv == AP_RESTART) { |
| 1246 | trawick | 93561 | /* make sure the start thread has finished; |
| 1247 | trawick | 94095 | * signal_threads() and join_workers depend on that |
| 1248 | trawick | 93561 | */ |
| 1249 | join_start_thread(start_thread_id); | ||
| 1250 | trawick | 94095 | signal_threads(rv == AP_GRACEFUL ? ST_GRACEFUL : ST_UNGRACEFUL); |
| 1251 | aaron | 93403 | break; |
| 1252 | } | ||
| 1253 | } | ||
| 1254 | rbb | 89781 | |
| 1255 | trawick | 94892 | /* A terminating signal was received. Now join each of the |
| 1256 | * workers to clean them up. | ||
| 1257 | * If the worker already exited, then the join frees | ||
| 1258 | * their resources and returns. | ||
| 1259 | * If the worker hasn't exited, then this blocks until | ||
| 1260 | * they have (then cleans up). | ||
| 1261 | */ | ||
| 1262 | join_workers(ts->listener, threads); | ||
| 1263 | aaron | 93403 | } |
| 1264 | |||
| 1265 | rbb | 89781 | free(threads); |
| 1266 | |||
| 1267 | gregames | 93366 | clean_child_exit(resource_shortage ? APEXIT_CHILDSICK : 0); |
| 1268 | rbb | 89781 | } |
| 1269 | |||
| 1270 | static int make_child(server_rec *s, int slot) | ||
| 1271 | { | ||
| 1272 | int pid; | ||
| 1273 | |||
| 1274 | if (slot + 1 > ap_max_daemons_limit) { | ||
| 1275 | jerenkrantz | 92473 | ap_max_daemons_limit = slot + 1; |
| 1276 | rbb | 89781 | } |
| 1277 | |||
| 1278 | if (one_process) { | ||
| 1279 | jerenkrantz | 92473 | set_signals(); |
| 1280 | rbb | 89781 | ap_scoreboard_image->parent[slot].pid = getpid(); |
| 1281 | jerenkrantz | 92473 | child_main(slot); |
| 1282 | rbb | 89781 | } |
| 1283 | |||
| 1284 | if ((pid = fork()) == -1) { | ||
| 1285 | jerenkrantz | 92473 | ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, |
| 1286 | "fork: Unable to fork new process"); | ||
| 1287 | rbb | 89781 | |
| 1288 | /* fork didn't succeed. Fix the scoreboard or else | ||
| 1289 | * it will say SERVER_STARTING forever and ever | ||
| 1290 | */ | ||
| 1291 | trawick | 92512 | ap_update_child_status_from_indexes(slot, 0, SERVER_DEAD, NULL); |
| 1292 | rbb | 89781 | |
| 1293 | jerenkrantz | 92473 | /* In case system resources are maxxed out, we don't want |
| 1294 | Apache running away with the CPU trying to fork over and | ||
| 1295 | over and over again. */ | ||
| 1296 | brianp | 95959 | apr_sleep(apr_time_from_sec(10)); |
| 1297 | rbb | 89781 | |
| 1298 | jerenkrantz | 92473 | return -1; |
| 1299 | rbb | 89781 | } |
| 1300 | |||
| 1301 | if (!pid) { | ||
| 1302 | #ifdef HAVE_BINDPROCESSOR | ||
| 1303 | /* By default, AIX binds to a single processor. This bit unbinds | ||
| 1304 | jerenkrantz | 92473 | * children which will then bind to another CPU. |
| 1305 | rbb | 89781 | */ |
| 1306 | int status = bindprocessor(BINDPROCESS, (int)getpid(), | ||
| 1307 | jerenkrantz | 92473 | PROCESSOR_CLASS_ANY); |
| 1308 | if (status != OK) | ||
| 1309 | trawick | 95149 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, |
| 1310 | jerenkrantz | 92473 | ap_server_conf, |
| 1311 | "processor unbind failed %d", status); | ||
| 1312 | rbb | 89781 | #endif |
| 1313 | RAISE_SIGSTOP(MAKE_CHILD); | ||
| 1314 | |||
| 1315 | apr_signal(SIGTERM, just_die); | ||
| 1316 | child_main(slot); | ||
| 1317 | |||
| 1318 | clean_child_exit(0); | ||
| 1319 | } | ||
| 1320 | /* else */ | ||
| 1321 | trawick | 109510 | if (ap_scoreboard_image->parent[slot].pid != 0) { |
| 1322 | /* This new child process is squatting on the scoreboard | ||
| 1323 | * entry owned by an exiting child process, which cannot | ||
| 1324 | * exit until all active requests complete. | ||
| 1325 | * Don't forget about this exiting child process, or we | ||
| 1326 | * won't be able to kill it if it doesn't exit by the | ||
| 1327 | * time the server is shut down. | ||
| 1328 | */ | ||
| 1329 | ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, | ||
| 1330 | "taking over scoreboard slot from %" APR_PID_T_FMT "%s", | ||
| 1331 | ap_scoreboard_image->parent[slot].pid, | ||
| 1332 | ap_scoreboard_image->parent[slot].quiescing ? | ||
| 1333 | " (quiescing)" : ""); | ||
| 1334 | ap_register_extra_mpm_process(ap_scoreboard_image->parent[slot].pid); | ||
| 1335 | } | ||
| 1336 | rbb | 89781 | ap_scoreboard_image->parent[slot].quiescing = 0; |
| 1337 | ap_scoreboard_image->parent[slot].pid = pid; | ||
| 1338 | return 0; | ||
| 1339 | } | ||
| 1340 | |||
| 1341 | /* start up a bunch of children */ | ||
| 1342 | static void startup_children(int number_to_start) | ||
| 1343 | { | ||
| 1344 | int i; | ||
| 1345 | |||
| 1346 | for (i = 0; number_to_start && i < ap_daemons_limit; ++i) { | ||
| 1347 | jerenkrantz | 92473 | if (ap_scoreboard_image->parent[i].pid != 0) { |
| 1348 | continue; | ||
| 1349 | } | ||
| 1350 | if (make_child(ap_server_conf, i) < 0) { | ||
| 1351 | break; | ||
| 1352 | } | ||
| 1353 | --number_to_start; | ||
| 1354 | rbb | 89781 | } |
| 1355 | } | ||
| 1356 | |||
| 1357 | |||
| 1358 | /* | ||
| 1359 | * idle_spawn_rate is the number of children that will be spawned on the | ||
| 1360 | * next maintenance cycle if there aren't enough idle servers. It is | ||
| 1361 | * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by | ||
| 1362 | * without the need to spawn. | ||
| 1363 | */ | ||
| 1364 | static int idle_spawn_rate = 1; | ||
| 1365 | #ifndef MAX_SPAWN_RATE | ||
| 1366 | jerenkrantz | 92473 | #define MAX_SPAWN_RATE (32) |
| 1367 | rbb | 89781 | #endif |
| 1368 | static int hold_off_on_exponential_spawning; | ||
| 1369 | |||
| 1370 | static void perform_idle_server_maintenance(void) | ||
| 1371 | { | ||
| 1372 | int i, j; | ||
| 1373 | int idle_thread_count; | ||
| 1374 | worker_score *ws; | ||
| 1375 | process_score *ps; | ||
| 1376 | int free_length; | ||
| 1377 | gregames | 89928 | int totally_free_length = 0; |
| 1378 | rbb | 89781 | int free_slots[MAX_SPAWN_RATE]; |
| 1379 | int last_non_dead; | ||
| 1380 | int total_non_dead; | ||
| 1381 | gregames | 168182 | int active_thread_count = 0; |
| 1382 | rbb | 89781 | |
| 1383 | /* initialize the free_list */ | ||
| 1384 | free_length = 0; | ||
| 1385 | |||
| 1386 | idle_thread_count = 0; | ||
| 1387 | last_non_dead = -1; | ||
| 1388 | total_non_dead = 0; | ||
| 1389 | |||
| 1390 | for (i = 0; i < ap_daemons_limit; ++i) { | ||
| 1391 | jerenkrantz | 92473 | /* Initialization to satisfy the compiler. It doesn't know |
| 1392 | * that ap_threads_per_child is always > 0 */ | ||
| 1393 | int status = SERVER_DEAD; | ||
| 1394 | int any_dying_threads = 0; | ||
| 1395 | int any_dead_threads = 0; | ||
| 1396 | int all_dead_threads = 1; | ||
| 1397 | rbb | 89781 | |
| 1398 | jerenkrantz | 92473 | if (i >= ap_max_daemons_limit && totally_free_length == idle_spawn_rate) |
| 1399 | break; | ||
| 1400 | rbb | 89781 | ps = &ap_scoreboard_image->parent[i]; |
| 1401 | jerenkrantz | 92473 | for (j = 0; j < ap_threads_per_child; j++) { |
| 1402 | rbb | 89781 | ws = &ap_scoreboard_image->servers[i][j]; |
| 1403 | jerenkrantz | 92473 | status = ws->status; |
| 1404 | rbb | 89781 | |
| 1405 | gregames | 89928 | /* XXX any_dying_threads is probably no longer needed GLA */ |
| 1406 | jerenkrantz | 92473 | any_dying_threads = any_dying_threads || |
| 1407 | (status == SERVER_GRACEFUL); | ||
| 1408 | any_dead_threads = any_dead_threads || (status == SERVER_DEAD); | ||
| 1409 | all_dead_threads = all_dead_threads && | ||
| 1410 | gregames | 89928 | (status == SERVER_DEAD || |
| 1411 | status == SERVER_GRACEFUL); | ||
| 1412 | rbb | 89781 | |
| 1413 | jerenkrantz | 92473 | /* We consider a starting server as idle because we started it |
| 1414 | * at least a cycle ago, and if it still hasn't finished starting | ||
| 1415 | * then we're just going to swamp things worse by forking more. | ||
| 1416 | * So we hopefully won't need to fork more if we count it. | ||
| 1417 | * This depends on the ordering of SERVER_READY and SERVER_STARTING. | ||
| 1418 | */ | ||
| 1419 | gregames | 168182 | if (ps->pid != 0) { /* XXX just set all_dead_threads in outer for |
| 1420 | loop if no pid? not much else matters */ | ||
| 1421 | if (status <= SERVER_READY && status != SERVER_DEAD && | ||
| 1422 | !ps->quiescing && | ||
| 1423 | ps->generation == ap_my_generation) { | ||
| 1424 | ++idle_thread_count; | ||
| 1425 | } | ||
| 1426 | if (status >= SERVER_READY && status < SERVER_GRACEFUL) { | ||
| 1427 | ++active_thread_count; | ||
| 1428 | } | ||
| 1429 | jerenkrantz | 92473 | } |
| 1430 | } | ||
| 1431 | trawick | 102425 | if (any_dead_threads && totally_free_length < idle_spawn_rate |
| 1432 | && free_length < MAX_SPAWN_RATE | ||
| 1433 | rbb | 89781 | && (!ps->pid /* no process in the slot */ |
| 1434 | || ps->quiescing)) { /* or at least one is going away */ | ||
| 1435 | gregames | 89928 | if (all_dead_threads) { |
| 1436 | /* great! we prefer these, because the new process can | ||
| 1437 | * start more threads sooner. So prioritize this slot | ||
| 1438 | * by putting it ahead of any slots with active threads. | ||
| 1439 | * | ||
| 1440 | * first, make room by moving a slot that's potentially still | ||
| 1441 | * in use to the end of the array | ||
| 1442 | */ | ||
| 1443 | free_slots[free_length] = free_slots[totally_free_length]; | ||
| 1444 | free_slots[totally_free_length++] = i; | ||
| 1445 | } | ||
| 1446 | else { | ||
| 1447 | /* slot is still in use - back of the bus | ||
| 1448 | */ | ||
| 1449 | gregames | 160211 | free_slots[free_length] = i; |
| 1450 | gregames | 89928 | } |
| 1451 | jerenkrantz | 92473 | ++free_length; |
| 1452 | } | ||
| 1453 | gregames | 89928 | /* XXX if (!ps->quiescing) is probably more reliable GLA */ |
| 1454 | jerenkrantz | 92473 | if (!any_dying_threads) { |
| 1455 | rbb | 89781 | last_non_dead = i; |
| 1456 | ++total_non_dead; | ||
| 1457 | } | ||
| 1458 | } | ||
| 1459 | gregames | 168182 | |
| 1460 | if (sick_child_detected) { | ||
| 1461 | if (active_thread_count > 0) { | ||
| 1462 | /* some child processes appear to be working. don't kill the | ||
| 1463 | * whole server. | ||
| 1464 | */ | ||
| 1465 | sick_child_detected = 0; | ||
| 1466 | } | ||
| 1467 | else { | ||
| 1468 | /* looks like a basket case. give up. | ||
| 1469 | */ | ||
| 1470 | shutdown_pending = 1; | ||
| 1471 | child_fatal = 1; | ||
| 1472 | ap_log_error(APLOG_MARK, APLOG_ALERT, 0, | ||
| 1473 | ap_server_conf, | ||
| 1474 | "No active workers found..." | ||
| 1475 | " Apache is exiting!"); | ||
| 1476 | /* the child already logged the failure details */ | ||
| 1477 | return; | ||
| 1478 | } | ||
| 1479 | } | ||
| 1480 | |||
| 1481 | rbb | 89781 | ap_max_daemons_limit = last_non_dead + 1; |
| 1482 | |||
| 1483 | if (idle_thread_count > max_spare_threads) { | ||
| 1484 | /* Kill off one child */ | ||
| 1485 | rbb | 93358 | ap_mpm_pod_signal(pod, TRUE); |
| 1486 | rbb | 89781 | idle_spawn_rate = 1; |
| 1487 | } | ||
| 1488 | else if (idle_thread_count < min_spare_threads) { | ||
| 1489 | /* terminate the free list */ | ||
| 1490 | if (free_length == 0) { | ||
| 1491 | jerenkrantz | 92473 | /* only report this condition once */ |
| 1492 | static int reported = 0; | ||
| 1493 | |||
| 1494 | if (!reported) { | ||
| 1495 | trawick | 95149 | ap_log_error(APLOG_MARK, APLOG_ERR, 0, |
| 1496 | jerenkrantz | 92473 | ap_server_conf, |
| 1497 | "server reached MaxClients setting, consider" | ||
| 1498 | " raising the MaxClients setting"); | ||
| 1499 | reported = 1; | ||
| 1500 | } | ||
| 1501 | idle_spawn_rate = 1; | ||
| 1502 | } | ||
| 1503 | else { | ||
| 1504 | gregames | 89928 | if (free_length > idle_spawn_rate) { |
| 1505 | free_length = idle_spawn_rate; | ||
| 1506 | } | ||
| 1507 | jerenkrantz | 92473 | if (idle_spawn_rate >= 8) { |
| 1508 | trawick | 95149 | ap_log_error(APLOG_MARK, APLOG_INFO, 0, |
| 1509 | jerenkrantz | 92473 | ap_server_conf, |
| 1510 | "server seems busy, (you may need " | ||
| 1511 | "to increase StartServers, ThreadsPerChild " | ||
| 1512 | rbb | 89781 | "or Min/MaxSpareThreads), " |
| 1513 | jerenkrantz | 92473 | "spawning %d children, there are around %d idle " |
| 1514 | rbb | 89781 | "threads, and %d total children", free_length, |
| 1515 | jerenkrantz | 92473 | idle_thread_count, total_non_dead); |
| 1516 | } | ||
| 1517 | for (i = 0; i < free_length; ++i) { | ||
| 1518 | make_child(ap_server_conf, free_slots[i]); | ||
| 1519 | } | ||
| 1520 | /* the next time around we want to spawn twice as many if this | ||
| 1521 | * wasn't good enough, but not if we've just done a graceful | ||
| 1522 | */ | ||
| 1523 | if (hold_off_on_exponential_spawning) { | ||
| 1524 | --hold_off_on_exponential_spawning; | ||
| 1525 | } | ||
| 1526 | else if (idle_spawn_rate < MAX_SPAWN_RATE) { | ||
| 1527 | idle_spawn_rate *= 2; | ||
| 1528 | } | ||
| 1529 | } | ||
| 1530 | rbb | 89781 | } |
| 1531 | else { | ||
| 1532 | idle_spawn_rate = 1; | ||
| 1533 | } | ||
| 1534 | } | ||
| 1535 | |||
| 1536 | static void server_main_loop(int remaining_children_to_start) | ||
| 1537 | { | ||
| 1538 | int child_slot; | ||
| 1539 | rbb | 91648 | apr_exit_why_e exitwhy; |
| 1540 | gregames | 93366 | int status, processed_status; |
| 1541 | rbb | 89781 | apr_proc_t pid; |
| 1542 | int i; | ||
| 1543 | |||
| 1544 | while (!restart_pending && !shutdown_pending) { | ||
| 1545 | rbb | 91648 | ap_wait_or_timeout(&exitwhy, &status, &pid, pconf); |
| 1546 | rbb | 89781 | |
| 1547 | if (pid.pid != -1) { | ||
| 1548 | gregames | 93366 | processed_status = ap_process_child_status(&pid, exitwhy, status); |
| 1549 | if (processed_status == APEXIT_CHILDFATAL) { | ||
| 1550 | trawick | 92019 | shutdown_pending = 1; |
| 1551 | child_fatal = 1; | ||
| 1552 | return; | ||
| 1553 | } | ||
| 1554 | gregames | 168182 | else if (processed_status == APEXIT_CHILDSICK) { |
| 1555 | /* tell perform_idle_server_maintenance to check into this | ||
| 1556 | * on the next timer pop | ||
| 1557 | */ | ||
| 1558 | sick_child_detected = 1; | ||
| 1559 | } | ||
| 1560 | rbb | 89781 | /* non-fatal death... note that it's gone in the scoreboard. */ |
| 1561 | child_slot = find_child_by_pid(&pid); | ||
| 1562 | if (child_slot >= 0) { | ||
| 1563 | for (i = 0; i < ap_threads_per_child; i++) | ||
| 1564 | trawick | 92512 | ap_update_child_status_from_indexes(child_slot, i, SERVER_DEAD, |
| 1565 | (request_rec *) NULL); | ||
| 1566 | rbb | 89781 | |
| 1567 | ap_scoreboard_image->parent[child_slot].pid = 0; | ||
| 1568 | ap_scoreboard_image->parent[child_slot].quiescing = 0; | ||
| 1569 | gregames | 93366 | if (processed_status == APEXIT_CHILDSICK) { |
| 1570 | /* resource shortage, minimize the fork rate */ | ||
| 1571 | idle_spawn_rate = 1; | ||
| 1572 | } | ||
| 1573 | else if (remaining_children_to_start | ||
| 1574 | jerenkrantz | 92473 | && child_slot < ap_daemons_limit) { |
| 1575 | /* we're still doing a 1-for-1 replacement of dead | ||
| 1576 | rbb | 89781 | * children with new children |
| 1577 | */ | ||
| 1578 | jerenkrantz | 92473 | make_child(ap_server_conf, child_slot); |
| 1579 | --remaining_children_to_start; | ||
| 1580 | } | ||
| 1581 | trawick | 109510 | } |
| 1582 | else if (ap_unregister_extra_mpm_process(pid.pid) == 1) { | ||
| 1583 | /* handled */ | ||
| 1584 | rbb | 89781 | #if APR_HAS_OTHER_CHILD |
| 1585 | jerenkrantz | 92473 | } |
| 1586 | brianp | 101858 | else if (apr_proc_other_child_alert(&pid, APR_OC_REASON_DEATH, |
| 1587 | status) == 0) { | ||
| 1588 | jerenkrantz | 92473 | /* handled */ |
| 1589 | rbb | 89781 | #endif |
| 1590 | jerenkrantz | 92473 | } |
| 1591 | else if (is_graceful) { | ||
| 1592 | /* Great, we've probably just lost a slot in the | ||
| 1593 | * scoreboard. Somehow we don't know about this child. | ||
| 1594 | */ | ||
| 1595 | trawick | 95149 | ap_log_error(APLOG_MARK, APLOG_WARNING, 0, |
| 1596 | jerenkrantz | 92473 | ap_server_conf, |
| 1597 | "long lost child came home! (pid %ld)", | ||
| 1598 | (long)pid.pid); | ||
| 1599 | } | ||
| 1600 | /* Don't perform idle maintenance when a child dies, | ||
| 1601 | rbb | 89781 | * only do it when there's a timeout. Remember only a |
| 1602 | * finite number of children can die, and it's pretty | ||
| 1603 | * pathological for a lot to die suddenly. | ||
| 1604 | */ | ||
| 1605 | jerenkrantz | 92473 | continue; |
| 1606 | } | ||
| 1607 | else if (remaining_children_to_start) { | ||
| 1608 | /* we hit a 1 second timeout in which none of the previous | ||
| 1609 | * generation of children needed to be reaped... so assume | ||
| 1610 | * they're all done, and pick up the slack if any is left. | ||
| 1611 | */ | ||
| 1612 | startup_children(remaining_children_to_start); | ||
| 1613 | remaining_children_to_start = 0; | ||
| 1614 | /* In any event we really shouldn't do the code below because | ||
| 1615 | * few of the servers we just started are in the IDLE state | ||
| 1616 | * yet, so we'd mistakenly create an extra server. | ||
| 1617 | */ | ||
| 1618 | continue; | ||
| 1619 | } | ||
| 1620 | rbb | 89781 | |
| 1621 | jerenkrantz | 92473 | perform_idle_server_maintenance(); |
| 1622 | rbb | 89781 | } |
| 1623 | } | ||
| 1624 | |||
| 1625 | rbb | 91955 | int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) |
| 1626 | { | ||
| 1627 | int remaining_children_to_start; | ||
| 1628 | apr_status_t rv; | ||
| 1629 | |||
| 1630 | rbb | 93227 | ap_log_pid(pconf, ap_pid_fname); |
| 1631 | |||
| 1632 | trawick | 92530 | first_server_limit = server_limit; |
| 1633 | first_thread_limit = thread_limit; | ||
| 1634 | if (changed_limit_at_restart) { | ||
| 1635 | trawick | 95149 | ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, |
| 1636 | trawick | 92530 | "WARNING: Attempt to change ServerLimit or ThreadLimit " |
| 1637 | "ignored during restart"); | ||
| 1638 | changed_limit_at_restart = 0; | ||
| 1639 | } | ||
| 1640 | |||
| 1641 | rbb | 89781 | /* Initialize cross-process accept lock */ |
| 1642 | wrowe | 93969 | ap_lock_fname = apr_psprintf(_pconf, "%s.%" APR_PID_T_FMT, |
| 1643 | trawick | 92412 | ap_server_root_relative(_pconf, ap_lock_fname), |
| 1644 | ap_my_pid); | ||
| 1645 | dreid | 92596 | |
| 1646 | trawick | 92653 | rv = apr_proc_mutex_create(&accept_mutex, ap_lock_fname, |
| 1647 | ap_accept_lock_mech, _pconf); | ||
| 1648 | rbb | 89781 | if (rv != APR_SUCCESS) { |
| 1649 | ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, | ||
| 1650 | "Couldn't create accept lock"); | ||
| 1651 | trawick | 102045 | mpm_state = AP_MPMQ_STOPPING; |
| 1652 | rbb | 89781 | return 1; |
| 1653 | } | ||
| 1654 | |||
| 1655 | trawick | 90213 | #if APR_USE_SYSVSEM_SERIALIZE |
| 1656 | if (ap_accept_lock_mech == APR_LOCK_DEFAULT || | ||
| 1657 | ap_accept_lock_mech == APR_LOCK_SYSVSEM) { | ||
| 1658 | #else | ||
| 1659 | if (ap_accept_lock_mech == APR_LOCK_SYSVSEM) { | ||
| 1660 | #endif | ||
| 1661 | aaron | 91580 | rv = unixd_set_proc_mutex_perms(accept_mutex); |
| 1662 | trawick | 90213 | if (rv != APR_SUCCESS) { |
| 1663 | ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, | ||
| 1664 | trawick | 94541 | "Couldn't set permissions on cross-process lock; " |
| 1665 | "check User and Group directives"); | ||
| 1666 | trawick | 102045 | mpm_state = AP_MPMQ_STOPPING; |
| 1667 | trawick | 90213 | return 1; |
| 1668 | } | ||
| 1669 | } | ||
| 1670 | |||
| 1671 | rbb | 89781 | if (!is_graceful) { |
| 1672 | wrowe | 94039 | if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) { |
| 1673 | trawick | 102045 | mpm_state = AP_MPMQ_STOPPING; |
| 1674 | trawick | 93055 | return 1; |
| 1675 | } | ||
| 1676 | trawick | 93998 | /* fix the generation number in the global score; we just got a new, |
| 1677 | * cleared scoreboard | ||
| 1678 | */ | ||
| 1679 | ap_scoreboard_image->global->running_generation = ap_my_generation; | ||
| 1680 | rbb | 89781 | } |
| 1681 | |||
| 1682 | set_signals(); | ||
| 1683 | /* Don't thrash... */ | ||
| 1684 | if (max_spare_threads < min_spare_threads + ap_threads_per_child) | ||
| 1685 | jerenkrantz | 92473 | max_spare_threads = min_spare_threads + ap_threads_per_child; |
| 1686 | rbb | 89781 | |
| 1687 | /* If we're doing a graceful_restart then we're going to see a lot | ||
| 1688 | * of children exiting immediately when we get into the main loop | ||
| 1689 | jerenkrantz | 91076 | * below (because we just sent them AP_SIG_GRACEFUL). This happens pretty |
| 1690 | rbb | 89781 | * rapidly... and for each one that exits we'll start a new one until |
| 1691 | * we reach at least daemons_min_free. But we may be permitted to | ||
| 1692 | * start more than that, so we'll just keep track of how many we're | ||
| 1693 | * supposed to start up without the 1 second penalty between each fork. | ||
| 1694 | */ | ||
| 1695 | remaining_children_to_start = ap_daemons_to_start; | ||
| 1696 | if (remaining_children_to_start > ap_daemons_limit) { | ||
| 1697 | jerenkrantz | 92473 | remaining_children_to_start = ap_daemons_limit; |
| 1698 | rbb | 89781 | } |
| 1699 | if (!is_graceful) { | ||
| 1700 | jerenkrantz | 92473 | startup_children(remaining_children_to_start); |
| 1701 | remaining_children_to_start = 0; | ||
| 1702 | rbb | 89781 | } |
| 1703 | else { | ||
| 1704 | jerenkrantz | 92473 | /* give the system some time to recover before kicking into |
| 1705 | * exponential mode */ | ||
| 1706 | hold_off_on_exponential_spawning = 10; | ||
| 1707 | rbb | 89781 | } |
| 1708 | |||
| 1709 | trawick | 95149 | ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, |
| 1710 | jerenkrantz | 92473 | "%s configured -- resuming normal operations", |
| 1711 | ap_get_server_version()); | ||
| 1712 | trawick | 95149 | ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, |
| 1713 | jerenkrantz | 92473 | "Server built: %s", ap_get_server_built()); |
| 1714 | jim | 94055 | #ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH |
| 1715 | trawick | 95149 | ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, |
| 1716 | jim | 95568 | "AcceptMutex: %s (default: %s)", |
| 1717 | apr_proc_mutex_name(accept_mutex), | ||
| 1718 | apr_proc_mutex_defname()); | ||
| 1719 | jim | 94055 | #endif |
| 1720 | rbb | 89781 | restart_pending = shutdown_pending = 0; |
| 1721 | trawick | 102045 | mpm_state = AP_MPMQ_RUNNING; |
| 1722 | |||
| 1723 | rbb | 89781 | server_main_loop(remaining_children_to_start); |
| 1724 | trawick | 102045 | mpm_state = AP_MPMQ_STOPPING; |
| 1725 | rbb | 89781 | |
| 1726 | colm | 290189 | if (shutdown_pending && !is_graceful) { |
| 1727 | /* Time to shut down: | ||
| 1728 | rbb | 89781 | * Kill child processes, tell them to call child_exit, etc... |
| 1729 | */ | ||
| 1730 | trawick | 94024 | ap_mpm_pod_killpg(pod, ap_daemons_limit, FALSE); |
| 1731 | jerenkrantz | 92473 | ap_reclaim_child_processes(1); /* Start with SIGTERM */ |
| 1732 | trawick | 92019 | |
| 1733 | if (!child_fatal) { | ||
| 1734 | /* cleanup pid file on normal shutdown */ | ||
| 1735 | rbb | 89781 | const char *pidfile = NULL; |
| 1736 | pidfile = ap_server_root_relative (pconf, ap_pid_fname); | ||
| 1737 | if ( pidfile != NULL && unlink(pidfile) == 0) | ||
| 1738 | trawick | 95149 | ap_log_error(APLOG_MARK, APLOG_INFO, 0, |
| 1739 | jerenkrantz | 92473 | ap_server_conf, |
| 1740 | colm | 290189 | "removed PID file %s (pid=%" APR_PID_T_FMT ")", |
| 1741 | pidfile, getpid()); | ||
| 1742 | trawick | 92019 | |
| 1743 | trawick | 95149 | ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, |
| 1744 | trawick | 92019 | ap_server_conf, "caught SIGTERM, shutting down"); |
| 1745 | rbb | 89781 | } |
| 1746 | jerenkrantz | 92473 | return 1; |
| 1747 | colm | 290189 | } else if (shutdown_pending) { |
| 1748 | /* Time to gracefully shut down: | ||
| 1749 | * Kill child processes, tell them to call child_exit, etc... | ||
| 1750 | */ | ||
| 1751 | int active_children; | ||
| 1752 | int index; | ||
| 1753 | apr_time_t cutoff = 0; | ||
| 1754 | |||
| 1755 | /* Close our listeners, and then ask our children to do same */ | ||
| 1756 | ap_close_listeners(); | ||
| 1757 | ap_mpm_pod_killpg(pod, ap_daemons_limit, TRUE); | ||
| 1758 | ap_relieve_child_processes(); | ||
| 1759 | |||
| 1760 | if (!child_fatal) { | ||
| 1761 | /* cleanup pid file on normal shutdown */ | ||
| 1762 | const char *pidfile = NULL; | ||
| 1763 | pidfile = ap_server_root_relative (pconf, ap_pid_fname); | ||
| 1764 | if ( pidfile != NULL && unlink(pidfile) == 0) | ||
| 1765 | ap_log_error(APLOG_MARK, APLOG_INFO, 0, | ||
| 1766 | ap_server_conf, | ||
| 1767 | "removed PID file %s (pid=%" APR_PID_T_FMT ")", | ||
| 1768 | pidfile, getpid()); | ||
| 1769 | |||
| 1770 | ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, | ||
| 1771 | ap_server_conf, "caught SIGTERM, shutting down"); | ||
| 1772 | } | ||
| 1773 | |||
| 1774 | /* Don't really exit until each child has finished */ | ||
| 1775 | shutdown_pending = 0; | ||
| 1776 | do { | ||
| 1777 | /* Pause for a second */ | ||
| 1778 | apr_sleep(apr_time_from_sec(1)); | ||
| 1779 | |||
| 1780 | /* Relieve any children which have now exited */ | ||
| 1781 | ap_relieve_child_processes(); | ||
| 1782 | |||
| 1783 | active_children = 0; | ||
| 1784 | for (index = 0; index < ap_daemons_limit; ++index) { | ||
| 1785 | if (MPM_CHILD_PID(index) != 0) { | ||
| 1786 | if (kill(MPM_CHILD_PID(index), 0) == 0) { | ||
| 1787 | active_children = 1; | ||
| 1788 | /* Having just one child is enough to stay around */ | ||
| 1789 | break; | ||
| 1790 | } | ||
| 1791 | } | ||
| 1792 | } | ||
| 1793 | } while (!shutdown_pending && active_children && | ||
| 1794 | (!ap_graceful_shutdown_timeout || apr_time_now() < cutoff)); | ||
| 1795 | |||
| 1796 | /* We might be here because we received SIGTERM, either | ||
| 1797 | * way, try and make sure that all of our processes are | ||
| 1798 | * really dead. | ||
| 1799 | */ | ||
| 1800 | ap_mpm_pod_killpg(pod, ap_daemons_limit, FALSE); | ||
| 1801 | ap_reclaim_child_processes(1); | ||
| 1802 | |||
| 1803 | return 1; | ||
| 1804 | rbb | 89781 | } |
| 1805 | |||
| 1806 | /* we've been told to restart */ | ||
| 1807 | apr_signal(SIGHUP, SIG_IGN); | ||
| 1808 | |||
| 1809 | if (one_process) { | ||
| 1810 | jerenkrantz | 92473 | /* not worth thinking about */ |
| 1811 | return 1; | ||
| 1812 | rbb | 89781 | } |
| 1813 | |||
| 1814 | /* advance to the next generation */ | ||
| 1815 | /* XXX: we really need to make sure this new generation number isn't in | ||
| 1816 | * use by any of the children. | ||
| 1817 | */ | ||
| 1818 | ++ap_my_generation; | ||
| 1819 | wrowe | 92791 | ap_scoreboard_image->global->running_generation = ap_my_generation; |
| 1820 | rbb | 89781 | |
| 1821 | if (is_graceful) { | ||
| 1822 | trawick | 95149 | ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, |
| 1823 | wrowe | 93264 | AP_SIG_GRACEFUL_STRING " received. Doing graceful restart"); |