Ultrix-3.1/src/etc/inetd.c

Compare this file to the similar file:
Show the results in this format:


/**********************************************************************
 *   Copyright (c) Digital Equipment Corporation 1984, 1985, 1986.    *
 *   All Rights Reserved. 					      *
 *   Reference "/usr/src/COPYRIGHT" for applicable restrictions.      *
 **********************************************************************/

#ifndef lint
/*
 * Based on "@(#)inetd.c	1.1	(ULTRIX)	4/11/85";
 * and "@(#)inetd.c	5.1 (Berkeley) 5/28/85";
 */
static char *Sccsid = "@(#)inetd.c	3.0	(ULTRIX-11)	4/22/86";
#endif

/*-----------------------------------------------------------------------
 *	Modification History
 *
 *	4/10/85 -- jrs
 *		Clean up little nits in code.  Also add timer to select
 *		call so we can momentarily ignore multithreaded datagram
 *		connections in order to give the server time to pick up
 *		the initial packet before we try to listen to the socket again.
 *
 *	Based on 4.2BSD labeled:
 *		inetd.c	4.2	84/05/18
 *
 *-----------------------------------------------------------------------
 */

/*
 * Inetd - Internet super-server
 *
 * This program invokes all internet services as needed.
 * connection-oriented services are invoked each time a
 * connection is made, by creating a process.  This process
 * is passed the connection as file descriptor 0 and is
 * expected to do a getpeername to find out the source host
 * and port.
 *
 * Datagram oriented services are invoked when a datagram
 * arrives; a process is created and passed a pending message
 * on file descriptor 0.  Datagram servers may either connect
 * to their peer, freeing up the original socket for inetd
 * to receive further messages on, or ``take over the socket'',
 * processing all arriving datagrams and, eventually, timing
 * out.	 The first type of server is said to be ``multi-threaded'';
 * the second type of server ``single-threaded''. 
 *
 * Inetd uses a configuration file which is read at startup
 * and, possibly, at some later time in response to a hangup signal.
 * The configuration file is ``free format'' with fields given in the
 * order shown below.  Continuation lines for an entry must being with
 * a space or tab.  All fields must be present in each entry.
 *
 *	service name			must be in /etc/services
 *	socket type			stream/dgram/raw/rdm/seqpacket
 *	protocol			must be in /etc/protocols
 *	wait/nowait			single-threaded/multi-threaded
 *	user				user to run daemon as
 *	server program			full path name
 *	server program arguments	maximum of MAXARGS (5)
 *
 * Comment lines are indicated by a `#' in column 1.
 */

#include <sys/param.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/file.h>
#ifdef	vax
#include <sys/wait.h>
#else	pdp11
#include <wait.h>
#define	getdtablesize()	NOFILE
#endif	vax

#include <netinet/in.h>
#include <arpa/inet.h>

#include <errno.h>
#include <stdio.h>
#include <signal.h>
#ifdef	pdp11
#define	signal sigset
#endif	pdp11
#include <netdb.h>
#include <syslog.h>
#include <pwd.h>

extern	int errno;

int	reapchild();
char	*index();
char	*malloc();

int	debug = 0;
#ifndef	pdp11
int	allsock;
int	delaysock = 0;
#else	pdp11
long	allsock;
long	delaysock = 0;
#endif	pdp11
#ifndef	pdp11
struct	timeval deltime = { 1, 0 };
struct	timeval *seltimer = NULL;
#else	pdp11
int	deltime = 1;
int	*seltimer = NULL;
#endif	pdp11
int	options;
struct	servent *sp;

struct	servtab {
	char	*se_service;		/* name of service */
	int	se_socktype;		/* type of socket to use */
	char	*se_proto;		/* protocol used */
	short	se_wait;		/* single threaded server */
	short	se_checked;		/* looked at during merge */
	char	*se_user;		/* user name to run as */
	char	*se_server;		/* server program */
#define MAXARGV 5
	char	*se_argv[MAXARGV+1];	/* program arguments */
	int	se_fd;			/* open descriptor */
	struct	sockaddr_in se_ctrladdr;/* bound address */
	struct	servtab *se_next;
} *servtab;

char	*CONFIG = "/etc/inetd.conf";

