blob: 6980a900311c749aad86c70f2ad749a82ee2d052 [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 struct servtab *getconfigent(void)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000289{
290 static struct servtab serv;
291 struct servtab *sep = &serv;
292 int argc;
Glenn L McGrath53766c42004-01-18 08:58:06 +0000293 char *cp = NULL;
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000294 char *cp_ptr;
295 char *cp_ptr_ptr = NULL;
Glenn L McGrath53766c42004-01-18 08:58:06 +0000296
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000297more:
298 free(cp);
299 cp = bb_get_chomped_line_from_file(fconfig);
300 if (feof(fconfig)) {
301 free(cp);
302 return (NULL);
303 }
304 if ((cp == NULL) || (*cp == '#')) {
Glenn L McGrath06e95652003-02-09 06:51:14 +0000305 goto more;
Glenn L McGrath53766c42004-01-18 08:58:06 +0000306 }
Glenn L McGrath06e95652003-02-09 06:51:14 +0000307
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000308 cp_ptr = strtok_r(cp, " \t", &cp_ptr_ptr);
309 if (cp_ptr == NULL) {
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000310 /* Error */
311 goto more;
312 }
313 sep->se_service = bb_xstrdup(cp_ptr);
314
315 cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr);
316 if (cp_ptr == NULL) {
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000317 /* Error */
318 goto more;
319 }
320 if (strcmp(cp_ptr, "stream") == 0)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000321 sep->se_socktype = SOCK_STREAM;
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000322 else if (strcmp(cp_ptr, "dgram") == 0)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000323 sep->se_socktype = SOCK_DGRAM;
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000324 else if (strcmp(cp_ptr, "rdm") == 0)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000325 sep->se_socktype = SOCK_RDM;
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000326 else if (strcmp(cp_ptr, "seqpacket") == 0)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000327 sep->se_socktype = SOCK_SEQPACKET;
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000328 else if (strcmp(cp_ptr, "raw") == 0)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000329 sep->se_socktype = SOCK_RAW;
330 else
331 sep->se_socktype = -1;
332
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000333 cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr);
334 if (cp_ptr == NULL) {
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000335 /* error */
336 goto more;
337 }
338 if (strcmp(cp_ptr, "unix") == 0) {
Glenn L McGrath06e95652003-02-09 06:51:14 +0000339 sep->se_family = AF_UNIX;
340 } else {
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000341 if (strncmp(cp_ptr, "rpc/", 4) == 0) {
Glenn L McGrath06e95652003-02-09 06:51:14 +0000342 syslog(LOG_ERR, "%s: rpc services not suported",
343 sep->se_service);
344 goto more;
345 }
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000346 sep->se_family = AF_INET;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000347 }
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000348 sep->se_proto = bb_xstrdup(cp_ptr);
349
350 cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr);
351 if (cp_ptr == NULL) {
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000352 /* error */
Glenn L McGrath06e95652003-02-09 06:51:14 +0000353 goto more;
Glenn L McGrath53766c42004-01-18 08:58:06 +0000354 }
Glenn L McGrath06e95652003-02-09 06:51:14 +0000355 {
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000356 char *s = strchr(cp_ptr, '.');
Glenn L McGrath06e95652003-02-09 06:51:14 +0000357 if (s) {
358 *s++ = '\0';
359 sep->se_max = atoi(s);
360 } else
361 sep->se_max = TOOMANY;
362 }
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000363 sep->se_wait = strcmp(cp_ptr, "wait") == 0;
364
365 cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr);
366 if (cp_ptr == NULL) {
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000367 /* error */
368 goto more;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000369 }
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000370 {
371 char *cp_ptr2 = strchr(cp_ptr, '.');
372 if (cp_ptr2) {
373 *cp_ptr2++ = '\0';
374 sep->se_group = bb_xstrdup(cp_ptr2);
375 }
376 }
377 sep->se_user = bb_xstrdup(cp_ptr);
378
379 cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr);
380 if (cp_ptr == NULL) {
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000381 /* error */
382 goto more;
383 }
384 if (strcmp(cp_ptr, "internal") == 0) {
Glenn L McGrathc3b134f2004-01-17 01:26:53 +0000385#ifdef INETD_FEATURE_ENABLED
Glenn L McGrath06e95652003-02-09 06:51:14 +0000386 const struct biltin *bi;
387
Glenn L McGrath53766c42004-01-18 08:58:06 +0000388 for (bi = biltins; bi->bi_service; bi++) {
389 if ((bi->bi_socktype == sep->se_socktype) &&
390 (strcmp(bi->bi_service, sep->se_service) == 0)) {
Glenn L McGrath06e95652003-02-09 06:51:14 +0000391 break;
Glenn L McGrath53766c42004-01-18 08:58:06 +0000392 }
393 }
Glenn L McGrath06e95652003-02-09 06:51:14 +0000394 if (bi->bi_service == 0) {
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000395 syslog(LOG_ERR, "internal service %s unknown", sep->se_service);
Glenn L McGrath06e95652003-02-09 06:51:14 +0000396 goto more;
397 }
398 sep->se_bi = bi;
399 sep->se_wait = bi->bi_wait;
400#else
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000401 syslog(LOG_ERR, "internal service %s unknown", cp_ptr);
402 goto more;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000403#endif
Glenn L McGrath06e95652003-02-09 06:51:14 +0000404 }
Glenn L McGrath53766c42004-01-18 08:58:06 +0000405#ifdef INETD_FEATURE_ENABLED
406 else {
407 sep->se_bi = NULL;
408 }
409#endif
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000410 sep->se_server = bb_xstrdup(cp_ptr);
411
Glenn L McGrath53766c42004-01-18 08:58:06 +0000412 argc = 0;
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000413 while ((cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr)) != NULL) {
Glenn L McGrath53766c42004-01-18 08:58:06 +0000414 if (argc < MAXARGV) {
Glenn L McGratheaf5bc02004-01-20 15:32:39 +0000415 sep->se_argv[argc++] = cp_ptr;
Glenn L McGrath53766c42004-01-18 08:58:06 +0000416 }
417 }
418 while (argc <= MAXARGV) {
Glenn L McGrath06e95652003-02-09 06:51:14 +0000419 sep->se_argv[argc++] = NULL;
Glenn L McGrath53766c42004-01-18 08:58:06 +0000420 }
421
Eric Andersen60bb3f32004-02-17 20:04:34 +0000422 //free(cp); // BUG: cp is the argv[] container; we must not free it here!
Glenn L McGrath06e95652003-02-09 06:51:14 +0000423 return (sep);
424}
425
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000426static void freeconfig(struct servtab *cp)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000427{
428 int i;
429
430 free(cp->se_service);
431 free(cp->se_proto);
432 free(cp->se_user);
433 /* Note: se_group is part of the newstr'ed se_user */
434 free(cp->se_server);
435 for (i = 0; i < MAXARGV; i++)
436 free(cp->se_argv[i]);
437}
438
Glenn L McGrathc3b134f2004-01-17 01:26:53 +0000439#ifdef INETD_FEATURE_ENABLED
Glenn L McGrath06e95652003-02-09 06:51:14 +0000440static char **Argv;
441static char *LastArg;
442
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000443static void setproctitle(char *a, int s)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000444{
445 size_t size;
446 char *cp;
447 struct sockaddr_in sn;
448 char buf[80];
449
450 cp = Argv[0];
451 size = sizeof(sn);
452 if (getpeername(s, (struct sockaddr *)&sn, &size) == 0)
453 (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sn.sin_addr));
454 else
455 (void) sprintf(buf, "-%s", a);
456 strncpy(cp, buf, LastArg - cp);
457 cp += strlen(cp);
458 while (cp < LastArg)
459 *cp++ = ' ';
460}
Glenn L McGrathc3b134f2004-01-17 01:26:53 +0000461#endif /* INETD_FEATURE_ENABLED */
Glenn L McGrath06e95652003-02-09 06:51:14 +0000462
Glenn L McGrath06e95652003-02-09 06:51:14 +0000463
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000464static void setup(struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000465{
466 int on = 1;
467
468 if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) {
469 syslog(LOG_ERR, "%s/%s: socket: %m",
470 sep->se_service, sep->se_proto);
471 return;
472 }
473 if (setsockopt(sep->se_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on,
474 sizeof(on)) < 0)
475 syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
476 if (bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size) < 0) {
477 syslog(LOG_ERR, "%s/%s: bind: %m",
478 sep->se_service, sep->se_proto);
479 (void) close(sep->se_fd);
480 sep->se_fd = -1;
481 if (!timingout) {
482 timingout = 1;
483 alarm(RETRYTIME);
484 }
485 return;
486 }
487 if (sep->se_socktype == SOCK_STREAM)
488 listen(sep->se_fd, global_queuelen);
489
490 FD_SET(sep->se_fd, &allsock);
491 nsock++;
492 if (sep->se_fd > maxsock) {
493 maxsock = sep->se_fd;
Glenn L McGratha277e022004-01-17 03:20:46 +0000494 if (maxsock > rlim_ofile_cur - FD_MARGIN) {
495#ifdef RLIMIT_NOFILE
496# define FD_CHUNK 32
497 struct rlimit rl;
498
499 if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
500 syslog(LOG_ERR, "getrlimit: %m");
501 return;
502 }
503 rl.rlim_cur = rl.rlim_max < (rl.rlim_cur + FD_CHUNK) ? rl.rlim_max : (rl.rlim_cur + FD_CHUNK);
504 if (rl.rlim_cur <= rlim_ofile_cur) {
505 syslog(LOG_ERR,
506# if _FILE_OFFSET_BITS == 64
507 "bump_nofile: cannot extend file limit, max = %lld",
508# else
509 "bump_nofile: cannot extend file limit, max = %ld",
510# endif
511 rl.rlim_cur);
512 return;
513 }
514
515 if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
516 syslog(LOG_ERR, "setrlimit: %m");
517 return;
518 }
519
520 rlim_ofile_cur = rl.rlim_cur;
521 return;
522#else
523 syslog(LOG_ERR, "bump_nofile: cannot extend file limit");
524 return;
525#endif /* RLIMIT_NOFILE */
526 }
Glenn L McGrath06e95652003-02-09 06:51:14 +0000527 }
528}
529
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000530static void config(int signum)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000531{
532 struct servtab *sep, *cp, **sepp;
533 sigset_t oldmask;
534 unsigned n;
535
536 (void)signum;
Glenn L McGratha277e022004-01-17 03:20:46 +0000537
538 if (fconfig != NULL) {
539 fseek(fconfig, 0L, L_SET);
540 } else {
541 fconfig = fopen(CONFIG, "r");
542 if (fconfig == NULL) {
543 syslog(LOG_ERR, "%s: %m", CONFIG);
544 return;
545 }
546 }
Glenn L McGrath06e95652003-02-09 06:51:14 +0000547
548 for (sep = servtab; sep; sep = sep->se_next)
549 sep->se_checked = 0;
550 while ((cp = getconfigent()) != NULL) {
551 for (sep = servtab; sep; sep = sep->se_next)
552 if (strcmp(sep->se_service, cp->se_service) == 0 &&
553 strcmp(sep->se_proto, cp->se_proto) == 0)
554 break;
555 if (sep != 0) {
556 int i;
557
558#define SWAP(type, a, b) {type c=(type)a; (type)a=(type)b; (type)b=(type)c;}
559
560 sigprocmask(SIG_BLOCK, &emptymask, &oldmask);
561 /*
562 * sep->se_wait may be holding the pid of a daemon
563 * that we're waiting for. If so, don't overwrite
564 * it unless the config file explicitly says don't
565 * wait.
566 */
567 if (
Glenn L McGrathc3b134f2004-01-17 01:26:53 +0000568#ifdef INETD_FEATURE_ENABLED
Glenn L McGrath06e95652003-02-09 06:51:14 +0000569 cp->se_bi == 0 &&
570#endif
571 (sep->se_wait == 1 || cp->se_wait == 0))
572 sep->se_wait = cp->se_wait;
573 if (cp->se_max != sep->se_max)
574 SWAP(int, cp->se_max, sep->se_max);
575 if (cp->se_user)
576 SWAP(char *, sep->se_user, cp->se_user);
577 if (cp->se_group)
578 SWAP(char *, sep->se_group, cp->se_group);
579 if (cp->se_server)
580 SWAP(char *, sep->se_server, cp->se_server);
581 for (i = 0; i < MAXARGV; i++)
582 SWAP(char *, sep->se_argv[i], cp->se_argv[i]);
583#undef SWAP
584 sigprocmask(SIG_SETMASK, &oldmask, NULL);
Eric Andersen60bb3f32004-02-17 20:04:34 +0000585 // This freeconfig() is probably a bug, since it will try and free()
586 // each of the argv[] values, which are really just pointers
587 // into the middle of a single line buffer for the config file.
588 //freeconfig(cp); // BUG?
Glenn L McGrath06e95652003-02-09 06:51:14 +0000589 } else {
Glenn L McGratha277e022004-01-17 03:20:46 +0000590 sep = (struct servtab *)xmalloc(sizeof (*sep));
591 *sep = *cp;
592 sep->se_fd = -1;
593 sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
594 sep->se_next = servtab;
595 servtab = sep;
596 sigprocmask(SIG_SETMASK, &oldmask, NULL);
Glenn L McGrath06e95652003-02-09 06:51:14 +0000597 }
598 sep->se_checked = 1;
599
600 switch (sep->se_family) {
601 case AF_UNIX:
602 if (sep->se_fd != -1)
603 break;
604 (void)unlink(sep->se_service);
605 n = strlen(sep->se_service);
606 if (n > sizeof(sep->se_ctrladdr_un.sun_path) - 1)
607 n = sizeof(sep->se_ctrladdr_un.sun_path) - 1;
608 strncpy(sep->se_ctrladdr_un.sun_path, sep->se_service, n);
609 sep->se_ctrladdr_un.sun_family = AF_UNIX;
610 sep->se_ctrladdr_size = n +
611 sizeof sep->se_ctrladdr_un.sun_family;
612 setup(sep);
613 break;
614 case AF_INET:
615 sep->se_ctrladdr_in.sin_family = AF_INET;
616 sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in;
617 {
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000618 u_short port = bb_lookup_port(sep->se_service, sep->se_proto, 0);
Glenn L McGrath06e95652003-02-09 06:51:14 +0000619
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000620 if (port == 0) {
621 syslog(LOG_ERR,
622 "%s/%s: unknown service",
623 sep->se_service, sep->se_proto);
624 continue;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000625 }
626 if (port != sep->se_ctrladdr_in.sin_port) {
627 sep->se_ctrladdr_in.sin_port = port;
628 if (sep->se_fd != -1) {
629 FD_CLR(sep->se_fd, &allsock);
630 nsock--;
631 (void) close(sep->se_fd);
632 }
633 sep->se_fd = -1;
634 }
635 if (sep->se_fd == -1)
636 setup(sep);
637 }
638 }
639 }
640 if (fconfig) {
641 (void) fclose(fconfig);
642 fconfig = NULL;
643 }
644 /*
645 * Purge anything not looked at above.
646 */
647 sigprocmask(SIG_SETMASK, &blockmask, &oldmask);
648 sepp = &servtab;
649 while ((sep = *sepp) != NULL) {
650 if (sep->se_checked) {
651 sepp = &sep->se_next;
652 continue;
653 }
654 *sepp = sep->se_next;
655 if (sep->se_fd != -1) {
656 FD_CLR(sep->se_fd, &allsock);
657 nsock--;
658 (void) close(sep->se_fd);
659 }
660 if (sep->se_family == AF_UNIX)
661 (void)unlink(sep->se_service);
662 freeconfig(sep);
663 free((char *)sep);
664 }
665 sigprocmask(SIG_SETMASK, &oldmask, NULL);
666}
667
668
669
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000670static void reapchild(int signum)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000671{
672 int status;
673 int pid;
674 struct servtab *sep;
675
676 (void)signum;
677 for (;;) {
678 pid = wait3(&status, WNOHANG, (struct rusage *)0);
679 if (pid <= 0)
680 break;
681 for (sep = servtab; sep; sep = sep->se_next)
682 if (sep->se_wait == pid) {
683 if (WIFEXITED(status) && WEXITSTATUS(status))
684 syslog(LOG_WARNING,
685 "%s: exit status 0x%x",
686 sep->se_server, WEXITSTATUS(status));
687 else if (WIFSIGNALED(status))
688 syslog(LOG_WARNING,
689 "%s: exit signal 0x%x",
690 sep->se_server, WTERMSIG(status));
691 sep->se_wait = 1;
692 FD_SET(sep->se_fd, &allsock);
693 nsock++;
694 }
695 }
696}
697
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000698static void retry(int signum)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000699{
700 struct servtab *sep;
701
702 (void)signum;
703 timingout = 0;
704 for (sep = servtab; sep; sep = sep->se_next) {
705 if (sep->se_fd == -1) {
706 switch (sep->se_family) {
707 case AF_UNIX:
708 case AF_INET:
709 setup(sep);
710 break;
711 }
712 }
713 }
714}
715
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000716static void goaway(int signum)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000717{
718 struct servtab *sep;
719
720 (void)signum;
721 for (sep = servtab; sep; sep = sep->se_next)
722 if (sep->se_fd != -1 && sep->se_family == AF_UNIX)
723 (void)unlink(sep->se_service);
724 (void)unlink(_PATH_INETDPID);
725 exit(0);
726}
727
728
729
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +0000730extern int inetd_main(int argc, char *argv[])
Glenn L McGrath06e95652003-02-09 06:51:14 +0000731{
732 struct servtab *sep;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000733 struct group *grp = NULL;
734 struct sigaction sa;
Eric Andersen35e643b2003-07-28 07:40:39 +0000735 int pid;
736 unsigned long opt;
737 char *sq;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000738 gid_t gid;
739
Glenn L McGrathc3b134f2004-01-17 01:26:53 +0000740#ifdef INETD_FEATURE_ENABLED
Glenn L McGrath06e95652003-02-09 06:51:14 +0000741 extern char **environ;
742#endif
743
744 gid = getgid();
745 setgroups(1, &gid);
746
Glenn L McGrathc3b134f2004-01-17 01:26:53 +0000747#ifdef INETD_FEATURE_ENABLED
Glenn L McGrath06e95652003-02-09 06:51:14 +0000748 Argv = argv;
749 if (environ == 0 || *environ == 0)
750 environ = argv;
751 while (*environ)
752 environ++;
753 LastArg = environ[-1] + strlen(environ[-1]);
754#endif
755
Eric Andersen35e643b2003-07-28 07:40:39 +0000756#if defined(__uClinux__)
757 opt = bb_getopt_ulflags(argc, argv, "q:f", &sq);
Eric Andersen68d4a852003-07-28 09:31:28 +0000758 if (!(opt & 2)) {
Eric Andersen35e643b2003-07-28 07:40:39 +0000759 daemon(0, 0);
760 /* reexec for vfork() do continue parent */
761 vfork_daemon_rexec(argc, argv, "-f");
762 }
763#else
Eric Andersen68d4a852003-07-28 09:31:28 +0000764 opt = bb_getopt_ulflags(argc, argv, "q:", &sq);
Eric Andersen35e643b2003-07-28 07:40:39 +0000765 daemon(0, 0);
766#endif /* uClinux */
767
768 if(opt & 1) {
Glenn L McGrath06e95652003-02-09 06:51:14 +0000769 global_queuelen = atoi(optarg);
770 if (global_queuelen < 8) global_queuelen=8;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000771 }
772 argc -= optind;
773 argv += optind;
774
775 if (argc > 0)
776 CONFIG = argv[0];
777
Manuel Novoa III cad53642003-03-19 09:13:01 +0000778 openlog(bb_applet_name, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
Glenn L McGrath06e95652003-02-09 06:51:14 +0000779 {
780 FILE *fp;
781
782 if ((fp = fopen(_PATH_INETDPID, "w")) != NULL) {
783 fprintf(fp, "%u\n", getpid());
784 (void)fclose(fp);
785 }
786 }
787
788#ifdef RLIMIT_NOFILE
789 if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) {
790 syslog(LOG_ERR, "getrlimit: %m");
791 } else {
792 rlim_ofile_cur = rlim_ofile.rlim_cur;
793 if (rlim_ofile_cur == RLIM_INFINITY) /* ! */
794 rlim_ofile_cur = OPEN_MAX;
795 }
796#endif
797
798 config(0);
799
800 sigemptyset(&emptymask);
801 sigemptyset(&blockmask);
802 sigaddset(&blockmask, SIGCHLD);
803 sigaddset(&blockmask, SIGHUP);
804 sigaddset(&blockmask, SIGALRM);
805
806 memset(&sa, 0, sizeof(sa));
807 sa.sa_mask = blockmask;
808 sa.sa_handler = retry;
809 sigaction(SIGALRM, &sa, NULL);
810 sa.sa_handler = config;
811 sigaction(SIGHUP, &sa, NULL);
812 sa.sa_handler = reapchild;
813 sigaction(SIGCHLD, &sa, NULL);
814 sa.sa_handler = goaway;
815 sigaction(SIGTERM, &sa, NULL);
816 sa.sa_handler = goaway;
817 sigaction(SIGINT, &sa, NULL);
818 sa.sa_handler = SIG_IGN;
819 sigaction(SIGPIPE, &sa, NULL);
820
821 {
822 /* space for daemons to overwrite environment for ps */
823#define DUMMYSIZE 100
824 char dummy[DUMMYSIZE];
825
826 (void)memset(dummy, 'x', DUMMYSIZE - 1);
827 dummy[DUMMYSIZE - 1] = '\0';
828
829 (void)setenv("inetd_dummy", dummy, 1);
830 }
831
832 for (;;) {
Glenn L McGrath82d42db2004-02-18 13:12:53 +0000833 fd_set readable;
834 int ctrl;
835 int n;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000836
Glenn L McGrath82d42db2004-02-18 13:12:53 +0000837 if (nsock == 0) {
838 sigprocmask(SIG_BLOCK, &blockmask, NULL);
839 while (nsock == 0) {
840 sigsuspend(&emptymask);
Glenn L McGrath06e95652003-02-09 06:51:14 +0000841 }
Glenn L McGrath82d42db2004-02-18 13:12:53 +0000842 sigprocmask(SIG_SETMASK, &emptymask, NULL);
843 }
844 readable = allsock;
845 n = select(maxsock + 1, &readable, (fd_set *)0, (fd_set *)0, (struct timeval *)0);
846 if (n <= 0) {
847 if (n < 0 && errno != EINTR) {
848 syslog(LOG_WARNING, "select: %m");
849 }
850 sleep(1);
851 continue;
852 }
853 for (sep = servtab; n && sep; sep = sep->se_next) {
854 if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
855 n--;
856 if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
857 /* Fixed AGC */
858 fcntl(sep->se_fd, F_SETFL, O_NDELAY);
859 /* --------- */
860 ctrl = accept(sep->se_fd, NULL, NULL);
861 fcntl(sep->se_fd, F_SETFL, 0);
862 if (ctrl < 0) {
863 if (errno == EINTR || errno == EWOULDBLOCK) {
864 continue;
865 }
866 syslog(LOG_WARNING, "accept (for %s): %m",
867 sep->se_service);
868 continue;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000869 }
Glenn L McGrath82d42db2004-02-18 13:12:53 +0000870 } else {
871 ctrl = sep->se_fd;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000872 }
Glenn L McGrath82d42db2004-02-18 13:12:53 +0000873 sigprocmask(SIG_BLOCK, &blockmask, NULL);
874 pid = 0;
Glenn L McGrathc3b134f2004-01-17 01:26:53 +0000875#ifdef INETD_FEATURE_ENABLED
Glenn L McGrath82d42db2004-02-18 13:12:53 +0000876 if (sep->se_bi == 0 || sep->se_bi->bi_fork)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000877#endif
Glenn L McGrath82d42db2004-02-18 13:12:53 +0000878 {
879 if (sep->se_count++ == 0) {
880 gettimeofday(&sep->se_time, (struct timezone *)0);
881 }
882 else if (sep->se_count >= sep->se_max) {
883 struct timeval now;
884
885 gettimeofday(&now, (struct timezone *)0);
886 if (now.tv_sec - sep->se_time.tv_sec > CNT_INTVL) {
887 sep->se_time = now;
888 sep->se_count = 1;
889 } else {
890 syslog(LOG_ERR,
891 "%s/%s server failing (looping), service terminated",
892 sep->se_service, sep->se_proto);
893 FD_CLR(sep->se_fd, &allsock);
894 close(sep->se_fd);
895 sep->se_fd = -1;
896 sep->se_count = 0;
897 nsock--;
898 sigprocmask(SIG_SETMASK, &emptymask, NULL);
899 if (!timingout) {
900 timingout = 1;
901 alarm(RETRYTIME);
902 }
903 continue;
904 }
905 }
906 pid = fork();
907 if (pid < 0) {
908 syslog(LOG_ERR, "fork: %m");
909 if (sep->se_socktype == SOCK_STREAM) {
910 close(ctrl);
911 }
912 sigprocmask(SIG_SETMASK, &emptymask, NULL);
913 sleep(1);
914 continue;
915 }
916 if (pid && sep->se_wait) {
917 sep->se_wait = pid;
918 FD_CLR(sep->se_fd, &allsock);
919 nsock--;
920 }
Glenn L McGrath06e95652003-02-09 06:51:14 +0000921 }
Glenn L McGrath82d42db2004-02-18 13:12:53 +0000922 sigprocmask(SIG_SETMASK, &emptymask, NULL);
923 if (pid == 0) {
924#ifdef INETD_FEATURE_ENABLED
925 if (sep->se_bi) {
926 (*sep->se_bi->bi_fn)(ctrl, sep);
927 } else
928#endif
929 {
930 struct passwd *pwd = getpwnam(sep->se_user);
931 if (pwd == NULL) {
932 syslog_err_and_discard_dg(
933 sep->se_socktype,
934 "getpwnam: %s: No such user",
935 sep->se_user);
936 }
937 if (sep->se_group && (grp = getgrnam(sep->se_group)) == NULL) {
938 syslog_err_and_discard_dg(sep->se_socktype,
939 "getgrnam: %s: No such group", sep->se_group);
940 }
941 /*
942 * Ok. There are four cases here:
943 * 1. nonroot user, no group specified
944 * 2. nonroot user, some group specified
945 * 3. root user, no group specified
946 * 4. root user, some group specified
947 * In cases 2 and 4 we setgid to the specified
948 * group. In cases 1 and 2 we run initgroups
949 * to run with the groups of the given user.
950 * In case 4 we do setgroups to run with the
951 * given group. In case 3 we do nothing.
952 */
953 if (pwd->pw_uid) {
954 if (sep->se_group) {
955 pwd->pw_gid = grp->gr_gid;
956 }
957 setgid((gid_t)pwd->pw_gid);
958 initgroups(pwd->pw_name, pwd->pw_gid);
959 setuid((uid_t)pwd->pw_uid);
960 } else if (sep->se_group) {
961 setgid((gid_t)grp->gr_gid);
962 setgroups(1, &grp->gr_gid);
963 }
964 dup2(ctrl, 0);
965 close(ctrl);
966 dup2(0, 1);
967 dup2(0, 2);
Glenn L McGrath06e95652003-02-09 06:51:14 +0000968#ifdef RLIMIT_NOFILE
Glenn L McGrath82d42db2004-02-18 13:12:53 +0000969 if (rlim_ofile.rlim_cur != rlim_ofile_cur) {
970 if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) {
971 syslog(LOG_ERR,"setrlimit: %m");
972 }
973 }
Glenn L McGrath06e95652003-02-09 06:51:14 +0000974#endif
Glenn L McGrath82d42db2004-02-18 13:12:53 +0000975 for (ctrl = rlim_ofile_cur-1; --ctrl > 2; ) {
976 (void)close(ctrl);
977 }
978 memset(&sa, 0, sizeof(sa));
979 sa.sa_handler = SIG_DFL;
980 sigaction(SIGPIPE, &sa, NULL);
Glenn L McGrath06e95652003-02-09 06:51:14 +0000981
Glenn L McGrath82d42db2004-02-18 13:12:53 +0000982 execv(sep->se_server, sep->se_argv);
983 syslog_err_and_discard_dg(sep->se_socktype, "execv %s: %m", sep->se_server);
984 }
985 }
986 if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
987 close(ctrl);
988 }
Glenn L McGrath06e95652003-02-09 06:51:14 +0000989 }
990 }
Glenn L McGrath06e95652003-02-09 06:51:14 +0000991 }
992}
993
994
995/*
996 * Internet services provided internally by inetd:
997 */
998#define BUFSIZE 4096
999
1000#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
1001/* Echo service -- echo data back */
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001002static void echo_stream(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001003{
1004 char buffer[BUFSIZE];
1005 int i;
1006
1007 setproctitle(sep->se_service, s);
1008 while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
1009 write(s, buffer, i) > 0)
1010 ;
1011 exit(0);
1012}
1013
1014/* Echo service -- echo data back */
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001015static void echo_dg(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001016{
1017 char buffer[BUFSIZE];
1018 int i;
1019 size_t size;
1020 struct sockaddr sa;
1021
1022 (void)sep;
1023
1024 size = sizeof(sa);
1025 if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0)
1026 return;
1027 (void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
1028}
1029#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO */
1030
1031
1032#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
1033/* Discard service -- ignore data */
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001034static void discard_stream(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001035{
1036 char buffer[BUFSIZE];
1037
1038 setproctitle(sep->se_service, s);
1039 while ((errno = 0, read(s, buffer, sizeof(buffer)) > 0) ||
1040 errno == EINTR)
1041 ;
1042 exit(0);
1043}
1044
1045/* Discard service -- ignore data */
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001046static void discard_dg(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001047{
1048 char buffer[BUFSIZE];
1049 (void)sep;
1050 read(s, buffer, sizeof(buffer));
1051}
1052#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD */
1053
1054
1055#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
1056#include <ctype.h>
1057#define LINESIZ 72
1058static char ring[128];
1059static char *endring;
1060
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001061static void initring(void)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001062{
1063 int i;
1064
1065 endring = ring;
1066
1067 for (i = 0; i <= 128; ++i)
1068 if (isprint(i))
1069 *endring++ = i;
1070}
1071
1072/* Character generator */
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001073static void chargen_stream(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001074{
1075 char *rs;
1076 int len;
1077 char text[LINESIZ+2];
1078
1079 setproctitle(sep->se_service, s);
1080
1081 if (!endring) {
1082 initring();
1083 rs = ring;
1084 }
1085
1086 text[LINESIZ] = '\r';
1087 text[LINESIZ + 1] = '\n';
1088 for (rs = ring;;) {
1089 if ((len = endring - rs) >= LINESIZ)
Glenn L McGrath3e77b4e2004-01-17 01:44:32 +00001090 memcpy(rs, text, LINESIZ);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001091 else {
Glenn L McGrath3e77b4e2004-01-17 01:44:32 +00001092 memcpy(rs, text, len);
1093 memcpy(ring, text + len, LINESIZ - len);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001094 }
1095 if (++rs == endring)
1096 rs = ring;
1097 if (write(s, text, sizeof(text)) != sizeof(text))
1098 break;
1099 }
1100 exit(0);
1101}
1102
1103/* Character generator */
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001104static void chargen_dg(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001105{
1106 struct sockaddr sa;
1107 static char *rs;
1108 size_t len, size;
1109 char text[LINESIZ+2];
1110
1111 (void)sep;
1112
1113 if (endring == 0) {
1114 initring();
1115 rs = ring;
1116 }
1117
1118 size = sizeof(sa);
1119 if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
1120 return;
1121
1122 if ((len = endring - rs) >= LINESIZ)
Glenn L McGrath3e77b4e2004-01-17 01:44:32 +00001123 memcpy(rs, text, LINESIZ);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001124 else {
Glenn L McGrath3e77b4e2004-01-17 01:44:32 +00001125 memcpy(rs, text, len);
1126 memcpy(ring, text + len, LINESIZ - len);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001127 }
1128 if (++rs == endring)
1129 rs = ring;
1130 text[LINESIZ] = '\r';
1131 text[LINESIZ + 1] = '\n';
1132 (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
1133}
1134#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN */
1135
1136
1137#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
1138/*
1139 * Return a machine readable date and time, in the form of the
1140 * number of seconds since midnight, Jan 1, 1900. Since gettimeofday
1141 * returns the number of seconds since midnight, Jan 1, 1970,
1142 * we must add 2208988800 seconds to this figure to make up for
1143 * some seventy years Bell Labs was asleep.
1144 */
1145
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001146static long machtime(void)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001147{
1148 struct timeval tv;
1149
1150 if (gettimeofday(&tv, (struct timezone *)0) < 0) {
1151 fprintf(stderr, "Unable to get time of day\n");
1152 return (0L);
1153 }
1154 return (htonl((long)tv.tv_sec + 2208988800UL));
1155}
1156
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001157static void machtime_stream(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001158{
1159 long result;
1160 (void)sep;
1161
1162 result = machtime();
1163 write(s, (char *) &result, sizeof(result));
1164}
1165
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001166static void machtime_dg(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001167{
1168 long result;
1169 struct sockaddr sa;
1170 size_t size;
1171 (void)sep;
1172
1173 size = sizeof(sa);
1174 if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0)
1175 return;
1176 result = machtime();
1177 (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
1178}
1179#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME */
1180
1181
1182#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
1183/* Return human-readable time of day */
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001184static int human_readable_time_sprintf(char *buffer)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001185{
1186 time_t clocc = time(NULL);
1187
1188 return sprintf(buffer, "%.24s\r\n", ctime(&clocc));
1189}
1190
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001191static void daytime_stream(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001192{
1193 char buffer[256];
1194 size_t st = human_readable_time_sprintf(buffer);
1195
1196 (void)sep;
1197
1198 write(s, buffer, st);
1199}
1200
1201/* Return human-readable time of day */
Glenn L McGrathff6ec8a2004-01-17 02:47:45 +00001202static void daytime_dg(int s, struct servtab *sep)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001203{
1204 char buffer[256];
1205 struct sockaddr sa;
1206 size_t size;
1207
1208 (void)sep;
1209
1210 size = sizeof(sa);
1211 if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
1212 return;
1213 size = human_readable_time_sprintf(buffer);
1214 sendto(s, buffer, size, 0, &sa, sizeof(sa));
1215}
1216#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME */