#include "innbbsconf.h"
#include "daemon.h"
#include <signal.h>
#include <setjmp.h>

static jmp_buf timebuf;

static void
timeout(sig)
  int sig;
{
  longjmp(timebuf, sig);
}

extern int errno;
static void reapchild (s)
int s;
{
	int state;
	while (waitpid(-1,&state,WNOHANG|WUNTRACED)>0) {
	/*	printf("reaping child\n");*/
	}
}

void dokill(s)
int s;
{
   kill(0,SIGKILL);
}

static INETDstart = 0;
int startfrominetd(flag)
{
   INETDstart = flag ;
}


int standalonesetup(fd)
int fd;
{
        int on =1;
	struct linger foobar;
	if (setsockopt(fd,SOL_SOCKET, SO_REUSEADDR,(char *)&on,sizeof(on)) < 0)
   	   syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
	foobar.l_onoff = 0;
	if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&foobar, sizeof (foobar))<0)
	   syslog(LOG_ERR, "setsockopt (SO_LINGER): %m");
}

static char *UNIX_SERVER_PATH;
static int (*halt)();

sethaltfunction(haltfunc)
int (*haltfunc)();
{
    halt = haltfunc;
}

void docompletehalt(s)
int s;
{
	/*printf("try to remove %s\n", UNIX_SERVER_PATH);
	unlink(UNIX_SERVER_PATH);*/
	exit(0);
	/*dokill();*/
}

void doremove(s)
int s;
{
   if (halt != NULL)
      (*halt)(s);
   else 
      docompletehalt(s);
}


initunixserver(path, protocol)
char *path;
char *protocol;
{
	struct sockaddr_un s_un;   
	/* unix endpoint address */
	struct protoent *pe;   /*protocol information entry*/
	int s;
	char *ptr;

	bzero((char*)&s_un,sizeof(s_un));
	s_un.sun_family= AF_UNIX;
	strcpy(s_un.sun_path, path);
	if (protocol==NULL)
	     protocol="tcp";
        /* map protocol name to protocol number */
	pe=getprotobyname(protocol);
	if (pe==NULL) {
	 fprintf(stderr,"%s: Unknown protocol.\n",protocol);
	 return (-1);
	}

        /* Allocate a socket */
	s = socket(PF_UNIX,strcmp(protocol,"tcp")?SOCK_DGRAM:SOCK_STREAM,0);
	if (s<0) {
		printf("protocol %d\n", pe->p_proto);
		perror("socket");
		return -1;
	}
	/*standalonesetup(s);*/
        signal(SIGHUP, SIG_IGN);
        signal(SIGUSR1, SIG_IGN);
	signal(SIGCHLD,reapchild);
	UNIX_SERVER_PATH = path;
	signal(SIGINT,doremove);
	signal(SIGTERM,doremove);

	chdir("/");
	if (bind(s,(struct sockaddr*)&s_un,sizeof (struct sockaddr_un))<0){
		perror("bind");
		perror(path);
		return -1;
	}
	listen(s,10);
	return s;
}