main(argc, argv)
	int argc;
	char *argv[];
{
	int ctrl;
	register struct servtab *sep;
	register struct passwd *pwd;
	char *cp, buf[50];
	int pid, i;

	argc--, argv++;
	while (argc > 0 && *argv[0] == '-') {
		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {

		case 'd':
			debug = 1;
			options |= SO_DEBUG;
			break;

		default:
			fprintf(stderr,
			    "inetd: Unknown flag -%c ignored.\n", *cp);
			break;
		}
nextopt:
		argc--, argv++;
	}
	if (argc > 0)
		CONFIG = argv[0];
#ifndef DEBUG
	if (fork())
		exit(0);
	{ int s;
	for (s = 0; s < 10; s++)
		(void) close(s);
	}
	(void) open("/", O_RDONLY);
	(void) dup2(0, 1);
	(void) dup2(0, 2);
	{ int tt = open("/dev/tty", O_RDWR);
	  if (tt > 0) {
		(void) ioctl(tt, TIOCNOTTY, 0);
		(void) close(tt);
	  }
	}
#endif
	openlog("inetd", LOG_PID, 0);
	config();
	(void) signal(SIGHUP, config);
	(void) signal(SIGCHLD, reapchild);
	for (;;) {
#ifndef	pdp11
		int readable, s, ctrl;
#else	pdp11
		long readable;
		int s, ctrl;
#endif	pdp11

		while (allsock == 0)
			sigpause(0);
		readable = allsock;
		if (select(32, &readable, 0, 0, seltimer) <= 0) {
			if (readable == 0 && seltimer != NULL) {
				allsock |= delaysock;
				delaysock = 0;
				seltimer = NULL;
			}
			continue;
		}
		s = ffs(readable)-1;
		if (s < 0)
			continue;
		for (sep = servtab; sep; sep = sep->se_next)
			if (s == sep->se_fd)
				goto found;
		abort(1);
	found:
		if (debug)
			fprintf(stderr, "someone wants %s\n", sep->se_service);
		if (sep->se_socktype == SOCK_STREAM) {
			ctrl = accept(s, 0, 0);
			if (debug)
				fprintf(stderr, "accept, ctrl %d\n", ctrl);
			if (ctrl < 0) {
				if (errno == EINTR)
					continue;
				syslog(LOG_WARNING, "accept: %m");
				continue;
			}
		} else
			ctrl = sep->se_fd;
#ifndef	pdp11
#define mask(sig)	(1L << (sig - 1))
		(void) sigblock(mask(SIGCHLD)|mask(SIGHUP));
#else	pdp11
		sighold(SIGCHLD);
		sighold(SIGHUP);
#endif	pdp11
		pid = fork();
		if (pid < 0) {
			if (sep->se_socktype == SOCK_STREAM)
				(void) close(ctrl);
			sleep(1);
			continue;
		}
		if (sep->se_wait) {
			sep->se_wait = pid;
			allsock &= ~(1L << s);
		} else {
			if (sep->se_socktype == SOCK_DGRAM) {
				delaysock |= (1L << s);
				allsock &= ~(1L << s);
				seltimer = &deltime;
			}
		}
#ifndef	pdp11
		(void) sigsetmask(0);
#else	pdp11
		sigrelse(SIGCHLD);
		sigrelse(SIGHUP);
#endif	pdp11
		if (pid == 0) {
#ifdef	DEBUG
			int tt = open("/dev/tty", O_RDWR);
			if (tt > 0) {
				(void) ioctl(tt, TIOCNOTTY, 0);
				(void) close(tt);
			}
#endif
			(void) dup2(ctrl, 0);
			(void) close(ctrl);
			(void) dup2(0, 1);
			for (i = getdtablesize(); --i > 2; )
				(void) close(i);
			if ((pwd = getpwnam(sep->se_user)) == NULL) {
				syslog(LOG_ERR, "getpwnam: %s: No such user"
					,sep->se_user);
				exit(1);
			}
			(void) setgid(pwd->pw_gid);
#ifndef	pdp11
			initgroups(pwd->pw_name, pwd->pw_gid);
#endif	pdp11
			(void) setuid(pwd->pw_uid);
			if (debug)
				fprintf(stderr, "%d execl %s\n",
				    getpid(), sep->se_server);
			execv(sep->se_server, sep->se_argv);
			if (sep->se_socktype != SOCK_STREAM)
				(void) recv(0, buf, sizeof (buf), 0);
			syslog(LOG_ERR, "execv %s: %m", sep->se_server);
			_exit(1);
		}
		if (sep->se_socktype == SOCK_STREAM)
			(void) close(ctrl);
	}
}

