]> granicus.if.org Git - fcron/commitdiff
refactored socket.[ch] into select.[ch] and fcrondyn_srv.[ch]
authorThibault Godouet <fcron@free.fr>
Sun, 12 Jun 2016 09:32:03 +0000 (10:32 +0100)
committerThibault Godouet <fcron@free.fr>
Sun, 12 Jun 2016 09:32:03 +0000 (10:32 +0100)
This is to prepare for getting notifed of time changes via timerfd_create() and select ()

Makefile.in
config.h.in
configure.in
fcron.c
fcrondyn_svr.c [moved from socket.c with 93% similarity]
fcrondyn_svr.h [moved from socket.h with 82% similarity]
global.h
log.h
select.c [new file with mode: 0644]
select.h [new file with mode: 0644]

index f9ee3be0092244d897d173e6833d08bcd7fb6c71..5349819b765736fd592bdcaa72dbc686028b70c9 100644 (file)
@@ -73,9 +73,9 @@ OPTION :=
 VERSION := @VERSION@
 CFLAGS += $(OPTIM) $(OPTION) $(DEFS) $(CPPFLAGS)
 ifeq ($(FCRONDYN), 1)
-LIBOBJS := socket.o $(LIBOBJS)
+LIBOBJS := $(LIBOBJS)
 endif
-OBJSD := fcron.o cl.o subs.o mem.o save.o temp_file.o log.o database.o job.o conf.o u_list.o exe_list.o lavg_list.o env_list.o fcronconf.o filesubs.o $(LIBOBJS)
+OBJSD := fcron.o cl.o subs.o mem.o save.o temp_file.o log.o database.o job.o conf.o u_list.o exe_list.o lavg_list.o env_list.o fcronconf.o filesubs.o select.o fcrondyn_svr.o $(LIBOBJS)
 OBJSTAB := fcrontab.o cl.o subs.o mem.o save.o temp_file.o  log.o fileconf.o allow.o read_string.o u_list.o env_list.o fcronconf.o filesubs.o
 OBJSDYN := fcrondyn.o subs.o mem.o log.o allow.o read_string.o fcronconf.o filesubs.o
 OBJCONV := convert-fcrontab.o cl.o subs.o mem.o save.o log.o u_list.o env_list.o fcronconf.o filesubs.o
index 74dc8f8a5f2dd8b46f65f686b8a4fcc36d88691d..97b56d7292c42ed3935b3fcac0b482264677e069 100644 (file)
 /* Define if you have the <sys/resource.h> header file.  */
 #undef HAVE_SYS_RESOURCE_H
 
+/* Define if you have the <sys/select.h> header file.  */
+#undef HAVE_SYS_SELECT_H
+
 /* Define if you have the <sys/socket.h> header file.  */
 #undef HAVE_SYS_SOCKET_H
 
index 1deb408791e900b7ad67a5e8cda46dd7759395f2..56694efbff179b40cb456512c18f440b4f7db318 100644 (file)
@@ -54,7 +54,7 @@ AC_CHECK_HEADERS(errno.h sys/fcntl.h getopt.h limits.h)
 AC_CHECK_HEADERS(stdarg.h)
 AC_CHECK_HEADERS(termios.h)
 AC_CHECK_HEADERS(strings.h)
-AC_CHECK_HEADERS(sys/types.h sys/socket.h sys/un.h)
+AC_CHECK_HEADERS(sys/select.h sys/types.h sys/socket.h sys/un.h)
 AC_CHECK_HEADERS(security/pam_appl.h pam/pam_appl.h crypt.h shadow.h libaudit.h)
 AC_CHECK_HEADERS(sys/resource.h)
 AC_CHECK_HEADERS(grp.h)
diff --git a/fcron.c b/fcron.c
index 15dc37ce20d508ea97f5efe04da02f1984110680..7f4599830c22a21d6f1b0d5e52b8ad0ac94a65c2 100644 (file)
--- a/fcron.c
+++ b/fcron.c
@@ -29,8 +29,9 @@
 #include "job.h"
 #include "temp_file.h"
 #include "fcronconf.h"
+#include "select.h"
 #ifdef FCRONDYN
