xref: /netbsd-src/usr.sbin/inetd/inetd.c (revision ce63d6c20fc4ec8ddc95c84bb229e3c4ecf82b69)
1 /*
2  * Copyright (c) 1983,1991 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 char copyright[] =
36 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
37  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 static char sccsid[] = "@(#)inetd.c	5.30 (Berkeley) 6/3/91";
42 #endif /* not lint */
43 
44 /*
45  * Inetd - Internet super-server
46  *
47  * This program invokes all internet services as needed.
48  * connection-oriented services are invoked each time a
49  * connection is made, by creating a process.  This process
50  * is passed the connection as file descriptor 0 and is
51  * expected to do a getpeername to find out the source host
52  * and port.
53  *
54  * Datagram oriented services are invoked when a datagram
55  * arrives; a process is created and passed a pending message
56  * on file descriptor 0.  Datagram servers may either connect
57  * to their peer, freeing up the original socket for inetd
58  * to receive further messages on, or ``take over the socket'',
59  * processing all arriving datagrams and, eventually, timing
60  * out.	 The first type of server is said to be ``multi-threaded'';
61  * the second type of server ``single-threaded''.
62  *
63  * Inetd uses a configuration file which is read at startup
64  * and, possibly, at some later time in response to a hangup signal.
65  * The configuration file is ``free format'' with fields given in the
66  * order shown below.  Continuation lines for an entry must being with
67  * a space or tab.  All fields must be present in each entry.
68  *
69  *	service name			must be in /etc/services
70  *	socket type			stream/dgram/raw/rdm/seqpacket
71  *	protocol			must be in /etc/protocols
72  *	wait/nowait			single-threaded/multi-threaded
73  *	user				user to run daemon as
74  *	server program			full path name
75  *	server program arguments	maximum of MAXARGS (20)
76  *
77  * Comment lines are indicated by a `#' in column 1.
78  */
79 #include <sys/param.h>
80 #include <sys/stat.h>
81 #include <sys/ioctl.h>
82 #include <sys/socket.h>
83 #include <sys/file.h>
84 #include <sys/wait.h>
85 #include <sys/time.h>
86 #include <sys/resource.h>
87 
88 #include <netinet/in.h>
89 #include <arpa/inet.h>
90 
91 #include <errno.h>
92 #include <signal.h>
93 #include <netdb.h>
94 #include <syslog.h>
95 #include <pwd.h>
96 #include <stdio.h>
97 #include <string.h>
98 #include "pathnames.h"
99 
100 #define	TOOMANY		40		/* don't start more than TOOMANY */
101 #define	CNT_INTVL	60		/* servers in CNT_INTVL sec. */
102 #define	RETRYTIME	(60*10)		/* retry after bind or server fail */
103 
104 #define	SIGBLOCK	(sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
105 
106 extern	int errno;
107 
108 void	config(), reapchild(), retry();
109 char	*index();
110 char	*malloc();
111 
112 int	debug = 0;
113 int	nsock, maxsock;
114 fd_set	allsock;
115 int	options;
116 int	timingout;
117 struct	servent *sp;
118 
119 struct	servtab {
120 	char	*se_service;		/* name of service */
121 	int	se_socktype;		/* type of socket to use */
122 	char	*se_proto;		/* protocol used */
123 	short	se_wait;		/* single threaded server */
124 	short	se_checked;		/* looked at during merge */
125 	char	*se_user;		/* user name to run as */
126 	struct	biltin *se_bi;		/* if built-in, description */
127 	char	*se_server;		/* server program */
128 #define	MAXARGV 20
129 	char	*se_argv[MAXARGV+1];	/* program arguments */
130 	int	se_fd;			/* open descriptor */
131 	struct	sockaddr_in se_ctrladdr;/* bound address */
132 	int	se_count;		/* number started since se_time */
133 	struct	timeval se_time;	/* start of se_count */
134 	struct	servtab *se_next;
135 } *servtab;
136 
137 int echo_stream(), discard_stream(), machtime_stream();
138 int daytime_stream(), chargen_stream();
139 int echo_dg(), discard_dg(), machtime_dg(), daytime_dg(), chargen_dg();
140 
141 struct biltin {
142 	char	*bi_service;		/* internally provided service name */
143 	int	bi_socktype;		/* type of socket supported */
144 	short	bi_fork;		/* 1 if should fork before call */
145 	short	bi_wait;		/* 1 if should wait for child */
146 	int	(*bi_fn)();		/* function which performs it */
147 } biltins[] = {
148 	/* Echo received data */
149 	"echo",		SOCK_STREAM,	1, 0,	echo_stream,
150 	"echo",		SOCK_DGRAM,	0, 0,	echo_dg,
151 
152 	/* Internet /dev/null */
153 	"discard",	SOCK_STREAM,	1, 0,	discard_stream,
154 	"discard",	SOCK_DGRAM,	0, 0,	discard_dg,
155 
156 	/* Return 32 bit time since 1970 */
157 	"time",		SOCK_STREAM,	0, 0,	machtime_stream,
158 	"time",		SOCK_DGRAM,	0, 0,	machtime_dg,
159 
160 	/* Return human-readable time */
161 	"daytime",	SOCK_STREAM,	0, 0,	daytime_stream,
162 	"daytime",	SOCK_DGRAM,	0, 0,	daytime_dg,
163 
164 	/* Familiar character generator */
165 	"chargen",	SOCK_STREAM,	1, 0,	chargen_stream,
166 	"chargen",	SOCK_DGRAM,	0, 0,	chargen_dg,
167 	0
168 };
169 
170 #define NUMINT	(sizeof(intab) / sizeof(struct inent))
171 char	*CONFIG = _PATH_INETDCONF;
172 char	**Argv;
173 char 	*LastArg;
174 
175 main(argc, argv, envp)
176 	int argc;
177 	char *argv[], *envp[];
178 {
179 	extern char *optarg;
180 	extern int optind;
181 	register struct servtab *sep;
182 	register struct passwd *pwd;
183 	register int tmpint;
184 	struct sigvec sv;
185 	int ch, pid, dofork;
186 	char buf[50];
187 
188 	Argv = argv;
189 	if (envp == 0 || *envp == 0)
190 		envp = argv;
191 	while (*envp)
192 		envp++;
193 	LastArg = envp[-1] + strlen(envp[-1]);
194 
195 	while ((ch = getopt(argc, argv, "d")) != EOF)
196 		switch(ch) {
197 		case 'd':
198 			debug = 1;
199 			options |= SO_DEBUG;
200 			break;
201 		case '?':
202 		default:
203 			fprintf(stderr, "usage: inetd [-d]");
204 			exit(1);
205 		}
206 	argc -= optind;
207 	argv += optind;
208 
209 	if (argc > 0)
210 		CONFIG = argv[0];
211 	if (debug == 0)
212 		daemon(0, 0);
213 	openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
214 	bzero((char *)&sv, sizeof(sv));
215 	sv.sv_mask = SIGBLOCK;
216 	sv.sv_handler = retry;
217 	sigvec(SIGALRM, &sv, (struct sigvec *)0);
218 	config();
219 	sv.sv_handler = config;
220 	sigvec(SIGHUP, &sv, (struct sigvec *)0);
221 	sv.sv_handler = reapchild;
222 	sigvec(SIGCHLD, &sv, (struct sigvec *)0);
223 
224 	{
225 		/* space for daemons to overwrite environment for ps */
226 #define	DUMMYSIZE	100
227 		char dummy[DUMMYSIZE];
228 
229 		(void)memset(dummy, 'x', sizeof(DUMMYSIZE) - 1);
230 		dummy[DUMMYSIZE - 1] = '\0';
231 		(void)setenv("inetd_dummy", dummy, 1);
232 	}
233 
234 	for (;;) {
235 	    int n, ctrl;
236 	    fd_set readable;
237 
238 	    if (nsock == 0) {
239 		(void) sigblock(SIGBLOCK);
240 		while (nsock == 0)
241 		    sigpause(0L);
242 		(void) sigsetmask(0L);
243 	    }
244 	    readable = allsock;
245 	    if ((n = select(maxsock + 1, &readable, (fd_set *)0,
246 		(fd_set *)0, (struct timeval *)0)) <= 0) {
247 		    if (n < 0 && errno != EINTR)
248 			syslog(LOG_WARNING, "select: %m\n");
249 		    sleep(1);
250 		    continue;
251 	    }
252 	    for (sep = servtab; n && sep; sep = sep->se_next)
253 	        if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
254 		    n--;
255 		    if (debug)
256 			    fprintf(stderr, "someone wants %s\n",
257 				sep->se_service);
258 		    if (sep->se_socktype == SOCK_STREAM) {
259 			    ctrl = accept(sep->se_fd, (struct sockaddr *)0,
260 				(int *)0);
261 			    if (debug)
262 				    fprintf(stderr, "accept, ctrl %d\n", ctrl);
263 			    if (ctrl < 0) {
264 				    if (errno == EINTR)
265 					    continue;
266 				    syslog(LOG_WARNING, "accept (for %s): %m",
267 					    sep->se_service);
268 				    continue;
269 			    }
270 		    } else
271 			    ctrl = sep->se_fd;
272 		    (void) sigblock(SIGBLOCK);
273 		    pid = 0;
274 		    dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
275 		    if (dofork) {
276 			    if (sep->se_count++ == 0)
277 				(void)gettimeofday(&sep->se_time,
278 				    (struct timezone *)0);
279 			    else if (sep->se_count >= TOOMANY) {
280 				struct timeval now;
281 
282 				(void)gettimeofday(&now, (struct timezone *)0);
283 				if (now.tv_sec - sep->se_time.tv_sec >
284 				    CNT_INTVL) {
285 					sep->se_time = now;
286 					sep->se_count = 1;
287 				} else {
288 					syslog(LOG_ERR,
289 			"%s/%s server failing (looping), service terminated\n",
290 					    sep->se_service, sep->se_proto);
291 					FD_CLR(sep->se_fd, &allsock);
292 					(void) close(sep->se_fd);
293 					sep->se_fd = -1;
294 					sep->se_count = 0;
295 					nsock--;
296 					if (!timingout) {
297 						timingout = 1;
298 						alarm(RETRYTIME);
299 					}
300 				}
301 			    }
302 			    pid = fork();
303 		    }
304 		    if (pid < 0) {
305 			    syslog(LOG_ERR, "fork: %m");
306 			    if (sep->se_socktype == SOCK_STREAM)
307 				    close(ctrl);
308 			    sigsetmask(0L);
309 			    sleep(1);
310 			    continue;
311 		    }
312 		    if (pid && sep->se_wait) {
313 			    sep->se_wait = pid;
314 			    if (sep->se_fd >= 0) {
315 				FD_CLR(sep->se_fd, &allsock);
316 			        nsock--;
317 			    }
318 		    }
319 		    sigsetmask(0L);
320 		    if (pid == 0) {
321 			    if (debug && dofork)
322 				setsid();
323 			    if (dofork)
324 				for (tmpint = maxsock; --tmpint > 2; )
325 					if (tmpint != ctrl)
326 						close(tmpint);
327 			    if (sep->se_bi)
328 				(*sep->se_bi->bi_fn)(ctrl, sep);
329 			    else {
330 				if (debug)
331 					fprintf(stderr, "%d execl %s\n",
332 					    getpid(), sep->se_server);
333 				dup2(ctrl, 0);
334 				close(ctrl);
335 				dup2(0, 1);
336 				dup2(0, 2);
337 				if ((pwd = getpwnam(sep->se_user)) == NULL) {
338 					syslog(LOG_ERR,
339 					    "getpwnam: %s: No such user",
340 					    sep->se_user);
341 					if (sep->se_socktype != SOCK_STREAM)
342 						recv(0, buf, sizeof (buf), 0);
343 					_exit(1);
344 				}
345 				if (pwd->pw_uid) {
346 					(void) setgid((gid_t)pwd->pw_gid);
347 					initgroups(pwd->pw_name, pwd->pw_gid);
348 					(void) setuid((uid_t)pwd->pw_uid);
349 				}
350 				execv(sep->se_server, sep->se_argv);
351 				if (sep->se_socktype != SOCK_STREAM)
352 					recv(0, buf, sizeof (buf), 0);
353 				syslog(LOG_ERR, "execv %s: %m", sep->se_server);
354 				_exit(1);
355 			    }
356 		    }
357 		    if (sep->se_socktype == SOCK_STREAM)
358 			    close(ctrl);
359 		}
360 	}
361 }
362 
363 void
364 reapchild()
365 {
366 	int status;
367 	int pid;
368 	register struct servtab *sep;
369 
370 	for (;;) {
371 		pid = wait3(&status, WNOHANG, (struct rusage *)0);
372 		if (pid <= 0)
373 			break;
374 		if (debug)
375 			fprintf(stderr, "%d reaped\n", pid);
376 		for (sep = servtab; sep; sep = sep->se_next)
377 			if (sep->se_wait == pid) {
378 				if (status)
379 					syslog(LOG_WARNING,
380 					    "%s: exit status 0x%x",
381 					    sep->se_server, status);
382 				if (debug)
383 					fprintf(stderr, "restored %s, fd %d\n",
384 					    sep->se_service, sep->se_fd);
385 				FD_SET(sep->se_fd, &allsock);
386 				nsock++;
387 				sep->se_wait = 1;
388 			}
389 	}
390 }
391 
392 void
393 config()
394 {
395 	register struct servtab *sep, *cp, **sepp;
396 	struct servtab *getconfigent(), *enter();
397 	long omask;
398 
399 	if (!setconfig()) {
400 		syslog(LOG_ERR, "%s: %m", CONFIG);
401 		return;
402 	}
403 	for (sep = servtab; sep; sep = sep->se_next)
404 		sep->se_checked = 0;
405 	while (cp = getconfigent()) {
406 		for (sep = servtab; sep; sep = sep->se_next)
407 			if (strcmp(sep->se_service, cp->se_service) == 0 &&
408 			    strcmp(sep->se_proto, cp->se_proto) == 0)
409 				break;
410 		if (sep != 0) {
411 			int i;
412 
413 			omask = sigblock(SIGBLOCK);
414 			/*
415 			 * sep->se_wait may be holding the pid of a daemon
416 			 * that we're waiting for.  If so, don't overwrite
417 			 * it unless the config file explicitly says don't
418 			 * wait.
419 			 */
420 			if (cp->se_bi == 0 &&
421 			    (sep->se_wait == 1 || cp->se_wait == 0))
422 				sep->se_wait = cp->se_wait;
423 #define SWAP(a, b) { char *c = a; a = b; b = c; }
424 			if (cp->se_user)
425 				SWAP(sep->se_user, cp->se_user);
426 			if (cp->se_server)
427 				SWAP(sep->se_server, cp->se_server);
428 			for (i = 0; i < MAXARGV; i++)
429 				SWAP(sep->se_argv[i], cp->se_argv[i]);
430 			sigsetmask(omask);
431 			freeconfig(cp);
432 			if (debug)
433 				print_service("REDO", sep);
434 		} else {
435 			sep = enter(cp);
436 			if (debug)
437 				print_service("ADD ", sep);
438 		}
439 		sep->se_checked = 1;
440 		sp = getservbyname(sep->se_service, sep->se_proto);
441 		if (sp == 0) {
442 			syslog(LOG_ERR, "%s/%s: unknown service",
443 			    sep->se_service, sep->se_proto);
444 			if (sep->se_fd != -1)
445 				(void) close(sep->se_fd);
446 			sep->se_fd = -1;
447 			continue;
448 		}
449 		if (sp->s_port != sep->se_ctrladdr.sin_port) {
450 			sep->se_ctrladdr.sin_port = sp->s_port;
451 			if (sep->se_fd != -1)
452 				(void) close(sep->se_fd);
453 			sep->se_fd = -1;
454 		}
455 		if (sep->se_fd == -1)
456 			setup(sep);
457 	}
458 	endconfig();
459 	/*
460 	 * Purge anything not looked at above.
461 	 */
462 	omask = sigblock(SIGBLOCK);
463 	sepp = &servtab;
464 	while (sep = *sepp) {
465 		if (sep->se_checked) {
466 			sepp = &sep->se_next;
467 			continue;
468 		}
469 		*sepp = sep->se_next;
470 		if (sep->se_fd != -1) {
471 			FD_CLR(sep->se_fd, &allsock);
472 			nsock--;
473 			(void) close(sep->se_fd);
474 		}
475 		if (debug)
476 			print_service("FREE", sep);
477 		freeconfig(sep);
478 		free((char *)sep);
479 	}
480 	(void) sigsetmask(omask);
481 }
482 
483 void
484 retry()
485 {
486 	register struct servtab *sep;
487 
488 	timingout = 0;
489 	for (sep = servtab; sep; sep = sep->se_next)
490 		if (sep->se_fd == -1)
491 			setup(sep);
492 }
493 
494 setup(sep)
495 	register struct servtab *sep;
496 {
497 	int on = 1;
498 
499 	if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
500 		syslog(LOG_ERR, "%s/%s: socket: %m",
501 		    sep->se_service, sep->se_proto);
502 		return;
503 	}
504 #define	turnon(fd, opt) \
505 setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
506 	if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
507 	    turnon(sep->se_fd, SO_DEBUG) < 0)
508 		syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
509 	if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
510 		syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
511 #undef turnon
512 	if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
513 	    sizeof (sep->se_ctrladdr)) < 0) {
514 		syslog(LOG_ERR, "%s/%s: bind: %m",
515 		    sep->se_service, sep->se_proto);
516 		(void) close(sep->se_fd);
517 		sep->se_fd = -1;
518 		if (!timingout) {
519 			timingout = 1;
520 			alarm(RETRYTIME);
521 		}
522 		return;
523 	}
524 	if (sep->se_socktype == SOCK_STREAM)
525 		listen(sep->se_fd, 10);
526 	FD_SET(sep->se_fd, &allsock);
527 	nsock++;
528 	if (sep->se_fd > maxsock)
529 		maxsock = sep->se_fd;
530 }
531 
532 struct servtab *
533 enter(cp)
534 	struct servtab *cp;
535 {
536 	register struct servtab *sep;
537 	long omask;
538 
539 	sep = (struct servtab *)malloc(sizeof (*sep));
540 	if (sep == (struct servtab *)0) {
541 		syslog(LOG_ERR, "Out of memory.");
542 		exit(-1);
543 	}
544 	*sep = *cp;
545 	sep->se_fd = -1;
546 	omask = sigblock(SIGBLOCK);
547 	sep->se_next = servtab;
548 	servtab = sep;
549 	sigsetmask(omask);
550 	return (sep);
551 }
552 
553 FILE	*fconfig = NULL;
554 struct	servtab serv;
555 char	line[256];
556 char	*skip(), *nextline();
557 
558 setconfig()
559 {
560 
561 	if (fconfig != NULL) {
562 		fseek(fconfig, 0L, L_SET);
563 		return (1);
564 	}
565 	fconfig = fopen(CONFIG, "r");
566 	return (fconfig != NULL);
567 }
568 
569 endconfig()
570 {
571 	if (fconfig) {
572 		(void) fclose(fconfig);
573 		fconfig = NULL;
574 	}
575 }
576 
577 struct servtab *
578 getconfigent()
579 {
580 	register struct servtab *sep = &serv;
581 	int argc;
582 	char *cp, *arg, *newstr();
583 
584 more:
585 	while ((cp = nextline(fconfig)) && *cp == '#')
586 		;
587 	if (cp == NULL)
588 		return ((struct servtab *)0);
589 	sep->se_service = newstr(skip(&cp));
590 	arg = skip(&cp);
591 	if (strcmp(arg, "stream") == 0)
592 		sep->se_socktype = SOCK_STREAM;
593 	else if (strcmp(arg, "dgram") == 0)
594 		sep->se_socktype = SOCK_DGRAM;
595 	else if (strcmp(arg, "rdm") == 0)
596 		sep->se_socktype = SOCK_RDM;
597 	else if (strcmp(arg, "seqpacket") == 0)
598 		sep->se_socktype = SOCK_SEQPACKET;
599 	else if (strcmp(arg, "raw") == 0)
600 		sep->se_socktype = SOCK_RAW;
601 	else
602 		sep->se_socktype = -1;
603 	sep->se_proto = newstr(skip(&cp));
604 	arg = skip(&cp);
605 	sep->se_wait = strcmp(arg, "wait") == 0;
606 	sep->se_user = newstr(skip(&cp));
607 	sep->se_server = newstr(skip(&cp));
608 	if (strcmp(sep->se_server, "internal") == 0) {
609 		register struct biltin *bi;
610 
611 		for (bi = biltins; bi->bi_service; bi++)
612 			if (bi->bi_socktype == sep->se_socktype &&
613 			    strcmp(bi->bi_service, sep->se_service) == 0)
614 				break;
615 		if (bi->bi_service == 0) {
616 			syslog(LOG_ERR, "internal service %s unknown\n",
617 				sep->se_service);
618 			goto more;
619 		}
620 		sep->se_bi = bi;
621 		sep->se_wait = bi->bi_wait;
622 	} else
623 		sep->se_bi = NULL;
624 	argc = 0;
625 	for (arg = skip(&cp); cp; arg = skip(&cp))
626 		if (argc < MAXARGV)
627 			sep->se_argv[argc++] = newstr(arg);
628 	while (argc <= MAXARGV)
629 		sep->se_argv[argc++] = NULL;
630 	return (sep);
631 }
632 
633 freeconfig(cp)
634 	register struct servtab *cp;
635 {
636 	int i;
637 
638 	if (cp->se_service)
639 		free(cp->se_service);
640 	if (cp->se_proto)
641 		free(cp->se_proto);
642 	if (cp->se_user)
643 		free(cp->se_user);
644 	if (cp->se_server)
645 		free(cp->se_server);
646 	for (i = 0; i < MAXARGV; i++)
647 		if (cp->se_argv[i])
648 			free(cp->se_argv[i]);
649 }
650 
651 char *
652 skip(cpp)
653 	char **cpp;
654 {
655 	register char *cp = *cpp;
656 	char *start;
657 
658 again:
659 	while (*cp == ' ' || *cp == '\t')
660 		cp++;
661 	if (*cp == '\0') {
662 		int c;
663 
664 		c = getc(fconfig);
665 		(void) ungetc(c, fconfig);
666 		if (c == ' ' || c == '\t')
667 			if (cp = nextline(fconfig))
668 				goto again;
669 		*cpp = (char *)0;
670 		return ((char *)0);
671 	}
672 	start = cp;
673 	while (*cp && *cp != ' ' && *cp != '\t')
674 		cp++;
675 	if (*cp != '\0')
676 		*cp++ = '\0';
677 	*cpp = cp;
678 	return (start);
679 }
680 
681 char *
682 nextline(fd)
683 	FILE *fd;
684 {
685 	char *cp;
686 
687 	if (fgets(line, sizeof (line), fd) == NULL)
688 		return ((char *)0);
689 	cp = index(line, '\n');
690 	if (cp)
691 		*cp = '\0';
692 	return (line);
693 }
694 
695 char *
696 newstr(cp)
697 	char *cp;
698 {
699 	if (cp = strdup(cp ? cp : ""))
700 		return(cp);
701 	syslog(LOG_ERR, "strdup: %m");
702 	exit(-1);
703 }
704 
705 setproctitle(a, s)
706 	char *a;
707 	int s;
708 {
709 	int size;
710 	register char *cp;
711 	struct sockaddr_in sin;
712 	char buf[80];
713 
714 	cp = Argv[0];
715 	size = sizeof(sin);
716 	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
717 		(void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr));
718 	else
719 		(void) sprintf(buf, "-%s", a);
720 	strncpy(cp, buf, LastArg - cp);
721 	cp += strlen(cp);
722 	while (cp < LastArg)
723 		*cp++ = ' ';
724 }
725 
726 /*
727  * Internet services provided internally by inetd:
728  */
729 #define	BUFSIZE	8192
730 
731 /* ARGSUSED */
732 echo_stream(s, sep)		/* Echo service -- echo data back */
733 	int s;
734 	struct servtab *sep;
735 {
736 	char buffer[BUFSIZE];
737 	int i;
738 
739 	setproctitle(sep->se_service, s);
740 	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
741 	    write(s, buffer, i) > 0)
742 		;
743 	exit(0);
744 }
745 
746 /* ARGSUSED */
747 echo_dg(s, sep)			/* Echo service -- echo data back */
748 	int s;
749 	struct servtab *sep;
750 {
751 	char buffer[BUFSIZE];
752 	int i, size;
753 	struct sockaddr sa;
754 
755 	size = sizeof(sa);
756 	if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0)
757 		return;
758 	(void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
759 }
760 
761 /* ARGSUSED */
762 discard_stream(s, sep)		/* Discard service -- ignore data */
763 	int s;
764 	struct servtab *sep;
765 {
766 	int ret;
767 	char buffer[BUFSIZE];
768 
769 	setproctitle(sep->se_service, s);
770 	while (1) {
771 		while ((ret = read(s, buffer, sizeof(buffer))) > 0)
772 			;
773 		if (ret == 0 || errno != EINTR)
774 			break;
775 	}
776 	exit(0);
777 }
778 
779 /* ARGSUSED */
780 discard_dg(s, sep)		/* Discard service -- ignore data */
781 	int s;
782 	struct servtab *sep;
783 {
784 	char buffer[BUFSIZE];
785 
786 	(void) read(s, buffer, sizeof(buffer));
787 }
788 
789 #include <ctype.h>
790 #define LINESIZ 72
791 char ring[128];
792 char *endring;
793 
794 initring()
795 {
796 	register int i;
797 
798 	endring = ring;
799 
800 	for (i = 0; i <= 128; ++i)
801 		if (isprint(i))
802 			*endring++ = i;
803 }
804 
805 /* ARGSUSED */
806 chargen_stream(s, sep)		/* Character generator */
807 	int s;
808 	struct servtab *sep;
809 {
810 	register char *rs;
811 	int len;
812 	char text[LINESIZ+2];
813 
814 	setproctitle(sep->se_service, s);
815 
816 	if (!endring) {
817 		initring();
818 		rs = ring;
819 	}
820 
821 	text[LINESIZ] = '\r';
822 	text[LINESIZ + 1] = '\n';
823 	for (rs = ring;;) {
824 		if ((len = endring - rs) >= LINESIZ)
825 			bcopy(rs, text, LINESIZ);
826 		else {
827 			bcopy(rs, text, len);
828 			bcopy(ring, text + len, LINESIZ - len);
829 		}
830 		if (++rs == endring)
831 			rs = ring;
832 		if (write(s, text, sizeof(text)) != sizeof(text))
833 			break;
834 	}
835 	exit(0);
836 }
837 
838 /* ARGSUSED */
839 chargen_dg(s, sep)		/* Character generator */
840 	int s;
841 	struct servtab *sep;
842 {
843 	struct sockaddr sa;
844 	static char *rs;
845 	int len, size;
846 	char text[LINESIZ+2];
847 
848 	if (endring == 0) {
849 		initring();
850 		rs = ring;
851 	}
852 
853 	size = sizeof(sa);
854 	if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
855 		return;
856 
857 	if ((len = endring - rs) >= LINESIZ)
858 		bcopy(rs, text, LINESIZ);
859 	else {
860 		bcopy(rs, text, len);
861 		bcopy(ring, text + len, LINESIZ - len);
862 	}
863 	if (++rs == endring)
864 		rs = ring;
865 	text[LINESIZ] = '\r';
866 	text[LINESIZ + 1] = '\n';
867 	(void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
868 }
869 
870 /*
871  * Return a machine readable date and time, in the form of the
872  * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
873  * returns the number of seconds since midnight, Jan 1, 1970,
874  * we must add 2208988800 seconds to this figure to make up for
875  * some seventy years Bell Labs was asleep.
876  */
877 
878 long
879 machtime()
880 {
881 	struct timeval tv;
882 
883 	if (gettimeofday(&tv, (struct timezone *)0) < 0) {
884 		fprintf(stderr, "Unable to get time of day\n");
885 		return (0L);
886 	}
887 	return (htonl((long)tv.tv_sec + 2208988800));
888 }
889 
890 /* ARGSUSED */
891 machtime_stream(s, sep)
892 	int s;
893 	struct servtab *sep;
894 {
895 	long result;
896 
897 	result = machtime();
898 	(void) write(s, (char *) &result, sizeof(result));
899 }
900 
901 /* ARGSUSED */
902 machtime_dg(s, sep)
903 	int s;
904 	struct servtab *sep;
905 {
906 	long result;
907 	struct sockaddr sa;
908 	int size;
909 
910 	size = sizeof(sa);
911 	if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0)
912 		return;
913 	result = machtime();
914 	(void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
915 }
916 
917 /* ARGSUSED */
918 daytime_stream(s, sep)		/* Return human-readable time of day */
919 	int s;
920 	struct servtab *sep;
921 {
922 	char buffer[256];
923 	time_t time(), clock;
924 	char *ctime();
925 
926 	clock = time((time_t *) 0);
927 
928 	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
929 	(void) write(s, buffer, strlen(buffer));
930 }
931 
932 /* ARGSUSED */
933 daytime_dg(s, sep)		/* Return human-readable time of day */
934 	int s;
935 	struct servtab *sep;
936 {
937 	char buffer[256];
938 	time_t time(), clock;
939 	struct sockaddr sa;
940 	int size;
941 	char *ctime();
942 
943 	clock = time((time_t *) 0);
944 
945 	size = sizeof(sa);
946 	if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
947 		return;
948 	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
949 	(void) sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa));
950 }
951 
952 /*
953  * print_service:
954  *	Dump relevant information to stderr
955  */
956 print_service(action, sep)
957 	char *action;
958 	struct servtab *sep;
959 {
960 	fprintf(stderr,
961 	    "%s: %s proto=%s, wait=%d, user=%s builtin=%x server=%s\n",
962 	    action, sep->se_service, sep->se_proto,
963 	    sep->se_wait, sep->se_user, (int)sep->se_bi, sep->se_server);
964 }
965