chiark / gitweb /
New poll(2) stuff etc. Does not work yet, but compiles.
authorian <ian>
Mon, 5 Jul 1999 01:50:58 +0000 (01:50 +0000)
committerian <ian>
Mon, 5 Jul 1999 01:50:58 +0000 (01:50 +0000)
acconfig.h
changelog
configure
configure.in
src/adns.h
src/adns.make
src/config.h.in
src/event.c
src/internal.h
src/poll.c [new file with mode: 0644]

index 73a7fe372fe7083dd2c36724e62d0bc2dcacdeb2..16672a6689bb91e564fb3828201e8332f2c21b43 100644 (file)
 
 /* Use the definitions: */
 
+#ifdef HAVE_POLL
+#include <sys/poll.h>
+#else
+struct pollfd { int fd; short events; short revents; };
+#define POLLIN  1
+#define POLLPRI 2
+#define POLLOUT 4
+#endif
+
 /* GNU C attributes. */
 #ifndef FUNCATTR
 #ifdef HAVE_GNUC25_ATTRIB
index d83d3c2073f5ab6e89558f7795a7b4d8667b50d8..207c322f1d0f1277c81f4ff8624a0538456bf43d 100644 (file)
--- a/changelog
+++ b/changelog
@@ -1,8 +1,14 @@
 adns (0.3) unstable; urgency=low
 
+  Incompatible changes:
+  * Low adns_status values (below adns_s_max_tempfail) renumbered to make
+    room for future locally-induced and locally-detected errors.
+  * Event loop functions for use by select(2) renamed and tidied up.
+  
   Features / improvements:
   * New adns_errabbrev() for getting status abbreviation strings.
   * regress/checkall prints summary list of failed tests, if any.
+  * Event loop functions for poll(2), and some raw variants.
   
   Bugfixes:
   * Non-RFC822 mailbox `domain' formatting now works, and clarified.
index 4bf677b15ae5562183b00b4daa4a201b4861add6..5df8065a5dbf76f1a43f0d0990a7edaea4f0a8be 100755 (executable)
--- a/configure
+++ b/configure
@@ -719,14 +719,69 @@ else
 fi
 
 
+for ac_func in poll
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:726: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 731 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:754: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
 
  echo $ac_n "checking for socket""... $ac_c" 1>&6
-echo "configure:725: checking for socket" >&5
+echo "configure:780: checking for socket" >&5
 if eval "test \"`echo '$''{'ac_cv_func_socket'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 730 "configure"
+#line 785 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char socket(); below.  */
@@ -749,7 +804,7 @@ socket();
 
 ; return 0; }
 EOF
