tcpsvd,udpsvd: make them NOMMU-capable

inetd: make udp nowait work

function                                             old     new   delta
inetd_main                                          1797    2036    +239
tcpudpsvd_main                                      1839    1973    +134
xsetenv_plain                                          -      39     +39
xsetenv_proto                                         23      40     +17
bump_nofile                                          169     170      +1
sig_term_handler                                      72      69      -3
sig_child_handler                                    239     233      -6
connection_status                                     37      31      -6
parse_one_line                                      1102    1092     -10
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 4/4 up/down: 430/-25)           Total: 405 bytes
   text    data     bss     dec     hex filename
 798437     661    7428  806526   c4e7e busybox_old
 798734     661    7428  806823   c4fa7 busybox_unstripped
This commit is contained in:
Denis Vlasenko 2008-03-17 08:35:44 +00:00
parent 9f153f610f
commit aefed941c2
3 changed files with 198 additions and 140 deletions

View File

@ -1,7 +1,7 @@
# #
# Automatically generated make config: don't edit # Automatically generated make config: don't edit
# Busybox version: 1.10.0.svn # Busybox version: 1.10.0.svn
# Thu Mar 6 20:18:05 2008 # Sat Mar 8 01:11:42 2008
# #
CONFIG_HAVE_DOT_CONFIG=y CONFIG_HAVE_DOT_CONFIG=y
@ -844,5 +844,5 @@ CONFIG_LPQ=y
# #
# ipsvd utilities # ipsvd utilities
# #
# CONFIG_TCPSVD is not set CONFIG_TCPSVD=y
# CONFIG_UDPSVD is not set CONFIG_UDPSVD=y

View File

@ -13,12 +13,12 @@
* *
* Code inside "#ifdef SSLSVD" is for sslsvd and is currently unused. * Code inside "#ifdef SSLSVD" is for sslsvd and is currently unused.
* *
* Output of verbose mode matches original (modulo bugs and * Busybox version exports TCPLOCALADDR instead of
* unimplemented stuff). Unnatural splitting of IP and PORT * TCPLOCALIP + TCPLOCALPORT pair. ADDR more closely matches reality
* is retained (personally I prefer one-value "IP:PORT" notation - * (which is "struct sockaddr_XXX". Port is not a separate entity,
* it is a natural string representation of struct sockaddr_XX). * it's just a part of (AF_INET[6]) sockaddr!).
* *
* TCPORIGDST{IP,PORT} is busybox-specific addition * TCPORIGDSTADDR is Busybox-specific addition.
* *
* udp server is hacked up by reusing TCP code. It has the following * udp server is hacked up by reusing TCP code. It has the following
* limitation inherent in Unix DGRAM sockets implementation: * limitation inherent in Unix DGRAM sockets implementation:
@ -46,6 +46,8 @@ struct globals {
unsigned cur_per_host; unsigned cur_per_host;
unsigned cnum; unsigned cnum;
unsigned cmax; unsigned cmax;
char **env_cur;
char *env_var[1]; /* actually bigger */
}; };
#define G (*(struct globals*)&bb_common_bufsiz1) #define G (*(struct globals*)&bb_common_bufsiz1)
#define verbose (G.verbose ) #define verbose (G.verbose )
@ -53,21 +55,46 @@ struct globals {
#define cur_per_host (G.cur_per_host) #define cur_per_host (G.cur_per_host)
#define cnum (G.cnum ) #define cnum (G.cnum )
#define cmax (G.cmax ) #define cmax (G.cmax )
#define env_cur (G.env_cur )
#define env_var (G.env_var )
#define INIT_G() \ #define INIT_G() \
do { \ do { \
cmax = 30; \ cmax = 30; \
env_cur = &env_var[0]; \
} while (0) } while (0)
/* We have to be careful about leaking memory in repeated setenv's */
static void xsetenv_plain(const char *n, const char *v)
{
char *var = xasprintf("%s=%s", n, v);
*env_cur++ = var;
putenv(var);
}
static void xsetenv_proto(const char *proto, const char *n, const char *v) static void xsetenv_proto(const char *proto, const char *n, const char *v)
{ {
putenv(xasprintf("%s%s=%s", proto, n, v)); char *var = xasprintf("%s%s=%s", proto, n, v);
*env_cur++ = var;
putenv(var);
}
static void undo_xsetenv(void)
{
char **pp = env_cur = &env_var[0];
while (*pp) {
char *var = *pp;
*strchrnul(var, '=') = '\0';
unsetenv(var);
free(var);
*pp++ = NULL;
}
} }
static void sig_term_handler(int sig) static void sig_term_handler(int sig)
{ {
if (verbose) if (verbose)
printf("%s: info: sigterm received, exit\n", applet_name); bb_error_msg("got signal %u, exit", sig);
kill_myself_with_sig(sig); kill_myself_with_sig(sig);
} }
@ -85,7 +112,7 @@ static void print_waitstat(unsigned pid, int wstat)
cause = "signal"; cause = "signal";
e = WTERMSIG(wstat); e = WTERMSIG(wstat);
} }
printf("%s: info: end %d %s %d\n", applet_name, pid, cause, e); bb_error_msg("end %d %s %d", pid, cause, e);
} }
/* Must match getopt32 in main! */ /* Must match getopt32 in main! */
@ -113,7 +140,7 @@ static void connection_status(void)
{ {
/* "only 1 client max" desn't need this */ /* "only 1 client max" desn't need this */
if (cmax > 1) if (cmax > 1)
printf("%s: info: status %u/%u\n", applet_name, cnum, cmax); bb_error_msg("status %u/%u", cnum, cmax);
} }
static void sig_child_handler(int sig) static void sig_child_handler(int sig)
@ -145,13 +172,11 @@ int tcpudpsvd_main(int argc, char **argv)
#ifndef SSLSVD #ifndef SSLSVD
struct bb_uidgid_t ugid; struct bb_uidgid_t ugid;
#endif #endif
bool need_hostnames, need_remote_ip, tcp; bool tcp;
uint16_t local_port; uint16_t local_port;
char *local_hostname = NULL; char *preset_local_hostname = NULL;
char *remote_hostname = (char*)""; /* "" used if no -h */ char *remote_hostname = remote_hostname; /* for compiler */
char *local_addr = local_addr; /* gcc */ char *remote_addr = remote_addr; /* for compiler */
char *remote_addr = remote_addr; /* gcc */
char *remote_ip = remote_addr; /* gcc */
len_and_sockaddr *lsa; len_and_sockaddr *lsa;
len_and_sockaddr local, remote; len_and_sockaddr local, remote;
socklen_t sa_len; socklen_t sa_len;
@ -168,12 +193,12 @@ int tcpudpsvd_main(int argc, char **argv)
opt_complementary = "-3:i--i:ph:vv"; opt_complementary = "-3:i--i:ph:vv";
#ifdef SSLSVD #ifdef SSLSVD
getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:", getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:",
&str_c, &str_C, &instructs, &instructs, &user, &local_hostname, &str_c, &str_C, &instructs, &instructs, &user, &preset_local_hostname,
&str_b, &str_t, &ssluser, &root, &cert, &key, &verbose &str_b, &str_t, &ssluser, &root, &cert, &key, &verbose
); );
#else #else
getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:v", getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:v",
&str_c, &str_C, &instructs, &instructs, &user, &local_hostname, &str_c, &str_C, &instructs, &instructs, &user, &preset_local_hostname,
&str_b, &str_t, &verbose &str_b, &str_t, &verbose
); );
#endif #endif
@ -210,26 +235,21 @@ int tcpudpsvd_main(int argc, char **argv)
if (!tcp) if (!tcp)
max_per_host = 0; max_per_host = 0;
/* stdout is used for logging, don't buffer */
setlinebuf(stdout);
bb_sanitize_stdio(); /* fd# 0,1,2 must be opened */ bb_sanitize_stdio(); /* fd# 0,1,2 must be opened */
need_hostnames = verbose || !(option_mask32 & OPT_E);
need_remote_ip = max_per_host || need_hostnames;
#ifdef SSLSVD #ifdef SSLSVD
sslser = user; sslser = user;
client = 0; client = 0;
if ((getuid() == 0) && !(option_mask32 & OPT_u)) { if ((getuid() == 0) && !(option_mask32 & OPT_u)) {
xfunc_exitcode = 100; xfunc_exitcode = 100;
bb_error_msg_and_die("fatal: -U ssluser must be set when running as root"); bb_error_msg_and_die("-U ssluser must be set when running as root");
} }
if (option_mask32 & OPT_u) if (option_mask32 & OPT_u)
if (!uidgid_get(&sslugid, ssluser, 1)) { if (!uidgid_get(&sslugid, ssluser, 1)) {
if (errno) { if (errno) {
bb_perror_msg_and_die("fatal: cannot get user/group: %s", ssluser); bb_perror_msg_and_die("fatal: cannot get user/group: %s", ssluser);
} }
bb_error_msg_and_die("fatal: unknown user/group '%s'", ssluser); bb_error_msg_and_die("unknown user/group '%s'", ssluser);
} }
if (!cert) cert = "./cert.pem"; if (!cert) cert = "./cert.pem";
if (!key) key = cert; if (!key) key = cert;
@ -246,7 +266,7 @@ int tcpudpsvd_main(int argc, char **argv)
sig_block(SIGCHLD); sig_block(SIGCHLD);
signal(SIGCHLD, sig_child_handler); signal(SIGCHLD, sig_child_handler);
signal(SIGTERM, sig_term_handler); bb_signals(BB_SIGS_FATAL, sig_term_handler);
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
if (max_per_host) if (max_per_host)
@ -254,6 +274,8 @@ int tcpudpsvd_main(int argc, char **argv)
local_port = bb_lookup_port(argv[1], tcp ? "tcp" : "udp", 0); local_port = bb_lookup_port(argv[1], tcp ? "tcp" : "udp", 0);
lsa = xhost2sockaddr(argv[0], local_port); lsa = xhost2sockaddr(argv[0], local_port);
argv += 2;
sock = xsocket(lsa->u.sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0); sock = xsocket(lsa->u.sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
setsockopt_reuseaddr(sock); setsockopt_reuseaddr(sock);
sa_len = lsa->len; /* I presume sockaddr len stays the same */ sa_len = lsa->len; /* I presume sockaddr len stays the same */
@ -274,14 +296,13 @@ int tcpudpsvd_main(int argc, char **argv)
if (verbose) { if (verbose) {
char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa); char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa);
printf("%s: info: listening on %s", applet_name, addr); bb_error_msg("listening on %s, starting", addr);
free(addr); free(addr);
#ifndef SSLSVD #ifndef SSLSVD
if (option_mask32 & OPT_u) if (option_mask32 & OPT_u)
printf(", uid %u, gid %u", printf(", uid %u, gid %u",
(unsigned)ugid.uid, (unsigned)ugid.gid); (unsigned)ugid.uid, (unsigned)ugid.gid);
#endif #endif
puts(", starting");
} }
/* Main accept() loop */ /* Main accept() loop */
@ -297,14 +318,15 @@ int tcpudpsvd_main(int argc, char **argv)
close(0); close(0);
again2: again2:
sig_unblock(SIGCHLD); sig_unblock(SIGCHLD);
local.len = remote.len = sa_len;
if (tcp) { if (tcp) {
remote.len = sa_len;
conn = accept(sock, &remote.u.sa, &remote.len); conn = accept(sock, &remote.u.sa, &remote.len);
} else { } else {
/* In case recv_from_to won't be able to recover local addr. /* In case recv_from_to won't be able to recover local addr.
* Also sets port - recv_from_to is unable to do it. */ * Also sets port - recv_from_to is unable to do it. */
local = *lsa; local = *lsa;
conn = recv_from_to(sock, NULL, 0, MSG_PEEK, &remote.u.sa, &local.u.sa, sa_len); conn = recv_from_to(sock, NULL, 0, MSG_DONTWAIT | MSG_PEEK,
&remote.u.sa, &local.u.sa, sa_len);
} }
sig_block(SIGCHLD); sig_block(SIGCHLD);
if (conn < 0) { if (conn < 0) {
@ -317,19 +339,19 @@ int tcpudpsvd_main(int argc, char **argv)
if (max_per_host) { if (max_per_host) {
/* Drop connection immediately if cur_per_host > max_per_host /* Drop connection immediately if cur_per_host > max_per_host
* (minimizing load under SYN flood) */ * (minimizing load under SYN flood) */
remote_ip = xmalloc_sockaddr2dotted_noport(&remote.u.sa); remote_addr = xmalloc_sockaddr2dotted_noport(&remote.u.sa);
cur_per_host = ipsvd_perhost_add(remote_ip, max_per_host, &hccp); cur_per_host = ipsvd_perhost_add(remote_addr, max_per_host, &hccp);
if (cur_per_host > max_per_host) { if (cur_per_host > max_per_host) {
/* ipsvd_perhost_add detected that max is exceeded /* ipsvd_perhost_add detected that max is exceeded
* (and did not store ip in connection table) */ * (and did not store ip in connection table) */
free(remote_ip); free(remote_addr);
if (msg_per_host) { if (msg_per_host) {
/* don't block or test for errors */ /* don't block or test for errors */
ndelay_on(0); send(0, msg_per_host, len_per_host, MSG_DONTWAIT);
write(0, msg_per_host, len_per_host);
} }
goto again1; goto again1;
} }
/* NB: remote_addr is not leaked, it is stored in conn table */
} }
if (!tcp) { if (!tcp) {
@ -372,19 +394,21 @@ int tcpudpsvd_main(int argc, char **argv)
#endif #endif
} }
pid = fork(); pid = vfork();
if (pid == -1) { if (pid == -1) {
bb_perror_msg("fork"); bb_perror_msg("vfork");
goto again; goto again;
} }
if (pid != 0) { if (pid != 0) {
/* parent */ /* Parent */
cnum++; cnum++;
if (verbose) if (verbose)
connection_status(); connection_status();
if (hccp) if (hccp)
hccp->pid = pid; hccp->pid = pid;
/* clean up changes done by vforked child */
undo_xsetenv();
goto again; goto again;
} }
@ -394,78 +418,93 @@ int tcpudpsvd_main(int argc, char **argv)
if (tcp) if (tcp)
close(sock); close(sock);
if (need_remote_ip) { /* vfork alert! every xmalloc in this block should be freed! */
remote_addr = xmalloc_sockaddr2dotted(&remote.u.sa); char *local_hostname = local_hostname; /* for compiler */
char *local_addr = NULL;
char *free_me0 = NULL;
char *free_me1 = NULL;
char *free_me2 = NULL;
if (need_hostnames) { if (verbose || !(option_mask32 & OPT_E)) {
if (option_mask32 & OPT_h) { if (!max_per_host) /* remote_addr is not yet known */
remote_hostname = xmalloc_sockaddr2host_noport(&remote.u.sa); free_me0 = remote_addr = xmalloc_sockaddr2dotted(&remote.u.sa);
if (!remote_hostname) { if (option_mask32 & OPT_h) {
bb_error_msg("warning: cannot look up hostname for %s", remote_addr); free_me1 = remote_hostname = xmalloc_sockaddr2host_noport(&remote.u.sa);
remote_hostname = (char*)""; if (!remote_hostname) {
bb_error_msg("cannot look up hostname for %s", remote_addr);
remote_hostname = remote_addr;
}
}
/* Find out local IP peer connected to.
* Errors ignored (I'm not paranoid enough to imagine kernel
* which doesn't know local IP). */
if (tcp)
getsockname(0, &local.u.sa, &local.len);
/* else: for UDP it is done earlier by parent */
local_addr = xmalloc_sockaddr2dotted(&local.u.sa);
if (option_mask32 & OPT_h) {
local_hostname = preset_local_hostname;
if (!local_hostname) {
free_me2 = local_hostname = xmalloc_sockaddr2host_noport(&local.u.sa);
if (!local_hostname)
bb_error_msg_and_die("cannot look up hostname for %s", local_addr);
}
/* else: local_hostname is not NULL, but is NOT malloced! */
} }
} }
/* Find out local IP peer connected to. if (verbose) {
* Errors ignored (I'm not paranoid enough to imagine kernel pid = getpid();
* which doesn't know local IP). */ if (max_per_host) {
if (tcp) { bb_error_msg("concurrency %s %u/%u",
local.len = sa_len; remote_addr,
getsockname(0, &local.u.sa, &local.len); cur_per_host, max_per_host);
}
bb_error_msg((option_mask32 & OPT_h)
? "start %u %s-%s (%s-%s)"
: "start %u %s-%s",
pid,
local_addr, remote_addr,
local_hostname, remote_hostname);
} }
local_addr = xmalloc_sockaddr2dotted(&local.u.sa);
if (!local_hostname) { if (!(option_mask32 & OPT_E)) {
local_hostname = xmalloc_sockaddr2host_noport(&local.u.sa); /* setup ucspi env */
if (!local_hostname) const char *proto = tcp ? "TCP" : "UDP";
bb_error_msg_and_die("warning: cannot look up hostname for %s"+9, local_addr);
/* Extract "original" destination addr:port
* from Linux firewall. Useful when you redirect
* an outbond connection to local handler, and it needs
* to know where it originally tried to connect */
if (tcp && getsockopt(0, SOL_IP, SO_ORIGINAL_DST, &local.u.sa, &local.len) == 0) {
char *addr = xmalloc_sockaddr2dotted(&local.u.sa);
xsetenv_plain("TCPORIGDSTADDR", addr);
free(addr);
}
xsetenv_plain("PROTO", proto);
xsetenv_proto(proto, "LOCALADDR", local_addr);
xsetenv_proto(proto, "REMOTEADDR", remote_addr);
if (option_mask32 & OPT_h) {
xsetenv_proto(proto, "LOCALHOST", local_hostname);
xsetenv_proto(proto, "REMOTEHOST", remote_hostname);
}
//compat? xsetenv_proto(proto, "REMOTEINFO", "");
/* additional */
if (cur_per_host > 0) /* can not be true for udp */
xsetenv_plain("TCPCONCURRENCY", utoa(cur_per_host));
} }
free(local_addr);
free(free_me0);
free(free_me1);
free(free_me2);
} }
if (verbose) { xdup2(0, 1);
pid = getpid();
printf("%s: info: pid %u from %s\n", applet_name, pid, remote_addr);
if (max_per_host)
printf("%s: info: concurrency %u %s %u/%u\n",
applet_name, pid, remote_ip, cur_per_host, max_per_host);
printf("%s: info: start %u %s:%s :%s:%s\n",
applet_name, pid,
local_hostname, local_addr,
remote_hostname, remote_addr);
}
if (!(option_mask32 & OPT_E)) {
/* setup ucspi env */
const char *proto = tcp ? "TCP" : "UDP";
/* Extract "original" destination addr:port
* from Linux firewall. Useful when you redirect
* an outbond connection to local handler, and it needs
* to know where it originally tried to connect */
if (tcp && getsockopt(0, SOL_IP, SO_ORIGINAL_DST, &lsa->u.sa, &lsa->len) == 0) {
char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa);
xsetenv("TCPORIGDSTADDR", addr);
free(addr);
}
xsetenv("PROTO", proto);
xsetenv_proto(proto, "LOCALADDR", local_addr);
xsetenv_proto(proto, "LOCALHOST", local_hostname);
xsetenv_proto(proto, "REMOTEADDR", remote_addr);
if (option_mask32 & OPT_h) {
xsetenv_proto(proto, "REMOTEHOST", remote_hostname);
}
xsetenv_proto(proto, "REMOTEINFO", "");
/* additional */
if (cur_per_host > 0) /* can not be true for udp */
xsetenv("TCPCONCURRENCY", utoa(cur_per_host));
}
dup2(0, 1);
signal(SIGTERM, SIG_DFL); signal(SIGTERM, SIG_DFL);
signal(SIGPIPE, SIG_DFL); signal(SIGPIPE, SIG_DFL);
signal(SIGCHLD, SIG_DFL); signal(SIGCHLD, SIG_DFL);
sig_unblock(SIGCHLD); sig_unblock(SIGCHLD);
argv += 2;
#ifdef SSLSVD #ifdef SSLSVD
strcpy(id, utoa(pid)); strcpy(id, utoa(pid));
ssl_io(0, argv); ssl_io(0, argv);

