Magellan Linux

Diff of /trunk/mkinitrd-magellan/busybox/networking/inetd.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 815 by niro, Sat Sep 1 22:45:15 2007 UTC revision 816 by niro, Fri Apr 24 18:33:46 2009 UTC
# Line 3  Line 3 
3  /*      $OpenBSD: inetd.c,v 1.79 2001/01/30 08:30:57 deraadt Exp $      */  /*      $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 $       */  /*      $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>     */  /* 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.   * Copyright (c) 1983,1991 The Regents of the University of California.
9   * All rights reserved.   * All rights reserved.
# Line 38  Line 39 
39    
40  /* Inetd - Internet super-server  /* Inetd - Internet super-server
41   *   *
42   * This program invokes all internet services as needed.   * This program invokes configured services when a connection
43   * connection-oriented services are invoked each time a   * 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   * connection is made, by creating a process.  This process
46   * is passed the connection as file descriptor 0 and is   * is passed the connection as file descriptor 0 and is
47   * expected to do a getpeername to find out the source host   * expected to do a getpeername to find out peer's host
48   * and port.   * and port.
  *  
49   * Datagram oriented services are invoked when a datagram   * Datagram oriented services are invoked when a datagram
50   * arrives; a process is created and passed a pending message   * arrives; a process is created and passed a pending message
51   * on file descriptor 0.  Datagram servers may either connect   * on file descriptor 0. peer's address can be obtained
52   * to their peer, freeing up the original socket for inetd   * using recvfrom.
  * to receive further messages on, or "take over the socket",  
  * processing all arriving datagrams and, eventually, timing  
  * out.  The first type of server is said to be "multi-threaded";  
  * the second type of server "single-threaded".  
53   *   *
54   * Inetd uses a configuration file which is read at startup   * Inetd uses a configuration file which is read at startup
55   * and, possibly, at some later time in response to a hangup signal.   * and, possibly, at some later time in response to a hangup signal.
# Line 60  Line 57 
57   * order shown below.  Continuation lines for an entry must begin with   * order shown below.  Continuation lines for an entry must begin with
58   * a space or tab.  All fields must be present in each entry.   * a space or tab.  All fields must be present in each entry.
59   *   *
60   *      service name                    must be in /etc/services   *      service_name                    must be in /etc/services
61   *      socket type                     stream/dgram/raw/rdm/seqpacket   *      socket_type                     stream/dgram/raw/rdm/seqpacket
62   *      protocol                        must be in /etc/protocols   *      protocol                        must be in /etc/protocols
63     *                                      (usually "tcp" or "udp")
64   *      wait/nowait[.max]               single-threaded/multi-threaded, max #   *      wait/nowait[.max]               single-threaded/multi-threaded, max #
65   *      user[.group] or user[:group]    user/group to run daemon as   *      user[.group] or user[:group]    user/group to run daemon as
66   *      server program                  full path name   *      server_program                  full path name
67   *      server program arguments        maximum of MAXARGS (20)   *      server_program_arguments        maximum of MAXARGS (20)
68   *   *
69   * For RPC services   * For RPC services
70   *      service name/version            must be in /etc/rpc   *      service_name/version            must be in /etc/rpc
71   *      socket type                     stream/dgram/raw/rdm/seqpacket   *      socket_type                     stream/dgram/raw/rdm/seqpacket
72   *      protocol                        must be in /etc/protocols   *      rpc/protocol                    "rpc/tcp" etc
73   *      wait/nowait[.max]               single-threaded/multi-threaded   *      wait/nowait[.max]               single-threaded/multi-threaded
74   *      user[.group] or user[:group]    user to run daemon as   *      user[.group] or user[:group]    user to run daemon as
75   *      server program                  full path name   *      server_program                  full path name
76   *      server program arguments        maximum of MAXARGS (20)   *      server_program_arguments        maximum of MAXARGS (20)
77   *   *
78   * For non-RPC services, the "service name" can be of the form   * For non-RPC services, the "service name" can be of the form
79   * hostaddress:servicename, in which case the hostaddress is used   * hostaddress:servicename, in which case the hostaddress is used
80   * as the host portion of the address to listen on.  If hostaddress   * as the host portion of the address to listen on.  If hostaddress
81   * consists of a single `*' character, INADDR_ANY is used.   * consists of a single '*' character, INADDR_ANY is used.
82   *   *
83   * A line can also consist of just   * A line can also consist of just
84   *      hostaddress:   *      hostaddress:
# Line 101  Line 99 
99   * one line for any given RPC service, even if the host-address   * one line for any given RPC service, even if the host-address
100   * specifiers are different.   * specifiers are different.
101   *   *
102   * Comment lines are indicated by a `#' in column 1.   * Comment lines are indicated by a '#' in column 1.
103   */   */
104    
105  /* inetd rules for passing file descriptors to children  /* inetd rules for passing file descriptors to children
# Line 120  Line 118 
118   * It should fork, and the parent should then exit to allow inetd to check   * 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   * for new service requests to spawn new servers.  Datagram servers which
120   * process all incoming datagrams on a socket and eventually time out are   * 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)   * 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   * 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.   * tftpd(8) utility is an example of a multi-threaded datagram server.
124   *   *
# Line 134  Line 132 
132   * connection requests until a timeout.   * connection requests until a timeout.
133   */   */
134    
135  /* Here's the scoop concerning the user[.:]group feature:  /* Despite of above doc saying that dgram services must use "wait",
136   *   * "udp nowait" servers are implemented in busyboxed inetd.
137   * 1) set-group-option off.   * 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   *      a) user = root: NO setuid() or setgid() is done
145   *   *      b) other:       initgroups(name, primary group)
146   *      b) other:       setgid(primary group as found in passwd)   *                      setgid(primary group as found in passwd)
  *                      initgroups(name, primary group)  
147   *                      setuid()   *                      setuid()
148   *   * 2) group is specified:
  * 2) set-group-option on.  
  *  
149   *      a) user = root: setgid(specified group)   *      a) user = root: setgid(specified group)
150   *                      NO initgroups()   *                      NO initgroups()
151   *                      NO setuid()   *                      NO setuid()
152   *   *      b) other:       initgroups(name, specified group)
153   *      b) other:       setgid(specified group)   *                      setgid(specified group)
  *                      initgroups(name, specified group)  
154   *                      setuid()   *                      setuid()
155   */   */
156    
 #include "busybox.h"  
157  #include <syslog.h>  #include <syslog.h>
158  #include <sys/un.h>  #include <sys/un.h>
159    
160  //#define ENABLE_FEATURE_INETD_RPC 1  #include "libbb.h"
 //#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO 1  
 //#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD 1  
 //#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME 1  
 //#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME 1  
 //#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN 1  
 //#define ENABLE_FEATURE_IPV6 1  
161    
162  #if ENABLE_FEATURE_INETD_RPC  #if ENABLE_FEATURE_INETD_RPC
163  #include <rpc/rpc.h>  #include <rpc/rpc.h>
164  #include <rpc/pmap_clnt.h>  #include <rpc/pmap_clnt.h>
165  #endif  #endif
166    
167  #define _PATH_INETDCONF "/etc/inetd.conf"  #if !BB_MMU
168    /* stream version of chargen is forking but not execing,
169     * can't do that (easily) on NOMMU */
170    #undef  ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
171    #define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN 0
172    #endif
173    
174  #define _PATH_INETDPID  "/var/run/inetd.pid"  #define _PATH_INETDPID  "/var/run/inetd.pid"
175    
176    #define CNT_INTERVAL    60      /* servers in CNT_INTERVAL sec. */
177    #define RETRYTIME       60      /* retry after bind or server fail */
178    
179  #define CNT_INTVL       60              /* servers in CNT_INTVL sec. */  // TODO: explain, or get rid of setrlimit games
 #define RETRYTIME       (60*10)         /* retry after bind or server fail */  
180    
181  #ifndef RLIMIT_NOFILE  #ifndef RLIMIT_NOFILE
182  #define RLIMIT_NOFILE   RLIMIT_OFILE  #define RLIMIT_NOFILE   RLIMIT_OFILE
# Line 189  Line 188 
188    
189  /* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */  /* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */
190  #define FD_MARGIN       8  #define FD_MARGIN       8
 static rlim_t rlim_ofile_cur = OPEN_MAX;  
 static struct rlimit rlim_ofile;  
191    
192    #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD \
193     || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO    \
194     || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN \
195     || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME    \
196     || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
197    # define INETD_BUILTINS_ENABLED
198    #endif
199    
200  /* Check unsupporting builtin */  typedef struct servtab_t {
201  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO || \   /* The most frequently referenced one: */
202   ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD || \   int se_fd;                            /* open descriptor */
203   ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME || \   /* NB: 'biggest fields last' saves on code size (~250 bytes) */
204   ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME || \   /* [addr:]service socktype proto wait user[:group] prog [args] */
205   ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN   char *se_local_hostname;              /* addr to listen on */
206  # define INETD_FEATURE_ENABLED   char *se_service;                     /* "80" or "www" or "mount/2[-3]" */
207  #endif   /* socktype is in se_socktype */      /* "stream" "dgram" "raw" "rdm" "seqpacket" */
208     char *se_proto;                       /* "unix" or "[rpc/]tcp[6]" */
 #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO || \  
  ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD || \  
  ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN  
 # define INETD_SETPROCTITLE  
 #endif  
   
 typedef struct servtab {  
  char *se_hostaddr;                    /* host address to listen on */  
  char *se_service;                     /* name of service */  
  int se_socktype;                      /* type of socket to use */  
  int se_family;                        /* address family */  
  char *se_proto;                       /* protocol used */  
209  #if ENABLE_FEATURE_INETD_RPC  #if ENABLE_FEATURE_INETD_RPC
210   int se_rpcprog;                       /* rpc program number */   int se_rpcprog;                       /* rpc program number */
211   int se_rpcversl;                      /* rpc program lowest version */   int se_rpcver_lo;                     /* rpc program lowest version */
212   int se_rpcversh;                      /* rpc program highest version */   int se_rpcver_hi;                     /* rpc program highest version */
213  #define isrpcservice(sep)       ((sep)->se_rpcversl != 0)  #define is_rpc_service(sep)       ((sep)->se_rpcver_lo != 0)
214  #else  #else
215  #define isrpcservice(sep)       0  #define is_rpc_service(sep)       0
216  #endif  #endif
217   pid_t se_wait;                        /* single threaded server */   pid_t se_wait;                        /* 0:"nowait", 1:"wait", >1:"wait" */
218   short se_checked;                     /* looked at during merge */                                        /* and waiting for this pid */
219     socktype_t se_socktype;               /* SOCK_STREAM/DGRAM/RDM/... */
220     family_t se_family;                   /* AF_UNIX/INET[6] */
221     /* se_proto_no is used by RPC code only... hmm */
222     smallint se_proto_no;                 /* IPPROTO_TCP/UDP, n/a for AF_UNIX */
223     smallint se_checked;                  /* looked at during merge */
224     unsigned se_max;                      /* allowed instances per minute */
225     unsigned se_count;                    /* number started since se_time */
226     unsigned se_time;                     /* when we started counting */
227   char *se_user;                        /* user name to run as */   char *se_user;                        /* user name to run as */
228   char *se_group;                       /* group name to run as */   char *se_group;                       /* group name to run as, can be NULL */
229  #ifdef INETD_FEATURE_ENABLED  #ifdef INETD_BUILTINS_ENABLED
230   const struct builtin *se_bi;          /* if built-in, description */   const struct builtin *se_builtin;     /* if built-in, description */
231  #endif  #endif
232   char *se_server;                      /* server program */   struct servtab_t *se_next;
233     len_and_sockaddr *se_lsa;
234     char *se_program;                     /* server program */
235  #define MAXARGV 20  #define MAXARGV 20
236   char *se_argv[MAXARGV + 1];           /* program arguments */   char *se_argv[MAXARGV + 1];           /* program arguments */
  int se_fd;                            /* open descriptor */  
  union {  
  struct sockaddr se_un_ctrladdr;  
  struct sockaddr_in se_un_ctrladdr_in;  
 #if ENABLE_FEATURE_IPV6  
  struct sockaddr_in6 se_un_ctrladdr_in6;  
 #endif  
  struct sockaddr_un se_un_ctrladdr_un;  
  } se_un;                              /* bound address */  
 #define se_ctrladdr     se_un.se_un_ctrladdr  
 #define se_ctrladdr_in  se_un.se_un_ctrladdr_in  
 #define se_ctrladdr_in6 se_un.se_un_ctrladdr_in6  
 #define se_ctrladdr_un  se_un.se_un_ctrladdr_un  
  int se_ctrladdr_size;  
  int se_max;                           /* max # of instances of this service */  
  int se_count;                         /* number started since se_time */  
  struct timeval se_time;               /* start of se_count */  
  struct servtab *se_next;  
237  } servtab_t;  } servtab_t;
238    
239  static servtab_t *servtab;  #ifdef INETD_BUILTINS_ENABLED
240    /* Echo received data */
 #ifdef INETD_FEATURE_ENABLED  
 struct builtin {  
  const char *bi_service;               /* internally provided service name */  
  int bi_socktype;                      /* type of socket supported */  
  short bi_fork;                        /* 1 if should fork before call */  
  short bi_wait;                        /* 1 if should wait for child */  
  void (*bi_fn) (int, servtab_t *);  
 };  
   
  /* Echo received data */  
241  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
242  static void echo_stream(int, servtab_t *);  static void echo_stream(int, servtab_t *);
243  static void echo_dg(int, servtab_t *);  static void echo_dg(int, servtab_t *);
244  #endif  #endif
245   /* Internet /dev/null */  /* Internet /dev/null */
246  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
247  static void discard_stream(int, servtab_t *);  static void discard_stream(int, servtab_t *);
248  static void discard_dg(int, servtab_t *);  static void discard_dg(int, servtab_t *);
249  #endif  #endif
250   /* Return 32 bit time since 1900 */  /* Return 32 bit time since 1900 */
251  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
252  static void machtime_stream(int, servtab_t *);  static void machtime_stream(int, servtab_t *);
253  static void machtime_dg(int, servtab_t *);  static void machtime_dg(int, servtab_t *);
254  #endif  #endif
255   /* Return human-readable time */  /* Return human-readable time */
256  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
257  static void daytime_stream(int, servtab_t *);  static void daytime_stream(int, servtab_t *);
258  static void daytime_dg(int, servtab_t *);  static void daytime_dg(int, servtab_t *);
259  #endif  #endif
260   /* Familiar character generator */  /* Familiar character generator */
261  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
262  static void chargen_stream(int, servtab_t *);  static void chargen_stream(int, servtab_t *);
263  static void chargen_dg(int, servtab_t *);  static void chargen_dg(int, servtab_t *);
264  #endif  #endif
265    
266    struct builtin {
267     /* NB: not necessarily NUL terminated */
268     char bi_service7[7];      /* internally provided service name */
269     uint8_t bi_fork;          /* 1 if stream fn should run in child */
270     void (*bi_stream_fn)(int, servtab_t *);
271     void (*bi_dgram_fn)(int, servtab_t *);
272    };
273    
274  static const struct builtin builtins[] = {  static const struct builtin builtins[] = {
275  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
276   /* Echo received data */   { "echo", 1, echo_stream, echo_dg },
  {"echo", SOCK_STREAM, 1, 0, echo_stream,},  
  {"echo", SOCK_DGRAM, 0, 0, echo_dg,},  
277  #endif  #endif
278  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
279   /* Internet /dev/null */   { "discard", 1, discard_stream, discard_dg },
280   {"discard", SOCK_STREAM, 1, 0, discard_stream,},  #endif
281   {"discard", SOCK_DGRAM, 0, 0, discard_dg,},  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
282     { "chargen", 1, chargen_stream, chargen_dg },
283  #endif  #endif
284  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
285   /* Return 32 bit time since 1900 */   { "time", 0, machtime_stream, machtime_dg },
  {"time", SOCK_STREAM, 0, 0, machtime_stream,},  
  {"time", SOCK_DGRAM, 0, 0, machtime_dg,},  
286  #endif  #endif
287  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
288   /* Return human-readable time */   { "daytime", 0, daytime_stream, daytime_dg },
  {"daytime", SOCK_STREAM, 0, 0, daytime_stream,},  
  {"daytime", SOCK_DGRAM, 0, 0, daytime_dg,},  
289  #endif  #endif
290    };
291    #endif /* INETD_BUILTINS_ENABLED */
292    
293    struct globals {
294     rlim_t rlim_ofile_cur;
295     struct rlimit rlim_ofile;
296     servtab_t *serv_list;
297     int global_queuelen;
298     int maxsock; /* max fd# in allsock, -1: unknown */
299     /* whenever maxsock grows, prev_maxsock is set to new maxsock,
300     * but if maxsock is set to -1, prev_maxsock is not changed */
301     int prev_maxsock;
302     unsigned max_concurrency;
303     smallint alarm_armed;
304     uid_t real_uid; /* user ID who ran us */
305     const char *config_filename;
306     parser_t *parser;
307     char *default_local_hostname;
308  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
309   /* Familiar character generator */   char *end_ring;
310   {"chargen", SOCK_STREAM, 1, 0, chargen_stream,},   char *ring_pos;
311   {"chargen", SOCK_DGRAM, 0, 0, chargen_dg,},   char ring[128];
312  #endif  #endif
313   {NULL, 0, 0, 0, NULL}   fd_set allsock;
314     /* Used in next_line(), and as scratch read buffer */
315     char line[256];          /* _at least_ 256, see LINE_SIZE */
316    };
317    #define G (*(struct globals*)&bb_common_bufsiz1)
318    enum { LINE_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line) };
319    struct BUG_G_too_big {
320     char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
321  };  };
322  #endif /* INETD_FEATURE_ENABLED */  #define rlim_ofile_cur  (G.rlim_ofile_cur )
323    #define rlim_ofile      (G.rlim_ofile     )
324    #define serv_list       (G.serv_list      )
325    #define global_queuelen (G.global_queuelen)
326    #define maxsock         (G.maxsock        )
327    #define prev_maxsock    (G.prev_maxsock   )
328    #define max_concurrency (G.max_concurrency)
329    #define alarm_armed     (G.alarm_armed    )
330    #define real_uid        (G.real_uid       )
331    #define config_filename (G.config_filename)
332    #define parser          (G.parser         )
333    #define default_local_hostname (G.default_local_hostname)
334    #define first_ps_byte   (G.first_ps_byte  )
335    #define last_ps_byte    (G.last_ps_byte   )
336    #define end_ring        (G.end_ring       )
337    #define ring_pos        (G.ring_pos       )
338    #define ring            (G.ring           )
339    #define allsock         (G.allsock        )
340    #define line            (G.line           )
341    #define INIT_G() do { \
342     rlim_ofile_cur = OPEN_MAX; \
343     global_queuelen = 128; \
344     config_filename = "/etc/inetd.conf"; \
345    } while (0)
346    
347  static int global_queuelen = 128;  static void maybe_close(int fd)
 static int nsock, maxsock;  
 static fd_set allsock;  
 static int toomany;  
 static int timingout;  
 static struct servent *sp;  
 static uid_t uid;  
   
 static char *CONFIG = _PATH_INETDCONF;  
   
 static FILE *fconfig;  
 static char line[1024];  
 static char *defhost;  
   
 /* xstrdup(NULL) returns NULL, but this one  
  * will return newly-allocated "" if called with NULL arg  
  * TODO: audit whether this makes any real difference  
  */  
 static char *xxstrdup(char *cp)  
348  {  {
349   return xstrdup(cp ? cp : "");   if (fd >= 0)
350     close(fd);
351  }  }
352    
353  static int setconfig(void)  // TODO: move to libbb?
354    static len_and_sockaddr *xzalloc_lsa(int family)
355  {  {
356   free(defhost);   len_and_sockaddr *lsa;
357   defhost = xstrdup("*");   int sz;
358   if (fconfig != NULL) {  
359   fseek(fconfig, 0L, SEEK_SET);   sz = sizeof(struct sockaddr_in);
360   return 1;   if (family == AF_UNIX)
361   }   sz = sizeof(struct sockaddr_un);
362   fconfig = fopen(CONFIG, "r");  #if ENABLE_FEATURE_IPV6
363   return (fconfig != NULL);   if (family == AF_INET6)
364     sz = sizeof(struct sockaddr_in6);
365    #endif
366     lsa = xzalloc(LSA_LEN_SIZE + sz);
367     lsa->len = sz;
368     lsa->u.sa.sa_family = family;
369     return lsa;
370  }  }
371    
372  static void endconfig(void)  static void rearm_alarm(void)
373  {  {
374   if (fconfig) {   if (!alarm_armed) {
375   (void) fclose(fconfig);   alarm_armed = 1;
376   fconfig = NULL;   alarm(RETRYTIME);
377   }   }
378   free(defhost);  }
379   defhost = 0;  
380    static void block_CHLD_HUP_ALRM(sigset_t *m)
381    {
382     sigemptyset(m);
383     sigaddset(m, SIGCHLD);
384     sigaddset(m, SIGHUP);
385     sigaddset(m, SIGALRM);
386     sigprocmask(SIG_BLOCK, m, m); /* old sigmask is stored in m */
387    }
388    
389    static void restore_sigmask(sigset_t *m)
390    {
391     sigprocmask(SIG_SETMASK, m, NULL);
392  }  }
393    
394  #if ENABLE_FEATURE_INETD_RPC  #if ENABLE_FEATURE_INETD_RPC
# Line 369  static void register_rpc(servtab_t *sep) Line 396  static void register_rpc(servtab_t *sep)
396  {  {
397   int n;   int n;
398   struct sockaddr_in ir_sin;   struct sockaddr_in ir_sin;
  struct protoent *pp;  
399   socklen_t size;   socklen_t size;
400    
401   if ((pp = getprotobyname(sep->se_proto + 4)) == NULL) {   size = sizeof(ir_sin);
  bb_perror_msg("%s: getproto", sep->se_proto);  
  return;  
  }  
  size = sizeof ir_sin;  
402   if (getsockname(sep->se_fd, (struct sockaddr *) &ir_sin, &size) < 0) {   if (getsockname(sep->se_fd, (struct sockaddr *) &ir_sin, &size) < 0) {
403   bb_perror_msg("%s/%s: getsockname",   bb_perror_msg("getsockname");
  sep->se_service, sep->se_proto);  
404   return;   return;
405   }   }
406    
407   for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) {   for (n = sep->se_rpcver_lo; n <= sep->se_rpcver_hi; n++) {
408   (void) pmap_unset(sep->se_rpcprog, n);   pmap_unset(sep->se_rpcprog, n);
409   if (!pmap_set(sep->se_rpcprog, n, pp->p_proto, ntohs(ir_sin.sin_port)))   if (!pmap_set(sep->se_rpcprog, n, sep->se_proto_no, ntohs(ir_sin.sin_port)))
410   bb_perror_msg("%s %s: pmap_set: %u %u %u %u",   bb_perror_msg("%s %s: pmap_set(%u,%u,%u,%u)",
411   sep->se_service, sep->se_proto,   sep->se_service, sep->se_proto,
412   sep->se_rpcprog, n, pp->p_proto, ntohs(ir_sin.sin_port));   sep->se_rpcprog, n, sep->se_proto_no, ntohs(ir_sin.sin_port));
413   }   }
414  }  }
415    
# Line 396  static void unregister_rpc(servtab_t *se Line 417  static void unregister_rpc(servtab_t *se
417  {  {
418   int n;   int n;
419    
420   for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) {   for (n = sep->se_rpcver_lo; n <= sep->se_rpcver_hi; n++) {
421   if (!pmap_unset(sep->se_rpcprog, n))   if (!pmap_unset(sep->se_rpcprog, n))
422   bb_error_msg("pmap_unset(%u, %u)", sep->se_rpcprog, n);   bb_perror_msg("pmap_unset(%u,%u)", sep->se_rpcprog, n);
423   }   }
424  }  }
425  #endif /* FEATURE_INETD_RPC */  #endif /* FEATURE_INETD_RPC */
426    
427  static void freeconfig(servtab_t *cp)  static void bump_nofile(void)
 {  
  int i;  
   
  free(cp->se_hostaddr);  
  free(cp->se_service);  
  free(cp->se_proto);  
  free(cp->se_user);  
  free(cp->se_group);  
  free(cp->se_server);  
  for (i = 0; i < MAXARGV; i++)  
  free(cp->se_argv[i]);  
 }  
   
 static int bump_nofile(void)  
428  {  {
429  #define FD_CHUNK        32   enum { FD_CHUNK = 32 };
   
430   struct rlimit rl;   struct rlimit rl;
431    
432   if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {   /* Never fails under Linux (except if you pass it bad arguments) */
433   bb_perror_msg("getrlimit");   getrlimit(RLIMIT_NOFILE, &rl);
  return -1;  
  }  
434   rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK);   rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK);
435   rl.rlim_cur = MIN(FD_SETSIZE, rl.rlim_cur + FD_CHUNK);   rl.rlim_cur = MIN(FD_SETSIZE, rl.rlim_cur + FD_CHUNK);
436   if (rl.rlim_cur <= rlim_ofile_cur) {   if (rl.rlim_cur <= rlim_ofile_cur) {
437   bb_error_msg("bump_nofile: cannot extend file limit, max = %d",   bb_error_msg("can't extend file limit, max = %d",
438   (int) rl.rlim_cur);   (int) rl.rlim_cur);
439   return -1;   return;
440   }   }
441    
442   if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {   if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
443   bb_perror_msg("setrlimit");   bb_perror_msg("setrlimit");
444   return -1;   return;
445   }   }
446    
447   rlim_ofile_cur = rl.rlim_cur;   rlim_ofile_cur = rl.rlim_cur;
  return 0;  
448  }  }
449    
450  static void setup(servtab_t *sep)  static void remove_fd_from_set(int fd)
451  {  {
452   int r;   if (fd >= 0) {
453     FD_CLR(fd, &allsock);
454     maxsock = -1;
455     }
456    }
457    
458    static void add_fd_to_set(int fd)
459    {
460     if (fd >= 0) {
461     FD_SET(fd, &allsock);
462     if (maxsock >= 0 && fd > maxsock) {
463     prev_maxsock = maxsock = fd;
464     if ((rlim_t)fd > rlim_ofile_cur - FD_MARGIN)
465     bump_nofile();
466     }
467     }
468    }
469    
470    static void recalculate_maxsock(void)
471    {
472     int fd = 0;
473    
474     /* We may have no services, in this case maxsock should still be >= 0
475     * (code elsewhere is not happy with maxsock == -1) */
476     maxsock = 0;
477     while (fd <= prev_maxsock) {
478     if (FD_ISSET(fd, &allsock))
479     maxsock = fd;
480     fd++;
481     }
482     prev_maxsock = maxsock;
483     if ((rlim_t)maxsock > rlim_ofile_cur - FD_MARGIN)
484     bump_nofile();
485    }
486    
487   sep->se_fd = socket(sep->se_family, sep->se_socktype, 0);  static void prepare_socket_fd(servtab_t *sep)
488   if (sep->se_fd < 0) {  {
489   bb_perror_msg("%s/%s: socket", sep->se_service, sep->se_proto);   int r, fd;
490    
491     fd = socket(sep->se_family, sep->se_socktype, 0);
492     if (fd < 0) {
493     bb_perror_msg("socket");
494   return;   return;
495   }   }
496   if (setsockopt_reuseaddr(sep->se_fd) < 0)   setsockopt_reuseaddr(fd);
  bb_perror_msg("setsockopt(SO_REUSEADDR)");  
497    
498  #if ENABLE_FEATURE_INETD_RPC  #if ENABLE_FEATURE_INETD_RPC
499   if (isrpcservice(sep)) {   if (is_rpc_service(sep)) {
500   struct passwd *pwd;   struct passwd *pwd;
501    
502   /*   /* zero out the port for all RPC services; let bind()
503   * for RPC services, attempt to use a reserved port   * find one. */
504   * if they are going to be running as root.   set_nport(sep->se_lsa, 0);
505   *  
506   * Also, zero out the port for all RPC services; let bind()   /* for RPC services, attempt to use a reserved port
507   * find one.   * if they are going to be running as root. */
508   */   if (real_uid == 0 && sep->se_family == AF_INET
509   sep->se_ctrladdr_in.sin_port = 0;   && (pwd = getpwnam(sep->se_user)) != NULL
510   if (sep->se_user && (pwd = getpwnam(sep->se_user)) &&   && pwd->pw_uid == 0
511   pwd->pw_uid == 0 && uid == 0)   ) {
512   r = bindresvport(sep->se_fd, &sep->se_ctrladdr_in);   r = bindresvport(fd, &sep->se_lsa->u.sin);
513   else {   } else {
514   r = bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size);   r = bind(fd, &sep->se_lsa->u.sa, sep->se_lsa->len);
515   if (r == 0) {   }
516   socklen_t len = sep->se_ctrladdr_size;   if (r == 0) {
517   int saveerrno = errno;   int saveerrno = errno;
518     /* update lsa with port# */
519   /* update se_ctrladdr_in.sin_port */   getsockname(fd, &sep->se_lsa->u.sa, &sep->se_lsa->len);
520   r = getsockname(sep->se_fd, &sep->se_ctrladdr, &len);   errno = saveerrno;
  if (r <= 0)  
  errno = saveerrno;  
  }  
521   }   }
522   } else   } else
523  #endif  #endif
524   r = bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size);   {
525   if (r < 0) {   if (sep->se_family == AF_UNIX) {
526   bb_perror_msg("%s/%s (%d): bind",   struct sockaddr_un *sun;
527   sep->se_service, sep->se_proto, sep->se_ctrladdr.sa_family);   sun = (struct sockaddr_un*)&(sep->se_lsa->u.sa);
528   close(sep->se_fd);   unlink(sun->sun_path);
  sep->se_fd = -1;  
  if (!timingout) {  
  timingout = 1;  
  alarm(RETRYTIME);  
529   }   }
530     r = bind(fd, &sep->se_lsa->u.sa, sep->se_lsa->len);
531     }
532     if (r < 0) {
533     bb_perror_msg("%s/%s: bind",
534     sep->se_service, sep->se_proto);
535     close(fd);
536     rearm_alarm();
537   return;   return;
538   }   }
539   if (sep->se_socktype == SOCK_STREAM)   if (sep->se_socktype == SOCK_STREAM)
540   listen(sep->se_fd, global_queuelen);   listen(fd, global_queuelen);
541    
542   FD_SET(sep->se_fd, &allsock);   add_fd_to_set(fd);
543   nsock++;   sep->se_fd = fd;
  if (sep->se_fd > maxsock) {  
  maxsock = sep->se_fd;  
  if ((rlim_t)maxsock > rlim_ofile_cur - FD_MARGIN)  
  bump_nofile();  
  }  
544  }  }
545    
546  static char *nextline(void)  static int reopen_config_file(void)
547  {  {
548   char *cp;   free(default_local_hostname);
549   FILE *fd = fconfig;   default_local_hostname = xstrdup("*");
550     if (parser != NULL)
551   if (fgets(line, sizeof(line), fd) == NULL)   config_close(parser);
552   return NULL;   parser = config_open(config_filename);
553   cp = strchr(line, '\n');   return (parser != NULL);
  if (cp)  
  *cp = '\0';  
  return line;  
554  }  }
555    
556  static char *skip(char **cpp) /* int report; */  static void close_config_file(void)
557  {  {
558   char *cp = *cpp;   if (parser) {
559   char *start;   config_close(parser);
560     parser = NULL;
 /* erp: */  
  if (*cpp == NULL) {  
  /* if (report) */  
  /* bb_error_msg("syntax error in inetd config file"); */  
  return NULL;  
561   }   }
562    }
563    
564  again:  static void free_servtab_strings(servtab_t *cp)
565   while (*cp == ' ' || *cp == '\t')  {
566   cp++;   int i;
  if (*cp == '\0') {  
  int c;  
   
  c = getc(fconfig);  
  (void) ungetc(c, fconfig);  
  if (c == ' ' || c == '\t')  
  if ((cp = nextline()))  
  goto again;  
  *cpp = NULL;  
  /* goto erp; */  
  return NULL;  
  }  
  start = cp;  
  while (*cp && *cp != ' ' && *cp != '\t')  
  cp++;  
  if (*cp != '\0')  
  *cp++ = '\0';  
  /* if ((*cpp = cp) == NULL) */  
  /* goto erp; */  
567    
568   *cpp = cp;   free(cp->se_local_hostname);
569   return start;   free(cp->se_service);
570     free(cp->se_proto);
571     free(cp->se_user);
572     free(cp->se_group);
573     free(cp->se_lsa); /* not a string in fact */
574     free(cp->se_program);
575     for (i = 0; i < MAXARGV; i++)
576     free(cp->se_argv[i]);
577  }  }
578    
579  static servtab_t *new_servtab(void)  static servtab_t *new_servtab(void)
580  {  {
581   return xmalloc(sizeof(servtab_t));   servtab_t *newtab = xzalloc(sizeof(servtab_t));
582     newtab->se_fd = -1; /* paranoia */
583     return newtab;
584  }  }
585    
586  static servtab_t *dupconfig(servtab_t *sep)  static servtab_t *dup_servtab(servtab_t *sep)
587  {  {
588   servtab_t *newtab;   servtab_t *newtab;
589   int argc;   int argc;
590    
591   newtab = new_servtab();   newtab = new_servtab();
592   memset(newtab, 0, sizeof(servtab_t));   *newtab = *sep; /* struct copy */
593   newtab->se_service = xstrdup(sep->se_service);   /* deep-copying strings */
594   newtab->se_socktype = sep->se_socktype;   newtab->se_service = xstrdup(newtab->se_service);
595   newtab->se_family = sep->se_family;   newtab->se_proto = xstrdup(newtab->se_proto);
596   newtab->se_proto = xstrdup(sep->se_proto);   newtab->se_user = xstrdup(newtab->se_user);
597  #if ENABLE_FEATURE_INETD_RPC   newtab->se_group = xstrdup(newtab->se_group);
598   newtab->se_rpcprog = sep->se_rpcprog;   newtab->se_program = xstrdup(newtab->se_program);
  newtab->se_rpcversl = sep->se_rpcversl;  
  newtab->se_rpcversh = sep->se_rpcversh;  
 #endif  
  newtab->se_wait = sep->se_wait;  
  newtab->se_user = xstrdup(sep->se_user);  
  newtab->se_group = xstrdup(sep->se_group);  
 #ifdef INETD_FEATURE_ENABLED  
  newtab->se_bi = sep->se_bi;  
 #endif  
  newtab->se_server = xstrdup(sep->se_server);  
   
599   for (argc = 0; argc <= MAXARGV; argc++)   for (argc = 0; argc <= MAXARGV; argc++)
600   newtab->se_argv[argc] = xstrdup(sep->se_argv[argc]);   newtab->se_argv[argc] = xstrdup(newtab->se_argv[argc]);
601   newtab->se_max = sep->se_max;   /* NB: se_fd, se_hostaddr and se_next are always
602     * overwrittend by callers, so we don't bother resetting them
603     * to NULL/0/-1 etc */
604    
605   return newtab;   return newtab;
606  }  }
607    
608  static servtab_t *getconfigent(void)  /* gcc generates much more code if this is inlined */
609    static servtab_t *parse_one_line(void)
610  {  {
  servtab_t *sep;  
611   int argc;   int argc;
612   char *cp, *arg;   char *token[6+MAXARGV];
613     char *p, *arg;
614   char *hostdelim;   char *hostdelim;
615     servtab_t *sep;
616   servtab_t *nsep;   servtab_t *nsep;
617   servtab_t *psep;   new:
   
618   sep = new_servtab();   sep = new_servtab();
   
  /* memset(sep, 0, sizeof *sep); */  
619   more:   more:
620   /* freeconfig(sep); */   argc = config_read(parser, token, 6+MAXARGV, 1, "# \t", PARSE_NORMAL);
621     if (!argc) {
622   while ((cp = nextline()) && *cp == '#') /* skip comment line */;   free(sep);
  if (cp == NULL) {  
  /* free(sep); */  
623   return NULL;   return NULL;
624   }   }
625    
626   memset((char *) sep, 0, sizeof *sep);   /* [host:]service socktype proto wait user[:group] prog [args] */
627   arg = skip(&cp);   /* Check for "host:...." line */
628   if (arg == NULL) {   arg = token[0];
  /* A blank line. */  
  goto more;  
  }  
   
  /* Check for a host name. */  
629   hostdelim = strrchr(arg, ':');   hostdelim = strrchr(arg, ':');
630   if (hostdelim) {   if (hostdelim) {
631   *hostdelim = '\0';   *hostdelim = '\0';
632   sep->se_hostaddr = xstrdup(arg);   sep->se_local_hostname = xstrdup(arg);
633   arg = hostdelim + 1;   arg = hostdelim + 1;
634   /*   if (*arg == '\0' && argc == 1) {
635   * If the line is of the form `host:', then just change the   /* Line has just "host:", change the
636   * default host for the following lines.   * default host for the following lines. */
637   */   free(default_local_hostname);
638   if (*arg == '\0') {   default_local_hostname = sep->se_local_hostname;
639   arg = skip(&cp);   goto more;
  if (cp == NULL) {  
  free(defhost);  
  defhost = sep->se_hostaddr;  
  goto more;  
  }  
640   }   }
641   } else   } else
642   sep->se_hostaddr = xxstrdup(defhost);   sep->se_local_hostname = xstrdup(default_local_hostname);
643    
644   sep->se_service = xxstrdup(arg);   /* service socktype proto wait user[:group] prog [args] */
645   arg = skip(&cp);   sep->se_service = xstrdup(arg);
646    
647   if (strcmp(arg, "stream") == 0)   /* socktype proto wait user[:group] prog [args] */
648   sep->se_socktype = SOCK_STREAM;   if (argc < 6) {
649   else if (strcmp(arg, "dgram") == 0)   parse_err:
650   sep->se_socktype = SOCK_DGRAM;   bb_error_msg("parse error on line %u, line is ignored",
651   else if (strcmp(arg, "rdm") == 0)   parser->lineno);
652   sep->se_socktype = SOCK_RDM;   free_servtab_strings(sep);
653   else if (strcmp(arg, "seqpacket") == 0)   /* Just "goto more" can make sep to carry over e.g.
654   sep->se_socktype = SOCK_SEQPACKET;   * "rpc"-ness (by having se_rpcver_lo != 0).
655   else if (strcmp(arg, "raw") == 0)   * We will be more paranoid: */
656   sep->se_socktype = SOCK_RAW;   free(sep);
657   else   goto new;
658   sep->se_socktype = -1;   }
659    
660   sep->se_proto = xxstrdup(skip(&cp));   {
661     static int8_t SOCK_xxx[] ALIGN1 = {
662     -1,
663     SOCK_STREAM, SOCK_DGRAM, SOCK_RDM,
664     SOCK_SEQPACKET, SOCK_RAW
665     };
666     sep->se_socktype = SOCK_xxx[1 + index_in_strings(
667     "stream""\0" "dgram""\0" "rdm""\0"
668     "seqpacket""\0" "raw""\0"
669     , token[1])];
670     }
671    
672   if (strcmp(sep->se_proto, "unix") == 0) {   /* {unix,[rpc/]{tcp,udp}[6]} wait user[:group] prog [args] */
673     sep->se_proto = arg = xstrdup(token[2]);
674     if (strcmp(arg, "unix") == 0) {
675   sep->se_family = AF_UNIX;   sep->se_family = AF_UNIX;
676   } else {   } else {
677     char *six;
678   sep->se_family = AF_INET;   sep->se_family = AF_INET;
679   if (sep->se_proto[strlen(sep->se_proto) - 1] == '6')   six = last_char_is(arg, '6');
680     if (six) {
681  #if ENABLE_FEATURE_IPV6  #if ENABLE_FEATURE_IPV6
682     *six = '\0';
683   sep->se_family = AF_INET6;   sep->se_family = AF_INET6;
684  #else  #else
685   bb_error_msg("%s: IPV6 not supported", sep->se_proto);   bb_error_msg("%s: no support for IPv6", sep->se_proto);
686     goto parse_err;
687  #endif  #endif
688   if (strncmp(sep->se_proto, "rpc/", 4) == 0) {   }
689     if (strncmp(arg, "rpc/", 4) == 0) {
690  #if ENABLE_FEATURE_INETD_RPC  #if ENABLE_FEATURE_INETD_RPC
691   char *p, *ccp;   unsigned n;
692   long l;   arg += 4;
   
693   p = strchr(sep->se_service, '/');   p = strchr(sep->se_service, '/');
694   if (p == 0) {   if (p == NULL) {
695   bb_error_msg("%s: no rpc version", sep->se_service);   bb_error_msg("no rpc version: '%s'", sep->se_service);
696   goto more;   goto parse_err;
697   }   }
698   *p++ = '\0';   *p++ = '\0';
699   l = strtol(p, &ccp, 0);   n = bb_strtou(p, &p, 10);
700   if (ccp == p || l < 0 || l > INT_MAX) {   if (n > INT_MAX) {
701   badafterall:   bad_ver_spec:
702   bb_error_msg("%s/%s: bad rpc version", sep->se_service, p);   bb_error_msg("bad rpc version");
703   goto more;   goto parse_err;
704     }
705     sep->se_rpcver_lo = sep->se_rpcver_hi = n;
706     if (*p == '-') {
707     p++;
708     n = bb_strtou(p, &p, 10);
709     if (n > INT_MAX || (int)n < sep->se_rpcver_lo)
710     goto bad_ver_spec;
711     sep->se_rpcver_hi = n;
712   }   }
713   sep->se_rpcversl = sep->se_rpcversh = l;   if (*p != '\0')
714   if (*ccp == '-') {   goto bad_ver_spec;
  p = ccp + 1;  
  l = strtol(p, &ccp, 0);  
  if (ccp == p || l < 0 || l > INT_MAX || l < sep->se_rpcversl || *ccp)  
  goto badafterall;  
  sep->se_rpcversh = l;  
  } else if (*ccp != '\0')  
  goto badafterall;  
715  #else  #else
716   bb_error_msg("%s: rpc services not supported", sep->se_service);   bb_error_msg("no support for rpc services");
717     goto parse_err;
718  #endif  #endif
719   }   }
720   }   /* we don't really need getprotobyname()! */
721   arg = skip(&cp);   if (strcmp(arg, "tcp") == 0)
722   if (arg == NULL)   sep->se_proto_no = IPPROTO_TCP; /* = 6 */
723   goto more;   if (strcmp(arg, "udp") == 0)
724     sep->se_proto_no = IPPROTO_UDP; /* = 17 */
725     if (six)
726     *six = '6';
727     if (!sep->se_proto_no) /* not tcp/udp?? */
728     goto parse_err;
729     }
730    
731     /* [no]wait[.max] user[:group] prog [args] */
732     arg = token[3];
733     sep->se_max = max_concurrency;
734     p = strchr(arg, '.');
735     if (p) {
736     *p++ = '\0';
737     sep->se_max = bb_strtou(p, NULL, 10);
738     if (errno)
739     goto parse_err;
740     }
741     sep->se_wait = (arg[0] != 'n' || arg[1] != 'o');
742     if (!sep->se_wait) /* "no" seen */
743     arg += 2;
744     if (strcmp(arg, "wait") != 0)
745     goto parse_err;
746    
747   {   /* user[:group] prog [args] */
748   char *s = strchr(arg, '.');   sep->se_user = xstrdup(token[4]);
  if (s) {  
  *s++ = '\0';  
  sep->se_max = xatoi(s);  
  } else  
  sep->se_max = toomany;  
  }  
  sep->se_wait = strcmp(arg, "wait") == 0;  
  /* if ((arg = skip(&cp, 1)) == NULL) */  
  /* goto more; */  
  sep->se_user = xxstrdup(skip(&cp));  
749   arg = strchr(sep->se_user, '.');   arg = strchr(sep->se_user, '.');
750   if (arg == NULL)   if (arg == NULL)
751   arg = strchr(sep->se_user, ':');   arg = strchr(sep->se_user, ':');
# Line 728  static servtab_t *getconfigent(void) Line 753  static servtab_t *getconfigent(void)
753   *arg++ = '\0';   *arg++ = '\0';
754   sep->se_group = xstrdup(arg);   sep->se_group = xstrdup(arg);
755   }   }
  /* if ((arg = skip(&cp, 1)) == NULL) */  
  /* goto more; */  
756    
757   sep->se_server = xxstrdup(skip(&cp));   /* prog [args] */
758   if (strcmp(sep->se_server, "internal") == 0) {   sep->se_program = xstrdup(token[5]);
759  #ifdef INETD_FEATURE_ENABLED  #ifdef INETD_BUILTINS_ENABLED
760   const struct builtin *bi;   if (strcmp(sep->se_program, "internal") == 0
761     && strlen(sep->se_service) <= 7
762   for (bi = builtins; bi->bi_service; bi++)   && (sep->se_socktype == SOCK_STREAM
763   if (bi->bi_socktype == sep->se_socktype &&       || sep->se_socktype == SOCK_DGRAM)
764   strcmp(bi->bi_service, sep->se_service) == 0)   ) {
765   break;   unsigned i;
766   if (bi->bi_service == 0) {   for (i = 0; i < ARRAY_SIZE(builtins); i++)
767   bb_error_msg("internal service %s unknown", sep->se_service);   if (strncmp(builtins[i].bi_service7, sep->se_service, 7) == 0)
768   goto more;   goto found_bi;
769   }   bb_error_msg("unknown internal service %s", sep->se_service);
770   sep->se_bi = bi;   goto parse_err;
771   sep->se_wait = bi->bi_wait;   found_bi:
772  #else   sep->se_builtin = &builtins[i];
773   bb_perror_msg("internal service %s unknown", sep->se_service);   /* stream builtins must be "nowait", dgram must be "wait" */
774   goto more;   if (sep->se_wait != (sep->se_socktype == SOCK_DGRAM))
775  #endif   goto parse_err;
776   }   }
 #ifdef INETD_FEATURE_ENABLED  
  else  
  sep->se_bi = NULL;  
777  #endif  #endif
778   argc = 0;   argc = 0;
779   for (arg = skip(&cp); cp; arg = skip(&cp)) {   while ((arg = token[6+argc]) != NULL && argc < MAXARGV)
780   if (argc < MAXARGV)   sep->se_argv[argc++] = xstrdup(arg);
  sep->se_argv[argc++] = xxstrdup(arg);  
  }  
  while (argc <= MAXARGV)  
  sep->se_argv[argc++] = NULL;  
   
  /*  
  * Now that we've processed the entire line, check if the hostname  
  * specifier was a comma separated list of hostnames. If so  
  * we'll make new entries for each address.  
  */  
  while ((hostdelim = strrchr(sep->se_hostaddr, ',')) != NULL) {  
  nsep = dupconfig(sep);  
   
  /*  
  * NULL terminate the hostname field of the existing entry,  
  * and make a dup for the new entry.  
  */  
  *hostdelim++ = '\0';  
  nsep->se_hostaddr = xstrdup(hostdelim);  
781    
782     /* catch mixups. "<service> stream udp ..." == wtf */
783     if (sep->se_socktype == SOCK_STREAM) {
784     if (sep->se_proto_no == IPPROTO_UDP)
785     goto parse_err;
786     }
787     if (sep->se_socktype == SOCK_DGRAM) {
788     if (sep->se_proto_no == IPPROTO_TCP)
789     goto parse_err;
790     }
791    
792    // bb_info_msg(
793    // "ENTRY[%s][%s][%s][%d][%d][%d][%d][%d][%s][%s][%s]",
794    // sep->se_local_hostname, sep->se_service, sep->se_proto, sep->se_wait, sep->se_proto_no,
795    // sep->se_max, sep->se_count, sep->se_time, sep->se_user, sep->se_group, sep->se_program);
796    
797     /* check if the hostname specifier is a comma separated list
798     * of hostnames. we'll make new entries for each address. */
799     while ((hostdelim = strrchr(sep->se_local_hostname, ',')) != NULL) {
800     nsep = dup_servtab(sep);
801     /* NUL terminate the hostname field of the existing entry,
802     * and make a dup for the new entry. */
803     *hostdelim++ = '\0';
804     nsep->se_local_hostname = xstrdup(hostdelim);
805   nsep->se_next = sep->se_next;   nsep->se_next = sep->se_next;
806   sep->se_next = nsep;   sep->se_next = nsep;
807   }   }
808    
809   nsep = sep;   /* was doing it here: */
810   while (nsep != NULL) {   /* DNS resolution, create copies for each IP address */
811   nsep->se_checked = 1;   /* IPv6-ization destroyed it :( */
  if (nsep->se_family == AF_INET) {  
  if (LONE_CHAR(nsep->se_hostaddr, '*'))  
  nsep->se_ctrladdr_in.sin_addr.s_addr = INADDR_ANY;  
  else if (!inet_aton(nsep->se_hostaddr, &nsep->se_ctrladdr_in.sin_addr)) {  
  struct hostent *hp;  
   
  hp = gethostbyname(nsep->se_hostaddr);  
  if (hp == 0) {  
  bb_error_msg("%s: unknown host", nsep->se_hostaddr);  
  nsep->se_checked = 0;  
  goto skip;  
  } else if (hp->h_addrtype != AF_INET) {  
  bb_error_msg("%s: address isn't an Internet "  
   "address", nsep->se_hostaddr);  
  nsep->se_checked = 0;  
  goto skip;  
  } else {  
  int i = 1;  
   
  memmove(&nsep->se_ctrladdr_in.sin_addr,  
    hp->h_addr_list[0], sizeof(struct in_addr));  
  while (hp->h_addr_list[i] != NULL) {  
  psep = dupconfig(nsep);  
  psep->se_hostaddr = xxstrdup(nsep->se_hostaddr);  
  psep->se_checked = 1;  
  memmove(&psep->se_ctrladdr_in.sin_addr,  
      hp->h_addr_list[i], sizeof(struct in_addr));  
  psep->se_ctrladdr_size = sizeof(psep->se_ctrladdr_in);  
  i++;  
  /* Prepend to list, don't want to look up */  
  /* its hostname again. */  
  psep->se_next = sep;  
  sep = psep;  
  }  
  }  
  }  
  }  
 /* XXX BUG?: is this skip: label supposed to remain? */  
  skip:  
  nsep = nsep->se_next;  
  }  
   
  /*  
  * Finally, free any entries which failed the gethostbyname  
  * check.  
  */  
  psep = NULL;  
  nsep = sep;  
  while (nsep != NULL) {  
  servtab_t *tsep;  
   
  if (nsep->se_checked == 0) {  
  tsep = nsep;  
  if (psep == NULL) {  
  sep = nsep->se_next;  
  nsep = sep;  
  } else {  
  nsep = nsep->se_next;  
  psep->se_next = nsep;  
  }  
  freeconfig(tsep);  
  } else {  
  nsep->se_checked = 0;  
  psep = nsep;  
  nsep = nsep->se_next;  
  }  
  }  
812    
813   return sep;   return sep;
814  }  }
815    
816  #define Block_Using_Signals(m) do { \  static servtab_t *insert_in_servlist(servtab_t *cp)
  sigemptyset(&m); \  
  sigaddset(&m, SIGCHLD); \  
  sigaddset(&m, SIGHUP); \  
  sigaddset(&m, SIGALRM); \  
  sigprocmask(SIG_BLOCK, &m, NULL); \  
 } while (0)  
   
 static servtab_t *enter(servtab_t *cp)  
817  {  {
818   servtab_t *sep;   servtab_t *sep;
819   sigset_t omask;   sigset_t omask;
820    
821   sep = new_servtab();   sep = new_servtab();
822   *sep = *cp;   *sep = *cp; /* struct copy */
823   sep->se_fd = -1;   sep->se_fd = -1;
824  #if ENABLE_FEATURE_INETD_RPC  #if ENABLE_FEATURE_INETD_RPC
825   sep->se_rpcprog = -1;   sep->se_rpcprog = -1;
826  #endif  #endif
827   Block_Using_Signals(omask);   block_CHLD_HUP_ALRM(&omask);
828   sep->se_next = servtab;   sep->se_next = serv_list;
829   servtab = sep;   serv_list = sep;
830   sigprocmask(SIG_UNBLOCK, &omask, NULL);   restore_sigmask(&omask);
831   return sep;   return sep;
832  }  }
833    
834  static int matchconf(servtab_t *old, servtab_t *new)  static int same_serv_addr_proto(servtab_t *old, servtab_t *new)
835  {  {
836   if (strcmp(old->se_service, new->se_service) != 0)   if (strcmp(old->se_local_hostname, new->se_local_hostname) != 0)
837   return 0;   return 0;
838     if (strcmp(old->se_service, new->se_service) != 0)
  if (strcmp(old->se_hostaddr, new->se_hostaddr) != 0)  
839   return 0;   return 0;
   
840   if (strcmp(old->se_proto, new->se_proto) != 0)   if (strcmp(old->se_proto, new->se_proto) != 0)
841   return 0;   return 0;
   
  /*  
  * If the new servtab is bound to a specific address, check that the  
  * old servtab is bound to the same entry. If the new service is not  
  * bound to a specific address then the check of se_hostaddr above  
  * is sufficient.  
  */  
   
  if (old->se_family == AF_INET && new->se_family == AF_INET &&  
  memcmp(&old->se_ctrladdr_in.sin_addr,  
  &new->se_ctrladdr_in.sin_addr,  
  sizeof(new->se_ctrladdr_in.sin_addr)) != 0)  
  return 0;  
   
 #if ENABLE_FEATURE_IPV6  
  if (old->se_family == AF_INET6 && new->se_family == AF_INET6 &&  
  memcmp(&old->se_ctrladdr_in6.sin6_addr,  
  &new->se_ctrladdr_in6.sin6_addr,  
  sizeof(new->se_ctrladdr_in6.sin6_addr)) != 0)  
  return 0;  
 #endif  
842   return 1;   return 1;
843  }  }
844    
845  static void config(int sig ATTRIBUTE_UNUSED)  static void reread_config_file(int sig UNUSED_PARAM)
846  {  {
847   servtab_t *sep, *cp, **sepp;   servtab_t *sep, *cp, **sepp;
848     len_and_sockaddr *lsa;
849   sigset_t omask;   sigset_t omask;
850   size_t n;   unsigned n;
851   char protoname[10];   uint16_t port;
852     int save_errno = errno;
853   if (!setconfig()) {  
854   bb_perror_msg("%s", CONFIG);   if (!reopen_config_file())
855   return;   goto ret;
856   }   for (sep = serv_list; sep; sep = sep->se_next)
  for (sep = servtab; sep; sep = sep->se_next)  
857   sep->se_checked = 0;   sep->se_checked = 0;
  cp = getconfigent();  
  while (cp != NULL) {  
  for (sep = servtab; sep; sep = sep->se_next)  
  if (matchconf(sep, cp))  
  break;  
858    
859   if (sep != 0) {   goto first_line;
860     while (1) {
861     if (cp == NULL) {
862     first_line:
863     cp = parse_one_line();
864     if (cp == NULL)
865     break;
866     }
867     for (sep = serv_list; sep; sep = sep->se_next)
868     if (same_serv_addr_proto(sep, cp))
869     goto equal_servtab;
870     /* not an "equal" servtab */
871     sep = insert_in_servlist(cp);
872     goto after_check;
873     equal_servtab:
874     {
875   int i;   int i;
876    
877  #define SWAP(type, a, b) do {type c=(type)a; a=(type)b; b=(type)c;} while (0)   block_CHLD_HUP_ALRM(&omask);
   
  Block_Using_Signals(omask);  
  /*  
  * sep->se_wait may be holding the pid of a daemon  
  * that we're waiting for.  If so, don't overwrite  
  * it unless the config file explicitly says don't  
  * wait.  
  */  
  if (  
 #ifdef INETD_FEATURE_ENABLED  
  cp->se_bi == 0 &&  
 #endif  
  (sep->se_wait == 1 || cp->se_wait == 0))  
  sep->se_wait = cp->se_wait;  
  SWAP(int, cp->se_max, sep->se_max);  
  SWAP(char *, sep->se_user, cp->se_user);  
  SWAP(char *, sep->se_group, cp->se_group);  
  SWAP(char *, sep->se_server, cp->se_server);  
  for (i = 0; i < MAXARGV; i++)  
  SWAP(char *, sep->se_argv[i], cp->se_argv[i]);  
 #undef SWAP  
   
878  #if ENABLE_FEATURE_INETD_RPC  #if ENABLE_FEATURE_INETD_RPC
879   if (isrpcservice(sep))   if (is_rpc_service(sep))
880   unregister_rpc(sep);   unregister_rpc(sep);
881   sep->se_rpcversl = cp->se_rpcversl;   sep->se_rpcver_lo = cp->se_rpcver_lo;
882   sep->se_rpcversh = cp->se_rpcversh;   sep->se_rpcver_hi = cp->se_rpcver_hi;
883  #endif  #endif
884   sigprocmask(SIG_UNBLOCK, &omask, NULL);   if (cp->se_wait == 0) {
885   freeconfig(cp);   /* New config says "nowait". If old one
886   } else {   * was "wait", we currently may be waiting
887   sep = enter(cp);   * for a child (and not accepting connects).
888     * Stop waiting, start listening again.
889     * (if it's not true, this op is harmless) */
890     add_fd_to_set(sep->se_fd);
891     }
892     sep->se_wait = cp->se_wait;
893     sep->se_max = cp->se_max;
894     /* string fields need more love - we don't want to leak them */
895    #define SWAP(type, a, b) do { type c = (type)a; a = (type)b; b = (type)c; } while (0)
896     SWAP(char*, sep->se_user, cp->se_user);
897     SWAP(char*, sep->se_group, cp->se_group);
898     SWAP(char*, sep->se_program, cp->se_program);
899     for (i = 0; i < MAXARGV; i++)
900     SWAP(char*, sep->se_argv[i], cp->se_argv[i]);
901    #undef SWAP
902     restore_sigmask(&omask);
903     free_servtab_strings(cp);
904   }   }
905     after_check:
906     /* cp->string_fields are consumed by insert_in_servlist()
907     * or freed at this point, cp itself is not yet freed. */
908   sep->se_checked = 1;   sep->se_checked = 1;
909    
910     /* create new len_and_sockaddr */
911   switch (sep->se_family) {   switch (sep->se_family) {
912     struct sockaddr_un *sun;
913   case AF_UNIX:   case AF_UNIX:
914   if (sep->se_fd != -1)   lsa = xzalloc_lsa(AF_UNIX);
915   break;   sun = (struct sockaddr_un*)&lsa->u.sa;
916   (void) unlink(sep->se_service);   safe_strncpy(sun->sun_path, sep->se_service, sizeof(sun->sun_path));
  n = strlen(sep->se_service);  
  if (n > sizeof sep->se_ctrladdr_un.sun_path - 1)  
  n = sizeof sep->se_ctrladdr_un.sun_path - 1;  
  safe_strncpy(sep->se_ctrladdr_un.sun_path, sep->se_service, n + 1);  
  sep->se_ctrladdr_un.sun_family = AF_UNIX;  
  sep->se_ctrladdr_size = n + sizeof sep->se_ctrladdr_un.sun_family;  
  setup(sep);  
917   break;   break;
  case AF_INET:  
  sep->se_ctrladdr_in.sin_family = AF_INET;  
  /* se_ctrladdr_in was set in getconfigent */  
  sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in;  
918    
919     default: /* case AF_INET, case AF_INET6 */
920     n = bb_strtou(sep->se_service, NULL, 10);
921  #if ENABLE_FEATURE_INETD_RPC  #if ENABLE_FEATURE_INETD_RPC
922   if (isrpcservice(sep)) {   if (is_rpc_service(sep)) {
923   struct rpcent *rp;   sep->se_rpcprog = n;
924   // FIXME: atoi_or_else(str, 0) would be handy here   if (errno) { /* se_service is not numeric */
925   sep->se_rpcprog = atoi(sep->se_service);   struct rpcent *rp = getrpcbyname(sep->se_service);
926   if (sep->se_rpcprog == 0) {   if (rp == NULL) {
  rp = getrpcbyname(sep->se_service);  
  if (rp == 0) {  
927   bb_error_msg("%s: unknown rpc service", sep->se_service);   bb_error_msg("%s: unknown rpc service", sep->se_service);
928   goto serv_unknown;   goto next_cp;
929   }   }
930   sep->se_rpcprog = rp->r_number;   sep->se_rpcprog = rp->r_number;
931   }   }
932   if (sep->se_fd == -1)   if (sep->se_fd == -1)
933   setup(sep);   prepare_socket_fd(sep);
934   if (sep->se_fd != -1)   if (sep->se_fd != -1)
935   register_rpc(sep);   register_rpc(sep);
936   } else   goto next_cp;
 #endif  
  {  
  uint16_t port = htons(atoi(sep->se_service));  
  // FIXME: atoi_or_else(str, 0) would be handy here  
  if (!port) {  
  /*XXX*/ strncpy(protoname, sep->se_proto, sizeof(protoname));  
  if (isdigit(protoname[strlen(protoname) - 1]))  
  protoname[strlen(protoname) - 1] = '\0';  
  sp = getservbyname(sep->se_service, protoname);  
  if (sp == 0) {  
  bb_error_msg("%s/%s: unknown service",  
  sep->se_service, sep->se_proto);  
  goto serv_unknown;  
  }  
  port = sp->s_port;  
  }  
  if (port != sep->se_ctrladdr_in.sin_port) {  
  sep->se_ctrladdr_in.sin_port = port;  
  if (sep->se_fd != -1) {  
  FD_CLR(sep->se_fd, &allsock);  
  nsock--;  
  (void) close(sep->se_fd);  
  }  
  sep->se_fd = -1;  
  }  
  if (sep->se_fd == -1)  
  setup(sep);  
937   }   }
  break;  
 #if ENABLE_FEATURE_IPV6  
  case AF_INET6:  
  sep->se_ctrladdr_in6.sin6_family = AF_INET6;  
  /* se_ctrladdr_in was set in getconfigent */  
  sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in6;  
   
 #if ENABLE_FEATURE_INETD_RPC  
  if (isrpcservice(sep)) {  
  struct rpcent *rp;  
   
  sep->se_rpcprog = atoi(sep->se_service);  
  if (sep->se_rpcprog == 0) {  
  rp = getrpcbyname(sep->se_service);  
  if (rp == 0) {  
  bb_error_msg("%s: unknown rpc service", sep->se_service);  
  goto serv_unknown;  
  }  
  sep->se_rpcprog = rp->r_number;  
  }  
  if (sep->se_fd == -1)  
  setup(sep);  
  if (sep->se_fd != -1)  
  register_rpc(sep);  
  } else  
938  #endif  #endif
939   {   /* what port to listen on? */
940   uint16_t port = htons(atoi(sep->se_service));   port = htons(n);
941     if (errno || n > 0xffff) { /* se_service is not numeric */
942   if (!port) {   char protoname[4];
943   /*XXX*/ strncpy(protoname, sep->se_proto, sizeof(protoname));   struct servent *sp;
944   if (isdigit(protoname[strlen(protoname) - 1]))   /* can result only in "tcp" or "udp": */
945   protoname[strlen(protoname) - 1] = '\0';   safe_strncpy(protoname, sep->se_proto, 4);
946   sp = getservbyname(sep->se_service, protoname);   sp = getservbyname(sep->se_service, protoname);
947   if (sp == 0) {   if (sp == NULL) {
948   bb_error_msg("%s/%s: unknown service",   bb_error_msg("%s/%s: unknown service",
949   sep->se_service, sep->se_proto);   sep->se_service, sep->se_proto);
950   goto serv_unknown;   goto next_cp;
  }  
  port = sp->s_port;  
951   }   }
952   if (port != sep->se_ctrladdr_in6.sin6_port) {   port = sp->s_port;
953   sep->se_ctrladdr_in6.sin6_port = port;   }
954   if (sep->se_fd != -1) {   if (LONE_CHAR(sep->se_local_hostname, '*')) {
955   FD_CLR(sep->se_fd, &allsock);   lsa = xzalloc_lsa(sep->se_family);
956   nsock--;   set_nport(lsa, port);
957   (void) close(sep->se_fd);   } else {
958   }   lsa = host_and_af2sockaddr(sep->se_local_hostname,
959   sep->se_fd = -1;   ntohs(port), sep->se_family);
960     if (!lsa) {
961     bb_error_msg("%s/%s: unknown host '%s'",
962     sep->se_service, sep->se_proto,
963     sep->se_local_hostname);
964     goto next_cp;
965   }   }
  if (sep->se_fd == -1)  
  setup(sep);  
966   }   }
967   break;   break;
968  #endif /* FEATURE_IPV6 */   } /* end of "switch (sep->se_family)" */
  }  
  serv_unknown:  
  if (cp->se_next != NULL) {  
  servtab_t *tmp = cp;  
969    
970   cp = cp->se_next;   /* did lsa change? Then close/open */
971   free(tmp);   if (sep->se_lsa == NULL
972     || lsa->len != sep->se_lsa->len
973     || memcmp(&lsa->u.sa, &sep->se_lsa->u.sa, lsa->len) != 0
974     ) {
975     remove_fd_from_set(sep->se_fd);
976     maybe_close(sep->se_fd);
977     free(sep->se_lsa);
978     sep->se_lsa = lsa;
979     sep->se_fd = -1;
980   } else {   } else {
981   free(cp);   free(lsa);
  cp = getconfigent();  
982   }   }
983   }   if (sep->se_fd == -1)
984   endconfig();   prepare_socket_fd(sep);
985   /*   next_cp:
986   * Purge anything not looked at above.   sep = cp->se_next;
987   */   free(cp);
988   Block_Using_Signals(omask);   cp = sep;
989   sepp = &servtab;   } /* end of "while (1) parse lines" */
990     close_config_file();
991    
992     /* Purge anything not looked at above - these are stale entries,
993     * new config file doesnt have them. */
994     block_CHLD_HUP_ALRM(&omask);
995     sepp = &serv_list;
996   while ((sep = *sepp)) {   while ((sep = *sepp)) {
997   if (sep->se_checked) {   if (sep->se_checked) {
998   sepp = &sep->se_next;   sepp = &sep->se_next;
999   continue;   continue;
1000   }   }
1001   *sepp = sep->se_next;   *sepp = sep->se_next;
1002   if (sep->se_fd != -1) {   remove_fd_from_set(sep->se_fd);
1003   FD_CLR(sep->se_fd, &allsock);   maybe_close(sep->se_fd);
  nsock--;  
  (void) close(sep->se_fd);  
  }  
1004  #if ENABLE_FEATURE_INETD_RPC  #if ENABLE_FEATURE_INETD_RPC
1005   if (isrpcservice(sep))   if (is_rpc_service(sep))
1006   unregister_rpc(sep);   unregister_rpc(sep);
1007  #endif  #endif
1008   if (sep->se_family == AF_UNIX)   if (sep->se_family == AF_UNIX)
1009   (void) unlink(sep->se_service);   unlink(sep->se_service);
1010   freeconfig(sep);   free_servtab_strings(sep);
1011   free(sep);   free(sep);
1012   }   }
1013   sigprocmask(SIG_UNBLOCK, &omask, NULL);   restore_sigmask(&omask);
1014     ret:
1015     errno = save_errno;
1016  }  }
1017    
1018    static void reap_child(int sig UNUSED_PARAM)
 static void reapchild(int sig ATTRIBUTE_UNUSED)  
1019  {  {
1020   pid_t pid;   pid_t pid;
1021   int save_errno = errno, status;   int status;
1022   servtab_t *sep;   servtab_t *sep;
1023     int save_errno = errno;
1024    
1025   for (;;) {   for (;;) {
1026   pid = wait3(&status, WNOHANG, NULL);   pid = wait_any_nohang(&status);
1027   if (pid <= 0)   if (pid <= 0)
1028   break;   break;
1029   for (sep = servtab; sep; sep = sep->se_next)   for (sep = serv_list; sep; sep = sep->se_next) {
1030   if (sep->se_wait == pid) {   if (sep->se_wait != pid)
1031   if (WIFEXITED(status) && WEXITSTATUS(status))   continue;
1032   bb_error_msg("%s: exit status 0x%x",   /* One of our "wait" services */
1033   sep->se_server, WEXITSTATUS(status));   if (WIFEXITED(status) && WEXITSTATUS(status))
1034   else if (WIFSIGNALED(status))   bb_error_msg("%s: exit status 0x%x",
1035   bb_error_msg("%s: exit signal 0x%x",   sep->se_program, WEXITSTATUS(status));
1036   sep->se_server, WTERMSIG(status));   else if (WIFSIGNALED(status))
1037   sep->se_wait = 1;   bb_error_msg("%s: exit signal 0x%x",
1038   FD_SET(sep->se_fd, &allsock);   sep->se_program, WTERMSIG(status));
1039   nsock++;   sep->se_wait = 1;
1040   }   add_fd_to_set(sep->se_fd);
1041     break;
1042     }
1043   }   }
1044   errno = save_errno;   errno = save_errno;
1045  }  }
1046    
1047  static void retry(int sig ATTRIBUTE_UNUSED)  static void retry_network_setup(int sig UNUSED_PARAM)
1048  {  {
1049     int save_errno = errno;
1050   servtab_t *sep;   servtab_t *sep;
1051    
1052   timingout = 0;   alarm_armed = 0;
1053   for (sep = servtab; sep; sep = sep->se_next) {   for (sep = serv_list; sep; sep = sep->se_next) {
1054   if (sep->se_fd == -1) {   if (sep->se_fd == -1) {
1055   switch (sep->se_family) {   prepare_socket_fd(sep);
  case AF_UNIX:  
  case AF_INET:  
 #if ENABLE_FEATURE_IPV6  
  case AF_INET6:  
 #endif  
  setup(sep);  
1056  #if ENABLE_FEATURE_INETD_RPC  #if ENABLE_FEATURE_INETD_RPC
1057   if (sep->se_fd != -1 && isrpcservice(sep))   if (sep->se_fd != -1 && is_rpc_service(sep))
1058   register_rpc(sep);   register_rpc(sep);
1059  #endif  #endif
  break;  
  }  
1060   }   }
1061   }   }
1062     errno = save_errno;
1063  }  }
1064    
1065  static void goaway(int sig ATTRIBUTE_UNUSED)  static void clean_up_and_exit(int sig UNUSED_PARAM)
1066  {  {
1067   servtab_t *sep;   servtab_t *sep;
1068    
1069   /* XXX signal race walking sep list */   /* XXX signal race walking sep list */
1070   for (sep = servtab; sep; sep = sep->se_next) {   for (sep = serv_list; sep; sep = sep->se_next) {
1071   if (sep->se_fd == -1)   if (sep->se_fd == -1)
1072   continue;   continue;
1073    
1074   switch (sep->se_family) {   switch (sep->se_family) {
1075   case AF_UNIX:   case AF_UNIX:
1076   (void) unlink(sep->se_service);   unlink(sep->se_service);
1077   break;   break;
1078   case AF_INET:   default: /* case AF_INET, AF_INET6 */
 #if ENABLE_FEATURE_IPV6  
  case AF_INET6:  
 #endif  
1079  #if ENABLE_FEATURE_INETD_RPC  #if ENABLE_FEATURE_INETD_RPC
1080   if (sep->se_wait == 1 && isrpcservice(sep))   if (sep->se_wait == 1 && is_rpc_service(sep))
1081   unregister_rpc(sep);   /* XXX signal race */   unregister_rpc(sep);   /* XXX signal race */
1082  #endif  #endif
1083   break;   break;
1084   }   }
1085   (void) close(sep->se_fd);   if (ENABLE_FEATURE_CLEAN_UP)
1086     close(sep->se_fd);
1087   }   }
1088   (void) unlink(_PATH_INETDPID);   remove_pidfile(_PATH_INETDPID);
1089   exit(0);   exit(EXIT_SUCCESS);
1090  }  }
1091    
1092    int inetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1093  #ifdef INETD_SETPROCTITLE  int inetd_main(int argc UNUSED_PARAM, char **argv)
 static char **Argv;  
 static char *LastArg;  
   
 static void  
 inetd_setproctitle(char *a, int s)  
 {  
  socklen_t size;  
  char *cp;  
  struct sockaddr_in prt_sin;  
  char buf[80];  
   
  cp = Argv[0];  
  size = sizeof(prt_sin);  
  (void) snprintf(buf, sizeof buf, "-%s", a);  
  if (getpeername(s, (struct sockaddr *) &prt_sin, &size) == 0) {  
  char *sa = inet_ntoa(prt_sin.sin_addr);  
   
  buf[sizeof(buf) - 1 - strlen(sa) - 3] = '\0';  
  strcat(buf, " [");  
  strcat(buf, sa);  
  strcat(buf, "]");  
  }  
  strncpy(cp, buf, LastArg - cp);  
  cp += strlen(cp);  
  while (cp < LastArg)  
  *cp++ = ' ';  
 }  
 #endif  
   
   
 int  
 inetd_main(int argc, char *argv[])  
1094  {  {
1095   servtab_t *sep;   struct sigaction sa, saved_pipe_handler;
1096     servtab_t *sep, *sep2;
1097   struct passwd *pwd;   struct passwd *pwd;
1098   struct group *grp = NULL;   struct group *grp = grp; /* for compiler */
  int tmpint;  
  struct sigaction sa, sapipe;  
1099   int opt;   int opt;
1100   pid_t pid;   pid_t pid;
1101   char buf[50];   sigset_t omask;
  char *stoomany;  
  sigset_t omask, wait_mask;  
   
 #ifdef INETD_SETPROCTITLE  
  extern char **environ;  
  char **envp = environ;  
   
  Argv = argv;  
  if (envp == 0 || *envp == 0)  
  envp = argv;  
  while (*envp)  
  envp++;  
  LastArg = envp[-1] + strlen(envp[-1]);  
 #endif  
   
  opt = getopt32(argc, argv, "R:f", &stoomany);  
  if(opt & 1) {  
  toomany = xatoi_u(stoomany);  
  }  
  argc -= optind;  
  argv += optind;  
1102    
1103   uid = getuid();   INIT_G();
1104   if (uid != 0)  
1105   CONFIG = NULL;   real_uid = getuid();
1106   if (argc > 0)   if (real_uid != 0) /* run by non-root user */
1107   CONFIG = argv[0];   config_filename = NULL;
1108   if (CONFIG == NULL)  
1109   bb_error_msg_and_die("non-root must specify a config file");   opt_complementary = "R+:q+"; /* -q N, -R N */
1110     opt = getopt32(argv, "R:feq:", &max_concurrency, &global_queuelen);
1111  #ifdef BB_NOMMU   argv += optind;
1112   if (!(opt & 2)) {   //argc -= optind;
1113   /* reexec for vfork() do continue parent */   if (argv[0])
1114   vfork_daemon_rexec(0, 0, argc, argv, "-f");   config_filename = argv[0];
1115     if (config_filename == NULL)
1116     bb_error_msg_and_die("non-root must specify config file");
1117     if (!(opt & 2))
1118     bb_daemonize_or_rexec(0, argv - optind);
1119     else
1120     bb_sanitize_stdio();
1121     if (!(opt & 4)) {
1122     openlog(applet_name, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
1123     logmode = LOGMODE_SYSLOG;
1124   }   }
  bb_sanitize_stdio();  
 #else  
  bb_sanitize_stdio_maybe_daemonize(!(opt & 2));  
 #endif  
  openlog(applet_name, LOG_PID | LOG_NOWAIT, LOG_DAEMON);  
  logmode = LOGMODE_SYSLOG;  
1125    
1126   if (uid == 0) {   if (real_uid == 0) {
1127   /* If run by hand, ensure groups vector gets trashed */   /* run by root, ensure groups vector gets trashed */
1128   gid_t gid = getgid();   gid_t gid = getgid();
1129   setgroups(1, &gid);   setgroups(1, &gid);
1130   }   }
1131    
1132   {   write_pidfile(_PATH_INETDPID);
  FILE *fp = fopen(_PATH_INETDPID, "w");  
  if (fp != NULL) {  
  fprintf(fp, "%u\n", getpid());  
  fclose(fp);  
  }  
  }  
1133    
1134   if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) {   /* never fails under Linux (except if you pass it bad arguments) */
1135   bb_perror_msg("getrlimit");   getrlimit(RLIMIT_NOFILE, &rlim_ofile);
1136   } else {   rlim_ofile_cur = rlim_ofile.rlim_cur;
1137   rlim_ofile_cur = rlim_ofile.rlim_cur;   if (rlim_ofile_cur == RLIM_INFINITY)    /* ! */
1138   if (rlim_ofile_cur == RLIM_INFINITY)    /* ! */   rlim_ofile_cur = OPEN_MAX;
  rlim_ofile_cur = OPEN_MAX;  
  }  
1139    
1140   memset((char *) &sa, 0, sizeof(sa));   memset(&sa, 0, sizeof(sa));
1141   sigemptyset(&sa.sa_mask);   /*sigemptyset(&sa.sa_mask); - memset did it */
1142   sigaddset(&sa.sa_mask, SIGALRM);   sigaddset(&sa.sa_mask, SIGALRM);
1143   sigaddset(&sa.sa_mask, SIGCHLD);   sigaddset(&sa.sa_mask, SIGCHLD);
1144   sigaddset(&sa.sa_mask, SIGHUP);   sigaddset(&sa.sa_mask, SIGHUP);
1145   sa.sa_handler = retry;   sa.sa_handler = retry_network_setup;
1146   sigaction(SIGALRM, &sa, NULL);   sigaction_set(SIGALRM, &sa);
1147   config(SIGHUP);   sa.sa_handler = reread_config_file;
1148   sa.sa_handler = config;   sigaction_set(SIGHUP, &sa);
1149   sigaction(SIGHUP, &sa, NULL);   sa.sa_handler = reap_child;
1150   sa.sa_handler = reapchild;   sigaction_set(SIGCHLD, &sa);
1151   sigaction(SIGCHLD, &sa, NULL);   sa.sa_handler = clean_up_and_exit;
1152   sa.sa_handler = goaway;   sigaction_set(SIGTERM, &sa);
1153   sigaction(SIGTERM, &sa, NULL);   sa.sa_handler = clean_up_and_exit;
1154   sa.sa_handler = goaway;   sigaction_set(SIGINT, &sa);
  sigaction(SIGINT, &sa, NULL);  
1155   sa.sa_handler = SIG_IGN;   sa.sa_handler = SIG_IGN;
1156   sigaction(SIGPIPE, &sa, &sapipe);   sigaction(SIGPIPE, &sa, &saved_pipe_handler);
  memset(&wait_mask, 0, sizeof(wait_mask));  
  {  
  /* space for daemons to overwrite environment for ps */  
 #define DUMMYSIZE       100  
  char dummy[DUMMYSIZE];  
1157    
1158   (void) memset(dummy, 'x', DUMMYSIZE - 1);   reread_config_file(SIGHUP); /* load config from file */
  dummy[DUMMYSIZE - 1] = '\0';  
   
  (void) setenv("inetd_dummy", dummy, 1);  
  }  
1159    
1160   for (;;) {   for (;;) {
1161   int n, ctrl = -1;   int ready_fd_cnt;
1162     int ctrl, accepted_fd, new_udp_fd;
1163   fd_set readable;   fd_set readable;
1164    
1165   if (nsock == 0) {   if (maxsock < 0)
1166   Block_Using_Signals(omask);   recalculate_maxsock();
  while (nsock == 0)  
  sigsuspend(&wait_mask);  
  sigprocmask(SIG_UNBLOCK, &omask, NULL);  
  }  
1167    
1168   readable = allsock;   readable = allsock; /* struct copy */
1169   n = select(maxsock + 1, &readable, NULL, NULL, NULL);   /* if there are no fds to wait on, we will block
1170   if (n <= 0) {   * until signal wakes us up (maxsock == 0, but readable
1171   if (n < 0 && errno != EINTR) {   * never contains fds 0 and 1...) */
1172     ready_fd_cnt = select(maxsock + 1, &readable, NULL, NULL, NULL);
1173     if (ready_fd_cnt < 0) {
1174     if (errno != EINTR) {
1175   bb_perror_msg("select");   bb_perror_msg("select");
1176   sleep(1);   sleep(1);
1177   }   }
1178   continue;   continue;
1179   }   }
1180    
1181   for (sep = servtab; n && sep; sep = sep->se_next) {   for (sep = serv_list; ready_fd_cnt && sep; sep = sep->se_next) {
1182   if (sep->se_fd == -1 || !FD_ISSET(sep->se_fd, &readable))   if (sep->se_fd == -1 || !FD_ISSET(sep->se_fd, &readable))
1183   continue;   continue;
1184    
1185   n--;   ready_fd_cnt--;
1186   if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {   ctrl = sep->se_fd;
1187   ctrl = accept(sep->se_fd, NULL, NULL);   accepted_fd = -1;
1188   if (ctrl < 0) {   new_udp_fd = -1;
1189   if (errno == EINTR)   if (!sep->se_wait) {
1190     if (sep->se_socktype == SOCK_STREAM) {
1191     ctrl = accepted_fd = accept(sep->se_fd, NULL, NULL);
1192     if (ctrl < 0) {
1193     if (errno != EINTR)
1194     bb_perror_msg("accept (for %s)", sep->se_service);
1195   continue;   continue;
1196   bb_perror_msg("accept (for %s)", sep->se_service);   }
  continue;  
1197   }   }
1198   if (sep->se_family == AF_INET && sep->se_socktype == SOCK_STREAM) {   /* "nowait" udp */
1199   struct sockaddr_in peer;   if (sep->se_socktype == SOCK_DGRAM
1200   socklen_t plen = sizeof(peer);   && sep->se_family != AF_UNIX
1201     ) {
1202   if (getpeername(ctrl, (struct sockaddr *) &peer, &plen) < 0) {  /* How udp "nowait" works:
1203   bb_error_msg("cannot getpeername");   * child peeks at (received and buffered by kernel) UDP packet,
1204   close(ctrl);   * performs connect() on the socket so that it is linked only
1205     * to this peer. But this also affects parent, because descriptors
1206     * are shared after fork() a-la dup(). When parent performs
1207     * select(), it will see this descriptor connected to the peer (!)
1208     * and still readable, will act on it and mess things up
1209     * (can create many copies of same child, etc).
1210     * Parent must create and use new socket instead. */
1211     new_udp_fd = socket(sep->se_family, SOCK_DGRAM, 0);
1212     if (new_udp_fd < 0) { /* error: eat packet, forget about it */
1213     udp_err:
1214     recv(sep->se_fd, line, LINE_SIZE, MSG_DONTWAIT);
1215   continue;   continue;
1216   }   }
1217   if (ntohs(peer.sin_port) == 20) {   setsockopt_reuseaddr(new_udp_fd);
1218   /* XXX ftp bounce */   /* TODO: better do bind after vfork in parent,
1219   close(ctrl);   * so that we don't have two wildcard bound sockets
1220   continue;   * even for a brief moment? */
1221     if (bind(new_udp_fd, &sep->se_lsa->u.sa, sep->se_lsa->len) < 0) {
1222     close(new_udp_fd);
1223     goto udp_err;
1224   }   }
1225   }   }
1226   } else   }
  ctrl = sep->se_fd;  
1227    
1228   Block_Using_Signals(omask);   block_CHLD_HUP_ALRM(&omask);
1229   pid = 0;   pid = 0;
1230  #ifdef INETD_FEATURE_ENABLED  #ifdef INETD_BUILTINS_ENABLED
1231   if (sep->se_bi == 0 || sep->se_bi->bi_fork)   /* do we need to fork? */
1232     if (sep->se_builtin == NULL
1233     || (sep->se_socktype == SOCK_STREAM
1234         && sep->se_builtin->bi_fork))
1235  #endif  #endif
1236   {   {
1237   if (sep->se_count++ == 0)   if (sep->se_max != 0) {
1238   (void) gettimeofday(&sep->se_time, NULL);   if (++sep->se_count == 1)
1239   else if (toomany > 0 && sep->se_count >= sep->se_max) {   sep->se_time = monotonic_sec();
1240   struct timeval now;   else if (sep->se_count >= sep->se_max) {
1241     unsigned now = monotonic_sec();
1242   (void) gettimeofday(&now, NULL);   /* did we accumulate se_max connects too quickly? */
1243   if (now.tv_sec - sep->se_time.tv_sec > CNT_INTVL) {   if (now - sep->se_time <= CNT_INTERVAL) {
1244   sep->se_time = now;   bb_error_msg("%s/%s: too many connections, pausing",
1245   sep->se_count = 1;   sep->se_service, sep->se_proto);
1246   } else {   remove_fd_from_set(sep->se_fd);
1247   if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)   close(sep->se_fd);
1248   close(ctrl);   sep->se_fd = -1;
1249   if (sep->se_family == AF_INET &&   sep->se_count = 0;
1250    ntohs(sep->se_ctrladdr_in.sin_port) >= IPPORT_RESERVED) {   rearm_alarm(); /* will revive it in RETRYTIME sec */
1251   /*   restore_sigmask(&omask);
1252   * Cannot close it -- there are   maybe_close(accepted_fd);
1253   * thieves on the system.   continue; /* -> check next fd in fd set */
  * Simply ignore the connection.  
  */  
  --sep->se_count;  
  continue;  
1254   }   }
  bb_error_msg("%s/%s server failing (looping), service terminated",  
       sep->se_service, sep->se_proto);  
  if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)  
  close(ctrl);  
  FD_CLR(sep->se_fd, &allsock);  
  (void) close(sep->se_fd);  
  sep->se_fd = -1;  
1255   sep->se_count = 0;   sep->se_count = 0;
  nsock--;  
  sigprocmask(SIG_UNBLOCK, &omask, NULL);  
  if (!timingout) {  
  timingout = 1;  
  alarm(RETRYTIME);  
  }  
  continue;  
1256   }   }
1257   }   }
1258   pid = fork();   /* on NOMMU, streamed chargen
1259   }   * builtin wouldn't work, but it is
1260   if (pid < 0) {   * not allowed on NOMMU (ifdefed out) */
1261   bb_perror_msg("fork");  #ifdef INETD_BUILTINS_ENABLED
1262   if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)   if (BB_MMU && sep->se_builtin)
1263   close(ctrl);   pid = fork();
1264   sigprocmask(SIG_UNBLOCK, &omask, NULL);   else
1265   sleep(1);  #endif
1266   continue;   pid = vfork();
1267    
1268     if (pid < 0) { /* fork error */
1269     bb_perror_msg("fork");
1270     sleep(1);
1271     restore_sigmask(&omask);
1272     maybe_close(accepted_fd);
1273     continue; /* -> check next fd in fd set */
1274     }
1275     if (pid == 0)
1276     pid--; /* -1: "we did fork and we are child" */
1277   }   }
1278   if (pid && sep->se_wait) {   /* if pid == 0 here, we never forked */
1279   sep->se_wait = pid;  
1280   FD_CLR(sep->se_fd, &allsock);   if (pid > 0) { /* parent */
1281   nsock--;   if (sep->se_wait) {
1282     /* tcp wait: we passed listening socket to child,
1283     * will wait for child to terminate */
1284     sep->se_wait = pid;
1285     remove_fd_from_set(sep->se_fd);
1286     }
1287     if (new_udp_fd >= 0) {
1288     /* udp nowait: child connected the socket,
1289     * we created and will use new, unconnected one */
1290     xmove_fd(new_udp_fd, sep->se_fd);
1291     }
1292     restore_sigmask(&omask);
1293     maybe_close(accepted_fd);
1294     continue; /* -> check next fd in fd set */
1295   }   }
1296   sigprocmask(SIG_UNBLOCK, &omask, NULL);  
1297   if (pid == 0) {   /* we are either child or didn't vfork at all */
1298  #ifdef INETD_FEATURE_ENABLED  #ifdef INETD_BUILTINS_ENABLED
1299   if (sep->se_bi) {   if (sep->se_builtin) {
1300   (*sep->se_bi->bi_fn)(ctrl, sep);   if (pid) { /* "pid" is -1: we did vfork */
1301   } else   close(sep->se_fd); /* listening socket */
1302  #endif   logmode = 0; /* make xwrite etc silent */
  {  
  pwd = getpwnam(sep->se_user);  
  if (pwd == NULL) {  
  bb_error_msg("getpwnam: %s: no such user", sep->se_user);  
  goto do_exit1;  
  }  
  if (setsid() < 0)  
  bb_perror_msg("%s: setsid", sep->se_service);  
  if (sep->se_group && (grp = getgrnam(sep->se_group)) == NULL) {  
  bb_error_msg("getgrnam: %s: no such group", sep->se_group);  
  goto do_exit1;  
  }  
  if (uid != 0) {  
  /* a user running private inetd */  
  if (uid != pwd->pw_uid)  
  _exit(1);  
  } else if (pwd->pw_uid) {  
  if (sep->se_group)  
  pwd->pw_gid = grp->gr_gid;  
  xsetgid((gid_t) pwd->pw_gid);  
  initgroups(pwd->pw_name, pwd->pw_gid);  
  xsetuid((uid_t) pwd->pw_uid);  
  } else if (sep->se_group) {  
  xsetgid(grp->gr_gid);  
  setgroups(1, &grp->gr_gid);  
  }  
  dup2(ctrl, 0);  
  if (ctrl) close(ctrl);  
  dup2(0, 1);  
  dup2(0, 2);  
  if (rlim_ofile.rlim_cur != rlim_ofile_cur)  
  if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0)  
  bb_perror_msg("setrlimit");  
  closelog();  
  for (tmpint = rlim_ofile_cur - 1; --tmpint > 2;)  
  (void) close(tmpint);  
  sigaction(SIGPIPE, &sapipe, NULL);  
  execv(sep->se_server, sep->se_argv);  
  bb_perror_msg("execv %s", sep->se_server);  
 do_exit1:  
  if (sep->se_socktype != SOCK_STREAM)  
  recv(0, buf, sizeof(buf), 0);  
  _exit(1);  
1303   }   }
1304     restore_sigmask(&omask);
1305     if (sep->se_socktype == SOCK_STREAM)
1306     sep->se_builtin->bi_stream_fn(ctrl, sep);
1307     else
1308     sep->se_builtin->bi_dgram_fn(ctrl, sep);
1309     if (pid) /* we did vfork */
1310     _exit(EXIT_FAILURE);
1311     maybe_close(accepted_fd);
1312     continue; /* -> check next fd in fd set */
1313     }
1314    #endif
1315     /* child */
1316     setsid();
1317     /* "nowait" udp */
1318     if (new_udp_fd >= 0) {
1319     len_and_sockaddr *lsa = xzalloc_lsa(sep->se_family);
1320     /* peek at the packet and remember peer addr */
1321     int r = recvfrom(ctrl, NULL, 0, MSG_PEEK|MSG_DONTWAIT,
1322     &lsa->u.sa, &lsa->len);
1323     if (r < 0)
1324     goto do_exit1;
1325     /* make this socket "connected" to peer addr:
1326     * only packets from this peer will be recv'ed,
1327     * and bare write()/send() will work on it */
1328     connect(ctrl, &lsa->u.sa, lsa->len);
1329     free(lsa);
1330     }
1331     /* prepare env and exec program */
1332     pwd = getpwnam(sep->se_user);
1333     if (pwd == NULL) {
1334     bb_error_msg("%s: no such %s", sep->se_user, "user");
1335     goto do_exit1;
1336     }
1337     if (sep->se_group && (grp = getgrnam(sep->se_group)) == NULL) {
1338     bb_error_msg("%s: no such %s", sep->se_group, "group");
1339     goto do_exit1;
1340     }
1341     if (real_uid != 0 && real_uid != pwd->pw_uid) {
1342     /* a user running private inetd */
1343     bb_error_msg("non-root must run services as himself");
1344     goto do_exit1;
1345   }   }
1346   if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)   if (pwd->pw_uid) {
1347   close(ctrl);   if (sep->se_group)
1348     pwd->pw_gid = grp->gr_gid;
1349     /* initgroups, setgid, setuid: */
1350     change_identity(pwd);
1351     } else if (sep->se_group) {
1352     xsetgid(grp->gr_gid);
1353     setgroups(1, &grp->gr_gid);
1354     }
1355     if (rlim_ofile.rlim_cur != rlim_ofile_cur)
1356     if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0)
1357     bb_perror_msg("setrlimit");
1358     closelog();
1359     xmove_fd(ctrl, 0);
1360     xdup2(0, 1);
1361     xdup2(0, 2);
1362     /* NB: among others, this loop closes listening socket
1363     * for nowait stream children */
1364     for (sep2 = serv_list; sep2; sep2 = sep2->se_next)
1365     maybe_close(sep2->se_fd);
1366     sigaction_set(SIGPIPE, &saved_pipe_handler);
1367     restore_sigmask(&omask);
1368     BB_EXECVP(sep->se_program, sep->se_argv);
1369     bb_perror_msg("exec %s", sep->se_program);
1370     do_exit1:
1371     /* eat packet in udp case */
1372     if (sep->se_socktype != SOCK_STREAM)
1373     recv(0, line, LINE_SIZE, MSG_DONTWAIT);
1374     _exit(EXIT_FAILURE);
1375   } /* for (sep = servtab...) */   } /* for (sep = servtab...) */
1376   } /* for (;;) */   } /* for (;;) */
1377  }  }
1378    
1379    #if !BB_MMU
1380    static const char *const cat_args[] = { "cat", NULL };
1381    #endif
1382    
1383  /*  /*
1384   * Internet services provided internally by inetd:   * Internet services provided internally by inetd:
1385   */   */
 #define BUFSIZE 4096  
   
 #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO || \  
  ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN || \  
  ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME  
 static int dg_badinput(struct sockaddr_in *dg_sin)  
 {  
  if (ntohs(dg_sin->sin_port) < IPPORT_RESERVED)  
  return 1;  
  if (dg_sin->sin_addr.s_addr == htonl(INADDR_BROADCAST))  
  return 1;  
  /* XXX compare against broadcast addresses in SIOCGIFCONF list? */  
  return 0;  
 }  
 #endif  
   