reapchild()
{
	union wait status;
	int pid;
	register struct servtab *sep;

	for (;;) {
#ifndef	pdp11
		pid = wait3(&status, WNOHANG, 0);
#else	pdp11
		pid = wait2(&status, WNOHANG);
#endif	pdp11
		if (pid <= 0)
			break;
		if (debug)
			fprintf(stderr, "%d reaped\n", pid);
		for (sep = servtab; sep; sep = sep->se_next)
			if (sep->se_wait == pid) {
				if (status.w_status)
					syslog(LOG_WARNING,
					    "%s: exit status 0x%x",
					    sep->se_server, status);
				if (debug)
					fprintf(stderr, "restored %s, fd %d\n",
					    sep->se_service, sep->se_fd);
				allsock |= 1L << sep->se_fd;
				sep->se_wait = 1;
			}
	}
}

config()
{
	register struct servtab *sep, *cp, **sepp;
	struct servtab *getconfigent(), *enter();
	int omask, on = 1;

	if (!setconfig()) {
		syslog(LOG_ERR, "%s: %m", CONFIG);
		return;
	}
	for (sep = servtab; sep; sep = sep->se_next)
		sep->se_checked = 0;
	while (cp = getconfigent()) {
		for (sep = servtab; sep; sep = sep->se_next)
			if (strcmp(sep->se_service, cp->se_service) == 0 &&
			    strcmp(sep->se_proto, cp->se_proto) == 0)
				break;
		if (sep != 0) {
			int i;

#ifndef	pdp11
			omask = sigblock(mask(SIGCHLD));
#else	pdp11
			sighold(SIGCHLD);
#endif	pdp11
			sep->se_wait = cp->se_wait;
#define SWAP(a, b) { char *c = a; a = b; b = c; }
			if (cp->se_server)
				SWAP(sep->se_server, cp->se_server);
			for (i = 0; i < MAXARGV; i++)
				SWAP(sep->se_argv[i], cp->se_argv[i]);
#ifndef	pdp11
			(void) sigsetmask(omask);
#else	pdp11
			sigrelse(SIGCHLD);
#endif	pdp11
			freeconfig(cp);
		} else
			sep = enter(cp);
		sep->se_checked = 1;
		if (sep->se_fd != -1)
			continue;
		sp = getservbyname(sep->se_service, sep->se_proto);
		if (sp == 0) {
			syslog(LOG_ERR, "%s/%s: unknown service",
			    sep->se_service, sep->se_proto);
			continue;
		}
		sep->se_ctrladdr.sin_port = sp->s_port;
		if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
			syslog(LOG_ERR, "%s/%s: socket: %m",
			    sep->se_service, sep->se_proto);
			continue;
		}
#define	turnon(fd, opt) \
	setsockopt(fd, SOL_SOCKET, opt, &on, sizeof (on))
		if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
		    turnon(sep->se_fd, SO_DEBUG) < 0)
			syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
		if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
			syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
#undef turnon
		if (bind(sep->se_fd, &sep->se_ctrladdr,
		    sizeof (sep->se_ctrladdr), 0) < 0) {
			syslog(LOG_ERR, "%s/%s: bind: %m",
			    sep->se_service, sep->se_proto);
			continue;
		}
		if (sep->se_socktype == SOCK_STREAM)
			(void) listen(sep->se_fd, 10);
		allsock |= 1L << sep->se_fd;
	}
	endconfig();
	/*
	 * Purge anything not looked at above.
	 */
#ifndef	pdp11
	omask = sigblock(mask(SIGCHLD));
#else	pdp11
	sighold(SIGCHLD);
#endif	pdp11
	sepp = &servtab;
	while (sep = *sepp) {
		if (sep->se_checked) {
			sepp = &sep->se_next;
			continue;
		}
		*sepp = sep->se_next;
		if (sep->se_fd != -1) {
			allsock &= ~(1L << sep->se_fd);
			if ((delaysock & (1L << sep->se_fd)) != 0) {
				delaysock &= ~(1L << sep->se_fd);
				if (delaysock == 0) {
					seltimer = NULL;
				}
			}
			(void) close(sep->se_fd);
		}
		freeconfig(sep);
		free((char *)sep);
	}
#ifndef	pdp11
	(void) sigsetmask(omask);
