/*
 * Thread-based TCP/IP HTTP server.
 *
 * Command-line format:
 *
 *	$ http_server err-file rules-file [http-port] [nocache-port2]
 *
 * args:
 *    log-file	    Name of log file to create, connections and query strings
 *		    are written to the log file.
 *
 *    rule-file	    Name of rules file.  The rules file maps aliases to
 *                  local files and restricts access.
 *
 *    http-port    Port number to listen for connects on.  Defaults to port
 *		    80.
 *
 *    nocache-port Port number to listen for connects on with caching disabled.
 *		   May be the same as http_port.  If different, a second
 *		   listener thread will be started.
 *
 * Environment variables:
 *    HTTP_CLIENT_LIMIT		If defined, limits maximum number of concurrent
 *				TCP connections the server is to support,
 *				default is 16.  At startup, current process
 *				quotas are checked and client limit will be
 *				reduced if needed to match the available
 *				resources.  See file client_limit.c for
 *				details.
 *
 *    HTTP_MIN_AVAILABLE	If defined, sets mininum number of clients free
 *				clients that must be available for the request
 *				to be processed.  If below this number, an
 *				error is returned.
 *
 *    HTTP_LOG_LEVEL		If defined, sets logging level (default=1):
 *				  common Use standard log file format.
 *				    0	Report errors, config file info.
 *				    1	Report connect/final stat info.
 *				    2	Report Identifier re-mapping
 *				    3	Report request/response byte counts.
 *				    5	Show decnet script transaction.
 *
 *    HTTP_DEFAULT_HOST		Internet host name to use for HTTP server
 *				redirects.  If not defined, re-directs on
 *				scripts will not be supported.  We use a
 *				an environment variable to keep TCP/IP
 *				operations to a minimum.
 *
 *    HTTP_REENTRANT_C_RTL	If defined, sets reentrancy for C runtime:
 *				    0	Library is not reentrant, global locks
 *					will be used to serialize RTL calls.
 *				    1	Library is reentrant, synchronization
 *					for library set to C$C_MULTITHREAD.
 *				If not defined, default value is 1 for DECC,
 *				0 for other.
 *
 *    HTTP_MANAGE_PORT		If defined, sets management port number for
 *				privileged management requests.
 *
 *    HTTP_MANAGE_HOST		If defined, set remote host the management
 *				requests come from.  Default is loopback
 *				address.  Specify this value as a signed
 *				numeric value.
 *
 *    HTTP_USER_ID		(Unix only).  Numeric user ID to setuid to
 *				after establishing listen port.
 *
 *    HTTP_GROUP_ID		(Unix only).  Numeric group ID to setgid to
 *				after establishing listen port.
 *
 *    HTTP_CRLF_NEWLINE		Sets how lines delimited in text document
 *				context: 0-LF, 1-CRLF.  HTTP response header 
 *				still uses CRLF).
 *
 * Logical names:
 *	WWW_SERVER_PORTS	Lists privileged ports server is permitted to 
 *				listen on.  Must be exec mode logical name.
 *
 *  Author:	David Jones
 *  Date:	12-FEB_1994
 *  Revised:	 9-MAR-1994	Added HTTP_HOST_NAME
 *  Revised:	27-MAY-1994	Added nocache-port support.
 *  Revised:	 7-JUN-1994	Make rule file errors fatal.
 *  Revised:	25-JUN-1994	Common log format.
 *  Revised:	23-JUL-1994	Do congestion checks.
 *  Revised:	29-JUL-1994	Set reentrancy of RTL dynamically at startup.
 *  Revised:	2-AUG-1994	Support of new DECNET_SEARCHLIST module.
 *  Revised:	20-AUG-1994	Add port manage and restart.
 *  Revised:	31-AUG-1994	Add http_server_version globals.
 *  Revised:	 1-SEP-1994	Fixed maxlen in tu_read_line (off by 1).
 *  Revised:	24-SEP-1994	Support ts_set_logging().
 *  Revised:	12-JAN-1995	Addded George Carrettes mods for localaddress
 *				(gjc@village.com).
 *  Revised:	24-FEB-1995	Support port rule in rule file.
 *  Revised:	21-APR-1995	Support statistics counters.
 *  Revised:     1-MAY-1995	Fixed tu_read_line() call, off by 1.
 *  Revised:	 4-MAY-1995	Add WWW_SERVER_PORTS logical.
 *  Revised:	23-MAY-1995	Fixes for OSF/1 version 3.
 *  Revised:	13-JUL-1995	Fix another bug handling buffer overflow
 *				on input loop.
 *  Revised:	15-JUL-1995	Redo declare_port calls to handle pooled
 *				client limits.
 *  Revised:	2-NOV-1995	Distinquish listener thread startup failures.
 *  Revised:	24-NOV-1995	Report correct management host when mulithomed.
 *  Revised:	20-APR-1995	Fix bug in privileged port check.
 *  Revised:	20-JUN-1996	Ignore blank lines while reading keep-alive
 *				requests.
 *  Revised:	26-JUL-1996	Make default reentrancy 1.
 *  Revised:	2-AUG-1996	Let command-line args override port number
 *				specified in rule file.
 *  Revised:	8-OCT-1996	Initialize header parse module (http_header.c).
 *  Revised:	7-DEC-1996	Redo counter module interface for chained
 *				locking.
 * Revised:	3-APR-1997	Reset connection timeout on keep-alives to
 *				regular request timeout after first line
 *				read.
 * REvsied:	15-AUG-1997	Remove small document cache references.
 * Revised:	15-NOV-1997	Use tfc_expire_all to invalidate cache rather
 *				than tcache_delete_all.
 * Revised:	12-DEC-1997	Add http_base_mst_globals struct for linkage
 *				to http_base_mst.exe globals (technically
 *				only required for VAX).
 * Revised:	25-FEB-1998	Set port attributes.
 * Revised:	6-AUG-1999	Include TCP option in server version string.
 * Revised:	23-SEP-1999	Abort startup if client_limit <= 0.
 * Revised:	6-MAR-2000	Hacks for Linux.
 * Revised:	11-JAN-2002	Redo http_ports[] global.  It is now an
 *				array of up to 8 port numbers.
 * Revised:	27-FEB-2002	Add tlog_rundown call before exit.
 */
#include "pthread_1c_np.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef VMS
#ifndef __GNUC__
#include <unixlib.h>
#endif
#include <prvdef.h>
#include <lnmdef.h>
#include <starlet.h>
#include <descrip.h>

int http_max_server_clients();
#else  /* OSF */
#ifdef linux
#include <signal.h>
#endif
#define http_max_server_clients(a,b) a
#define $DESCRIPTOR(a,b) static char *a = b
#if !defined(VMS) || (!defined(__DECC) && !defined(VAXC))
#include <sys/types.h>
uid_t http_saved_uid; gid_t http_saved_gid;
#endif
#endif
#include "tutil.h"		/* threaded utility routines */
#include "file_access.h"
#include "decnet_searchlist.h"
#include "tserver_tcp.h"	/* TCP/IP port listener */
#include "counters.h"		/* statistics counters */
#include "script_manage.h"	/* import decode_manage_host prototype */
#include "http_header.h"	/* Request header parse support */
#include "access.h"
#include "multihome.h"
#include "file_cache.h"

int tlog_putlog ( int level, char * ctlstr, ... );
int (*tlog_putlog_cb) ( int level, char * ctlstr, ... );
int tlog_set_stack_trace ( int level, int (*callback)() );
int peek_init();
int http_start_authenticator();
int http_session ( void *ctx, 
	short port, unsigned char *rem_address, int ndx, int available );
int http_manage_request ( void *ctx, short port, int *shutdown_time );
int tlog_init();
int http_read_rules ();
int http_set_cache_inhibit(), tlog_rundown(), http_set_port_attributes();

/* forward references */
static void setup_security (long *priv_mask, long *prev_priv);
static void load_configuration ( int argc, char **argv, int *client_limit );
static void test_port_rights(long *prev_priv);
static void restart_server(int,char **);
static void setup_manage_host ( char *vname );

#define RESPONSE_LINE_LIMIT 100
#define HTTP_DEFAULT_PORT 80
#define HTTP_DEFAULT_CLIENT_LIMIT 16
#define DEFAULT_CACHE_SIZE 0
#define DEFAULT_LOG_LEVEL 1
typedef struct { int l; char *s; } string;
/*
 * Global (program-wide) variables.
 */
