xref: /csrg-svn/usr.sbin/inetd/inetd.c (revision 66779)
121134Sdist /*
266778Spendry  * 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[] =
1066778Spendry "@(#) 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*66779Skarels static char sccsid[] = "@(#)inetd.c	8.4 (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
main(argc,argv,envp)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);
294*66779Skarels 		    if (!sep->se_wait && 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");
351*66779Skarels 			    if (!sep->se_wait &&
352*66779Skarels 				sep->se_socktype == SOCK_STREAM)
35346973Skarels 				    close(ctrl);
35446973Skarels 			    sigsetmask(0L);
35546973Skarels 			    sleep(1);
35646973Skarels 			    continue;
35746973Skarels 		    }
35846973Skarels 		    if (pid && sep->se_wait) {
35946973Skarels 			    sep->se_wait = pid;
36049806Sbostic 			    if (sep->se_fd >= 0) {
36149806Sbostic 				FD_CLR(sep->se_fd, &allsock);
36249806Sbostic 			        nsock--;
36349806Sbostic 			    }
36446973Skarels 		    }
36546973Skarels 		    sigsetmask(0L);
36646973Skarels 		    if (pid == 0) {
36746973Skarels 			    if (debug && dofork)
36844711Skarels 				setsid();
36957769Sandrew 			    if (dofork) {
37057769Sandrew 				if (debug)
37157769Sandrew 					fprintf(stderr, "+ Closing from %d\n",
37257769Sandrew 						maxsock);
37357769Sandrew 				for (tmpint = maxsock; tmpint > 2; tmpint--)
37436603Sbostic 					if (tmpint != ctrl)
37536603Sbostic 						close(tmpint);
37657769Sandrew 			    }
37746973Skarels 			    if (sep->se_bi)
37826877Skarels 				(*sep->se_bi->bi_fn)(ctrl, sep);
37946973Skarels 			    else {
38046973Skarels 				if (debug)
38146973Skarels 					fprintf(stderr, "%d execl %s\n",
38246973Skarels 					    getpid(), sep->se_server);
38326877Skarels 				dup2(ctrl, 0);
38426877Skarels 				close(ctrl);
38526877Skarels 				dup2(0, 1);
38626877Skarels 				dup2(0, 2);
38726877Skarels 				if ((pwd = getpwnam(sep->se_user)) == NULL) {
38826877Skarels 					syslog(LOG_ERR,
38957769Sandrew 					    "%s/%s: %s: No such user",
39057769Sandrew 						sep->se_service, sep->se_proto,
39157769Sandrew 						sep->se_user);
39227535Skarels 					if (sep->se_socktype != SOCK_STREAM)
39327535Skarels 						recv(0, buf, sizeof (buf), 0);
39426877Skarels 					_exit(1);
39526877Skarels 				}
39626877Skarels 				if (pwd->pw_uid) {
39757769Sandrew 					if (setgid(pwd->pw_gid) < 0) {
39857769Sandrew 						syslog(LOG_ERR,
39957769Sandrew 						  "%s: can't set gid %d: %m",
40057769Sandrew 						  sep->se_service, pwd->pw_gid);
40157769Sandrew 						_exit(1);
40257769Sandrew 					}
40357769Sandrew 					(void) initgroups(pwd->pw_name,
40457769Sandrew 							pwd->pw_gid);
40557769Sandrew 					if (setuid(pwd->pw_uid) < 0) {
40657769Sandrew 						syslog(LOG_ERR,
40757769Sandrew 						  "%s: can't set uid %d: %m",
40857769Sandrew 						  sep->se_service, pwd->pw_uid);
40957769Sandrew 						_exit(1);
41057769Sandrew 					}
41126877Skarels 				}
41226877Skarels 				execv(sep->se_server, sep->se_argv);
41326877Skarels 				if (sep->se_socktype != SOCK_STREAM)
41426877Skarels 					recv(0, buf, sizeof (buf), 0);
41557769Sandrew 				syslog(LOG_ERR,
41657769Sandrew 				    "cannot execute %s: %m", sep->se_server);
41726877Skarels 				_exit(1);
41846973Skarels 			    }
41946973Skarels 		    }
420*66779Skarels 		    if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
42146973Skarels 			    close(ctrl);
42216374Skarels 		}
42316374Skarels 	}
42416374Skarels }
42516374Skarels 
42639838Sbostic void
reapchild(signo)42766696Spendry reapchild(signo)
42866696Spendry 	int signo;
42916374Skarels {
43046973Skarels 	int status;
43166696Spendry 	pid_t pid;
43266696Spendry 	struct servtab *sep;
43316374Skarels 
43416374Skarels 	for (;;) {
43546973Skarels 		pid = wait3(&status, WNOHANG, (struct rusage *)0);
43616374Skarels 		if (pid <= 0)
43716374Skarels 			break;
43816374Skarels 		if (debug)
43957769Sandrew 			fprintf(stderr, "%d reaped, status %#x\n",
44057769Sandrew 				pid, status);
44116374Skarels 		for (sep = servtab; sep; sep = sep->se_next)
44216374Skarels 			if (sep->se_wait == pid) {
44346973Skarels 				if (status)
44416510Sralph 					syslog(LOG_WARNING,
44516510Sralph 					    "%s: exit status 0x%x",
44616374Skarels 					    sep->se_server, status);
44716374Skarels 				if (debug)
44816374Skarels 					fprintf(stderr, "restored %s, fd %d\n",
44916374Skarels 					    sep->se_service, sep->se_fd);
45025046Skarels 				FD_SET(sep->se_fd, &allsock);
45125046Skarels 				nsock++;
45216374Skarels 				sep->se_wait = 1;
45316374Skarels 			}
45416374Skarels 	}
45516374Skarels }
45616374Skarels 
45739838Sbostic void
config(signo)45866696Spendry config(signo)
45966696Spendry 	int signo;
46016374Skarels {
46166696Spendry 	struct servtab *sep, *cp, **sepp;
46257769Sandrew 	struct passwd *pwd;
46332246Sbostic 	long omask;
46416374Skarels 
46516374Skarels 	if (!setconfig()) {
46616510Sralph 		syslog(LOG_ERR, "%s: %m", CONFIG);
46716374Skarels 		return;
46816374Skarels 	}
46916374Skarels 	for (sep = servtab; sep; sep = sep->se_next)
47016374Skarels 		sep->se_checked = 0;
47116374Skarels 	while (cp = getconfigent()) {
47257769Sandrew 		if ((pwd = getpwnam(cp->se_user)) == NULL) {
47357769Sandrew 			syslog(LOG_ERR,
47457769Sandrew 				"%s/%s: No such user '%s', service ignored",
47557769Sandrew 				cp->se_service, cp->se_proto, cp->se_user);
47657769Sandrew 			continue;
47757769Sandrew 		}
47816374Skarels 		for (sep = servtab; sep; sep = sep->se_next)
47916374Skarels 			if (strcmp(sep->se_service, cp->se_service) == 0 &&
48016374Skarels 			    strcmp(sep->se_proto, cp->se_proto) == 0)
48116374Skarels 				break;
48216374Skarels 		if (sep != 0) {
48316374Skarels 			int i;
48416374Skarels 
48527535Skarels 			omask = sigblock(SIGBLOCK);
48640745Sbostic 			/*
48740745Sbostic 			 * sep->se_wait may be holding the pid of a daemon
48840745Sbostic 			 * that we're waiting for.  If so, don't overwrite
48940745Sbostic 			 * it unless the config file explicitly says don't
49040745Sbostic 			 * wait.
49140745Sbostic 			 */
49240745Sbostic 			if (cp->se_bi == 0 &&
49340745Sbostic 			    (sep->se_wait == 1 || cp->se_wait == 0))
49427535Skarels 				sep->se_wait = cp->se_wait;
49516374Skarels #define SWAP(a, b) { char *c = a; a = b; b = c; }
49626921Slepreau 			if (cp->se_user)
49726921Slepreau 				SWAP(sep->se_user, cp->se_user);
49816374Skarels 			if (cp->se_server)
49916374Skarels 				SWAP(sep->se_server, cp->se_server);
50016374Skarels 			for (i = 0; i < MAXARGV; i++)
50116374Skarels 				SWAP(sep->se_argv[i], cp->se_argv[i]);
50216374Skarels 			sigsetmask(omask);
50316374Skarels 			freeconfig(cp);
50429794Skarels 			if (debug)
50529794Skarels 				print_service("REDO", sep);
50629794Skarels 		} else {
50716374Skarels 			sep = enter(cp);
50829794Skarels 			if (debug)
50929794Skarels 				print_service("ADD ", sep);
51029794Skarels 		}
51116374Skarels 		sep->se_checked = 1;
51257769Sandrew 		if (ISMUX(sep)) {
51357769Sandrew 			sep->se_fd = -1;
51457769Sandrew 			continue;
51557769Sandrew 		}
51616374Skarels 		sp = getservbyname(sep->se_service, sep->se_proto);
51716374Skarels 		if (sp == 0) {
51816510Sralph 			syslog(LOG_ERR, "%s/%s: unknown service",
51916374Skarels 			    sep->se_service, sep->se_proto);
52057769Sandrew 			sep->se_checked = 0;
52116374Skarels 			continue;
52216374Skarels 		}
52327535Skarels 		if (sp->s_port != sep->se_ctrladdr.sin_port) {
52460924Smckusick 			sep->se_ctrladdr.sin_family = AF_INET;
52527535Skarels 			sep->se_ctrladdr.sin_port = sp->s_port;
52657769Sandrew 			if (sep->se_fd >= 0)
52757769Sandrew 				close_sep(sep);
52816374Skarels 		}
52927535Skarels 		if (sep->se_fd == -1)
53027535Skarels 			setup(sep);
53116374Skarels 	}
53216374Skarels 	endconfig();
53316374Skarels 	/*
53416374Skarels 	 * Purge anything not looked at above.
53516374Skarels 	 */
53627535Skarels 	omask = sigblock(SIGBLOCK);
53716374Skarels 	sepp = &servtab;
53816374Skarels 	while (sep = *sepp) {
53916374Skarels 		if (sep->se_checked) {
54016374Skarels 			sepp = &sep->se_next;
54116374Skarels 			continue;
54216374Skarels 		}
54316374Skarels 		*sepp = sep->se_next;
54457769Sandrew 		if (sep->se_fd >= 0)
54557769Sandrew 			close_sep(sep);
54629794Skarels 		if (debug)
54729794Skarels 			print_service("FREE", sep);
54816374Skarels 		freeconfig(sep);
54916374Skarels 		free((char *)sep);
55016374Skarels 	}
55116374Skarels 	(void) sigsetmask(omask);
55216374Skarels }
55316374Skarels 
55439838Sbostic void
retry(signo)55566696Spendry retry(signo)
55666696Spendry 	int signo;
55727535Skarels {
55866696Spendry 	struct servtab *sep;
55927535Skarels 
56027535Skarels 	timingout = 0;
56127535Skarels 	for (sep = servtab; sep; sep = sep->se_next)
56227535Skarels 		if (sep->se_fd == -1)
56327535Skarels 			setup(sep);
56427535Skarels }
56527535Skarels 
56666696Spendry void
setup(sep)56727535Skarels setup(sep)
56866696Spendry 	struct servtab *sep;
56927535Skarels {
57027535Skarels 	int on = 1;
57127535Skarels 
57227535Skarels 	if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
57357769Sandrew 		if (debug)
57457769Sandrew 			fprintf(stderr, "socket failed on %s/%s: %s\n",
57557769Sandrew 				sep->se_service, sep->se_proto,
57657769Sandrew 				strerror(errno));
57727535Skarels 		syslog(LOG_ERR, "%s/%s: socket: %m",
57827535Skarels 		    sep->se_service, sep->se_proto);
57927535Skarels 		return;
58027535Skarels 	}
58127535Skarels #define	turnon(fd, opt) \
58227535Skarels setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
58327535Skarels 	if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
58427535Skarels 	    turnon(sep->se_fd, SO_DEBUG) < 0)
58527535Skarels 		syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
58627535Skarels 	if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
58727535Skarels 		syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
58827535Skarels #undef turnon
58946907Sbostic 	if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
59027535Skarels 	    sizeof (sep->se_ctrladdr)) < 0) {
59157769Sandrew 		if (debug)
59257769Sandrew 			fprintf(stderr, "bind failed on %s/%s: %s\n",
59357769Sandrew 				sep->se_service, sep->se_proto,
59457769Sandrew 				strerror(errno));
59527535Skarels 		syslog(LOG_ERR, "%s/%s: bind: %m",
59627535Skarels 		    sep->se_service, sep->se_proto);
59727535Skarels 		(void) close(sep->se_fd);
59827535Skarels 		sep->se_fd = -1;
59927535Skarels 		if (!timingout) {
60027535Skarels 			timingout = 1;
60127535Skarels 			alarm(RETRYTIME);
60227535Skarels 		}
60327535Skarels 		return;
60427535Skarels 	}
60527535Skarels 	if (sep->se_socktype == SOCK_STREAM)
60627535Skarels 		listen(sep->se_fd, 10);
60727535Skarels 	FD_SET(sep->se_fd, &allsock);
60827535Skarels 	nsock++;
60927535Skarels 	if (sep->se_fd > maxsock)
61027535Skarels 		maxsock = sep->se_fd;
61157769Sandrew 	if (debug) {
61257769Sandrew 		fprintf(stderr, "registered %s on %d\n",
61357769Sandrew 			sep->se_server, sep->se_fd);
61457769Sandrew 	}
61527535Skarels }
61627535Skarels 
61757769Sandrew /*
61857769Sandrew  * Finish with a service and its socket.
61957769Sandrew  */
62066696Spendry void
close_sep(sep)62157769Sandrew close_sep(sep)
62266696Spendry 	struct servtab *sep;
62357769Sandrew {
62457769Sandrew 	if (sep->se_fd >= 0) {
62557769Sandrew 		nsock--;
62657769Sandrew 		FD_CLR(sep->se_fd, &allsock);
62757769Sandrew 		(void) close(sep->se_fd);
62857769Sandrew 		sep->se_fd = -1;
62957769Sandrew 	}
63057769Sandrew 	sep->se_count = 0;
63157769Sandrew 	/*
63257769Sandrew 	 * Don't keep the pid of this running deamon: when reapchild()
63357769Sandrew 	 * reaps this pid, it would erroneously increment nsock.
63457769Sandrew 	 */
63557769Sandrew 	if (sep->se_wait > 1)
63657769Sandrew 		sep->se_wait = 1;
63757769Sandrew }
63857769Sandrew 
63916374Skarels struct servtab *
enter(cp)64016374Skarels enter(cp)
64116374Skarels 	struct servtab *cp;
64216374Skarels {
64366696Spendry 	struct servtab *sep;
64432246Sbostic 	long omask;
64516374Skarels 
64616374Skarels 	sep = (struct servtab *)malloc(sizeof (*sep));
64716374Skarels 	if (sep == (struct servtab *)0) {
64816510Sralph 		syslog(LOG_ERR, "Out of memory.");
64916374Skarels 		exit(-1);
65016374Skarels 	}
65116374Skarels 	*sep = *cp;
65216374Skarels 	sep->se_fd = -1;
65327535Skarels 	omask = sigblock(SIGBLOCK);
65416374Skarels 	sep->se_next = servtab;
65516374Skarels 	servtab = sep;
65616374Skarels 	sigsetmask(omask);
65716374Skarels 	return (sep);
65816374Skarels }
65916374Skarels 
66016374Skarels FILE	*fconfig = NULL;
66116374Skarels struct	servtab serv;
66266696Spendry char	line[LINE_MAX];
66316374Skarels 
66466696Spendry int
setconfig()66516374Skarels setconfig()
66616374Skarels {
66716374Skarels 
66816374Skarels 	if (fconfig != NULL) {
66957769Sandrew 		fseek(fconfig, 0L, SEEK_SET);
67016374Skarels 		return (1);
67116374Skarels 	}
67216374Skarels 	fconfig = fopen(CONFIG, "r");
67316374Skarels 	return (fconfig != NULL);
67416374Skarels }
67516374Skarels 
67666696Spendry void
endconfig()67716374Skarels endconfig()
67816374Skarels {
67936603Sbostic 	if (fconfig) {
68036603Sbostic 		(void) fclose(fconfig);
68136603Sbostic 		fconfig = NULL;
68236603Sbostic 	}
68316374Skarels }
68416374Skarels 
68516374Skarels struct servtab *
getconfigent()68616374Skarels getconfigent()
68716374Skarels {
68866696Spendry 	struct servtab *sep = &serv;
68916374Skarels 	int argc;
69066696Spendry 	char *cp, *arg;
69157769Sandrew 	static char TCPMUX_TOKEN[] = "tcpmux/";
69257769Sandrew #define MUX_LEN		(sizeof(TCPMUX_TOKEN)-1)
69316374Skarels 
69426877Skarels more:
69557769Sandrew 	while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0'))
69616374Skarels 		;
69716374Skarels 	if (cp == NULL)
69816374Skarels 		return ((struct servtab *)0);
69957769Sandrew 	/*
70057769Sandrew 	 * clear the static buffer, since some fields (se_ctrladdr,
70157769Sandrew 	 * for example) don't get initialized here.
70257769Sandrew 	 */
70366696Spendry 	memset((caddr_t)sep, 0, sizeof *sep);
70416374Skarels 	arg = skip(&cp);
70557769Sandrew 	if (cp == NULL) {
70657769Sandrew 		/* got an empty line containing just blanks/tabs. */
70757769Sandrew 		goto more;
70857769Sandrew 	}
70957769Sandrew 	if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
71057769Sandrew 		char *c = arg + MUX_LEN;
71157769Sandrew 		if (*c == '+') {
71257769Sandrew 			sep->se_type = MUXPLUS_TYPE;
71357769Sandrew 			c++;
71457769Sandrew 		} else
71557769Sandrew 			sep->se_type = MUX_TYPE;
71657769Sandrew 		sep->se_service = newstr(c);
71757769Sandrew 	} else {
71857769Sandrew 		sep->se_service = newstr(arg);
71957769Sandrew 		sep->se_type = NORM_TYPE;
72057769Sandrew 	}
72157769Sandrew 	arg = sskip(&cp);
72216374Skarels 	if (strcmp(arg, "stream") == 0)
72316374Skarels 		sep->se_socktype = SOCK_STREAM;
72416374Skarels 	else if (strcmp(arg, "dgram") == 0)
72516374Skarels 		sep->se_socktype = SOCK_DGRAM;
72616374Skarels 	else if (strcmp(arg, "rdm") == 0)
72716374Skarels 		sep->se_socktype = SOCK_RDM;
72816374Skarels 	else if (strcmp(arg, "seqpacket") == 0)
72916374Skarels 		sep->se_socktype = SOCK_SEQPACKET;
73016374Skarels 	else if (strcmp(arg, "raw") == 0)
73116374Skarels 		sep->se_socktype = SOCK_RAW;
73216374Skarels 	else
73316374Skarels 		sep->se_socktype = -1;
73457769Sandrew 	sep->se_proto = newstr(sskip(&cp));
73557769Sandrew 	arg = sskip(&cp);
73616374Skarels 	sep->se_wait = strcmp(arg, "wait") == 0;
73757769Sandrew 	if (ISMUX(sep)) {
73857769Sandrew 		/*
73957769Sandrew 		 * Silently enforce "nowait" for TCPMUX services since
74057769Sandrew 		 * they don't have an assigned port to listen on.
74157769Sandrew 		 */
74257769Sandrew 		sep->se_wait = 0;
74357769Sandrew 
74457769Sandrew 		if (strcmp(sep->se_proto, "tcp")) {
74557769Sandrew 			syslog(LOG_ERR,
74657769Sandrew 				"%s: bad protocol for tcpmux service %s",
74757769Sandrew 				CONFIG, sep->se_service);
74857769Sandrew 			goto more;
74957769Sandrew 		}
75057769Sandrew 		if (sep->se_socktype != SOCK_STREAM) {
75157769Sandrew 			syslog(LOG_ERR,
75257769Sandrew 				"%s: bad socket type for tcpmux service %s",
75357769Sandrew 				CONFIG, sep->se_service);
75457769Sandrew 			goto more;
75557769Sandrew 		}
75657769Sandrew 	}
75757769Sandrew 	sep->se_user = newstr(sskip(&cp));
75857769Sandrew 	sep->se_server = newstr(sskip(&cp));
75926877Skarels 	if (strcmp(sep->se_server, "internal") == 0) {
76066696Spendry 		struct biltin *bi;
76126877Skarels 
76226877Skarels 		for (bi = biltins; bi->bi_service; bi++)
76326877Skarels 			if (bi->bi_socktype == sep->se_socktype &&
76426877Skarels 			    strcmp(bi->bi_service, sep->se_service) == 0)
76526877Skarels 				break;
76626877Skarels 		if (bi->bi_service == 0) {
76757769Sandrew 			syslog(LOG_ERR, "internal service %s unknown",
76826877Skarels 				sep->se_service);
76926877Skarels 			goto more;
77026877Skarels 		}
77126877Skarels 		sep->se_bi = bi;
77226877Skarels 		sep->se_wait = bi->bi_wait;
77329794Skarels 	} else
77429794Skarels 		sep->se_bi = NULL;
77516374Skarels 	argc = 0;
77616374Skarels 	for (arg = skip(&cp); cp; arg = skip(&cp))
77716374Skarels 		if (argc < MAXARGV)
77846907Sbostic 			sep->se_argv[argc++] = newstr(arg);
77916374Skarels 	while (argc <= MAXARGV)
78016374Skarels 		sep->se_argv[argc++] = NULL;
78116374Skarels 	return (sep);
78216374Skarels }
78316374Skarels 
78466696Spendry void
freeconfig(cp)78516374Skarels freeconfig(cp)
78666696Spendry 	struct servtab *cp;
78716374Skarels {
78816374Skarels 	int i;
78916374Skarels 
79016374Skarels 	if (cp->se_service)
79116374Skarels 		free(cp->se_service);
79216374Skarels 	if (cp->se_proto)
79316374Skarels 		free(cp->se_proto);
79426921Slepreau 	if (cp->se_user)
79526921Slepreau 		free(cp->se_user);
79616374Skarels 	if (cp->se_server)
79716374Skarels 		free(cp->se_server);
79816374Skarels 	for (i = 0; i < MAXARGV; i++)
79916374Skarels 		if (cp->se_argv[i])
80016374Skarels 			free(cp->se_argv[i]);
80116374Skarels }
80216374Skarels 
80357769Sandrew 
80457769Sandrew /*
80557769Sandrew  * Safe skip - if skip returns null, log a syntax error in the
80657769Sandrew  * configuration file and exit.
80757769Sandrew  */
80816374Skarels char *
sskip(cpp)80957769Sandrew sskip(cpp)
81057769Sandrew 	char **cpp;
81157769Sandrew {
81266696Spendry 	char *cp;
81357769Sandrew 
81457769Sandrew 	cp = skip(cpp);
81557769Sandrew 	if (cp == NULL) {
81657769Sandrew 		syslog(LOG_ERR, "%s: syntax error", CONFIG);
81757769Sandrew 		exit(-1);
81857769Sandrew 	}
81957769Sandrew 	return (cp);
82057769Sandrew }
82157769Sandrew 
82257769Sandrew char *
skip(cpp)82316374Skarels skip(cpp)
82416374Skarels 	char **cpp;
82516374Skarels {
82666696Spendry 	char *cp = *cpp;
82716374Skarels 	char *start;
82816374Skarels 
82916374Skarels again:
83016374Skarels 	while (*cp == ' ' || *cp == '\t')
83116374Skarels 		cp++;
83216374Skarels 	if (*cp == '\0') {
83343455Sbostic 		int c;
83416374Skarels 
83516374Skarels 		c = getc(fconfig);
83636603Sbostic 		(void) ungetc(c, fconfig);
83716374Skarels 		if (c == ' ' || c == '\t')
83816374Skarels 			if (cp = nextline(fconfig))
83916374Skarels 				goto again;
84016374Skarels 		*cpp = (char *)0;
84116374Skarels 		return ((char *)0);
84216374Skarels 	}
84316374Skarels 	start = cp;
84416374Skarels 	while (*cp && *cp != ' ' && *cp != '\t')
84516374Skarels 		cp++;
84616374Skarels 	if (*cp != '\0')
84716374Skarels 		*cp++ = '\0';
84816374Skarels 	*cpp = cp;
84916374Skarels 	return (start);
85016374Skarels }
85116374Skarels 
85216374Skarels char *
nextline(fd)85316374Skarels nextline(fd)
85416374Skarels 	FILE *fd;
85516374Skarels {
85616374Skarels 	char *cp;
85716374Skarels 
85826921Slepreau 	if (fgets(line, sizeof (line), fd) == NULL)
85916374Skarels 		return ((char *)0);
86066696Spendry 	cp = strchr(line, '\n');
86116374Skarels 	if (cp)
86216374Skarels 		*cp = '\0';
86316374Skarels 	return (line);
86416374Skarels }
86516374Skarels 
86616374Skarels char *
newstr(cp)86746907Sbostic newstr(cp)
86816374Skarels 	char *cp;
86916374Skarels {
87046973Skarels 	if (cp = strdup(cp ? cp : ""))
87166696Spendry 		return (cp);
87246973Skarels 	syslog(LOG_ERR, "strdup: %m");
87346907Sbostic 	exit(-1);
87416374Skarels }
87526877Skarels 
87666696Spendry void
setproctitle(a,s)87726877Skarels setproctitle(a, s)
87826877Skarels 	char *a;
87926877Skarels 	int s;
88026877Skarels {
88126877Skarels 	int size;
88266696Spendry 	char *cp;
88326877Skarels 	struct sockaddr_in sin;
88426877Skarels 	char buf[80];
88526877Skarels 
88626877Skarels 	cp = Argv[0];
88726877Skarels 	size = sizeof(sin);
88846907Sbostic 	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
88932442Sbostic 		(void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr));
89026877Skarels 	else
89132442Sbostic 		(void) sprintf(buf, "-%s", a);
89226877Skarels 	strncpy(cp, buf, LastArg - cp);
89326877Skarels 	cp += strlen(cp);
89426877Skarels 	while (cp < LastArg)
89526877Skarels 		*cp++ = ' ';
89626877Skarels }
89726877Skarels 
89826877Skarels /*
89926877Skarels  * Internet services provided internally by inetd:
90026877Skarels  */
90149986Skarels #define	BUFSIZE	8192
90226877Skarels 
90326877Skarels /* ARGSUSED */
90466696Spendry void
echo_stream(s,sep)90526877Skarels echo_stream(s, sep)		/* Echo service -- echo data back */
90626877Skarels 	int s;
90726877Skarels 	struct servtab *sep;
90826877Skarels {
90938573Skarels 	char buffer[BUFSIZE];
91026877Skarels 	int i;
91126877Skarels 
91232091Skarels 	setproctitle(sep->se_service, s);
91326877Skarels 	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
91426877Skarels 	    write(s, buffer, i) > 0)
91526877Skarels 		;
91626877Skarels 	exit(0);
91726877Skarels }
91826877Skarels 
91926877Skarels /* ARGSUSED */
92066696Spendry void
echo_dg(s,sep)92126877Skarels echo_dg(s, sep)			/* Echo service -- echo data back */
92226877Skarels 	int s;
92326877Skarels 	struct servtab *sep;
92426877Skarels {
92538573Skarels 	char buffer[BUFSIZE];
92626877Skarels 	int i, size;
92726877Skarels 	struct sockaddr sa;
92826877Skarels 
92926877Skarels 	size = sizeof(sa);
93026877Skarels 	if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0)
93126877Skarels 		return;
93226877Skarels 	(void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
93326877Skarels }
93426877Skarels 
93526877Skarels /* ARGSUSED */
93666696Spendry void
discard_stream(s,sep)93726877Skarels discard_stream(s, sep)		/* Discard service -- ignore data */
93826877Skarels 	int s;
93926877Skarels 	struct servtab *sep;
94026877Skarels {
94149986Skarels 	int ret;
94238573Skarels 	char buffer[BUFSIZE];
94326877Skarels 
94432091Skarels 	setproctitle(sep->se_service, s);
94526877Skarels 	while (1) {
94649986Skarels 		while ((ret = read(s, buffer, sizeof(buffer))) > 0)
94726877Skarels 			;
94849986Skarels 		if (ret == 0 || errno != EINTR)
94926877Skarels 			break;
95026877Skarels 	}
95126877Skarels 	exit(0);
95226877Skarels }
95326877Skarels 
95426877Skarels /* ARGSUSED */
95566696Spendry void
discard_dg(s,sep)95626877Skarels discard_dg(s, sep)		/* Discard service -- ignore data */
95726877Skarels 	int s;
95826877Skarels 	struct servtab *sep;
95926877Skarels {
96038573Skarels 	char buffer[BUFSIZE];
96126877Skarels 
96226877Skarels 	(void) read(s, buffer, sizeof(buffer));
96326877Skarels }
96426877Skarels 
96526877Skarels #include <ctype.h>
96626877Skarels #define LINESIZ 72
96726877Skarels char ring[128];
96826877Skarels char *endring;
96926877Skarels 
97066696Spendry void
initring()97126877Skarels initring()
97226877Skarels {
97366696Spendry 	int i;
97426877Skarels 
97526877Skarels 	endring = ring;
97626877Skarels 
97726877Skarels 	for (i = 0; i <= 128; ++i)
97826877Skarels 		if (isprint(i))
97926877Skarels 			*endring++ = i;
98026877Skarels }
98126877Skarels 
98226877Skarels /* ARGSUSED */
98366696Spendry void
chargen_stream(s,sep)98426877Skarels chargen_stream(s, sep)		/* Character generator */
98526877Skarels 	int s;
98626877Skarels 	struct servtab *sep;
98726877Skarels {
98832246Sbostic 	int len;
98966696Spendry 	char *rs, text[LINESIZ+2];
99026877Skarels 
99132091Skarels 	setproctitle(sep->se_service, s);
99232246Sbostic 
99332246Sbostic 	if (!endring) {
99426877Skarels 		initring();
99532246Sbostic 		rs = ring;
99632246Sbostic 	}
99726877Skarels 
99832246Sbostic 	text[LINESIZ] = '\r';
99932246Sbostic 	text[LINESIZ + 1] = '\n';
100032246Sbostic 	for (rs = ring;;) {
100132246Sbostic 		if ((len = endring - rs) >= LINESIZ)
100266696Spendry 			memmove(text, rs, LINESIZ);
100332246Sbostic 		else {
100466696Spendry 			memmove(text, rs, len);
100566696Spendry 			memmove(text + len, ring, LINESIZ - len);
100632246Sbostic 		}
100732246Sbostic 		if (++rs == endring)
100826877Skarels 			rs = ring;
100932246Sbostic 		if (write(s, text, sizeof(text)) != sizeof(text))
101026877Skarels 			break;
101126877Skarels 	}
101226877Skarels 	exit(0);
101326877Skarels }
101426877Skarels 
101526877Skarels /* ARGSUSED */
101666696Spendry void
chargen_dg(s,sep)101726877Skarels chargen_dg(s, sep)		/* Character generator */
101826877Skarels 	int s;
101926877Skarels 	struct servtab *sep;
102026877Skarels {
102132246Sbostic 	struct sockaddr sa;
102232246Sbostic 	static char *rs;
102332246Sbostic 	int len, size;
102426877Skarels 	char text[LINESIZ+2];
102526877Skarels 
102632246Sbostic 	if (endring == 0) {
102726877Skarels 		initring();
102832246Sbostic 		rs = ring;
102932246Sbostic 	}
103026877Skarels 
103126877Skarels 	size = sizeof(sa);
103226877Skarels 	if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
103326877Skarels 		return;
103432246Sbostic 
103532246Sbostic 	if ((len = endring - rs) >= LINESIZ)
103666696Spendry 		memmove(text, rs, LINESIZ);
103732246Sbostic 	else {
103866696Spendry 		memmove(text, rs, len);
103966696Spendry 		memmove(text + len, ring, LINESIZ - len);
104032246Sbostic 	}
104132246Sbostic 	if (++rs == endring)
104226877Skarels 		rs = ring;
104332246Sbostic 	text[LINESIZ] = '\r';
104432246Sbostic 	text[LINESIZ + 1] = '\n';
104526877Skarels 	(void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
104626877Skarels }
104726877Skarels 
104826877Skarels /*
104926877Skarels  * Return a machine readable date and time, in the form of the
105026877Skarels  * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
105126877Skarels  * returns the number of seconds since midnight, Jan 1, 1970,
105226877Skarels  * we must add 2208988800 seconds to this figure to make up for
105326877Skarels  * some seventy years Bell Labs was asleep.
105426877Skarels  */
105526877Skarels 
105626877Skarels long
machtime()105726877Skarels machtime()
105826877Skarels {
105926877Skarels 	struct timeval tv;
106026877Skarels 
106126921Slepreau 	if (gettimeofday(&tv, (struct timezone *)0) < 0) {
106257769Sandrew 		if (debug)
106357769Sandrew 			fprintf(stderr, "Unable to get time of day\n");
106426921Slepreau 		return (0L);
106526877Skarels 	}
106660073Storek #define	OFFSET ((u_long)25567 * 24*60*60)
106760073Storek 	return (htonl((long)(tv.tv_sec + OFFSET)));
106860073Storek #undef OFFSET
106926877Skarels }
107026877Skarels 
107126877Skarels /* ARGSUSED */
107266696Spendry void
machtime_stream(s,sep)107326877Skarels machtime_stream(s, sep)
107426877Skarels 	int s;
107526877Skarels 	struct servtab *sep;
107626877Skarels {
107726877Skarels 	long result;
107826877Skarels 
107926877Skarels 	result = machtime();
108026877Skarels 	(void) write(s, (char *) &result, sizeof(result));
108126877Skarels }
108226877Skarels 
108326877Skarels /* ARGSUSED */
108466696Spendry void
machtime_dg(s,sep)108526877Skarels machtime_dg(s, sep)
108626877Skarels 	int s;
108726877Skarels 	struct servtab *sep;
108826877Skarels {
108926877Skarels 	long result;
109026877Skarels 	struct sockaddr sa;
109126877Skarels 	int size;
109226877Skarels 
109326877Skarels 	size = sizeof(sa);
109426921Slepreau 	if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0)
109526877Skarels 		return;
109626877Skarels 	result = machtime();
109726877Skarels 	(void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
109826877Skarels }
109926877Skarels 
110026877Skarels /* ARGSUSED */
110166696Spendry void
daytime_stream(s,sep)110226877Skarels daytime_stream(s, sep)		/* Return human-readable time of day */
110326877Skarels 	int s;
110426877Skarels 	struct servtab *sep;
110526877Skarels {
110626877Skarels 	char buffer[256];
110757769Sandrew 	time_t clock;
110826877Skarels 
110926877Skarels 	clock = time((time_t *) 0);
111026877Skarels 
111132442Sbostic 	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
111226921Slepreau 	(void) write(s, buffer, strlen(buffer));
111326877Skarels }
111426877Skarels 
111526877Skarels /* ARGSUSED */
111666696Spendry void
daytime_dg(s,sep)111726877Skarels daytime_dg(s, sep)		/* Return human-readable time of day */
111826877Skarels 	int s;
111926877Skarels 	struct servtab *sep;
112026877Skarels {
112126877Skarels 	char buffer[256];
112257769Sandrew 	time_t clock;
112326877Skarels 	struct sockaddr sa;
112426877Skarels 	int size;
112526877Skarels 
112626877Skarels 	clock = time((time_t *) 0);
112726877Skarels 
112826877Skarels 	size = sizeof(sa);
112926877Skarels 	if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
113026877Skarels 		return;
113132442Sbostic 	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
113226877Skarels 	(void) sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa));
113326877Skarels }
113429794Skarels 
113529794Skarels /*
113629794Skarels  * print_service:
113729794Skarels  *	Dump relevant information to stderr
113829794Skarels  */
113966696Spendry void
print_service(action,sep)114029794Skarels print_service(action, sep)
114129794Skarels 	char *action;
114229794Skarels 	struct servtab *sep;
114329794Skarels {
114429794Skarels 	fprintf(stderr,
114529794Skarels 	    "%s: %s proto=%s, wait=%d, user=%s builtin=%x server=%s\n",
114629794Skarels 	    action, sep->se_service, sep->se_proto,
114736603Sbostic 	    sep->se_wait, sep->se_user, (int)sep->se_bi, sep->se_server);
114829794Skarels }
114957769Sandrew 
115057769Sandrew /*
115157769Sandrew  *  Based on TCPMUX.C by Mark K. Lottor November 1988
115257769Sandrew  *  sri-nic::ps:<mkl>tcpmux.c
115357769Sandrew  */
115457769Sandrew 
115557769Sandrew 
115657769Sandrew static int		/* # of characters upto \r,\n or \0 */
getline(fd,buf,len)115757769Sandrew getline(fd, buf, len)
115857769Sandrew 	int fd;
115957769Sandrew 	char *buf;
116057769Sandrew 	int len;
116157769Sandrew {
116257769Sandrew 	int count = 0, n;
116357769Sandrew 
116457769Sandrew 	do {
116557769Sandrew 		n = read(fd, buf, len-count);
116657769Sandrew 		if (n == 0)
116766696Spendry 			return (count);
116857769Sandrew 		if (n < 0)
116957769Sandrew 			return (-1);
117057769Sandrew 		while (--n >= 0) {
117157769Sandrew 			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
117266696Spendry 				return (count);
117357769Sandrew 			count++;
117457769Sandrew 			buf++;
117557769Sandrew 		}
117657769Sandrew 	} while (count < len);
117757769Sandrew 	return (count);
117857769Sandrew }
117957769Sandrew 
118057769Sandrew #define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
118157769Sandrew 
118257769Sandrew #define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
118357769Sandrew 
118457769Sandrew struct servtab *
tcpmux(s)118557769Sandrew tcpmux(s)
118657769Sandrew 	int s;
118757769Sandrew {
118866696Spendry 	struct servtab *sep;
118957769Sandrew 	char service[MAX_SERV_LEN+1];
119057769Sandrew 	int len;
119157769Sandrew 
119257769Sandrew 	/* Get requested service name */
119357769Sandrew 	if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
119466696Spendry 		strwrite(s, "-Error reading service name\r\n");
119566696Spendry 		return (NULL);
119657769Sandrew 	}
119757769Sandrew 	service[len] = '\0';
119857769Sandrew 
119957769Sandrew 	if (debug)
120066696Spendry 		fprintf(stderr, "tcpmux: someone wants %s\n", service);
120157769Sandrew 
120257769Sandrew 	/*
120357769Sandrew 	 * Help is a required command, and lists available services,
120457769Sandrew 	 * one per line.
120557769Sandrew 	 */
120666696Spendry 	if (!strcasecmp(service, "help")) {
120766696Spendry 		for (sep = servtab; sep; sep = sep->se_next) {
120866696Spendry 			if (!ISMUX(sep))
120966696Spendry 				continue;
121066696Spendry 			(void)write(s,sep->se_service,strlen(sep->se_service));
121166696Spendry 			strwrite(s, "\r\n");
121266696Spendry 		}
121366696Spendry 		return (NULL);
121457769Sandrew 	}
121557769Sandrew 
121657769Sandrew 	/* Try matching a service in inetd.conf with the request */
121757769Sandrew 	for (sep = servtab; sep; sep = sep->se_next) {
121866696Spendry 		if (!ISMUX(sep))
121966696Spendry 			continue;
122066696Spendry 		if (!strcasecmp(service, sep->se_service)) {
122166696Spendry 			if (ISMUXPLUS(sep)) {
122266696Spendry 				strwrite(s, "+Go\r\n");
122366696Spendry 			}
122466696Spendry 			return (sep);
122557769Sandrew 		}
122657769Sandrew 	}
122757769Sandrew 	strwrite(s, "-Service not available\r\n");
122866696Spendry 	return (NULL);
122957769Sandrew }
1230