initinetserver(service,protocol)
char *service;
char *protocol;
{
        struct servent *se;    /*service information entry*/
	struct hostent *he;    /*host information entry*/
	struct protoent *pe;   /*protocol information entry*/
	struct sockaddr_in sin;/*Internet endpoint address*/
	int port,s;
	int randomport=0;

	bzero((char*)&sin,sizeof(sin));
	sin.sin_family= AF_INET;
	if (!strcmp("0",service)) {
		randomport = 1;
		sin.sin_addr.s_addr = INADDR_ANY;
	}

	if (service==NULL) 
	     service=DEFAULTPORT;
	if (protocol==NULL)
	     protocol="tcp";
        /* map service name to port number */
	/* service ---> port */
	se = getservbyname(service,protocol);
	if (se==NULL) {
	  port = htons((u_short)atoi(service)); 
	  if (port==0 && !randomport) {
	   fprintf (stderr, "%s/%s: Unknown service.\n",service,protocol);
	   return (-1);
	  }
        } else 
	        port=se->s_port;
	sin.sin_port = port;

        /* map protocol name to protocol number */
	pe=getprotobyname(protocol);
	if (pe==NULL) {
	 fprintf(stderr,"%s: Unknown protocol.\n",protocol);
	 return (-1);
	}

        /* Allocate a socket */
	s = socket(PF_INET,strcmp(protocol,"tcp")?SOCK_DGRAM:SOCK_STREAM,pe->p_proto);
	if (s<0) {
		perror("socket");
		return -1;
	}
	standalonesetup(s);
        signal(SIGHUP, SIG_IGN);
        signal(SIGUSR1, SIG_IGN);
	signal(SIGCHLD,reapchild);
	signal(SIGINT,dokill);
	signal(SIGTERM,dokill);

	chdir("/");
	if (bind(s,(struct sockaddr*)&sin,sizeof (struct sockaddr_in))<0){
		perror("bind");
		return -1;
	}
	listen(s,10);
#ifdef DEBUG
	{ int length=sizeof(sin);
	getsockname(s,&sin,&length);
	printf("portnum alocalted %d\n",sin.sin_port);
	}
#endif
	return s;
}

int open_unix_listen(path,protocol,initfunc)
char *path;
char *protocol;
int (*initfunc) ARG((int));
{
	int s;
	s = initunixserver(path,protocol);
	if (s<0) {
		return -1;
	}
	if (initfunc != NULL) {
	   printf("in inetsingleserver before initfunc s %d\n",s);
	   if ((*initfunc)(s)<0) {
		perror("initfunc error");
		return -1;
	   }
	   printf("end inetsingleserver before initfunc \n");
        }
	return s;
}

int open_listen(service,protocol,initfunc)
char *service;
char *protocol;
int (*initfunc) ARG((int));
{
	int s;
	if (! INETDstart)
	  s = initinetserver(service,protocol);
        else
	  s = 0;
	if (s<0) {
		return -1;
	}
	if (initfunc != NULL) {
	   printf("in inetsingleserver before initfunc s %d\n",s);
	   if ((*initfunc)(s)<0) {
		perror("initfunc error");
		return -1;
	   }
	   printf("end inetsingleserver before initfunc \n");
        }
	return s;
}

int inetsingleserver(service,protocol,serverfunc,initfunc)
char *service;
char *protocol;
int (*initfunc) ARG((int));
int (*serverfunc) ARG((int));
{
	int s;
	if (!INETDstart)
	  s = initinetserver(service,protocol);
        else
	  s = 0;
	if (s<0) {
		return -1;
	}
	if (initfunc != NULL) {
	   printf("in inetsingleserver before initfunc s %d\n",s);
	   if ((*initfunc)(s)<0) {
		perror("initfunc error");
		return -1;
	   }
	   printf("end inetsingleserver before initfunc \n");
        }
	{
		int ns=tryaccept(s);
		int result=0;
		if (ns < 0 && errno != EINTR){
#ifdef DEBUGSERVER
			perror("accept");
#endif
		}
		close(s);
		if (serverfunc != NULL)
		   result = (*serverfunc)(ns);
		close(ns);
		return(result);
	}
}


int tryaccept(s)
int s;
{
	int ns,fromlen;
	struct sockaddr sockaddr;/*Internet endpoint address*/
	fromlen=sizeof (struct sockaddr_in);

#ifdef DEBUGSERVER
	fputs("Listening again\n",stdout);
#endif
	do {
	   ns = accept(s,&sockaddr,&fromlen);
	   errno = 0;
	} while ( ns < 0 && errno == EINTR );
	return ns;
}

int inetserver(service,protocol,serverfunc)
char *service;
char *protocol;
int (*serverfunc) ARG((int));
{
	int port,s;

        if (!INETDstart)
	 s = initinetserver(service,protocol);
        else
	 s = 0;
	if (s<0) {
		return -1;
	}
	for (;;) {
		int ns=tryaccept(s);
		int result=0;
		int pid;
		if (ns < 0 && errno != EINTR){
#ifdef DEBUGSERVER
			perror("accept");
#endif
			continue;
		}
#ifdef DEBUGSERVER
		fputs("Accept OK\n",stdout);
#endif
		pid = fork();
		if (pid==0) {
			close(s);
			if (serverfunc != NULL)
			   result = (*serverfunc)(ns);
			close(ns);
			exit(result);
		} else if (pid < 0) {
			perror("fork");
			return -1;
		}
		close(ns);
	}
	return 0;
}

int inetclient(server,service,protocol) 
char *server;
char *protocol;
char *service;
{
        struct servent *se;    /*service information entry*/
	struct hostent *he;    /*host information entry*/
	struct protoent *pe;   /*protocol information entry*/
	struct sockaddr_in sin;/*Internet endpoint address*/
	int port,s;

	bzero((char*)&sin,sizeof(sin));
	sin.sin_family= AF_INET;

	if (service==NULL) 
	     service=DEFAULTPORT;
	if (protocol==NULL)
	     protocol="tcp";
	if (server==NULL)
	     server=DEFAULTSERVER;
        /* map service name to port number */
	/* service ---> port */
	se = getservbyname(service,protocol);
	if (se==NULL) {
	  port = htons((u_short)atoi(service)); 
	  if (port==0) {
	   fprintf (stderr, "%s/%s: Unknown service.\n",service,protocol);
	   return (-1);
	  }
        } else 
	        port=se->s_port;
	sin.sin_port = port;

	/* map server hostname to IP address, allowing for dotted decimal */
	he=gethostbyname(server);
	if (he==NULL) {
	  sin.sin_addr.s_addr = inet_addr(server);
	  if (sin.sin_addr.s_addr==INADDR_NONE) {
	   fprintf (stderr, "%s: Unknown host.\n",server);
	   return (-1);
	  }
	} else
	  bcopy(he->h_addr,(char*)&sin.sin_addr,he->h_length);

        /* map protocol name to protocol number */
	pe=getprotobyname(protocol);
	if (pe==NULL) {
	 fprintf(stderr,"%s: Unknown protocol.\n",protocol);
	 return (-1);
	}

        /* Allocate a socket */
	s = socket(PF_INET,strcmp(protocol,"tcp")?SOCK_DGRAM:SOCK_STREAM,pe->p_proto);
	if (s<0) {
		perror("socket");
		return -1;
	}

       if (setjmp(timebuf) == 0)
       {
        signal(SIGALRM, timeout);
        alarm(5);
        if (connect(s,(struct sockaddr*)&sin,sizeof(sin))<0)
          {
                alarm(0);
                return -1;
          }
       }
       else
       {
                alarm(0);
                return -1;
       }
        alarm(0);

	return s;
}

int unixclient(path,protocol) 
char *path;
char *protocol;
{
	struct protoent *pe;    /*protocol information entry*/
	struct sockaddr_un s_un;/*unix endpoint address*/
	int s;

	bzero((char*)&s_un,sizeof(s_un));
	s_un.sun_family= AF_UNIX;

	if (path==NULL) 
	     path=DEFAULTPATH;
	if (protocol==NULL)
	     protocol="tcp";
	strcpy(s_un.sun_path , path);

        /* map protocol name to protocol number */
	pe=getprotobyname(protocol);
	if (pe==NULL) {
	 fprintf(stderr,"%s: Unknown protocol.\n",protocol);
	 return (-1);
	}
        /* Allocate a socket */
	s = socket(PF_UNIX,strcmp(protocol,"tcp")?SOCK_DGRAM:SOCK_STREAM,0);
	if (s<0) {
		perror("socket");
		return -1;
	}
	/* Connect the socket to the server */
	if (connect(s,(struct sockaddr*)&s_un,sizeof(s_un))<0) {
		/*perror("connect");*/
		return -1;
	}
	return s;
}