blob: 1f2b339f03b1758c89e7a76a03edc7fdf968a109 [file] [log] [blame]
Glenn L McGrath06e95652003-02-09 06:51:14 +00001/*
2 * Copyright (c) 1983,1991 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * David A. Holland.
7 *
8 * Busybox port by Vladimir Oleynik (C) 2001-2003 <dzo@simtreas.ru>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 */
25
26/*
27 * Inetd - Internet super-server
28 *
29 * This program invokes all internet services as needed.
30 * connection-oriented services are invoked each time a
31 * connection is made, by creating a process. This process
32 * is passed the connection as file descriptor 0 and is
33 * expected to do a getpeername to find out the source host
34 * and port.
35 *
36 * Datagram oriented services are invoked when a datagram
37 * arrives; a process is created and passed a pending message
38 * on file descriptor 0. Datagram servers may either connect
39 * to their peer, freeing up the original socket for inetd
40 * to receive further messages on, or ``take over the socket'',
41 * processing all arriving datagrams and, eventually, timing
42 * out. The first type of server is said to be ``multi-threaded'';
43 * the second type of server ``single-threaded''.
44 *
45 * Inetd uses a configuration file which is read at startup
46 * and, possibly, at some later time in response to a hangup signal.
47 * The configuration file is ``free format'' with fields given in the
48 * order shown below. Continuation lines for an entry must being with
49 * a space or tab. All fields must be present in each entry.
50 *
51 * service name must be in /etc/services
52 * socket type stream/dgram/raw/rdm/seqpacket
53 * protocol must be in /etc/protocols
54 * wait/nowait[.max] single-threaded/multi-threaded, max #
55 * user[.group] user/group to run daemon as
56 * server program full path name
57 * server program arguments maximum of MAXARGS (20)
58 *
59 * RPC services unsuported
60 *
61 * Comment lines are indicated by a `#' in column 1.
62 */
63
64/*
65 * Here's the scoop concerning the user.group feature:
66 *
67 * 1) No group listed.
68 *
69 * a) for root: NO setuid() or setgid() is done
70 *
71 * b) nonroot: setuid()
72 * setgid(primary group as found in passwd)
73 * initgroups(name, primary group)
74 *
75 * 2) set-group-option on.
76 *
77 * a) for root: NO setuid()
78 * setgid(specified group)
79 * setgroups(1, specified group)
80 *
81 * b) nonroot: setuid()
82 * setgid(specified group)
83 * initgroups(name, specified group)
84 *
85 * All supplementary groups are discarded at startup in case inetd was
86 * run manually.
87 */
88
89#define __USE_BSD_SIGNAL
90
91#include "busybox.h"
92
93#include <sys/param.h>
94#include <sys/stat.h>
95#include <sys/ioctl.h>
96#include <sys/socket.h>
97#include <sys/un.h>
98#include <sys/file.h>
99#include <sys/wait.h>
100#include <sys/time.h>
101#include <sys/resource.h>
102
103#ifndef __linux__
104#ifndef RLIMIT_NOFILE
105#define RLIMIT_NOFILE RLIMIT_OFILE
106#endif
107#endif
108
109#include <sys/param.h>
110#include <sys/stat.h>
111#include <sys/ioctl.h>
112#include <sys/socket.h>
113#include <sys/file.h>
114#include <sys/wait.h>
115#include <sys/time.h>
116#include <sys/resource.h>
117
118#include <netinet/in.h>
119#include <netinet/ip.h>
120#include <arpa/inet.h>
121
122#include <errno.h>
123#include <signal.h>
124#include <netdb.h>
125#include <syslog.h>
Glenn L McGrath06e95652003-02-09 06:51:14 +0000126#include <stdio.h>
127#include <stdlib.h>
128#include <string.h>
129#include <getopt.h>
130#include <unistd.h>
131#include <stdarg.h>
Manuel Novoa III c2843562003-02-11 07:06:06 +0000132#include <time.h>
Glenn L McGrath06e95652003-02-09 06:51:14 +0000133
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000134#ifndef OPEN_MAX
135#define OPEN_MAX 64
136#endif
137
Glenn L McGrath06e95652003-02-09 06:51:14 +0000138#define _PATH_INETDCONF "/etc/inetd.conf"
139#define _PATH_INETDPID "/var/run/inetd.pid"
140
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000141#define TOOMANY 40 /* don't start more than TOOMANY */
142#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */
143#define RETRYTIME (60*10) /* retry after bind or server fail */
144#define MAXARGV 20
Glenn L McGrath06e95652003-02-09 06:51:14 +0000145
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000146#define se_ctrladdr se_un.se_un_ctrladdr
147#define se_ctrladdr_in se_un.se_un_ctrladdr_in
148#define se_ctrladdr_un se_un.se_un_ctrladdr_un
Glenn L McGrath06e95652003-02-09 06:51:14 +0000149
150/* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000151#define FD_MARGIN (8)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000152
Glenn L McGrathb1207b32003-02-10 22:31:09 +0000153/* Check unsupporting builtin */
Glenn L McGrathc3b134f2004-01-17 01:26:53 +0000154#if defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO || \
155 defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD || \
156 defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME || \
157 defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME || \
158 defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
159# define INETD_FEATURE_ENABLED
Glenn L McGrathb1207b32003-02-10 22:31:09 +0000160#endif
Glenn L McGrath06e95652003-02-09 06:51:14 +0000161
162static struct servtab {
163 char *se_service; /* name of service */
164 int se_socktype; /* type of socket to use */
165 int se_family; /* address family */
166 char *se_proto; /* protocol used */
167 short se_wait; /* single threaded server */
168 short se_checked; /* looked at during merge */
169 char *se_user; /* user name to run as */
170 char *se_group; /* group name to run as */
Glenn L McGrathc3b134f2004-01-17 01:26:53 +0000171#ifdef INETD_FEATURE_ENABLED
Glenn L McGrath06e95652003-02-09 06:51:14 +0000172 const struct biltin *se_bi; /* if built-in, description */
173#endif
174 char *se_server; /* server program */
Glenn L McGrath06e95652003-02-09 06:51:14 +0000175 char *se_argv[MAXARGV+1]; /* program arguments */
176 int se_fd; /* open descriptor */
177 union {
178 struct sockaddr se_un_ctrladdr;
179 struct sockaddr_in se_un_ctrladdr_in;
180 struct sockaddr_un se_un_ctrladdr_un;
181 } se_un; /* bound address */
Glenn L McGrath06e95652003-02-09 06:51:14 +0000182 int se_ctrladdr_size;
183 int se_max; /* max # of instances of this service */
184 int se_count; /* number started since se_time */
185 struct timeval se_time; /* start of se_count */
186 struct servtab *se_next;
187} *servtab;
188
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000189#ifdef INETD_FEATURE_ENABLED
190struct biltin {
191 const char *bi_service; /* internally provided service name */
192 int bi_socktype; /* type of socket supported */
193 short bi_fork; /* 1 if should fork before call */
194 short bi_wait; /* 1 if should wait for child */
195 void (*bi_fn)(int, struct servtab *); /* fn which performs it */
196};
Glenn L McGrath06e95652003-02-09 06:51:14 +0000197
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000198 /* Echo received data */
Glenn L McGrath06e95652003-02-09 06:51:14 +0000199#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
Glenn L McGrath06e95652003-02-09 06:51:14 +0000200static void echo_stream(int, struct servtab *);
201static void echo_dg(int, struct servtab *);
202#endif
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000203 /* Internet /dev/null */
Glenn L McGrath06e95652003-02-09 06:51:14 +0000204#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
Glenn L McGrath06e95652003-02-09 06:51:14 +0000205static void discard_stream(int, struct servtab *);
206static void discard_dg(int, struct servtab *);
207#endif
208 /* Return 32 bit time since 1900 */
209#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
Glenn L McGrath06e95652003-02-09 06:51:14 +0000210static void machtime_stream(int, struct servtab *);
211static void machtime_dg(int, struct servtab *);
212#endif
213 /* Return human-readable time */
214#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
Glenn L McGrath06e95652003-02-09 06:51:14 +0000215static void daytime_stream(int, struct servtab *);
216static void daytime_dg(int, struct servtab *);
217#endif
218 /* Familiar character generator */
219#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
Glenn L McGrath06e95652003-02-09 06:51:14 +0000220static void chargen_stream(int, struct servtab *);
221static void chargen_dg(int, struct servtab *);
222#endif
223
Glenn L McGrath06e95652003-02-09 06:51:14 +0000224static const struct biltin biltins[] = {
225#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
226 /* Echo received data */
227 { "echo", SOCK_STREAM, 1, 0, echo_stream, },
228 { "echo", SOCK_DGRAM, 0, 0, echo_dg, },
229#endif
230#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
231 /* Internet /dev/null */
232 { "discard", SOCK_STREAM, 1, 0, discard_stream, },
233 { "discard", SOCK_DGRAM, 0, 0, discard_dg, },
234#endif
235#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
236 /* Return 32 bit time since 1900 */
237 { "time", SOCK_STREAM, 0, 0, machtime_stream, },
238 { "time", SOCK_DGRAM, 0, 0, machtime_dg, },
239#endif
240#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
241 /* Return human-readable time */
242 { "daytime", SOCK_STREAM, 0, 0, daytime_stream, },
243 { "daytime", SOCK_DGRAM, 0, 0, daytime_dg, },
244#endif
245#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
246 /* Familiar character generator */
247 { "chargen", SOCK_STREAM, 1, 0, chargen_stream, },
248 { "chargen", SOCK_DGRAM, 0, 0, chargen_dg, },
249#endif
250 { NULL, 0, 0, 0, NULL }
251};
Glenn L McGrathc3b134f2004-01-17 01:26:53 +0000252#endif /* INETD_FEATURE_ENABLED */
Glenn L McGrath06e95652003-02-09 06:51:14 +0000253
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000254#ifdef RLIMIT_NOFILE
255static struct rlimit rlim_ofile;
256#endif
257
258/* Length of socket listen queue. Should be per-service probably. */
259static int global_queuelen = 128;
260
261static FILE *fconfig;
262static sigset_t blockmask;
263static sigset_t emptymask;
264static fd_set allsock;
265static int nsock;
266static int maxsock;
267static int timingout;
268static int rlim_ofile_cur = OPEN_MAX;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000269static const char *CONFIG = _PATH_INETDCONF;
270
Glenn L McGrath06e95652003-02-09 06:51:14 +0000271static void
272syslog_err_and_discard_dg(int se_socktype, const char *msg, ...)
273 __attribute__ ((noreturn, format (printf, 2, 3)));
274
275static void
276syslog_err_and_discard_dg(int se_socktype, const char *msg, ...)
277{
278 char buf[50];
279 va_list p;
280
281 va_start(p, msg);
282 vsyslog(LOG_ERR, msg, p);
283 if (se_socktype != SOCK_STREAM)
284 recv(0, buf, sizeof (buf), 0);
285 _exit(1);
286}
287
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000288static char *skip(char **cpp)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000289{
290 char *cp = *cpp;
291 char *start;
292
Glenn L McGrath53766c42004-01-18 08:58:06 +0000293 if ((cpp == NULL) || (*cpp == NULL) || (**cpp == 0)) {
294 return (NULL);
295 }
Glenn L McGrath06e95652003-02-09 06:51:14 +0000296
297again:
298 while (*cp == ' ' || *cp == '\t')
299 cp++;
300 if (*cp == '\0') {
301 int c;
302
303 c = getc(fconfig);
304 (void) ungetc(c, fconfig);
305 if (c == ' ' || c == '\t')
Glenn L McGrath2faf3062004-01-17 00:34:31 +0000306 cp = bb_get_chomped_line_from_file(fconfig);
307 if (cp != NULL)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000308 goto again;
309 *cpp = NULL;
310 return NULL;
311 }
312 start = cp;
313 while (*cp && *cp != ' ' && *cp != '\t')
314 cp++;
315 if (*cp != '\0')
316 *cp++ = '\0';
317 *cpp = cp;
318 return (start);
319}
320
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000321static char *newstr(char *cp)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000322{
323 cp = strdup(cp ? cp : "");
324 if (cp)
325 return(cp);
326
327 syslog_err_and_discard_dg(SOCK_STREAM, "strdup: %m");
328}
329
330
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000331static struct servtab *getconfigent(void)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000332{
333 static struct servtab serv;
334 struct servtab *sep = &serv;
335 int argc;
Glenn L McGrath53766c42004-01-18 08:58:06 +0000336 char *cp = NULL;
337 char *arg;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000338more:
Glenn L McGrath53766c42004-01-18 08:58:06 +0000339 do {
340 if (feof(fconfig)) {
341 return ((struct servtab *)0);
342 }
343 free(cp);
344 cp = bb_get_chomped_line_from_file(fconfig);
345 } while ((cp == NULL) || (*cp == '#'));
346
Glenn L McGrath06e95652003-02-09 06:51:14 +0000347 memset((char *)sep, 0, sizeof *sep);
348 sep->se_service = newstr(skip(&cp));
349 arg = skip(&cp);
Glenn L McGrath53766c42004-01-18 08:58:06 +0000350 if (arg == NULL) {
Glenn L McGrath06e95652003-02-09 06:51:14 +0000351 goto more;
Glenn L McGrath53766c42004-01-18 08:58:06 +0000352 }
Glenn L McGrath06e95652003-02-09 06:51:14 +0000353
354 if (strcmp(arg, "stream") == 0)
355 sep->se_socktype = SOCK_STREAM;
356 else if (strcmp(arg, "dgram") == 0)
357 sep->se_socktype = SOCK_DGRAM;
358 else if (strcmp(arg, "rdm") == 0)
359 sep->se_socktype = SOCK_RDM;
360 else if (strcmp(arg, "seqpacket") == 0)
361 sep->se_socktype = SOCK_SEQPACKET;
362 else if (strcmp(arg, "raw") == 0)
363 sep->se_socktype = SOCK_RAW;
364 else
365 sep->se_socktype = -1;
366
367 sep->se_proto = newstr(skip(&cp));
368 if (strcmp(sep->se_proto, "unix") == 0) {
369 sep->se_family = AF_UNIX;
370 } else {
371 sep->se_family = AF_INET;
372 if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
373 syslog(LOG_ERR, "%s: rpc services not suported",
374 sep->se_service);
375 goto more;
376 }
377 }
378 arg = skip(&cp);
Glenn L McGrath53766c42004-01-18 08:58:06 +0000379 if (arg == NULL) {
Glenn L McGrath06e95652003-02-09 06:51:14 +0000380 goto more;
Glenn L McGrath53766c42004-01-18 08:58:06 +0000381 }
Glenn L McGrath06e95652003-02-09 06:51:14 +0000382 {
383 char *s = strchr(arg, '.');
384 if (s) {
385 *s++ = '\0';
386 sep->se_max = atoi(s);
387 } else
388 sep->se_max = TOOMANY;
389 }
390 sep->se_wait = strcmp(arg, "wait") == 0;
391 sep->se_user = newstr(skip(&cp));
392 sep->se_group = strchr(sep->se_user, '.');
393 if (sep->se_group) {
394 *sep->se_group++ = '\0';
395 }
396 sep->se_server = newstr(skip(&cp));
397 if (strcmp(sep->se_server, "internal") == 0) {
Glenn L McGrathc3b134f2004-01-17 01:26:53 +0000398#ifdef INETD_FEATURE_ENABLED
Glenn L McGrath06e95652003-02-09 06:51:14 +0000399 const struct biltin *bi;
400
Glenn L McGrath53766c42004-01-18 08:58:06 +0000401 for (bi = biltins; bi->bi_service; bi++) {
402 if ((bi->bi_socktype == sep->se_socktype) &&
403 (strcmp(bi->bi_service, sep->se_service) == 0)) {
Glenn L McGrath06e95652003-02-09 06:51:14 +0000404 break;
Glenn L McGrath53766c42004-01-18 08:58:06 +0000405 }
406 }
Glenn L McGrath06e95652003-02-09 06:51:14 +0000407 if (bi->bi_service == 0) {
408 syslog(LOG_ERR, "internal service %s unknown",
409 sep->se_service);
410 goto more;
411 }
412 sep->se_bi = bi;
413 sep->se_wait = bi->bi_wait;
414#else
415 syslog(LOG_ERR, "internal service %s unknown",
416 sep->se_service);
417 goto more;
418#endif
Glenn L McGrath06e95652003-02-09 06:51:14 +0000419 }
Glenn L McGrath53766c42004-01-18 08:58:06 +0000420#ifdef INETD_FEATURE_ENABLED
421 else {
422 sep->se_bi = NULL;
423 }
424#endif
425 argc = 0;
426 for (arg = skip(&cp); cp && arg; arg = skip(&cp)) {
427 if (argc < MAXARGV) {
428 sep->se_argv[argc++] = newstr(arg);
429 }
430 }
431 while (argc <= MAXARGV) {
Glenn L McGrath06e95652003-02-09 06:51:14 +0000432 sep->se_argv[argc++] = NULL;
Glenn L McGrath53766c42004-01-18 08:58:06 +0000433 }
434
Glenn L McGrath06e95652003-02-09 06:51:14 +0000435 return (sep);
436}
437
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000438static void freeconfig(struct servtab *cp)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000439{
440 int i;
441
442 free(cp->se_service);
443 free(cp->se_proto);
444 free(cp->se_user);
445 /* Note: se_group is part of the newstr'ed se_user */
446 free(cp->se_server);
447 for (i = 0; i < MAXARGV; i++)
448 free(cp->se_argv[i]);
449}
450
Glenn L McGrathc3b134f2004-01-17 01:26:53 +0000451#ifdef INETD_FEATURE_ENABLED
Glenn L McGrath06e95652003-02-09 06:51:14 +0000452static char **Argv;
453static char *LastArg;
454
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000455static void setproctitle(char *a, int s)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000456{
457 size_t size;
458 char *cp;
459 struct sockaddr_in sn;
460 char buf[80];
461
462 cp = Argv[0];
463 size = sizeof(sn);
464 if (getpeername(s, (struct sockaddr *)&sn, &size) == 0)
465 (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sn.sin_addr));
466 else
467 (void) sprintf(buf, "-%s", a);
468 strncpy(cp, buf, LastArg - cp);
469 cp += strlen(cp);
470 while (cp < LastArg)
471 *cp++ = ' ';
472}
Glenn L McGrathc3b134f2004-01-17 01:26:53 +0000473#endif /* INETD_FEATURE_ENABLED */
Glenn L McGrath06e95652003-02-09 06:51:14 +0000474
Glenn L McGrath06e95652003-02-09 06:51:14 +0000475
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000476static void setup(struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000477{
478 int on = 1;
479
480 if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) {
481 syslog(LOG_ERR, "%s/%s: socket: %m",
482 sep->se_service, sep->se_proto);
483 return;
484 }
485 if (setsockopt(sep->se_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on,
486 sizeof(on)) < 0)
487 syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
488 if (bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size) < 0) {
489 syslog(LOG_ERR, "%s/%s: bind: %m",
490 sep->se_service, sep->se_proto);
491 (void) close(sep->se_fd);
492 sep->se_fd = -1;
493 if (!timingout) {
494 timingout = 1;
495 alarm(RETRYTIME);
496 }
497 return;
498 }
499 if (sep->se_socktype == SOCK_STREAM)
500 listen(sep->se_fd, global_queuelen);
501
502 FD_SET(sep->se_fd, &allsock);
503 nsock++;
504 if (sep->se_fd > maxsock) {
505 maxsock = sep->se_fd;
Glenn L McGratha277e022004-01-17 03:20:46 +0000506 if (maxsock > rlim_ofile_cur - FD_MARGIN) {
507#ifdef RLIMIT_NOFILE
508# define FD_CHUNK 32
509 struct rlimit rl;
510
511 if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
512 syslog(LOG_ERR, "getrlimit: %m");
513 return;
514 }
515 rl.rlim_cur = rl.rlim_max < (rl.rlim_cur + FD_CHUNK) ? rl.rlim_max : (rl.rlim_cur + FD_CHUNK);
516 if (rl.rlim_cur <= rlim_ofile_cur) {
517 syslog(LOG_ERR,
518# if _FILE_OFFSET_BITS == 64
519 "bump_nofile: cannot extend file limit, max = %lld",
520# else
521 "bump_nofile: cannot extend file limit, max = %ld",
522# endif
523 rl.rlim_cur);
524 return;
525 }
526
527 if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
528 syslog(LOG_ERR, "setrlimit: %m");
529 return;
530 }
531
532 rlim_ofile_cur = rl.rlim_cur;
533 return;
534#else
535 syslog(LOG_ERR, "bump_nofile: cannot extend file limit");
536 return;
537#endif /* RLIMIT_NOFILE */
538 }
Glenn L McGrath06e95652003-02-09 06:51:14 +0000539 }
540}
541
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000542static void config(int signum)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000543{
544 struct servtab *sep, *cp, **sepp;
545 sigset_t oldmask;
546 unsigned n;
547
548 (void)signum;
Glenn L McGratha277e022004-01-17 03:20:46 +0000549
550 if (fconfig != NULL) {
551 fseek(fconfig, 0L, L_SET);
552 } else {
553 fconfig = fopen(CONFIG, "r");
554 if (fconfig == NULL) {
555 syslog(LOG_ERR, "%s: %m", CONFIG);
556 return;
557 }
558 }
Glenn L McGrath06e95652003-02-09 06:51:14 +0000559
560 for (sep = servtab; sep; sep = sep->se_next)
561 sep->se_checked = 0;
562 while ((cp = getconfigent()) != NULL) {
563 for (sep = servtab; sep; sep = sep->se_next)
564 if (strcmp(sep->se_service, cp->se_service) == 0 &&
565 strcmp(sep->se_proto, cp->se_proto) == 0)
566 break;
567 if (sep != 0) {
568 int i;
569
570#define SWAP(type, a, b) {type c=(type)a; (type)a=(type)b; (type)b=(type)c;}
571
572 sigprocmask(SIG_BLOCK, &emptymask, &oldmask);
573 /*
574 * sep->se_wait may be holding the pid of a daemon
575 * that we're waiting for. If so, don't overwrite
576 * it unless the config file explicitly says don't
577 * wait.
578 */
579 if (
Glenn L McGrathc3b134f2004-01-17 01:26:53 +0000580#ifdef INETD_FEATURE_ENABLED
Glenn L McGrath06e95652003-02-09 06:51:14 +0000581 cp->se_bi == 0 &&
582#endif
583 (sep->se_wait == 1 || cp->se_wait == 0))
584 sep->se_wait = cp->se_wait;
585 if (cp->se_max != sep->se_max)
586 SWAP(int, cp->se_max, sep->se_max);
587 if (cp->se_user)
588 SWAP(char *, sep->se_user, cp->se_user);
589 if (cp->se_group)
590 SWAP(char *, sep->se_group, cp->se_group);
591 if (cp->se_server)
592 SWAP(char *, sep->se_server, cp->se_server);
593 for (i = 0; i < MAXARGV; i++)
594 SWAP(char *, sep->se_argv[i], cp->se_argv[i]);
595#undef SWAP
596 sigprocmask(SIG_SETMASK, &oldmask, NULL);
597 freeconfig(cp);
598 } else {
Glenn L McGratha277e022004-01-17 03:20:46 +0000599 sep = (struct servtab *)xmalloc(sizeof (*sep));
600 *sep = *cp;
601 sep->se_fd = -1;
602 sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
603 sep->se_next = servtab;
604 servtab = sep;
605 sigprocmask(SIG_SETMASK, &oldmask, NULL);
Glenn L McGrath06e95652003-02-09 06:51:14 +0000606 }
607 sep->se_checked = 1;
608
609 switch (sep->se_family) {
610 case AF_UNIX:
611 if (sep->se_fd != -1)
612 break;
613 (void)unlink(sep->se_service);
614 n = strlen(sep->se_service);
615 if (n > sizeof(sep->se_ctrladdr_un.sun_path) - 1)
616 n = sizeof(sep->se_ctrladdr_un.sun_path) - 1;
617 strncpy(sep->se_ctrladdr_un.sun_path, sep->se_service, n);
618 sep->se_ctrladdr_un.sun_family = AF_UNIX;
619 sep->se_ctrladdr_size = n +
620 sizeof sep->se_ctrladdr_un.sun_family;
621 setup(sep);
622 break;
623 case AF_INET:
624 sep->se_ctrladdr_in.sin_family = AF_INET;
625 sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in;
626 {
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000627 u_short port = bb_lookup_port(sep->se_service, sep->se_proto, 0);
Glenn L McGrath06e95652003-02-09 06:51:14 +0000628
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000629 if (port == 0) {
630 syslog(LOG_ERR,
631 "%s/%s: unknown service",
632 sep->se_service, sep->se_proto);
633 continue;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000634 }
635 if (port != sep->se_ctrladdr_in.sin_port) {
636 sep->se_ctrladdr_in.sin_port = port;
637 if (sep->se_fd != -1) {
638 FD_CLR(sep->se_fd, &allsock);
639 nsock--;
640 (void) close(sep->se_fd);
641 }
642 sep->se_fd = -1;
643 }
644 if (sep->se_fd == -1)
645 setup(sep);
646 }
647 }
648 }
649 if (fconfig) {
650 (void) fclose(fconfig);
651 fconfig = NULL;
652 }
653 /*
654 * Purge anything not looked at above.
655 */
656 sigprocmask(SIG_SETMASK, &blockmask, &oldmask);
657 sepp = &servtab;
658 while ((sep = *sepp) != NULL) {
659 if (sep->se_checked) {
660 sepp = &sep->se_next;
661 continue;
662 }
663 *sepp = sep->se_next;
664 if (sep->se_fd != -1) {
665 FD_CLR(sep->se_fd, &allsock);
666 nsock--;
667 (void) close(sep->se_fd);
668 }
669 if (sep->se_family == AF_UNIX)
670 (void)unlink(sep->se_service);
671 freeconfig(sep);
672 free((char *)sep);
673 }
674 sigprocmask(SIG_SETMASK, &oldmask, NULL);
675}
676
677
678
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000679static void reapchild(int signum)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000680{
681 int status;
682 int pid;
683 struct servtab *sep;
684
685 (void)signum;
686 for (;;) {
687 pid = wait3(&status, WNOHANG, (struct rusage *)0);
688 if (pid <= 0)
689 break;
690 for (sep = servtab; sep; sep = sep->se_next)
691 if (sep->se_wait == pid) {
692 if (WIFEXITED(status) && WEXITSTATUS(status))
693 syslog(LOG_WARNING,
694 "%s: exit status 0x%x",
695 sep->se_server, WEXITSTATUS(status));
696 else if (WIFSIGNALED(status))
697 syslog(LOG_WARNING,
698 "%s: exit signal 0x%x",
699 sep->se_server, WTERMSIG(status));
700 sep->se_wait = 1;
701 FD_SET(sep->se_fd, &allsock);
702 nsock++;
703 }
704 }
705}
706
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000707static void retry(int signum)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000708{
709 struct servtab *sep;
710
711 (void)signum;
712 timingout = 0;
713 for (sep = servtab; sep; sep = sep->se_next) {
714 if (sep->se_fd == -1) {
715 switch (sep->se_family) {
716 case AF_UNIX:
717 case AF_INET:
718 setup(sep);
719 break;
720 }
721 }
722 }
723}
724
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000725static void goaway(int signum)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000726{
727 struct servtab *sep;
728
729 (void)signum;
730 for (sep = servtab; sep; sep = sep->se_next)
731 if (sep->se_fd != -1 && sep->se_family == AF_UNIX)
732 (void)unlink(sep->se_service);
733 (void)unlink(_PATH_INETDPID);
734 exit(0);
735}
736
737
738
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000739extern int inetd_main(int argc, char *argv[])
Glenn L McGrath06e95652003-02-09 06:51:14 +0000740{
741 struct servtab *sep;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000742 struct group *grp = NULL;
743 struct sigaction sa;
Eric Andersen35e643b2003-07-28 07:40:39 +0000744 int pid;
745 unsigned long opt;
746 char *sq;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000747 gid_t gid;
748
Glenn L McGrathc3b134f2004-01-17 01:26:53 +0000749#ifdef INETD_FEATURE_ENABLED
Glenn L McGrath06e95652003-02-09 06:51:14 +0000750 extern char **environ;
751#endif
752
753 gid = getgid();
754 setgroups(1, &gid);
755
Glenn L McGrathc3b134f2004-01-17 01:26:53 +0000756#ifdef INETD_FEATURE_ENABLED
Glenn L McGrath06e95652003-02-09 06:51:14 +0000757 Argv = argv;
758 if (environ == 0 || *environ == 0)
759 environ = argv;
760 while (*environ)
761 environ++;
762 LastArg = environ[-1] + strlen(environ[-1]);
763#endif
764
Eric Andersen35e643b2003-07-28 07:40:39 +0000765#if defined(__uClinux__)
766 opt = bb_getopt_ulflags(argc, argv, "q:f", &sq);
Eric Andersen68d4a852003-07-28 09:31:28 +0000767 if (!(opt & 2)) {
Eric Andersen35e643b2003-07-28 07:40:39 +0000768 daemon(0, 0);
769 /* reexec for vfork() do continue parent */
770 vfork_daemon_rexec(argc, argv, "-f");
771 }
772#else
Eric Andersen68d4a852003-07-28 09:31:28 +0000773 opt = bb_getopt_ulflags(argc, argv, "q:", &sq);
Eric Andersen35e643b2003-07-28 07:40:39 +0000774 daemon(0, 0);
775#endif /* uClinux */
776
777 if(opt & 1) {
Glenn L McGrath06e95652003-02-09 06:51:14 +0000778 global_queuelen = atoi(optarg);
779 if (global_queuelen < 8) global_queuelen=8;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000780 }
781 argc -= optind;
782 argv += optind;
783
784 if (argc > 0)
785 CONFIG = argv[0];
786
Manuel Novoa III cad53642003-03-19 09:13:01 +0000787 openlog(bb_applet_name, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
Glenn L McGrath06e95652003-02-09 06:51:14 +0000788 {
789 FILE *fp;
790
791 if ((fp = fopen(_PATH_INETDPID, "w")) != NULL) {
792 fprintf(fp, "%u\n", getpid());
793 (void)fclose(fp);
794 }
795 }
796
797#ifdef RLIMIT_NOFILE
798 if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) {
799 syslog(LOG_ERR, "getrlimit: %m");
800 } else {
801 rlim_ofile_cur = rlim_ofile.rlim_cur;
802 if (rlim_ofile_cur == RLIM_INFINITY) /* ! */
803 rlim_ofile_cur = OPEN_MAX;
804 }
805#endif
806
807 config(0);
808
809 sigemptyset(&emptymask);
810 sigemptyset(&blockmask);
811 sigaddset(&blockmask, SIGCHLD);
812 sigaddset(&blockmask, SIGHUP);
813 sigaddset(&blockmask, SIGALRM);
814
815 memset(&sa, 0, sizeof(sa));
816 sa.sa_mask = blockmask;
817 sa.sa_handler = retry;
818 sigaction(SIGALRM, &sa, NULL);
819 sa.sa_handler = config;
820 sigaction(SIGHUP, &sa, NULL);
821 sa.sa_handler = reapchild;
822 sigaction(SIGCHLD, &sa, NULL);
823 sa.sa_handler = goaway;
824 sigaction(SIGTERM, &sa, NULL);
825 sa.sa_handler = goaway;
826 sigaction(SIGINT, &sa, NULL);
827 sa.sa_handler = SIG_IGN;
828 sigaction(SIGPIPE, &sa, NULL);
829
830 {
831 /* space for daemons to overwrite environment for ps */
832#define DUMMYSIZE 100
833 char dummy[DUMMYSIZE];
834
835 (void)memset(dummy, 'x', DUMMYSIZE - 1);
836 dummy[DUMMYSIZE - 1] = '\0';
837
838 (void)setenv("inetd_dummy", dummy, 1);
839 }
840
841 for (;;) {
842 int n, ctrl;
843 fd_set readable;
844
845 if (nsock == 0) {
846 sigprocmask(SIG_BLOCK, &blockmask, NULL);
847 while (nsock == 0)
848 sigsuspend(&emptymask);
849 sigprocmask(SIG_SETMASK, &emptymask, NULL);
850 }
851 readable = allsock;
852 if ((n = select(maxsock + 1, &readable, (fd_set *)0,
853 (fd_set *)0, (struct timeval *)0)) <= 0) {
854 if (n < 0 && errno != EINTR)
855 syslog(LOG_WARNING, "select: %m");
856 sleep(1);
857 continue;
858 }
859 for (sep = servtab; n && sep; sep = sep->se_next)
860 if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
861 n--;
862 if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
863 /* Fixed AGC */
864 fcntl(sep->se_fd, F_SETFL, O_NDELAY);
865 /* --------- */
866 ctrl = accept(sep->se_fd, NULL, NULL);
867 fcntl(sep->se_fd, F_SETFL, 0);
868 if (ctrl < 0) {
869 if (errno == EINTR || errno == EWOULDBLOCK)
870 continue;
871 syslog(LOG_WARNING, "accept (for %s): %m",
872 sep->se_service);
873 continue;
874 }
875 } else
876 ctrl = sep->se_fd;
877 sigprocmask(SIG_BLOCK, &blockmask, NULL);
878 pid = 0;
Glenn L McGrathc3b134f2004-01-17 01:26:53 +0000879#ifdef INETD_FEATURE_ENABLED
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000880 if (sep->se_bi == 0 || sep->se_bi->bi_fork)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000881#endif
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000882 {
Glenn L McGrath06e95652003-02-09 06:51:14 +0000883 if (sep->se_count++ == 0)
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000884 (void)gettimeofday(&sep->se_time, (struct timezone *)0);
Glenn L McGrath06e95652003-02-09 06:51:14 +0000885 else if (sep->se_count >= sep->se_max) {
886 struct timeval now;
887
888 (void)gettimeofday(&now, (struct timezone *)0);
889 if (now.tv_sec - sep->se_time.tv_sec >
890 CNT_INTVL) {
891 sep->se_time = now;
892 sep->se_count = 1;
893 } else {
894 syslog(LOG_ERR,
895 "%s/%s server failing (looping), service terminated",
896 sep->se_service, sep->se_proto);
897 FD_CLR(sep->se_fd, &allsock);
898 (void) close(sep->se_fd);
899 sep->se_fd = -1;
900 sep->se_count = 0;
901 nsock--;
902 sigprocmask(SIG_SETMASK, &emptymask,
903 NULL);
904 if (!timingout) {
905 timingout = 1;
906 alarm(RETRYTIME);
907 }
908 continue;
909 }
910 }
911 pid = fork();
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000912 if (pid < 0) {
913 syslog(LOG_ERR, "fork: %m");
914 if (sep->se_socktype == SOCK_STREAM)
915 close(ctrl);
916 sigprocmask(SIG_SETMASK, &emptymask, NULL);
917 sleep(1);
918 continue;
919 }
920 if (pid && sep->se_wait) {
921 sep->se_wait = pid;
922 FD_CLR(sep->se_fd, &allsock);
923 nsock--;
924 }
Glenn L McGrath06e95652003-02-09 06:51:14 +0000925 }
926 sigprocmask(SIG_SETMASK, &emptymask, NULL);
927 if (pid == 0) {
Glenn L McGrathc3b134f2004-01-17 01:26:53 +0000928#ifdef INETD_FEATURE_ENABLED
Glenn L McGrath06e95652003-02-09 06:51:14 +0000929 if (sep->se_bi)
930 (*sep->se_bi->bi_fn)(ctrl, sep);
931 else
932#endif
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000933 {
934 struct passwd *pwd = getpwnam(sep->se_user);
935 if (pwd == NULL) {
Glenn L McGrath06e95652003-02-09 06:51:14 +0000936 syslog_err_and_discard_dg(
937 sep->se_socktype,
938 "getpwnam: %s: No such user",
939 sep->se_user);
940 }
941 if (sep->se_group &&
942 (grp = getgrnam(sep->se_group)) == NULL) {
943 syslog_err_and_discard_dg(
944 sep->se_socktype,
945 "getgrnam: %s: No such group",
946 sep->se_group);
947 }
948 /*
949 * Ok. There are four cases here:
950 * 1. nonroot user, no group specified
951 * 2. nonroot user, some group specified
952 * 3. root user, no group specified
953 * 4. root user, some group specified
954 * In cases 2 and 4 we setgid to the specified
955 * group. In cases 1 and 2 we run initgroups
956 * to run with the groups of the given user.
957 * In case 4 we do setgroups to run with the
958 * given group. In case 3 we do nothing.
959 */
960 if (pwd->pw_uid) {
961 if (sep->se_group)
962 pwd->pw_gid = grp->gr_gid;
963 setgid((gid_t)pwd->pw_gid);
964 initgroups(pwd->pw_name, pwd->pw_gid);
965 setuid((uid_t)pwd->pw_uid);
966 } else if (sep->se_group) {
967 setgid((gid_t)grp->gr_gid);
968 setgroups(1, &grp->gr_gid);
969 }
970 dup2(ctrl, 0);
971 close(ctrl);
972 dup2(0, 1);
973 dup2(0, 2);
974#ifdef RLIMIT_NOFILE
975 if (rlim_ofile.rlim_cur != rlim_ofile_cur) {
976 if (setrlimit(RLIMIT_NOFILE,
977 &rlim_ofile) < 0)
978 syslog(LOG_ERR,"setrlimit: %m");
979 }
980#endif
981 for (ctrl = rlim_ofile_cur-1; --ctrl > 2; )
982 (void)close(ctrl);
983
984 memset(&sa, 0, sizeof(sa));
985 sa.sa_handler = SIG_DFL;
986 sigaction(SIGPIPE, &sa, NULL);
987
988 execv(sep->se_server, sep->se_argv);
989 syslog_err_and_discard_dg(sep->se_socktype,
990 "execv %s: %m", sep->se_server);
991 }
992 }
993 if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
994 close(ctrl);
995 }
996 }
997}
998
999
1000/*
1001 * Internet services provided internally by inetd:
1002 */
1003#define BUFSIZE 4096
1004
1005#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
1006/* Echo service -- echo data back */
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001007static void echo_stream(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001008{
1009 char buffer[BUFSIZE];
1010 int i;
1011
1012 setproctitle(sep->se_service, s);
1013 while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
1014 write(s, buffer, i) > 0)
1015 ;
1016 exit(0);
1017}
1018
1019/* Echo service -- echo data back */
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001020static void echo_dg(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001021{
1022 char buffer[BUFSIZE];
1023 int i;
1024 size_t size;
1025 struct sockaddr sa;
1026
1027 (void)sep;
1028
1029 size = sizeof(sa);
1030 if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0)
1031 return;
1032 (void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
1033}
1034#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO */
1035
1036
1037#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
1038/* Discard service -- ignore data */
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001039static void discard_stream(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001040{
1041 char buffer[BUFSIZE];
1042
1043 setproctitle(sep->se_service, s);
1044 while ((errno = 0, read(s, buffer, sizeof(buffer)) > 0) ||
1045 errno == EINTR)
1046 ;
1047 exit(0);
1048}
1049
1050/* Discard service -- ignore data */
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001051static void discard_dg(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001052{
1053 char buffer[BUFSIZE];
1054 (void)sep;
1055 read(s, buffer, sizeof(buffer));
1056}
1057#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD */
1058
1059
1060#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
1061#include <ctype.h>
1062#define LINESIZ 72
1063static char ring[128];
1064static char *endring;
1065
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001066static void initring(void)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001067{
1068 int i;
1069
1070 endring = ring;
1071
1072 for (i = 0; i <= 128; ++i)
1073 if (isprint(i))
1074 *endring++ = i;
1075}
1076
1077/* Character generator */
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001078static void chargen_stream(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001079{
1080 char *rs;
1081 int len;
1082 char text[LINESIZ+2];
1083
1084 setproctitle(sep->se_service, s);
1085
1086 if (!endring) {
1087 initring();
1088 rs = ring;
1089 }
1090
1091 text[LINESIZ] = '\r';
1092 text[LINESIZ + 1] = '\n';
1093 for (rs = ring;;) {
1094 if ((len = endring - rs) >= LINESIZ)
Glenn L McGrath3e77b4e2004-01-17 01:44:32 +00001095 memcpy(rs, text, LINESIZ);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001096 else {
Glenn L McGrath3e77b4e2004-01-17 01:44:32 +00001097 memcpy(rs, text, len);
1098 memcpy(ring, text + len, LINESIZ - len);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001099 }
1100 if (++rs == endring)
1101 rs = ring;
1102 if (write(s, text, sizeof(text)) != sizeof(text))
1103 break;
1104 }
1105 exit(0);
1106}
1107
1108/* Character generator */
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001109static void chargen_dg(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001110{
1111 struct sockaddr sa;
1112 static char *rs;
1113 size_t len, size;
1114 char text[LINESIZ+2];
1115
1116 (void)sep;
1117
1118 if (endring == 0) {
1119 initring();
1120 rs = ring;
1121 }
1122
1123 size = sizeof(sa);
1124 if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
1125 return;
1126
1127 if ((len = endring - rs) >= LINESIZ)
Glenn L McGrath3e77b4e2004-01-17 01:44:32 +00001128 memcpy(rs, text, LINESIZ);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001129 else {
Glenn L McGrath3e77b4e2004-01-17 01:44:32 +00001130 memcpy(rs, text, len);
1131 memcpy(ring, text + len, LINESIZ - len);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001132 }
1133 if (++rs == endring)
1134 rs = ring;
1135 text[LINESIZ] = '\r';
1136 text[LINESIZ + 1] = '\n';
1137 (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
1138}
1139#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN */
1140
1141
1142#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
1143/*
1144 * Return a machine readable date and time, in the form of the
1145 * number of seconds since midnight, Jan 1, 1900. Since gettimeofday
1146 * returns the number of seconds since midnight, Jan 1, 1970,
1147 * we must add 2208988800 seconds to this figure to make up for
1148 * some seventy years Bell Labs was asleep.
1149 */
1150
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001151static long machtime(void)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001152{
1153 struct timeval tv;
1154
1155 if (gettimeofday(&tv, (struct timezone *)0) < 0) {
1156 fprintf(stderr, "Unable to get time of day\n");
1157 return (0L);
1158 }
1159 return (htonl((long)tv.tv_sec + 2208988800UL));
1160}
1161
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001162static void machtime_stream(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001163{
1164 long result;
1165 (void)sep;
1166
1167 result = machtime();
1168 write(s, (char *) &result, sizeof(result));
1169}
1170
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001171static void machtime_dg(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001172{
1173 long result;
1174 struct sockaddr sa;
1175 size_t size;
1176 (void)sep;
1177
1178 size = sizeof(sa);
1179 if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0)
1180 return;
1181 result = machtime();
1182 (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
1183}
1184#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME */
1185
1186
1187#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
1188/* Return human-readable time of day */
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001189static int human_readable_time_sprintf(char *buffer)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001190{
1191 time_t clocc = time(NULL);
1192
1193 return sprintf(buffer, "%.24s\r\n", ctime(&clocc));
1194}
1195
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001196static void daytime_stream(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001197{
1198 char buffer[256];
1199 size_t st = human_readable_time_sprintf(buffer);
1200
1201 (void)sep;
1202
1203 write(s, buffer, st);
1204}
1205
1206/* Return human-readable time of day */
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001207static void daytime_dg(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001208{
1209 char buffer[256];
1210 struct sockaddr sa;
1211 size_t size;
1212
1213 (void)sep;
1214
1215 size = sizeof(sa);
1216 if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
1217 return;
1218 size = human_readable_time_sprintf(buffer);
1219 sendto(s, buffer, size, 0, &sa, sizeof(sa));
1220}
1221#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME */