/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/
/* * httpd.c: simple http daemon for answering WWW file requests * * * 03-21-93 Rob McCool wrote original code (up to NCSA HTTPd 1.3) * * 03-06-95 blong * changed server number for child-alone processes to 0 and changed name * of processes * * 03-10-95 blong * Added numerous speed hacks proposed by Robert S. Thau (rst@ai.mit.edu) * including set group before fork, and call gettime before to fork * to set up libraries. * * 04-14-95 rst / rh * Brandon's code snarfed from NCSA 1.4, but tinkered to work with the * Apache server, and also to have child processes do accept() directly. * * April-July '95 rst * Extensive rework for Apache.
*/
/* Limit on the total --- clients will be locked out if more servers than * this are needed. It is intended solely to keep the server from crashing * when things get out of hand. * * We keep a hard maximum number of servers, for two reasons --- first off, * in case something goes seriously wrong, we want to stop the fork bomb * short of actually crashing the machine we're running on by filling some * kernel table. Secondly, it keeps the size of the scoreboard file small * enough that we can read the whole thing without worrying too much about * the overhead.
*/ #ifndef HARD_SERVER_LIMIT #define HARD_SERVER_LIMIT 1 #endif
/* * The max child slot ever assigned, preserved across restarts. Necessary * to deal with MaxRequestWorkers changes across SIGWINCH restarts. We use this * value to optimize routines that have to scan the entire scoreboard.
*/ staticint ap_max_workers_limit = -1;
int hold_screen_on_exit = 0; /* Indicates whether the screen should be held open */
static fd_set listenfds; staticint listenmaxfd;
static apr_pool_t *pconf; /* Pool for config stuff */ static apr_pool_t *pmain; /* Pool for httpd child stuff */
static pid_t ap_my_pid; /* it seems silly to call getpid all the time */ staticchar *ap_my_addrspace = NULL;
staticint die_now = 0;
/* Keep track of the number of worker threads currently active */ staticunsignedlong worker_thread_count; staticint request_count;
/* Structure used to register/deregister a console handler with the OS */ staticint InstallConsoleHandler(void); staticvoid RemoveConsoleHandler(void); staticint CommandLineInterpreter(scr_t screenID, constchar *commandLine); static CommandParser_t ConsoleHandler = {0, NULL, 0}; #define HANDLEDCOMMAND 0 #define NOTMYCOMMAND 1
staticvoid sig_term(int sig)
{ if (shutdown_pending == 1) { /* Um, is this _probably_ not an error, if the user has * tried to do a shutdown twice quickly, so we won't * worry about reporting it.
*/ return;
}
shutdown_pending = 1;
DBPRINT0 ("waiting for threads\n"); while (wait_to_finish) {
apr_thread_yield();
}
DBPRINT0 ("goodbye\n");
}
/* restart() is the signal handler for SIGHUP and SIGWINCH * in the parent process, unless running in ONE_PROCESS mode
*/ staticvoid restart(void)
{ if (restart_pending == 1) { /* Probably not an error - don't bother reporting it */ return;
}
restart_pending = 1;
is_graceful = 1;
}
int nlmUnloadSignaled(int wait)
{
shutdown_pending = 1;
if (wait) { while (wait_to_finish) {
NXThreadYield();
}
}
return 0;
}
/***************************************************************** * Child process main loop. * The following vars are static to avoid getting clobbered by longjmp(); * they are really private to child_main.
*/
/* * Wait for an acceptable connection to arrive.
*/
for (;;) { if (shutdown_pending || restart_pending || (ap_scoreboard_image->servers[0][my_worker_num].status == WORKER_IDLE_KILL)) {
DBPRINT1 ("\nThread slot %d is shutting down\n", my_worker_num);
clean_child_exit(0, my_worker_num, ptrans, bucket_alloc);
}
/* Check the listen queue on all sockets for requests */
memcpy(&main_fds, &listenfds, sizeof(fd_set));
srv = select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv);
if (srv <= 0) { if (srv < 0) {
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00217) "select() failed on listen socket");
apr_thread_yield();
} continue;
}
/* remember the last_lr we searched last time around so that
we don't end up starving any particular listening socket */ if (last_lr == NULL) {
lr = ap_listeners;
} else {
lr = last_lr->next; if (!lr)
lr = ap_listeners;
}
first_lr = lr; do {
apr_os_sock_get(&sockdes, lr->sd); if (FD_ISSET(sockdes, &main_fds)) goto got_listener;
lr = lr->next; if (!lr)
lr = ap_listeners;
} while (lr != first_lr); /* if we get here, something unexpected happened. Go back into the select state and try again.
*/ continue;
got_listener:
last_lr = lr;
sd = lr->sd;
wouldblock_retry = MAX_WB_RETRIES;
while (wouldblock_retry) { if ((stat = apr_socket_accept(&csd, sd, ptrans)) == APR_SUCCESS) { break;
} else { /* if the error is a wouldblock then maybe we were too quick try to pull the next request from the listen queue. Try a few more times then return to our idle
listen state. */ if (!APR_STATUS_IS_EAGAIN(stat)) { break;
}
if (wouldblock_retry--) {
apr_thread_yield();
}
}
}
/* If we got a new socket, set it to non-blocking mode and process
it. Otherwise handle the error. */ if (stat == APR_SUCCESS) {
apr_socket_opt_set(csd, APR_SO_NONBLOCK, 0); #ifdef DBINFO_ON if (wouldblock_retry < MAX_WB_RETRIES) {
retry_success++;
avg_retries += (MAX_WB_RETRIES-wouldblock_retry);
} #endif break; /* We have a socket ready for reading */
} else { #ifdef DBINFO_ON if (APR_STATUS_IS_EAGAIN(stat)) {
would_block++;
retry_fail++;
} elseif ( #else if (APR_STATUS_IS_EAGAIN(stat) || #endif
APR_STATUS_IS_ECONNRESET(stat) ||
APR_STATUS_IS_ETIMEDOUT(stat) ||
APR_STATUS_IS_EHOSTUNREACH(stat) ||
APR_STATUS_IS_ENETUNREACH(stat)) {
;
} #ifdef USE_WINSOCK elseif (APR_STATUS_IS_ENETDOWN(stat)) { /* * When the network layer has been shut down, there * is not much use in simply exiting: the parent * would simply re-create us (and we'd fail again). * Use the CHILDFATAL code to tear the server down. * @@@ Martin's idea for possible improvement: * A different approach would be to define * a new APEXIT_NETDOWN exit code, the reception * of which would make the parent shutdown all * children, then idle-loop until it detected that * the network is up again, and restart the children. * Ben Hyde noted that temporary ENETDOWN situations * occur in mobile IP.
*/
ap_log_error(APLOG_MARK, APLOG_EMERG, stat, ap_server_conf, APLOGNO(00218) "apr_socket_accept: giving up.");
clean_child_exit(APEXIT_CHILDFATAL, my_worker_num, ptrans,
bucket_alloc);
} #endif else {
ap_log_error(APLOG_MARK, APLOG_ERR, stat, ap_server_conf, APLOGNO(00219) "apr_socket_accept: (client socket)");
clean_child_exit(1, my_worker_num, ptrans, bucket_alloc);
}
}
}
ap_create_sb_handle(&sbh, ptrans, 0, my_worker_num); /* * We now have a connection, so set it up with the appropriate * socket options, file descriptors, and read/write buffers.
*/
current_conn = ap_run_create_connection(ptrans, ap_server_conf, csd,
my_worker_num, sbh,
bucket_alloc); if (current_conn) {
current_conn->current_thread = thd;
ap_process_connection(current_conn, csd);
ap_lingering_close(current_conn);
}
request_count++;
}
clean_child_exit(0, my_worker_num, ptrans, bucket_alloc);
}
staticint make_child(server_rec *s, int slot)
{ int tid; int err=0;
NXContext_t ctx;
if (err) { /* create thread didn't succeed. Fix the scoreboard or else * it will say SERVER_STARTING forever and ever
*/
ap_update_child_status_from_indexes(0, slot, WORKER_DEAD,
(request_rec *) NULL);
/* In case system resources are maxxed out, we don't want Apache running away with the CPU trying to fork over and
over and over again. */
apr_thread_yield();
return -1;
}
ap_scoreboard_image->servers[0][slot].tid = tid;
return 0;
}
/* start up a bunch of worker threads */ staticvoid startup_workers(int number_to_start)
{ int i;
for (i = 0; number_to_start && i < ap_threads_limit; ++i) { if (ap_scoreboard_image->servers[0][i].status != WORKER_DEAD) { continue;
} if (make_child(ap_server_conf, i) < 0) { break;
}
--number_to_start;
}
}
/* * idle_spawn_rate is the number of children that will be spawned on the * next maintenance cycle if there aren't enough idle servers. It is * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by * without the need to spawn.
*/ staticint idle_spawn_rate = 1; #ifndef MAX_SPAWN_RATE #define MAX_SPAWN_RATE (64) #endif staticint hold_off_on_exponential_spawning;
staticvoid perform_idle_server_maintenance(apr_pool_t *p)
{ int i; int idle_count;
worker_score *ws; int free_length; int free_slots[MAX_SPAWN_RATE]; int last_non_dead; int total_non_dead;
for (i = 0; i < ap_threads_limit; ++i) { int status;
if (i >= ap_max_workers_limit && free_length == idle_spawn_rate) break;
ws = &ap_scoreboard_image->servers[0][i];
status = ws->status; if (status == WORKER_DEAD) { /* try to keep children numbers as low as possible */ if (free_length < idle_spawn_rate) {
free_slots[free_length] = i;
++free_length;
}
} elseif (status == WORKER_IDLE_KILL) { /* If it is already marked to die, skip it */ continue;
} else { /* We consider a starting server as idle because we started it * at least a cycle ago, and if it still hasn't finished starting * then we're just going to swamp things worse by forking more. * So we hopefully won't need to fork more if we count it. * This depends on the ordering of SERVER_READY and SERVER_STARTING.
*/ if (status <= WORKER_READY) {
++ idle_count;
}
++total_non_dead;
last_non_dead = i;
}
}
DBPRINT2("Total: %d Idle Count: %d \r", total_non_dead, idle_count);
ap_max_workers_limit = last_non_dead + 1; if (idle_count > ap_threads_max_free) { /* kill off one child... we use the pod because that'll cause it to * shut down gracefully, in case it happened to pick up a request * while we were counting
*/
idle_spawn_rate = 1;
ap_update_child_status_from_indexes(0, last_non_dead, WORKER_IDLE_KILL,
(request_rec *) NULL);
DBPRINT1("\nKilling idle thread: %d\n", last_non_dead);
} elseif (idle_count < ap_threads_min_free) { /* terminate the free list */ if (free_length == 0) { /* only report this condition once */ staticint reported = 0;
if (!reported) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00220) "server reached MaxRequestWorkers setting, consider" " raising the MaxRequestWorkers setting");
reported = 1;
}
idle_spawn_rate = 1;
} else { if (idle_spawn_rate >= 8) {
ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(00221) "server seems busy, (you may need " "to increase StartServers, or Min/MaxSpareServers), " "spawning %d children, there are %d idle, and " "%d total children", idle_spawn_rate,
idle_count, total_non_dead);
}
DBPRINT0("\n"); for (i = 0; i < free_length; ++i) {
DBPRINT1("Spawning additional thread slot: %d\n", free_slots[i]);
make_child(ap_server_conf, free_slots[i]);
} /* the next time around we want to spawn twice as many if this * wasn't good enough, but not if we've just done a graceful
*/ if (hold_off_on_exponential_spawning) {
--hold_off_on_exponential_spawning;
} elseif (idle_spawn_rate < MAX_SPAWN_RATE) {
idle_spawn_rate *= 2;
}
}
} else {
idle_spawn_rate = 1;
}
}
staticvoid display_settings()
{ int status_array[SERVER_NUM_STATUS]; int i, status, total=0; int reqs = request_count; #ifdef DBINFO_ON int wblock = would_block;
printf("%s\n", ap_get_server_description()); if (ap_my_addrspace && (ap_my_addrspace[0] != 'O') && (ap_my_addrspace[1] != 'S'))
printf(" Running in address space %s\n", ap_my_addrspace);
/* Display listening ports */
printf(" Listening on port(s):");
lr = ap_listeners; do {
printf(" %d", lr->bind_addr->port);
lr = lr->next;
} while (lr && lr != ap_listeners);
if (!is_graceful) { if (ap_run_pre_mpm(s->process->pool, SB_NOT_SHARED) != OK) { return !OK;
}
}
/* Only set slot 0 since that is all NetWare will ever have. */
ap_scoreboard_image->parent[0].pid = getpid();
ap_scoreboard_image->parent[0].generation = ap_my_generation;
ap_run_child_status(ap_server_conf,
ap_scoreboard_image->parent[0].pid,
ap_my_generation,
0,
MPM_CHILD_STARTED);
/* Allow the Apache screen to be closed normally on exit() only if it has not been explicitly forced to close on exit(). (ie. the -E flag
was specified at startup) */ if (hold_screen_on_exit > 0) {
hold_screen_on_exit = 0;
}
/* Shutdown the listen sockets so that we don't get stuck in a blocking call.
shutdown_listeners();*/
if (shutdown_pending) { /* Got an unload from the console */
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00226) "caught SIGTERM, shutting down");
while (worker_thread_count > 0) {
printf ("\rShutdown pending. Waiting for %lu thread(s) to terminate...",
worker_thread_count);
apr_thread_yield();
}
mpm_main_cleanup(); return DONE;
} else { /* the only other way out is a restart */ /* advance to the next generation */ /* XXX: we really need to make sure this new generation number isn't in * use by any of the children.
*/
++ap_my_generation;
ap_scoreboard_image->global->running_generation = ap_my_generation;
/* Wait for all of the threads to terminate before initiating the restart */ while (worker_thread_count > 0) {
printf ("\rRestart pending. Waiting for %lu thread(s) to terminate...",
worker_thread_count);
apr_thread_yield();
}
printf ("\nRestarting...\n");
}
/* we want this only the first time around */ if (restart_num++ == 0) {
startup = 1;
}
if (ap_threads_limit > HARD_THREAD_LIMIT) { if (startup) {
ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00228) "WARNING: MaxThreads of %d exceeds compile-time " "limit of %d threads, decreasing to %d. " "To increase, please see the HARD_THREAD_LIMIT " "define in server/mpm/netware%s.",
ap_threads_limit, HARD_THREAD_LIMIT, HARD_THREAD_LIMIT,
MPM_HARD_LIMITS_FILE);
} else {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00229) "MaxThreads of %d exceeds compile-time limit " "of %d, decreasing to match",
ap_threads_limit, HARD_THREAD_LIMIT);
}
ap_threads_limit = HARD_THREAD_LIMIT;
} elseif (ap_threads_limit < 1) { if (startup) {
ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00230) "WARNING: MaxThreads of %d not allowed, " "increasing to 1.", ap_threads_limit);
} else {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(02661) "MaxThreads of %d not allowed, increasing to 1",
ap_threads_limit);
}
ap_threads_limit = 1;
}
/* ap_threads_to_start > ap_threads_limit effectively checked in * call to startup_workers(ap_threads_to_start) in ap_mpm_run()
*/ if (ap_threads_to_start < 0) { if (startup) {
ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00231) "WARNING: StartThreads of %d not allowed, " "increasing to 1.", ap_threads_to_start);
} else {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00232) "StartThreads of %d not allowed, increasing to 1",
ap_threads_to_start);
}
ap_threads_to_start = 1;
}
if (ap_threads_min_free < 1) { if (startup) {
ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00233) "WARNING: MinSpareThreads of %d not allowed, " "increasing to 1 to avoid almost certain server failure. " "Please read the documentation.", ap_threads_min_free);
} else {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00234) "MinSpareThreads of %d not allowed, increasing to 1",
ap_threads_min_free);
}
ap_threads_min_free = 1;
}
/* ap_threads_max_free < ap_threads_min_free + 1 checked in ap_mpm_run() */
return OK;
}
staticvoid netware_mpm_hooks(apr_pool_t *p)
{ /* Run the pre-config hook after core's so that it can override the * default setting of ThreadStackSize for NetWare.
*/ staticconstchar * const predecessors[] = {"core.c", NULL};
/* Make sure to hold the Apache screen open if exit() is called */
hold_screen_on_exit = 1;
/* Rewrite process->argv[]; * * add default -d serverroot from the path of this executable * * The end result will look like: * The -d serverroot default from the running executable
*/ if (process->argc > 0) { char *s = apr_pstrdup (process->pconf, process->argv[0]); if (s) { int i, len = strlen(s);
for (i=len; i; i--) { if (s[i] == '\\' || s[i] == '/') {
s[i] = '\0';
apr_filepath_merge(&def_server_root, NULL, s,
APR_FILEPATH_TRUENAME, process->pool); break;
}
} /* Use process->pool so that the rewritten argv * lasts for the lifetime of the server process, * because pconf will be destroyed after the * initial pre-flight of the config parser.
*/
mpm_new_argv = apr_array_make(process->pool, process->argc + 2, sizeof(constchar *));
*(constchar **)apr_array_push(mpm_new_argv) = process->argv[0];
*(constchar **)apr_array_push(mpm_new_argv) = "-d";
*(constchar **)apr_array_push(mpm_new_argv) = def_server_root;
optbuf[0] = '-';
optbuf[2] = '\0';
apr_getopt_init(&opt, process->pool, process->argc, process->argv); while (apr_getopt(opt, AP_SERVER_BASEARGS"n:", optbuf + 1, &opt_arg) == APR_SUCCESS) { switch (optbuf[1]) { case'n': if (opt_arg) {
renamescreen(opt_arg);
} break; case'E': /* Don't need to hold the screen open if the output is going to a file */
hold_screen_on_exit = -1; default:
*(constchar **)apr_array_push(mpm_new_argv) =
apr_pstrdup(process->pool, optbuf);
if (!strnicmp(szCommand, szcommandLine, iCommandLen)) {
ActivateScreen (getscreenhandle());
/* If an instance id was not given but the nlm is loaded in protected space, then the command belongs to the
OS address space instance to pass it on. */
pID = strstr (szcommandLine, "-p"); if ((pID == NULL) && nlmisloadedprotected()) return NOTMYCOMMAND;
/* If we got an instance id but it doesn't match this
instance of the nlm, pass it on. */ if (pID) {
pID = &pID[2]; while (*pID && (*pID == ' '))
pID++;
} if (pID && ap_my_addrspace && strnicmp(pID, ap_my_addrspace, strlen(ap_my_addrspace))) return NOTMYCOMMAND;
/* If we have determined that this command belongs to this
instance of the nlm, then handle it. */ if (!strnicmp("RESTART",&szcommandLine[iCommandLen],3)) {
printf("Restart Requested...\n");
restart();
} elseif (!strnicmp("VERSION",&szcommandLine[iCommandLen],3)) {
printf("Server version: %s\n", ap_get_server_description());
printf("Server built: %s\n", ap_get_server_built());
} elseif (!strnicmp("MODULES",&szcommandLine[iCommandLen],3)) {
ap_show_modules();
} elseif (!strnicmp("DIRECTIVES",&szcommandLine[iCommandLen],3)) {
ap_show_directives();
} elseif (!strnicmp("SHUTDOWN",&szcommandLine[iCommandLen],3)) {
printf("Shutdown Requested...\n");
shutdown_pending = 1;
} elseif (!strnicmp("SETTINGS",&szcommandLine[iCommandLen],3)) { if (show_settings) {
show_settings = 0;
ClearScreen (getscreenhandle());
show_server_data();
} else {
show_settings = 1;
display_settings();
}
} else {
show_settings = 0; if (strnicmp("HELP",&szcommandLine[iCommandLen],3))
printf("Unknown APACHE2 command %s\n", &szcommandLine[iCommandLen]);
printf("Usage: APACHE2 [command] [-p ]\n");
printf("Commands:\n");
printf("\tDIRECTIVES - Show directives\n");
printf("\tHELP - Display this help information\n");
printf("\tMODULES - Show a list of the loaded modules\n");
printf("\tRESTART - Reread the configuration file and restart Apache\n");
printf("\tSETTINGS - Show current thread status\n");
printf("\tSHUTDOWN - Shutdown Apache\n");
printf("\tVERSION - Display the server version information\n");
}
/* Tell NetWare we handled the command */ return HANDLEDCOMMAND;
}
/* Tell NetWare that the command isn't mine */ return NOTMYCOMMAND;
}
staticint InstallConsoleHandler(void)
{ /* Our command line handler interfaces the system operator
with this NLM */
staticconst command_rec netware_mpm_cmds[] = {
LISTEN_COMMANDS,
AP_INIT_TAKE1("StartThreads", set_threads_to_start, NULL, RSRC_CONF, "Number of worker threads launched at server startup"),
AP_INIT_TAKE1("MinSpareThreads", set_min_free_threads, NULL, RSRC_CONF, "Minimum number of idle threads, to handle request spikes"),
AP_INIT_TAKE1("MaxSpareThreads", set_max_free_threads, NULL, RSRC_CONF, "Maximum number of idle threads"),
AP_INIT_TAKE1("MaxThreads", set_thread_limit, NULL, RSRC_CONF, "Maximum number of worker threads alive at the same time"),
{ NULL }
};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.