xref: /csrg-svn/usr.sbin/inetd/inetd.c (revision 16510)
116374Skarels #ifndef lint
2*16510Sralph static	char sccsid[] = "@(#)inetd.c	4.2 (Berkeley) 05/18/84";
316374Skarels #endif
416374Skarels 
516374Skarels /*
616374Skarels  * Inetd - Internet super-server
716374Skarels  *
816374Skarels  * This program invokes all internet services as needed.
916374Skarels  * connection-oriented services are invoked each time a
1016374Skarels  * connection is made, by creating a process.  This process
1116374Skarels  * is passed the connection as file descriptor 0 and is
1216374Skarels  * expected to do a getpeername to find out the source host
1316374Skarels  * and port.
1416374Skarels  *
1516374Skarels  * Datagram oriented services are invoked when a datagram
1616374Skarels  * arrives; a process is created and passed a pending message
1716374Skarels  * on file descriptor 0.  Datagram servers may either connect
1816374Skarels  * to their peer, freeing up the original socket for inetd
1916374Skarels  * to receive further messages on, or ``take over the socket'',
2016374Skarels  * processing all arriving datagrams and, eventually, timing
2116374Skarels  * out.	 The first type of server is said to be ``multi-threaded'';
2216374Skarels  * the second type of server ``single-threaded''.
2316374Skarels  *
2416374Skarels  * Inetd uses a configuration file which is read at startup
2516374Skarels  * and, possibly, at some later time in response to a hangup signal.
2616374Skarels  * The configuration file is ``free format'' with fields given in the
2716374Skarels  * order shown below.  Continuation lines for an entry must being with
2816374Skarels  * a space or tab.  All fields must be present in each entry.
2916374Skarels  *
3016374Skarels  *	service name			must be in /etc/services
3116374Skarels  *	socket type			stream/dgram/raw/rdm/seqpacket
3216374Skarels  *	protocol			must be in /etc/protocols
3316374Skarels  *	wait/nowait			single-threaded/multi-threaded
3416374Skarels  *	server program			full path name
3516374Skarels  *	server program arguments	maximum of MAXARGS (5)
3616374Skarels  *
3716374Skarels  * Comment lines are indicated by a `#' in column 1.
3816374Skarels  */
3916374Skarels #include <sys/param.h>
4016374Skarels #include <sys/stat.h>
4116374Skarels #include <sys/ioctl.h>
4216374Skarels #include <sys/socket.h>
4316374Skarels #include <sys/file.h>
4416374Skarels #include <sys/wait.h>
4516374Skarels 
4616374Skarels #include <netinet/in.h>
4716374Skarels #include <arpa/inet.h>
4816374Skarels 
4916374Skarels #include <errno.h>
5016374Skarels #include <stdio.h>
5116374Skarels #include <signal.h>
5216374Skarels #include <netdb.h>
53*16510Sralph #include <syslog.h>
5416374Skarels 
5516374Skarels extern	int errno;
5616374Skarels 
5716374Skarels int	reapchild();
5816374Skarels char	*index();
5916374Skarels char	*malloc();
6016374Skarels 
6116374Skarels int	debug = 0;
6216374Skarels int	allsock;
6316374Skarels int	options;
6416374Skarels struct	servent *sp;
6516374Skarels 
6616374Skarels struct	servtab {
6716374Skarels 	char	*se_service;		/* name of service */
6816374Skarels 	int	se_socktype;		/* type of socket to use */
6916374Skarels 	char	*se_proto;		/* protocol used */
7016374Skarels 	short	se_wait;		/* single threaded server */
7116374Skarels 	short	se_checked;		/* looked at during merge */
7216374Skarels 	char	*se_server;		/* server program */
7316374Skarels #define MAXARGV 5
7416374Skarels 	char	*se_argv[MAXARGV+1];	/* program arguments */
7516374Skarels 	int	se_fd;			/* open descriptor */
7616374Skarels 	struct	sockaddr_in se_ctrladdr;/* bound address */
7716374Skarels 	struct	servtab *se_next;
7816374Skarels } *servtab;
7916374Skarels 
8016374Skarels char	*CONFIG = "/etc/inetd.conf";
8116374Skarels 
8216374Skarels main(argc, argv)
8316374Skarels 	int argc;
8416374Skarels 	char *argv[];
8516374Skarels {
8616374Skarels 	int ctrl;
8716374Skarels 	register struct servtab *sep;
8816374Skarels 	char *cp, buf[50];
8916374Skarels 	int pid, i;
9016374Skarels 
9116374Skarels 	argc--, argv++;
9216374Skarels 	while (argc > 0 && *argv[0] == '-') {
9316374Skarels 		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
9416374Skarels 
9516374Skarels 		case 'd':
9616374Skarels 			debug = 1;
9716374Skarels 			options |= SO_DEBUG;
9816374Skarels 			break;
9916374Skarels 
10016374Skarels 		default:
10116374Skarels 			fprintf(stderr,
10216374Skarels 			    "inetd: Unknown flag -%c ignored.\n", *cp);
10316374Skarels 			break;
10416374Skarels 		}
10516374Skarels nextopt:
10616374Skarels 		argc--, argv++;
10716374Skarels 	}
10816374Skarels 	if (argc > 0)
10916374Skarels 		CONFIG = argv[0];
11016374Skarels #ifndef DEBUG
11116374Skarels 	if (fork())
11216374Skarels 		exit(0);
11316374Skarels 	{ int s;
11416374Skarels 	for (s = 0; s < 10; s++)
11516374Skarels 		(void) close(s);
11616374Skarels 	}
11716374Skarels 	(void) open("/", O_RDONLY);
11816374Skarels 	(void) dup2(0, 1);
11916374Skarels 	(void) dup2(0, 2);
12016374Skarels 	{ int tt = open("/dev/tty", O_RDWR);
12116374Skarels 	  if (tt > 0) {
12216374Skarels 		ioctl(tt, TIOCNOTTY, 0);
12316374Skarels 		close(tt);
12416374Skarels 	  }
12516374Skarels 	}
12616374Skarels #endif
127*16510Sralph 	openlog("inetd", LOG_PID, 0);
12816374Skarels 	config();
12916374Skarels 	signal(SIGHUP, config);
13016374Skarels 	signal(SIGCHLD, reapchild);
13116374Skarels 	for (;;) {
13216374Skarels 		int readable, s, ctrl;
13316374Skarels 
13416374Skarels 		while (allsock == 0)
13516374Skarels 			sigpause(0);
13616374Skarels 		readable = allsock;
13716374Skarels 		if (select(32, &readable, 0, 0, 0) <= 0)
13816374Skarels 			continue;
13916374Skarels 		s = ffs(readable)-1;
14016374Skarels 		if (s < 0)
14116374Skarels 			continue;
14216374Skarels 		for (sep = servtab; sep; sep = sep->se_next)
14316374Skarels 			if (s == sep->se_fd)
14416374Skarels 				goto found;
14516374Skarels 		abort(1);
14616374Skarels 	found:
14716374Skarels 		if (debug)
14816374Skarels 			fprintf(stderr, "someone wants %s\n", sep->se_service);
14916374Skarels 		if (sep->se_socktype == SOCK_STREAM) {
15016374Skarels 			ctrl = accept(s, 0, 0);
151*16510Sralph 			if (debug)
152*16510Sralph 				fprintf(stderr, "accept, ctrl %d\n", ctrl);
15316374Skarels 			if (ctrl < 0) {
15416374Skarels 				if (errno == EINTR)
15516374Skarels 					continue;
156*16510Sralph 				syslog(LOG_WARNING, "accept: %m");
15716374Skarels 				continue;
15816374Skarels 			}
15916374Skarels 		} else
16016374Skarels 			ctrl = sep->se_fd;
16116374Skarels #define mask(sig)	(1 << (sig - 1))
16216374Skarels 		sigblock(mask(SIGCHLD)|mask(SIGHUP));
16316374Skarels 		pid = fork();
16416374Skarels 		if (pid < 0) {
16516374Skarels 			if (sep->se_socktype == SOCK_STREAM)
16616374Skarels 				close(ctrl);
16716374Skarels 			sleep(1);
16816374Skarels 			continue;
16916374Skarels 		}
17016374Skarels 		if (sep->se_wait) {
17116374Skarels 			sep->se_wait = pid;
17216374Skarels 			allsock &= ~(1 << s);
17316374Skarels 		}
17416374Skarels 		sigsetmask(0);
17516374Skarels 		if (pid == 0) {
17616374Skarels #ifdef	DEBUG
17716374Skarels 			int tt = open("/dev/tty", O_RDWR);
17816374Skarels 			if (tt > 0) {
17916374Skarels 				ioctl(tt, TIOCNOTTY, 0);
18016374Skarels 				close(tt);
18116374Skarels 			}
18216374Skarels #endif
18316374Skarels 			dup2(ctrl, 0), close(ctrl), dup2(0, 1);
18416374Skarels 			for (i = getdtablesize(); --i > 2; )
18516374Skarels 				close(i);
18616374Skarels 			if (debug)
18716374Skarels 				fprintf(stderr, "%d execl %s\n",
18816374Skarels 				    getpid(), sep->se_server);
18916374Skarels 			execv(sep->se_server, sep->se_argv);
19016374Skarels 			if (sep->se_socktype != SOCK_STREAM)
191*16510Sralph 				recv(0, buf, sizeof (buf), 0);
192*16510Sralph 			syslog(LOG_ERR, "execv %s: %m", sep->se_server);
19316374Skarels 			_exit(1);
19416374Skarels 		}
19516374Skarels 		if (sep->se_socktype == SOCK_STREAM)
19616374Skarels 			close(ctrl);
19716374Skarels 	}
19816374Skarels }
19916374Skarels 
20016374Skarels reapchild()
20116374Skarels {
20216374Skarels 	union wait status;
20316374Skarels 	int pid;
20416374Skarels 	register struct servtab *sep;
20516374Skarels 
20616374Skarels 	for (;;) {
20716374Skarels 		pid = wait3(&status, WNOHANG, 0);
20816374Skarels 		if (pid <= 0)
20916374Skarels 			break;
21016374Skarels 		if (debug)
21116374Skarels 			fprintf(stderr, "%d reaped\n", pid);
21216374Skarels 		for (sep = servtab; sep; sep = sep->se_next)
21316374Skarels 			if (sep->se_wait == pid) {
21416374Skarels 				if (status.w_status)
215*16510Sralph 					syslog(LOG_WARNING,
216*16510Sralph 					    "%s: exit status 0x%x",
21716374Skarels 					    sep->se_server, status);
21816374Skarels 				if (debug)
21916374Skarels 					fprintf(stderr, "restored %s, fd %d\n",
22016374Skarels 					    sep->se_service, sep->se_fd);
22116374Skarels 				allsock |= 1 << sep->se_fd;
22216374Skarels 				sep->se_wait = 1;
22316374Skarels 			}
22416374Skarels 	}
22516374Skarels }
22616374Skarels 
22716374Skarels config()
22816374Skarels {
22916374Skarels 	register struct servtab *sep, *cp, **sepp;
23016374Skarels 	struct servtab *getconfigent(), *enter();
23116374Skarels 	int omask;
23216374Skarels 
23316374Skarels 	if (!setconfig()) {
234*16510Sralph 		syslog(LOG_ERR, "%s: %m", CONFIG);
23516374Skarels 		return;
23616374Skarels 	}
23716374Skarels 	for (sep = servtab; sep; sep = sep->se_next)
23816374Skarels 		sep->se_checked = 0;
23916374Skarels 	while (cp = getconfigent()) {
24016374Skarels 		for (sep = servtab; sep; sep = sep->se_next)
24116374Skarels 			if (strcmp(sep->se_service, cp->se_service) == 0 &&
24216374Skarels 			    strcmp(sep->se_proto, cp->se_proto) == 0)
24316374Skarels 				break;
24416374Skarels 		if (sep != 0) {
24516374Skarels 			int i;
24616374Skarels 
24716374Skarels 			omask = sigblock(mask(SIGCHLD));
24816374Skarels 			sep->se_wait = cp->se_wait;
24916374Skarels #define SWAP(a, b) { char *c = a; a = b; b = c; }
25016374Skarels 			if (cp->se_server)
25116374Skarels 				SWAP(sep->se_server, cp->se_server);
25216374Skarels 			for (i = 0; i < MAXARGV; i++)
25316374Skarels 				SWAP(sep->se_argv[i], cp->se_argv[i]);
25416374Skarels 			sigsetmask(omask);
25516374Skarels 			freeconfig(cp);
25616374Skarels 		} else
25716374Skarels 			sep = enter(cp);
25816374Skarels 		sep->se_checked = 1;
25916374Skarels 		if (sep->se_fd != -1)
26016374Skarels 			continue;
26116374Skarels 		sp = getservbyname(sep->se_service, sep->se_proto);
26216374Skarels 		if (sp == 0) {
263*16510Sralph 			syslog(LOG_ERR, "%s/%s: unknown service",
26416374Skarels 			    sep->se_service, sep->se_proto);
26516374Skarels 			continue;
26616374Skarels 		}
26716374Skarels 		sep->se_ctrladdr.sin_port = sp->s_port;
26816374Skarels 		if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
269*16510Sralph 			syslog(LOG_ERR, "%s/%s: socket: %m",
27016374Skarels 			    sep->se_service, sep->se_proto);
27116374Skarels 			continue;
27216374Skarels 		}
27316374Skarels 		if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
27416374Skarels 		    setsockopt(sep->se_fd, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
275*16510Sralph 			syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
27616374Skarels 		if (setsockopt(sep->se_fd, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0)
277*16510Sralph 			syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
27816374Skarels 		if (bind(sep->se_fd, &sep->se_ctrladdr,
27916374Skarels 		    sizeof (sep->se_ctrladdr), 0) < 0) {
280*16510Sralph 			syslog(LOG_ERR, "%s/%s: bind: %m",
28116374Skarels 			    sep->se_service, sep->se_proto);
28216374Skarels 			continue;
28316374Skarels 		}
28416374Skarels 		if (sep->se_socktype == SOCK_STREAM)
28516374Skarels 			listen(sep->se_fd, 10);
28616374Skarels 		allsock |= 1 << sep->se_fd;
28716374Skarels 	}
28816374Skarels 	endconfig();
28916374Skarels 	/*
29016374Skarels 	 * Purge anything not looked at above.
29116374Skarels 	 */
29216374Skarels 	omask = sigblock(mask(SIGCHLD));
29316374Skarels 	sepp = &servtab;
29416374Skarels 	while (sep = *sepp) {
29516374Skarels 		if (sep->se_checked) {
29616374Skarels 			sepp = &sep->se_next;
29716374Skarels 			continue;
29816374Skarels 		}
29916374Skarels 		*sepp = sep->se_next;
30016374Skarels 		if (sep->se_fd != -1) {
30116374Skarels 			allsock &= ~(1 << sep->se_fd);
30216374Skarels 			(void) close(sep->se_fd);
30316374Skarels 		}
30416374Skarels 		freeconfig(sep);
30516374Skarels 		free((char *)sep);
30616374Skarels 	}
30716374Skarels 	(void) sigsetmask(omask);
30816374Skarels }
30916374Skarels 
31016374Skarels struct servtab *
31116374Skarels enter(cp)
31216374Skarels 	struct servtab *cp;
31316374Skarels {
31416374Skarels 	register struct servtab *sep;
31516374Skarels 	int omask, i;
31616374Skarels 	char *strdup();
31716374Skarels 
31816374Skarels 	sep = (struct servtab *)malloc(sizeof (*sep));
31916374Skarels 	if (sep == (struct servtab *)0) {
320*16510Sralph 		syslog(LOG_ERR, "Out of memory.");
32116374Skarels 		exit(-1);
32216374Skarels 	}
32316374Skarels 	*sep = *cp;
32416374Skarels 	sep->se_fd = -1;
32516374Skarels 	omask = sigblock(mask(SIGCHLD));
32616374Skarels 	sep->se_next = servtab;
32716374Skarels 	servtab = sep;
32816374Skarels 	sigsetmask(omask);
32916374Skarels 	return (sep);
33016374Skarels }
33116374Skarels 
33216374Skarels FILE	*fconfig = NULL;
33316374Skarels struct	servtab serv;
33416374Skarels char	line[256];
33516374Skarels char	*skip(), *nextline();
33616374Skarels 
33716374Skarels setconfig()
33816374Skarels {
33916374Skarels 
34016374Skarels 	if (fconfig != NULL) {
34116374Skarels 		fseek(fconfig, 0, L_SET);
34216374Skarels 		return (1);
34316374Skarels 	}
34416374Skarels 	fconfig = fopen(CONFIG, "r");
34516374Skarels 	return (fconfig != NULL);
34616374Skarels }
34716374Skarels 
34816374Skarels endconfig()
34916374Skarels {
35016374Skarels 
35116374Skarels 	if (fconfig == NULL)
35216374Skarels 		return;
35316374Skarels 	fclose(fconfig);
35416374Skarels 	fconfig = NULL;
35516374Skarels }
35616374Skarels 
35716374Skarels struct servtab *
35816374Skarels getconfigent()
35916374Skarels {
36016374Skarels 	register struct servtab *sep = &serv;
36116374Skarels 	char *cp, *arg;
36216374Skarels 	int argc;
36316374Skarels 
36416374Skarels 	while ((cp = nextline(fconfig)) && *cp == '#')
36516374Skarels 		;
36616374Skarels 	if (cp == NULL)
36716374Skarels 		return ((struct servtab *)0);
36816374Skarels 	sep->se_service = strdup(skip(&cp));
36916374Skarels 	arg = skip(&cp);
37016374Skarels 	if (strcmp(arg, "stream") == 0)
37116374Skarels 		sep->se_socktype = SOCK_STREAM;
37216374Skarels 	else if (strcmp(arg, "dgram") == 0)
37316374Skarels 		sep->se_socktype = SOCK_DGRAM;
37416374Skarels 	else if (strcmp(arg, "rdm") == 0)
37516374Skarels 		sep->se_socktype = SOCK_RDM;
37616374Skarels 	else if (strcmp(arg, "seqpacket") == 0)
37716374Skarels 		sep->se_socktype = SOCK_SEQPACKET;
37816374Skarels 	else if (strcmp(arg, "raw") == 0)
37916374Skarels 		sep->se_socktype = SOCK_RAW;
38016374Skarels 	else
38116374Skarels 		sep->se_socktype = -1;
38216374Skarels 	sep->se_proto = strdup(skip(&cp));
38316374Skarels 	arg = skip(&cp);
38416374Skarels 	sep->se_wait = strcmp(arg, "wait") == 0;
38516374Skarels 	sep->se_server = strdup(skip(&cp));
38616374Skarels 	argc = 0;
38716374Skarels 	for (arg = skip(&cp); cp; arg = skip(&cp))
38816374Skarels 		if (argc < MAXARGV)
38916374Skarels 			sep->se_argv[argc++] = strdup(arg);
39016374Skarels 	while (argc <= MAXARGV)
39116374Skarels 		sep->se_argv[argc++] = NULL;
39216374Skarels 	return (sep);
39316374Skarels }
39416374Skarels 
39516374Skarels freeconfig(cp)
39616374Skarels 	register struct servtab *cp;
39716374Skarels {
39816374Skarels 	int i;
39916374Skarels 
40016374Skarels 	if (cp->se_service)
40116374Skarels 		free(cp->se_service);
40216374Skarels 	if (cp->se_proto)
40316374Skarels 		free(cp->se_proto);
40416374Skarels 	if (cp->se_server)
40516374Skarels 		free(cp->se_server);
40616374Skarels 	for (i = 0; i < MAXARGV; i++)
40716374Skarels 		if (cp->se_argv[i])
40816374Skarels 			free(cp->se_argv[i]);
40916374Skarels }
41016374Skarels 
41116374Skarels char *
41216374Skarels skip(cpp)
41316374Skarels 	char **cpp;
41416374Skarels {
41516374Skarels 	register char *cp = *cpp;
41616374Skarels 	char *start;
41716374Skarels 
41816374Skarels again:
41916374Skarels 	while (*cp == ' ' || *cp == '\t')
42016374Skarels 		cp++;
42116374Skarels 	if (*cp == '\0') {
42216374Skarels 		char c;
42316374Skarels 
42416374Skarels 		c = getc(fconfig);
42516374Skarels 		ungetc(c, fconfig);
42616374Skarels 		if (c == ' ' || c == '\t')
42716374Skarels 			if (cp = nextline(fconfig))
42816374Skarels 				goto again;
42916374Skarels 		*cpp = (char *)0;
43016374Skarels 		return ((char *)0);
43116374Skarels 	}
43216374Skarels 	start = cp;
43316374Skarels 	while (*cp && *cp != ' ' && *cp != '\t')
43416374Skarels 		cp++;
43516374Skarels 	if (*cp != '\0')
43616374Skarels 		*cp++ = '\0';
43716374Skarels 	*cpp = cp;
43816374Skarels 	return (start);
43916374Skarels }
44016374Skarels 
44116374Skarels char *
44216374Skarels nextline(fd)
44316374Skarels 	FILE *fd;
44416374Skarels {
44516374Skarels 	char *cp;
44616374Skarels 
44716374Skarels 	if (fgets(line, sizeof (line), fconfig) == NULL)
44816374Skarels 		return ((char *)0);
44916374Skarels 	cp = index(line, '\n');
45016374Skarels 	if (cp)
45116374Skarels 		*cp = '\0';
45216374Skarels 	return (line);
45316374Skarels }
45416374Skarels 
45516374Skarels char *
45616374Skarels strdup(cp)
45716374Skarels 	char *cp;
45816374Skarels {
45916374Skarels 	char *new;
46016374Skarels 
46116374Skarels 	if (cp == NULL)
46216374Skarels 		cp = "";
46316374Skarels 	new = malloc(strlen(cp) + 1);
46416374Skarels 	if (new == (char *)0) {
465*16510Sralph 		syslog(LOG_ERR, "Out of memory.");
46616374Skarels 		exit(-1);
46716374Skarels 	}
46816374Skarels 	strcpy(new, cp);
46916374Skarels 	return (new);
47016374Skarels }
471