svn commit: trunk/busybox: include libbb networking

vda at busybox.net vda at busybox.net
Sun Jan 14 01:29:08 UTC 2007


Author: vda
Date: 2007-01-13 17:29:06 -0800 (Sat, 13 Jan 2007)
New Revision: 17275

Log:
By popular request reinstate fakeidentd's standalone mode.
Since this is also needed for other applets like telnetd,
introduce generic driver for such things.
It even supports inetd-wait ('linger') mode, when inetd
hands out listen socket to child and waits to it to die,
instead of handing out accepted socket and continuing
listening itself (nowait mode).
Code growth ~200 bytes.
NB: our inetd doesn't support wait mode yet (or mabe it is buggy).


Added:
   trunk/busybox/networking/isrv.c
   trunk/busybox/networking/isrv_identd.c

Modified:
   trunk/busybox/include/libbb.h
   trunk/busybox/include/usage.h
   trunk/busybox/libbb/xfuncs.c
   trunk/busybox/networking/Kbuild
   trunk/busybox/networking/inetd.c


Changeset:
Modified: trunk/busybox/include/libbb.h
===================================================================
--- trunk/busybox/include/libbb.h	2007-01-14 01:04:33 UTC (rev 17274)
+++ trunk/busybox/include/libbb.h	2007-01-14 01:29:06 UTC (rev 17275)
@@ -278,6 +278,8 @@
 extern void xsetgid(gid_t gid);
 extern void xsetuid(uid_t uid);
 extern void xdaemon(int nochdir, int noclose);
+/* More clever/thorough xdaemon */
+extern void bb_sanitize_stdio(int daemonize);
 extern void xchdir(const char *path);
 extern void xsetenv(const char *key, const char *value);
 extern int xopen(const char *pathname, int flags);

Modified: trunk/busybox/include/usage.h
===================================================================
--- trunk/busybox/include/usage.h	2007-01-14 01:04:33 UTC (rev 17274)
+++ trunk/busybox/include/usage.h	2007-01-14 01:29:06 UTC (rev 17275)
@@ -826,22 +826,16 @@
        "\\( and \\) or null; if \\( and \\) are not used, they return the number\n" \
        "of characters matched or 0."
 
-#if 0 /* bloaty */
 #define fakeidentd_trivial_usage \
-       "[-b ip] [STRING]"
+       "[-fiw] [-b ADDR] [STRING]"
 #define fakeidentd_full_usage \
-       "Return a set string to auth requests" \
-       "\n\nOptions:\n" \
-       "	-b	Bind to ip address\n" \
-       "	STRING	The ident answer string (default is nobody)"
-#else /* inetd-only */
-#define fakeidentd_trivial_usage \
-       "[username]"
-#define fakeidentd_full_usage \
-       "Return a (faked) ident response.\n" \
-       "This applet is meant to run from inetd.\n" \
-       "Optional argument is the username to return (default is 'nobody')."
-#endif
+       "Provide fake ident (auth) service" \
+       "\n\nOptions:" \
+       "\n	-f	Run in foreground" \
+       "\n	-i	Inetd mode" \
+       "\n	-w	Inetd 'wait' mode" \
+       "\n	-b ADDR	Bind to specified address" \
+       "\n	STRING	Ident answer string (default is 'nobody')"
 
 #define false_trivial_usage \
        ""

Modified: trunk/busybox/libbb/xfuncs.c
===================================================================
--- trunk/busybox/libbb/xfuncs.c	2007-01-14 01:04:33 UTC (rev 17274)
+++ trunk/busybox/libbb/xfuncs.c	2007-01-14 01:29:06 UTC (rev 17275)
@@ -509,6 +509,30 @@
 }
 #endif
 
