/*
 * server.c 
  This is the entry point of a rather general server S. Thorne 6/30/90
  Each connection is handled by its own light weight process(LWP).
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/file.h>
#include <signal.h>
#include "inet.h"
#include "lwp/lwp.h"
#include "event.h"

CONN            conntab[FD_SETSIZE]; 

fd_set   read_fds;

#define	sock_readable(s)	FD_ISSET(s, &read_fds)

#define	conn_readable(cn)	sock_readable(conntab[cn].c_socket)
#define	conn_established(cn)	(conntab[cn].c_socket != -1)
#define	conn_timedout(cn)	(conntab[cn].c_flags & C_TIMEOUT) 

static int      num_connection;
static int      listen_sock;
extern int      PROV; /* the socket # of the current provider */
char *msglist[100];
int	debug = 0; /* no debugging*/

load_msgs() /* load the file of error messages. error file has structure of
	     <error #>:<error text> */
{

	FILE           *fopen(), *msgfile;
        char      msgline[101];
        int pos = 0;
        int len;

	msgfile = fopen(MSG_FILE, "r");
	if (!msgfile) {
		perror(MSG_FILE);
		puts("Could not find the message file");
		exit(1);
	}
	while (fgets(msgline, 100, msgfile) != '\0') {
	        len = strlen(msgline);
		msgline[len - 1] = '\0';
		msglist[pos] = (char *) domalloc(len);
		strcpy(msglist[pos],msgline);
           pos++;
			 
	}
	fclose(msgfile);
	return 0;		/* close file */

}

void
broken_pipe()
{
	puts("SIGPIPE - No one to read our write.");
}


log_session(line)		    /* write the line to the log file */
	char           *line;
{
	int dfd;
	if ((dfd = open(LOG_FILE, O_APPEND | O_CREAT | O_WRONLY, 0644)) == 0)
		printf("error logging transaction\n");
	write(dfd, line, strlen(line));
	close(dfd);
	return;
}

close_connection(CONN *conn)
{
	time_t          secs;
	char       line[100];

	time(&secs);
	printf("Closing connection to %s, port %d, socket #%d.\n",
	       conn->c_hostname,
	       conn->c_portnum,
	       conn->c_socket);
	sprintf(line,"%d  %s  %.24s, active %.1f minutes - SERVER\n",
		conn->c_portnum,
		conn->c_hostname,
		ctime(&secs),
		(float) (secs - conn->c_made) /60.0);
	free(conn->c_hostname);
	close(conn->c_socket);
	conn->c_socket = -1;
	num_connection--;
	log_session(line);
}


/*
 * Find the connection using the given socket, and mark it as having
 * timed out, so the server can close it off next time around.
 */

void
sock_timeout(int sock)
{
	int	i;

	for (i = 0; i < FD_SETSIZE; i++) {
		if (conntab[i].c_socket == sock) {
			conntab[i].c_flags |= C_TIMEOUT;
			break;
		}
	}
	printf("Sock #%d marked for timeout termination.\n",
		conntab[i].c_socket);
}

void
server_shutdown()
{
	int             i;

	log_trans("Shuting down the server");
	puts("\nServer shutdown, closing connections:");

	for (i = 0; i < FD_SETSIZE; i++)	/* Close Connections */
		if (conn_established(i))
			close_connection(&conntab[i]);

	close(listen_sock);  
	puts("Server has shut down -- Exiting.");
	exit(0);
}

CONN *
make_new_connection(listen_sock)
	int             listen_sock;
{
	CONN           *conn;
	char           *hostname,line[100];
	int             length, i, red;
	time_t    secs;
	time(&secs);

	for (i = 0; i < FD_SETSIZE; i++) 
	  if (!conn_established(i))
	    break;

	if (i  == FD_SETSIZE) {
		printf("Server: at %d connection limit.\n",
		       FD_SETSIZE);
		return;
	}
	conn = &conntab[i];
	conn->c_socket = inet_accept_connection(listen_sock, &hostname,
						&conn->c_portnum);

	if (conn->c_socket == -1) {
		puts("accept_connection failed");
		return;
	}
	conn->c_hostname = (char *) domalloc(strlen(hostname) + 1);
	strcpy(conn->c_hostname, hostname);
	conn->c_uid = (char *) domalloc(20), *conn->c_uid = '\0';
	conn->c_flags = 0;
	conn->c_type = "uknkown";
	conn->c_last = time(&conn->c_made);
	conn->sendbuf_pos = &(conn->sendbuf);

	num_connection++;
	printf("Accepted new connection to %s, thru port %d, over socket #%d.\n",
	       conn->c_hostname,
	       conn->c_portnum,
	       conn->c_socket);

	write(conn->c_socket, BANNER, strlen(BANNER));
	write(conn->c_socket, EOM, EOM_LEN);

	if (fcntl(conn->c_socket, F_SETFL, FNDELAY) == -1)
		perror("fcntl"); /* non-blocking socket */

	sprintf(line,"%d  %s  %.24s - SERVER\n",
		conn->c_portnum,
		conn->c_hostname,
		ctime(&secs));
	log_session(line);
	return conn; /* connection # */
}

