📄 mpm_netware.c
字号:
/* 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.
*/
#include "apr.h"
#include "apr_portable.h"
#include "apr_strings.h"
#include "apr_thread_proc.h"
#include "apr_signal.h"
#include "apr_tables.h"
#include "apr_getopt.h"
#include "apr_thread_mutex.h"
#define APR_WANT_STDIO
#define APR_WANT_STRFUNC
#include "apr_want.h"
#if APR_HAVE_UNISTD_H
#include <unistd.h>
#endif
#if APR_HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#define CORE_PRIVATE
#include "ap_config.h"
#include "httpd.h"
#include "mpm_default.h"
#include "http_main.h"
#include "http_log.h"
#include "http_config.h"
#include "http_core.h" /* for get_remote_host */
#include "http_connection.h"
#include "scoreboard.h"
#include "ap_mpm.h"
#include "mpm_common.h"
#include "ap_listen.h"
#include "ap_mmn.h"
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#include <signal.h>
#include <netware.h>
#include <nks/netware.h>
#include <library.h>
#include <screen.h>
/* 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
#define WORKER_DEAD SERVER_DEAD
#define WORKER_STARTING SERVER_STARTING
#define WORKER_READY SERVER_READY
#define WORKER_IDLE_KILL SERVER_IDLE_KILL
/* config globals */
int ap_threads_per_child=0; /* Worker threads per child */
int ap_thread_stack_size=65536;
static int ap_threads_to_start=0;
static int ap_threads_min_free=0;
static int ap_threads_max_free=0;
static int ap_threads_limit=0;
static int mpm_state = AP_MPMQ_STARTING;
/*
* The max child slot ever assigned, preserved across restarts. Necessary
* to deal with MaxClients changes across SIGWINCH restarts. We use this
* value to optimize routines that have to scan the entire scoreboard.
*/
int ap_max_workers_limit = -1;
server_rec *ap_server_conf;
/* *Non*-shared http_main globals... */
int hold_screen_on_exit = 0; /* Indicates whether the screen should be held open */
static fd_set listenfds;
static int 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 */
static char *ap_my_addrspace = NULL;
static int die_now = 0;
/* Keep track of the number of worker threads currently active */
static unsigned long worker_thread_count;
static int request_count;
/* Structure used to register/deregister a console handler with the OS */
static int InstallConsoleHandler(void);
static void RemoveConsoleHandler(void);
static int CommandLineInterpreter(scr_t screenID, const char *commandLine);
static CommandParser_t ConsoleHandler = {0, NULL, 0};
#define HANDLEDCOMMAND 0
#define NOTMYCOMMAND 1
static int show_settings = 0;
//#define DBINFO_ON
//#define DBPRINT_ON
#ifdef DBPRINT_ON
#define DBPRINT0(s) printf(s)
#define DBPRINT1(s,v1) printf(s,v1)
#define DBPRINT2(s,v1,v2) printf(s,v1,v2)
#else
#define DBPRINT0(s)
#define DBPRINT1(s,v1)
#define DBPRINT2(s,v1,v2)
#endif
/* volatile just in case */
static int volatile shutdown_pending;
static int volatile restart_pending;
static int volatile is_graceful;
static int volatile wait_to_finish=1;
ap_generation_t volatile ap_my_generation=0;
/* a clean exit from a child with proper cleanup */
static void clean_child_exit(int code, int worker_num, apr_pool_t *ptrans,
apr_bucket_alloc_t *bucket_alloc) __attribute__ ((noreturn));
static void clean_child_exit(int code, int worker_num, apr_pool_t *ptrans,
apr_bucket_alloc_t *bucket_alloc)
{
apr_bucket_alloc_destroy(bucket_alloc);
if (!shutdown_pending) {
apr_pool_destroy(ptrans);
}
atomic_dec (&worker_thread_count);
if (worker_num >=0)
ap_update_child_status_from_indexes(0, worker_num, WORKER_DEAD,
(request_rec *) NULL);
NXThreadExit((void*)&code);
}
AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
{
switch(query_code){
case AP_MPMQ_MAX_DAEMON_USED:
*result = 1;
return APR_SUCCESS;
case AP_MPMQ_IS_THREADED:
*result = AP_MPMQ_DYNAMIC;
return APR_SUCCESS;
case AP_MPMQ_IS_FORKED:
*result = AP_MPMQ_NOT_SUPPORTED;
return APR_SUCCESS;
case AP_MPMQ_HARD_LIMIT_DAEMONS:
*result = HARD_SERVER_LIMIT;
return APR_SUCCESS;
case AP_MPMQ_HARD_LIMIT_THREADS:
*result = HARD_THREAD_LIMIT;
return APR_SUCCESS;
case AP_MPMQ_MAX_THREADS:
*result = ap_threads_limit;
return APR_SUCCESS;
case AP_MPMQ_MIN_SPARE_DAEMONS:
*result = 0;
return APR_SUCCESS;
case AP_MPMQ_MIN_SPARE_THREADS:
*result = ap_threads_min_free;
return APR_SUCCESS;
case AP_MPMQ_MAX_SPARE_DAEMONS:
*result = 0;
return APR_SUCCESS;
case AP_MPMQ_MAX_SPARE_THREADS:
*result = ap_threads_max_free;
return APR_SUCCESS;
case AP_MPMQ_MAX_REQUESTS_DAEMON:
*result = ap_max_requests_per_child;
return APR_SUCCESS;
case AP_MPMQ_MAX_DAEMONS:
*result = 1;
return APR_SUCCESS;
case AP_MPMQ_MPM_STATE:
*result = mpm_state;
return APR_SUCCESS;
}
return APR_ENOTIMPL;
}
/*****************************************************************
* Connection structures and accounting...
*/
static void mpm_term(void)
{
RemoveConsoleHandler();
wait_to_finish = 0;
NXThreadYield();
}
static void 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
*/
static void restart(void)
{
if (restart_pending == 1) {
/* Probably not an error - don't bother reporting it */
return;
}
restart_pending = 1;
is_graceful = 1;
}
static void set_signals(void)
{
apr_signal(SIGTERM, sig_term);
apr_signal(SIGABRT, sig_term);
}
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.
*/
int ap_graceful_stop_signalled(void)
{
/* not ever called anymore... */
return 0;
}
#define MAX_WB_RETRIES 3
#ifdef DBINFO_ON
static int would_block = 0;
static int retry_success = 0;
static int retry_fail = 0;
static int avg_retries = 0;
#endif
/*static */
void worker_main(void *arg)
{
ap_listen_rec *lr, *first_lr, *last_lr = NULL;
apr_pool_t *ptrans;
apr_pool_t *pbucket;
apr_allocator_t *allocator;
apr_bucket_alloc_t *bucket_alloc;
conn_rec *current_conn;
apr_status_t stat = APR_EINIT;
ap_sb_handle_t *sbh;
int my_worker_num = (int)arg;
apr_socket_t *csd = NULL;
int requests_this_child = 0;
apr_socket_t *sd = NULL;
fd_set main_fds;
int sockdes;
int srv;
struct timeval tv;
int wouldblock_retry;
tv.tv_sec = 1;
tv.tv_usec = 0;
apr_allocator_create(&allocator);
apr_allocator_max_free_set(allocator, ap_max_mem_free);
apr_pool_create_ex(&ptrans, pmain, NULL, allocator);
apr_allocator_owner_set(allocator, ptrans);
apr_pool_tag(ptrans, "transaction");
bucket_alloc = apr_bucket_alloc_create_ex(allocator);
atomic_inc (&worker_thread_count);
while (!die_now) {
/*
* (Re)initialize this child to a pre-connection state.
*/
current_conn = NULL;
apr_pool_clear(ptrans);
if ((ap_max_requests_per_child > 0
&& requests_this_child++ >= ap_max_requests_per_child)) {
DBPRINT1 ("\n**Thread slot %d is shutting down", my_worker_num);
clean_child_exit(0, my_worker_num, ptrans, bucket_alloc);
}
ap_update_child_status_from_indexes(0, my_worker_num, WORKER_READY,
(request_rec *) NULL);
/*
* 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, WSAGetLastError(), ap_server_conf,
"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_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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -