/*
 * main.c - webster server
 *
 * This file has all the code to handle the network connections.
 *
 * David A. Curry
 * Purdue University
 * Engineering Computer Network
 * April, 1986
 *
 * $Log:	main.c,v $
 * Revision 1.5  91/07/09  00:20:44  ambar
 * reformatted
 * 
 * Revision 1.4  88/02/25  22:47:20  ambar
 * changed the location of our header files.
 * 
 * Revision 1.3  88/02/20  05:39:13  ambar
 * ifdef'd out if SUNRPC is defined.
 * 
 * Revision 1.2  87/06/16  23:40:25  jtkohl
 * fix lossage with signals and openlog
 * 
 * Revision 1.1  87/06/13  19:21:54  jtkohl
 * Initial revision
 * 
 * Revision 1.3  86/12/26  22:03:43  davy
 * Changed cpu time limit to 15 minutes.
 * 
 * Revision 1.2  86/12/26  22:02:38  davy
 * Changed to work with DBM files.
 * 
 */
#ifndef lint
static char rcsid_main_c[] = "$Header: /afs/sipb.mit.edu/project/sipbsrc/src/webster/src/server/RCS/main.c,v 1.5 91/07/09 00:20:44 ambar Exp $";
#endif

#ifndef SUNRPC

#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <syslog.h>
#include <netdb.h>
#include <errno.h>
#include <stdio.h>
#include <ndbm.h>

#include <index.h>
#include <webster.h>
#include <wordfiles.h>

FILE *input;				/* input file pointer		*/
FILE *output;				/* output file pointer		*/

DBM *db;				/* database file pointer	*/
FILE *indexfp;				/* index file pointer		*/
struct header hdr;			/* index file header		*/

extern int errno;

main()
{
	char *whereis();
	union wait status;
	struct rlimit rlim;
	register int i, s, sock;
	int length, myport, hisport;
	char myname[64], hisname[64];
	extern int byebye(), pickup();
	struct sockaddr_in mysin, hissin;

#ifndef DEBUG
	/*
	 * Detach from the parent.
	 */
	if (fork())
		exit(0);

	/*
	 * Zap the process group.
	 */
	setpgrp(0, 0);

	/*
	 * Close files.
	 */
	for (i = 0; i < NOFILE; i++)
		close(i);

	/*
	 * Ignore signals.
	 */
	for (i = 0; i < NSIG; i++)
		signal(i, SIG_IGN);

	signal(SIGTERM, byebye);
	signal(SIGXCPU, byebye);
	signal(SIGCHLD, pickup);
	signal(SIGSEGV, SIG_DFL);
	signal(SIGBUS, SIG_DFL);
	signal(SIGFPE, SIG_DFL);

	/*
	 * Reset stdin, stdout, stderr.
	 */
	open("/dev/null", O_RDONLY);
	open("/dev/null", O_WRONLY);
	dup(1);

	/*
	 * Clear controlling tty.
	 */
	if ((i = open("/dev/tty", O_RDWR)) > 0) {
		ioctl(i, TIOCNOTTY, 0);
		close(i);
	}
#endif

	/*
	 * Kick our limits.
	 */
	rlim.rlim_max = rlim.rlim_cur = RLIM_INFINITY;
	setrlimit(RLIMIT_FSIZE, &rlim);
	setrlimit(RLIMIT_STACK, &rlim);
	setrlimit(RLIMIT_DATA, &rlim);

	/*
	 * Open the logfile.
	 */
	openlog("websterd", LOG_PID, LOG_LOCAL5);

	/*
	 * Figure out who we are.
	 */
	whoami(myname, &myport, &mysin);

	/*
	 * Get a socket.
	 */
	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		syslog(LOG_ERR, "socket: %m");
		exit(1);
	}

	/*
	 * Bind a name to the socket, so clients can connect
	 * to us.
	 */
	if (bind(sock, &mysin, sizeof(struct sockaddr_in)) < 0) {
		syslog(LOG_ERR, "bind: %m");
		exit(1);
	}

	/*
	 * Listen for connections.
	 */
	if (listen(sock, 5) < 0) {
		syslog(LOG_ERR, "listen: %m");
		exit(1);
	}

	/*
	 * Forever...
	 */
	for (;;) {
		/*
		 * Pick up dead children.
		 */
		while (wait3(&status, WNOHANG, 0) > 0)
			;

		/*
		 * Accept a connection.  This blocks until a
		 * connection arrives.
		 */
		length = sizeof(struct sockaddr_in);

		/*
		 * EINTR is okay; that comes from SIGCHLD's
		 * coming in.
		 */
		if ((s = accept(sock, &hissin, &length)) < 0) {
			if (errno != EINTR)
				syslog(LOG_ERR, "accept: %m");
			continue;
		}

		/*
		 * Save information about our client.
		 */
		hisport = ntohs(hissin.sin_port);
		strcpy(hisname, whereis(hissin.sin_addr.s_addr));

		/*
		 * The child process waits on the client.
		 */
		if (fork() == 0) {
			input = fdopen(s, "r");
			output = fdopen(s, "w");

			/*
			 * Chop CPU time limit in case we don't get
			 * disconnected right and spin instead.
			 */
			rlim.rlim_max = rlim.rlim_cur = 15 * 60;  /* 15 min */
			setrlimit(RLIMIT_CPU, &rlim);

			/*
			 * Load the index file, then
			 * go to the dictionary.
			 */
			loadindex();
			webster();
			byebye();
		}

		close(s);
	}
}

