#include "innbbsconf.h"
#include "daemon.h"

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;
	}
	/* Connect the socket to the server */
	if (connect(s, (struct sockaddr *) & sin, sizeof(sin)) < 0) {
		/* perror("connect"); */
		return -1;
	}
	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;
}
