source: MondoRescue/branches/3.3/mindi-busybox/networking/inetd.c@ 3734

Last change on this file since 3734 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 49.3 KB
RevLine 
[1765]1/* vi: set sw=4 ts=4: */
[821]2/* $Slackware: inetd.c 1.79s 2001/02/06 13:18:00 volkerdi Exp $ */
3/* $OpenBSD: inetd.c,v 1.79 2001/01/30 08:30:57 deraadt Exp $ */
4/* $NetBSD: inetd.c,v 1.11 1996/02/22 11:14:41 mycroft Exp $ */
5/* Busybox port by Vladimir Oleynik (C) 2001-2005 <dzo@simtreas.ru> */
[2725]6/* IPv6 support, many bug fixes by Denys Vlasenko (c) 2008 */
[821]7/*
8 * Copyright (c) 1983,1991 The Regents of the University of California.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
26 *
[1765]27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
[821]28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 */
39
[1765]40/* Inetd - Internet super-server
[821]41 *
[2725]42 * This program invokes configured services when a connection
43 * from a peer is established or a datagram arrives.
44 * Connection-oriented services are invoked each time a
[821]45 * connection is made, by creating a process. This process
46 * is passed the connection as file descriptor 0 and is
[2725]47 * expected to do a getpeername to find out peer's host
[821]48 * and port.
49 * Datagram oriented services are invoked when a datagram
50 * arrives; a process is created and passed a pending message
[2725]51 * on file descriptor 0. peer's address can be obtained
52 * using recvfrom.
[821]53 *
54 * Inetd uses a configuration file which is read at startup
55 * and, possibly, at some later time in response to a hangup signal.
[1765]56 * The configuration file is "free format" with fields given in the
[821]57 * order shown below. Continuation lines for an entry must begin with
58 * a space or tab. All fields must be present in each entry.
59 *
[2725]60 * service_name must be in /etc/services
61 * socket_type stream/dgram/raw/rdm/seqpacket
[821]62 * protocol must be in /etc/protocols
[2725]63 * (usually "tcp" or "udp")
[821]64 * wait/nowait[.max] single-threaded/multi-threaded, max #
65 * user[.group] or user[:group] user/group to run daemon as
[2725]66 * server_program full path name
67 * server_program_arguments maximum of MAXARGS (20)
[821]68 *
69 * For RPC services
[2725]70 * service_name/version must be in /etc/rpc
71 * socket_type stream/dgram/raw/rdm/seqpacket
72 * rpc/protocol "rpc/tcp" etc
[821]73 * wait/nowait[.max] single-threaded/multi-threaded
74 * user[.group] or user[:group] user to run daemon as
[2725]75 * server_program full path name
76 * server_program_arguments maximum of MAXARGS (20)
[821]77 *
78 * For non-RPC services, the "service name" can be of the form
79 * hostaddress:servicename, in which case the hostaddress is used
80 * as the host portion of the address to listen on. If hostaddress
[2725]81 * consists of a single '*' character, INADDR_ANY is used.
[821]82 *
83 * A line can also consist of just
84 * hostaddress:
85 * where hostaddress is as in the preceding paragraph. Such a line must
86 * have no further fields; the specified hostaddress is remembered and
87 * used for all further lines that have no hostaddress specified,
88 * until the next such line (or EOF). (This is why * is provided to
89 * allow explicit specification of INADDR_ANY.) A line
90 * *:
91 * is implicitly in effect at the beginning of the file.
92 *
93 * The hostaddress specifier may (and often will) contain dots;
94 * the service name must not.
95 *
96 * For RPC services, host-address specifiers are accepted and will
97 * work to some extent; however, because of limitations in the
98 * portmapper interface, it will not work to try to give more than
99 * one line for any given RPC service, even if the host-address
100 * specifiers are different.
101 *
[2725]102 * Comment lines are indicated by a '#' in column 1.
[821]103 */
104
[1765]105/* inetd rules for passing file descriptors to children
106 * (http://www.freebsd.org/cgi/man.cgi?query=inetd):
[821]107 *
[1765]108 * The wait/nowait entry specifies whether the server that is invoked by
109 * inetd will take over the socket associated with the service access point,
110 * and thus whether inetd should wait for the server to exit before listen-
111 * ing for new service requests. Datagram servers must use "wait", as
112 * they are always invoked with the original datagram socket bound to the
113 * specified service address. These servers must read at least one datagram
114 * from the socket before exiting. If a datagram server connects to its
115 * peer, freeing the socket so inetd can receive further messages on the
116 * socket, it is said to be a "multi-threaded" server; it should read one
117 * datagram from the socket and create a new socket connected to the peer.
118 * It should fork, and the parent should then exit to allow inetd to check
119 * for new service requests to spawn new servers. Datagram servers which
120 * process all incoming datagrams on a socket and eventually time out are
[2725]121 * said to be "single-threaded". The comsat(8), biff(1) and talkd(8)
[1765]122 * utilities are both examples of the latter type of datagram server. The
123 * tftpd(8) utility is an example of a multi-threaded datagram server.
124 *
125 * Servers using stream sockets generally are multi-threaded and use the
126 * "nowait" entry. Connection requests for these services are accepted by
127 * inetd, and the server is given only the newly-accepted socket connected
128 * to a client of the service. Most stream-based services operate in this
129 * manner. Stream-based servers that use "wait" are started with the lis-
130 * tening service socket, and must accept at least one connection request
131 * before exiting. Such a server would normally accept and process incoming
132 * connection requests until a timeout.
133 */
134
[2725]135/* Despite of above doc saying that dgram services must use "wait",
136 * "udp nowait" servers are implemented in busyboxed inetd.
137 * IPv6 addresses are also implemented. However, they may look ugly -
138 * ":::service..." means "address '::' (IPv6 wildcard addr)":"service"...
139 * You have to put "tcp6"/"udp6" in protocol field to select IPv6.
140 */
141
142/* Here's the scoop concerning the user[:group] feature:
143 * 1) group is not specified:
[821]144 * a) user = root: NO setuid() or setgid() is done
[2725]145 * b) other: initgroups(name, primary group)
146 * setgid(primary group as found in passwd)
[821]147 * setuid()
[2725]148 * 2) group is specified:
[821]149 * a) user = root: setgid(specified group)
150 * NO initgroups()
151 * NO setuid()
[2725]152 * b) other: initgroups(name, specified group)
153 * setgid(specified group)
[821]154 * setuid()
155 */
156
[3232]157//usage:#define inetd_trivial_usage
158//usage: "[-fe] [-q N] [-R N] [CONFFILE]"
159//usage:#define inetd_full_usage "\n\n"
160//usage: "Listen for network connections and launch programs\n"
161//usage: "\n -f Run in foreground"
162//usage: "\n -e Log to stderr"
163//usage: "\n -q N Socket listen queue (default: 128)"
164//usage: "\n -R N Pause services after N connects/min"
165//usage: "\n (default: 0 - disabled)"
166
[1765]167#include <syslog.h>
[3232]168#include <sys/resource.h> /* setrlimit */
169#include <sys/socket.h> /* un.h may need this */
[821]170#include <sys/un.h>
171
[2725]172#include "libbb.h"
[3621]173#include "common_bufsiz.h"
[821]174
[1765]175#if ENABLE_FEATURE_INETD_RPC
[3232]176# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
177# error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support"
178# endif
179# include <rpc/rpc.h>
180# include <rpc/pmap_clnt.h>
[821]181#endif
182
[2725]183#if !BB_MMU
184/* stream version of chargen is forking but not execing,
185 * can't do that (easily) on NOMMU */
186#undef ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
187#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN 0
188#endif
[821]189
[2725]190#define CNT_INTERVAL 60 /* servers in CNT_INTERVAL sec. */
191#define RETRYTIME 60 /* retry after bind or server fail */
[821]192
[2725]193// TODO: explain, or get rid of setrlimit games
194
[821]195#ifndef RLIMIT_NOFILE
196#define RLIMIT_NOFILE RLIMIT_OFILE
197#endif
198
199#ifndef OPEN_MAX
200#define OPEN_MAX 64
201#endif
202
203/* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */
[1765]204#define FD_MARGIN 8
[821]205
[2725]206#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD \
207 || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO \
208 || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN \
209 || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME \
210 || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
211# define INETD_BUILTINS_ENABLED
[821]212#endif
213
[2725]214typedef struct servtab_t {
215 /* The most frequently referenced one: */
216 int se_fd; /* open descriptor */
217 /* NB: 'biggest fields last' saves on code size (~250 bytes) */
218 /* [addr:]service socktype proto wait user[:group] prog [args] */
219 char *se_local_hostname; /* addr to listen on */
220 char *se_service; /* "80" or "www" or "mount/2[-3]" */
221 /* socktype is in se_socktype */ /* "stream" "dgram" "raw" "rdm" "seqpacket" */
222 char *se_proto; /* "unix" or "[rpc/]tcp[6]" */
[1765]223#if ENABLE_FEATURE_INETD_RPC
224 int se_rpcprog; /* rpc program number */
[2725]225 int se_rpcver_lo; /* rpc program lowest version */
226 int se_rpcver_hi; /* rpc program highest version */
227#define is_rpc_service(sep) ((sep)->se_rpcver_lo != 0)
[821]228#else
[2725]229#define is_rpc_service(sep) 0
[821]230#endif
[2725]231 pid_t se_wait; /* 0:"nowait", 1:"wait", >1:"wait" */
232 /* and waiting for this pid */
233 socktype_t se_socktype; /* SOCK_STREAM/DGRAM/RDM/... */
234 family_t se_family; /* AF_UNIX/INET[6] */
235 /* se_proto_no is used by RPC code only... hmm */
236 smallint se_proto_no; /* IPPROTO_TCP/UDP, n/a for AF_UNIX */
237 smallint se_checked; /* looked at during merge */
238 unsigned se_max; /* allowed instances per minute */
239 unsigned se_count; /* number started since se_time */
240 unsigned se_time; /* when we started counting */
[1765]241 char *se_user; /* user name to run as */
[2725]242 char *se_group; /* group name to run as, can be NULL */
243#ifdef INETD_BUILTINS_ENABLED
244 const struct builtin *se_builtin; /* if built-in, description */
[821]245#endif
[2725]246 struct servtab_t *se_next;
247 len_and_sockaddr *se_lsa;
248 char *se_program; /* server program */
[821]249#define MAXARGV 20
[1765]250 char *se_argv[MAXARGV + 1]; /* program arguments */
[821]251} servtab_t;
252
[2725]253#ifdef INETD_BUILTINS_ENABLED
254/* Echo received data */
[1765]255#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
[2725]256static void FAST_FUNC echo_stream(int, servtab_t *);
257static void FAST_FUNC echo_dg(int, servtab_t *);
[821]258#endif
[2725]259/* Internet /dev/null */
[1765]260#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
[2725]261static void FAST_FUNC discard_stream(int, servtab_t *);
262static void FAST_FUNC discard_dg(int, servtab_t *);
[821]263#endif
[2725]264/* Return 32 bit time since 1900 */
[1765]265#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
[2725]266static void FAST_FUNC machtime_stream(int, servtab_t *);
267static void FAST_FUNC machtime_dg(int, servtab_t *);
[821]268#endif
[2725]269/* Return human-readable time */
[1765]270#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
[2725]271static void FAST_FUNC daytime_stream(int, servtab_t *);
272static void FAST_FUNC daytime_dg(int, servtab_t *);
[821]273#endif
[2725]274/* Familiar character generator */
[1765]275#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
[2725]276static void FAST_FUNC chargen_stream(int, servtab_t *);
277static void FAST_FUNC chargen_dg(int, servtab_t *);
[821]278#endif
279
[2725]280struct builtin {
281 /* NB: not necessarily NUL terminated */
282 char bi_service7[7]; /* internally provided service name */
283 uint8_t bi_fork; /* 1 if stream fn should run in child */
284 void (*bi_stream_fn)(int, servtab_t *) FAST_FUNC;
285 void (*bi_dgram_fn)(int, servtab_t *) FAST_FUNC;
286};
287
[821]288static const struct builtin builtins[] = {
[1765]289#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
[2725]290 { "echo", 1, echo_stream, echo_dg },
[821]291#endif
[1765]292#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
[2725]293 { "discard", 1, discard_stream, discard_dg },
[821]294#endif
[2725]295#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
296 { "chargen", 1, chargen_stream, chargen_dg },
297#endif
[1765]298#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
[2725]299 { "time", 0, machtime_stream, machtime_dg },
[821]300#endif
[1765]301#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
[2725]302 { "daytime", 0, daytime_stream, daytime_dg },
[821]303#endif
[2725]304};
305#endif /* INETD_BUILTINS_ENABLED */
306
307struct globals {
308 rlim_t rlim_ofile_cur;
309 struct rlimit rlim_ofile;
310 servtab_t *serv_list;
311 int global_queuelen;
312 int maxsock; /* max fd# in allsock, -1: unknown */
313 /* whenever maxsock grows, prev_maxsock is set to new maxsock,
314 * but if maxsock is set to -1, prev_maxsock is not changed */
315 int prev_maxsock;
316 unsigned max_concurrency;
317 smallint alarm_armed;
318 uid_t real_uid; /* user ID who ran us */
319 const char *config_filename;
320 parser_t *parser;
321 char *default_local_hostname;
[1765]322#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
[2725]323 char *end_ring;
324 char *ring_pos;
325 char ring[128];
[821]326#endif
[2725]327 fd_set allsock;
328 /* Used in next_line(), and as scratch read buffer */
329 char line[256]; /* _at least_ 256, see LINE_SIZE */
330} FIX_ALIASING;
[3621]331#define G (*(struct globals*)bb_common_bufsiz1)
[2725]332enum { LINE_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line) };
333#define rlim_ofile_cur (G.rlim_ofile_cur )
334#define rlim_ofile (G.rlim_ofile )
335#define serv_list (G.serv_list )
336#define global_queuelen (G.global_queuelen)
337#define maxsock (G.maxsock )
338#define prev_maxsock (G.prev_maxsock )
339#define max_concurrency (G.max_concurrency)
340#define alarm_armed (G.alarm_armed )
341#define real_uid (G.real_uid )
342#define config_filename (G.config_filename)
343#define parser (G.parser )
344#define default_local_hostname (G.default_local_hostname)
345#define first_ps_byte (G.first_ps_byte )
346#define last_ps_byte (G.last_ps_byte )
347#define end_ring (G.end_ring )
348#define ring_pos (G.ring_pos )
349#define ring (G.ring )
350#define allsock (G.allsock )
351#define line (G.line )
352#define INIT_G() do { \
[3621]353 setup_common_bufsiz(); \
354 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
[2725]355 rlim_ofile_cur = OPEN_MAX; \
356 global_queuelen = 128; \
357 config_filename = "/etc/inetd.conf"; \
358} while (0)
[821]359
[3232]360#if 1
361# define dbg(...) ((void)0)
362#else
363# define dbg(...) \
364do { \
365 int dbg_fd = open("inetd_debug.log", O_WRONLY | O_CREAT | O_APPEND, 0666); \
366 if (dbg_fd >= 0) { \
367 fdprintf(dbg_fd, "%d: ", getpid()); \
368 fdprintf(dbg_fd, __VA_ARGS__); \
369 close(dbg_fd); \
370 } \
371} while (0)
372#endif
373
[2725]374static void maybe_close(int fd)
375{
[3232]376 if (fd >= 0) {
[2725]377 close(fd);
[3232]378 dbg("closed fd:%d\n", fd);
379 }
[2725]380}
[821]381
[2725]382// TODO: move to libbb?
383static len_and_sockaddr *xzalloc_lsa(int family)
384{
385 len_and_sockaddr *lsa;
386 int sz;
[821]387
[2725]388 sz = sizeof(struct sockaddr_in);
389 if (family == AF_UNIX)
390 sz = sizeof(struct sockaddr_un);
391#if ENABLE_FEATURE_IPV6
392 if (family == AF_INET6)
393 sz = sizeof(struct sockaddr_in6);
394#endif
395 lsa = xzalloc(LSA_LEN_SIZE + sz);
396 lsa->len = sz;
397 lsa->u.sa.sa_family = family;
398 return lsa;
399}
[821]400
[2725]401static void rearm_alarm(void)
[821]402{
[2725]403 if (!alarm_armed) {
404 alarm_armed = 1;
405 alarm(RETRYTIME);
406 }
[821]407}
408
[2725]409static void block_CHLD_HUP_ALRM(sigset_t *m)
[821]410{
[2725]411 sigemptyset(m);
412 sigaddset(m, SIGCHLD);
413 sigaddset(m, SIGHUP);
414 sigaddset(m, SIGALRM);
415 sigprocmask(SIG_BLOCK, m, m); /* old sigmask is stored in m */
[821]416}
417
[2725]418static void restore_sigmask(sigset_t *m)
[821]419{
[2725]420 sigprocmask(SIG_SETMASK, m, NULL);
[821]421}
422
[1765]423#if ENABLE_FEATURE_INETD_RPC
424static void register_rpc(servtab_t *sep)
[821]425{
[1765]426 int n;
427 struct sockaddr_in ir_sin;
428 socklen_t size;
[821]429
[2725]430 size = sizeof(ir_sin);
[1765]431 if (getsockname(sep->se_fd, (struct sockaddr *) &ir_sin, &size) < 0) {
[2725]432 bb_perror_msg("getsockname");
[1765]433 return;
434 }
[821]435
[2725]436 for (n = sep->se_rpcver_lo; n <= sep->se_rpcver_hi; n++) {
437 pmap_unset(sep->se_rpcprog, n);
438 if (!pmap_set(sep->se_rpcprog, n, sep->se_proto_no, ntohs(ir_sin.sin_port)))
439 bb_perror_msg("%s %s: pmap_set(%u,%u,%u,%u)",
440 sep->se_service, sep->se_proto,
441 sep->se_rpcprog, n, sep->se_proto_no, ntohs(ir_sin.sin_port));
[1765]442 }
[821]443}
444
[1765]445static void unregister_rpc(servtab_t *sep)
[821]446{
[1765]447 int n;
[821]448
[2725]449 for (n = sep->se_rpcver_lo; n <= sep->se_rpcver_hi; n++) {
[1765]450 if (!pmap_unset(sep->se_rpcprog, n))
[2725]451 bb_perror_msg("pmap_unset(%u,%u)", sep->se_rpcprog, n);
[1765]452 }
[821]453}
[1765]454#endif /* FEATURE_INETD_RPC */
[821]455
[2725]456static void bump_nofile(void)
[821]457{
[2725]458 enum { FD_CHUNK = 32 };
[1765]459 struct rlimit rl;
[821]460
[2725]461 /* Never fails under Linux (except if you pass it bad arguments) */
462 getrlimit(RLIMIT_NOFILE, &rl);
[1765]463 rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK);
464 rl.rlim_cur = MIN(FD_SETSIZE, rl.rlim_cur + FD_CHUNK);
465 if (rl.rlim_cur <= rlim_ofile_cur) {
[2725]466 bb_error_msg("can't extend file limit, max = %d",
[1765]467 (int) rl.rlim_cur);
[2725]468 return;
[1765]469 }
[821]470
[1765]471 if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
472 bb_perror_msg("setrlimit");
[2725]473 return;
[1765]474 }
[821]475
[1765]476 rlim_ofile_cur = rl.rlim_cur;
[821]477}
478
[2725]479static void remove_fd_from_set(int fd)
[821]480{
[2725]481 if (fd >= 0) {
482 FD_CLR(fd, &allsock);
[3232]483 dbg("stopped listening on fd:%d\n", fd);
[2725]484 maxsock = -1;
[3232]485 dbg("maxsock:%d\n", maxsock);
[2725]486 }
487}
[821]488
[2725]489static void add_fd_to_set(int fd)
490{
491 if (fd >= 0) {
492 FD_SET(fd, &allsock);
[3232]493 dbg("started listening on fd:%d\n", fd);
[2725]494 if (maxsock >= 0 && fd > maxsock) {
495 prev_maxsock = maxsock = fd;
[3232]496 dbg("maxsock:%d\n", maxsock);
[2725]497 if ((rlim_t)fd > rlim_ofile_cur - FD_MARGIN)
498 bump_nofile();
499 }
500 }
501}
502
503static void recalculate_maxsock(void)
504{
505 int fd = 0;
506
507 /* We may have no services, in this case maxsock should still be >= 0
508 * (code elsewhere is not happy with maxsock == -1) */
509 maxsock = 0;
510 while (fd <= prev_maxsock) {
511 if (FD_ISSET(fd, &allsock))
512 maxsock = fd;
513 fd++;
514 }
[3232]515 dbg("recalculated maxsock:%d\n", maxsock);
[2725]516 prev_maxsock = maxsock;
517 if ((rlim_t)maxsock > rlim_ofile_cur - FD_MARGIN)
518 bump_nofile();
519}
520
521static void prepare_socket_fd(servtab_t *sep)
522{
523 int r, fd;
524
525 fd = socket(sep->se_family, sep->se_socktype, 0);
526 if (fd < 0) {
527 bb_perror_msg("socket");
[1765]528 return;
529 }
[2725]530 setsockopt_reuseaddr(fd);
[821]531
[1765]532#if ENABLE_FEATURE_INETD_RPC
[2725]533 if (is_rpc_service(sep)) {
[1765]534 struct passwd *pwd;
[821]535
[2725]536 /* zero out the port for all RPC services; let bind()
537 * find one. */
[3232]538 set_nport(&sep->se_lsa->u.sa, 0);
[821]539
[2725]540 /* for RPC services, attempt to use a reserved port
541 * if they are going to be running as root. */
542 if (real_uid == 0 && sep->se_family == AF_INET
543 && (pwd = getpwnam(sep->se_user)) != NULL
544 && pwd->pw_uid == 0
545 ) {
546 r = bindresvport(fd, &sep->se_lsa->u.sin);
547 } else {
548 r = bind(fd, &sep->se_lsa->u.sa, sep->se_lsa->len);
[1765]549 }
[2725]550 if (r == 0) {
551 int saveerrno = errno;
552 /* update lsa with port# */
553 getsockname(fd, &sep->se_lsa->u.sa, &sep->se_lsa->len);
554 errno = saveerrno;
555 }
[1765]556 } else
[821]557#endif
[2725]558 {
559 if (sep->se_family == AF_UNIX) {
560 struct sockaddr_un *sun;
561 sun = (struct sockaddr_un*)&(sep->se_lsa->u.sa);
562 unlink(sun->sun_path);
563 }
564 r = bind(fd, &sep->se_lsa->u.sa, sep->se_lsa->len);
565 }
[1765]566 if (r < 0) {
[2725]567 bb_perror_msg("%s/%s: bind",
568 sep->se_service, sep->se_proto);
569 close(fd);
570 rearm_alarm();
[1765]571 return;
[821]572 }
[3232]573
574 if (sep->se_socktype == SOCK_STREAM) {
[2725]575 listen(fd, global_queuelen);
[3232]576 dbg("new sep->se_fd:%d (stream)\n", fd);
577 } else {
578 dbg("new sep->se_fd:%d (!stream)\n", fd);
579 }
[821]580
[2725]581 add_fd_to_set(fd);
582 sep->se_fd = fd;
[821]583}
584
[2725]585static int reopen_config_file(void)
[821]586{
[2725]587 free(default_local_hostname);
588 default_local_hostname = xstrdup("*");
589 if (parser != NULL)
590 config_close(parser);
591 parser = config_open(config_filename);
592 return (parser != NULL);
[821]593}
594
[2725]595static void close_config_file(void)
[821]596{
[2725]597 if (parser) {
598 config_close(parser);
599 parser = NULL;
[1765]600 }
[2725]601}
[821]602
[2725]603static void free_servtab_strings(servtab_t *cp)
604{
605 int i;
[821]606
[2725]607 free(cp->se_local_hostname);
608 free(cp->se_service);
609 free(cp->se_proto);
610 free(cp->se_user);
611 free(cp->se_group);
612 free(cp->se_lsa); /* not a string in fact */
613 free(cp->se_program);
614 for (i = 0; i < MAXARGV; i++)
615 free(cp->se_argv[i]);
[821]616}
617
618static servtab_t *new_servtab(void)
619{
[2725]620 servtab_t *newtab = xzalloc(sizeof(servtab_t));
621 newtab->se_fd = -1; /* paranoia */
622 return newtab;
[821]623}
624
[2725]625static servtab_t *dup_servtab(servtab_t *sep)
[821]626{
[1765]627 servtab_t *newtab;
628 int argc;
[821]629
[1765]630 newtab = new_servtab();
[2725]631 *newtab = *sep; /* struct copy */
632 /* deep-copying strings */
633 newtab->se_service = xstrdup(newtab->se_service);
634 newtab->se_proto = xstrdup(newtab->se_proto);
635 newtab->se_user = xstrdup(newtab->se_user);
636 newtab->se_group = xstrdup(newtab->se_group);
637 newtab->se_program = xstrdup(newtab->se_program);
[1765]638 for (argc = 0; argc <= MAXARGV; argc++)
[2725]639 newtab->se_argv[argc] = xstrdup(newtab->se_argv[argc]);
640 /* NB: se_fd, se_hostaddr and se_next are always
641 * overwrittend by callers, so we don't bother resetting them
642 * to NULL/0/-1 etc */
[821]643
[1765]644 return newtab;
[821]645}
646
[2725]647/* gcc generates much more code if this is inlined */
[3621]648static NOINLINE servtab_t *parse_one_line(void)
[821]649{
[1765]650 int argc;
[2725]651 char *token[6+MAXARGV];
652 char *p, *arg;
[1765]653 char *hostdelim;
[2725]654 servtab_t *sep;
[1765]655 servtab_t *nsep;
[2725]656 new:
[1765]657 sep = new_servtab();
658 more:
[2725]659 argc = config_read(parser, token, 6+MAXARGV, 1, "# \t", PARSE_NORMAL);
660 if (!argc) {
661 free(sep);
[1765]662 return NULL;
663 }
[821]664
[2725]665 /* [host:]service socktype proto wait user[:group] prog [args] */
666 /* Check for "host:...." line */
667 arg = token[0];
[1765]668 hostdelim = strrchr(arg, ':');
669 if (hostdelim) {
670 *hostdelim = '\0';
[2725]671 sep->se_local_hostname = xstrdup(arg);
[1765]672 arg = hostdelim + 1;
[2725]673 if (*arg == '\0' && argc == 1) {
674 /* Line has just "host:", change the
675 * default host for the following lines. */
676 free(default_local_hostname);
677 default_local_hostname = sep->se_local_hostname;
[3621]678 /*sep->se_local_hostname = NULL; - redundant */
679 /* (we'll overwrite this field anyway) */
[2725]680 goto more;
[1765]681 }
682 } else
[2725]683 sep->se_local_hostname = xstrdup(default_local_hostname);
[821]684
[2725]685 /* service socktype proto wait user[:group] prog [args] */
686 sep->se_service = xstrdup(arg);
[821]687
[2725]688 /* socktype proto wait user[:group] prog [args] */
689 if (argc < 6) {
690 parse_err:
691 bb_error_msg("parse error on line %u, line is ignored",
692 parser->lineno);
693 /* Just "goto more" can make sep to carry over e.g.
694 * "rpc"-ness (by having se_rpcver_lo != 0).
695 * We will be more paranoid: */
[3621]696 free_servtab_strings(sep);
[2725]697 free(sep);
698 goto new;
699 }
[821]700
[2725]701 {
702 static const int8_t SOCK_xxx[] ALIGN1 = {
703 -1,
704 SOCK_STREAM, SOCK_DGRAM, SOCK_RDM,
705 SOCK_SEQPACKET, SOCK_RAW
706 };
707 sep->se_socktype = SOCK_xxx[1 + index_in_strings(
708 "stream""\0" "dgram""\0" "rdm""\0"
709 "seqpacket""\0" "raw""\0"
710 , token[1])];
711 }
[1765]712
[2725]713 /* {unix,[rpc/]{tcp,udp}[6]} wait user[:group] prog [args] */
714 sep->se_proto = arg = xstrdup(token[2]);
715 if (strcmp(arg, "unix") == 0) {
[1765]716 sep->se_family = AF_UNIX;
717 } else {
[2725]718 char *six;
[1765]719 sep->se_family = AF_INET;
[2725]720 six = last_char_is(arg, '6');
721 if (six) {
[1765]722#if ENABLE_FEATURE_IPV6
[2725]723 *six = '\0';
[1765]724 sep->se_family = AF_INET6;
[821]725#else
[2725]726 bb_error_msg("%s: no support for IPv6", sep->se_proto);
727 goto parse_err;
[821]728#endif
[2725]729 }
[3621]730 if (is_prefixed_with(arg, "rpc/")) {
[1765]731#if ENABLE_FEATURE_INETD_RPC
[2725]732 unsigned n;
733 arg += 4;
[1765]734 p = strchr(sep->se_service, '/');
[2725]735 if (p == NULL) {
736 bb_error_msg("no rpc version: '%s'", sep->se_service);
737 goto parse_err;
[1765]738 }
739 *p++ = '\0';
[2725]740 n = bb_strtou(p, &p, 10);
741 if (n > INT_MAX) {
742 bad_ver_spec:
743 bb_error_msg("bad rpc version");
744 goto parse_err;
[1765]745 }
[2725]746 sep->se_rpcver_lo = sep->se_rpcver_hi = n;
747 if (*p == '-') {
748 p++;
749 n = bb_strtou(p, &p, 10);
750 if (n > INT_MAX || (int)n < sep->se_rpcver_lo)
751 goto bad_ver_spec;
752 sep->se_rpcver_hi = n;
753 }
754 if (*p != '\0')
755 goto bad_ver_spec;
[821]756#else
[2725]757 bb_error_msg("no support for rpc services");
758 goto parse_err;
[821]759#endif
[1765]760 }
[2725]761 /* we don't really need getprotobyname()! */
762 if (strcmp(arg, "tcp") == 0)
763 sep->se_proto_no = IPPROTO_TCP; /* = 6 */
764 if (strcmp(arg, "udp") == 0)
765 sep->se_proto_no = IPPROTO_UDP; /* = 17 */
766 if (six)
767 *six = '6';
768 if (!sep->se_proto_no) /* not tcp/udp?? */
769 goto parse_err;
[821]770 }
771
[2725]772 /* [no]wait[.max] user[:group] prog [args] */
773 arg = token[3];
774 sep->se_max = max_concurrency;
775 p = strchr(arg, '.');
776 if (p) {
777 *p++ = '\0';
778 sep->se_max = bb_strtou(p, NULL, 10);
779 if (errno)
780 goto parse_err;
[1765]781 }
[2725]782 sep->se_wait = (arg[0] != 'n' || arg[1] != 'o');
783 if (!sep->se_wait) /* "no" seen */
784 arg += 2;
785 if (strcmp(arg, "wait") != 0)
786 goto parse_err;
787
788 /* user[:group] prog [args] */
789 sep->se_user = xstrdup(token[4]);
[1765]790 arg = strchr(sep->se_user, '.');
791 if (arg == NULL)
792 arg = strchr(sep->se_user, ':');
793 if (arg) {
794 *arg++ = '\0';
795 sep->se_group = xstrdup(arg);
796 }
[821]797
[2725]798 /* prog [args] */
799 sep->se_program = xstrdup(token[5]);
800#ifdef INETD_BUILTINS_ENABLED
801 if (strcmp(sep->se_program, "internal") == 0
802 && strlen(sep->se_service) <= 7
803 && (sep->se_socktype == SOCK_STREAM
804 || sep->se_socktype == SOCK_DGRAM)
805 ) {
806 unsigned i;
807 for (i = 0; i < ARRAY_SIZE(builtins); i++)
808 if (strncmp(builtins[i].bi_service7, sep->se_service, 7) == 0)
809 goto found_bi;
810 bb_error_msg("unknown internal service %s", sep->se_service);
811 goto parse_err;
812 found_bi:
813 sep->se_builtin = &builtins[i];
814 /* stream builtins must be "nowait", dgram must be "wait" */
815 if (sep->se_wait != (sep->se_socktype == SOCK_DGRAM))
816 goto parse_err;
[1765]817 }
[821]818#endif
[1765]819 argc = 0;
[3621]820 while (argc < MAXARGV && (arg = token[6+argc]) != NULL)
[2725]821 sep->se_argv[argc++] = xstrdup(arg);
822 /* Some inetd.conf files have no argv's, not even argv[0].
823 * Fix them up.
824 * (Technically, programs can be execed with argv[0] = NULL,
825 * but many programs do not like that at all) */
826 if (argc == 0)
827 sep->se_argv[0] = xstrdup(sep->se_program);
828
829 /* catch mixups. "<service> stream udp ..." == wtf */
830 if (sep->se_socktype == SOCK_STREAM) {
831 if (sep->se_proto_no == IPPROTO_UDP)
832 goto parse_err;
[1765]833 }
[2725]834 if (sep->se_socktype == SOCK_DGRAM) {
835 if (sep->se_proto_no == IPPROTO_TCP)
836 goto parse_err;
837 }
[821]838
[3621]839 //bb_error_msg(
840 // "ENTRY[%s][%s][%s][%d][%d][%d][%d][%d][%s][%s][%s]",
841 // sep->se_local_hostname, sep->se_service, sep->se_proto, sep->se_wait, sep->se_proto_no,
842 // sep->se_max, sep->se_count, sep->se_time, sep->se_user, sep->se_group, sep->se_program);
[821]843
[2725]844 /* check if the hostname specifier is a comma separated list
845 * of hostnames. we'll make new entries for each address. */
846 while ((hostdelim = strrchr(sep->se_local_hostname, ',')) != NULL) {
847 nsep = dup_servtab(sep);
848 /* NUL terminate the hostname field of the existing entry,
849 * and make a dup for the new entry. */
[1765]850 *hostdelim++ = '\0';
[2725]851 nsep->se_local_hostname = xstrdup(hostdelim);
[1765]852 nsep->se_next = sep->se_next;
853 sep->se_next = nsep;
854 }
[821]855
[2725]856 /* was doing it here: */
857 /* DNS resolution, create copies for each IP address */
858 /* IPv6-ization destroyed it :( */
[821]859
[1765]860 return sep;
[821]861}
862
[2725]863static servtab_t *insert_in_servlist(servtab_t *cp)
[821]864{
[1765]865 servtab_t *sep;
866 sigset_t omask;
[821]867
[1765]868 sep = new_servtab();
[2725]869 *sep = *cp; /* struct copy */
[1765]870 sep->se_fd = -1;
871#if ENABLE_FEATURE_INETD_RPC
872 sep->se_rpcprog = -1;
[821]873#endif
[2725]874 block_CHLD_HUP_ALRM(&omask);
875 sep->se_next = serv_list;
876 serv_list = sep;
877 restore_sigmask(&omask);
[1765]878 return sep;
[821]879}
880
[2725]881static int same_serv_addr_proto(servtab_t *old, servtab_t *new)
[821]882{
[2725]883 if (strcmp(old->se_local_hostname, new->se_local_hostname) != 0)
884 return 0;
[1765]885 if (strcmp(old->se_service, new->se_service) != 0)
886 return 0;
887 if (strcmp(old->se_proto, new->se_proto) != 0)
888 return 0;
889 return 1;
[821]890}
891
[2725]892static void reread_config_file(int sig UNUSED_PARAM)
[821]893{
[1765]894 servtab_t *sep, *cp, **sepp;
[2725]895 len_and_sockaddr *lsa;
[1765]896 sigset_t omask;
[2725]897 unsigned n;
898 uint16_t port;
899 int save_errno = errno;
[821]900
[2725]901 if (!reopen_config_file())
902 goto ret;
903 for (sep = serv_list; sep; sep = sep->se_next)
[1765]904 sep->se_checked = 0;
[2725]905
906 goto first_line;
907 while (1) {
908 if (cp == NULL) {
909 first_line:
910 cp = parse_one_line();
911 if (cp == NULL)
[1765]912 break;
[2725]913 }
914 for (sep = serv_list; sep; sep = sep->se_next)
915 if (same_serv_addr_proto(sep, cp))
916 goto equal_servtab;
917 /* not an "equal" servtab */
918 sep = insert_in_servlist(cp);
919 goto after_check;
920 equal_servtab:
921 {
[1765]922 int i;
923
[2725]924 block_CHLD_HUP_ALRM(&omask);
925#if ENABLE_FEATURE_INETD_RPC
926 if (is_rpc_service(sep))
927 unregister_rpc(sep);
928 sep->se_rpcver_lo = cp->se_rpcver_lo;
929 sep->se_rpcver_hi = cp->se_rpcver_hi;
[821]930#endif
[2725]931 if (cp->se_wait == 0) {
932 /* New config says "nowait". If old one
933 * was "wait", we currently may be waiting
934 * for a child (and not accepting connects).
935 * Stop waiting, start listening again.
936 * (if it's not true, this op is harmless) */
937 add_fd_to_set(sep->se_fd);
938 }
939 sep->se_wait = cp->se_wait;
940 sep->se_max = cp->se_max;
941 /* string fields need more love - we don't want to leak them */
942#define SWAP(type, a, b) do { type c = (type)a; a = (type)b; b = (type)c; } while (0)
943 SWAP(char*, sep->se_user, cp->se_user);
944 SWAP(char*, sep->se_group, cp->se_group);
945 SWAP(char*, sep->se_program, cp->se_program);
[1765]946 for (i = 0; i < MAXARGV; i++)
[2725]947 SWAP(char*, sep->se_argv[i], cp->se_argv[i]);
[821]948#undef SWAP
[2725]949 restore_sigmask(&omask);
950 free_servtab_strings(cp);
[1765]951 }
[2725]952 after_check:
953 /* cp->string_fields are consumed by insert_in_servlist()
954 * or freed at this point, cp itself is not yet freed. */
[1765]955 sep->se_checked = 1;
[821]956
[2725]957 /* create new len_and_sockaddr */
[1765]958 switch (sep->se_family) {
[2725]959 struct sockaddr_un *sun;
[1765]960 case AF_UNIX:
[2725]961 lsa = xzalloc_lsa(AF_UNIX);
962 sun = (struct sockaddr_un*)&lsa->u.sa;
963 safe_strncpy(sun->sun_path, sep->se_service, sizeof(sun->sun_path));
[1765]964 break;
[821]965
[2725]966 default: /* case AF_INET, case AF_INET6 */
967 n = bb_strtou(sep->se_service, NULL, 10);
[1765]968#if ENABLE_FEATURE_INETD_RPC
[2725]969 if (is_rpc_service(sep)) {
970 sep->se_rpcprog = n;
971 if (errno) { /* se_service is not numeric */
972 struct rpcent *rp = getrpcbyname(sep->se_service);
973 if (rp == NULL) {
[1765]974 bb_error_msg("%s: unknown rpc service", sep->se_service);
[2725]975 goto next_cp;
[1765]976 }
977 sep->se_rpcprog = rp->r_number;
978 }
979 if (sep->se_fd == -1)
[2725]980 prepare_socket_fd(sep);
[1765]981 if (sep->se_fd != -1)
982 register_rpc(sep);
[2725]983 goto next_cp;
984 }
[821]985#endif
[2725]986 /* what port to listen on? */
987 port = htons(n);
988 if (errno || n > 0xffff) { /* se_service is not numeric */
989 char protoname[4];
990 struct servent *sp;
991 /* can result only in "tcp" or "udp": */
992 safe_strncpy(protoname, sep->se_proto, 4);
993 sp = getservbyname(sep->se_service, protoname);
994 if (sp == NULL) {
995 bb_error_msg("%s/%s: unknown service",
996 sep->se_service, sep->se_proto);
997 goto next_cp;
[1765]998 }
[2725]999 port = sp->s_port;
[1765]1000 }
[2725]1001 if (LONE_CHAR(sep->se_local_hostname, '*')) {
1002 lsa = xzalloc_lsa(sep->se_family);
[3232]1003 set_nport(&lsa->u.sa, port);
[2725]1004 } else {
1005 lsa = host_and_af2sockaddr(sep->se_local_hostname,
1006 ntohs(port), sep->se_family);
1007 if (!lsa) {
1008 bb_error_msg("%s/%s: unknown host '%s'",
1009 sep->se_service, sep->se_proto,
1010 sep->se_local_hostname);
1011 goto next_cp;
[1765]1012 }
1013 }
1014 break;
[2725]1015 } /* end of "switch (sep->se_family)" */
[821]1016
[2725]1017 /* did lsa change? Then close/open */
1018 if (sep->se_lsa == NULL
1019 || lsa->len != sep->se_lsa->len
1020 || memcmp(&lsa->u.sa, &sep->se_lsa->u.sa, lsa->len) != 0
1021 ) {
1022 remove_fd_from_set(sep->se_fd);
1023 maybe_close(sep->se_fd);
1024 free(sep->se_lsa);
1025 sep->se_lsa = lsa;
1026 sep->se_fd = -1;
[1765]1027 } else {
[2725]1028 free(lsa);
[821]1029 }
[2725]1030 if (sep->se_fd == -1)
1031 prepare_socket_fd(sep);
1032 next_cp:
1033 sep = cp->se_next;
1034 free(cp);
1035 cp = sep;
1036 } /* end of "while (1) parse lines" */
1037 close_config_file();
1038
1039 /* Purge anything not looked at above - these are stale entries,
1040 * new config file doesnt have them. */
1041 block_CHLD_HUP_ALRM(&omask);
1042 sepp = &serv_list;
[3232]1043 while ((sep = *sepp) != NULL) {
[1765]1044 if (sep->se_checked) {
1045 sepp = &sep->se_next;
1046 continue;
1047 }
1048 *sepp = sep->se_next;
[2725]1049 remove_fd_from_set(sep->se_fd);
1050 maybe_close(sep->se_fd);
[1765]1051#if ENABLE_FEATURE_INETD_RPC
[2725]1052 if (is_rpc_service(sep))
[1765]1053 unregister_rpc(sep);
1054#endif
1055 if (sep->se_family == AF_UNIX)
[2725]1056 unlink(sep->se_service);
1057 free_servtab_strings(sep);
[1765]1058 free(sep);
[821]1059 }
[2725]1060 restore_sigmask(&omask);
1061 ret:
1062 errno = save_errno;
[821]1063}
1064
[2725]1065static void reap_child(int sig UNUSED_PARAM)
[821]1066{
[1765]1067 pid_t pid;
[2725]1068 int status;
[1765]1069 servtab_t *sep;
[2725]1070 int save_errno = errno;
[821]1071
[1765]1072 for (;;) {
[2725]1073 pid = wait_any_nohang(&status);
[1765]1074 if (pid <= 0)
1075 break;
[2725]1076 for (sep = serv_list; sep; sep = sep->se_next) {
1077 if (sep->se_wait != pid)
1078 continue;
1079 /* One of our "wait" services */
1080 if (WIFEXITED(status) && WEXITSTATUS(status))
1081 bb_error_msg("%s: exit status %u",
1082 sep->se_program, WEXITSTATUS(status));
1083 else if (WIFSIGNALED(status))
1084 bb_error_msg("%s: exit signal %u",
1085 sep->se_program, WTERMSIG(status));
1086 sep->se_wait = 1;
1087 add_fd_to_set(sep->se_fd);
1088 break;
1089 }
[1765]1090 }
1091 errno = save_errno;
[821]1092}
1093
[2725]1094static void retry_network_setup(int sig UNUSED_PARAM)
[821]1095{
[2725]1096 int save_errno = errno;
[1765]1097 servtab_t *sep;
[821]1098
[2725]1099 alarm_armed = 0;
1100 for (sep = serv_list; sep; sep = sep->se_next) {
[1765]1101 if (sep->se_fd == -1) {
[2725]1102 prepare_socket_fd(sep);
[1765]1103#if ENABLE_FEATURE_INETD_RPC
[2725]1104 if (sep->se_fd != -1 && is_rpc_service(sep))
1105 register_rpc(sep);
[821]1106#endif
[1765]1107 }
[821]1108 }
[2725]1109 errno = save_errno;
[821]1110}
1111
[2725]1112static void clean_up_and_exit(int sig UNUSED_PARAM)
[821]1113{
[1765]1114 servtab_t *sep;
[821]1115
[1765]1116 /* XXX signal race walking sep list */
[2725]1117 for (sep = serv_list; sep; sep = sep->se_next) {
[1765]1118 if (sep->se_fd == -1)
1119 continue;
[821]1120
[1765]1121 switch (sep->se_family) {
1122 case AF_UNIX:
[2725]1123 unlink(sep->se_service);
[1765]1124 break;
[2725]1125 default: /* case AF_INET, AF_INET6 */
[1765]1126#if ENABLE_FEATURE_INETD_RPC
[2725]1127 if (sep->se_wait == 1 && is_rpc_service(sep))
[1765]1128 unregister_rpc(sep); /* XXX signal race */
[821]1129#endif
[1765]1130 break;
1131 }
[2725]1132 if (ENABLE_FEATURE_CLEAN_UP)
1133 close(sep->se_fd);
[821]1134 }
[3232]1135 remove_pidfile(CONFIG_PID_FILE_PATH "/inetd.pid");
[2725]1136 exit(EXIT_SUCCESS);
[821]1137}
1138
[2725]1139int inetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1140int inetd_main(int argc UNUSED_PARAM, char **argv)
[821]1141{
[2725]1142 struct sigaction sa, saved_pipe_handler;
1143 servtab_t *sep, *sep2;
[1765]1144 struct passwd *pwd;
[2725]1145 struct group *grp = grp; /* for compiler */
[1765]1146 int opt;
1147 pid_t pid;
[2725]1148 sigset_t omask;
[821]1149
[2725]1150 INIT_G();
[821]1151
[2725]1152 real_uid = getuid();
1153 if (real_uid != 0) /* run by non-root user */
[1765]1154 config_filename = NULL;
[821]1155
[2725]1156 opt_complementary = "R+:q+"; /* -q N, -R N */
1157 opt = getopt32(argv, "R:feq:", &max_concurrency, &global_queuelen);
[1765]1158 argv += optind;
[2725]1159 //argc -= optind;
1160 if (argv[0])
[1765]1161 config_filename = argv[0];
1162 if (config_filename == NULL)
[2725]1163 bb_error_msg_and_die("non-root must specify config file");
[1765]1164 if (!(opt & 2))
1165 bb_daemonize_or_rexec(0, argv - optind);
1166 else
1167 bb_sanitize_stdio();
[2725]1168 if (!(opt & 4)) {
1169 /* LOG_NDELAY: connect to syslog daemon NOW.
1170 * Otherwise, we may open syslog socket
1171 * in vforked child, making opened fds and syslog()
1172 * internal state inconsistent.
1173 * This was observed to leak file descriptors. */
1174 openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
1175 logmode = LOGMODE_SYSLOG;
1176 }
[1765]1177
[2725]1178 if (real_uid == 0) {
1179 /* run by root, ensure groups vector gets trashed */
[1765]1180 gid_t gid = getgid();
1181 setgroups(1, &gid);
[821]1182 }
1183
[3232]1184 write_pidfile(CONFIG_PID_FILE_PATH "/inetd.pid");
[821]1185
[2725]1186 /* never fails under Linux (except if you pass it bad arguments) */
1187 getrlimit(RLIMIT_NOFILE, &rlim_ofile);
1188 rlim_ofile_cur = rlim_ofile.rlim_cur;
1189 if (rlim_ofile_cur == RLIM_INFINITY) /* ! */
1190 rlim_ofile_cur = OPEN_MAX;
[821]1191
[2725]1192 memset(&sa, 0, sizeof(sa));
1193 /*sigemptyset(&sa.sa_mask); - memset did it */
[1765]1194 sigaddset(&sa.sa_mask, SIGALRM);
1195 sigaddset(&sa.sa_mask, SIGCHLD);
1196 sigaddset(&sa.sa_mask, SIGHUP);
[3232]1197//FIXME: explain why no SA_RESTART
1198//FIXME: retry_network_setup is unsafe to run in signal handler (many reasons)!
[2725]1199 sa.sa_handler = retry_network_setup;
1200 sigaction_set(SIGALRM, &sa);
[3232]1201//FIXME: reread_config_file is unsafe to run in signal handler(many reasons)!
[2725]1202 sa.sa_handler = reread_config_file;
1203 sigaction_set(SIGHUP, &sa);
[3232]1204//FIXME: reap_child is unsafe to run in signal handler (uses stdio)!
[2725]1205 sa.sa_handler = reap_child;
1206 sigaction_set(SIGCHLD, &sa);
[3232]1207//FIXME: clean_up_and_exit is unsafe to run in signal handler (uses stdio)!
[2725]1208 sa.sa_handler = clean_up_and_exit;
1209 sigaction_set(SIGTERM, &sa);
1210 sa.sa_handler = clean_up_and_exit;
1211 sigaction_set(SIGINT, &sa);
[1765]1212 sa.sa_handler = SIG_IGN;
[2725]1213 sigaction(SIGPIPE, &sa, &saved_pipe_handler);
[821]1214
[2725]1215 reread_config_file(SIGHUP); /* load config from file */
[821]1216
[1765]1217 for (;;) {
[2725]1218 int ready_fd_cnt;
1219 int ctrl, accepted_fd, new_udp_fd;
[1765]1220 fd_set readable;
[821]1221
[2725]1222 if (maxsock < 0)
1223 recalculate_maxsock();
[821]1224
[2725]1225 readable = allsock; /* struct copy */
1226 /* if there are no fds to wait on, we will block
1227 * until signal wakes us up (maxsock == 0, but readable
1228 * never contains fds 0 and 1...) */
1229 ready_fd_cnt = select(maxsock + 1, &readable, NULL, NULL, NULL);
1230 if (ready_fd_cnt < 0) {
1231 if (errno != EINTR) {
[1765]1232 bb_perror_msg("select");
1233 sleep(1);
1234 }
1235 continue;
1236 }
[3232]1237 dbg("ready_fd_cnt:%d\n", ready_fd_cnt);
[821]1238
[2725]1239 for (sep = serv_list; ready_fd_cnt && sep; sep = sep->se_next) {
[1765]1240 if (sep->se_fd == -1 || !FD_ISSET(sep->se_fd, &readable))
1241 continue;
[821]1242
[3232]1243 dbg("ready fd:%d\n", sep->se_fd);
[2725]1244 ready_fd_cnt--;
1245 ctrl = sep->se_fd;
1246 accepted_fd = -1;
1247 new_udp_fd = -1;
1248 if (!sep->se_wait) {
1249 if (sep->se_socktype == SOCK_STREAM) {
1250 ctrl = accepted_fd = accept(sep->se_fd, NULL, NULL);
[3232]1251 dbg("accepted_fd:%d\n", accepted_fd);
[2725]1252 if (ctrl < 0) {
1253 if (errno != EINTR)
1254 bb_perror_msg("accept (for %s)", sep->se_service);
[1765]1255 continue;
[2725]1256 }
[1765]1257 }
[2725]1258 /* "nowait" udp */
1259 if (sep->se_socktype == SOCK_DGRAM
1260 && sep->se_family != AF_UNIX
1261 ) {
1262/* How udp "nowait" works:
1263 * child peeks at (received and buffered by kernel) UDP packet,
1264 * performs connect() on the socket so that it is linked only
1265 * to this peer. But this also affects parent, because descriptors
1266 * are shared after fork() a-la dup(). When parent performs
1267 * select(), it will see this descriptor connected to the peer (!)
1268 * and still readable, will act on it and mess things up
1269 * (can create many copies of same child, etc).
1270 * Parent must create and use new socket instead. */
1271 new_udp_fd = socket(sep->se_family, SOCK_DGRAM, 0);
[3232]1272 dbg("new_udp_fd:%d\n", new_udp_fd);
[2725]1273 if (new_udp_fd < 0) { /* error: eat packet, forget about it */
1274 udp_err:
1275 recv(sep->se_fd, line, LINE_SIZE, MSG_DONTWAIT);
[1765]1276 continue;
1277 }
[2725]1278 setsockopt_reuseaddr(new_udp_fd);
[3232]1279 /* TODO: better do bind after fork in parent,
[2725]1280 * so that we don't have two wildcard bound sockets
1281 * even for a brief moment? */
1282 if (bind(new_udp_fd, &sep->se_lsa->u.sa, sep->se_lsa->len) < 0) {
[3232]1283 dbg("bind(new_udp_fd) failed\n");
[2725]1284 close(new_udp_fd);
1285 goto udp_err;
[1765]1286 }
[3232]1287 dbg("bind(new_udp_fd) succeeded\n");
[1765]1288 }
[2725]1289 }
[821]1290
[2725]1291 block_CHLD_HUP_ALRM(&omask);
[1765]1292 pid = 0;
[2725]1293#ifdef INETD_BUILTINS_ENABLED
1294 /* do we need to fork? */
1295 if (sep->se_builtin == NULL
1296 || (sep->se_socktype == SOCK_STREAM
1297 && sep->se_builtin->bi_fork))
[821]1298#endif
[1765]1299 {
[2725]1300 if (sep->se_max != 0) {
1301 if (++sep->se_count == 1)
1302 sep->se_time = monotonic_sec();
1303 else if (sep->se_count >= sep->se_max) {
1304 unsigned now = monotonic_sec();
1305 /* did we accumulate se_max connects too quickly? */
1306 if (now - sep->se_time <= CNT_INTERVAL) {
1307 bb_error_msg("%s/%s: too many connections, pausing",
1308 sep->se_service, sep->se_proto);
1309 remove_fd_from_set(sep->se_fd);
1310 close(sep->se_fd);
1311 sep->se_fd = -1;
1312 sep->se_count = 0;
1313 rearm_alarm(); /* will revive it in RETRYTIME sec */
1314 restore_sigmask(&omask);
[3232]1315 maybe_close(new_udp_fd);
[2725]1316 maybe_close(accepted_fd);
1317 continue; /* -> check next fd in fd set */
[1765]1318 }
1319 sep->se_count = 0;
1320 }
1321 }
[2725]1322 /* on NOMMU, streamed chargen
1323 * builtin wouldn't work, but it is
1324 * not allowed on NOMMU (ifdefed out) */
1325#ifdef INETD_BUILTINS_ENABLED
1326 if (BB_MMU && sep->se_builtin)
1327 pid = fork();
1328 else
1329#endif
1330 pid = vfork();
1331
1332 if (pid < 0) { /* fork error */
1333 bb_perror_msg("vfork"+1);
1334 sleep(1);
1335 restore_sigmask(&omask);
[3232]1336 maybe_close(new_udp_fd);
[2725]1337 maybe_close(accepted_fd);
1338 continue; /* -> check next fd in fd set */
1339 }
1340 if (pid == 0)
1341 pid--; /* -1: "we did fork and we are child" */
[1765]1342 }
[3232]1343 /* if pid == 0 here, we didn't fork */
[2725]1344
1345 if (pid > 0) { /* parent */
1346 if (sep->se_wait) {
[3232]1347 /* wait: we passed socket to child,
[2725]1348 * will wait for child to terminate */
1349 sep->se_wait = pid;
1350 remove_fd_from_set(sep->se_fd);
1351 }
1352 if (new_udp_fd >= 0) {
1353 /* udp nowait: child connected the socket,
1354 * we created and will use new, unconnected one */
1355 xmove_fd(new_udp_fd, sep->se_fd);
[3232]1356 dbg("moved new_udp_fd:%d to sep->se_fd:%d\n", new_udp_fd, sep->se_fd);
[2725]1357 }
1358 restore_sigmask(&omask);
1359 maybe_close(accepted_fd);
1360 continue; /* -> check next fd in fd set */
[821]1361 }
[2725]1362
[3232]1363 /* we are either child or didn't fork at all */
[2725]1364#ifdef INETD_BUILTINS_ENABLED
1365 if (sep->se_builtin) {
[3232]1366 if (pid) { /* "pid" is -1: we did fork */
[2725]1367 close(sep->se_fd); /* listening socket */
[3232]1368 dbg("closed sep->se_fd:%d\n", sep->se_fd);
[2725]1369 logmode = LOGMODE_NONE; /* make xwrite etc silent */
1370 }
1371 restore_sigmask(&omask);
1372 if (sep->se_socktype == SOCK_STREAM)
1373 sep->se_builtin->bi_stream_fn(ctrl, sep);
1374 else
1375 sep->se_builtin->bi_dgram_fn(ctrl, sep);
[3232]1376 if (pid) /* we did fork */
[2725]1377 _exit(EXIT_FAILURE);
1378 maybe_close(accepted_fd);
1379 continue; /* -> check next fd in fd set */
[1765]1380 }
[821]1381#endif
[2725]1382 /* child */
1383 setsid();
1384 /* "nowait" udp */
1385 if (new_udp_fd >= 0) {
[3232]1386 len_and_sockaddr *lsa;
1387 int r;
1388
1389 close(new_udp_fd);
1390 dbg("closed new_udp_fd:%d\n", new_udp_fd);
1391 lsa = xzalloc_lsa(sep->se_family);
[2725]1392 /* peek at the packet and remember peer addr */
[3232]1393 r = recvfrom(ctrl, NULL, 0, MSG_PEEK|MSG_DONTWAIT,
[2725]1394 &lsa->u.sa, &lsa->len);
1395 if (r < 0)
1396 goto do_exit1;
1397 /* make this socket "connected" to peer addr:
1398 * only packets from this peer will be recv'ed,
1399 * and bare write()/send() will work on it */
1400 connect(ctrl, &lsa->u.sa, lsa->len);
[3232]1401 dbg("connected ctrl:%d to remote peer\n", ctrl);
[2725]1402 free(lsa);
1403 }
1404 /* prepare env and exec program */
1405 pwd = getpwnam(sep->se_user);
1406 if (pwd == NULL) {
1407 bb_error_msg("%s: no such %s", sep->se_user, "user");
1408 goto do_exit1;
1409 }
1410 if (sep->se_group && (grp = getgrnam(sep->se_group)) == NULL) {
1411 bb_error_msg("%s: no such %s", sep->se_group, "group");
1412 goto do_exit1;
1413 }
1414 if (real_uid != 0 && real_uid != pwd->pw_uid) {
1415 /* a user running private inetd */
1416 bb_error_msg("non-root must run services as himself");
1417 goto do_exit1;
1418 }
[3232]1419 if (pwd->pw_uid != 0) {
[2725]1420 if (sep->se_group)
1421 pwd->pw_gid = grp->gr_gid;
1422 /* initgroups, setgid, setuid: */
1423 change_identity(pwd);
1424 } else if (sep->se_group) {
1425 xsetgid(grp->gr_gid);
1426 setgroups(1, &grp->gr_gid);
1427 }
1428 if (rlim_ofile.rlim_cur != rlim_ofile_cur)
1429 if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0)
1430 bb_perror_msg("setrlimit");
1431
1432 /* closelog(); - WRONG. we are after vfork,
1433 * this may confuse syslog() internal state.
1434 * Let's hope libc sets syslog fd to CLOEXEC...
1435 */
1436 xmove_fd(ctrl, STDIN_FILENO);
1437 xdup2(STDIN_FILENO, STDOUT_FILENO);
[3232]1438 dbg("moved ctrl:%d to fd 0,1[,2]\n", ctrl);
[2725]1439 /* manpages of inetd I managed to find either say
1440 * that stderr is also redirected to the network,
1441 * or do not talk about redirection at all (!) */
1442 if (!sep->se_wait) /* only for usual "tcp nowait" */
1443 xdup2(STDIN_FILENO, STDERR_FILENO);
1444 /* NB: among others, this loop closes listening sockets
1445 * for nowait stream children */
1446 for (sep2 = serv_list; sep2; sep2 = sep2->se_next)
1447 if (sep2->se_fd != ctrl)
1448 maybe_close(sep2->se_fd);
1449 sigaction_set(SIGPIPE, &saved_pipe_handler);
1450 restore_sigmask(&omask);
[3232]1451 dbg("execing:'%s'\n", sep->se_program);
[2725]1452 BB_EXECVP(sep->se_program, sep->se_argv);
1453 bb_perror_msg("can't execute '%s'", sep->se_program);
[1765]1454 do_exit1:
[2725]1455 /* eat packet in udp case */
1456 if (sep->se_socktype != SOCK_STREAM)
1457 recv(0, line, LINE_SIZE, MSG_DONTWAIT);
1458 _exit(EXIT_FAILURE);
[1765]1459 } /* for (sep = servtab...) */
1460 } /* for (;;) */
[821]1461}
1462
[2725]1463#if !BB_MMU
1464static const char *const cat_args[] = { "cat", NULL };
1465#endif
1466
[821]1467/*
1468 * Internet services provided internally by inetd:
1469 */
[1765]1470#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
[2725]1471/* Echo service -- echo data back. */
[821]1472/* ARGSUSED */
[2725]1473static void FAST_FUNC echo_stream(int s, servtab_t *sep UNUSED_PARAM)
[821]1474{
[2725]1475#if BB_MMU
[1765]1476 while (1) {
[2725]1477 ssize_t sz = safe_read(s, line, LINE_SIZE);
1478 if (sz <= 0)
1479 break;
1480 xwrite(s, line, sz);
[1765]1481 }
[2725]1482#else
1483 /* We are after vfork here! */
1484 /* move network socket to stdin/stdout */
1485 xmove_fd(s, STDIN_FILENO);
1486 xdup2(STDIN_FILENO, STDOUT_FILENO);
1487 /* no error messages please... */
1488 close(STDERR_FILENO);
1489 xopen(bb_dev_null, O_WRONLY);
1490 BB_EXECVP("cat", (char**)cat_args);
1491 /* on failure we return to main, which does exit(EXIT_FAILURE) */
1492#endif
[821]1493}
[2725]1494static void FAST_FUNC echo_dg(int s, servtab_t *sep)
[821]1495{
[2725]1496 enum { BUFSIZE = 12*1024 }; /* for jumbo sized packets! :) */
1497 char *buf = xmalloc(BUFSIZE); /* too big for stack */
1498 int sz;
1499 len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
[821]1500
[2725]1501 lsa->len = sep->se_lsa->len;
1502 /* dgram builtins are non-forking - DONT BLOCK! */
1503 sz = recvfrom(s, buf, BUFSIZE, MSG_DONTWAIT, &lsa->u.sa, &lsa->len);
1504 if (sz > 0)
1505 sendto(s, buf, sz, 0, &lsa->u.sa, lsa->len);
1506 free(buf);
[821]1507}
[1765]1508#endif /* FEATURE_INETD_SUPPORT_BUILTIN_ECHO */
[821]1509
[2725]1510
[1765]1511#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
[2725]1512/* Discard service -- ignore data. */
[821]1513/* ARGSUSED */
[2725]1514static void FAST_FUNC discard_stream(int s, servtab_t *sep UNUSED_PARAM)
[821]1515{
[2725]1516#if BB_MMU
1517 while (safe_read(s, line, LINE_SIZE) > 0)
1518 continue;
1519#else
1520 /* We are after vfork here! */
1521 /* move network socket to stdin */
1522 xmove_fd(s, STDIN_FILENO);
1523 /* discard output */
1524 close(STDOUT_FILENO);
1525 xopen(bb_dev_null, O_WRONLY);
1526 /* no error messages please... */
1527 xdup2(STDOUT_FILENO, STDERR_FILENO);
1528 BB_EXECVP("cat", (char**)cat_args);
1529 /* on failure we return to main, which does exit(EXIT_FAILURE) */
1530#endif
[821]1531}
1532/* ARGSUSED */
[2725]1533static void FAST_FUNC discard_dg(int s, servtab_t *sep UNUSED_PARAM)
[821]1534{
[2725]1535 /* dgram builtins are non-forking - DONT BLOCK! */
1536 recv(s, line, LINE_SIZE, MSG_DONTWAIT);
[821]1537}
[1765]1538#endif /* FEATURE_INETD_SUPPORT_BUILTIN_DISCARD */
[821]1539
1540
[1765]1541#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
[821]1542#define LINESIZ 72
[2725]1543static void init_ring(void)
[821]1544{
[1765]1545 int i;
[821]1546
[2725]1547 end_ring = ring;
1548 for (i = ' '; i < 127; i++)
1549 *end_ring++ = i;
[821]1550}
[2725]1551/* Character generator. MMU arches only. */
[821]1552/* ARGSUSED */
[2725]1553static void FAST_FUNC chargen_stream(int s, servtab_t *sep UNUSED_PARAM)
[821]1554{
[1765]1555 char *rs;
1556 int len;
1557 char text[LINESIZ + 2];
[821]1558
[2725]1559 if (!end_ring) {
1560 init_ring();
[1765]1561 rs = ring;
1562 }
1563
1564 text[LINESIZ] = '\r';
1565 text[LINESIZ + 1] = '\n';
[821]1566 rs = ring;
[1765]1567 for (;;) {
[2725]1568 len = end_ring - rs;
[1765]1569 if (len >= LINESIZ)
1570 memmove(text, rs, LINESIZ);
1571 else {
1572 memmove(text, rs, len);
1573 memmove(text + len, ring, LINESIZ - len);
1574 }
[2725]1575 if (++rs == end_ring)
[1765]1576 rs = ring;
[2725]1577 xwrite(s, text, sizeof(text));
[821]1578 }
1579}
1580/* ARGSUSED */
[2725]1581static void FAST_FUNC chargen_dg(int s, servtab_t *sep)
[821]1582{
[1765]1583 int len;
1584 char text[LINESIZ + 2];
[2725]1585 len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
[821]1586
[2725]1587 /* Eat UDP packet which started it all */
1588 /* dgram builtins are non-forking - DONT BLOCK! */
1589 lsa->len = sep->se_lsa->len;
1590 if (recvfrom(s, text, sizeof(text), MSG_DONTWAIT, &lsa->u.sa, &lsa->len) < 0)
1591 return;
1592
1593 if (!end_ring) {
1594 init_ring();
1595 ring_pos = ring;
[1765]1596 }
[821]1597
[2725]1598 len = end_ring - ring_pos;
1599 if (len >= LINESIZ)
1600 memmove(text, ring_pos, LINESIZ);
[1765]1601 else {
[2725]1602 memmove(text, ring_pos, len);
[1765]1603 memmove(text + len, ring, LINESIZ - len);
1604 }
[2725]1605 if (++ring_pos == end_ring)
1606 ring_pos = ring;
[1765]1607 text[LINESIZ] = '\r';
1608 text[LINESIZ + 1] = '\n';
[2725]1609 sendto(s, text, sizeof(text), 0, &lsa->u.sa, lsa->len);
[821]1610}
[1765]1611#endif /* FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN */
[821]1612
1613
[1765]1614#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
[821]1615/*
1616 * Return a machine readable date and time, in the form of the
1617 * number of seconds since midnight, Jan 1, 1900. Since gettimeofday
1618 * returns the number of seconds since midnight, Jan 1, 1970,
1619 * we must add 2208988800 seconds to this figure to make up for
1620 * some seventy years Bell Labs was asleep.
1621 */
[2725]1622static uint32_t machtime(void)
[821]1623{
[1765]1624 struct timeval tv;
[821]1625
[2725]1626 gettimeofday(&tv, NULL);
1627 return htonl((uint32_t)(tv.tv_sec + 2208988800));
[821]1628}
1629/* ARGSUSED */
[2725]1630static void FAST_FUNC machtime_stream(int s, servtab_t *sep UNUSED_PARAM)
[821]1631{
[2725]1632 uint32_t result;
[821]1633
[1765]1634 result = machtime();
[2725]1635 full_write(s, &result, sizeof(result));
[821]1636}
[2725]1637static void FAST_FUNC machtime_dg(int s, servtab_t *sep)
[821]1638{
[2725]1639 uint32_t result;
1640 len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
[821]1641
[2725]1642 lsa->len = sep->se_lsa->len;
1643 if (recvfrom(s, line, LINE_SIZE, MSG_DONTWAIT, &lsa->u.sa, &lsa->len) < 0)
[1765]1644 return;
[2725]1645
[1765]1646 result = machtime();
[2725]1647 sendto(s, &result, sizeof(result), 0, &lsa->u.sa, lsa->len);
[821]1648}
[1765]1649#endif /* FEATURE_INETD_SUPPORT_BUILTIN_TIME */
[821]1650
1651
[1765]1652#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
[821]1653/* Return human-readable time of day */
1654/* ARGSUSED */
[2725]1655static void FAST_FUNC daytime_stream(int s, servtab_t *sep UNUSED_PARAM)
[821]1656{
[1765]1657 time_t t;
[821]1658
[3621]1659 time(&t);
[2725]1660 fdprintf(s, "%.24s\r\n", ctime(&t));
[821]1661}
[2725]1662static void FAST_FUNC daytime_dg(int s, servtab_t *sep)
[821]1663{
[1765]1664 time_t t;
[2725]1665 len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
[821]1666
[2725]1667 lsa->len = sep->se_lsa->len;
1668 if (recvfrom(s, line, LINE_SIZE, MSG_DONTWAIT, &lsa->u.sa, &lsa->len) < 0)
1669 return;
1670
[1765]1671 t = time(NULL);
[2725]1672 sprintf(line, "%.24s\r\n", ctime(&t));
1673 sendto(s, line, strlen(line), 0, &lsa->u.sa, lsa->len);
[821]1674}
[1765]1675#endif /* FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME */
Note: See TracBrowser for help on using the repository browser.