int http_log_level;			/* Logging detail control */
char * http_default_host;		/* Host name for re-directs */
char *http_authenticator_image;		/* Program for authent. sub-process */
int http_dns_enable;			/* If true, lookup IP addresses */
#define PORT_LIMIT 9
int http_ports[PORT_LIMIT];		/* Rule-file port/nocache port */
int http_nocache_ports[PORT_LIMIT];
char *http_port_attributes[PORT_LIMIT];
int http_request_time_limit;		/* timeout for reading request */
int http_response_time_limit;		/* timeout for sending data */
int http_keepalive_limit;		/* Max keepalives allowed or 0 */
int http_keepalive_time;		/* time limit for keepalive */
int http_manage_port;			/* 'magic' client port */
int http_min_available;			/* Threshold for rejecting connects */
int http_session_style;			/* 0-thread/request, 1-thread/connect */
char *http_manage_host;			/* magic client host */
int http_cache_params[4];		/* filecache parameters */
int http_crlf_newline;			/* Mode for text doc. content */
/*
 * Do not trim the extra spaces at the end of http_server_version in order
 * to allow additional items to be appended to the string.
 */
char http_server_version[] = "3.10;ucx                                   ";
char http_server_date[] = "24-MAY-2002";
/*
 * On VAX, universals exported by shareable images are seen by DECC as locations
 * in this image's fixup vector (value at this is address of actual data).
 * Http_base_mst.exe places the globals we need to initialize in a structure
 * with the global address http_base_mst_globals that we see as a pointer
 * to this structure.  VAXC can only see these universals symbols if qualified
 * by globalref, in which case the symbol resolves to the actual data address.
 * To maintain consistent usage for http_base_mst_globals, the shareable
 * must be built to include the symbol http_base_mst_globals_p that holds
 * the address of that image's http_base_mst_globals along with a compiler-time
 * redefine of http_base_mst_globals to http_base_mst_globals_ptr.
 *
 * On ALPHA, the globals in question are exported by the SYMBOL_VECTOR and
 * are shared directly so the http_base_mst_globals pointer is superflous
 * (pointer will be initialized to point local storage).
 */
#ifdef VAXC
#define http_base_mst_globals http_base_mst_globals_p
globalref
#endif
struct global_share {
   int log_level;
   int (*log_callback)(int,char*,...); 
   int reentrant_c, reentrant_vms;
} *http_base_mst_globals;
/*
 * Wrapper routine for exit that flushes logger buffers before standard exit.
 */
