/*

$Id: main.c,v 1.2 93/11/24 16:38:13 bert Exp $

*/

#include "extern.h"
#include "proto.h"
#include <sys/types.h>
#include <sys/time.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <malloc.h>

char *progname;                 /* what is our name? */
struct clt_sock *f_io = NULL;   /* all the connections: first client */
struct clt_sock *l_io = NULL;                     /* ... last client */

int main(int argc, char **argv)
{
  int l_sock;

  /* get program name from argv[0] */

  if ((progname = strrchr(argv[0],'/')))
    progname++;
  else
    progname = argv[0];

#ifdef BACKGROUND
  /* run in background */

  background();
#endif

  /* install signal handlers */

  deal_with_signals();

  /* start syslogging */

  openlog(progname, SRV_SYSLOG_OPT, SRV_SYSLOG_LVL);

  /* allocate server socket */

  if ((l_sock = open_socket(SRV_PORT)) < 0) {
    SYSLOG_ERROR("can't open socket!");
    exit(1);
  }

  while (1) {
    struct clt_sock *x_io, *y_io;
    fd_set readfds;
    int maxfd, a_sock, del;
    struct sockaddr_in addr;
    int size = sizeof(addr);
    struct timeval noblock = {0, 0};

    /* accept a connection and deal with the client */

    if ((a_sock = accept(l_sock, (struct sockaddr*)&addr, &size)) >= 0) {
      if (l_io) {        /* if there are other sockets, tack to the end... */
	l_io->next = (struct clt_sock*) malloc(sizeof(struct clt_sock));
	l_io = (l_io->next);
      } else {                                /* ...otherwise just plug in */
	f_io = l_io = (struct clt_sock*) malloc(sizeof(struct clt_sock));
      }
      CHECK_MALLOC(l_io);

      l_io->next = NULL;                       /* initialize the structure */
      l_io->method = l_io->path = l_io->ver = l_io->agent = l_io->reply = NULL;
      l_io->begin = l_io->size = 0;
      l_io->fd = a_sock;
      l_io->host = find_peer_name(&addr);
    }
    else if (errno != EWOULDBLOCK)         /* this'll usually try to block */
      SYSLOG_EWARN("accept call failed");

    /* check if Discuss or clients have things to tell us */

    if (f_io) {                     /* if we have no clients, don't bother */
      x_io = f_io;
      maxfd = -1;
      FD_ZERO(&readfds);                    /* set up the fd_set structure */
      while (x_io) {
	if (x_io->fd > maxfd) maxfd = x_io->fd;
	FD_SET(x_io->fd, &readfds);
	x_io = x_io->next;
      }
      while ((select(maxfd+1, &readfds,NULL,NULL, &noblock)) < 0) {
	if (errno!=EINTR) {
	  SYSLOG_ERROR("error in select");
	  exit(1);
	}
      }

      x_io = f_io;
      y_io = NULL;
      while (x_io) {
	if (x_io->reply) {                           /* do we have output? */
	  write(x_io->fd, x_io->reply, strlen(x_io->reply));
	  del = 1;
	} else if (FD_ISSET(x_io->fd, &readfds)) {     /* do we get input? */
	  del = (talk_to_client(x_io) < 0);
	} else del = 0;

	if (del) {                            /* get rid of this client... */
	  close(x_io->fd);
	  free(x_io->host);
	  free(x_io->method);  free(x_io->path);  free(x_io->ver);
	  free(x_io->agent);
	  free(x_io->reply);
	  if (! x_io->next)
	    l_io = y_io;
	  if (y_io) {
	    y_io->next = x_io->next;
	    free(x_io);
	    x_io = y_io->next;
	  } else {
	    f_io = x_io->next;
	    free(x_io);
	    x_io = f_io;
	  }
	} else {                            /* ...or just look at next one */
	  y_io = x_io;
	  x_io = x_io->next;
	}
      }
    }

    /* now go back to the loop */
  }
}
