#ifndef	lint
static char *copyright = "\n\
\n\
Copyright (c) 1993, 1996, 1999 by Bennet Yee.\n\
\n\
This material was developed by the author.  Permission to copy this\n\
software, to redistribute it, and to use it for any purpose is hereby\n\
granted, subject to the following five restrictions.\n\
\n\
1. Any copy made of this software must include this copyright notice in\n\
full.\n\
\n\
2. All materials containing this software or derivatives thereof must\n\
provide this software in source form or provide a means of obtaining\n\
this software in source form; no fees may be charged for this software\n\
except those for covering media costs and handling.\n\
\n\
3. All materials developed as a consequence of the use of this software\n\
shall duly acknowledge such use, in accordance with the usual standards\n\
of acknowledging credit in academic research.\n\
\n\
4. The author makes no warranty or representation that the operation\n\
of this software will be error-free, and the author is under no\n\
obligation to provide any services, by way of maintenance, update, or\n\
otherwise.\n\
\n\
5. In conjunction with products arising from the use of this material,\n\
there shall be no use of the names of the author, of Carnegie-Mellon\n\
University, nor of any adaptation thereof in any advertising,\n\
promotional, or sales literature without prior written consent from the\n\
author and Carnegie-Mellon University in each case.\n\
\n\
If you have questions, you can contact the author at bsy@cs.cmu.edu\n\
\n\
Users of this software is requested to make their best efforts (a) to\n\
return to the author any improvements or extensions that they make, so\n\
that these may be included in future releases; (b) to document clearly\n\
any improvements or extensions made to this software; and (c) to inform\n\
the author of noteworthy uses of this software.  Any improvements or\n\
extensions to this software may be placed under different copyright\n\
restrictions by the author of those improvements or extensions.  That\n\
those improvements or extensions be placed under the same copyright as\n\
this software is encouraged.\n\
";
#endif	/* lint */

/*
 * pseudo-inetd -- new sources, not based on Berklix/AT&T code.
 *
 * First pass:  much more limited that inetd -- no configuration table,
 * we just listen to a particular tcp socket as specified at the command line
 * and then run a inetd-style server program with the socket at stdin/out.
 *
 * Hacked from my how.to/tcp.client.server/server.c
 *
 * 12/13/90, -bsy
 */

#include <stdio.h>
#include <ctype.h>
#include <syslog.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/file.h>
#if	!defined(posix_tty)
# include <sgtty.h>
#endif
#include <netinet/in.h>
#include <sys/errno.h>
#include <unistd.h>
#include <string.h>
#if	!defined(O_APPEND)
# include <sys/fcntl.h>
#endif

extern int	errno;

char		*me;

#define	SERVERPORT	5103

unsigned long	servers_allowed = ~0ul;
int		verbose = 0;

reaper()
{
	/* linux / SysV style reenablement, bletch */
#if	!defined(USE_SIGSET)
	(void) signal(SIGCHLD,(void (*)(int)) reaper);
#endif
	if (verbose) write(2,"reaper: ",8);
#if	defined(_POSIX_VERSION)
	if (wait((int *) 0) > 0) {
		++servers_allowed;
		if (verbose) write(2,".",1);
	}
#else
	if (wait((union wait *) 0) > 0) {
		++servers_allowed;
		if (verbose) write(2,".",1);
	}
#endif
	if (verbose) write(2," reaped\n",8);
}

usage()
{
	fprintf(stderr,"Usage:  %s [-dlv] [-p port] [-c count-to-exit] [-s server-count-limit] [-D descriptor-num] -- cmd [args...]\n",me);
}

struct dlist {
	struct dlist	*nxt;
	int		d;
} def1, def2, def3;

main(ac,av)
int	ac;
char	**av;
{
	int			s, ss;
	struct sockaddr_in	name;
	int			set,i,ch,namelen;
#if	defined(TIOCNOTTY)
	int			detach;
#endif
	u_long			client;
	u_short			port = SERVERPORT;
	int			log = 0, count = -1;

	struct dlist		*dlist = 0, *nd;
	int			alias;

	extern int		getopt(), optind;
	extern char		*optarg;

	me = strrchr(av[0],'/');
	if (!me) me = av[0]; else ++me;

#if	defined(TIOCNOTTY)
	detach = 0;
#endif
	while ((ch = getopt(ac,av,"c:lLp:s:d:Dv")) != EOF) switch (ch) {
	case 'c':	count = atoi(optarg);		break;
	case 'l':	log = 1;			break;
	case 'L':	log = 0;			break;
	case 'p':	port = atoi(optarg);		break;
	case 's':	servers_allowed = atoi(optarg);	break;
#if	defined(TIOCNOTTY)
	case 'D':	detach = 1;			break;
#else
	case 'D':
		fprintf(stderr,"%s:  detach option ignored; POSIX has no way to force a disassociation\n from the control terminal except implicitly through process group\n orphanhood.\n",me);
		break;
#endif
	case 'd':
		if (!(nd = (struct dlist *) malloc(sizeof *nd))) {
			perror(me);
			fprintf(stderr,"%s:  no memory for descriptor list\n",me);
			exit(1);
		}
		nd->nxt = dlist;
		nd->d = atoi(optarg);
		dlist = nd;
		break;
	case 'v':	verbose = 1;			break;
	default:	usage(); exit(1);
	}

	if (optind >= ac) {
		usage(); exit(1);
	}
	if (!dlist) {
		dlist = &def1;
		def1.nxt = &def2; def1.d = 0;
		def2.nxt = &def3; def2.d = 1;
		def3.nxt = 0; def3.d = 2;
	}
#if	defined(TIOCNOTTY)
	if (detach) {
		i = open("/dev/tty",O_RDONLY,0);
		if (i != -1) {
			(void) ioctl(i,TIOCNOTTY,(char *) 0);
			(void) close(i);
		}
	}
#endif
	if ((s = socket(PF_INET,SOCK_STREAM,0)) == -1) {
		perror("socket");
		exit(1);
	}

	set = 1;
	if (setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(const char *) &set, sizeof(set)) == -1) {
		perror("setsockoopt");
		exit(1);
	}

	memset((char *)&name,0,sizeof(name));
	name.sin_family = AF_INET;
	name.sin_port = htons(port);

	if (bind(s,(struct sockaddr *) &name,sizeof(name)) == -1) {
		perror("bind");
		exit(1);
	}

	/* backlogs?  how many client at a time? */
	if (listen(s,4) == -1) {
		perror("listen");
		exit(1);
	}

#if	USE_SIGSET
	(void) sigset(SIGCHLD,(void (*)(int)) reaper);
#else
	/* linux, bsd */
	(void) signal(SIGCHLD,(void (*)(int)) reaper);
#endif
again:
	while ((namelen = sizeof name), ((ss = accept(s,(struct sockaddr *)&name,&namelen)) != -1)) {
		client = name.sin_addr.s_addr;
		client = ntohl(client);
		if (verbose) {
			/* we do not use the other sockaddr info */
			fprintf(stderr,"[%d.%d.%d.%d]\n",(client>>24)&0xff,(client>>16)&0xff,(client>>8)&0xff,client&0xff);
		}
		if (log) {
			char	buf[256];
			sprintf(buf,"netd: Connect on port %d from %d.%d.%d.%d",port,(client>>24)&0xff,(client>>16)&0xff,(client>>8)&0xff,client&0xff);
			syslog(LOG_INFO | LOG_DAEMON,buf);
		}
		/* run prog */
		switch (fork()) {
		case 0:
			(void) close(s);
			alias = 0;
			for (nd = dlist; nd; nd = nd->nxt) {
				if (nd->d != ss) dup2(ss,nd->d);
				else ++alias;
			}
			if (!alias) (void) close(ss);
			execvp(av[optind],av+optind);
			perror("exec");
			exit(1);
		case -1:
			perror("fork");
			exit(1);
		default:
			(void) close(ss);
			--servers_allowed;
			break;
		}
		if (count > 0 && --count == 0) exit(0);
		while (!servers_allowed) {
			if (verbose) fprintf(stderr,"Pausing\n");
			pause();
			if (verbose) fprintf(stderr,"paused, servers_allowed %d\n",servers_allowed);
		}
	}
	if (errno == EINTR) goto again;
	perror("accept");
	exit(1);
	/* NOTREACHED */
}