1386  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
1387  /* Echo service -- echo data back */  /* Echo service -- echo data back. */
1388  /* ARGSUSED */  /* ARGSUSED */
1389  static void  static void echo_stream(int s, servtab_t *sep UNUSED_PARAM)
 echo_stream(int s, servtab_t *sep)  
1390  {  {
1391   char buffer[BUFSIZE];  #if BB_MMU
  int i;  
   
  inetd_setproctitle(sep->se_service, s);  
1392   while (1) {   while (1) {
1393   i = read(s, buffer, sizeof(buffer));   ssize_t sz = safe_read(s, line, LINE_SIZE);
1394   if (i <= 0) break;   if (sz <= 0)
1395   /* FIXME: this isnt correct - safe_write()? */   break;
1396   if (write(s, buffer, i) <= 0) break;   xwrite(s, line, sz);
1397   }   }
1398   exit(0);  #else
1399  }   /* We are after vfork here! */
1400     /* move network socket to stdin/stdout */
1401  /* Echo service -- echo data back */   xmove_fd(s, STDIN_FILENO);
1402  /* ARGSUSED */   xdup2(STDIN_FILENO, STDOUT_FILENO);
1403  static void   /* no error messages please... */
1404  echo_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)   close(STDERR_FILENO);
1405  {   xopen(bb_dev_null, O_WRONLY);
1406   char buffer[BUFSIZE];   BB_EXECVP("cat", (char**)cat_args);
1407   int i;   /* on failure we return to main, which does exit(EXIT_FAILURE) */
1408   socklen_t size;  #endif
1409   /* struct sockaddr_storage ss; */  }
1410   struct sockaddr sa;  static void echo_dg(int s, servtab_t *sep)
1411    {
1412   size = sizeof(sa);   enum { BUFSIZE = 12*1024 }; /* for jumbo sized packets! :) */
1413   i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size);   char *buf = xmalloc(BUFSIZE); /* too big for stack */
1414   if (i < 0)   int sz;
1415   return;   len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
1416   if (dg_badinput((struct sockaddr_in *) &sa))  
1417   return;   lsa->len = sep->se_lsa->len;
1418   (void) sendto(s, buffer, i, 0, &sa, sizeof(sa));   /* dgram builtins are non-forking - DONT BLOCK! */
1419     sz = recvfrom(s, buf, BUFSIZE, MSG_DONTWAIT, &lsa->u.sa, &lsa->len);
1420     if (sz > 0)
1421     sendto(s, buf, sz, 0, &lsa->u.sa, lsa->len);
1422     free(buf);
1423  }  }
1424  #endif  /* FEATURE_INETD_SUPPORT_BUILTIN_ECHO */  #endif  /* FEATURE_INETD_SUPPORT_BUILTIN_ECHO */
1425    
1426    
1427  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
1428  /* Discard service -- ignore data */  /* Discard service -- ignore data. */
1429  /* ARGSUSED */  /* ARGSUSED */
1430  static void  static void discard_stream(int s, servtab_t *sep UNUSED_PARAM)
 discard_stream(int s, servtab_t *sep)  
1431  {  {
1432   char buffer[BUFSIZE];  #if BB_MMU
1433     while (safe_read(s, line, LINE_SIZE) > 0)
1434   inetd_setproctitle(sep->se_service, s);   continue;
1435   while (1) {  #else
1436   errno = 0;   /* We are after vfork here! */
1437   if (read(s, buffer, sizeof(buffer)) <= 0 && errno != EINTR)   /* move network socket to stdin */
1438   exit(0);   xmove_fd(s, STDIN_FILENO);
1439   }   /* discard output */
1440     close(STDOUT_FILENO);
1441     xopen(bb_dev_null, O_WRONLY);
1442     /* no error messages please... */
1443     xdup2(STDOUT_FILENO, STDERR_FILENO);
1444     BB_EXECVP("cat", (char**)cat_args);
1445     /* on failure we return to main, which does exit(EXIT_FAILURE) */
1446    #endif
1447  }  }
   
 /* Discard service -- ignore data */  
1448  /* ARGSUSED */  /* ARGSUSED */
1449  static void  static void discard_dg(int s, servtab_t *sep UNUSED_PARAM)
 discard_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)  
1450  {  {
1451   char buffer[BUFSIZE];   /* dgram builtins are non-forking - DONT BLOCK! */
1452     recv(s, line, LINE_SIZE, MSG_DONTWAIT);
  (void) read(s, buffer, sizeof(buffer));  
1453  }  }
1454  #endif /* FEATURE_INETD_SUPPORT_BUILTIN_DISCARD */  #endif /* FEATURE_INETD_SUPPORT_BUILTIN_DISCARD */
1455    
1456    
1457  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
1458  #define LINESIZ 72  #define LINESIZ 72
1459  static char ring[128];  static void init_ring(void)
 static char *endring;  
   
 static void  
 initring(void)  
1460  {  {
1461   int i;   int i;
1462    
1463   endring = ring;   end_ring = ring;
   
1464   for (i = 0; i <= 128; ++i)   for (i = 0; i <= 128; ++i)
1465   if (isprint(i))   if (isprint(i))
1466   *endring++ = i;   *end_ring++ = i;
1467  }  }
1468    /* Character generator. MMU arches only. */
 /* Character generator */  
1469  /* ARGSUSED */  /* ARGSUSED */
1470  static void  static void chargen_stream(int s, servtab_t *sep UNUSED_PARAM)
 chargen_stream(int s, servtab_t *sep)  
1471  {  {
1472   char *rs;   char *rs;
1473   int len;   int len;
1474   char text[LINESIZ + 2];   char text[LINESIZ + 2];
1475    
1476   inetd_setproctitle(sep->se_service, s);   if (!end_ring) {
1477     init_ring();
  if (!endring) {  
  initring();  
1478   rs = ring;   rs = ring;
1479   }   }
1480    
# Line 1645  chargen_stream(int s, servtab_t *sep) Line 1482  chargen_stream(int s, servtab_t *sep)
1482   text[LINESIZ + 1] = '\n';   text[LINESIZ + 1] = '\n';
1483   rs = ring;   rs = ring;
1484   for (;;) {   for (;;) {
1485   len = endring - rs;   len = end_ring - rs;
1486   if (len >= LINESIZ)   if (len >= LINESIZ)
1487   memmove(text, rs, LINESIZ);   memmove(text, rs, LINESIZ);
1488   else {   else {
1489   memmove(text, rs, len);   memmove(text, rs, len);
1490   memmove(text + len, ring, LINESIZ - len);   memmove(text + len, ring, LINESIZ - len);
1491   }   }
1492   if (++rs == endring)   if (++rs == end_ring)
1493   rs = ring;   rs = ring;
1494   if (write(s, text, sizeof(text)) != sizeof(text))   xwrite(s, text, sizeof(text));
  break;  
1495   }   }
  exit(0);  
1496  }  }
   
 /* Character generator */  
1497  /* ARGSUSED */  /* ARGSUSED */
1498  static void  static void chargen_dg(int s, servtab_t *sep)
 chargen_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)  
1499  {  {
  /* struct sockaddr_storage ss; */  
  struct sockaddr sa;  
  static char *rs;  
1500   int len;   int len;
1501   char text[LINESIZ + 2];   char text[LINESIZ + 2];
1502   socklen_t size;   len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
1503    
1504   if (endring == 0) {   /* Eat UDP packet which started it all */
1505   initring();   /* dgram builtins are non-forking - DONT BLOCK! */
1506   rs = ring;   lsa->len = sep->se_lsa->len;
1507   }   if (recvfrom(s, text, sizeof(text), MSG_DONTWAIT, &lsa->u.sa, &lsa->len) < 0)
   
  size = sizeof(sa);  
  if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)  
  return;  
  if (dg_badinput((struct sockaddr_in *) &sa))  
1508   return;   return;
1509    
1510   if ((len = endring - rs) >= LINESIZ)   if (!end_ring) {
1511   memmove(text, rs, LINESIZ);   init_ring();
1512     ring_pos = ring;
1513     }
1514    
1515     len = end_ring - ring_pos;
1516     if (len >= LINESIZ)
1517     memmove(text, ring_pos, LINESIZ);
1518   else {   else {
1519   memmove(text, rs, len);   memmove(text, ring_pos, len);
1520   memmove(text + len, ring, LINESIZ - len);   memmove(text + len, ring, LINESIZ - len);
1521   }   }
1522   if (++rs == endring)   if (++ring_pos == end_ring)
1523   rs = ring;   ring_pos = ring;
1524   text[LINESIZ] = '\r';   text[LINESIZ] = '\r';
1525   text[LINESIZ + 1] = '\n';   text[LINESIZ + 1] = '\n';
1526   (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));   sendto(s, text, sizeof(text), 0, &lsa->u.sa, lsa->len);
1527  }  }
1528  #endif /* FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN */  #endif /* FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN */
1529    
# Line 1706  chargen_dg(int s, servtab_t *sep ATTRIBU Line 1536  chargen_dg(int s, servtab_t *sep ATTRIBU
1536   * we must add 2208988800 seconds to this figure to make up for   * we must add 2208988800 seconds to this figure to make up for
1537   * some seventy years Bell Labs was asleep.   * some seventy years Bell Labs was asleep.
1538   */   */
1539    static uint32_t machtime(void)
 static u_int machtime(void)  
1540  {  {
1541   struct timeval tv;   struct timeval tv;
1542    
1543   if (gettimeofday(&tv, NULL) < 0) {   gettimeofday(&tv, NULL);
1544   fprintf(stderr, "Unable to get time of day\n");   return htonl((uint32_t)(tv.tv_sec + 2208988800));
  return 0L;  
  }  
  return htonl((u_int) tv.tv_sec + 2208988800UL);  
1545  }  }
   
1546  /* ARGSUSED */  /* ARGSUSED */
1547  static void  static void machtime_stream(int s, servtab_t *sep UNUSED_PARAM)
 machtime_stream(int s, servtab_t *sep ATTRIBUTE_UNUSED)  
1548  {  {
1549   u_int result;   uint32_t result;
1550    
1551   result = machtime();   result = machtime();
1552   (void) write(s, (char *) &result, sizeof(result));   full_write(s, &result, sizeof(result));
1553  }  }
1554    static void machtime_dg(int s, servtab_t *sep)
 /* ARGSUSED */  
 static void  
 machtime_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)  
1555  {  {
1556   u_int result;   uint32_t result;
1557   /* struct sockaddr_storage ss; */   len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
  struct sockaddr sa;  
  struct sockaddr_in *dg_sin;  
  socklen_t size;  
1558    
1559   size = sizeof(sa);   lsa->len = sep->se_lsa->len;
1560   if (recvfrom(s, (char *) &result, sizeof(result), 0, &sa, &size) < 0)   if (recvfrom(s, line, LINE_SIZE, MSG_DONTWAIT, &lsa->u.sa, &lsa->len) < 0)
  return;  
  /* if (dg_badinput((struct sockaddr *)&ss)) */  
  dg_sin = (struct sockaddr_in *) &sa;  
  if (dg_sin->sin_addr.s_addr == htonl(INADDR_BROADCAST) ||  
  ntohs(dg_sin->sin_port) < IPPORT_RESERVED / 2)  
1561   return;   return;
1562    
1563   result = machtime();   result = machtime();
1564   (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));   sendto(s, &result, sizeof(result), 0, &lsa->u.sa, lsa->len);
1565  }  }
1566  #endif /* FEATURE_INETD_SUPPORT_BUILTIN_TIME */  #endif /* FEATURE_INETD_SUPPORT_BUILTIN_TIME */
1567    
# Line 1755  machtime_dg(int s, servtab_t *sep ATTRIB Line 1569  machtime_dg(int s, servtab_t *sep ATTRIB
1569  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME  #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
1570  /* Return human-readable time of day */  /* Return human-readable time of day */
1571  /* ARGSUSED */  /* ARGSUSED */
1572  static void daytime_stream(int s, servtab_t *sep ATTRIBUTE_UNUSED)  static void daytime_stream(int s, servtab_t *sep UNUSED_PARAM)
1573  {  {
  char buffer[32];  
1574   time_t t;   time_t t;
1575    
1576   t = time(NULL);   t = time(NULL);
1577     fdprintf(s, "%.24s\r\n", ctime(&t));
 // fdprintf instead?  
  (void) sprintf(buffer, "%.24s\r\n", ctime(&t));  
  (void) write(s, buffer, strlen(buffer));  
1578  }  }
1579    static void daytime_dg(int s, servtab_t *sep)
 /* Return human-readable time of day */  
 /* ARGSUSED */  
 void  
 daytime_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)  
1580  {  {
  char buffer[256];  
1581   time_t t;   time_t t;
1582   /* struct sockaddr_storage ss; */   len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
  struct sockaddr sa;  
  socklen_t size;  
1583    
1584   t = time(NULL);   lsa->len = sep->se_lsa->len;
1585     if (recvfrom(s, line, LINE_SIZE, MSG_DONTWAIT, &lsa->u.sa, &lsa->len) < 0)
  size = sizeof(sa);  
  if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)  
  return;  
  if (dg_badinput((struct sockaddr_in *) &sa))  
1586   return;   return;
1587   (void) sprintf(buffer, "%.24s\r\n", ctime(&t));  
1588   (void) sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa));   t = time(NULL);
1589     sprintf(line, "%.24s\r\n", ctime(&t));
1590     sendto(s, line, strlen(line), 0, &lsa->u.sa, lsa->len);
1591  }  }
1592  #endif /* FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME */  #endif /* FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME */

Legend:
Removed from v.815  
changed lines
  Added in v.816