xref: /csrg-svn/usr.sbin/inetd/inetd.c (revision 66778)
121134Sdist /*
2*66778Spendry  * Copyright (c) 1983, 1991, 1993, 1994
361830Sbostic  *	The Regents of the University of California.  All rights reserved.
433136Sbostic  *
542799Sbostic  * %sccs.include.redist.c%
621134Sdist  */
721134Sdist 
816374Skarels #ifndef lint
961830Sbostic static char copyright[] =
10*66778Spendry "@(#) Copyright (c) 1983, 1991, 1993, 1994\n\
1161830Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1233136Sbostic #endif /* not lint */
1316374Skarels 
1421134Sdist #ifndef lint
15*66778Spendry static char sccsid[] = "@(#)inetd.c	8.3 (Berkeley) 04/13/94";
1633136Sbostic #endif /* not lint */
1721134Sdist 
1816374Skarels /*
1916374Skarels  * Inetd - Internet super-server
2016374Skarels  *
2157769Sandrew  * This program invokes all internet services as needed.  Connection-oriented
2257769Sandrew  * services are invoked each time a connection is made, by creating a process.
2357769Sandrew  * This process is passed the connection as file descriptor 0 and is expected
2457769Sandrew  * to do a getpeername to find out the source host and port.
2516374Skarels  *
2616374Skarels  * Datagram oriented services are invoked when a datagram
2716374Skarels  * arrives; a process is created and passed a pending message
2816374Skarels  * on file descriptor 0.  Datagram servers may either connect
2916374Skarels  * to their peer, freeing up the original socket for inetd
3016374Skarels  * to receive further messages on, or ``take over the socket'',
3116374Skarels  * processing all arriving datagrams and, eventually, timing
3216374Skarels  * out.	 The first type of server is said to be ``multi-threaded'';
3316374Skarels  * the second type of server ``single-threaded''.
3416374Skarels  *
3516374Skarels  * Inetd uses a configuration file which is read at startup
3616374Skarels  * and, possibly, at some later time in response to a hangup signal.
3716374Skarels  * The configuration file is ``free format'' with fields given in the
3816374Skarels  * order shown below.  Continuation lines for an entry must being with
3916374Skarels  * a space or tab.  All fields must be present in each entry.
4016374Skarels  *
4157769Sandrew  *	service name			must be in /etc/services or must
4257769Sandrew  *					name a tcpmux service
4316374Skarels  *	socket type			stream/dgram/raw/rdm/seqpacket
4416374Skarels  *	protocol			must be in /etc/protocols
4516374Skarels  *	wait/nowait			single-threaded/multi-threaded
4617346Sbloom  *	user				user to run daemon as
4716374Skarels  *	server program			full path name
4840740Sbostic  *	server program arguments	maximum of MAXARGS (20)
4916374Skarels  *
5057769Sandrew  * TCP services without official port numbers are handled with the
5157769Sandrew  * RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for
5257769Sandrew  * requests. When a connection is made from a foreign host, the service
5357769Sandrew  * requested is passed to tcpmux, which looks it up in the servtab list
5457769Sandrew  * and returns the proper entry for the service. Tcpmux returns a
5557769Sandrew  * negative reply if the service doesn't exist, otherwise the invoked
5657769Sandrew  * server is expected to return the positive reply if the service type in
5757769Sandrew  * inetd.conf file has the prefix "tcpmux/". If the service type has the
5857769Sandrew  * prefix "tcpmux/+", tcpmux will return the positive reply for the
5957769Sandrew  * process; this is for compatibility with older server code, and also
6057769Sandrew  * allows you to invoke programs that use stdin/stdout without putting any
6157769Sandrew  * special server code in them. Services that use tcpmux are "nowait"
6257769Sandrew  * because they do not have a well-known port and hence cannot listen
6357769Sandrew  * for new requests.
6457769Sandrew  *
6516374Skarels  * Comment lines are indicated by a `#' in column 1.
6616374Skarels  */
6716374Skarels #include <sys/param.h>
6816374Skarels #include <sys/stat.h>
6916374Skarels #include <sys/ioctl.h>
7016374Skarels #include <sys/socket.h>
7116374Skarels #include <sys/wait.h>
7226921Slepreau #include <sys/time.h>
7326921Slepreau #include <sys/resource.h>
7416374Skarels 
7516374Skarels #include <netinet/in.h>
7616374Skarels #include <arpa/inet.h>
7716374Skarels 
7816374Skarels #include <errno.h>
7957769Sandrew #include <fcntl.h>
8016374Skarels #include <netdb.h>
8117346Sbloom #include <pwd.h>
8257769Sandrew #include <signal.h>
8336603Sbostic #include <stdio.h>
8457769Sandrew #include <stdlib.h>
8542067Sbostic #include <string.h>
8657769Sandrew #include <syslog.h>
8757769Sandrew #include <unistd.h>
8857769Sandrew 
8937282Sbostic #include "pathnames.h"
9016374Skarels 
9127535Skarels #define	TOOMANY		40		/* don't start more than TOOMANY */
9227535Skarels #define	CNT_INTVL	60		/* servers in CNT_INTVL sec. */
9327535Skarels #define	RETRYTIME	(60*10)		/* retry after bind or server fail */
9427535Skarels 
9527535Skarels #define	SIGBLOCK	(sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
9627535Skarels 
9716374Skarels 
9816374Skarels int	debug = 0;
9925046Skarels int	nsock, maxsock;
10025046Skarels fd_set	allsock;
10116374Skarels int	options;
10227535Skarels int	timingout;
10357769Sandrew int	toomany = TOOMANY;
10416374Skarels struct	servent *sp;
10516374Skarels 
10616374Skarels struct	servtab {
10716374Skarels 	char	*se_service;		/* name of service */
10816374Skarels 	int	se_socktype;		/* type of socket to use */
10916374Skarels 	char	*se_proto;		/* protocol used */
11016374Skarels 	short	se_wait;		/* single threaded server */
11116374Skarels 	short	se_checked;		/* looked at during merge */
11217346Sbloom 	char	*se_user;		/* user name to run as */
11326877Skarels 	struct	biltin *se_bi;		/* if built-in, description */
11416374Skarels 	char	*se_server;		/* server program */
11540740Sbostic #define	MAXARGV 20
11616374Skarels 	char	*se_argv[MAXARGV+1];	/* program arguments */
11716374Skarels 	int	se_fd;			/* open descriptor */
11857769Sandrew 	int	se_type;		/* type */
11916374Skarels 	struct	sockaddr_in se_ctrladdr;/* bound address */
12027535Skarels 	int	se_count;		/* number started since se_time */
12127535Skarels 	struct	timeval se_time;	/* start of se_count */
12216374Skarels 	struct	servtab *se_next;
12316374Skarels } *servtab;
12416374Skarels 
12557769Sandrew #define NORM_TYPE	0
12657769Sandrew #define MUX_TYPE	1
12757769Sandrew #define MUXPLUS_TYPE	2
12857769Sandrew #define ISMUX(sep)	(((sep)->se_type == MUX_TYPE) || \
12957769Sandrew 			 ((sep)->se_type == MUXPLUS_TYPE))
13057769Sandrew #define ISMUXPLUS(sep)	((sep)->se_type == MUXPLUS_TYPE)
13157769Sandrew 
13226877Skarels 
13366696Spendry void		chargen_dg __P((int, struct servtab *));
13466696Spendry void		chargen_stream __P((int, struct servtab *));
13566696Spendry void		close_sep __P((struct servtab *));
13666696Spendry void		config __P((int));
13766696Spendry void		daytime_dg __P((int, struct servtab *));
13866696Spendry void		daytime_stream __P((int, struct servtab *));
13966696Spendry void		discard_dg __P((int, struct servtab *));
14066696Spendry void		discard_stream __P((int, struct servtab *));
14166696Spendry void		echo_dg __P((int, struct servtab *));
14266696Spendry void		echo_stream __P((int, struct servtab *));
14366696Spendry void		endconfig __P((void));
14466696Spendry struct servtab *enter __P((struct servtab *));
14566696Spendry void		freeconfig __P((struct servtab *));
14666696Spendry struct servtab *getconfigent __P((void));
14766696Spendry void		machtime_dg __P((int, struct servtab *));
14866696Spendry void		machtime_stream __P((int, struct servtab *));
14966696Spendry char	       *newstr __P((char *));
15066696Spendry char	       *nextline __P((FILE *));
15166696Spendry void		print_service __P((char *, struct servtab *));
15266696Spendry void		reapchild __P((int));
15366696Spendry void		retry __P((int));
15466696Spendry int		setconfig __P((void));
15566696Spendry void		setup __P((struct servtab *));
15666696Spendry char	       *sskip __P((char **));
15766696Spendry char	       *skip __P((char **));
15866696Spendry struct servtab *tcpmux __P((int));
15966696Spendry 
16026877Skarels struct biltin {
16126877Skarels 	char	*bi_service;		/* internally provided service name */
16226877Skarels 	int	bi_socktype;		/* type of socket supported */
16326877Skarels 	short	bi_fork;		/* 1 if should fork before call */
16426877Skarels 	short	bi_wait;		/* 1 if should wait for child */
16566696Spendry 	void	(*bi_fn)();		/* function which performs it */
16626877Skarels } biltins[] = {
16726877Skarels 	/* Echo received data */
16866696Spendry 	{ "echo",	SOCK_STREAM,	1, 0,	echo_stream },
16966696Spendry 	{ "echo",	SOCK_DGRAM,	0, 0,	echo_dg },
17026877Skarels 
17126877Skarels 	/* Internet /dev/null */
17266696Spendry 	{ "discard",	SOCK_STREAM,	1, 0,	discard_stream },
17366696Spendry 	{ "discard",	SOCK_DGRAM,	0, 0,	discard_dg },
17426877Skarels 
17526877Skarels 	/* Return 32 bit time since 1970 */
17666696Spendry 	{ "time",	SOCK_STREAM,	0, 0,	machtime_stream },
17766696Spendry 	{ "time",	SOCK_DGRAM,	0, 0,	machtime_dg },
17826877Skarels 
17926877Skarels 	/* Return human-readable time */
18066696Spendry 	{ "daytime",	SOCK_STREAM,	0, 0,	daytime_stream },
18166696Spendry 	{ "daytime",	SOCK_DGRAM,	0, 0,	daytime_dg },
18226877Skarels 
18326877Skarels 	/* Familiar character generator */
18466696Spendry 	{ "chargen",	SOCK_STREAM,	1, 0,	chargen_stream },
18566696Spendry 	{ "chargen",	SOCK_DGRAM,	0, 0,	chargen_dg },
18657769Sandrew 
18766696Spendry 	{ "tcpmux",	SOCK_STREAM,	1, 0,	(void (*)())tcpmux },
18857769Sandrew 
18966696Spendry 	{ NULL }
19026877Skarels };
19126877Skarels 
19226877Skarels #define NUMINT	(sizeof(intab) / sizeof(struct inent))
19337282Sbostic char	*CONFIG = _PATH_INETDCONF;
19426877Skarels char	**Argv;
19526877Skarels char 	*LastArg;
19616374Skarels 
19766696Spendry int
19826877Skarels main(argc, argv, envp)
19916374Skarels 	int argc;
20026877Skarels 	char *argv[], *envp[];
20116374Skarels {
20266696Spendry 	struct servtab *sep;
20366696Spendry 	struct passwd *pwd;
20427535Skarels 	struct sigvec sv;
20566696Spendry 	int tmpint, ch, dofork;
20666696Spendry 	pid_t pid;
20736603Sbostic 	char buf[50];
20816374Skarels 
20926877Skarels 	Argv = argv;
21026877Skarels 	if (envp == 0 || *envp == 0)
21126877Skarels 		envp = argv;
21226877Skarels 	while (*envp)
21326877Skarels 		envp++;
21426877Skarels 	LastArg = envp[-1] + strlen(envp[-1]);
21516374Skarels 
21657769Sandrew 	openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
21757769Sandrew 
21857769Sandrew 	while ((ch = getopt(argc, argv, "dR:")) != EOF)
21936603Sbostic 		switch(ch) {
22016374Skarels 		case 'd':
22116374Skarels 			debug = 1;
22216374Skarels 			options |= SO_DEBUG;
22316374Skarels 			break;
22457769Sandrew 		case 'R': {	/* invocation rate */
22557769Sandrew 			char *p;
22657769Sandrew 
22757769Sandrew 			tmpint = strtol(optarg, &p, 0);
22857769Sandrew 			if (tmpint < 1 || *p)
22957769Sandrew 				syslog(LOG_ERR,
23057769Sandrew 			         "-R %s: bad value for service invocation rate",
23157769Sandrew 					optarg);
23257769Sandrew 			else
23357769Sandrew 				toomany = tmpint;
23457769Sandrew 			break;
23557769Sandrew 		}
23636603Sbostic 		case '?':
23716374Skarels 		default:
23857769Sandrew 			syslog(LOG_ERR,
23957769Sandrew 				"usage: inetd [-d] [-R rate] [conf-file]");
24036603Sbostic 			exit(1);
24116374Skarels 		}
24236603Sbostic 	argc -= optind;
24336603Sbostic 	argv += optind;
24436603Sbostic 
24516374Skarels 	if (argc > 0)
24616374Skarels 		CONFIG = argv[0];
24757769Sandrew 	if (debug == 0) {
24844711Skarels 		daemon(0, 0);
24957769Sandrew 	}
25066696Spendry 	memset(&sv, 0, sizeof(sv));
25127535Skarels 	sv.sv_mask = SIGBLOCK;
25227535Skarels 	sv.sv_handler = retry;
25327535Skarels 	sigvec(SIGALRM, &sv, (struct sigvec *)0);
25466696Spendry 	config(SIGHUP);
25527535Skarels 	sv.sv_handler = config;
25627535Skarels 	sigvec(SIGHUP, &sv, (struct sigvec *)0);
25727535Skarels 	sv.sv_handler = reapchild;
25827535Skarels 	sigvec(SIGCHLD, &sv, (struct sigvec *)0);
25927535Skarels 
26036603Sbostic 	{
26136603Sbostic 		/* space for daemons to overwrite environment for ps */
26236603Sbostic #define	DUMMYSIZE	100
26336603Sbostic 		char dummy[DUMMYSIZE];
26436603Sbostic 
26536603Sbostic 		(void)memset(dummy, 'x', sizeof(DUMMYSIZE) - 1);
26636603Sbostic 		dummy[DUMMYSIZE - 1] = '\0';
26736603Sbostic 		(void)setenv("inetd_dummy", dummy, 1);
26836603Sbostic 	}
26936603Sbostic 
27016374Skarels 	for (;;) {
27136603Sbostic 	    int n, ctrl;
27227535Skarels 	    fd_set readable;
27316374Skarels 
27432091Skarels 	    if (nsock == 0) {
27532091Skarels 		(void) sigblock(SIGBLOCK);
27632091Skarels 		while (nsock == 0)
27732246Sbostic 		    sigpause(0L);
27832246Sbostic 		(void) sigsetmask(0L);
27932091Skarels 	    }
28027535Skarels 	    readable = allsock;
28127535Skarels 	    if ((n = select(maxsock + 1, &readable, (fd_set *)0,
28227535Skarels 		(fd_set *)0, (struct timeval *)0)) <= 0) {
28327535Skarels 		    if (n < 0 && errno != EINTR)
28457769Sandrew 			syslog(LOG_WARNING, "select: %m");
28527535Skarels 		    sleep(1);
28627535Skarels 		    continue;
28727535Skarels 	    }
28827535Skarels 	    for (sep = servtab; n && sep; sep = sep->se_next)
28946973Skarels 	        if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
29046973Skarels 		    n--;
29146973Skarels 		    if (debug)
29246973Skarels 			    fprintf(stderr, "someone wants %s\n",
29346973Skarels 				sep->se_service);
29446973Skarels 		    if (sep->se_socktype == SOCK_STREAM) {
29546973Skarels 			    ctrl = accept(sep->se_fd, (struct sockaddr *)0,
29646973Skarels 				(int *)0);
29746973Skarels 			    if (debug)
29846973Skarels 				    fprintf(stderr, "accept, ctrl %d\n", ctrl);
29946973Skarels 			    if (ctrl < 0) {
30057769Sandrew 				    if (errno != EINTR)
30157769Sandrew 					    syslog(LOG_WARNING,
30257769Sandrew 						"accept (for %s): %m",
30357769Sandrew 						sep->se_service);
30446973Skarels 				    continue;
30546973Skarels 			    }
30657769Sandrew 			    /*
30757769Sandrew 			     * Call tcpmux to find the real service to exec.
30857769Sandrew 			     */
30957769Sandrew 			    if (sep->se_bi &&
31066696Spendry 				sep->se_bi->bi_fn == (void (*)()) tcpmux) {
31157769Sandrew 				    sep = tcpmux(ctrl);
31257769Sandrew 				    if (sep == NULL) {
31357769Sandrew 					    close(ctrl);
31457769Sandrew 					    continue;
31557769Sandrew 				    }
31657769Sandrew 			    }
31746973Skarels 		    } else
31846973Skarels 			    ctrl = sep->se_fd;
31946973Skarels 		    (void) sigblock(SIGBLOCK);
32046973Skarels 		    pid = 0;
32146973Skarels 		    dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
32246973Skarels 		    if (dofork) {
32346973Skarels 			    if (sep->se_count++ == 0)
32446973Skarels 				(void)gettimeofday(&sep->se_time,
32546973Skarels 				    (struct timezone *)0);
32657769Sandrew 			    else if (sep->se_count >= toomany) {
32727535Skarels 				struct timeval now;
32827535Skarels 
32927535Skarels 				(void)gettimeofday(&now, (struct timezone *)0);
33027535Skarels 				if (now.tv_sec - sep->se_time.tv_sec >
33127535Skarels 				    CNT_INTVL) {
33227535Skarels 					sep->se_time = now;
33327535Skarels 					sep->se_count = 1;
33427535Skarels 				} else {
33527535Skarels 					syslog(LOG_ERR,
33657769Sandrew 			"%s/%s server failing (looping), service terminated",
33727535Skarels 					    sep->se_service, sep->se_proto);
33857769Sandrew 					close_sep(sep);
33957769Sandrew 					sigsetmask(0L);
34027535Skarels 					if (!timingout) {
34127535Skarels 						timingout = 1;
34227535Skarels 						alarm(RETRYTIME);
34327535Skarels 					}
34457769Sandrew 					continue;
34527535Skarels 				}
34646973Skarels 			    }
34746973Skarels 			    pid = fork();
34846973Skarels 		    }
34946973Skarels 		    if (pid < 0) {
35046973Skarels 			    syslog(LOG_ERR, "fork: %m");
35146973Skarels 			    if (sep->se_socktype == SOCK_STREAM)
35246973Skarels 				    close(ctrl);
35346973Skarels 			    sigsetmask(0L);
35446973Skarels 			    sleep(1);
35546973Skarels 			    continue;
35646973Skarels 		    }
35746973Skarels 		    if (pid && sep->se_wait) {
35846973Skarels 			    sep->se_wait = pid;
35949806Sbostic 			    if (sep->se_fd >= 0) {
36049806Sbostic 				FD_CLR(sep->se_fd, &allsock);
36149806Sbostic 			        nsock--;
36249806Sbostic 			    }
36346973Skarels 		    }
36446973Skarels 		    sigsetmask(0L);
36546973Skarels 		    if (pid == 0) {
36646973Skarels 			    if (debug && dofork)
36744711Skarels 				setsid();
36857769Sandrew 			    if (dofork) {
36957769Sandrew 				if (debug)
37057769Sandrew 					fprintf(stderr, "+ Closing from %d\n",
37157769Sandrew 						maxsock);
37257769Sandrew 				for (tmpint = maxsock; tmpint > 2; tmpint--)
37336603Sbostic 					if (tmpint != ctrl)
37436603Sbostic 						close(tmpint);
37557769Sandrew 			    }
37646973Skarels 			    if (sep->se_bi)
37726877Skarels 				(*sep->se_bi->bi_fn)(ctrl, sep);
37846973Skarels 			    else {
37946973Skarels 				if (debug)
38046973Skarels 					fprintf(stderr, "%d execl %s\n",
38146973Skarels 					    getpid(), sep->se_server);
38226877Skarels 				dup2(ctrl, 0);
38326877Skarels 				close(ctrl);
38426877Skarels 				dup2(0, 1);
38526877Skarels 				dup2(0, 2);
38626877Skarels 				if ((pwd = getpwnam(sep->se_user)) == NULL) {
38726877Skarels 					syslog(LOG_ERR,
38857769Sandrew 					    "%s/%s: %s: No such user",
38957769Sandrew 						sep->se_service, sep->se_proto,
39057769Sandrew 						sep->se_user);
39127535Skarels 					if (sep->se_socktype != SOCK_STREAM)
39227535Skarels 						recv(0, buf, sizeof (buf), 0);
39326877Skarels 					_exit(1);
39426877Skarels 				}
39526877Skarels 				if (pwd->pw_uid) {
39657769Sandrew 					if (setgid(pwd->pw_gid) < 0) {
39757769Sandrew 						syslog(LOG_ERR,
39857769Sandrew 						  "%s: can't set gid %d: %m",
39957769Sandrew 						  sep->se_service, pwd->pw_gid);
40057769Sandrew 						_exit(1);
40157769Sandrew 					}
40257769Sandrew 					(void) initgroups(pwd->pw_name,
40357769Sandrew 							pwd->pw_gid);
40457769Sandrew 					if (setuid(pwd->pw_uid) < 0) {
40557769Sandrew 						syslog(LOG_ERR,
40657769Sandrew 						  "%s: can't set uid %d: %m",
40757769Sandrew 						  sep->se_service, pwd->pw_uid);
40857769Sandrew 						_exit(1);
40957769Sandrew 					}
41026877Skarels 				}
41126877Skarels 				execv(sep->se_server, sep->se_argv);
41226877Skarels 				if (sep->se_socktype != SOCK_STREAM)
41326877Skarels 					recv(0, buf, sizeof (buf), 0);
41457769Sandrew 				syslog(LOG_ERR,
41557769Sandrew 				    "cannot execute %s: %m", sep->se_server);
41626877Skarels 				_exit(1);
41746973Skarels 			    }
41846973Skarels 		    }
41946973Skarels 		    if (sep->se_socktype == SOCK_STREAM)
42046973Skarels 			    close(ctrl);
42116374Skarels 		}
42216374Skarels 	}
42316374Skarels }
42416374Skarels 
42539838Sbostic void
42666696Spendry reapchild(signo)
42766696Spendry 	int signo;
42816374Skarels {
42946973Skarels 	int status;
43066696Spendry 	pid_t pid;
43166696Spendry 	struct servtab *sep;
43216374Skarels 
43316374Skarels 	for (;;) {
43446973Skarels 		pid = wait3(&status, WNOHANG, (struct rusage *)0);
43516374Skarels 		if (pid <= 0)
43616374Skarels 			break;
43716374Skarels 		if (debug)
43857769Sandrew 			fprintf(stderr, "%d reaped, status %#x\n",
43957769Sandrew 				pid, status);
44016374Skarels 		for (sep = servtab; sep; sep = sep->se_next)
44116374Skarels 			if (sep->se_wait == pid) {
44246973Skarels 				if (status)
44316510Sralph 					syslog(LOG_WARNING,
44416510Sralph 					    "%s: exit status 0x%x",
44516374Skarels 					    sep->se_server, status);
44616374Skarels 				if (debug)
44716374Skarels 					fprintf(stderr, "restored %s, fd %d\n",
44816374Skarels 					    sep->se_service, sep->se_fd);
44925046Skarels 				FD_SET(sep->se_fd, &allsock);
45025046Skarels 				nsock++;
45116374Skarels 				sep->se_wait = 1;
45216374Skarels 			}
45316374Skarels 	}
45416374Skarels }
45516374Skarels 
45639838Sbostic void
45766696Spendry config(signo)
45866696Spendry 	int signo;
45916374Skarels {
46066696Spendry 	struct servtab *sep, *cp, **sepp;
46157769Sandrew 	struct passwd *pwd;
46232246Sbostic 	long omask;
46316374Skarels 
46416374Skarels 	if (!setconfig()) {
46516510Sralph 		syslog(LOG_ERR, "%s: %m", CONFIG);
46616374Skarels 		return;
46716374Skarels 	}
46816374Skarels 	for (sep = servtab; sep; sep = sep->se_next)
46916374Skarels 		sep->se_checked = 0;
47016374Skarels 	while (cp = getconfigent()) {
47157769Sandrew 		if ((pwd = getpwnam(cp->se_user)) == NULL) {
47257769Sandrew 			syslog(LOG_ERR,
47357769Sandrew 				"%s/%s: No such user '%s', service ignored",
47457769Sandrew 				cp->se_service, cp->se_proto, cp->se_user);
47557769Sandrew 			continue;
47657769Sandrew 		}
47716374Skarels 		for (sep = servtab; sep; sep = sep->se_next)
47816374Skarels 			if (strcmp(sep->se_service, cp->se_service) == 0 &&
47916374Skarels 			    strcmp(sep->se_proto, cp->se_proto) == 0)
48016374Skarels 				break;
48116374Skarels 		if (sep != 0) {
48216374Skarels 			int i;
48316374Skarels 
48427535Skarels 			omask = sigblock(SIGBLOCK);
48540745Sbostic 			/*
48640745Sbostic 			 * sep->se_wait may be holding the pid of a daemon
48740745Sbostic 			 * that we're waiting for.  If so, don't overwrite
48840745Sbostic 			 * it unless the config file explicitly says don't
48940745Sbostic 			 * wait.
49040745Sbostic 			 */
49140745Sbostic 			if (cp->se_bi == 0 &&
49240745Sbostic 			    (sep->se_wait == 1 || cp->se_wait == 0))
49327535Skarels 				sep->se_wait = cp->se_wait;
49416374Skarels #define SWAP(a, b) { char *c = a; a = b; b = c; }
49526921Slepreau 			if (cp->se_user)
49626921Slepreau 				SWAP(sep->se_user, cp->se_user);
49716374Skarels 			if (cp->se_server)
49816374Skarels 				SWAP(sep->se_server, cp->se_server);
49916374Skarels 			for (i = 0; i < MAXARGV; i++)
50016374Skarels 				SWAP(sep->se_argv[i], cp->se_argv[i]);
50116374Skarels 			sigsetmask(omask);
50216374Skarels 			freeconfig(cp);
50329794Skarels 			if (debug)
50429794Skarels 				print_service("REDO", sep);
50529794Skarels 		} else {
50616374Skarels 			sep = enter(cp);
50729794Skarels 			if (debug)
50829794Skarels 				print_service("ADD ", sep);
50929794Skarels 		}
51016374Skarels 		sep->se_checked = 1;
51157769Sandrew 		if (ISMUX(sep)) {
51257769Sandrew 			sep->se_fd = -1;
51357769Sandrew 			continue;
51457769Sandrew 		}
51516374Skarels 		sp = getservbyname(sep->se_service, sep->se_proto);
51616374Skarels 		if (sp == 0) {
51716510Sralph 			syslog(LOG_ERR, "%s/%s: unknown service",
51816374Skarels 			    sep->se_service, sep->se_proto);
51957769Sandrew 			sep->se_checked = 0;
52016374Skarels 			continue;
52116374Skarels 		}
52227535Skarels 		if (sp->s_port != sep->se_ctrladdr.sin_port) {
52360924Smckusick 			sep->se_ctrladdr.sin_family = AF_INET;
52427535Skarels 			sep->se_ctrladdr.sin_port = sp->s_port;
52557769Sandrew 			if (sep->se_fd >= 0)
52657769Sandrew 				close_sep(sep);
52716374Skarels 		}
52827535Skarels 		if (sep->se_fd == -1)
52927535Skarels 			setup(sep);
53016374Skarels 	}
53116374Skarels 	endconfig();
53216374Skarels 	/*
53316374Skarels 	 * Purge anything not looked at above.
53416374Skarels 	 */
53527535Skarels 	omask = sigblock(SIGBLOCK);
53616374Skarels 	sepp = &servtab;
53716374Skarels 	while (sep = *sepp) {
53816374Skarels 		if (sep->se_checked) {
53916374Skarels 			sepp = &sep->se_next;
54016374Skarels 			continue;
54116374Skarels 		}
54216374Skarels 		*sepp = sep->se_next;
54357769Sandrew 		if (sep->se_fd >= 0)
54457769Sandrew 			close_sep(sep);
54529794Skarels 		if (debug)
54629794Skarels 			print_service("FREE", sep);
54716374Skarels 		freeconfig(sep);
54816374Skarels 		free((char *)sep);
54916374Skarels 	}
55016374Skarels 	(void) sigsetmask(omask);
55116374Skarels }
55216374Skarels 
55339838Sbostic void
55466696Spendry retry(signo)
55566696Spendry 	int signo;
55627535Skarels {
55766696Spendry 	struct servtab *sep;
55827535Skarels 
55927535Skarels 	timingout = 0;
56027535Skarels 	for (sep = servtab; sep; sep = sep->se_next)
56127535Skarels 		if (sep->se_fd == -1)
56227535Skarels 			setup(sep);
56327535Skarels }
56427535Skarels 
56566696Spendry void
56627535Skarels setup(sep)
56766696Spendry 	struct servtab *sep;
56827535Skarels {
56927535Skarels 	int on = 1;
57027535Skarels 
57127535Skarels 	if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
57257769Sandrew 		if (debug)
57357769Sandrew 			fprintf(stderr, "socket failed on %s/%s: %s\n",
57457769Sandrew 				sep->se_service, sep->se_proto,
57557769Sandrew 				strerror(errno));
57627535Skarels 		syslog(LOG_ERR, "%s/%s: socket: %m",
57727535Skarels 		    sep->se_service, sep->se_proto);
57827535Skarels 		return;
57927535Skarels 	}
58027535Skarels #define	turnon(fd, opt) \
58127535Skarels setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
58227535Skarels 	if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
58327535Skarels 	    turnon(sep->se_fd, SO_DEBUG) < 0)
58427535Skarels 		syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
58527535Skarels 	if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
58627535Skarels 		syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
58727535Skarels #undef turnon
58846907Sbostic 	if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
58927535Skarels 	    sizeof (sep->se_ctrladdr)) < 0) {
59057769Sandrew 		if (debug)
59157769Sandrew 			fprintf(stderr, "bind failed on %s/%s: %s\n",
59257769Sandrew 				sep->se_service, sep->se_proto,
59357769Sandrew 				strerror(errno));
59427535Skarels 		syslog(LOG_ERR, "%s/%s: bind: %m",
59527535Skarels 		    sep->se_service, sep->se_proto);
59627535Skarels 		(void) close(sep->se_fd);
59727535Skarels 		sep->se_fd = -1;
59827535Skarels 		if (!timingout) {
59927535Skarels 			timingout = 1;
60027535Skarels 			alarm(RETRYTIME);
60127535Skarels 		}
60227535Skarels 		return;
60327535Skarels 	}
60427535Skarels 	if (sep->se_socktype == SOCK_STREAM)
60527535Skarels 		listen(sep->se_fd, 10);
60627535Skarels 	FD_SET(sep->se_fd, &allsock);
60727535Skarels 	nsock++;
60827535Skarels 	if (sep->se_fd > maxsock)
60927535Skarels 		maxsock = sep->se_fd;
61057769Sandrew 	if (debug) {
61157769Sandrew 		fprintf(stderr, "registered %s on %d\n",
61257769Sandrew 			sep->se_server, sep->se_fd);
61357769Sandrew 	}
61427535Skarels }
61527535Skarels 
61657769Sandrew /*
61757769Sandrew  * Finish with a service and its socket.
61857769Sandrew  */
61966696Spendry void
62057769Sandrew close_sep(sep)
62166696Spendry 	struct servtab *sep;
62257769Sandrew {
62357769Sandrew 	if (sep->se_fd >= 0) {
62457769Sandrew 		nsock--;
62557769Sandrew 		FD_CLR(sep->se_fd, &allsock);
62657769Sandrew 		(void) close(sep->se_fd);
62757769Sandrew 		sep->se_fd = -1;
62857769Sandrew 	}
62957769Sandrew 	sep->se_count = 0;
63057769Sandrew 	/*
63157769Sandrew 	 * Don't keep the pid of this running deamon: when reapchild()
63257769Sandrew 	 * reaps this pid, it would erroneously increment nsock.
63357769Sandrew 	 */
63457769Sandrew 	if (sep->se_wait > 1)
63557769Sandrew 		sep->se_wait = 1;
63657769Sandrew }
63757769Sandrew 
63816374Skarels struct servtab *
63916374Skarels enter(cp)
64016374Skarels 	struct servtab *cp;
64116374Skarels {
64266696Spendry 	struct servtab *sep;
64332246Sbostic 	long omask;
64416374Skarels 
64516374Skarels 	sep = (struct servtab *)malloc(sizeof (*sep));
64616374Skarels 	if (sep == (struct servtab *)0) {
64716510Sralph 		syslog(LOG_ERR, "Out of memory.");
64816374Skarels 		exit(-1);
64916374Skarels 	}
65016374Skarels 	*sep = *cp;
65116374Skarels 	sep->se_fd = -1;
65227535Skarels 	omask = sigblock(SIGBLOCK);
65316374Skarels 	sep->se_next = servtab;
65416374Skarels 	servtab = sep;
65516374Skarels 	sigsetmask(omask);
65616374Skarels 	return (sep);
65716374Skarels }
65816374Skarels 
65916374Skarels FILE	*fconfig = NULL;
66016374Skarels struct	servtab serv;
66166696Spendry char	line[LINE_MAX];
66216374Skarels 
66366696Spendry int
66416374Skarels setconfig()
66516374Skarels {
66616374Skarels 
66716374Skarels 	if (fconfig != NULL) {
66857769Sandrew 		fseek(fconfig, 0L, SEEK_SET);
66916374Skarels 		return (1);
67016374Skarels 	}
67116374Skarels 	fconfig = fopen(CONFIG, "r");
67216374Skarels 	return (fconfig != NULL);
67316374Skarels }
67416374Skarels 
67566696Spendry void
67616374Skarels endconfig()
67716374Skarels {
67836603Sbostic 	if (fconfig) {
67936603Sbostic 		(void) fclose(fconfig);
68036603Sbostic 		fconfig = NULL;
68136603Sbostic 	}
68216374Skarels }
68316374Skarels 
68416374Skarels struct servtab *
68516374Skarels getconfigent()
68616374Skarels {
68766696Spendry 	struct servtab *sep = &serv;
68816374Skarels 	int argc;
68966696Spendry 	char *cp, *arg;
69057769Sandrew 	static char TCPMUX_TOKEN[] = "tcpmux/";
69157769Sandrew #define MUX_LEN		(sizeof(TCPMUX_TOKEN)-1)
69216374Skarels 
69326877Skarels more:
69457769Sandrew 	while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0'))
69516374Skarels 		;
69616374Skarels 	if (cp == NULL)
69716374Skarels 		return ((struct servtab *)0);
69857769Sandrew 	/*
69957769Sandrew 	 * clear the static buffer, since some fields (se_ctrladdr,
70057769Sandrew 	 * for example) don't get initialized here.
70157769Sandrew 	 */
70266696Spendry 	memset((caddr_t)sep, 0, sizeof *sep);
70316374Skarels 	arg = skip(&cp);
70457769Sandrew 	if (cp == NULL) {
70557769Sandrew 		/* got an empty line containing just blanks/tabs. */
70657769Sandrew 		goto more;
70757769Sandrew 	}
70857769Sandrew 	if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
70957769Sandrew 		char *c = arg + MUX_LEN;
71057769Sandrew 		if (*c == '+') {
71157769Sandrew 			sep->se_type = MUXPLUS_TYPE;
71257769Sandrew 			c++;
71357769Sandrew 		} else
71457769Sandrew 			sep->se_type = MUX_TYPE;
71557769Sandrew 		sep->se_service = newstr(c);
71657769Sandrew 	} else {
71757769Sandrew 		sep->se_service = newstr(arg);
71857769Sandrew 		sep->se_type = NORM_TYPE;
71957769Sandrew 	}
72057769Sandrew 	arg = sskip(&cp);
72116374Skarels 	if (strcmp(arg, "stream") == 0)
72216374Skarels 		sep->se_socktype = SOCK_STREAM;
72316374Skarels 	else if (strcmp(arg, "dgram") == 0)
72416374Skarels 		sep->se_socktype = SOCK_DGRAM;
72516374Skarels 	else if (strcmp(arg, "rdm") == 0)
72616374Skarels 		sep->se_socktype = SOCK_RDM;
72716374Skarels 	else if (strcmp(arg, "seqpacket") == 0)
72816374Skarels 		sep->se_socktype = SOCK_SEQPACKET;
72916374Skarels 	else if (strcmp(arg, "raw") == 0)
73016374Skarels 		sep->se_socktype = SOCK_RAW;
73116374Skarels 	else
73216374Skarels 		sep->se_socktype = -1;
73357769Sandrew 	sep->se_proto = newstr(sskip(&cp));
73457769Sandrew 	arg = sskip(&cp);
73516374Skarels 	sep->se_wait = strcmp(arg, "wait") == 0;
73657769Sandrew 	if (ISMUX(sep)) {
73757769Sandrew 		/*
73857769Sandrew 		 * Silently enforce "nowait" for TCPMUX services since
73957769Sandrew 		 * they don't have an assigned port to listen on.
74057769Sandrew 		 */
74157769Sandrew 		sep->se_wait = 0;
74257769Sandrew 
74357769Sandrew 		if (strcmp(sep->se_proto, "tcp")) {
74457769Sandrew 			syslog(LOG_ERR,
74557769Sandrew 				"%s: bad protocol for tcpmux service %s",
74657769Sandrew 				CONFIG, sep->se_service);
74757769Sandrew 			goto more;
74857769Sandrew 		}
74957769Sandrew 		if (sep->se_socktype != SOCK_STREAM) {
75057769Sandrew 			syslog(LOG_ERR,
75157769Sandrew 				"%s: bad socket type for tcpmux service %s",
75257769Sandrew 				CONFIG, sep->se_service);
75357769Sandrew 			goto more;
75457769Sandrew 		}
75557769Sandrew 	}
75657769Sandrew 	sep->se_user = newstr(sskip(&cp));
75757769Sandrew 	sep->se_server = newstr(sskip(&cp));
75826877Skarels 	if (strcmp(sep->se_server, "internal") == 0) {
75966696Spendry 		struct biltin *bi;
76026877Skarels 
76126877Skarels 		for (bi = biltins; bi->bi_service; bi++)
76226877Skarels 			if (bi->bi_socktype == sep->se_socktype &&
76326877Skarels 			    strcmp(bi->bi_service, sep->se_service) == 0)
76426877Skarels 				break;
76526877Skarels 		if (bi->bi_service == 0) {
76657769Sandrew 			syslog(LOG_ERR, "internal service %s unknown",
76726877Skarels 				sep->se_service);
76826877Skarels 			goto more;
76926877Skarels 		}
77026877Skarels 		sep->se_bi = bi;
77126877Skarels 		sep->se_wait = bi->bi_wait;
77229794Skarels 	} else
77329794Skarels 		sep->se_bi = NULL;
77416374Skarels 	argc = 0;
77516374Skarels 	for (arg = skip(&cp); cp; arg = skip(&cp))
77616374Skarels 		if (argc < MAXARGV)
77746907Sbostic 			sep->se_argv[argc++] = newstr(arg);
77816374Skarels 	while (argc <= MAXARGV)
77916374Skarels 		sep->se_argv[argc++] = NULL;
78016374Skarels 	return (sep);
78116374Skarels }
78216374Skarels 
78366696Spendry void
78416374Skarels freeconfig(cp)
78566696Spendry 	struct servtab *cp;
78616374Skarels {
78716374Skarels 	int i;
78816374Skarels 
78916374Skarels 	if (cp->se_service)
79016374Skarels 		free(cp->se_service);
79116374Skarels 	if (cp->se_proto)
79216374Skarels 		free(cp->se_proto);
79326921Slepreau 	if (cp->se_user)
79426921Slepreau 		free(cp->se_user);
79516374Skarels 	if (cp->se_server)
79616374Skarels 		free(cp->se_server);
79716374Skarels 	for (i = 0; i < MAXARGV; i++)
79816374Skarels 		if (cp->se_argv[i])
79916374Skarels 			free(cp->se_argv[i]);
80016374Skarels }
80116374Skarels 
80257769Sandrew 
80357769Sandrew /*
80457769Sandrew  * Safe skip - if skip returns null, log a syntax error in the
80557769Sandrew  * configuration file and exit.
80657769Sandrew  */
80716374Skarels char *
80857769Sandrew sskip(cpp)
80957769Sandrew 	char **cpp;
81057769Sandrew {
81166696Spendry 	char *cp;
81257769Sandrew 
81357769Sandrew 	cp = skip(cpp);
81457769Sandrew 	if (cp == NULL) {
81557769Sandrew 		syslog(LOG_ERR, "%s: syntax error", CONFIG);
81657769Sandrew 		exit(-1);
81757769Sandrew 	}
81857769Sandrew 	return (cp);
81957769Sandrew }
82057769Sandrew 
82157769Sandrew char *
82216374Skarels skip(cpp)
82316374Skarels 	char **cpp;
82416374Skarels {
82566696Spendry 	char *cp = *cpp;
82616374Skarels 	char *start;
82716374Skarels 
82816374Skarels again:
82916374Skarels 	while (*cp == ' ' || *cp == '\t')
83016374Skarels 		cp++;
83116374Skarels 	if (*cp == '\0') {
83243455Sbostic 		int c;
83316374Skarels 
83416374Skarels 		c = getc(fconfig);
83536603Sbostic 		(void) ungetc(c, fconfig);
83616374Skarels 		if (c == ' ' || c == '\t')
83716374Skarels 			if (cp = nextline(fconfig))
83816374Skarels 				goto again;
83916374Skarels 		*cpp = (char *)0;
84016374Skarels 		return ((char *)0);
84116374Skarels 	}
84216374Skarels 	start = cp;
84316374Skarels 	while (*cp && *cp != ' ' && *cp != '\t')
84416374Skarels 		cp++;
84516374Skarels 	if (*cp != '\0')
84616374Skarels 		*cp++ = '\0';
84716374Skarels 	*cpp = cp;
84816374Skarels 	return (start);
84916374Skarels }
85016374Skarels 
85116374Skarels char *
85216374Skarels nextline(fd)
85316374Skarels 	FILE *fd;
85416374Skarels {
85516374Skarels 	char *cp;
85616374Skarels 
85726921Slepreau 	if (fgets(line, sizeof (line), fd) == NULL)
85816374Skarels 		return ((char *)0);
85966696Spendry 	cp = strchr(line, '\n');
86016374Skarels 	if (cp)
86116374Skarels 		*cp = '\0';
86216374Skarels 	return (line);
86316374Skarels }
86416374Skarels 
86516374Skarels char *
86646907Sbostic newstr(cp)
86716374Skarels 	char *cp;
86816374Skarels {
86946973Skarels 	if (cp = strdup(cp ? cp : ""))
87066696Spendry 		return (cp);
87146973Skarels 	syslog(LOG_ERR, "strdup: %m");
87246907Sbostic 	exit(-1);
87316374Skarels }
87426877Skarels 
87566696Spendry void
87626877Skarels setproctitle(a, s)
87726877Skarels 	char *a;
87826877Skarels 	int s;
87926877Skarels {
88026877Skarels 	int size;
88166696Spendry 	char *cp;
88226877Skarels 	struct sockaddr_in sin;
88326877Skarels 	char buf[80];
88426877Skarels 
88526877Skarels 	cp = Argv[0];
88626877Skarels 	size = sizeof(sin);
88746907Sbostic 	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
88832442Sbostic 		(void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr));
88926877Skarels 	else
89032442Sbostic 		(void) sprintf(buf, "-%s", a);
89126877Skarels 	strncpy(cp, buf, LastArg - cp);
89226877Skarels 	cp += strlen(cp);
89326877Skarels 	while (cp < LastArg)
89426877Skarels 		*cp++ = ' ';
89526877Skarels }
89626877Skarels 
89726877Skarels /*
89826877Skarels  * Internet services provided internally by inetd:
89926877Skarels  */
90049986Skarels #define	BUFSIZE	8192
90126877Skarels 
90226877Skarels /* ARGSUSED */
90366696Spendry void
90426877Skarels echo_stream(s, sep)		/* Echo service -- echo data back */
90526877Skarels 	int s;
90626877Skarels 	struct servtab *sep;
90726877Skarels {
90838573Skarels 	char buffer[BUFSIZE];
90926877Skarels 	int i;
91026877Skarels 
91132091Skarels 	setproctitle(sep->se_service, s);
91226877Skarels 	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
91326877Skarels 	    write(s, buffer, i) > 0)
91426877Skarels 		;
91526877Skarels 	exit(0);
91626877Skarels }
91726877Skarels 
91826877Skarels /* ARGSUSED */
91966696Spendry void
92026877Skarels echo_dg(s, sep)			/* Echo service -- echo data back */
92126877Skarels 	int s;
92226877Skarels 	struct servtab *sep;
92326877Skarels {
92438573Skarels 	char buffer[BUFSIZE];
92526877Skarels 	int i, size;
92626877Skarels 	struct sockaddr sa;
92726877Skarels 
92826877Skarels 	size = sizeof(sa);
92926877Skarels 	if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0)
93026877Skarels 		return;
93126877Skarels 	(void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
93226877Skarels }
93326877Skarels 
93426877Skarels /* ARGSUSED */
93566696Spendry void
93626877Skarels discard_stream(s, sep)		/* Discard service -- ignore data */
93726877Skarels 	int s;
93826877Skarels 	struct servtab *sep;
93926877Skarels {
94049986Skarels 	int ret;
94138573Skarels 	char buffer[BUFSIZE];
94226877Skarels 
94332091Skarels 	setproctitle(sep->se_service, s);
94426877Skarels 	while (1) {
94549986Skarels 		while ((ret = read(s, buffer, sizeof(buffer))) > 0)
94626877Skarels 			;
94749986Skarels 		if (ret == 0 || errno != EINTR)
94826877Skarels 			break;
94926877Skarels 	}
95026877Skarels 	exit(0);
95126877Skarels }
95226877Skarels 
95326877Skarels /* ARGSUSED */
95466696Spendry void
95526877Skarels discard_dg(s, sep)		/* Discard service -- ignore data */
95626877Skarels 	int s;
95726877Skarels 	struct servtab *sep;
95826877Skarels {
95938573Skarels 	char buffer[BUFSIZE];
96026877Skarels 
96126877Skarels 	(void) read(s, buffer, sizeof(buffer));
96226877Skarels }
96326877Skarels 
96426877Skarels #include <ctype.h>
96526877Skarels #define LINESIZ 72
96626877Skarels char ring[128];
96726877Skarels char *endring;
96826877Skarels 
96966696Spendry void
97026877Skarels initring()
97126877Skarels {
97266696Spendry 	int i;
97326877Skarels 
97426877Skarels 	endring = ring;
97526877Skarels 
97626877Skarels 	for (i = 0; i <= 128; ++i)
97726877Skarels 		if (isprint(i))
97826877Skarels 			*endring++ = i;
97926877Skarels }
98026877Skarels 
98126877Skarels /* ARGSUSED */
98266696Spendry void
98326877Skarels chargen_stream(s, sep)		/* Character generator */
98426877Skarels 	int s;
98526877Skarels 	struct servtab *sep;
98626877Skarels {
98732246Sbostic 	int len;
98866696Spendry 	char *rs, text[LINESIZ+2];
98926877Skarels 
99032091Skarels 	setproctitle(sep->se_service, s);
99132246Sbostic 
99232246Sbostic 	if (!endring) {
99326877Skarels 		initring();
99432246Sbostic 		rs = ring;
99532246Sbostic 	}
99626877Skarels 
99732246Sbostic 	text[LINESIZ] = '\r';
99832246Sbostic 	text[LINESIZ + 1] = '\n';
99932246Sbostic 	for (rs = ring;;) {
100032246Sbostic 		if ((len = endring - rs) >= LINESIZ)
100166696Spendry 			memmove(text, rs, LINESIZ);
100232246Sbostic 		else {
100366696Spendry 			memmove(text, rs, len);
100466696Spendry 			memmove(text + len, ring, LINESIZ - len);
100532246Sbostic 		}
100632246Sbostic 		if (++rs == endring)
100726877Skarels 			rs = ring;
100832246Sbostic 		if (write(s, text, sizeof(text)) != sizeof(text))
100926877Skarels 			break;
101026877Skarels 	}
101126877Skarels 	exit(0);
101226877Skarels }
101326877Skarels 
101426877Skarels /* ARGSUSED */
101566696Spendry void
101626877Skarels chargen_dg(s, sep)		/* Character generator */
101726877Skarels 	int s;
101826877Skarels 	struct servtab *sep;
101926877Skarels {
102032246Sbostic 	struct sockaddr sa;
102132246Sbostic 	static char *rs;
102232246Sbostic 	int len, size;
102326877Skarels 	char text[LINESIZ+2];
102426877Skarels 
102532246Sbostic 	if (endring == 0) {
102626877Skarels 		initring();
102732246Sbostic 		rs = ring;
102832246Sbostic 	}
102926877Skarels 
103026877Skarels 	size = sizeof(sa);
103126877Skarels 	if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
103226877Skarels 		return;
103332246Sbostic 
103432246Sbostic 	if ((len = endring - rs) >= LINESIZ)
103566696Spendry 		memmove(text, rs, LINESIZ);
103632246Sbostic 	else {
103766696Spendry 		memmove(text, rs, len);
103866696Spendry 		memmove(text + len, ring, LINESIZ - len);
103932246Sbostic 	}
104032246Sbostic 	if (++rs == endring)
104126877Skarels 		rs = ring;
104232246Sbostic 	text[LINESIZ] = '\r';
104332246Sbostic 	text[LINESIZ + 1] = '\n';
104426877Skarels 	(void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
104526877Skarels }
104626877Skarels 
104726877Skarels /*
104826877Skarels  * Return a machine readable date and time, in the form of the
104926877Skarels  * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
105026877Skarels  * returns the number of seconds since midnight, Jan 1, 1970,
105126877Skarels  * we must add 2208988800 seconds to this figure to make up for
105226877Skarels  * some seventy years Bell Labs was asleep.
105326877Skarels  */
105426877Skarels 
105526877Skarels long
105626877Skarels machtime()
105726877Skarels {
105826877Skarels 	struct timeval tv;
105926877Skarels 
106026921Slepreau 	if (gettimeofday(&tv, (struct timezone *)0) < 0) {
106157769Sandrew 		if (debug)
106257769Sandrew 			fprintf(stderr, "Unable to get time of day\n");
106326921Slepreau 		return (0L);
106426877Skarels 	}
106560073Storek #define	OFFSET ((u_long)25567 * 24*60*60)
106660073Storek 	return (htonl((long)(tv.tv_sec + OFFSET)));
106760073Storek #undef OFFSET
106826877Skarels }
106926877Skarels 
107026877Skarels /* ARGSUSED */
107166696Spendry void
107226877Skarels machtime_stream(s, sep)
107326877Skarels 	int s;
107426877Skarels 	struct servtab *sep;
107526877Skarels {
107626877Skarels 	long result;
107726877Skarels 
107826877Skarels 	result = machtime();
107926877Skarels 	(void) write(s, (char *) &result, sizeof(result));
108026877Skarels }
108126877Skarels 
108226877Skarels /* ARGSUSED */
108366696Spendry void
108426877Skarels machtime_dg(s, sep)
108526877Skarels 	int s;
108626877Skarels 	struct servtab *sep;
108726877Skarels {
108826877Skarels 	long result;
108926877Skarels 	struct sockaddr sa;
109026877Skarels 	int size;
109126877Skarels 
109226877Skarels 	size = sizeof(sa);
109326921Slepreau 	if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0)
109426877Skarels 		return;
109526877Skarels 	result = machtime();
109626877Skarels 	(void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
109726877Skarels }
109826877Skarels 
109926877Skarels /* ARGSUSED */
110066696Spendry void
110126877Skarels daytime_stream(s, sep)		/* Return human-readable time of day */
110226877Skarels 	int s;
110326877Skarels 	struct servtab *sep;
110426877Skarels {
110526877Skarels 	char buffer[256];
110657769Sandrew 	time_t clock;
110726877Skarels 
110826877Skarels 	clock = time((time_t *) 0);
110926877Skarels 
111032442Sbostic 	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
111126921Slepreau 	(void) write(s, buffer, strlen(buffer));
111226877Skarels }
111326877Skarels 
111426877Skarels /* ARGSUSED */
111566696Spendry void
111626877Skarels daytime_dg(s, sep)		/* Return human-readable time of day */
111726877Skarels 	int s;
111826877Skarels 	struct servtab *sep;
111926877Skarels {
112026877Skarels 	char buffer[256];
112157769Sandrew 	time_t clock;
112226877Skarels 	struct sockaddr sa;
112326877Skarels 	int size;
112426877Skarels 
112526877Skarels 	clock = time((time_t *) 0);
112626877Skarels 
112726877Skarels 	size = sizeof(sa);
112826877Skarels 	if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
112926877Skarels 		return;
113032442Sbostic 	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
113126877Skarels 	(void) sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa));
113226877Skarels }
113329794Skarels 
113429794Skarels /*
113529794Skarels  * print_service:
113629794Skarels  *	Dump relevant information to stderr
113729794Skarels  */
113866696Spendry void
113929794Skarels print_service(action, sep)
114029794Skarels 	char *action;
114129794Skarels 	struct servtab *sep;
114229794Skarels {
114329794Skarels 	fprintf(stderr,
114429794Skarels 	    "%s: %s proto=%s, wait=%d, user=%s builtin=%x server=%s\n",
114529794Skarels 	    action, sep->se_service, sep->se_proto,
114636603Sbostic 	    sep->se_wait, sep->se_user, (int)sep->se_bi, sep->se_server);
114729794Skarels }
114857769Sandrew 
114957769Sandrew /*
115057769Sandrew  *  Based on TCPMUX.C by Mark K. Lottor November 1988
115157769Sandrew  *  sri-nic::ps:<mkl>tcpmux.c
115257769Sandrew  */
115357769Sandrew 
115457769Sandrew 
115557769Sandrew static int		/* # of characters upto \r,\n or \0 */
115657769Sandrew getline(fd, buf, len)
115757769Sandrew 	int fd;
115857769Sandrew 	char *buf;
115957769Sandrew 	int len;
116057769Sandrew {
116157769Sandrew 	int count = 0, n;
116257769Sandrew 
116357769Sandrew 	do {
116457769Sandrew 		n = read(fd, buf, len-count);
116557769Sandrew 		if (n == 0)
116666696Spendry 			return (count);
116757769Sandrew 		if (n < 0)
116857769Sandrew 			return (-1);
116957769Sandrew 		while (--n >= 0) {
117057769Sandrew 			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
117166696Spendry 				return (count);
117257769Sandrew 			count++;
117357769Sandrew 			buf++;
117457769Sandrew 		}
117557769Sandrew 	} while (count < len);
117657769Sandrew 	return (count);
117757769Sandrew }
117857769Sandrew 
117957769Sandrew #define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
118057769Sandrew 
118157769Sandrew #define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
118257769Sandrew 
118357769Sandrew struct servtab *
118457769Sandrew tcpmux(s)
118557769Sandrew 	int s;
118657769Sandrew {
118766696Spendry 	struct servtab *sep;
118857769Sandrew 	char service[MAX_SERV_LEN+1];
118957769Sandrew 	int len;
119057769Sandrew 
119157769Sandrew 	/* Get requested service name */
119257769Sandrew 	if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
119366696Spendry 		strwrite(s, "-Error reading service name\r\n");
119466696Spendry 		return (NULL);
119557769Sandrew 	}
119657769Sandrew 	service[len] = '\0';
119757769Sandrew 
119857769Sandrew 	if (debug)
119966696Spendry 		fprintf(stderr, "tcpmux: someone wants %s\n", service);
120057769Sandrew 
120157769Sandrew 	/*
120257769Sandrew 	 * Help is a required command, and lists available services,
120357769Sandrew 	 * one per line.
120457769Sandrew 	 */
120566696Spendry 	if (!strcasecmp(service, "help")) {
120666696Spendry 		for (sep = servtab; sep; sep = sep->se_next) {
120766696Spendry 			if (!ISMUX(sep))
120866696Spendry 				continue;
120966696Spendry 			(void)write(s,sep->se_service,strlen(sep->se_service));
121066696Spendry 			strwrite(s, "\r\n");
121166696Spendry 		}
121266696Spendry 		return (NULL);
121357769Sandrew 	}
121457769Sandrew 
121557769Sandrew 	/* Try matching a service in inetd.conf with the request */
121657769Sandrew 	for (sep = servtab; sep; sep = sep->se_next) {
121766696Spendry 		if (!ISMUX(sep))
121866696Spendry 			continue;
121966696Spendry 		if (!strcasecmp(service, sep->se_service)) {
122066696Spendry 			if (ISMUXPLUS(sep)) {
122166696Spendry 				strwrite(s, "+Go\r\n");
122266696Spendry 			}
122366696Spendry 			return (sep);
122457769Sandrew 		}
122557769Sandrew 	}
122657769Sandrew 	strwrite(s, "-Service not available\r\n");
122766696Spendry 	return (NULL);
122857769Sandrew }
1229