-#include "socket.h"
+#include "fcrondyn_svr.h"
 #endif
 
 
@@ -45,7 +46,7 @@ RETSIGTYPE sigchild_handler(int x);
 RETSIGTYPE sigusr1_handler(int x);
 RETSIGTYPE sigusr2_handler(int x);
 RETSIGTYPE sigcont_handler(int x);
-long int get_suspend_duration(time_t slept_from);
+long int read_suspend_duration(time_t slept_from);
 void check_suspend(time_t slept_from, time_t nwt);
 int parseopt(int argc, char *argv[]);
 void get_lock(void);
@@ -112,7 +113,6 @@ short int lavg_serial_running;  /* number of serialized lavg job being running *
 
 exe_list_t *exe_list;           /* jobs which are executed */
 
-time_t begin_sleep;             /* the time at which sleep began */
 time_t now;                     /* the current time */
 
 #ifdef HAVE_LIBPAM
@@ -204,7 +204,7 @@ xexit(int exit_value)
     save_file(NULL);
 
 #ifdef FCRONDYN
-    close_socket();
+    fcrondyn_socket_close(NULL);
 #endif
 
     f = file_base;
@@ -534,8 +534,9 @@ sigcont_handler(int x)
 }
 
 long int
-get_suspend_duration(time_t slept_from)
-  /* Return the amount of time the system was suspended (to mem or disk).
+read_suspend_duration(time_t slept_from)
+  /* Return the amount of time the system was suspended (to mem or disk),
+   * as read from suspendfile.
    * Return 0 on error.
    *
    * The idea is that:
@@ -660,7 +661,7 @@ check_suspend(time_t slept_from, time_t nwt)
     long int suspend_duration;  /* amount of time the system was suspended */
     long int time_diff;         /* estimate of suspend_duration (as fallback) */
 
-    suspend_duration = get_suspend_duration(slept_from);
+    suspend_duration = read_suspend_duration(slept_from);
 
     /* Also check if there was an unaccounted sleep duration, in case
      * the OS is not configured to let fcron properly know about suspends
@@ -852,11 +853,6 @@ main(int argc, char **argv)
     lavg_list->max_entries = lavg_queue_max;
     lavg_serial_running = 0;
 
-#ifdef FCRONDYN
-    /* initialize socket */
-    init_socket();
-#endif
-
     /* initialize random number generator :
      * WARNING : easy to guess !!! */
     /* we use the hostname and tv_usec in order to get different seeds
@@ -947,15 +943,20 @@ main_loop()
     time_t slept_from;          /* time it was when we went into sleep */
     time_t nwt;                 /* next wake time */
 #ifdef HAVE_GETTIMEOFDAY
+    struct select_instance main_select;
     struct timeval now_tv;      /* we use usec field to get more precision */
-#endif
-#if defined(FCRONDYN) && defined(HAVE_GETTIMEOFDAY)
-    int retcode = 0;
     struct timeval sleep_tv;    /* we use usec field to get more precision */
 #endif
 
     debug("Entering main loop");
 
+#ifdef HAVE_GETTIMEOFDAY
+    select_init(&main_select);
+#ifdef FCRONDYN
+    fcrondyn_socket_init(&main_select);
+#endif
+#endif
+
     now = my_time();
 
     synchronize_dir(".", is_system_startup());
@@ -979,7 +980,6 @@ main_loop()
         slept_from = time(NULL);
 
 #ifdef HAVE_GETTIMEOFDAY
-#ifdef FCRONDYN
         gettimeofday(&now_tv, NULL);
         debug("now gettimeofday tv_sec=%ld, tv_usec=%ld %s", now_tv.tv_sec,
               now_tv.tv_usec, ctime(&nwt));
@@ -1011,19 +1011,7 @@ main_loop()
         /* note: read_set is set in socket.c */
         debug("nwt=%s, sleep sec=%ld, usec=%ld", ctime(&nwt), sleep_tv.tv_sec,
               sleep_tv.tv_usec);
-        if ((retcode =
-             select(set_max_fd + 1, &read_set, NULL, NULL, &sleep_tv)) < 0
-            && errno != EINTR)
-            die_e("select returned %d", errno);
-#else                           /* FCRONDYN */
-        if (nwt - now > 0) {
-            sleep(nwt - now - 1);
-        }
-        gettimeofday(&now_tv, NULL);
-        /* we set tv_usec to slightly more than necessary to avoid
-         * infinite loop */
-        usleep(1000000 + min_sleep_usec - now_tv.tv_usec);
-#endif                          /* FCRONDYN */
+        select_call(&main_select, &sleep_tv);
 #else                           /* HAVE_GETTIMEOFDAY */
         if (nwt - now > 0) {
             sleep(nwt - now);
@@ -1087,7 +1075,7 @@ main_loop()
 #if defined(FCRONDYN) && defined(HAVE_GETTIMEOFDAY)
         /* check if there's a new connection, a new command to answer, etc ... */
         /* we do that *after* other checks, to avoid Denial Of Service attacks */
-        check_socket(retcode);
+        fcrondyn_socket_check(&main_select);
 #endif
 
         nwt = check_lavg(save);
similarity index 93%
rename from socket.c
rename to fcrondyn_svr.c
index b3016a0ac0d47edaa6254d4acd4e63252baa5c37..497b69ef25060726e7cd336156836783340c448c 100644 (file)
--- a/socket.c
 
 /* This file contains all fcron's code (server) to handle communication with fcrondyn */
 
-
 #include "fcron.h"
-#include "socket.h"
+#include "fcrondyn_svr.h"
+#include "select.h"
 #include "getloadavg.h"
 #include "database.h"
 #include "fcronconf.h"
 
 
 void remove_connection(struct fcrondyn_cl **client,
-                       struct fcrondyn_cl *prev_client);
+                       struct fcrondyn_cl *prev_client, select_instance *si);
 void exe_cmd(struct fcrondyn_cl *client);
 #ifdef SO_PEERCRED              /* linux */
 void auth_client_so_peercred(struct fcrondyn_cl *client);