static	time_t          secs;

static int   DormantConn = 0; /* Number of connections waiting for activity */
static int   WaitConn = 0; /* # of connections with trans, swapped out */

SleepThread() 
{
	WaitConn++;
}

WakeThread()
{
	WaitConn--;
}

#define	POLL	&tv
#define BLOCK	0

CONN *
get_curr_conn() {
  int i;

  for (i = 0; i < FD_SETSIZE; i++) /* do we need to look at them all ? */
    if (LWP_ActiveProcess == conntab[i].c_pid)
      return &conntab[i];
  return 0;

}

void
hdl_conn() /* called by server_loop, multi thread, one exists for each conn */
{
CONN *conn;
fd_set read_fds;
int rc, quit;

      conn = make_new_connection(listen_sock);
      conn->c_pid = LWP_ActiveProcess;
      for (;;) {

	FD_ZERO(&read_fds);
	FD_SET(conn->c_socket, &read_fds);
	DormantConn++;
	rc  = IOMGR_Select(FD_SETSIZE,	&read_fds, 0, 0,BLOCK);
	DormantConn--;
	printf("   %d Connections: %d dormant & %d active (%d waiting)\n",
	       num_connection,DormantConn,(num_connection-DormantConn),WaitConn);
		if (sock_readable(conn->c_socket))
		  quit = hdl_transact(conn);
	        else {
		  printf("nothing to read\n");
		  close_connection(conn);
		  LWP_DestroyProcess(LWP_ActiveProcess);
		}
	if (quit) {
		  close_connection(conn);
		  LWP_DestroyProcess(LWP_ActiveProcess);
		}
      }
}

void
server_loop()	 /* create a LWP for each connection */	
{
PROCESS procptr;
fd_set read_fds;
int rc;
  for (;;) {
	FD_ZERO(&read_fds);
	FD_SET(listen_sock, &read_fds);
	rc = IOMGR_Select(FD_SETSIZE,	&read_fds, 0, 0,BLOCK);
		if (sock_readable(listen_sock))
		  LWP_CreateProcess(hdl_conn,10240,3,0,"conn",&procptr);
	      }
}


static PROCESS server_proc;
int type_size;
int sponsor_size;
int source_size;

main(argc, argv)
int argc;
char *argv[];
{
	PROCESS		main_proc;
	int i;

	if (argc > 1)
		debug = TRUE;	
	
	for (i = 0; i < FD_SETSIZE; i++)
		conntab[i].c_socket = -1;	
	signal(SIGINT, server_shutdown);
	printf("\n%s\n", BANNER);
	time(&secs);
	printf("Started %.24s, pid = %d\n\n", ctime(&secs), getpid());
	listen_sock = inet_make_listner(EVENTS_PORT);
	if (listen_sock < 0) {
		puts("Unable to accept connections!");
		exit(1);
	}
	if (access(PROV_FILE, F_OK)) {
		puts("Could not find the provider file");
		exit(1);
	}
	FD_ZERO(&read_fds);
	log_trans("Starting server");
	num_connection = 0;
	signal(SIGPIPE, broken_pipe);
	load_msgs(); /* load the message list from disk */
	bzero(type_table,MAX_TYPES*sizeof(TABLE_ENTRY));
	bzero(source_table,MAX_SOURCES*sizeof(TABLE_ENTRY));
	bzero(sponsor_table,MAX_SPONSORS*sizeof(TABLE_ENTRY));
	type_size = load_table(type_table,TYPE_FILE); /* load valid tables */
	sponsor_size = load_table(sponsor_table,SPONSOR_FILE);
	source_size = load_table(source_table,SOURCE_FILE);
	
        LWP_InitializeProcessSupport(4, &main_proc);
	LWP_CreateProcess(server_loop, 10240, 3, 0, "Server", &server_proc);

	/*
	 * Immediatly we do this destroy, which will free up main
	 * and then leave the server_loop as all that's left.
	 */

	IOMGR_Initialize();

	LWP_DestroyProcess(main_proc);
}