static void clean_exit ( int status, char *reason )
{
    tlog_rundown();		/* flush and close log files */
    if ( reason ) fprintf ( stderr, "%s\n", reason );
    exit ( status );		/* exit program */
}
/**************************************************************************/
/* Main program. */
int main ( int argc, char **argv )
{
    int status, port_num, client_limit, exit_status, i;
    long priv_mask[2], prev_priv[2];
    char *envval, *log_file_name, *tcp_ver;
    pthread_t http_thread[9], http_thread2;
    pthread_attr_t client_attr;
    struct global_share fallback_block;

    setup_security(priv_mask,prev_priv);	/* Clear privileges */
    /*
     * Validate command line arguments, defaulting if needed.
     */
    log_file_name = "sys$output";
    if ( argc > 1 ) log_file_name = argv[1];
    /*
     * Load fallback address for base_mst_globals if on ALPHA.
     */
    printf("http_base_mst_globals is %x, loc=%x\n", http_base_mst_globals,
		&http_base_mst_globals );
    if ( !http_base_mst_globals ) http_base_mst_globals = &fallback_block;
    /*
     * Ask the TCP module what driver it talks to and insert in http_server_version.
     */
    tcp_ver = strchr ( http_server_version, ';' );
    if ( tcp_ver ) {
	unsigned int rem_addr; int rem_port;
	static char *tcp_name[] = { "???", "UCX", "Multinet", "Pathway",
		"CMUTCP", "TCPware" };
	ts_set_logging ( tlog_putlog );		/* forces module init */
        status = ts_tcp_info ( (int) 0, &rem_port, &rem_addr );
	if ( status > 0 ) sprintf ( tcp_ver+1, "%s", tcp_name[status] );
	else {
	    /* Trim the semi-colon  */
	    *tcp_ver = '\0';
	}
    }
    /*
     * Init log file and shout out our name.
     */
    http_log_level = DEFAULT_LOG_LEVEL;
    envval = getenv("HTTP_LOG_LEVEL");
    if ( envval ) {
	if ( (*envval == 'C') || (*envval == 'c') ) http_log_level = -1;
	else http_log_level = atoi ( envval );
    }
    http_base_mst_globals->log_level = http_log_level;
    http_base_mst_globals->log_callback = &tlog_putlog;
    tlog_putlog_cb = &tlog_putlog;
    status = tlog_init ( log_file_name );
    printf("Log file '%s' init status: %d\n\n", log_file_name, status );
    tlog_putlog ( 0,
	"HTTP DECThreads server, version !AZ, !AZ, compiled !AZ !AZ!/", 
	http_server_version, http_server_date, __DATE__, __TIME__ );
    /*
     * INitialize modules.
     */
    load_configuration(argc, argv, &client_limit);
    /*
     * Check for adequate privileges to listen on requested port.
     */
    test_port_rights(prev_priv);

    tlog_putlog ( 0,
	"!/Port: !SL, Client limit: !SL, logger level: !SL!/", http_ports[0],
		client_limit, http_log_level );
    if ( client_limit <= 0 ) {
	tlog_putlog ( 0, "Insufficient process quota!/" );
	clean_exit ( 1, "Aborting due to insufficient process quotas" );
    }
    /*
     * Start listening on TCP ports.  Temporarily re-enable privs.
     */
    ts_set_logging ( tlog_putlog );
    INITIALIZE_THREAD_ATTR ( &client_attr );
    pthread_attr_setinheritsched ( &client_attr, PTHREAD_EXPLICIT_SCHED );
    pthread_attr_setstacksize ( &client_attr, 62000 );
#ifdef VMS
    status = sys$setprv ( 1, priv_mask, 0, 0 );
#else
    if ( 0 != setreuid ( getuid (), getuid() ) ) {
#ifdef linux
	/* Main thread exit doesn't seem to kill the other threads, kill
	 * them explicitly */
	status = kill ( 0, SIGTERM );
#endif
	clean_exit ( 1, "Unable to set uid back to original" );
    }
#endif
    for ( i = 0; http_ports[i]; i++ ) {
	/*
	 * All ports we start listening on use same client_attr so will
	 * draw from same client pool.
	 */
        status = ts_declare_tcp_port ( http_ports[i], client_limit, 
		&client_attr, &http_thread[i], http_session);

	tlog_putlog ( 1, "Port !SL declared, status: !SL!/", http_ports[i],
		status );
    }
#ifdef VMS
    (void) sys$setprv ( 0, priv_mask, 0, 0 );
#else
    setgid ( http_saved_gid );
    if ( 0 != setreuid ( getuid (), http_saved_uid ) ) {
#ifdef linux
	/* Main thread exit doesn't seem to kill the other threads, kill
	 * them explicitly */
	status = kill ( 0, SIGTERM );
#endif
	clean_exit ( 1, "Unable to kill privs" );
    }
#endif
    /*
     * Wait for TCP communication thread to rundown.
     */
    if ( status == 0 ) status = 
		pthread_join ( http_thread[0], (void *) &exit_status );
    else {
	tlog_putlog ( 0, "Listener thread startup failure: !SL!/", status );
	exit_status = status;
    }
    tlog_putlog ( 0, "!/listener thread exit status: !SL!/", exit_status );
    if ( (status == 0) && (exit_status == 3) ) restart_server(argc,argv);

#ifdef linux
    /* Main thread exit doesn't seem to kill the other threads, kill
	 * them explicitly */
    status = kill ( 0, SIGKILL );		/* kill all in group */
#endif
    tlog_rundown();		/* close files */
    if ( status == 0 ) return exit_status;
    else return status;
}
/****************************************************************************/
static void setup_security (long *priv_mask, long *prev_priv)
{
#ifdef VMS
    unsigned long time_a[2], time_b[2], time_c[2];
    int status, is_sysgrp();
    int LIB$SUB_TIMES(unsigned long *, unsigned long *, unsigned long *);
    /*
     * Disable sysprv privilege.
     */
    priv_mask[0] = PRV$M_SYSPRV; priv_mask[1] = 0;
    status = sys$setprv ( 0, priv_mask, 0, prev_priv );
    printf("disable SYSPRV status: %d, prev set: %d\n", status,
	(prev_priv[0]&PRV$M_SYSPRV) ? 1 : 0 );
    if ( is_sysgrp() ) printf 
	( "Warning, system group id gives implicit SYSPRV\n" );
     /*
      * Check for delta-time problem.
      */
    time_a[0] = 0; time_a[1] = 12342871;	/* number > 10k days */
    time_b[0] = time_b[1] = 0;
    status = LIB$SUB_TIMES ( time_a, time_b, time_c );
    if ( (status&1) == 0 ) {
	/*
	 * Delta-time patch not present, abort.
	 */
	printf ( "%s%s%s\n",
	  "This program cannot run becaus this system does not support",
	  " delta-time\ncomputation > 10,000 days.  You must apply",
	  " DEC-supplied patches to the\nOpenVMS runtime libraries." );
	exit(status);
    }
#else
#define sys$setprv(a,b,c,d) 1
#if !defined(__DECCx) && !defined(VAXC) && !defined(VMS)
#include <pwd.h>
#include <grp.h>
    int status;
    char *alt_user, *alt_group;
    /*
     * Determine uid/gid we are to run under and make that our effective ID,
     * leave real ID at root so we can enable it again.
     */
    http_saved_gid = getegid();		/* initial defaults */
    http_saved_uid = geteuid();
    if ( alt_user = getenv("HTTP_USER_ID") ) {
	/*
	 * Accept either #nnn or username form.
	 */
	if ( *alt_user == '#' ) http_saved_uid = atoi ( &alt_user[1] );
	else {
	    struct passwd *ent;
	    ent = getpwnam ( alt_user );
	    if ( ent ) {
		http_saved_uid = ent->pw_uid;
		http_saved_gid = ent->pw_gid;
	    }
	    else fprintf(stderr,"Error looking up UID for %s\n", alt_user );
	}
    }
    if ( alt_group = getenv("HTTP_GROUP_ID") ) {
	if ( *alt_group == '#' ) http_saved_gid = atoi ( &alt_group[1] );
	else {
	    struct group *ent;
	    ent = getgrnam ( alt_group );
	    if ( ent ) {
		http_saved_gid = ent->gr_gid;
	    }
	    else fprintf(stderr,"Error looking up group %s\n", alt_group );
	}
    }
    printf("Starting httpd with uid = %d and gid = %d\n",
	http_saved_uid, http_saved_gid );

    if ( 0 != setregid ( getgid (), http_saved_gid ) ) {
	printf("Unable to set gid\n");
	exit ( 1 );
    }
    if ( 0 != setreuid ( getuid (), http_saved_uid ) ) {
	printf("Unable to set uid\n");
	exit ( 1 );
    }
    /*
     * Make ourselves a detached process.
     */
    status = fork();
    if ( status < 0 ) printf( "Error detaching process\n");
    if ( status != 0 ) exit ( status > 0 ? 0 : 1 );
#endif
#endif
}
static void test_port_rights(long *prev_priv) {
    char *envval;
#ifdef VMS
    int i, j;
    int priv_ports[9], port_verified[9];
    /*
     * Build list of ports that require privileges and for each one set the
     * port_verified state to false.
     */
    for ( j = i = 0; http_ports[i]; i++ ) {
	if ( http_ports[i] < 1024 ) {
	    priv_ports[j] = http_ports[i];
	    port_verified[j] = 0;
	    j++;

	    if ( (prev_priv[0]&PRV$M_SYSPRV) == 0 ) {
		printf("%s%s%d%s\n", 
		    "Warning, insufficient privilege to listen on requested",
		    " TCP port ", http_ports[i], " (SYSPRV required)");
	    }
	}
    }
    priv_ports[j] = 0;		/* terminate list */
    /*
     * Check against WWW_SERVER_PORTS logical name to see if allowed,
     * we don't want to listen on a well-known port by accident or malice.
     */
    if ( priv_ports[0] ) {
	int acmode, length, max_index, n, status;
	struct { short length, code; char *buffer; int *retlen; } item[4];
	char port_str[64];
	$DESCRIPTOR(table_name,"LNM$FILE_DEV");
	$DESCRIPTOR(port_logical,"WWW_SERVER_PORTS");
	item[2].code = LNM$_STRING; item[2].length = sizeof(port_str)-1;
	item[2].buffer = port_str; item[2].retlen = &length; length = 0;
	item[1].code = LNM$_ACMODE; item[1].length = sizeof(acmode);
	item[1].buffer = (char *) &acmode; item[1].retlen = (int *) 0;
	item[0].code = LNM$_MAX_INDEX; item[0].length = sizeof(max_index);
	item[0].buffer = (char *) &max_index; item[0].retlen = (int *) 0;
	item[3].code = item[3].length = 0;
	acmode = max_index = 0;
	for ( i = 0; i <= max_index; i++ ) {
	    /*
	     * Get next logical name in list and convert to integer.
	     * Initial call gets max_index, following get partical index.
	     */
	    status = sys$trnlnm ( 0, &table_name, &port_logical, 0, item );
	    if ( ((status&1) == 0) || (acmode > 1) ) break;
	    item[0].code = LNM$_INDEX; item[0].buffer = (char *) &i;
	    port_str[length] = '\0';
	    n = atoi(port_str);
	    for ( j = 0; priv_ports[j]; j++ ) {
		if ( n == priv_ports[j] ) port_verified[j] = 1;
	    }
	}
	for ( j = 0; priv_ports[j]; j++ ) if ( !port_verified[j] ) {
	    printf("Cannot listen on privileged port unless verifed by %s logical\n",
		"WWW_SERVER_PORTS" );
	    clean_exit ( 20, (char *) 0 );
	}
    }
    envval = getenv ( "HTTP_PEEK_PFLAG" );
    if ( envval ) peek_init( atoi(envval) );
#endif
}


static void setup_manage_host ( char *vname )
{
    char *envval;
    envval = getenv ( vname );
    if ( http_manage_port || envval ) {
	int mh_status;
	unsigned int manage_host;
	char *first_host;
	unsigned char *octet;
	/*
	 * Non-zero manage port will enable the management interface,
	 * check for http_manage_host (default is 127.0.0.1).
	 */
	if ( http_manage_port == 0 ) http_manage_port = atoi ( envval );
	else if ( envval ) tlog_putlog ( 0, "Warning - !AZ, !AZ!/",
		"Manage port defined in rule file",
		"HTTP_MANAGE_PORT variable ignored" );
            
	envval = getenv ( "HTTP_MANAGE_HOST" );
	if ( envval ) {
	    /*
	     * Save variable in http_manage_host (in dot format) for decoding 
	     * below.
	     */
	    if ( http_manage_host ) {
		tlog_putlog ( 0, "Warning - !AZ, !AZ!/",
			"Manage port defined in rule file",
			"HTTP_MANAGE_HOST variable ignored" );
            } else if ( tu_strstr ( envval, "." ) ) {
		/* Dot format address */
		http_manage_host = malloc ( tu_strlen ( envval ) + 1 );
		tu_strcpy ( http_manage_host, envval );
	    } else {
		/*
		 * Single integer, conver to dot format.
		 */
		if ( envval ) manage_host = atoi ( envval );
		http_manage_host = malloc ( 32 );
		sprintf ( http_manage_host, "%d.%d.%d.%d", manage_host&255,
		    (manage_host>>8)&255, (manage_host>>16)&255,
		    (manage_host>>24)&255 );
	    }
	} else if ( !http_manage_host ) {
	    http_manage_host = "127.0.0.1";
	}

	if ( !http_decode_manage_host ( http_manage_host, &manage_host,
		&http_manage_port ) ) {
	    tlog_putlog(0,
		"Error decoding manage host, '!AZ', defaulting to loopback!/",
	    	http_manage_host );
	    manage_host = 0x0100007F;
	}
	ts_set_manage_port ( http_manage_port, manage_host, 
		http_manage_request );
	/*
	 * If multihomed and manage_host is loopback, TCP module overrides.
	 */
	mh_status = http_multihome_scan ( 0, &first_host, &octet );
	if ( (manage_host == 0x0100007f) && (mh_status == 1) ) {
	    tlog_putlog ( 0, 
		"Enabled management interface, !UB.!UB.!UB.!UB:!UW (!AZ)!/",
		octet[0], octet[1], octet[2], octet[3], http_manage_port,
		first_host );
	}
	else tlog_putlog ( 0, 
	    "Enabled management interface, requests must come from !UB.!UB.!UB.!UB:!UW!/",
	    manage_host, manage_host>>8, manage_host>>16, manage_host>>24,
	    http_manage_port );
    }
}
void load_configuration(int argc, char **argv, int *client_limit)
{
    int status, cache_bytes, cache_refresh, cache_limit, i;
    char * envval, *rule_file_name;

    if ( argc > 2 ) rule_file_name = argv[2]; else rule_file_name = "";

    http_session_style=1;		/* Distinquishes version 3 from V2 */
    http_counters = (struct counter_block *) 0;
    http_default_host = NULL;
    http_ports[0] = http_ports[1] = http_ports[2] = 0;
    http_nocache_ports[0] = http_nocache_ports[1] = 0;

    envval = getenv("HTTP_REENTRANT_C_RTL");
#ifdef __DECC
    http_reentrant_c_rtl = envval ? atoi ( envval ) : 1;
#else
    http_reentrant_c_rtl = envval ? atoi ( envval ) : 0;
#endif
    http_base_mst_globals->reentrant_c = http_reentrant_c_rtl;
    http_base_mst_globals->reentrant_vms = http_reentrant_vms_rtl;
#ifdef __DECC
    if ( http_reentrant_c_rtl ) {
#ifdef VMS
	decc$set_reentrancy ( C$C_MULTITHREAD );
#endif
        tlog_putlog ( 0, "Set DECC library for multi-thread use!/" );
     }
#endif
    http_min_available = 0;
    envval = getenv("HTTP_MIN_AVAILABLE");
    if ( envval ) http_min_available = atoi ( envval );

    envval = getenv("HTTP_STACK_TRACE");
    if ( envval ) tlog_set_stack_trace ( atoi(envval), ts_tcp_stack_used );
    else tlog_set_stack_trace ( 997, ts_tcp_stack_used );	/* for compatibility */

    status = dnetx_initialize ( "WWWEXEC" );		/* Init node search lists */
    if ( (status&1) == 0 ) clean_exit ( status, "DECnet problem" );

    http_init_standard_atoms();

    *client_limit = HTTP_DEFAULT_CLIENT_LIMIT;
    envval = getenv ( "HTTP_CLIENT_LIMIT" );
    if ( envval ) *client_limit = atoi ( envval );
    envval = getenv ( "HTTP_CRLF_NEWLINE" );
    http_crlf_newline = envval ? atoi ( envval ) : 1;
    tf_initialize("");
    /*
     * Read configuration file.
     */
    status = http_read_rules ( rule_file_name );	/* Load configuration*/
    if ( (status&1) == 0 ) clean_exit ( status, "Rule file problem" );
    /*
     * Check for command-line override of http_ports.
     */
    if ( argc > 3 ) if ( argv[3][0] ) {
	int ndx;
	ndx = http_set_port_attributes ( 0, argv[3] );
	if ( ndx > 0 ) {
	    /*
	     * Port did not match primary port number in config file, supercede.
	     */
	    tlog_putlog ( 0,
		"Overriding port !SL specified in rule file!/", http_ports[0] );
	    http_ports[0] = http_ports[ndx];
	    http_port_attributes[0] = http_port_attributes[ndx];
	    while ( http_ports[ndx] ) {
		http_ports[ndx] = http_ports[ndx+1];
		http_port_attributes[ndx] = http_port_attributes[ndx+1];
		ndx++;
	    }
	}
    }
    if ( argc > 4 ) if ( argv[4][0] ) {
	int ndx, i, j;
	ndx = http_set_port_attributes ( 1, argv[4] );
	if ( ndx < 0 ) {
	    tlog_putlog ( 0, "Invalid nocache port specification: !AZ!/",
		argv[4] );
	} else if ( http_nocache_ports[0] != http_ports[ndx] ) {
	    /*
	     * Port did not match primary port number in config file, warn.
	     */
	    for ( i = 1; http_ports[i]; i++ ) if ( http_ports[i] ==
		http_nocache_ports[0] ) {
		tlog_putlog ( 0,
			"Overriding nocache port !SL specified in rule file!/", 
			http_nocache_ports[0] );
		/*
		 * remove from the ports and nocache ports lists.
		 */
		for ( j = i; http_ports[j]; j++ ) {
		    http_ports[j] = http_ports[j+1];
		    http_port_attributes[j] = http_port_attributes[j+1];
		}
		for ( j = 0; http_nocache_ports[j]; j++ ) {
		    http_nocache_ports[j] = http_nocache_ports[j+1];
		}
		break;
	    }
	}
    }
    /*
     * Use default port if none specified (formerly in rule_file.c).
     */
    if ( http_ports[0] == 0 ) http_set_port_attributes ( 0, "80" );
    /*
     * Set management stuff.
     */
    setup_manage_host("HTTP_MANAGE_PORT");
    /*
     * Initialize document cache.  Size set by global array inited by rule
     * file directives.
     */
    for ( i = 0; http_nocache_ports[i]; i++ ) {
	tlog_putlog ( 0, "Setting nocache on port !SL!/", http_nocache_ports[i] );
	http_set_cache_inhibit ( http_nocache_ports[i] );
    }
    if ( http_cache_params[3] > 0 ) {
	/* Override record size */
	int new_size;
	new_size = tcache_set_max_recsize ( http_cache_params[3] );
	tlog_putlog(1,"Reset data cache max recordsize to !SL (!SL) bytes!/",
		http_cache_params[3], new_size );
    }
    tfc_init ( http_cache_params[0], http_cache_params[1],
	http_cache_params[2] );
    /*
     * Set host name global based on environment variable if rule file didn't
     * set it.
     */
    envval = getenv ( "HTTP_DEFAULT_HOST" );
    if ( (http_default_host == NULL) && envval ) {
	http_default_host = malloc ( strlen ( envval ) + 1 );
	strcpy ( http_default_host, envval );
    }
    /*
     * Start authenticator if defined in rules file.
     */
    if ( http_authenticator_image ) {
	status = http_start_authenticator ( http_authenticator_image );
    if ( http_log_level > 5 ) tlog_putlog ( 5,
	"authenticator image: !AZ, and status: !SL!/",
	http_authenticator_image, status );
    }
    /*
     * Compute client limit, we do after authenticator startup so its
     * resource consumption is included.
     */
    *client_limit = http_max_server_clients ( *client_limit, 
	http_cache_params[1] );
}