@@ -52,14 +52,9 @@ void cmd_renice(struct fcrondyn_cl *client, long int *cmd, int fd, exe_t * e,
 void cmd_send_signal(struct fcrondyn_cl *client, long int *cmd, int fd,
                      exe_t * e);
 void cmd_run(struct fcrondyn_cl *client, long int *cmd, int fd, int is_root);
-void add_to_select_set(int fd, fd_set * set, int *max_fd);
-void remove_from_select_set(int fd, fd_set * set, int *max_fd);
 
 fcrondyn_cl *fcrondyn_cl_base;  /* list of connected fcrondyn clients */
 int fcrondyn_cl_num = 0;        /* number of fcrondyn clients currently connected */
-fd_set read_set;                /* client fds list : cmd waiting ? */
-fd_set master_set;              /* master set : needed since select() modify read_set */
-int set_max_fd = 0;             /* needed by select() */
 int listen_fd = -1;             /* fd which catches incoming connection */
 int auth_fail = 0;              /* number of auth failure */
 time_t auth_nofail_since = 0;   /* we refuse auth since x due to too many failures */
@@ -109,49 +104,13 @@ char err_others_nallowed_str[] =
 
 
 void
-add_to_select_set(int fd, fd_set * set, int *max_fd)
-    /* add fd to set, and update max_fd if necessary (for select()) */
-{
-    FD_SET(fd, set);
-    if (fd > *max_fd)
-        *max_fd = fd;
-}
-
-
-void
-remove_from_select_set(int fd, fd_set * set, int *max_fd)
-    /* remove fd to set, and update max_fd if necessary (for select()) */
-{
-    FD_CLR(fd, set);
-    if (fd == *max_fd) {
-        /* find the biggest fd in order to update max_fd */
-        struct fcrondyn_cl *client;
-        int tmp_max_fd = listen_fd;
-
-        for (client = fcrondyn_cl_base; client != NULL;
-             client = client->fcl_next) {
-            if (client->fcl_sock_fd > tmp_max_fd)
-                tmp_max_fd = client->fcl_sock_fd;
-        }
-
-        /* update max_fd */
-        *max_fd = tmp_max_fd;
-    }
-}
-
-
-void
-init_socket(void)
+fcrondyn_socket_init(select_instance *si)
     /* do everything needed to get a working listening socket */
 {
     struct sockaddr_un addr;
     int len = 0;
     int sun_len = 0;
 
-    /* used in fcron.c:main_loop():select() */
-    FD_ZERO(&read_set);
-    FD_ZERO(&master_set);
-
     if ((listen_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
         error_e("Could not create socket : fcrondyn won't work");
         return;
@@ -201,16 +160,13 @@ init_socket(void)
     }
 
     /* no error */
-    add_to_select_set(listen_fd, &master_set, &set_max_fd);
+    select_add_read(si, listen_fd);
 
-    /* copy master in read_fs, because read_fs will be modified by select() */
-    read_set = master_set;
-    debug("Socket initialized : listen_fd : %d set_max_fd : %d ", listen_fd,
-          set_max_fd);
+    debug("Socket initialized : listen_fd : %d", listen_fd);
     return;
 
  err:
-    close_socket();
+    fcrondyn_socket_close(si);
 
 }
 
@@ -886,13 +842,13 @@ exe_cmd(struct fcrondyn_cl *client)
 }
 
 void
-remove_connection(struct fcrondyn_cl **client, struct fcrondyn_cl *prev_client)
+remove_connection(struct fcrondyn_cl **client, struct fcrondyn_cl *prev_client, select_instance *si)
 /* close the connection, remove it from the list 
 and make client points to the next entry */
 {
     debug("closing connection : fd : %d", (*client)->fcl_sock_fd);
     shutdown((*client)->fcl_sock_fd, SHUT_RDWR);
-    remove_from_select_set((*client)->fcl_sock_fd, &master_set, &set_max_fd);
+    select_rm_read(si, (*client)->fcl_sock_fd);
     xclose_check(&((*client)->fcl_sock_fd), "client fd");
     if (prev_client == NULL) {
         fcrondyn_cl_base = (*client)->fcl_next;
@@ -910,7 +866,7 @@ and make client points to the next entry */
 }
 
 void
-check_socket(int num)
+fcrondyn_socket_check(select_instance *si)
     /* check for new connection, command, connection closed */
 {
     int fd = -1, avoid_fd = -1;
@@ -920,19 +876,19 @@ check_socket(int num)
     int read_len = 0;
     struct fcrondyn_cl *client = NULL, *prev_client = NULL;
 
-    if (num <= 0)
+    if (si->retcode <= 0)
         /* no socket to check : go directly to the end of that function */
-        goto final_settings;
+        return;
 
     debug("Checking socket ...");
 
-    if (FD_ISSET(listen_fd, &read_set)) {
+    if (FD_ISSET(listen_fd, &si->readfds)) {
         debug("got new connection ...");
         fd = accept(listen_fd, (struct sockaddr *)&client_addr, &addr_len);
         if (fd == -1) {
             error_e
                 ("could not accept new connection : isset(listen_fd = %d) = %d",
-                 listen_fd, FD_ISSET(listen_fd, &read_set));
+                 listen_fd, FD_ISSET(listen_fd, &si->readfds));
         }
         else {
             fcntl(fd, F_SETFD, 1);
@@ -957,7 +913,7 @@ check_socket(int num)
                 /* to avoid trying to read from it in this call */
                 avoid_fd = fd;
 
-                add_to_select_set(fd, &master_set, &set_max_fd);
+                select_add_read(si, fd);
                 fcrondyn_cl_num += 1;
 
                 debug("Added connection fd : %d - %d connections", fd,
@@ -974,19 +930,19 @@ check_socket(int num)
 
     client = fcrondyn_cl_base;
     while (client != NULL) {
-        if (!FD_ISSET(client->fcl_sock_fd, &read_set)
+        if (!FD_ISSET(client->fcl_sock_fd, &si->readfds)
             || client->fcl_sock_fd == avoid_fd) {
             /* check if the connection has not been idle for too long ... */
             if (client->fcl_user == NULL
                 && now - client->fcl_idle_since > MAX_AUTH_TIME) {
                 warn("Connection with no auth for more than %ds : closing it.",
                      MAX_AUTH_TIME);
-                remove_connection(&client, prev_client);
+                remove_connection(&client, prev_client, si);
             }
             else if (now - client->fcl_idle_since > MAX_IDLE_TIME) {
                 warn("Connection of %s is idle for more than %ds : closing it.",
                      client->fcl_user, MAX_IDLE_TIME);
-                remove_connection(&client, prev_client);
+                remove_connection(&client, prev_client, si);
             }
             else {
                 /* nothing to do on this one ... check the next one */
@@ -1000,7 +956,7 @@ check_socket(int num)
              recv(client->fcl_sock_fd, buf_int, sizeof(buf_int), 0)) <= 0) {
             if (read_len == 0) {
                 /* connection closed by client */
-                remove_connection(&client, prev_client);
+                remove_connection(&client, prev_client, si);
             }
             else {
                 error_e("error recv() from sock fd %d", client->fcl_sock_fd);
@@ -1024,15 +980,13 @@ check_socket(int num)
         }
     }
 
- final_settings:
-    /* copy master_set in read_set, because read_set is modified by select() */
-    read_set = master_set;
 }
 
 
 void
-close_socket(void)
-    /* close connections, close socket, remove socket file */
+fcrondyn_socket_close(select_instance *si)
+    /* close connections, close socket, remove socket file.
+     * If si is not NULL, then remove the fds from si's readfds */
 {
     struct fcrondyn_cl *client, *client_buf = NULL;
 
@@ -1045,6 +999,9 @@ close_socket(void)
         while (client != NULL) {
             shutdown(client->fcl_sock_fd, SHUT_RDWR);
             xclose_check(&(client->fcl_sock_fd), "client fd");
+            if (si != NULL) {
+                select_rm_read(si, client->fcl_sock_fd);
+            }
 
             client_buf = client->fcl_next;
             Free_safe(client);
similarity index 82%
rename from socket.h
rename to fcrondyn_svr.h
index 79a64d1cc1ebeba65f13841203b38f1a69a9b496..a75f531f832a58081ad71028f58af410201fbb32 100644 (file)
--- a/socket.h
 
 /* This file describe the communication protocol between fcron and fcrondyn */
 
-#ifndef __SOCKET_H__
-#define __SOCKET_H__
+#ifndef __FCRONDYN_SVR_H__
+#define __FCRONDYN_SVR_H__
 
 #include "dyncom.h"
+#include "select.h"
 #ifdef HAVE_SYS_RESOURCE_H
 /* needed by setpriority() */
 #include <sys/resource.h>
 #endif
 
-/* public var defined by socket.c */
-extern fd_set read_set;
-extern int set_max_fd;
-
 /* functions prototypes */
-extern void init_socket(void);
-extern void check_socket(int num);
-extern void close_socket(void);
-
-
+extern void fcrondyn_socket_init(select_instance *si);
+extern void fcrondyn_socket_check(select_instance *si);
+extern void fcrondyn_socket_close(select_instance *si);
 
 /* struct used by fcron : */
 typedef struct fcrondyn_cl {
@@ -55,4 +50,4 @@ typedef struct fcrondyn_cl {
 } fcrondyn_cl;
 
 
-#endif                          /* __SOCKET_H__ */
+#endif                          /* __FCRONDYN_SVR_H__ */
index 1a656e2e9b67abfa9f7fef30159b4aeb5c8ded2f..7888b21cdb2945f427d50424bcd7cbeffe323217 100644 (file)
--- a/global.h
+++ b/global.h
  * which is a (not long) int (32bits).
  * As a time_t of INT_MAX=2^31 is 'only' in year 2038, we try to use a larger value
  * if we can. */
-// FIXME: test on 32bit system
+/* // FIXME: test on 32bit system */
 /* 2^33 = 8589934592, so LONG is 64bits at least */
 #if (LONG_MAX > INT_MAX) && (LONG_MAX > 8589934592)
 /* defined as time_t of 1st Jan of year (SHRT_MAX-1900) at 00:00:00 */
diff --git a/log.h b/log.h
index e5d42ea53639b4f061f917798dab99e90cec4d69..f928a4d702384c9e6feccd4466c4c75b707a30c9 100644 (file)
--- a/log.h
+++ b/log.h
 #ifndef __LOG_H__
 #define __LOG_H__
 
+#ifdef HAVE_LIBPAM
+#   include "pam.h"
+#endif
+
 extern char debug_opt;
 extern char *logfile_path;      /* path to a file to log to. Set to NULL to disable logging to a file */
 extern int dosyslog;            /* set to 1 when we log messages to syslog, else 0 */
diff --git a/select.c b/select.c
new file mode 100644 (file)
index 0000000..7cacfc8
--- /dev/null
+++ b/select.c
@@ -0,0 +1,82 @@
+/*
+ * FCRON - periodic command scheduler 
+ *
+ *  Copyright 2000-2014 Thibault Godouet <fcron@free.fr>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ * 
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ *  The GNU General Public License can also be found in the file
+ *  `LICENSE' that comes with the fcron source distribution.
+ */
+
+/* This file contains helper functions around an instance of select() */
+
+#include "select.h"
+#include "unistd.h"
+#include "errno.h"
+#include "global.h"
+
+void
+select_init(struct select_instance *si)
+    /* Initialize the structure (zero it) */
+{
+    FD_ZERO(&si->readfds);
+    FD_ZERO(&si->__readfds_master);
+    si->retcode = 0;
+    si->__fd_max = -1;
+}
+
+void
+select_add_read(select_instance *si, int fd)
+    /* Add a fd to the read set */
+{
+    FD_SET(fd, &si->__readfds_master);
+    if (fd > si->__fd_max) {
+        si->__fd_max = fd;
+    }
+    debug("select: added fd %d (fd_max=%d)", fd, si->__fd_max);
+
+}
+
+void
+select_rm_read(select_instance *si, int fd)
+    /* remove a fd to the read set */
+{
+    FD_CLR(fd, &si->__readfds_master);
+    if (fd >= si->__fd_max) {
+        /* fds in the fd_set may not be continuous, so we need
+         * to check them all to find the new max */
+        int i;
+        for (i=fd; i >= 0; i--) {
+            if (FD_ISSET(i, &si->__readfds_master))
+            si->__fd_max = i;
+        }
+    }
+    debug("select: removed fd %d (fd_max=%d)", fd, si->__fd_max);
+}
+
+void
+select_call(struct select_instance *si, struct timeval *timeout)
+    /* call select for instance 'si' */
+{
+    /* re-initialize readfds -- select() will change it */
+    si->readfds = si->__readfds_master;
+
+    si->retcode = select(si->__fd_max + 1, &si->readfds, NULL, NULL, timeout);
+    if (si->retcode < 0 && errno != EINTR) {
+        die_e("select returned %d", errno);
+    }
+}
+
diff --git a/select.h b/select.h
new file mode 100644 (file)
index 0000000..21dedbd
--- /dev/null
+++ b/select.h
@@ -0,0 +1,54 @@
+/*
+ * FCRON - periodic command scheduler 
+ *
+ *  Copyright 2000-2014 Thibault Godouet <fcron@free.fr>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ * 
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ *  The GNU General Public License can also be found in the file
+ *  `LICENSE' that comes with the fcron source distribution.
+ */
+
+/* This file contains helper functions around an instance of select() */
+
+#ifndef __SELECT_H__
+#define __SELECT_H__
+
+#include "config.h"
+
+#ifdef HAVE_SYS_SELECT_H
+#   include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#   include <sys/types.h>
+#endif
+
+
+typedef struct select_instance {
+    /* public (read-only) */
+    int retcode; /* return value of the last call of select() */
+    fd_set readfds; /* to check the status of fds after select_call */
+    /* private */
+    fd_set __readfds_master; /* select() modifies readfds, so we maintain a list in parallel */
+    int __fd_max;
+} select_instance;
+
+/* functions prototypes */
+extern void select_init(struct select_instance *si);
+extern void select_add_read(select_instance *si, int fd);
+extern void select_rm_read(select_instance *si, int fd);
+extern void select_call(struct select_instance *si, struct timeval *timeout);
+
+#endif                          /* __SELECT_H__ */