/*
 * whoami - find out what host we're on, what port we should
 *	    be listening to, etc.
 */
whoami(name, port, sin)
char *name;
int *port;
struct sockaddr_in *sin;
{
	char host[64];
	register struct hostent *hp;
	register struct servent *sp;
	struct hostent *gethostbyname();
	struct servent *getservbyname();

	/*
	 * Get our hostname.
	 */
	if (gethostname(host, sizeof(host)) < 0) {
		syslog(LOG_ERR, "gethostname: %m");
		exit(1);
	}

	/*
	 * Look ourselves up.
	 */
	if ((hp = gethostbyname(host)) == NULL) {
		syslog(LOG_ERR, "%s: unknown host.", host);
		exit(1);
	}

	/*
	 * Get the socket number we listen on.
	 */
	if ((sp = getservbyname(WEBSTERNAME, "tcp")) == NULL)
		*port = WEBSTERPORT;
	else
		*port = ntohs(sp->s_port);

	/*
	 * Use our formal name.
	 */
	strcpy(name, hp->h_name);

	/*
	 * Construct our address.
	 */
	bzero(sin, sizeof(struct sockaddr_in));

	sin->sin_family = AF_INET;
	sin->sin_port = htons(*port);
	bcopy(hp->h_addr, &(sin->sin_addr), hp->h_length);
}

/*
 * whereis - find out the host name which goes with an address.
 */
char *whereis(addr)
long addr;
{
	register struct hostent *hp;
	struct hostent *gethostbyaddr();

	if ((hp = gethostbyaddr(&addr, sizeof(long), AF_INET)) == NULL)
		return("(unknown)");

	return(hp->h_name);
}

/*
 * loadindex - read in the index file header, open the database.
 */
loadindex()
{
	FILE *fp;
	char buf[BUFSIZ];

	/*
	 * Header.
	 */
	sprintf(buf, "%s/%s", wordfiledir, wordfilehdr);

	if ((fp = fopen(buf, "r")) == NULL) {
		syslog(LOG_ERR, "cannot open \"%s\".", buf);
		exit(1);
	}

	if (fread(&hdr, sizeof(struct header), 1, fp) != 1) {
		syslog(LOG_ERR, "bad hdr read from \"%s\".", buf);
		exit(1);
	}

	fclose(fp);

	/*
	 * Index file.
	 */
	sprintf(buf, "%s/%s", wordfiledir, wordfileindex);
	
	if ((indexfp = fopen(buf, "r")) == NULL) {
		syslog(LOG_ERR, "cannot open \"%s\".", buf);
		exit(1);
	}

	/*
	 * Database.
	 */
	if ((db = dbm_open(buf, O_RDONLY, 0644)) == NULL) {
		syslog(LOG_ERR, "cannot open index database.");
		exit(1);
	}
}

byebye()
{
	register int i;
	static int beenhere = 0;

	/*
	 * Sigh.  This lets us avoid hanging on a close.
	 */
	if (!beenhere) {
		beenhere = 1;

		signal(SIGALRM, byebye);
		alarm(30);

		for (i = 0; i < NOFILE; i++)
			close(i);
	}

	_exit(0);
}

pickup()
{
	union wait status;

	while (wait3(&status, WNOHANG, 0) > 0)
		;
}

#ifdef DEBUG
openlog(s, p, n)
char *s;
int p, n;
{
	return;
}

syslog(n, s, a)
int n;
char *s, *a;
{
	fprintf(stderr, s, a);
}
#endif

#endif !SUNRPC
