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