#else	pdp11
	sigrelse(SIGCHLD);
#endif	pdp11
}

struct servtab *
enter(cp)
	struct servtab *cp;
{
	register struct servtab *sep;
	int omask, i;
	char *strdup();

	sep = (struct servtab *)malloc(sizeof (*sep));
	if (sep == (struct servtab *)0) {
		syslog(LOG_ERR, "Out of memory.");
		exit(-1);
	}
	*sep = *cp;
	sep->se_fd = -1;
#ifndef	pdp11
	omask = sigblock(mask(SIGCHLD));
	sep->se_next = servtab;
	servtab = sep;
	(void) sigsetmask(omask);
#else	pdp11
	sighold(SIGCHLD);
	sep->se_next = servtab;
	servtab = sep;
	(void) sigrelse(SIGCHLD);
#endif	pdp11
	return (sep);
}

FILE	*fconfig = NULL;
struct	servtab serv;
char	line[256];
char	*skip(), *nextline();

setconfig()
{

	if (fconfig != NULL) {
		(void) fseek(fconfig, 0L, L_SET);
		return (1);
	}
	fconfig = fopen(CONFIG, "r");
	return (fconfig != NULL);
}

endconfig()
{

	if (fconfig == NULL)
		return;
	(void) fclose(fconfig);
	fconfig = NULL;
}

struct servtab *
getconfigent()
{
	register struct servtab *sep = &serv;
	char *cp, *arg;
	int argc;

	while ((cp = nextline(fconfig)) && *cp == '#')
		;
	if (cp == NULL)
		return ((struct servtab *)0);
	sep->se_service = strdup(skip(&cp));
	arg = skip(&cp);
	if (strcmp(arg, "stream") == 0)
		sep->se_socktype = SOCK_STREAM;
	else if (strcmp(arg, "dgram") == 0)
		sep->se_socktype = SOCK_DGRAM;
	else if (strcmp(arg, "rdm") == 0)
		sep->se_socktype = SOCK_RDM;
	else if (strcmp(arg, "seqpacket") == 0)
		sep->se_socktype = SOCK_SEQPACKET;
	else if (strcmp(arg, "raw") == 0)
		sep->se_socktype = SOCK_RAW;
	else
		sep->se_socktype = -1;
	sep->se_proto = strdup(skip(&cp));
	arg = skip(&cp);
	sep->se_wait = strcmp(arg, "wait") == 0;
	sep->se_user = strdup(skip(&cp));
	sep->se_server = strdup(skip(&cp));
	argc = 0;
	for (arg = skip(&cp); cp; arg = skip(&cp))
		if (argc < MAXARGV)
			sep->se_argv[argc++] = strdup(arg);
	while (argc <= MAXARGV)
		sep->se_argv[argc++] = NULL;
	return (sep);
}

freeconfig(cp)
	register struct servtab *cp;
{
	int i;

	if (cp->se_service)
		free(cp->se_service);
	if (cp->se_proto)
		free(cp->se_proto);
	if (cp->se_server)
		free(cp->se_server);
	for (i = 0; i < MAXARGV; i++)
		if (cp->se_argv[i])
			free(cp->se_argv[i]);
}

char *
skip(cpp)
	char **cpp;
{
	register char *cp = *cpp;
	char *start;

again:
	while (*cp == ' ' || *cp == '\t')
		cp++;
	if (*cp == '\0') {
		char c;

		c = getc(fconfig);
		(void) ungetc(c, fconfig);
		if (c == ' ' || c == '\t')
			if (cp = nextline(fconfig))
				goto again;
		*cpp = (char *)0;
		return ((char *)0);
	}
	start = cp;
	while (*cp && *cp != ' ' && *cp != '\t')
		cp++;
	if (*cp != '\0')
		*cp++ = '\0';
	*cpp = cp;
	return (start);
}

char *
nextline(fd)
	FILE *fd;
{
	char *cp;

	if (fgets(line, sizeof (line), fd) == NULL)
		return ((char *)0);
	cp = index(line, '\n');
	if (cp)
		*cp = '\0';
	return (line);
}

char *
strdup(cp)
	char *cp;
{
	char *new;

	if (cp == NULL)
		cp = "";
	new = malloc(strlen(cp) + 1);
	if (new == (char *)0) {
		syslog(LOG_ERR, "Out of memory.");
		exit(-1);
	}
	(void) strcpy(new, cp);
	return (new);
}