* Generic non-forking server infrastructure.
* Intended to make writing telnetd-type servers easier.
*
- * Copyright (C) 2007 Denis Vlasenko
+ * Copyright (C) 2007 Denys Vlasenko
*
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
*/
-#include "busybox.h"
+#include "libbb.h"
#include "isrv.h"
#define DEBUG 0
/* 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 {
{
int n, newfd;
- fcntl(fd, F_SETFL, (int)(PARAM_TBL[0]) | O_NONBLOCK);
+ /* 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)(PARAM_TBL[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) */
+ * (for example, non-socket). Don't want
+ * to loop forever. */
bb_perror_msg_and_die("accept");
}
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))
}
break; /* all words are zero */
found_fd:
- if (fd >= fd_cnt) /* paranoia */
+ 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;
/* this peer is gone */
remove_peer(state, peer);
} else if (TIMEOUT) {
- TIMEO_TBL[peer] = monotonic_time();
+ TIMEO_TBL[peer] = monotonic_sec();
}
}
}
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: 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)
int (*do_wr)(int fd, void **),
int (*do_timeout)(void **),
int timeout,
- int exit_if_no_clients)
+ int linger_timeout)
{
isrv_state_t *state = xzalloc(sizeof(*state));
state->new_peer = new_peer;
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));
+ /* (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;
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) {
wrp = ≀
}
- DPRINTF("run: select(FD_COUNT:%d,timeout:%d)...", FD_COUNT, timeout);
- n = select(FD_COUNT, &rd, wrp, NULL, timeout ? &tv : NULL);
+ 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) {
continue;
}
- if (exit_if_no_clients && n == 0 && PEER_COUNT <= 1)
+ if (n == 0 && linger_timeout && PEER_COUNT <= 1)
break;
if (timeout) {
- time_t t = monotonic_time();
+ time_t t = monotonic_sec();
if (t != CURTIME) {
CURTIME = t;
handle_timeout(state, do_timeout);
}
}
DPRINTF("run: bailout");
+ /* NB: accept socket is not closed. Caller is to decide what to do */
}