View File

@ -130,8 +130,13 @@
* tening service socket, and must accept at least one connection request * tening service socket, and must accept at least one connection request
* before exiting. Such a server would normally accept and process incoming * before exiting. Such a server would normally accept and process incoming
* connection requests until a timeout. * connection requests until a timeout.
* */
* In short: "stream" can be "wait" or "nowait"; "dgram" must be "wait".
/* Despite of above doc saying that dgram services must use "wait",
* "udp nowait" servers are implemented in busyboxed inetd.
* IPv6 addresses are also implemented. However, they may look ugly -
* ":::service..." means "address '::' (IPv6 wildcard addr)":"service"...
* You have to put "tcp6"/"udp6" in protocol field to select IPv6.
*/ */
/* Here's the scoop concerning the user[:group] feature: /* Here's the scoop concerning the user[:group] feature:
@ -832,9 +837,6 @@ static NOINLINE servtab_t *parse_one_line(void)
if (sep->se_socktype == SOCK_DGRAM) { if (sep->se_socktype == SOCK_DGRAM) {
if (sep->se_proto_no == IPPROTO_TCP) if (sep->se_proto_no == IPPROTO_TCP)
goto parse_err; goto parse_err;
/* "udp nowait" is a small fork bomb :) */
if (!sep->se_wait)
goto parse_err;
} }
/* check if the hostname specifier is a comma separated list /* check if the hostname specifier is a comma separated list
@ -1195,7 +1197,7 @@ int inetd_main(int argc, char **argv)
for (;;) { for (;;) {
int ready_fd_cnt; int ready_fd_cnt;
int ctrl, accepted_fd; int ctrl, accepted_fd, new_udp_fd;
fd_set readable; fd_set readable;
if (maxsock < 0) if (maxsock < 0)
@ -1220,12 +1222,43 @@ int inetd_main(int argc, char **argv)
ready_fd_cnt--; ready_fd_cnt--;
ctrl = sep->se_fd; ctrl = sep->se_fd;
accepted_fd = -1; accepted_fd = -1;
if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) { new_udp_fd = -1;
ctrl = accepted_fd = accept(sep->se_fd, NULL, NULL); if (!sep->se_wait) {
if (ctrl < 0) { if (sep->se_socktype == SOCK_STREAM) {
if (errno != EINTR) ctrl = accepted_fd = accept(sep->se_fd, NULL, NULL);
bb_perror_msg("accept (for %s)", sep->se_service); if (ctrl < 0) {
continue; if (errno != EINTR)
bb_perror_msg("accept (for %s)", sep->se_service);
continue;
}
}
/* "nowait" udp */
if (sep->se_socktype == SOCK_DGRAM
&& sep->se_family != AF_UNIX
) {
/* How udp "nowait" works:
* child peeks at (received and buffered by kernel) UDP packet,
* performs connect() on the socket so that it is linked only
* to this peer. But this also affects parent, because descriptors
* are shared after fork() a-la dup(). When parent performs
* select(), it will see this descriptor connected to the peer (!)
* and still readable, will act on it and mess things up
* (can create many copies of same child, etc).
* Parent must create and use new socket instead. */
new_udp_fd = socket(sep->se_family, SOCK_DGRAM, 0);
if (new_udp_fd < 0) { /* error: eat packet, forget about it */
udp_err:
recv(sep->se_fd, line, LINE_SIZE, MSG_DONTWAIT);
continue;
}
setsockopt_reuseaddr(new_udp_fd);
/* TODO: better do bind after vfork in parent,
* so that we don't have two wildcard bound sockets
* even for a brief moment? */
if (bind(new_udp_fd, &sep->se_lsa->u.sa, sep->se_lsa->len) < 0) {
close(new_udp_fd);
goto udp_err;
}
} }
} }
@ -1283,10 +1316,15 @@ int inetd_main(int argc, char **argv)
if (pid > 0) { /* parent */ if (pid > 0) { /* parent */
if (sep->se_wait) { if (sep->se_wait) {
/* tcp wait: we passed listening socket to child,
* will wait for child to terminate */
sep->se_wait = pid; sep->se_wait = pid;
remove_fd_from_set(sep->se_fd); remove_fd_from_set(sep->se_fd);
/* we passed listening socket to child, }
* will wait for child to terminate */ if (new_udp_fd >= 0) {
/* udp nowait: child connected the socket,
* we created and will use new, unconnected one */
xmove_fd(new_udp_fd, sep->se_fd);
} }
restore_sigmask(&omask); restore_sigmask(&omask);
maybe_close(accepted_fd); maybe_close(accepted_fd);
@ -1313,39 +1351,20 @@ int inetd_main(int argc, char **argv)
#endif #endif
/* child */ /* child */
setsid(); setsid();
#if 0 /* "nowait" udp */
/* This does not work. if (new_udp_fd >= 0) {
* Actually, it _almost_ works. The idea behind it is: child
* can peek at (already received and buffered by kernel) UDP packet,
* and perform connect() on the socket so that it is linked only
* to this peer. But this also affects parent, because descriptors
* are shared after fork() a-la dup(). When parent returns to
* select(), it will see this descriptor attached to the peer (!)
* and likely still readable, will act on it and mess things up
* (can create many copies of same child, etc).
* If child will create new socket instead, then bind() and
* connect() it to peer's address, descriptor aliasing problem
* is solved, but first packet cannot be "transferred" to the new
* socket. It is not a problem if child can account for this,
* but our child will exec - and exec'ed program does not know
* about this "lost packet" problem! Pity... */
/* "nowait" udp[6]. Hmmm... */
if (!sep->se_wait
&& sep->se_socktype == SOCK_DGRAM
&& sep->se_family != AF_UNIX
) {
len_and_sockaddr *lsa = xzalloc_lsa(sep->se_family); len_and_sockaddr *lsa = xzalloc_lsa(sep->se_family);
/* peek at the packet and remember peer addr */ /* peek at the packet and remember peer addr */
int r = recvfrom(ctrl, NULL, 0, MSG_PEEK|MSG_DONTWAIT, int r = recvfrom(ctrl, NULL, 0, MSG_PEEK|MSG_DONTWAIT,
&lsa->u.sa, &lsa->len); &lsa->u.sa, &lsa->len);
if (r >= 0) if (r < 0)
/* make this socket "connected" to peer addr: goto do_exit1;
* only packets from this peer will be recv'ed, /* make this socket "connected" to peer addr:
* and bare write()/send() will work on it */ * only packets from this peer will be recv'ed,
connect(ctrl, &lsa->u.sa, lsa->len); * and bare write()/send() will work on it */
connect(ctrl, &lsa->u.sa, lsa->len);
free(lsa); free(lsa);
} }
#endif
/* prepare env and exec program */ /* prepare env and exec program */
pwd = getpwnam(sep->se_user); pwd = getpwnam(sep->se_user);
if (pwd == NULL) { if (pwd == NULL) {