httpd: fix handling of range requests
[oweals/busybox.git] / networking / isrv.c
index 02ca1d787f65c0874f54296a852a01d161f16f54..3673db715e45b58a8f78f7f0192fa33fff61f2f7 100644 (file)
@@ -3,12 +3,12 @@
  * 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 {
@@ -188,13 +176,15 @@ static void handle_accept(isrv_state_t *state, int fd)
 {
        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");
        }
 
@@ -204,16 +194,15 @@ static void handle_accept(isrv_state_t *state, int fd)
                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();
+       BUILD_BUG_ON(LONG_CNT * sizeof(long) != sizeof(fd_set));
 
        fds_pos = 0;
        while (1) {
@@ -235,10 +224,15 @@ static void handle_fd_set(isrv_state_t *state, fd_set *fds, int (*h)(int, void *
                }
                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;
@@ -248,7 +242,7 @@ static void handle_fd_set(isrv_state_t *state, fd_set *fds, int (*h)(int, void *
                        /* this peer is gone */
                        remove_peer(state, peer);
                } else if (TIMEOUT) {
-                       TIMEO_TBL[peer] = monotonic_time();
+                       TIMEO_TBL[peer] = monotonic_sec();
                }
        }
 }
@@ -259,9 +253,9 @@ static void handle_timeout(isrv_state_t *state, int (*do_timeout)(void **))
        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)
@@ -279,7 +273,7 @@ void isrv_run(
        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;
@@ -290,7 +284,8 @@ void isrv_run(
        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;
@@ -300,6 +295,8 @@ void isrv_run(
                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) {
@@ -307,8 +304,9 @@ void isrv_run(
                        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(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) {
@@ -317,11 +315,11 @@ void isrv_run(
                        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);
@@ -334,4 +332,5 @@ void isrv_run(
                }
        }
        DPRINTF("run: bailout");
+       /* NB: accept socket is not closed. Caller is to decide what to do */
 }