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