+void bb_sanitize_stdio(int daemonize)
+{
+	int fd;
+	/* Mega-paranoid */
+	fd = xopen(bb_dev_null, O_RDWR);
+	while (fd < 2)
+		fd = dup(fd); /* have 0,1,2 open at least to /dev/null */
+	if (daemonize) {
+		pid_t pid = fork();
+		if (pid < 0) /* wtf? */
+			bb_perror_msg_and_die("fork");
+		if (pid) /* parent */
+			exit(0);
+    		/* child */
+		setsid();
+		/* if daemonizing, make sure we detach from stdio */
+		dup2(fd, 0);
+		dup2(fd, 1);
+		dup2(fd, 2);
+	}
+	while (fd > 2)
+		close(fd--); /* close everything after fd#2 */
+}
+
 // Die with an error message if we can't open a new socket.
 int xsocket(int domain, int type, int protocol)
 {

Modified: trunk/busybox/networking/Kbuild
===================================================================
--- trunk/busybox/networking/Kbuild	2007-01-14 01:04:33 UTC (rev 17274)
+++ trunk/busybox/networking/Kbuild	2007-01-14 01:29:06 UTC (rev 17275)
@@ -9,7 +9,8 @@
 lib-$(CONFIG_ARPING)       += arping.o
 lib-$(CONFIG_DNSD)         += dnsd.o
 lib-$(CONFIG_ETHER_WAKE)   += ether-wake.o
-lib-$(CONFIG_FAKEIDENTD)   += fakeidentd.o
+#lib-$(CONFIG_FAKEIDENTD)   += fakeidentd.o
+lib-$(CONFIG_FAKEIDENTD)   += isrv_identd.o isrv.o
 lib-$(CONFIG_FTPGET)       += ftpgetput.o
 lib-$(CONFIG_FTPPUT)       += ftpgetput.o
 lib-$(CONFIG_HOSTNAME)     += hostname.o

Modified: trunk/busybox/networking/inetd.c
===================================================================
--- trunk/busybox/networking/inetd.c	2007-01-14 01:04:33 UTC (rev 17274)
+++ trunk/busybox/networking/inetd.c	2007-01-14 01:29:06 UTC (rev 17275)
@@ -1289,31 +1289,28 @@
 	if (CONFIG == NULL)
 		bb_error_msg_and_die("non-root must specify a config file");
 
-	if (!(opt & 2)) {
 #ifdef BB_NOMMU
+	if (!(opt & 2)) {
 		/* reexec for vfork() do continue parent */
 		vfork_daemon_rexec(0, 0, argc, argv, "-f");
+	}
+	bb_sanitize_stdio(0);
 #else
-		xdaemon(0, 0);
+	bb_sanitize_stdio(!(opt & 2));
 #endif
-	} else {
-		setsid();
-	}
 	logmode = LOGMODE_SYSLOG;
 
 	if (uid == 0) {
-		gid_t gid = getgid();
-
 		/* If run by hand, ensure groups vector gets trashed */
+		gid_t gid = getgid();
 		setgroups(1, &gid);
 	}
 
 	{
 		FILE *fp = fopen(_PATH_INETDPID, "w");
-
 		if (fp != NULL) {
 			fprintf(fp, "%u\n", getpid());
-			(void) fclose(fp);
+			fclose(fp);
 		}
 	}
 

Added: trunk/busybox/networking/isrv.c
===================================================================
--- trunk/busybox/networking/isrv.c	                        (rev 0)
+++ trunk/busybox/networking/isrv.c	2007-01-14 01:29:06 UTC (rev 17275)
@@ -0,0 +1,337 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Generic non-forking server infrastructure.
+ * Intended to make writing telnetd-type servers easier.
+ *
+ * Copyright (C) 2007 Denis Vlasenko
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include "isrv.h"
+
+#define DEBUG 0
+
+#if DEBUG
+#define DPRINTF(args...) bb_error_msg(args)
+#else
+#define DPRINTF(args...) ((void)0)
+#endif
+
+/* Helpers */
+
+#if 0 /*def _POSIX_MONOTONIC_CLOCK*/
+static time_t monotonic_time(void)
+{
+	struct timespec ts;
+	if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
+		time(&ts.tv_sec);
+	return ts.tv_sec;
+}
+#else
+#define monotonic_time() (time(NULL))
+#endif
+
+/* Opaque structure */
+
+struct isrv_state_t {
+	short  *fd2peer; /* one per registered fd */
+	void  **param_tbl; /* one per registered peer */
+	/* one per registered peer; doesn't exist if !timeout */
+	time_t *timeo_tbl;
+	int   (*new_peer)(isrv_state_t *state, int fd);
+	time_t  curtime;
+	int     timeout;
+	int     fd_count;
+	int     peer_count;
+	int     wr_count;
+	fd_set  rd;
+	fd_set  wr;
+};
+#define FD2PEER    (state->fd2peer)
+#define PARAM_TBL  (state->param_tbl)
+#define TIMEO_TBL  (state->timeo_tbl)
+#define CURTIME    (state->curtime)
+#define TIMEOUT    (state->timeout)
+#define FD_COUNT   (state->fd_count)
+#define PEER_COUNT (state->peer_count)
+#define WR_COUNT   (state->wr_count)
+
+/* callback */
+void isrv_want_rd(isrv_state_t *state, int fd)
+{
+	FD_SET(fd, &state->rd);
+}
+
+/* callback */
+void isrv_want_wr(isrv_state_t *state, int fd)
+{
+	if (!FD_ISSET(fd, &state->wr)) {
+		WR_COUNT++;
+		FD_SET(fd, &state->wr);
+	}
+}
+
+/* callback */
+void isrv_dont_want_rd(isrv_state_t *state, int fd)
+{
+	FD_CLR(fd, &state->rd);
+}
+
+/* callback */
+void isrv_dont_want_wr(isrv_state_t *state, int fd)
+{
+	if (FD_ISSET(fd, &state->wr)) {
+		WR_COUNT--;
+		FD_CLR(fd, &state->wr);
+	}
+}
+
+/* callback */
+int isrv_register_fd(isrv_state_t *state, int peer, int fd)
+{
+	int n;
+
+	DPRINTF("register_fd(peer:%d,fd:%d)", peer, fd);
+
+	if (FD_COUNT >= FD_SETSIZE) return -1;
+	if (FD_COUNT <= fd) {
+		n = FD_COUNT;
+		FD_COUNT = fd + 1;
+
+		DPRINTF("register_fd: FD_COUNT %d", FD_COUNT);
+
+		FD2PEER = xrealloc(FD2PEER, FD_COUNT * sizeof(FD2PEER[0]));
+		while (n < fd) FD2PEER[n++] = -1;
+	}
+
+	DPRINTF("register_fd: FD2PEER[%d] = %d", fd, peer);
+
+	FD2PEER[fd] = peer;
+	return 0;
+}
+
+/* callback */
+void isrv_close_fd(isrv_state_t *state, int fd)
+{
+	DPRINTF("close_fd(%d)", fd);
+
+	close(fd);
+	isrv_dont_want_rd(state, fd);
+	if (WR_COUNT) isrv_dont_want_wr(state, fd);
+
+	FD2PEER[fd] = -1;
+	if (fd == FD_COUNT-1) {
+		do fd--; while (fd >= 0 && FD2PEER[fd] == -1);
+		FD_COUNT = fd + 1;
+
+		DPRINTF("close_fd: FD_COUNT %d", FD_COUNT);
+
+		FD2PEER = xrealloc(FD2PEER, FD_COUNT * sizeof(FD2PEER[0]));
+	}
+}
+
+/* callback */
+int isrv_register_peer(isrv_state_t *state, void *param)
+{
+	int n;
+
+	if (PEER_COUNT >= FD_SETSIZE) return -1;
+	n = PEER_COUNT++;
+
+	DPRINTF("register_peer: PEER_COUNT %d", PEER_COUNT);
+
+	PARAM_TBL = xrealloc(PARAM_TBL, PEER_COUNT * sizeof(PARAM_TBL[0]));
+	PARAM_TBL[n] = param;
+	if (TIMEOUT) {
+		TIMEO_TBL = xrealloc(TIMEO_TBL, PEER_COUNT * sizeof(TIMEO_TBL[0]));
+		TIMEO_TBL[n] = CURTIME;
+	}
+	return n;
+}
+
+static void remove_peer(isrv_state_t *state, int peer)
+{
+	int movesize;
+	int fd;
+
+	DPRINTF("remove_peer(%d)", peer);
+
+	fd = FD_COUNT - 1;
+	while (fd >= 0) {
+		if (FD2PEER[fd] == peer) {
+			isrv_close_fd(state, fd);
+			fd--;
+			continue;
+		}
+		if (FD2PEER[fd] > peer)
+			FD2PEER[fd]--;
+		fd--;
+	}
+
+	PEER_COUNT--;
+	DPRINTF("remove_peer: PEER_COUNT %d", PEER_COUNT);
+
+	movesize = (PEER_COUNT - peer) * sizeof(void*);
+	if (movesize > 0) {
+		memcpy(&PARAM_TBL[peer], &PARAM_TBL[peer+1], movesize);
+		if (TIMEOUT)
+			memcpy(&TIMEO_TBL[peer], &TIMEO_TBL[peer+1], movesize);
+	}
+	PARAM_TBL = xrealloc(PARAM_TBL, PEER_COUNT * sizeof(PARAM_TBL[0]));
+	if (TIMEOUT)
+		TIMEO_TBL = xrealloc(TIMEO_TBL, PEER_COUNT * sizeof(TIMEO_TBL[0]));
+}
+
+static void handle_accept(isrv_state_t *state, int fd)
+{
+	int n, newfd;
+
+	fcntl(fd, F_SETFL, (int)(PARAM_TBL[0]) | O_NONBLOCK);
+	newfd = accept(fd, NULL, 0);
+	fcntl(fd, F_SETFL, (int)(PARAM_TBL[0]));
+	if (newfd < 0) {
+		if (errno == EAGAIN) return;
+		/* Most probably someone gave us wrong fd type
+		 * (for example, non-socket) */
+		bb_perror_msg_and_die("accept");
+	}
+
+	DPRINTF("new_peer(%d)", newfd);
+	n = state->new_peer(state, newfd);
+	if (n)
+		remove_peer(state, n); /* unsuccesful peer start */
+}
+
+void BUG_sizeof_fd_set_is_strange(void);
+static void handle_fd_set(isrv_state_t *state, fd_set *fds, int (*h)(int, void **))
+{
+	enum { LONG_CNT = sizeof(fd_set) / sizeof(long) };
+	int fds_pos;
+	int fd, peer;
+	int fd_cnt = FD_COUNT;
+
+	if (LONG_CNT * sizeof(long) != sizeof(fd_set))
+		BUG_sizeof_fd_set_is_strange();
+
+	fds_pos = 0;
+	while (1) {
+		/* Find next nonzero bit */
+		while (fds_pos < LONG_CNT) {
+			if (((long*)fds)[fds_pos] == 0) {
+				fds_pos++;
+				continue;
+			}
+			/* Found non-zero word */
+			fd = fds_pos * sizeof(long)*8; /* word# -> bit# */
+			while (1) {
+				if (FD_ISSET(fd, fds)) {
+					FD_CLR(fd, fds);
+					goto found_fd;
+				}
+				fd++;
+			}
+		}
+		break; /* all words are zero */
+ found_fd:
+		if (fd >= fd_cnt) /* paranoia */
+			break;
+		DPRINTF("handle_fd_set: fd %d is active", fd);
+		peer = FD2PEER[fd];
+		if (peer == 0) {
+			handle_accept(state, fd);
+			continue;
+		}
+		DPRINTF("h(fd:%d)", fd);
+		if (h(fd, &PARAM_TBL[peer])) {
+			/* this peer is gone */
+			remove_peer(state, peer);
+		} else if (TIMEOUT) {
+			TIMEO_TBL[peer] = monotonic_time();
+		}
+	}
+}
+
+static void handle_timeout(isrv_state_t *state, int (*do_timeout)(void **))
+{
+	int n, peer;
+	peer = PEER_COUNT-1;
+	/* peer 0 is not checked */
+	while (peer > 0) {
+		DPRINTF("peer %d: time diff %d", peer, (int)(CURTIME - TIMEO_TBL[peer]));
+
+		if ((CURTIME - TIMEO_TBL[peer]) > TIMEOUT) {
+			DPRINTF("peer %d: do_timeout()", peer);
+			n = do_timeout(&PARAM_TBL[peer]);
+			if (n)
+				remove_peer(state, peer);
+		}
+		peer--;
+	}
+}
+
+/* Driver */
+void isrv_run(
+	int listen_fd,
+	int (*new_peer)(isrv_state_t *state, int fd),
+	int (*do_rd)(int fd, void **),
+	int (*do_wr)(int fd, void **),
+	int (*do_timeout)(void **),
+	int timeout,
+	int exit_if_no_clients)
+{
+	isrv_state_t *state = xzalloc(sizeof(*state));
+	state->new_peer = new_peer;
+	state->timeout  = timeout;
+
+	/* register "peer" #0 - it will accept new connections */
+	isrv_register_peer(state, NULL);
+	isrv_register_fd(state, /*peer:*/ 0, listen_fd);
+	isrv_want_rd(state, listen_fd);
+	/* remember flags to make blocking<->nonblocking switch faster */
+	PARAM_TBL[0] = (void*) (fcntl(listen_fd, F_GETFL, 0));
+
+	while (1) {
+		struct timeval tv;
+		fd_set rd;
+		fd_set wr;
+		fd_set *wrp = NULL;
+		int n;
+
+		tv.tv_sec = timeout;
+		tv.tv_usec = 0;
+		rd = state->rd;
+		if (WR_COUNT) {
+			wr = state->wr;
+			wrp = &wr;
+		}
+
+		DPRINTF("run: select(FD_COUNT:%d,timeout:%d)...", FD_COUNT, timeout);
+		n = select(FD_COUNT, &rd, wrp, NULL, timeout ? &tv : NULL);
+		DPRINTF("run: ...select:%d", n);
+
+		if (n < 0) {
+			if (errno != EINTR)
+				bb_perror_msg("select");
+			continue;
+		}
+
+		if (exit_if_no_clients && n == 0 && PEER_COUNT <= 1)
+			break;
+
+		if (timeout) {
+			time_t t = monotonic_time();
+			if (t != CURTIME) {
+				CURTIME = t;
+				handle_timeout(state, do_timeout);
+			}
+		}
+		if (n > 0) {
+			handle_fd_set(state, &rd, do_rd);
+			if (wrp)
+				handle_fd_set(state, wrp, do_wr);
+		}
+	}
+	DPRINTF("run: bailout");
+}

Added: trunk/busybox/networking/isrv_identd.c
===================================================================
--- trunk/busybox/networking/isrv_identd.c	                        (rev 0)
+++ trunk/busybox/networking/isrv_identd.c	2007-01-14 01:29:06 UTC (rev 17275)
@@ -0,0 +1,144 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Fake identd server.
+ *
+ * Copyright (C) 2007 Denis Vlasenko
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include <syslog.h>
+#include "busybox.h"
+#include "isrv.h"
+
+enum { TIMEOUT = 20 };
+
+/* Why use alarm(TIMEOUT-1)?
+ * isrv's internal select() will run with timeout=TIMEOUT.
+ * If nothing happens during TIMEOUT-1 seconds (no accept/read),
+ * then ALL sessions timed out by now. Instead of closing them one-by-one
+ * (isrv calls do_timeout for each 'stale' session),
+ * SIGALRM triggered by alarm(TIMEOUT-1) will kill us, terminating them all.
+ */
+
+typedef struct identd_buf_t {
+	int pos;
+	int fd_flag;
+	char buf[64 - 2*sizeof(int)];
+} identd_buf_t;
+
+static const char *bogouser = "nobody";
+
+static int new_peer(isrv_state_t *state, int fd)
+{
+	int peer;
+	identd_buf_t *buf = xzalloc(sizeof(*buf));
+
+	alarm(TIMEOUT - 1);
+
+	peer = isrv_register_peer(state, buf);
+	if (peer < 0)
+		return 0; /* failure */
+	if (isrv_register_fd(state, peer, fd) < 0)
+		return peer; /* failure, unregister peer */
+
+	buf->fd_flag = fcntl(fd, F_GETFL, 0) | O_NONBLOCK;
+	isrv_want_rd(state, fd);
+	return 0;
+}
+
+static int do_rd(int fd, void **paramp)
+{
+	identd_buf_t *buf = *paramp;
+	char *cur, *p;
+	int sz;
+
+	alarm(TIMEOUT - 1);
+
+	cur = buf->buf + buf->pos;
+
+	fcntl(fd, F_SETFL, buf->fd_flag | O_NONBLOCK);
+	sz = safe_read(fd, cur, sizeof(buf->buf) - buf->pos);
+
+	if (sz < 0) {
+		if (errno != EAGAIN)
+			goto term; /* terminate this session if !EAGAIN */
+		goto ok;
+	}
+
+	buf->pos += sz;
+	buf->buf[buf->pos] = '\0';
+	p = strpbrk(cur, "\r\n");
+	if (p)
+		*p = '\0';
+	if (p || !sz || buf->pos == sizeof(buf->buf)) {
+		/* fd is still in nonblocking mode - we never block here */
+		fdprintf(fd, "%s : USERID : UNIX : %s\r\n", buf->buf, bogouser);
+		goto term;
+	}
+ ok:
+	fcntl(fd, F_SETFL, buf->fd_flag & ~O_NONBLOCK);
+	return 0;
+ term:
+	fcntl(fd, F_SETFL, buf->fd_flag & ~O_NONBLOCK);
+	free(buf);
+	return 1;
+}
+
+static int do_timeout(void **paramp)
+{
+	return 1; /* terminate session */
+}
+
+static void inetd_mode(void)
+{
+	identd_buf_t *buf = xzalloc(sizeof(*buf));
+	/* We do NOT want nonblocking I/O here! */
+	buf->fd_flag = fcntl(0, F_GETFL, 0);
+	while (do_rd(0, (void*)&buf) == 0) /* repeat */;
+}
+
+int fakeidentd_main(int argc, char **argv)
+{
+	enum {
+		OPT_foreground = 0x1,
+		OPT_inetd      = 0x2,
+		OPT_inetdwait  = 0x4,
+		OPT_nodeamon   = 0x7,
+		OPT_bindaddr   = 0x8,
+	};
+
+	const char *bind_address = NULL;
+	unsigned opt;
+	int fd;
+
+	opt = getopt32(argc, argv, "fiwb:", &bind_address);
+	if (optind < argc)
+		bogouser = argv[optind];
+
+	/* Daemonize if no -f or -i or -w */
+	bb_sanitize_stdio(!(opt & OPT_nodeamon));
+	if (!(opt & OPT_nodeamon)) {
+		openlog(applet_name, 0, LOG_DAEMON);
+		logmode = LOGMODE_SYSLOG;
+	}
+
+	if (opt & OPT_inetd) {
+		inetd_mode();
+		return 0;
+	}
+
+	/* Ignore closed connections when writing */
+	signal(SIGPIPE, SIG_IGN);
+
+	if (opt & OPT_inetdwait) {
+		fd = 0;
+	} else {
+		fd = create_and_bind_stream_or_die(bind_address,
+				bb_lookup_port("identd", "tcp", 113));
+		xlisten(fd, 5);
+	}
+
+	isrv_run(fd, new_peer, do_rd, NULL, do_timeout, TIMEOUT, 1);
+	return 0;
+}




More information about the busybox-cvs mailing list