[T106][ZXW-22]7520V3SCV2.01.01.02P42U09_VEC_V0.8_AP_VEC origin source commit

Change-Id: Ic6e05d89ecd62fc34f82b23dcf306c93764aec4b
diff --git a/ap/app/busybox/src/networking/isrv.c b/ap/app/busybox/src/networking/isrv.c
new file mode 100644
index 0000000..1c6491e
--- /dev/null
+++ b/ap/app/busybox/src/networking/isrv.c
@@ -0,0 +1,338 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Generic non-forking server infrastructure.
+ * Intended to make writing telnetd-type servers easier.
+ *
+ * Copyright (C) 2007 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "isrv.h"
+
+#define DEBUG 0
+
+#if DEBUG
+#define DPRINTF(args...) bb_error_msg(args)
+#else
+#define DPRINTF(args...) ((void)0)
+#endif
+
+/* Helpers */
+
+/* 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;
+
+	/* suppress gcc warning "cast from ptr to int of different size" */
+	fcntl(fd, F_SETFL, (int)(ptrdiff_t)(PARAM_TBL[0]) | O_NONBLOCK);
+	newfd = accept(fd, NULL, 0);
+	fcntl(fd, F_SETFL, (int)(ptrdiff_t)(PARAM_TBL[0]));
+	if (newfd < 0) {
+		if (errno == EAGAIN) return;
+		/* Most probably someone gave us wrong fd type
+		 * (for example, non-socket). Don't want
+		 * to loop forever. */
+		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;
+	/* need to know value at _the beginning_ of this routine */
+	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 */
+			DPRINTF("handle_fd_set: fd > fd_cnt?? (%d > %d)",
+					fd, fd_cnt);
+			break;
+		}
+		DPRINTF("handle_fd_set: fd %d is active", fd);
+		peer = FD2PEER[fd];
+		if (peer < 0)
+			continue; /* peer is already gone */
+		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_sec();
+		}
+	}
+}
+
+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 linger_timeout)
+{
+	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 */
+	/* (suppress gcc warning "cast from ptr to int of different size") */
+	PARAM_TBL[0] = (void*)(ptrdiff_t)(fcntl(listen_fd, F_GETFL));
+
+	while (1) {
+		struct timeval tv;
+		fd_set rd;
+		fd_set wr;
+		fd_set *wrp = NULL;
+		int n;
+
+		tv.tv_sec = timeout;
+		if (PEER_COUNT <= 1)
+			tv.tv_sec = linger_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, (int)tv.tv_sec);
+		n = select(FD_COUNT, &rd, wrp, NULL, tv.tv_sec ? &tv : NULL);
+		DPRINTF("run: ...select:%d", n);
+
+		if (n < 0) {
+			if (errno != EINTR)
+				bb_perror_msg("select");
+			continue;
+		}
+
+		if (n == 0 && linger_timeout && PEER_COUNT <= 1)
+			break;
+
+		if (timeout) {
+			time_t t = monotonic_sec();
+			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");
+	/* NB: accept socket is not closed. Caller is to decide what to do */
+}