static void restart_server(int argc, char **argv)
{
	/*
	 * Restart server 
	 */
#ifdef VMS
	char restart_cmd[1000];
	int length, status, CLI$GET_VALUE(), LIB$DO_COMMAND();
	$DESCRIPTOR ( entity, "$LINE" );
	$DESCRIPTOR ( cmd, "" );

	tlog_putlog(0,"Server restart requested: !%D!/", 0 );
	tlog_rundown();		/* close files */
	tu_strcpy ( restart_cmd, "MCR " );
	cmd.dsc$a_pointer = &restart_cmd[4];
	cmd.dsc$w_length = sizeof(restart_cmd) - 5;
	length = 0;
	status = CLI$GET_VALUE ( &entity, &cmd, &length );
	if ( (status&1) == 1 ) {
	    /*
	     * If $LINE doesn't start with "MCR prepend it.
	     */
	    cmd.dsc$w_length = length;
	    if ( 0!=tu_strncmp("MCR ", &restart_cmd[4], 4) ) {
	        cmd.dsc$a_pointer = restart_cmd;
	        cmd.dsc$w_length = length + 4;
	    }
	    /*
	     * Exit and re-run.
	     */
	    status = LIB$DO_COMMAND ( &cmd );
	    printf("Unexpected continuation from do_command, status: %d...\n",
		status );
#ifndef PTHREAD_USE_D4
	    if ( (status&1) == 0 ) {
		/* Try pausing for a second to let rundown of other threads
		 * finish. */
		struct timespec interval;
		interval.tv_sec = 0;
		interval.tv_nsec = 500000000;
		pthread_delay_np ( &interval );
	    }
#endif
	    exit(status);
	}
#else
	tlog_rundown();
#if !defined(__DECC) && !defined(VAXC)
	execv ( argv[0], argv );
#endif
	printf("restart not supported yet.\n");
#endif
}

/*
 * Old 'small document' cache has been replaced with a new module.  Provide
 * substitute http_invalidate_doc_cache function.
 */
int http_invalidate_doc_cache ( )
{
    /* tcache_delete_all();*/
    tfc_expire_all();
    return 0;
}