-if { (eval echo configure:753: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+if { (eval echo configure:808: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
   rm -rf conftest*
   eval "ac_cv_func_socket=yes"
 else
@@ -768,7 +823,7 @@ else
   echo "$ac_t""no" 1>&6
 
   echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6
-echo "configure:772: checking for socket in -lsocket" >&5
+echo "configure:827: checking for socket in -lsocket" >&5
 ac_lib_var=`echo socket'_'socket | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -776,7 +831,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lsocket  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 780 "configure"
+#line 835 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -787,7 +842,7 @@ int main() {
 socket()
 ; return 0; }
 EOF
-if { (eval echo configure:791: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+if { (eval echo configure:846: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -823,12 +878,12 @@ fi
 
 
  echo $ac_n "checking for inet_ntoa""... $ac_c" 1>&6
-echo "configure:827: checking for inet_ntoa" >&5
+echo "configure:882: checking for inet_ntoa" >&5
 if eval "test \"`echo '$''{'ac_cv_func_inet_ntoa'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 832 "configure"
+#line 887 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char inet_ntoa(); below.  */
@@ -851,7 +906,7 @@ inet_ntoa();
 
 ; return 0; }
 EOF
-if { (eval echo configure:855: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+if { (eval echo configure:910: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
   rm -rf conftest*
   eval "ac_cv_func_inet_ntoa=yes"
 else
@@ -870,7 +925,7 @@ else
   echo "$ac_t""no" 1>&6
 
   echo $ac_n "checking for inet_ntoa in -lnsl""... $ac_c" 1>&6
-echo "configure:874: checking for inet_ntoa in -lnsl" >&5
+echo "configure:929: checking for inet_ntoa in -lnsl" >&5
 ac_lib_var=`echo nsl'_'inet_ntoa | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -878,7 +933,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lnsl  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 882 "configure"
+#line 937 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -889,7 +944,7 @@ int main() {
 inet_ntoa()
 ; return 0; }
 EOF
-if { (eval echo configure:893: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+if { (eval echo configure:948: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -926,12 +981,12 @@ fi
 
 
  echo $ac_n "checking for inet_aton""... $ac_c" 1>&6
-echo "configure:930: checking for inet_aton" >&5
+echo "configure:985: checking for inet_aton" >&5
 if eval "test \"`echo '$''{'ac_cv_func_inet_aton'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 935 "configure"
+#line 990 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char inet_aton(); below.  */
@@ -954,7 +1009,7 @@ inet_aton();
 
 ; return 0; }
 EOF
-if { (eval echo configure:958: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+if { (eval echo configure:1013: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
   rm -rf conftest*
   eval "ac_cv_func_inet_aton=yes"
 else
@@ -973,7 +1028,7 @@ else
   echo "$ac_t""no" 1>&6
 
   echo $ac_n "checking for inet_aton in -lresolv""... $ac_c" 1>&6
-echo "configure:977: checking for inet_aton in -lresolv" >&5
+echo "configure:1032: checking for inet_aton in -lresolv" >&5
 ac_lib_var=`echo resolv'_'inet_aton | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -981,7 +1036,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lresolv  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 985 "configure"
+#line 1040 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -992,7 +1047,7 @@ int main() {
 inet_aton()
 ; return 0; }
 EOF
-if { (eval echo configure:996: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+if { (eval echo configure:1051: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -1026,20 +1081,20 @@ fi
 
  
  echo $ac_n "checking __attribute__((,,))""... $ac_c" 1>&6
-echo "configure:1030: checking __attribute__((,,))" >&5
+echo "configure:1085: checking __attribute__((,,))" >&5
  if eval "test \"`echo '$''{'adns_cv_c_attribute_supported'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   
   cat > conftest.$ac_ext <<EOF
-#line 1036 "configure"
+#line 1091 "configure"
 #include "confdefs.h"
 
 int main() {
 extern int testfunction(int x) __attribute__((,,))
 ; return 0; }
 EOF
-if { (eval echo configure:1043: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:1098: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   adns_cv_c_attribute_supported=yes
 else
@@ -1061,20 +1116,20 @@ EOF
 
    
  echo $ac_n "checking __attribute__((noreturn))""... $ac_c" 1>&6
-echo "configure:1065: checking __attribute__((noreturn))" >&5
+echo "configure:1120: checking __attribute__((noreturn))" >&5
  if eval "test \"`echo '$''{'adns_cv_c_attribute_noreturn'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   
   cat > conftest.$ac_ext <<EOF
-#line 1071 "configure"
+#line 1126 "configure"
 #include "confdefs.h"
 
 int main() {
 extern int testfunction(int x) __attribute__((noreturn))
 ; return 0; }
 EOF
-if { (eval echo configure:1078: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:1133: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   adns_cv_c_attribute_noreturn=yes
 else
@@ -1101,20 +1156,20 @@ EOF
 
    
  echo $ac_n "checking __attribute__((const))""... $ac_c" 1>&6
-echo "configure:1105: checking __attribute__((const))" >&5
+echo "configure:1160: checking __attribute__((const))" >&5
  if eval "test \"`echo '$''{'adns_cv_c_attribute_const'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   
   cat > conftest.$ac_ext <<EOF
-#line 1111 "configure"
+#line 1166 "configure"
 #include "confdefs.h"
 
 int main() {
 extern int testfunction(int x) __attribute__((const))
 ; return 0; }
 EOF
-if { (eval echo configure:1118: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:1173: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   adns_cv_c_attribute_const=yes
 else
@@ -1141,20 +1196,20 @@ EOF
 
    
  echo $ac_n "checking __attribute__((format...))""... $ac_c" 1>&6
-echo "configure:1145: checking __attribute__((format...))" >&5
+echo "configure:1200: checking __attribute__((format...))" >&5
  if eval "test \"`echo '$''{'adns_cv_attribute_format'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   
   cat > conftest.$ac_ext <<EOF
-#line 1151 "configure"
+#line 1206 "configure"
 #include "confdefs.h"
 
 int main() {
 extern int testfunction(char *y, ...) __attribute__((format(printf,1,2)))
 ; return 0; }
 EOF
-if { (eval echo configure:1158: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:1213: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   adns_cv_attribute_format=yes
 else
index 0267a37fcccb2c9b26be309d4d8f9e05683f8cf6..6e342c07cd35c47ad279042a5452209d12dc0294 100644 (file)
@@ -21,6 +21,7 @@ AC_CONFIG_HEADER(src/config.h)
 AC_PROG_CC
 AC_PROG_RANLIB
 
+AC_CHECK_FUNCS(poll)
 ADNS_C_GETFUNC(socket,socket)
 ADNS_C_GETFUNC(inet_ntoa,nsl)
 
index 88b51f8dc8a58f48baf4fb65fab813b777e351d5..ce92c8b2d614da3ae6de048b9e9208a0e878504c 100644 (file)
@@ -111,6 +111,9 @@ typedef enum {
   /* locally induced errors */
   adns_s_nomemory,
   adns_s_unknownrrtype,
+  adns_s_systemfail,
+
+  adns_s_max_localfail= 29,
   
   /* remotely induced errors, detected locally */
   adns_s_timeout,
@@ -118,6 +121,8 @@ typedef enum {
   adns_s_norecurse,
   adns_s_invalidresponse,
   adns_s_unknownformat,
+
+  adns_s_max_remotefail= 59,
   
   /* remotely induced errors, reported by remote server to us */
   adns_s_rcodeservfail,
@@ -125,7 +130,7 @@ typedef enum {
   adns_s_rcodenotimplemented,
   adns_s_rcoderefused,
   adns_s_rcodeunknown,
-  
+
   adns_s_max_tempfail= 99,
 
   /* remote configuration errors */
@@ -228,15 +233,25 @@ typedef struct {
  *  If the call is successful, *query_io, *answer_r, and *context_r
  *  will all be set.
  * Errors:
- *  Return values are 0 or an errno value;
- *  Seriously fatal system errors (eg, failure to create sockets,
- *  malloc failure, etc.) return errno values;
- *  Other errors (nameserver failure, timed out connections, &c)
- *  are returned in the status field of the answer.  If status is
- *  nonzero then nrrs will be 0, otherwise it will be >0.
- *  type will always be the type requested;
- *  If no (appropriate) requests are done adns_check returns EWOULDBLOCK;
- *  If no (appropriate) requests are outstanding adns_query and adns_wait return ESRCH;
+ *  Return values are 0 or an errno value.
+ *
+ *  For _init, _init_strcfg, _submit and _synchronous, system errors
+ *  (eg, failure to create sockets, malloc failure, etc.) return errno
+ *  values.
+ * 
+ *  For _wait and _check failures are reported in the answer
+ *  structure, and only 0, ESRCH or (for _check) EWOULDBLOCK is
+ *  returned: if no (appropriate) requests are done adns_check returns
+ *  EWOULDBLOCK; if no (appropriate) requests are outstanding both
+ *  adns_query and adns_wait return ESRCH.
+ *
+ *  Additionally, _wait can return EINTR if you set adns_if_eintr.
+ *
+ *  All other errors (nameserver failure, timed out connections, &c)
+ *  are returned in the status field of the answer.  After a
+ *  successful _wait or _check, if status is nonzero then nrrs will be
+ *  0, otherwise it will be >0.  type will always be the type
+ *  requested.
  */
 
 int adns_init(adns_state *newstate_r, adns_initflags flags,
@@ -252,8 +267,8 @@ int adns_synchronous(adns_state ads,
                     adns_answer **answer_r);
 
 /* NB: if you set adns_if_noautosys then _submit and _check do not
- * make any system calls; you must use adns_callback (possibly after
- * adns_interest) to actually get things to happen.
+ * make any system calls; you must use some of the asynch-io event
+ * processing functions to actually get things to happen.
  */
 
 int adns_submit(adns_state ads,
@@ -275,36 +290,37 @@ int adns_wait(adns_state ads,
 
 void adns_cancel(adns_query query);
 
-void adns_finish(adns_state);
+/* The adns_query you get back from _submit is valid (ie, can be
+ * legitimately passed into adns functions) until it is returned by
+ * adns_check or adns_wait, or passed to adns_cancel.  After that it
+ * must not be used.  You can rely on it not being reused until the
+ * first adns_submit or _transact call using the same adns_state after
+ * it became invalid, so you may compare it for equality with other
+ * query handles until you next call _query or _transact.
+ */
+
+void adns_finish(adns_state ads);
 /* You may call this even if you have queries outstanding;
  * they will be cancelled.
  */
 
-int adns_callback(adns_state, int maxfd, const fd_set *readfds, const fd_set *writefds,
-                 const fd_set *exceptfds);
-/* Gives adns flow-of-control for a bit.  This will never block.
- * If maxfd == -1 then adns will check (make nonblocking system calls on)
- * all of its own filedescriptors; otherwise it will only use those
- * < maxfd and specified in the fd_set's, as if select had returned them.
- * Other fd's may be in the fd_sets, and will be ignored.
- * _callback returns how many adns fd's were in the various sets, so
- * you can tell if your select handling code has missed something and is going awol.
- *
- * May also return -1 if a critical syscall failed, setting errno.
- */
 
-void adns_interest(adns_state, int *maxfd_io, fd_set *readfds_io,
-                  fd_set *writefds_io, fd_set *exceptfds_io,
-                  struct timeval **tv_mod, struct timeval *tv_buf);
-/* Find out file descriptors adns is interested in, and when it
- * would like the opportunity to time something out.  If you do not plan to
- * block then tv_mod may be 0.  Otherwise, tv_mod may point to 0 meaning
- * you have no timeout of your own, in which case tv_buf must be non-null and
- * _interest may fill it in and set *tv_mod=tv_buf.
- * readfds, writefds, exceptfds and maxfd may not be 0.
+struct adns_query adns_forallqueries_begin(adns_state ads, void **context_r);
+struct adns_query adns_forallqueries_next(adns_state ads, adns_query, void **context_r);
+/* Iterator functions, which you can use to loop over the outstanding
+ * (submitted but not yet successfuly checked/waited) queries.  Each
+ * function returns a query handle and a corresponding context pointer,
+ * or returns 0 setting *context_r to 0 if there are no (more) queries.
+ * There is no need to explicitly finish an iteration.  context_r may be 0.
+ *
+ * IMPORTANT: you MUST NOT call ANY other adns function with the same
+ * adns_state, or with a query in the same adns_state, while you are
+ * doing one of these iterations.  After such a call the iterator
+ * value has undefined meaning and must not be used.
  */
 
-/* Example expected/legal calling sequences:
+/*
+ * Example expected/legal calling sequence for submit/check/wait:
  *  adns_init
  *  adns_submit 1
  *  adns_submit 2
@@ -315,18 +331,195 @@ void adns_interest(adns_state, int *maxfd_io, fd_set *readfds_io,
  *  adns_wait 3
  *  ....
  *  adns_finish
+ */
+
+/*
+ * Entrypoints for generic asynch io:
+ * (these entrypoints are not very useful except in combination with *
+ * some of the other I/O model calls which can tell you which fds to
+ * be interested in):
+ *
+ * Note that any adns call may cause adns to open and close fds, so
+ * you must call beforeselect or beforepoll again just before
+ * blocking, or you may not have an up-to-date list of it's fds.
+ */
+
+int adns_processany(adns_state ads);
+/* Gives adns flow-of-control for a bit.  This will never block, and
+ * can be used with any threading/asynch-io model.  If some error
+ * occurred which might cause an event loop to spin then the errno
+ * value is returned.
+ */
+
+int adns_processreadable(adns_state ads, int fd, const struct timeval *now);
+int adns_processwriteable(adns_state ads, int fd, const struct timeval *now);
+int adns_processexceptional(adns_state ads, int fd, const struct timeval *now);
+/* Gives adns flow-of-control so that it can process incoming data
+ * from, or send outgoing data via, fd.  Very like _processany.  If it
+ * returns zero then fd will no longer be readable or writeable
+ * (unless of course more data has arrived since).  adns will _only_
+ * that fd and only in the manner specified, regardless of whether
+ * adns_if_noautosys was specified.
+ *
+ * adns_processexceptional should be called when select(2) reports an
+ * exceptional condition, or poll(2) reports POLLPRI.
+ *
+ * It is fine to call _processreabable or _processwriteable when the
+ * fd is not ready, or with an fd that doesn't belong to adns; it will
+ * then just return 0.
+ *
+ * If some error occurred which might prevent an event loop to spin
+ * then the errno value is returned.
+ */
+
+void adns_processtimeouts(adns_state ads, const struct timeval *now);
+/* Gives adns flow-of-control so that it can process any timeouts
+ * which might have happened.  Very like _processreadable/writeable.
+ *
+ * now may be 0; if it isn't, *now must be the current time, recently
+ * obtained from gettimeofday.
+ */
+
+void adns_firsttimeout(adns_state ads,
+                      struct timeval **tv_mod, struct timeval *tv_buf,
+                      struct timeval now);
+/* Asks adns when it would first like the opportunity to time
+ * something out.  now must be the current time, from gettimeofday.
+ * 
+ * If tv_mod points to 0 then tv_buf must be non-null, and
+ * _firsttimeout will fill in *tv_buf with the time until the first
+ * timeout, and make *tv_mod point to tv_buf.  If adns doesn't have
+ * anything that might need timing out it will leave *tv_mod as 0.
+ *
+ * If *tv_mod is not 0 then tv_buf is not used.  adns will update
+ * *tv_mod if it has any earlier timeout, and leave it alone if it
+ * doesn't.
+ *
+ * This call will not actually do any I/O, or change the fds that adns
+ * is using.  It always succeeds and never blocks.
+ */
+
+void adns_globalsystemfailure(adns_state ads);
+/* If serious problem(s) happen which globally affect your ability to
+ * interact properly with adns, or adns's ability to function
+ * properly, you or adns can call this function.
+ *
+ * All currently outstanding queries will be made to fail with
+ * adns_s_systemfail, and adns will close any stream sockets it has
+ * open.
+ *
+ * This is used by adns, for example, if gettimeofday() fails.
+ * Without this the program's event loop might start to spin !
+ *
+ * This call will never block.
+ */
+
+/*
+ * Entrypoints for select-loop based asynch io:
+ */
+
+void adns_beforeselect(adns_state ads, int *maxfd, fd_set *readfds,
+                      fd_set *writefds, fd_set *exceptfds,
+                      struct timeval **tv_mod, struct timeval *tv_buf,
+                      const struct timeval *now);
+/* Find out file descriptors adns is interested in, and when it would
+ * like the opportunity to time something out.  If you do not plan to
+ * block then tv_mod may be 0.  Otherwise, tv_mod and tv_buf are as
+ * for adns_firsttimeout.  readfds, writefds, exceptfds and maxfd_io may
+ * not be 0.
+ *
+ * If *now is not 0 then this will never actually do any I/O, or
+ * change the fds that adns is using or the timeouts it wants.  In any
+ * case it won't block.
+ */
+
+void adns_afterselect(adns_state ads, int maxfd, const fd_set *readfds,
+                     const fd_set *writefds, const fd_set *exceptfds,
+                     const struct timeval *now);
+/* Gives adns flow-of-control for a bit; intended for use after
+ * select.  This is just a fancy way of calling adns_processreadable/
+ * writeable/timeouts as appropriate, as if select had returned the
+ * data being passed.  Always succeeds.
+ */
+
+/*
+ * Example calling sequence:
  *
  *  adns_init _noautosys
  *  loop {
- *   adns_interest
+ *   adns_beforeselect
  *   select
- *   adns_callback
+ *   adns_afterselect
  *   ...
  *   adns_submit / adns_check
  *   ...
  *  }
  */
 
+/*
+ * Entrypoints for poll-loop based asynch io:
+ */
+
+struct pollfd;
+/* In case your system doesn't have it or you forgot to include
+ * <sys/poll.h>, to stop the following declarations from causing
+ * problems.  If your system doesn't have poll then the following
+ * entrypoints will not be defined in libadns.  Sorry !
+ */
+
+int adns_beforepoll(adns_state ads, struct pollfd *fds, int *nfds_io, int *timeout_io,
+                   const struct timeval *now);
+/* Finds out which fd's adns is interested in, and when it would like
+ * to be able to time things out.  This is in a form suitable for use
+ * with poll(2).
+ * 
+ * On entry, usually fds should point to at least *nfds_io structs.
+ * adns will fill up to that many structs will information for poll,
+ * and record in *nfds_io how many structs it filled.  If it wants to
+ * listen for more structs then *nfds_io will be set to the number
+ * required and _beforepoll will return ERANGE.
+ *
+ * NOTE that if now is 0, adns may acquire additional fds from one
+ * call to the next, so you must put adns_beforepoll in a loop, rather
+ * than assuming that the second call (with the buffer size requested
+ * by the first) will not return ERANGE.
+ *
+ * You may call _beforepoll with fds==0 and *nfds_io 0, in which case
+ * adns will fill in the number of fds that it might be interested in
+ * in *nfds_io, and always return 0.
+ *
+ * adns only ever sets POLLIN, POLLOUT and POLLPRI in its pollfd
+ * structs, and only ever looks at those bits.  POLLPRI is required to
+ * detect TCP Urgent Data, which should not be used by a DNS server,
+ * so that adns can know that the TCP stream is now useless.
+ *
+ * In any case, *timeout_io should be a timeout value as for poll(2),
+ * which adns will modify downwards as required.  If the caller does
+ * not plan to block then *timeout_io should be 0 on entry.
+ *
+ * adns_beforepoll will return 0 on success, and will not fail for any
+ * reason other than the fds buffer being too small (ERANGE).
+ *
+ * If *now is not 0 then this call will never actually do any I/O, or
+ * change the fds that adns is using or the timeouts it wants.  In any
+ * case it won't block.
+ */
+
+#define ADNS_POLLFDS_RECOMMENDED 2
+/* If you allocate an fds buf with at least RECOMMENDED entries then
+ * you are unlikely to need to enlarge it.  You are recommended to do
+ * so if it's convenient.  However, you must be prepared for adns to
+ * require more space than this.
+ */
+
+void adns_afterpoll(adns_state ads, const struct pollfd *fds, int nfds,
+                   const struct timeval *now);
+/* Gives adns flow-of-control for a bit; intended for use after
+ * poll(2).  fds and nfds should be the results from poll().  pollfd
+ * structs mentioning fds not belonging to adns will be ignored.
+ */
+
+
 adns_status adns_rr_info(adns_rrtype type,
                         const char **rrtname_r, const char **fmtname_r,
                         int *len_r,
@@ -357,5 +550,10 @@ adns_status adns_rr_info(adns_rrtype type,
 
 const char *adns_strerror(adns_status st);
 const char *adns_errabbrev(adns_status st);
+/* Like strerror but for adns_status values.  adns_errabbrev returns
+ * the abbreviation of the error - eg, for adns_s_timeout it returns
+ * "timeout".  You MUST NOT call these functions with status values
+ * not returned by the same adns library.
+ */
 
 #endif
index 6d3979a5b7c5255bbf600c2e910fdc57c3428c31..36b6820f74ee52cb747f38933e0d80861b12447a 100644 (file)
@@ -16,4 +16,5 @@
 #  along with this program; if not, write to the Free Software Foundation,
 #  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
 
-LIBOBJS=       types.o event.o query.o reply.o general.o setup.o transmit.o parse.o
+LIBOBJS=       types.o event.o query.o reply.o general.o setup.o transmit.o \
+               parse.o poll.o
index 74267bfb20a024071958cc3ae4a8121499b428cb..80590fb617267e9ddbeed822ec2edf036886d378 100644 (file)
@@ -12,6 +12,9 @@
 /* Define if printf-format argument lists a la GCC are available.  */
 #undef HAVE_GNUC25_PRINTFFORMAT
 
+/* Define if you have the poll function.  */
+#undef HAVE_POLL
+
 /* Define if you have the nsl library (-lnsl).  */
 #undef HAVE_LIBNSL
 
 
 /* Use the definitions: */
 
+#ifdef HAVE_POLL
+#include <sys/poll.h>
+#else
+struct pollfd { int fd; short events; short revents; };
+#define POLLIN  1
+#define POLLPRI 2
+#define POLLOUT 4
+#endif
+
 /* GNU C attributes. */
 #ifndef FUNCATTR
 #ifdef HAVE_GNUC25_ATTRIB
index 154a9b8f5c2dd3c62849b1e06ce8a16b4ecc7936..d54d8479c38151b36e914f298b99e99af1922491 100644 (file)
 
 #include "internal.h"
 
-/* TCP connection management */
+/* TCP connection management. */
+
+void adns__tcp_closenext(adns_state ads) {
+  int serv;
+  
+  serv= ads->tcpserver;
+  close(ads->tcpsocket);
+  ads->tcpstate= server_disconnected;
+  ads->tcprecv.used= ads->tcpsend.used= 0;
+  ads->tcpserver= (serv+1)%ads->nservers;
+}
 
 void adns__tcp_broken(adns_state ads, const char *what, const char *why) {
   int serv;
@@ -45,8 +55,7 @@ void adns__tcp_broken(adns_state ads, const char *what, const char *why) {
   assert(ads->tcpstate == server_connecting || ads->tcpstate == server_ok);
   serv= ads->tcpserver;
   adns__warn(ads,serv,0,"TCP connection lost: %s: %s",what,why);
-  close(ads->tcpsocket);
-  ads->tcpstate= server_disconnected;
+  adns__tcp_closenext(ads);
   
   for (qu= ads->timew.head; qu; qu= nqu) {
     nqu= qu->next;
@@ -59,9 +68,6 @@ void adns__tcp_broken(adns_state ads, const char *what, const char *why) {
       adns__query_fail(qu,adns_s_allservfail);
     }
   }
-
-  ads->tcprecv.used= ads->tcpsend.used= 0;
-  ads->tcpserver= (serv+1)%ads->nservers;
 }
 
 static void tcp_connected(adns_state ads, struct timeval now) {
@@ -114,9 +120,20 @@ void adns__tcp_tryconnect(adns_state ads, struct timeval now) {
   }
 }
 
-/* `Interest' functions - find out which fd's we might be interested in,
- * and when we want to be called back for a timeout.
- */
+/* Timeout handling functions. */
+
+void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io,
+                            struct timeval *tv_buf) {
+  const struct timeval *now;
+  int r;
+
+  now= *now_io;
+  if (now) return;
+  r= gettimeofday(tv_buf,0); if (!r) { *now_io= tv_buf; return; }
+  adns__diag(ads,-1,0,"gettimeofday failed: %s",strerror(errno));
+  adns_globalsystemfailure(ads);
+  return;
+}
 
 static void inter_maxto(struct timeval **tv_io, struct timeval *tvbuf,
                        struct timeval maxto) {
@@ -149,174 +166,131 @@ static void inter_maxtoabs(struct timeval **tv_io, struct timeval *tvbuf,
   inter_maxto(tv_io,tvbuf,maxtime);
 }
 
-static void inter_addfd(int *maxfd, fd_set *fds, int fd) {
-  if (!maxfd || !fds) return;
-  if (fd>=*maxfd) *maxfd= fd+1;
-  FD_SET(fd,fds);
-}
-
-static void checktimeouts(adns_state ads, struct timeval now,
-                         struct timeval **tv_io, struct timeval *tvbuf) {
+void adns__timeouts(adns_state ads, int act,
+                   struct timeval **tv_io, struct timeval *tvbuf,
+                   struct timeval now) {
   adns_query qu, nqu;
-  
+
   for (qu= ads->timew.head; qu; qu= nqu) {
     nqu= qu->next;
-    if (timercmp(&now,&qu->timeout,>)) {
+    if (timercmp(&now,&qu->timeout,<=)) {
+      if (!tv_io) continue;
+      inter_maxtoabs(tv_io,tvbuf,now,qu->timeout);
+    } else {
+      if (!act) continue;
       LIST_UNLINK(ads->timew,qu);
       if (qu->state != query_udp) {
        adns__query_fail(qu,adns_s_timeout);
       } else {
        adns__query_udp(qu,now);
       }
-    } else {
-      inter_maxtoabs(tv_io,tvbuf,now,qu->timeout);
+      nqu= ads->timew.head;
     }
   }
 }  
-void adns_interest(adns_state ads, int *maxfd,
-                  fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
-                  struct timeval **tv_io, struct timeval *tvbuf) {
-  struct timeval now;
-  struct timeval tvto_lr;
-  int r;
-  
-/*fprintf(stderr,"adns_interest\n");*/
 
-  r= gettimeofday(&now,0);
-  if (r) {
-    adns__warn(ads,-1,0,"gettimeofday failed - will sleep for a bit: %s",
-              strerror(errno));
-    timerclear(&tvto_lr); timevaladd(&tvto_lr,LOCALRESOURCEMS);
-    inter_maxto(tv_io, tvbuf, tvto_lr);
-  } else {
-    checktimeouts(ads,now,tv_io,tvbuf);
-  }
-  
-  inter_addfd(maxfd,readfds,ads->udpsocket);
+void adns_firsttimeout(adns_state ads,
+                      struct timeval **tv_io, struct timeval *tvbuf,
+                      struct timeval now) {
+  adns__timeouts(ads, 0, tv_io,tvbuf, now);
+}
+
+void adns_processtimeouts(adns_state ads, const struct timeval *now) {
+  struct timeval tv_buf;
+
+  adns__must_gettimeofday(ads,&now,&tv_buf); if (!now) return;
+  adns__timeouts(ads, 1, 0,0, *now);
+}
+
+/* fd handling functions.  These are the top-level of the real work of
+ * reception and often transmission.
+ */
+
+int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]) {
+  /* Returns the number of entries filled in.  Always zeroes revents. */
+
+  assert(MAX_POLLFDS==2);
+
+  pollfds_buf[0].fd= ads->udpsocket;
+  pollfds_buf[0].events= POLLIN;
+  pollfds_buf[0].revents= 0;
 
   switch (ads->tcpstate) {
   case server_disconnected:
-    break;
+    return 1;
   case server_connecting:
-    inter_addfd(maxfd,writefds,ads->tcpsocket);
+    pollfds_buf[1].events= POLLOUT;
     break;
   case server_ok:
-    inter_addfd(maxfd,readfds,ads->tcpsocket);
-    inter_addfd(maxfd,exceptfds,ads->tcpsocket);
-    if (ads->tcpsend.used) inter_addfd(maxfd,writefds,ads->tcpsocket);
+    pollfds_buf[1].events= ads->tcpsend.used ? POLLIN|POLLOUT|POLLPRI : POLLIN|POLLPRI;
     break;
   default:
     abort();
   }
+  pollfds_buf[1].fd= ads->tcpsocket;
+  pollfds_buf[1].revents= 0;
+  return 2;
 }
 
-/* Callback procedures - these do the real work of reception and timeout, etc. */
-
-static int callb_checkfd(int maxfd, const fd_set *fds, int fd) {
-  return maxfd<0 || !fds ? 1 :
-         fd<maxfd && FD_ISSET(fd,fds);
-}
-
-static int internal_callback(adns_state ads, int maxfd,
-                            const fd_set *readfds, const fd_set *writefds,
-                            const fd_set *exceptfds,
-                            struct timeval now) {
-  int skip, want, dgramlen, count, udpaddrlen, r, serv;
+int adns_processreadable(adns_state ads, int fd, const struct timeval *now) {
+  int skip, want, dgramlen, r, udpaddrlen, serv;
   byte udpbuf[DNS_MAXUDP];
   struct sockaddr_in udpaddr;
-
-  count= 0;
-
+  
   switch (ads->tcpstate) {
   case server_disconnected:
-    break;
   case server_connecting:
-    if (callb_checkfd(maxfd,writefds,ads->tcpsocket)) {
-      count++;
-      assert(ads->tcprecv.used==0);
-      if (!adns__vbuf_ensure(&ads->tcprecv,1)) return -1;
-      if (ads->tcprecv.buf) {
-       r= read(ads->tcpsocket,&ads->tcprecv.buf,1);
-       if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) {
-         tcp_connected(ads,now);
-       } else if (r>0) {
-         adns__tcp_broken(ads,"connect/read","sent data before first request");
-       } else if (errno!=EINTR) {
-         adns__tcp_broken(ads,"connect/read",strerror(errno));
-       }
-      }
-    }
     break;
   case server_ok:
-    count+= callb_checkfd(maxfd,readfds,ads->tcpsocket) +
-            callb_checkfd(maxfd,exceptfds,ads->tcpsocket) +
-      (ads->tcpsend.used && callb_checkfd(maxfd,writefds,ads->tcpsocket));
-    if (callb_checkfd(maxfd,readfds,ads->tcpsocket)) {
-      skip= 0;
-      for (;;) {
-       if (ads->tcprecv.used<skip+2) {
-         want= 2;
-       } else {
-         dgramlen= (ads->tcprecv.buf[skip]<<8) | ads->tcprecv.buf[skip+1];
-         if (ads->tcprecv.used<skip+2+dgramlen) {
-           want= 2+dgramlen;
-         } else {
-           adns__procdgram(ads,ads->tcprecv.buf+skip+2,dgramlen,ads->tcpserver,now);
-           skip+= 2+dgramlen; continue;
-         }
-       }
-       ads->tcprecv.used -= skip;
-       memmove(ads->tcprecv.buf,ads->tcprecv.buf+skip,ads->tcprecv.used);
-       skip= 0;
-       if (!adns__vbuf_ensure(&ads->tcprecv,want)) return -1;
-       assert(ads->tcprecv.used <= ads->tcprecv.avail);
-       if (ads->tcprecv.used == ads->tcprecv.avail) continue;
-       r= read(ads->tcpsocket,
-               ads->tcprecv.buf+ads->tcprecv.used,
-               ads->tcprecv.avail-ads->tcprecv.used);
-       if (r>0) {
-         ads->tcprecv.used+= r;
+    if (fd != ads->tcpsocket) break;
+    skip= 0;
+    for (;;) {
+      if (ads->tcprecv.used<skip+2) {
+       want= 2;
+      } else {
+       dgramlen= (ads->tcprecv.buf[skip]<<8) | ads->tcprecv.buf[skip+1];
+       if (ads->tcprecv.used<skip+2+dgramlen) {
+         want= 2+dgramlen;
        } else {
-         if (r<0) {
-           if (errno==EAGAIN || errno==EWOULDBLOCK || errno==ENOMEM) break;
-           if (errno==EINTR) continue;
-         }
-         adns__tcp_broken(ads,"read",r?strerror(errno):"closed");
-         break;
+         adns__procdgram(ads,ads->tcprecv.buf+skip+2,dgramlen,ads->tcpserver,*now);
+         skip+= 2+dgramlen; continue;
        }
       }
-    } else if (callb_checkfd(maxfd,exceptfds,ads->tcpsocket)) {
-      adns__tcp_broken(ads,"select","exceptional condition detected");
-    } else if (ads->tcpsend.used && callb_checkfd(maxfd,writefds,ads->tcpsocket)) {
-      adns__sigpipe_protect(ads);
-      r= write(ads->tcpsocket,ads->tcpsend.buf,ads->tcpsend.used);
-      adns__sigpipe_unprotect(ads);
-      if (r<0) {
-       if (errno!=EAGAIN && errno!=EWOULDBLOCK && errno!=ENOMEM && errno!=EINTR) {
-         adns__tcp_broken(ads,"write",strerror(errno));
+      ads->tcprecv.used -= skip;
+      memmove(ads->tcprecv.buf,ads->tcprecv.buf+skip,ads->tcprecv.used);
+      skip= 0;
+      if (!adns__vbuf_ensure(&ads->tcprecv,want)) return ENOMEM;
+      assert(ads->tcprecv.used <= ads->tcprecv.avail);
+      if (ads->tcprecv.used == ads->tcprecv.avail) continue;
+      r= read(ads->tcpsocket,
+             ads->tcprecv.buf+ads->tcprecv.used,
+             ads->tcprecv.avail-ads->tcprecv.used);
+      if (r>0) {
+       ads->tcprecv.used+= r;
+      } else {
+       if (r) {
+         if (errno==EAGAIN || errno==EWOULDBLOCK) return 0;
+         if (errno==EINTR) continue;
+         if (errno_resources(errno)) return errno;
        }
-      } else if (r>0) {
-       ads->tcpsend.used -= r;
-       memmove(ads->tcpsend.buf,ads->tcpsend.buf+r,ads->tcpsend.used);
+       adns__tcp_broken(ads,"read",r?strerror(errno):"closed");
+       return 0;
       }
-    }
-    break;
+    } /* never reached */
   default:
     abort();
   }
-
-  if (callb_checkfd(maxfd,readfds,ads->udpsocket)) {
-    count++;
+  if (fd == ads->udpsocket) {
     for (;;) {
       udpaddrlen= sizeof(udpaddr);
       r= recvfrom(ads->udpsocket,udpbuf,sizeof(udpbuf),0,
                  (struct sockaddr*)&udpaddr,&udpaddrlen);
       if (r<0) {
-       if (!(errno == EAGAIN || errno == EWOULDBLOCK ||
-             errno == EINTR || errno == ENOMEM || errno == ENOBUFS))
-         adns__warn(ads,-1,0,"datagram receive error: %s",strerror(errno));
-       break;
+       if (errno == EAGAIN || errno == EWOULDBLOCK) return 0;
+       if (errno == EINTR) continue;
+       if (errno_resources(errno)) return errno;
+       adns__warn(ads,-1,0,"datagram receive error: %s",strerror(errno));
+       return 0;
       }
       if (udpaddrlen != sizeof(udpaddr)) {
        adns__diag(ads,-1,0,"datagram received with wrong address length %d"
@@ -342,28 +316,195 @@ static int internal_callback(adns_state ads, int maxfd,
                   inet_ntoa(udpaddr.sin_addr));
        continue;
       }
-      adns__procdgram(ads,udpbuf,r,serv,now);
+      adns__procdgram(ads,udpbuf,r,serv,*now);
     }
   }
-  return count;
+  return 0;
 }
 
-int adns_callback(adns_state ads, int maxfd,
-                 const fd_set *readfds, const fd_set *writefds,
-                 const fd_set *exceptfds) {
-  struct timeval now;
+int adns_processwriteable(adns_state ads, int fd, const struct timeval *now) {
+  int r;
+  
+  switch (ads->tcpstate) {
+  case server_disconnected:
+    break;
+  case server_connecting:
+    if (fd != ads->tcpsocket) break;
+    assert(ads->tcprecv.used==0);
+    for (;;) {
+      if (!adns__vbuf_ensure(&ads->tcprecv,1)) return ENOMEM;
+      r= read(ads->tcpsocket,&ads->tcprecv.buf,1);
+      if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) {
+       tcp_connected(ads,*now);
+       return 0;
+      }
+      if (r>0) {
+       adns__tcp_broken(ads,"connect/read","sent data before first request");
+       return 0;
+      }
+      if (errno==EINTR) continue;
+      if (errno_resources(errno)) return errno;
+      adns__tcp_broken(ads,"connect/read",strerror(errno));
+      return 0;
+    } /* not reached */
+  case server_ok:
+    if (!(ads->tcpsend.used && fd == ads->tcpsocket)) break;
+    for (;;) {
+      adns__sigpipe_protect(ads);
+      r= write(ads->tcpsocket,ads->tcpsend.buf,ads->tcpsend.used);
+      adns__sigpipe_unprotect(ads);
+      if (r<0) {
+       if (errno==EINTR) continue;
+       if (errno==EAGAIN || errno==EWOULDBLOCK) return 0;
+       if (errno_resources(errno)) return errno;
+       adns__tcp_broken(ads,"write",strerror(errno));
+       return 0;
+      } else if (r>0) {
+       ads->tcpsend.used -= r;
+       memmove(ads->tcpsend.buf,ads->tcpsend.buf+r,ads->tcpsend.used);
+      }
+    } /* not reached */
+  default:
+    abort();
+  }
+  return 0;
+}
+  
+int adns_processexceptional(adns_state ads, int fd, const struct timeval *now) {
+  switch (ads->tcpstate) {
+  case server_disconnected:
+    break;
+  case server_connecting:
+  case server_ok:
+    if (fd != ads->tcpsocket) break;
+    adns__tcp_broken(ads,"poll/select","exceptional condition detected");
+    return 0;
+  default:
+    abort();
+  }
+  return 0;
+}
+
+static void fd_event(adns_state ads, int fd,
+                    int revent, int pollflag,
+                    int maxfd, const fd_set *fds,
+                    int (*func)(adns_state, int fd, const struct timeval *now),
+                    struct timeval now, int *r_r) {
   int r;
+  
+  if (!(revent & pollflag)) return;
+  if (fds && !(fd<maxfd && FD_ISSET(fd,fds))) return;
+  r= func(ads,fd,&now);
+  if (r) {
+    if (r_r) {
+      *r_r= r;
+    } else {
+      adns__diag(ads,-1,0,"process fd failed after select: %s",strerror(errno));
+      adns_globalsystemfailure(ads);
+    }
+  }
+}
+
+void adns__fdevents(adns_state ads,
+                   const struct pollfd *pollfds, int npollfds,
+                   int maxfd, const fd_set *readfds,
+                   const fd_set *writefds, const fd_set *exceptfds,
+                   struct timeval now, int *r_r) {
+  int i, fd, revents;
+
+  for (i=0; i<npollfds; i++) {
+    fd= pollfds[i].fd;
+    if (fd >= maxfd) maxfd= fd+1;
+    revents= pollfds[i].revents;
+    fd_event(ads,fd, revents,POLLIN, maxfd,readfds, adns_processreadable,now,r_r);
+    fd_event(ads,fd, revents,POLLOUT, maxfd,writefds, adns_processwriteable,now,r_r);
+    fd_event(ads,fd, revents,POLLPRI, maxfd,exceptfds, adns_processexceptional,now,r_r);
+  }
+}
 
-  r= gettimeofday(&now,0); if (r) return -1;
-  checktimeouts(ads,now,0,0);
-  return internal_callback(ads,maxfd,readfds,writefds,exceptfds,now);
+/* Wrappers for select(2). */
+
+void adns_beforeselect(adns_state ads, int *maxfd_io, fd_set *readfds_io,
+                      fd_set *writefds_io, fd_set *exceptfds_io,
+                      struct timeval **tv_mod, struct timeval *tv_tobuf,
+                      const struct timeval *now) {
+  struct timeval tv_nowbuf;
+  struct pollfd pollfds[MAX_POLLFDS];
+  int i, fd, maxfd, npollfds;
+  
+  if (tv_mod && (!*tv_mod || (*tv_mod)->tv_sec || (*tv_mod)->tv_usec)) {
+    /* The caller is planning to sleep. */
+    adns__must_gettimeofday(ads,&now,&tv_nowbuf);
+    if (!now) return;
+    adns__timeouts(ads, 1, tv_mod,tv_tobuf, *now);
+  }
+
+  npollfds= adns__pollfds(ads,pollfds);
+  maxfd= *maxfd_io;
+  for (i=0; i<npollfds; i++) {
+    fd= pollfds[i].fd;
+    if (fd >= maxfd) maxfd= fd+1;
+    if (pollfds[i].events & POLLIN) FD_SET(fd,readfds_io);
+    if (pollfds[i].events & POLLOUT) FD_SET(fd,writefds_io);
+    if (pollfds[i].events & POLLPRI) FD_SET(fd,exceptfds_io);
+  }
+  *maxfd_io= maxfd;
+}
+
+void adns_afterselect(adns_state ads, int maxfd, const fd_set *readfds,
+                     const fd_set *writefds, const fd_set *exceptfds,
+                     const struct timeval *now) {
+  struct pollfd pollfds[MAX_POLLFDS];
+  int npollfds;
+
+  adns_processtimeouts(ads,now);
+
+  npollfds= adns__pollfds(ads,pollfds);
+  adns__fdevents(ads,
+                pollfds,npollfds,
+                maxfd,readfds,writefds,exceptfds,
+                *now, 0);
+}
+
+/* General helpful functions. */
+
+void adns_globalsystemfailure(adns_state ads) {
+  while (ads->timew.head) {
+    adns__query_fail(ads->timew.head, adns_s_systemfail);
+  }
+  
+  switch (ads->tcpstate) {
+  case server_connecting:
+  case server_ok:
+    adns__tcp_closenext(ads);
+    break;
+  case server_disconnected:
+    break;
+  default:
+    abort();
+  }
 }
 
-/* User-visible functions and their implementation. */
+int adns_processany(adns_state ads) {
+  int r;
+  struct timeval now;
+  struct pollfd pollfds[MAX_POLLFDS];
+  int npollfds;
+
+  r= gettimeofday(&now,0);
+  if (!r) adns_processtimeouts(ads,&now);
+
+  npollfds= adns__pollfds(ads,pollfds);
+  adns__fdevents(ads,
+                pollfds,npollfds,
+                0,0,0,0,
+                now,&r);
+  return r;
+}
 
 void adns__autosys(adns_state ads, struct timeval now) {
   if (ads->iflags & adns_if_noautosys) return;
-  adns_callback(ads,-1,0,0,0);
+  adns_processany(ads);
 }
 
 static int internal_check(adns_state ads,
@@ -390,7 +531,7 @@ int adns_wait(adns_state ads,
              adns_query *query_io,
              adns_answer **answer_r,
              void **context_r) {
-  int r, maxfd, rsel, rcb;
+  int r, maxfd, rsel;
   fd_set readfds, writefds, exceptfds;
   struct timeval tvbuf, *tvp;
   
@@ -399,14 +540,19 @@ int adns_wait(adns_state ads,
     if (r != EWOULDBLOCK) return r;
     maxfd= 0; tvp= 0;
     FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
-    adns_interest(ads,&maxfd,&readfds,&writefds,&exceptfds,&tvp,&tvbuf);
+    adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds,&tvp,&tvbuf,0);
     rsel= select(maxfd,&readfds,&writefds,&exceptfds,tvp);
     if (rsel==-1) {
-      if (errno == EINTR && !(ads->iflags & adns_if_eintr)) continue;
-      return errno;
+      if (errno == EINTR) {
+       if (ads->iflags & adns_if_eintr) return EINTR;
+      } else {
+       adns__diag(ads,-1,0,"select failed in wait: %s",strerror(errno));
+       adns_globalsystemfailure(ads);
+      }
+    } else {
+      assert(rsel >= 0);
+      adns_afterselect(ads,maxfd,&readfds,&writefds,&exceptfds,0);
     }
-    rcb= adns_callback(ads,maxfd,&readfds,&writefds,&exceptfds);
-    assert(rcb==rsel);
   }
 }
 
@@ -417,7 +563,8 @@ int adns_check(adns_state ads,
   struct timeval now;
   int r;
   
-  r= gettimeofday(&now,0); if (r) return errno;
-  adns__autosys(ads,now);
+  r= gettimeofday(&now,0);
+  if (!r) adns__autosys(ads,now);
+  
   return internal_check(ads,query_io,answer_r,context_r);
 }
index b813215f9071043fc04730bca6d5fbbd3e427708..dadd854a88e42c05b75a88471bc446f90ba21988 100644 (file)
@@ -32,6 +32,7 @@ typedef unsigned char byte;
 #include <assert.h>
 #include <unistd.h>
 #include <signal.h>
+#include <errno.h>
 
 #include <sys/time.h>
 
@@ -44,7 +45,6 @@ typedef unsigned char byte;
 #define UDPMAXRETRIES 15
 #define UDPRETRYMS 2000
 #define TCPMS 30000
-#define LOCALRESOURCEMS 20
 #define MAXTTLBELIEVE (7*86400) /* any TTL > 7 days is capped */
 
 #define DNS_PORT 53
@@ -56,6 +56,8 @@ typedef unsigned char byte;
 
 #define DNS_INADDR_ARPA "in-addr", "arpa"
 
+#define MAX_POLLFDS  ADNS_POLLFDS_RECOMMENDED
+
 typedef enum {
   rcode_noerror,
   rcode_formaterror,
@@ -269,6 +271,7 @@ struct adns__state {
   struct timeval tcptimeout;
   struct sigaction stdsigpipe;
   sigset_t stdsigmask;
+  struct pollfd pollfds_buf[MAX_POLLFDS];
   struct server {
     struct in_addr addr;
   } servers[MAXSERVERS];
@@ -598,11 +601,24 @@ int vbuf__append_quoted1035(vbuf *vb, const byte *buf, int len);
 /* From event.c: */
 
 void adns__tcp_broken(adns_state ads, const char *what, const char *why);
+void adns__tcp_closenext(adns_state ads);
 void adns__tcp_tryconnect(adns_state ads, struct timeval now);
 
 void adns__autosys(adns_state ads, struct timeval now);
 /* Make all the system calls we want to if the application wants us to. */
 
+void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io,
+                            struct timeval *tv_buf);
+void adns__timeouts(adns_state ads, int act,
+                   struct timeval **tv_io, struct timeval *tvbuf,
+                   struct timeval now);
+int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]);
+void adns__fdevents(adns_state ads,
+                   const struct pollfd *pollfds, int npollfds,
+                   int maxfd, const fd_set *readfds,
+                   const fd_set *writefds, const fd_set *exceptfds,
+                   struct timeval now, int *r_r);
+
 /* Useful static inline functions: */
 
 static inline void timevaladd(struct timeval *tv_io, long ms) {
@@ -621,6 +637,8 @@ static inline int ctype_alpha(int c) {
   return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
 }
 
+static inline int errno_resources(int e) { return e==ENOMEM || e==ENOBUFS; }
+
 /* Useful macros */
 
 #define MEM_ROUND(sz) \
diff --git a/src/poll.c b/src/poll.c
new file mode 100644 (file)
index 0000000..03f153a
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * poll.c
+ * - wrappers for poll(2)
+ */
+/*
+ *  This file is part of adns, which is Copyright (C) 1997-1999 Ian Jackson
+ *  
+ *  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, 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. 
+ */
+
+#include <limits.h>
+
+#include "internal.h"
+
+#ifdef HAVE_POLL
+
+int adns_beforepoll(adns_state ads, struct pollfd *fds, int *nfds_io, int *timeout_io,
+                   const struct timeval *now) {
+  struct timeval tv_nowbuf, tv_tobuf, *tv_to;
+  int space, found, timeout_ms;
+  struct pollfd fds_tmp[MAX_POLLFDS];
+
+  adns__must_gettimeofday(ads,&now,&tv_nowbuf);
+  if (!now) { *nfds_io= 0; return 0; }
+
+  timeout_ms= *timeout_io;
+  if (timeout_ms == -1) {
+    tv_to= 0;
+  } else {
+    tv_tobuf.tv_sec= timeout_ms / 1000;
+    tv_tobuf.tv_usec= (timeout_ms % 1000)*1000;
+    tv_to= &tv_tobuf;
+  }
+
+  adns__timeouts(ads, 1, &tv_to,&tv_tobuf, *now);
+
+  if (tv_to) {
+    assert(tv_to == &tv_tobuf);
+    timeout_ms= (tv_tobuf.tv_usec+999)/1000;
+    assert(tv_tobuf.tv_sec < (INT_MAX-timeout_ms)/1000);
+    timeout_ms += tv_tobuf.tv_sec*1000;
+  } else {
+    timeout_ms= -1;
+  }
+  *timeout_io= timeout_ms;
+  
+  space= *nfds_io;
+  if (space >= MAX_POLLFDS) {
+    found= adns__pollfds(ads,fds);
+    *nfds_io= found;
+  } else {
+    found= adns__pollfds(ads,fds_tmp);
+    *nfds_io= found;
+    if (found < space) return space ? ERANGE : 0;
+    memcpy(fds,fds_tmp,sizeof(struct pollfd)*found);
+  }
+  return 0;
+}
+
+void adns_afterpoll(adns_state ads, const struct pollfd *fds, int nfds,
+                   const struct timeval *now) {
+  struct timeval tv_buf;
+
+  adns__must_gettimeofday(ads,&now,&tv_buf);
+  if (!now) return;
+
+  adns__timeouts(ads,1, 0,0, *now);
+  adns__fdevents(ads, fds,nfds, 0,0,0,0, *now,0);
+}
+
+#endif