Migrate net-server loop to use libverto
authorGreg Hudson <ghudson@mit.edu>
Fri, 2 Sep 2011 17:07:59 +0000 (17:07 +0000)
committerGreg Hudson <ghudson@mit.edu>
Fri, 2 Sep 2011 17:07:59 +0000 (17:07 +0000)
From npmccallum@redhat.com.

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25132 dc483132-0cff-0310-8789-dd5450dbe970

src/include/net-server.h
src/kadmin/server/Makefile.in
src/kadmin/server/ovsec_kadmd.c
src/kdc/Makefile.in
src/kdc/main.c
src/lib/apputils/net-server.c

index 144156de000a17739a640d8f54e64a43c7c689f3..b8414a158a6d1e25ac780e38acedd02cc39faa18 100644 (file)
 #ifndef NET_SERVER_H
 #define NET_SERVER_H
 
+#include <verto.h>
+
 typedef struct _krb5_fulladdr {
     krb5_address *      address;
     krb5_ui_4           port;
 } krb5_fulladdr;
 
 /* exported from network.c */
-extern volatile int signal_requests_exit, signal_requests_reset;
 void init_addr(krb5_fulladdr *, struct sockaddr *);
+
+/* exported from net-server.c */
+verto_ctx *loop_init(verto_ev_type types, void *handle, void (*reset)());
 krb5_error_code loop_add_udp_port(int port);
 krb5_error_code loop_add_tcp_port(int port);
 krb5_error_code loop_add_rpc_service(int port, u_long prognum, u_long versnum,
                                      void (*dispatch)());
-krb5_error_code loop_setup_network(void *handle, const char *prog,
-                                   int no_reconfig);
-krb5_error_code loop_listen_and_process(void *handle, const char *prog,
-                                        void (*reset)(void));
-void loop_closedown_network(void);
+krb5_error_code loop_setup_routing_socket(verto_ctx *ctx, void *handle,
+                                          const char *progname);
+krb5_error_code loop_setup_network(verto_ctx *ctx, void *handle,
+                                   const char *progname);
+void loop_free(verto_ctx *ctx);
 
 /* to be supplied by the server application */
 
@@ -56,7 +60,7 @@ void loop_closedown_network(void);
  * The first, dispatch(), is for normal processing of a request.  The
  * second, make_toolong_error(), is obviously for generating an error
  * to send back when the incoming message is bigger than
- * loop_listen_and_process can accept.
+ * the main loop can accept.
  */
 krb5_error_code dispatch (void *handle,
                           struct sockaddr *local_addr,
index 7872872d573254aa3849fe268c03d0eeae3f8844..f063e25d37cd663e6271046bba2624daefa80b80 100644 (file)
@@ -16,8 +16,8 @@ SRCS = kadm_rpc_svc.c server_stubs.c ovsec_kadmd.c schpw.c misc.c ipropd_svc.c
 
 all:: $(PROG)
 
-$(PROG): $(OBJS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(APPUTILS_DEPLIB)
-       $(CC_LINK) -o $(PROG) $(OBJS) $(APPUTILS_LIB) $(KADMSRV_LIBS) $(KDB_DEP_LIB) $(KRB5_BASE_LIBS)
+$(PROG): $(OBJS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(APPUTILS_DEPLIB) $(VERTO_DEPLIB)
+       $(CC_LINK) -o $(PROG) $(OBJS) $(APPUTILS_LIB) $(KADMSRV_LIBS) $(KDB_DEP_LIB) $(KRB5_BASE_LIBS) $(VERTO_LIBS)
 
 install::
        $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(SERVER_BINDIR)/$(PROG)
index 91cd255ece0c97dfc3e83d7e91b075cd8f88ce81..2800f877881eaa352096e76fb5db0c0465f06969 100644 (file)
 extern int daemon(int, int);
 #endif
 
-void    setup_signal_handlers(iprop_role iproprole);
-void    request_exit(int);
-void    request_hup(int);
-
-#ifdef POSIX_SIGNALS
-static struct sigaction s_action;
-#endif /* POSIX_SIGNALS */
-
-
 #define TIMEOUT 15
 
 gss_name_t gss_changepw_name = NULL, gss_oldchangepw_name = NULL;
@@ -235,6 +226,8 @@ int main(int argc, char *argv[])
 
     kdb_log_context *log_ctx;
 
+    verto_ctx *ctx;
+
     setlocale(LC_MESSAGES, "");
     setvbuf(stderr, NULL, _IONBF, 0);
 
@@ -364,6 +357,18 @@ int main(int argc, char *argv[])
         exit(1);
     }
 
+    ctx = loop_init(VERTO_EV_TYPE_SIGNAL, global_server_handle, NULL);
+    if (!ctx) {
+        krb5_klog_syslog(LOG_ERR,
+                         _("%s: could not initialize loop, aborting"),
+                         whoami);
+        fprintf(stderr, _("%s: could not initialize loop, aborting\n"),
+                        whoami);
+        kadm5_destroy(global_server_handle);
+        krb5_klog_close(context);
+        exit(1);
+    }
+
 #define server_handle ((kadm5_server_handle_t)global_server_handle)
     if ((ret = loop_add_udp_port(server_handle->params.kpasswd_port))
         || (ret = loop_add_tcp_port(server_handle->params.kpasswd_port))
@@ -377,12 +382,14 @@ int main(int argc, char *argv[])
             : 0)
 #endif
 #undef server_handle
-        || (ret = loop_setup_network(global_server_handle, whoami, 0))) {
+        || (ret = loop_setup_routing_socket(ctx, global_server_handle, whoami))
+        || (ret = loop_setup_network(ctx, global_server_handle, whoami))) {
         const char *e_txt = krb5_get_error_message (context, ret);
         krb5_klog_syslog(LOG_ERR, _("%s: %s while initializing network, "
                                     "aborting"), whoami, e_txt);
         fprintf(stderr, _("%s: %s while initializing network, aborting\n"),
                 whoami, e_txt);
+        loop_free(ctx);
         kadm5_destroy(global_server_handle);
         krb5_klog_close(context);
         exit(1);
@@ -395,6 +402,7 @@ int main(int argc, char *argv[])
                                     "names, failing."));
         fprintf(stderr, _("%s: Cannot build GSS-API authentication names.\n"),
                 whoami);
+        loop_free(ctx);
         kadm5_destroy(global_server_handle);
         krb5_klog_close(context);
         exit(1);
@@ -429,6 +437,7 @@ kterr:
     if (ret) {
         krb5_klog_syslog(LOG_ERR, "%s", krb5_get_error_message (context, ret));
         fprintf(stderr, _("%s: Can't set up keytab for RPC.\n"), whoami);
+        loop_free(ctx);
         kadm5_destroy(global_server_handle);
         krb5_klog_close(context);
         exit(1);
@@ -440,6 +449,7 @@ kterr:
         fprintf(stderr, _("%s: Cannot set GSS-API authentication names.\n"),
                 whoami);
         svcauth_gssapi_unset_names();
+        loop_free(ctx);
         kadm5_destroy(global_server_handle);
         krb5_klog_close(context);
         exit(1);
@@ -462,6 +472,7 @@ kterr:
     if (svcauth_gss_set_svc_name(GSS_C_NO_NAME) != TRUE) {
         fprintf(stderr, _("%s: Cannot initialize RPCSEC_GSS service name.\n"),
                 whoami);
+        loop_free(ctx);
         exit(1);
     }
 
@@ -471,6 +482,7 @@ kterr:
         fprintf(stderr, _("%s: Cannot initialize acl file: %s\n"),
                 whoami, errmsg);
         svcauth_gssapi_unset_names();
+        loop_free(ctx);
         kadm5_destroy(global_server_handle);
         krb5_klog_close(context);
         exit(1);
@@ -482,6 +494,7 @@ kterr:
         krb5_klog_syslog(LOG_ERR, _("Cannot detach from tty: %s"), errmsg);
         fprintf(stderr, _("%s: Cannot detach from tty: %s\n"), whoami, errmsg);
         svcauth_gssapi_unset_names();
+        loop_free(ctx);
         kadm5_destroy(global_server_handle);
         krb5_klog_close(context);
         exit(1);
@@ -493,6 +506,7 @@ kterr:
             krb5_klog_syslog(LOG_ERR, _("Cannot create PID file %s: %s"),
                              pid_file, errmsg);
             svcauth_gssapi_unset_names();
+            loop_free(ctx);
             kadm5_destroy(global_server_handle);
             krb5_klog_close(context);
             exit(1);
@@ -505,6 +519,7 @@ kterr:
         krb5_klog_syslog(LOG_ERR, _("Error getting random seed: %s, aborting"),
                          krb5_get_error_message(context, ret));
         svcauth_gssapi_unset_names();
+        loop_free(ctx);
         kadm5_destroy(global_server_handle);
         krb5_klog_close(context);
         exit(1);
@@ -530,6 +545,7 @@ kterr:
             krb5_klog_syslog(LOG_ERR,
                              _("%s while mapping update log (`%s.ulog')"),
                              error_message(ret), params.dbname);
+            loop_free(ctx);
             krb5_klog_close(context);
             exit(1);
         }
@@ -551,6 +567,7 @@ kterr:
             krb5_klog_syslog(LOG_ERR,
                              _("Cannot create IProp RPC service (PROG=%d, VERS=%d), failing."),
                              KRB5_IPROP_PROG, KRB5_IPROP_VERS);
+            loop_free(ctx);
             krb5_klog_close(context);
             exit(1);
         }
@@ -566,6 +583,7 @@ kterr:
             fprintf(stderr,
                     _("%s: %s while getting IProp svc name, failing\n"),
                     whoami, error_message(ret));
+            loop_free(ctx);
             krb5_klog_close(context);
             exit(1);
         }
@@ -601,24 +619,24 @@ kterr:
                         err.system_error);
             }
 
+            loop_free(ctx);
             exit(1);
         }
         free(kiprop_name);
 #endif
     }
 
-    setup_signal_handlers(log_ctx->iproprole);
     krb5_klog_syslog(LOG_INFO, _("starting"));
     if (nofork)
         fprintf(stderr, _("%s: starting...\n"), whoami);
 
-    loop_listen_and_process(global_server_handle, whoami, NULL);
+    verto_run(ctx);
     krb5_klog_syslog(LOG_INFO, _("finished, exiting"));
 
     /* Clean up memory, etc */
     svcauth_gssapi_unset_names();
     kadm5_destroy(global_server_handle);
-    loop_closedown_network();
+    loop_free(ctx);
     kadm5int_acl_finish(context, 0);
     if(gss_changepw_name) {
         (void) gss_release_name(&OMret, &gss_changepw_name);
@@ -637,91 +655,6 @@ kterr:
     exit(2);
 }
 
-/*
- * Function: setup_signal_handlers
- *
- * Purpose: Setup signal handling functions using POSIX's sigaction()
- * if possible, otherwise with System V's signal().
- */
-
-void setup_signal_handlers(iprop_role iproprole) {
-#ifdef POSIX_SIGNALS
-    (void) sigemptyset(&s_action.sa_mask);
-    s_action.sa_handler = request_exit;
-    (void) sigaction(SIGINT, &s_action, (struct sigaction *) NULL);
-    (void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL);
-    (void) sigaction(SIGQUIT, &s_action, (struct sigaction *) NULL);
-    s_action.sa_handler = request_hup;
-    (void) sigaction(SIGHUP, &s_action, (struct sigaction *) NULL);
-    s_action.sa_handler = SIG_IGN;
-    (void) sigaction(SIGPIPE, &s_action, (struct sigaction *) NULL);
-
-    /*
-     * IProp will fork for a full-resync, we don't want to
-     * wait on it and we don't want the living dead procs either.
-     */
-    if (iproprole == IPROP_MASTER) {
-        s_action.sa_handler = SIG_IGN;
-        (void) sigaction(SIGCHLD, &s_action, (struct sigaction *) NULL);
-    }
-#else /* POSIX_SIGNALS */
-    signal(SIGINT, request_exit);
-    signal(SIGTERM, request_exit);
-    signal(SIGQUIT, request_exit);
-    signal(SIGHUP, request_hup);
-    signal(SIGPIPE, SIG_IGN);
-
-    /*
-     * IProp will fork for a full-resync, we don't want to
-     * wait on it and we don't want the living dead procs either.
-     */
-    if (iproprole == IPROP_MASTER)
-        (void) signal(SIGCHLD, SIG_IGN);
-#endif /* POSIX_SIGNALS */
-}
-
-/*
- * Function: request_hup
- *
- * Purpose: sets flag saying the server got a signal and that it should
- *              reset the database files when convenient.
- *
- * Arguments:
- * Requires:
- * Effects:
- * Modifies:
- *      sets signal_requests_reset to one
- */
-
-void request_hup(int signum)
-{
-    signal_requests_reset = 1;
-    return;
-}
-
-/*
- * Function: request_exit
- *
- * Purpose: sets flags saying the server got a signal and that it
- *          should exit when convient.
- *
- * Arguments:
- * Requires:
- * Effects:
- *      modifies signal_requests_exit which ideally makes the server exit
- *      at some point.
- *
- * Modifies:
- *      signal_requests_exit
- */
-
-void request_exit(int signum)
-{
-    krb5_klog_syslog(LOG_DEBUG, _("Got signal to request exit"));
-    signal_requests_exit = 1;
-    return;
-}
-
 /*
  * Function: build_princ_name
  *
index f46cad3da0343c87eb3906393347169dea42fde3..1c7bbf9611f2f583005fa8a505ef45ad30f8fcb6 100644 (file)
@@ -53,8 +53,8 @@ kdc5_err.h: kdc5_err.et
 
 kdc5_err.o: kdc5_err.h
 
-krb5kdc: $(OBJS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(APPUTILS_DEPLIB)
-       $(CC_LINK) -o krb5kdc $(OBJS) $(APPUTILS_LIB) $(KADMSRV_LIBS) $(KRB5_BASE_LIBS)
+krb5kdc: $(OBJS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(APPUTILS_DEPLIB) $(VERTO_DEPLIB)
+       $(CC_LINK) -o krb5kdc $(OBJS) $(APPUTILS_LIB) $(KADMSRV_LIBS) $(KRB5_BASE_LIBS) $(VERTO_LIBS)
 
 rtest: $(RT_OBJS) $(KDB5_DEPLIBS) $(KADM_COMM_DEPLIBS) $(KRB5_BASE_DEPLIBS)
        $(CC_LINK) -o rtest $(RT_OBJS) $(KDB5_LIBS) $(KADM_COMM_LIBS) $(KRB5_BASE_LIBS)
index a6a5751660bd86902ba40f37947a81162f01f602..979c2173a00e3cd72d79384047667e8dec256206 100644 (file)
@@ -78,11 +78,6 @@ extern int daemon(int, int);
 
 static void usage (char *);
 
-static krb5_sigtype request_exit (int);
-static krb5_sigtype request_hup  (int);
-
-static void setup_signal_handlers (void);
-
 static krb5_error_code setup_sam (void);
 
 static void initialize_realms (krb5_context, int, char **);
@@ -93,10 +88,8 @@ static int nofork = 0;
 static int workers = 0;
 static const char *pid_file = NULL;
 static int rkey_init_done = 0;
-
-#ifdef POSIX_SIGNALS
-static struct sigaction s_action;
-#endif /* POSIX_SIGNALS */
+static volatile int signal_received = 0;
+static volatile int sighup_received = 0;
 
 #define KRB5_KDC_MAX_REALMS     32
 
@@ -486,9 +479,9 @@ whoops:
 }
 
 static krb5_sigtype
-request_exit(int signo)
+on_monitor_signal(int signo)
 {
-    signal_requests_exit = 1;
+    signal_received = signo;
 
 #ifdef POSIX_SIGTYPE
     return;
@@ -498,9 +491,9 @@ request_exit(int signo)
 }
 
 static krb5_sigtype
-request_hup(int signo)
+on_monitor_sighup(int signo)
 {
-    signal_requests_reset = 1;
+    sighup_received = 1;
 
 #ifdef POSIX_SIGTYPE
     return;
@@ -509,29 +502,6 @@ request_hup(int signo)
 #endif
 }
 
-static void
-setup_signal_handlers(void)
-{
-#ifdef POSIX_SIGNALS
-    (void) sigemptyset(&s_action.sa_mask);
-    s_action.sa_flags = 0;
-    s_action.sa_handler = request_exit;
-    (void) sigaction(SIGINT, &s_action, (struct sigaction *) NULL);
-    (void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL);
-    s_action.sa_handler = request_hup;
-    (void) sigaction(SIGHUP, &s_action, (struct sigaction *) NULL);
-    s_action.sa_handler = SIG_IGN;
-    (void) sigaction(SIGPIPE, &s_action, (struct sigaction *) NULL);
-#else  /* POSIX_SIGNALS */
-    signal(SIGINT, request_exit);
-    signal(SIGTERM, request_exit);
-    signal(SIGHUP, request_hup);
-    signal(SIGPIPE, SIG_IGN);
-#endif /* POSIX_SIGNALS */
-
-    return;
-}
-
 /*
  * Kill the worker subprocesses given by pids[0..bound-1], skipping any which
  * are set to -1, and wait for them to exit (so that we know the ports are no
@@ -564,10 +534,13 @@ terminate_workers(pid_t *pids, int bound, int num_active)
  * function in error cases.
  */
 static krb5_error_code
-create_workers(int num)
+create_workers(verto_ctx *ctx, int num)
 {
     int i, status, numleft;
     pid_t pid, *pids;
+#ifdef POSIX_SIGNALS
+    struct sigaction s_action;
+#endif /* POSIX_SIGNALS */
 
     /* Create child worker processes; return in each child. */
     krb5_klog_syslog(LOG_INFO, _("creating %d worker processes"), num);
@@ -591,9 +564,29 @@ create_workers(int num)
         pids[i] = pid;
     }
 
+    /* We're going to use our own main loop here. */
+    loop_free(ctx);
+
+    /* Setup our signal handlers which will forward to the children. */
+#ifdef POSIX_SIGNALS
+    (void) sigemptyset(&s_action.sa_mask);
+    s_action.sa_flags = 0;
+    s_action.sa_handler = on_monitor_signal;
+    (void) sigaction(SIGINT, &s_action, (struct sigaction *) NULL);
+    (void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL);
+    (void) sigaction(SIGQUIT, &s_action, (struct sigaction *) NULL);
+    s_action.sa_handler = on_monitor_sighup;
+    (void) sigaction(SIGHUP, &s_action, (struct sigaction *) NULL);
+#else  /* POSIX_SIGNALS */
+    signal(SIGINT, on_monitor_signal);
+    signal(SIGTERM, on_monitor_signal);
+    signal(SIGQUIT, on_monitor_signal);
+    signal(SIGHUP, on_monitor_sighup);
+#endif /* POSIX_SIGNALS */
+
     /* Supervise the worker processes. */
     numleft = num;
-    while (!signal_requests_exit) {
+    while (!signal_received) {
         /* Wait until a worker process exits or we get a signal. */
         pid = wait(&status);
         if (pid >= 0) {
@@ -612,18 +605,17 @@ create_workers(int num)
         }
 
         /* Propagate HUP signal to worker processes if we received one. */
-        if (signal_requests_reset) {
+        if (sighup_received) {
+            sighup_received = 0;
             for (i = 0; i < num; i++) {
                 if (pids[i] != -1)
                     kill(pids[i], SIGHUP);
             }
-            signal_requests_reset = 0;
         }
     }
-    if (signal_requests_exit) {
-        krb5_klog_syslog(LOG_INFO,
-                         _("shutdown signal received in supervisor"));
-    }
+    if (signal_received)
+        krb5_klog_syslog(LOG_INFO, _("signal %d received in supervisor"),
+                         signal_received);
 
     terminate_workers(pids, num, numleft);
     free(pids);
@@ -948,6 +940,7 @@ int main(int argc, char **argv)
 {
     krb5_error_code     retval;
     krb5_context        kcontext;
+    verto_ctx *ctx;
     int errout = 0;
     int i;
 
@@ -989,7 +982,12 @@ int main(int argc, char **argv)
      */
     initialize_realms(kcontext, argc, argv);
 
-    setup_signal_handlers();
+    ctx = loop_init(VERTO_EV_TYPE_NONE, NULL, reset_for_hangup);
+    if (!ctx) {
+        kdc_err(kcontext, ENOMEM, _("while creating main loop"));
+        finish_realms();
+        return 1;
+    }
 
     load_preauth_plugins(kcontext);
     load_authdata_plugins(kcontext);
@@ -1039,7 +1037,15 @@ int main(int argc, char **argv)
      * children won't be able to re-open the listener sockets.  Hopefully our
      * platform has pktinfo support and doesn't need reconfigs.
      */
-    if ((retval = loop_setup_network(NULL, kdc_progname, (workers > 0)))) {
+    if (workers == 0) {
+        retval = loop_setup_routing_socket(ctx, NULL, kdc_progname);
+        if (retval) {
+            kdc_err(kcontext, retval, _("while initializing routing socket"));
+            finish_realms();
+            return 1;
+        }
+    }
+    if ((retval = loop_setup_network(ctx, NULL, kdc_progname))) {
     net_init_error:
         kdc_err(kcontext, retval, _("while initializing network"));
         finish_realms();
@@ -1060,7 +1066,7 @@ int main(int argc, char **argv)
     }
     if (workers > 0) {
         finish_realms();
-        retval = create_workers(workers);
+        retval = create_workers(ctx, workers);
         if (retval) {
             kdc_err(kcontext, errno, _("creating worker processes"));
             return 1;
@@ -1071,11 +1077,9 @@ int main(int argc, char **argv)
     krb5_klog_syslog(LOG_INFO, _("commencing operation"));
     if (nofork)
         fprintf(stderr, _("%s: starting...\n"), kdc_progname);
-    if ((retval = loop_listen_and_process(0, kdc_progname, reset_for_hangup))) {
-        kdc_err(kcontext, retval, _("while processing network requests"));
-        errout++;
-    }
-    loop_closedown_network();
+
+    verto_run(ctx);
+    loop_free(ctx);
     krb5_klog_syslog(LOG_INFO, _("shutting down"));
     unload_preauth_plugins(kcontext);
     unload_authdata_plugins(kcontext);
index 5afc3c0fa0bba295d15ae87d4d143688e6fe8902..152f11c2ba5f8899ea9d7c858a2b81862e9c44b0 100644 (file)
 #include "fake-addrinfo.h"
 #include "net-server.h"
 
+#include <signal.h>
+
 /* XXX */
 #define KDC5_NONET                               (-1779992062L)
 
-volatile int signal_requests_exit = 0, signal_requests_reset = 0;
-
-static void loop_closedown_network_sockets(void);
+static int tcp_or_rpc_data_counter;
+static int max_tcp_or_rpc_data_connections = 45;
 
 /* Misc utility routines.  */
 static void
@@ -187,9 +188,9 @@ enum conn_type {
 
 /* Per-connection info.  */
 struct connection {
-    int fd;
+    void *handle;
+    const char *prog;
     enum conn_type type;
-    void (*service)(void *handle, struct connection *, const char *, int);
     union {
         /* Type-specific information.  */
         struct {
@@ -215,6 +216,7 @@ struct connection {
         } tcp;
         struct {
             SVCXPRT *transp;
+            int closed;
         } rpc;
     } u;
 };
@@ -250,33 +252,84 @@ struct connection {
 #define FREE_SET_DATA(set)                                      \
     (free(set.data), set.data = 0, set.max = 0, set.n = 0)
 
-
-/* Set<struct connection *> connections; */
-static SET(struct connection *) connections;
-#define n_sockets       connections.n
-#define conns           connections.data
-
-/* Set<u_short> udp_port_data, tcp_port_data; */
 /*
  * N.B.: The Emacs cc-mode indentation code seems to get confused if
  * the macro argument here is one word only.  So use "unsigned short"
  * instead of the "u_short" we were using before.
  */
-static SET(unsigned short) udp_port_data, tcp_port_data;
-
 struct rpc_svc_data {
     u_short port;
     u_long prognum;
     u_long versnum;
     void (*dispatch)();
 };
-
+static SET(unsigned short) udp_port_data, tcp_port_data;
 static SET(struct rpc_svc_data) rpc_svc_data;
+static SET(verto_ev *) events;
+
+static void
+do_break(verto_ctx *ctx, verto_ev *ev)
+{
+    krb5_klog_syslog(LOG_DEBUG, _("Got signal to request exit"));
+    verto_break(ctx);
+}
 
-#include "cm.h"
+struct sighup_context {
+    void *handle;
+    void (*reset)();
+};
 
-static struct select_state sstate;
-static fd_set rpc_listenfds;
+static void
+do_reset(verto_ctx *ctx, verto_ev *ev)
+{
+    struct sighup_context *sc = (struct sighup_context*) verto_get_private(ev);
+
+    krb5_klog_syslog(LOG_DEBUG, _("Got signal to reset"));
+    krb5_klog_reopen(get_context(sc->handle));
+    if (sc->reset)
+        sc->reset();
+}
+
+static void
+free_sighup_context(verto_ctx *ctx, verto_ev *ev)
+{
+    free(verto_get_private(ev));
+}
+
+verto_ctx *
+loop_init(verto_ev_type types, void *handle, void (*reset)())
+{
+    struct sighup_context *sc;
+    verto_ctx *ctx;
+    verto_ev  *ev;
+
+    types |= VERTO_EV_TYPE_IO;
+    types |= VERTO_EV_TYPE_SIGNAL;
+    types |= VERTO_EV_TYPE_TIMEOUT;
+    ctx = verto_default(NULL, types);
+    if (!verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_break, SIGINT)  ||
+        !verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_break, SIGTERM) ||
+        !verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_break, SIGQUIT) ||
+        !verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, VERTO_SIG_IGN, SIGPIPE))
+        goto error;
+
+    ev = verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_reset, SIGHUP);
+    if (!ev)
+        goto error;
+
+    sc = malloc(sizeof(*sc));
+    if (!sc)
+        goto error;
+    sc->handle = handle;
+    sc->reset = reset;
+
+    verto_set_private(ev, sc, free_sighup_context);
+    return ctx;
+
+error:
+    verto_free(ctx);
+    return NULL;
+}
 
 krb5_error_code
 loop_add_udp_port(int port)
@@ -348,6 +401,8 @@ loop_add_rpc_service(int port, u_long prognum,
 #include "foreachaddr.h"
 
 struct socksetup {
+    verto_ctx *ctx;
+    void *handle;
     const char *prog;
     krb5_error_code retval;
     int udp_flags;
@@ -355,12 +410,133 @@ struct socksetup {
 #define UDP_DO_IPV6 2
 };
 
-static struct connection *
+static void
+free_connection(struct connection *conn)
+{
+    if (!conn)
+        return;
+    if (conn->u.tcp.response)
+        krb5_free_data(get_context(conn->handle), conn->u.tcp.response);
+    if (conn->u.tcp.buffer)
+        free(conn->u.tcp.buffer);
+    if (conn->type == CONN_RPC_LISTENER && conn->u.rpc.transp != NULL)
+        svc_destroy(conn->u.rpc.transp);
+    free(conn);
+}
+
+static void
+remove_event_from_set(verto_ev *ev)
+{
+    verto_ev *tmp;
+    int i;
+
+    /* Remove the event from the events. */
+    FOREACH_ELT(events, i, tmp)
+        if (tmp == ev) {
+            DEL(events, i);
+            break;
+        }
+}
+
+static void
+free_socket(verto_ctx *ctx, verto_ev *ev)
+{
+    struct connection *conn = NULL;
+    fd_set fds;
+    int fd;
+
+    remove_event_from_set(ev);
+
+    fd = verto_get_fd(ev);
+    conn = verto_get_private(ev);
+
+    /* Close the file descriptor. */
+    krb5_klog_syslog(LOG_INFO, _("closing down fd %d"), fd);
+    if (fd >= 0 && (!conn || conn->type != CONN_RPC || conn->u.rpc.closed))
+        close(fd);
+
+    /* Free the connection struct. */
+    if (conn) {
+        switch (conn->type) {
+            case CONN_RPC:
+                if (conn->u.rpc.closed) {
+                    FD_ZERO(&fds);
+                    FD_SET(fd, &fds);
+                    svc_getreqset(&fds);
+                    if (FD_ISSET(fd, &svc_fdset)) {
+                        krb5_klog_syslog(LOG_ERR,
+                                         _("descriptor %d closed but still "
+                                           "in svc_fdset"),
+                                         fd);
+                    }
+                }
+                /* Fall through. */
+            case CONN_TCP:
+                tcp_or_rpc_data_counter--;
+                break;
+            default:
+                break;
+        }
+
+        free_connection(conn);
+    }
+}
+
+static verto_ev *
+make_event(verto_ctx *ctx, verto_ev_flag flags, verto_callback callback,
+           int sock, struct connection *conn, int addevent)
+{
+    verto_ev *ev, *tmp;
+
+    ev = verto_add_io(ctx, flags, callback, sock);
+    if (!ev) {
+        com_err(conn->prog, ENOMEM, _("cannot create io event"));
+        return NULL;
+    }
+
+    if (addevent) {
+        if (!ADD(events, ev, tmp)) {
+            com_err(conn->prog, ENOMEM, _("cannot save event"));
+            verto_del(ev);
+            return NULL;
+        }
+    }
+
+    verto_set_private(ev, conn, free_socket);
+    return ev;
+}
+
+static verto_ev *
+convert_event(verto_ctx *ctx, verto_ev *ev, verto_ev_flag flags,
+              verto_callback callback)
+{
+    struct connection *conn;
+    verto_ev *newev;
+    int sock;
+
+    conn = verto_get_private(ev);
+    sock = verto_get_fd(ev);
+    if (sock < 0)
+        return NULL;
+
+    newev = make_event(ctx, flags, callback, sock, conn, 1);
+
+    /* Delete the read event without closing the socket
+     * or freeing the connection struct. */
+    if (newev) {
+        verto_set_private(ev, NULL, NULL); /* Reset the destructor. */
+        remove_event_from_set(ev); /* Remove it from the set. */
+        verto_del(ev);
+    }
+
+    return newev;
+}
+
+static verto_ev *
 add_fd(struct socksetup *data, int sock, enum conn_type conntype,
-       void (*service)(void *handle, struct connection *, const char *, int))
+       verto_ev_flag flags, verto_callback callback, int addevent)
 {
     struct connection *newconn;
-    void *tmp;
 
 #ifndef _WIN32
     if (sock >= FD_SETSIZE) {
@@ -377,62 +553,43 @@ add_fd(struct socksetup *data, int sock, enum conn_type conntype,
                 _("cannot allocate storage for connection info"));
         return 0;
     }
-    if (!ADD(connections, newconn, tmp)) {
-        data->retval = ENOMEM;
-        com_err(data->prog, ENOMEM, _("cannot save socket info"));
-        free(newconn);
-        return 0;
-    }
-
     memset(newconn, 0, sizeof(*newconn));
+    newconn->handle = data->handle;
+    newconn->prog = data->prog;
     newconn->type = conntype;
-    newconn->fd = sock;
-    newconn->service = service;
-    return newconn;
+
+    return make_event(data->ctx, flags, callback, sock, newconn, addevent);
 }
 
-static void process_packet(void *handle, struct connection *, const char *,
-                           int);
-static void accept_tcp_connection(void *handle, struct connection *,
-                                  const char *, int);
-static void process_tcp_connection(void *handle, struct connection *,
-                                   const char *, int);
-static void accept_rpc_connection(void *handle, struct connection *,
-                                  const char *, int);
-static void process_rpc_connection(void *handle, struct connection *,
-                                   const char *, int);
-
-static struct connection *
+static void process_packet(verto_ctx *ctx, verto_ev *ev);
+static void accept_tcp_connection(verto_ctx *ctx, verto_ev *ev);
+static void process_tcp_connection_read(verto_ctx *ctx, verto_ev *ev);
+static void process_tcp_connection_write(verto_ctx *ctx, verto_ev *ev);
+static void accept_rpc_connection(verto_ctx *ctx, verto_ev *ev);
+static void process_rpc_connection(verto_ctx *ctx, verto_ev *ev);
+
+static verto_ev *
 add_udp_fd(struct socksetup *data, int sock, int pktinfo)
 {
     return add_fd(data, sock, pktinfo ? CONN_UDP_PKTINFO : CONN_UDP,
-                  process_packet);
+                  VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST,
+                  process_packet, 1);
 }
 
-static struct connection *
+static verto_ev *
 add_tcp_listener_fd(struct socksetup *data, int sock)
 {
-    return add_fd(data, sock, CONN_TCP_LISTENER, accept_tcp_connection);
-}
-
-static struct connection *
-add_tcp_data_fd(struct socksetup *data, int sock)
-{
-    return add_fd(data, sock, CONN_TCP, process_tcp_connection);
+    return add_fd(data, sock, CONN_TCP_LISTENER,
+                  VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST,
+                  accept_tcp_connection, 1);
 }
 
-static void
-delete_fd(struct connection *xconn)
+static verto_ev *
+add_tcp_read_fd(struct socksetup *data, int sock)
 {
-    struct connection *conn;
-    int i;
-
-    FOREACH_ELT(connections, i, conn)
-        if (conn == xconn) {
-            DEL(connections, i);
-            break;
-        }
-    free(xconn);
+    return add_fd(data, sock, CONN_TCP,
+                  VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST,
+                  process_tcp_connection_read, 1);
 }
 
 /*
@@ -494,21 +651,25 @@ create_server_socket(struct socksetup *data, struct sockaddr *addr, int type)
     return sock;
 }
 
-static struct connection *
+static verto_ev *
 add_rpc_listener_fd(struct socksetup *data, struct rpc_svc_data *svc, int sock)
 {
     struct connection *conn;
+    verto_ev *ev;
 
-    conn = add_fd(data, sock, CONN_RPC_LISTENER, accept_rpc_connection);
-    if (conn == NULL)
+    ev = add_fd(data, sock, CONN_RPC_LISTENER,
+                VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST,
+                accept_rpc_connection, 1);
+    if (ev == NULL)
         return NULL;
 
+    conn = verto_get_private(ev);
     conn->u.rpc.transp = svctcp_create(sock, 0, 0);
     if (conn->u.rpc.transp == NULL) {
         krb5_klog_syslog(LOG_ERR,
                          _("Cannot create RPC service: %s; continuing"),
                          strerror(errno));
-        delete_fd(conn);
+        verto_del(ev);
         return NULL;
     }
 
@@ -517,17 +678,19 @@ add_rpc_listener_fd(struct socksetup *data, struct rpc_svc_data *svc, int sock)
         krb5_klog_syslog(LOG_ERR,
                          _("Cannot register RPC service: %s; continuing"),
                          strerror(errno));
-        delete_fd(conn);
+        verto_del(ev);
         return NULL;
     }
 
-    return conn;
+    return ev;
 }
 
-static struct connection *
+static verto_ev *
 add_rpc_data_fd(struct socksetup *data, int sock)
 {
-    return add_fd(data, sock, CONN_RPC, process_rpc_connection);
+    return add_fd(data, sock, CONN_RPC,
+                  VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST,
+                  process_rpc_connection, 1);
 }
 
 static const int one = 1;
@@ -637,9 +800,6 @@ setup_tcp_listener_ports(struct socksetup *data)
             if (add_tcp_listener_fd(data, s4) == NULL)
                 close(s4);
             else {
-                FD_SET(s4, &sstate.rfds);
-                if (s4 >= sstate.max)
-                    sstate.max = s4 + 1;
                 krb5_klog_syslog(LOG_INFO, _("listening on fd %d: tcp %s"),
                                  s4, paddr((struct sockaddr *)&sin4));
             }
@@ -650,9 +810,6 @@ setup_tcp_listener_ports(struct socksetup *data)
                 close(s6);
                 s6 = -1;
             } else {
-                FD_SET(s6, &sstate.rfds);
-                if (s6 >= sstate.max)
-                    sstate.max = s6 + 1;
                 krb5_klog_syslog(LOG_INFO, _("listening on fd %d: tcp %s"),
                                  s6, paddr((struct sockaddr *)&sin6));
             }
@@ -704,13 +861,9 @@ setup_rpc_listener_ports(struct socksetup *data)
 
         if (add_rpc_listener_fd(data, &svc, s4) == NULL)
             close(s4);
-        else {
-            FD_SET(s4, &sstate.rfds);
-            if (s4 >= sstate.max)
-                sstate.max = s4 + 1;
+        else
             krb5_klog_syslog(LOG_INFO, _("listening on fd %d: rpc %s"),
                              s4, paddr((struct sockaddr *)&sin4));
-        }
 
 #ifdef KRB5_USE_INET6
         if (ipv6_enabled()) {
@@ -722,18 +875,13 @@ setup_rpc_listener_ports(struct socksetup *data)
 
             if (add_rpc_listener_fd(data, &svc, s6) == NULL)
                 close(s6);
-            else {
-                FD_SET(s6, &sstate.rfds);
-                if (s6 >= sstate.max)
-                    sstate.max = s6 + 1;
+            else
                 krb5_klog_syslog(LOG_INFO, _("listening on fd %d: rpc %s"),
                                  s6, paddr((struct sockaddr *)&sin6));
-            }
         }
 #endif
     }
-    FD_ZERO(&rpc_listenfds);
-    rpc_listenfds = svc_fdset;
+
     return 0;
 }
 
@@ -789,7 +937,7 @@ setup_udp_pktinfo_ports(struct socksetup *data)
 }
 #else /* no pktinfo compile-time support */
 static void
-setup_udp_pktinfo_ports(struct socksetup *data)
+setup_udp_pktinfo_ports(verto_ctx *ctx, struct socksetup *data)
 {
 }
 #endif
@@ -829,9 +977,6 @@ setup_udp_port_1(struct socksetup *data, struct sockaddr *addr,
             close(sock);
             return 1;
         }
-        FD_SET (sock, &sstate.rfds);
-        if (sock >= sstate.max)
-            sstate.max = sock + 1;
     }
     return 0;
 }
@@ -939,8 +1084,6 @@ scan_for_newlines:
 }
 #endif
 
-static int network_reconfiguration_needed = 0;
-
 #ifdef HAVE_STRUCT_RT_MSGHDR
 #include <net/route.h>
 
@@ -969,13 +1112,88 @@ rtm_type_name(int type)
 }
 
 static void
-process_routing_update(void *handle, struct connection *conn, const char *prog,
-                       int selflags)
+do_network_reconfig(verto_ctx *ctx, verto_ev *ev)
 {
-    int n_read;
+    struct connection *conn = verto_get_private(ev);
+    assert(loop_setup_network(ctx, conn->handle, conn->prog) == 0);
+}
+
+static int
+routing_update_needed(struct rt_msghdr *rtm)
+{
+    switch (rtm->rtm_type) {
+    case RTM_ADD:
+    case RTM_DELETE:
+    case RTM_NEWADDR:
+    case RTM_DELADDR:
+    case RTM_IFINFO:
+    case RTM_OLDADD:
+    case RTM_OLDDEL:
+        /*
+         * Some flags indicate routing table updates that don't
+         * indicate local address changes.  They may come from
+         * redirects, or ARP, etc.
+         *
+         * This set of symbols is just an initial guess based on
+         * some messages observed in real life; working out which
+         * other flags also indicate messages we should ignore,
+         * and which flags are portable to all system and thus
+         * don't need to be conditionalized, is left as a future
+         * exercise.
+         */
+#ifdef RTF_DYNAMIC
+        if (rtm->rtm_flags & RTF_DYNAMIC)
+            break;
+#endif
+#ifdef RTF_CLONED
+        if (rtm->rtm_flags & RTF_CLONED)
+            break;
+#endif
+#ifdef RTF_LLINFO
+        if (rtm->rtm_flags & RTF_LLINFO)
+            break;
+#endif
+#if 0
+        krb5_klog_syslog(LOG_DEBUG,
+                         "network reconfiguration message (%s) received",
+                         rtm_type_name(rtm->rtm_type));
+#endif
+        return 1;
+    case RTM_RESOLVE:
+#ifdef RTM_NEWMADDR
+    case RTM_NEWMADDR:
+    case RTM_DELMADDR:
+#endif
+    case RTM_MISS:
+    case RTM_REDIRECT:
+    case RTM_LOSING:
+    case RTM_GET:
+        /* Not interesting.  */
+#if 0
+        krb5_klog_syslog(LOG_DEBUG, "routing msg not interesting");
+#endif
+        break;
+    default:
+        krb5_klog_syslog(LOG_INFO,
+                         _("unhandled routing message type %d, "
+                           "will reconfigure just for the fun of it"),
+                         rtm->rtm_type);
+        return 1;
+    }
+
+    return 0;
+}
+
+static void
+process_routing_update(verto_ctx *ctx, verto_ev *ev)
+{
+    int n_read, fd;
     struct rt_msghdr rtm;
+    struct connection *conn;
 
-    while ((n_read = read(conn->fd, &rtm, sizeof(rtm))) > 0) {
+    fd = verto_get_fd(ev);
+    conn = verto_get_private(ev);
+    while ((n_read = read(fd, &rtm, sizeof(rtm))) > 0) {
         if (n_read < sizeof(rtm)) {
             /* Quick hack to figure out if the interesting
                fields are present in a short read.
@@ -1007,73 +1225,31 @@ process_routing_update(void *handle, struct connection *conn, const char *prog,
                              _("read %d from routing socket but msglen is %d"),
                              n_read, rtm.rtm_msglen);
         }
-        switch (rtm.rtm_type) {
-        case RTM_ADD:
-        case RTM_DELETE:
-        case RTM_NEWADDR:
-        case RTM_DELADDR:
-        case RTM_IFINFO:
-        case RTM_OLDADD:
-        case RTM_OLDDEL:
-            /*
-             * Some flags indicate routing table updates that don't
-             * indicate local address changes.  They may come from
-             * redirects, or ARP, etc.
-             *
-             * This set of symbols is just an initial guess based on
-             * some messages observed in real life; working out which
-             * other flags also indicate messages we should ignore,
-             * and which flags are portable to all system and thus
-             * don't need to be conditionalized, is left as a future
-             * exercise.
-             */
-#ifdef RTF_DYNAMIC
-            if (rtm.rtm_flags & RTF_DYNAMIC)
-                break;
-#endif
-#ifdef RTF_CLONED
-            if (rtm.rtm_flags & RTF_CLONED)
-                break;
-#endif
-#ifdef RTF_LLINFO
-            if (rtm.rtm_flags & RTF_LLINFO)
-                break;
-#endif
-#if 0
-            krb5_klog_syslog(LOG_DEBUG,
-                             "network reconfiguration message (%s) received",
-                             rtm_type_name(rtm.rtm_type));
-#endif
-            network_reconfiguration_needed = 1;
-            break;
-        case RTM_RESOLVE:
-#ifdef RTM_NEWMADDR
-        case RTM_NEWMADDR:
-        case RTM_DELMADDR:
-#endif
-        case RTM_MISS:
-        case RTM_REDIRECT:
-        case RTM_LOSING:
-        case RTM_GET:
-            /* Not interesting.  */
-#if 0
-            krb5_klog_syslog(LOG_DEBUG, "routing msg not interesting");
-#endif
-            break;
-        default:
-            krb5_klog_syslog(LOG_INFO,
-                             _("unhandled routing message type %d, "
-                               "will reconfigure just for the fun of it"),
-                             rtm.rtm_type);
-            network_reconfiguration_needed = 1;
-            break;
+
+        if (routing_update_needed(&rtm)) {
+            /* Ideally we would use idle here instead of timeout. However, idle
+             * is not universally supported yet in all backends. So let's just
+             * use timeout for now to avoid locking into a loop. */
+            ev = verto_add_timeout(ctx, VERTO_EV_FLAG_NONE,
+                                   do_network_reconfig, 0);
+            verto_set_private(ev, conn, NULL);
+            assert(ev);
         }
     }
 }
+#endif
 
-static void
-setup_routing_socket(struct socksetup *data)
+krb5_error_code
+loop_setup_routing_socket(verto_ctx *ctx, void *handle, const char *progname)
 {
+#ifdef HAVE_STRUCT_RT_MSGHDR
+    struct socksetup data;
+
+    data.ctx = ctx;
+    data.handle = handle;
+    data.prog = progname;
+    data.retval = 0;
+
     int sock = socket(PF_ROUTE, SOCK_RAW, 0);
     if (sock < 0) {
         int e = errno;
@@ -1081,37 +1257,38 @@ setup_routing_socket(struct socksetup *data)
                          strerror(e));
     } else {
         krb5_klog_syslog(LOG_INFO, _("routing socket is fd %d"), sock);
-        add_fd(data, sock, CONN_ROUTING, process_routing_update);
         setnbio(sock);
-        FD_SET(sock, &sstate.rfds);
+        add_fd(&data, sock, CONN_ROUTING,
+               VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST,
+               process_routing_update, 0);
     }
-}
 #endif
+    return 0;
+}
 
 /* XXX */
-extern int krb5int_debug_sendto_kdc;
 extern void (*krb5int_sendtokdc_debug_handler)(const void*, size_t);
 
 krb5_error_code
-loop_setup_network(void *handle, const char *prog, int no_reconfig)
+loop_setup_network(verto_ctx *ctx, void *handle, const char *prog)
 {
     struct socksetup setup_data;
+    verto_ev *ev;
+    int i;
 
-    FD_ZERO(&sstate.rfds);
-    FD_ZERO(&sstate.wfds);
-    FD_ZERO(&sstate.xfds);
-    sstate.max = 0;
-
-    /* krb5int_debug_sendto_kdc = 1; */
     krb5int_sendtokdc_debug_handler = klog_handler;
 
+    /* Close any open connections. */
+    FOREACH_ELT(events, i, ev)
+        verto_del(ev);
+    events.n = 0;
+
+    setup_data.ctx = ctx;
+    setup_data.handle = handle;
     setup_data.prog = prog;
     setup_data.retval = 0;
     krb5_klog_syslog(LOG_INFO, _("setting up network..."));
-#ifdef HAVE_STRUCT_RT_MSGHDR
-    if (!no_reconfig)
-        setup_routing_socket(&setup_data);
-#endif
+
     /*
      * To do: Use RFC 2292 interface (or follow-on) and IPV6_PKTINFO,
      * so we might need only one UDP socket; fall back to binding
@@ -1127,8 +1304,8 @@ loop_setup_network(void *handle, const char *prog, int no_reconfig)
     }
     setup_tcp_listener_ports(&setup_data);
     setup_rpc_listener_ports(&setup_data);
-    krb5_klog_syslog (LOG_INFO, _("set up %d sockets"), (int)n_sockets);
-    if (n_sockets == 0) {
+    krb5_klog_syslog (LOG_INFO, _("set up %d sockets"), (int) events.n);
+    if (events.n == 0) {
         com_err(prog, 0, _("no sockets set up?"));
         exit (1);
     }
@@ -1359,8 +1536,7 @@ send_to_from(int s, void *buf, size_t len, int flags,
 }
 
 static void
-process_packet(void *handle, struct connection *conn, const char *prog,
-               int selflags)
+process_packet(verto_ctx *ctx, verto_ev *ev)
 {
     int cc;
     socklen_t saddr_len, daddr_len;
@@ -1371,8 +1547,13 @@ process_packet(void *handle, struct connection *conn, const char *prog,
     krb5_data request;
     krb5_data *response;
     char pktbuf[MAX_DGRAM_SIZE];
-    int port_fd = conn->fd;
+    int port_fd;
     union aux_addressing_info auxaddr;
+    struct connection *conn;
+
+    port_fd = verto_get_fd(ev);
+    conn = verto_get_private(ev);
+    assert(port_fd >= 0);
 
     response = NULL;
     saddr_len = sizeof(saddr);
@@ -1391,7 +1572,7 @@ process_packet(void *handle, struct connection *conn, const char *prog,
              */
             && errno != ECONNREFUSED
         )
-            com_err(prog, errno, _("while receiving from network"));
+            com_err(conn->prog, errno, _("while receiving from network"));
         return;
     }
     if (!cc)
@@ -1403,7 +1584,7 @@ process_packet(void *handle, struct connection *conn, const char *prog,
         if (getnameinfo(ss2sa(&daddr), daddr_len, addrbuf, sizeof(addrbuf),
                         0, 0, NI_NUMERICHOST))
             strlcpy(addrbuf, "?", sizeof(addrbuf));
-        com_err(prog, 0, "pktinfo says local addr is %s", addrbuf);
+        com_err(conn->prog, 0, _("pktinfo says local addr is %s"), addrbuf);
     }
 #endif
 
@@ -1424,9 +1605,10 @@ process_packet(void *handle, struct connection *conn, const char *prog,
     faddr.address = &addr;
     init_addr(&faddr, ss2sa(&saddr));
     /* This address is in net order. */
-    retval = dispatch(handle, ss2sa(&daddr), &faddr, &request, &response, 0);
+    retval = dispatch(conn->handle, ss2sa(&daddr),
+                      &faddr, &request, &response, 0);
     if (retval) {
-        com_err(prog, retval, _("while dispatching (udp)"));
+        com_err(conn->prog, retval, _("while dispatching (udp)"));
         return;
     }
     if (response == NULL)
@@ -1441,7 +1623,7 @@ process_packet(void *handle, struct connection *conn, const char *prog,
         char saddrbuf[NI_MAXHOST], sportbuf[NI_MAXSERV];
         char daddrbuf[NI_MAXHOST];
         int e = errno;
-        krb5_free_data(get_context(handle), response);
+        krb5_free_data(get_context(conn->handle), response);
         if (getnameinfo((struct sockaddr *)&daddr, daddr_len,
                         daddrbuf, sizeof(daddrbuf), 0, 0,
                         NI_NUMERICHOST) != 0) {
@@ -1453,69 +1635,72 @@ process_packet(void *handle, struct connection *conn, const char *prog,
             strlcpy(saddrbuf, "?", sizeof(saddrbuf));
             strlcpy(sportbuf, "?", sizeof(sportbuf));
         }
-        com_err(prog, e, _("while sending reply to %s/%s from %s"),
+        com_err(conn->prog, e, _("while sending reply to %s/%s from %s"),
                 saddrbuf, sportbuf, daddrbuf);
         return;
     }
     if ((size_t)cc != response->length) {
-        com_err(prog, 0, _("short reply write %d vs %d\n"),
+        com_err(conn->prog, 0, _("short reply write %d vs %d\n"),
                 response->length, cc);
     }
-    krb5_free_data(get_context(handle), response);
+    krb5_free_data(get_context(conn->handle), response);
     return;
 }
 
-static int tcp_or_rpc_data_counter;
-static int max_tcp_or_rpc_data_connections = 45;
-
-static void kill_tcp_or_rpc_connection(void *, struct connection *,
-                                       int isForcedClose);
-
 static int
-kill_lru_tcp_or_rpc_connection(void *handle, struct connection *newconn)
+kill_lru_tcp_or_rpc_connection(void *handle, verto_ev *newev)
 {
-    struct connection *oldest_tcp = NULL;
-    struct connection *c;
+    struct connection *c, *oldest_c = NULL;
+    verto_ev *ev, *oldest_ev = NULL;
     int i, fd = -1;
 
     krb5_klog_syslog(LOG_INFO, _("too many connections"));
 
-    FOREACH_ELT (connections, i, c) {
-        if (c->type != CONN_TCP && c->type != CONN_RPC)
+    FOREACH_ELT (events, i, ev) {
+        if (ev == newev)
+            continue;
+
+        c = verto_get_private(ev);
+        if (!c)
             continue;
-        if (c == newconn)
+        if (c->type != CONN_TCP && c->type != CONN_RPC)
             continue;
 #if 0
-        krb5_klog_syslog(LOG_INFO, "fd %d started at %ld", c->fd,
+        krb5_klog_syslog(LOG_INFO, "fd %d started at %ld",
+                         verto_get_fd(oldest_ev),
                          c->u.tcp.start_time);
 #endif
-        if (oldest_tcp == NULL
-            || oldest_tcp->u.tcp.start_time > c->u.tcp.start_time)
-            oldest_tcp = c;
+        if (oldest_c == NULL
+            || oldest_c->u.tcp.start_time > c->u.tcp.start_time) {
+            oldest_ev = ev;
+            oldest_c = c;
+        }
     }
-    if (oldest_tcp != NULL) {
+    if (oldest_c != NULL) {
         krb5_klog_syslog(LOG_INFO, _("dropping %s fd %d from %s"),
                          c->type == CONN_RPC ? "rpc" : "tcp",
-                         oldest_tcp->fd, oldest_tcp->u.tcp.addrbuf);
-        fd = oldest_tcp->fd;
-        kill_tcp_or_rpc_connection(handle, oldest_tcp, 1);
+                         verto_get_fd(oldest_ev), oldest_c->u.tcp.addrbuf);
+        if (oldest_c->type == CONN_RPC)
+            oldest_c->u.rpc.closed = 1;
+        verto_del(oldest_ev);
     }
     return fd;
 }
 
 static void
-accept_tcp_connection(void *handle, struct connection *conn, const char *prog,
-                      int selflags)
+accept_tcp_connection(verto_ctx *ctx, verto_ev *ev)
 {
     int s;
     struct sockaddr_storage addr_s;
     struct sockaddr *addr = (struct sockaddr *)&addr_s;
     socklen_t addrlen = sizeof(addr_s);
     struct socksetup sockdata;
-    struct connection *newconn;
+    struct connection *newconn, *conn;
     char tmpbuf[10];
+    verto_ev *newev;
 
-    s = accept(conn->fd, addr, &addrlen);
+    conn = verto_get_private(ev);
+    s = accept(verto_get_fd(ev), addr, &addrlen);
     if (s < 0)
         return;
     set_cloexec_fd(s);
@@ -1527,12 +1712,17 @@ accept_tcp_connection(void *handle, struct connection *conn, const char *prog,
 #endif
     setnbio(s), setnolinger(s), setkeepalive(s);
 
-    sockdata.prog = prog;
+    sockdata.ctx = ctx;
+    sockdata.handle = conn->handle;
+    sockdata.prog = conn->prog;
     sockdata.retval = 0;
 
-    newconn = add_tcp_data_fd(&sockdata, s);
-    if (newconn == NULL)
+    newev = add_tcp_read_fd(&sockdata, s);
+    if (newev == NULL) {
+        close(s);
         return;
+    }
+    newconn = verto_get_private(newev);
 
     if (getnameinfo((struct sockaddr *)&addr_s, addrlen,
                     newconn->u.tcp.addrbuf, sizeof(newconn->u.tcp.addrbuf),
@@ -1561,15 +1751,13 @@ accept_tcp_connection(void *handle, struct connection *conn, const char *prog,
     newconn->u.tcp.start_time = time(0);
 
     if (++tcp_or_rpc_data_counter > max_tcp_or_rpc_data_connections)
-        kill_lru_tcp_or_rpc_connection(handle, newconn);
+        kill_lru_tcp_or_rpc_connection(conn->handle, newev);
 
     if (newconn->u.tcp.buffer == 0) {
-        com_err(prog, errno,
+        com_err(conn->prog, errno,
                 _("allocating buffer for new TCP session from %s"),
                 newconn->u.tcp.addrbuf);
-        delete_fd(newconn);
-        close(s);
-        tcp_or_rpc_data_counter--;
+        verto_del(newev);
         return;
     }
     newconn->u.tcp.offset = 0;
@@ -1577,92 +1765,126 @@ accept_tcp_connection(void *handle, struct connection *conn, const char *prog,
     init_addr(&newconn->u.tcp.faddr, ss2sa(&newconn->u.tcp.addr_s));
     SG_SET(&newconn->u.tcp.sgbuf[0], newconn->u.tcp.lenbuf, 4);
     SG_SET(&newconn->u.tcp.sgbuf[1], 0, 0);
-
-    FD_SET(s, &sstate.rfds);
-    if (sstate.max <= s)
-        sstate.max = s + 1;
 }
 
 static void
-kill_tcp_or_rpc_connection(void *handle, struct connection *conn,
-                           int isForcedClose)
+process_tcp_connection_read(verto_ctx *ctx, verto_ev *ev)
 {
-    assert(conn->type == CONN_TCP || conn->type == CONN_RPC);
-    assert(conn->fd != -1);
-
-    if (conn->u.tcp.response)
-        krb5_free_data(get_context(handle), conn->u.tcp.response);
-    if (conn->u.tcp.buffer)
-        free(conn->u.tcp.buffer);
-    FD_CLR(conn->fd, &sstate.rfds);
-    FD_CLR(conn->fd, &sstate.wfds);
-    if (sstate.max == conn->fd + 1)
-        while (sstate.max > 0
-               && ! FD_ISSET(sstate.max-1, &sstate.rfds)
-               && ! FD_ISSET(sstate.max-1, &sstate.wfds)
-               /* && ! FD_ISSET(sstate.max-1, &sstate.xfds) */
-        )
-            sstate.max--;
-
-    /* In the non-forced case, the RPC runtime will close the descriptor for
-     * us. */
-    if (conn->type == CONN_TCP || isForcedClose) {
-        close(conn->fd);
-    }
-
-    /* For RPC connections, call into RPC runtime to flush out any internal
-     * state. */
-    if (conn->type == CONN_RPC && isForcedClose) {
-        fd_set fds;
-
-        FD_ZERO(&fds);
-        FD_SET(conn->fd, &fds);
+    struct connection *conn;
+    ssize_t nread;
+    size_t len;
+    int sock;
 
-        svc_getreqset(&fds);
+    conn = verto_get_private(ev);
+    sock = verto_get_fd(ev);
 
-        if (FD_ISSET(conn->fd, &svc_fdset)) {
-            krb5_klog_syslog(LOG_ERR,
-                             _("descriptor %d closed but still in svc_fdset"),
-                             conn->fd);
+    /*
+     * Read message length and data into one big buffer, already allocated
+     * at connect time.  If we have a complete message, we stop reading, so
+     * we should only be here if there is no data in the buffer, or only an
+     * incomplete message.
+     */
+    if (conn->u.tcp.offset < 4) {
+        /* msglen has not been computed.  XXX Doing at least two reads
+         * here, letting the kernel worry about buffering. */
+        len = 4 - conn->u.tcp.offset;
+        nread = SOCKET_READ(sock,
+                            conn->u.tcp.buffer + conn->u.tcp.offset, len);
+        if (nread < 0) /* error */
+            goto kill_tcp_connection;
+        if (nread == 0) /* eof */
+            goto kill_tcp_connection;
+        conn->u.tcp.offset += nread;
+        if (conn->u.tcp.offset == 4) {
+            unsigned char *p = (unsigned char *)conn->u.tcp.buffer;
+            conn->u.tcp.msglen = load_32_be(p);
+            if (conn->u.tcp.msglen > conn->u.tcp.bufsiz - 4) {
+                krb5_error_code err;
+                /* Message too big. */
+                krb5_klog_syslog(LOG_ERR, _("TCP client %s wants %lu bytes, "
+                                 "cap is %lu"), conn->u.tcp.addrbuf,
+                                 (unsigned long) conn->u.tcp.msglen,
+                                 (unsigned long) conn->u.tcp.bufsiz - 4);
+                /* XXX Should return an error.  */
+                err = make_toolong_error (conn->handle,
+                                          &conn->u.tcp.response);
+                if (err) {
+                    krb5_klog_syslog(LOG_ERR, _("error constructing "
+                                     "KRB_ERR_FIELD_TOOLONG error! %s"),
+                                     error_message(err));
+                    goto kill_tcp_connection;
+                }
+                goto have_response;
+            }
         }
+    } else {
+        /* msglen known. */
+        krb5_data request;
+        krb5_error_code err;
+        struct sockaddr_storage local_saddr;
+        socklen_t local_saddrlen = sizeof(local_saddr);
+        struct sockaddr *local_saddrp = NULL;
+
+        len = conn->u.tcp.msglen - (conn->u.tcp.offset - 4);
+        nread = SOCKET_READ(sock,
+                            conn->u.tcp.buffer + conn->u.tcp.offset, len);
+        if (nread < 0) /* error */
+            goto kill_tcp_connection;
+        if (nread == 0) /* eof */
+            goto kill_tcp_connection;
+        conn->u.tcp.offset += nread;
+        if (conn->u.tcp.offset < conn->u.tcp.msglen + 4)
+            return;
+        /* Have a complete message, and exactly one message. */
+        request.length = conn->u.tcp.msglen;
+        request.data = conn->u.tcp.buffer + 4;
+
+        if (getsockname(sock, ss2sa(&local_saddr),
+                        &local_saddrlen) == 0)
+            local_saddrp = ss2sa(&local_saddr);
+
+        err = dispatch(conn->handle, local_saddrp, &conn->u.tcp.faddr,
+                       &request, &conn->u.tcp.response, 1);
+        if (err) {
+            com_err(conn->prog, err, _("while dispatching (tcp)"));
+            goto kill_tcp_connection;
+        }
+        if (conn->u.tcp.response == NULL)
+            goto kill_tcp_connection;
+    have_response:
+        /* Queue outgoing response. */
+        store_32_be(conn->u.tcp.response->length, conn->u.tcp.lenbuf);
+        SG_SET(&conn->u.tcp.sgbuf[1], conn->u.tcp.response->data,
+               conn->u.tcp.response->length);
+        conn->u.tcp.sgp = conn->u.tcp.sgbuf;
+        conn->u.tcp.sgnum = 2;
+
+        if (convert_event(ctx, ev,
+                          VERTO_EV_FLAG_IO_WRITE | VERTO_EV_FLAG_PERSIST,
+                          process_tcp_connection_write))
+            return;
     }
 
-    conn->fd = -1;
-    delete_fd(conn);
-    tcp_or_rpc_data_counter--;
-}
+    return;
 
-static void
-queue_tcp_outgoing_response(struct connection *conn)
-{
-    store_32_be(conn->u.tcp.response->length, conn->u.tcp.lenbuf);
-    SG_SET(&conn->u.tcp.sgbuf[1], conn->u.tcp.response->data,
-           conn->u.tcp.response->length);
-    conn->u.tcp.sgp = conn->u.tcp.sgbuf;
-    conn->u.tcp.sgnum = 2;
-    FD_SET(conn->fd, &sstate.wfds);
+kill_tcp_connection:
+    verto_del(ev);
 }
 
 static void
-process_tcp_connection(void *handle, struct connection *conn, const char *prog,
-                       int selflags)
+process_tcp_connection_write(verto_ctx *ctx, verto_ev *ev)
 {
-    int isForcedClose = 1; /* not used now, but for completeness */
+    struct connection *conn;
+    SOCKET_WRITEV_TEMP tmp;
+    ssize_t nwrote;
+    int sock;
 
-    if (selflags & SSF_WRITE) {
-        ssize_t nwrote;
-        SOCKET_WRITEV_TEMP tmp;
+    conn = verto_get_private(ev);
+    sock = verto_get_fd(ev);
 
-        nwrote = SOCKET_WRITEV(conn->fd, conn->u.tcp.sgp, conn->u.tcp.sgnum,
-                               tmp);
-        if (nwrote < 0) {
-            goto kill_tcp_connection;
-        }
-        if (nwrote == 0) {
-            /* eof */
-            isForcedClose = 0;
-            goto kill_tcp_connection;
-        }
+    nwrote = SOCKET_WRITEV(sock, conn->u.tcp.sgp,
+                           conn->u.tcp.sgnum, tmp);
+    if (nwrote > 0) { /* non-error and non-eof */
         while (nwrote) {
             sg_buf *sgp = conn->u.tcp.sgp;
             if ((size_t)nwrote < SG_LEN(sgp)) {
@@ -1676,354 +1898,133 @@ process_tcp_connection(void *handle, struct connection *conn, const char *prog,
                     abort();
             }
         }
-        if (conn->u.tcp.sgnum == 0) {
-            /*
-             * Finished sending.  We should go back to reading, though if we
-             * sent a FIELD_TOOLONG error in reply to a length with the high
-             * bit set, RFC 4120 says we have to close the TCP stream.
-             */
-            isForcedClose = 0;
-            goto kill_tcp_connection;
-        }
-    } else if (selflags & SSF_READ) {
-        /*
-         * Read message length and data into one big buffer, already allocated
-         * at connect time.  If we have a complete message, we stop reading, so
-         * we should only be here if there is no data in the buffer, or only an
-         * incomplete message.
-         */
-        size_t len;
-        ssize_t nread;
-        if (conn->u.tcp.offset < 4) {
-            /* msglen has not been computed.  XXX Doing at least two reads
-             * here, letting the kernel worry about buffering. */
-            len = 4 - conn->u.tcp.offset;
-            nread = SOCKET_READ(conn->fd,
-                                conn->u.tcp.buffer + conn->u.tcp.offset, len);
-            if (nread < 0)
-                /* error */
-                goto kill_tcp_connection;
-            if (nread == 0)
-                /* eof */
-                goto kill_tcp_connection;
-            conn->u.tcp.offset += nread;
-            if (conn->u.tcp.offset == 4) {
-                unsigned char *p = (unsigned char *)conn->u.tcp.buffer;
-                conn->u.tcp.msglen = load_32_be(p);
-                if (conn->u.tcp.msglen > conn->u.tcp.bufsiz - 4) {
-                    krb5_error_code err;
-                    /* Message too big. */
-                    krb5_klog_syslog(LOG_ERR,
-                                     _("TCP client %s wants %lu bytes, "
-                                       "cap is %lu"), conn->u.tcp.addrbuf,
-                                     (unsigned long) conn->u.tcp.msglen,
-                                     (unsigned long) conn->u.tcp.bufsiz - 4);
-                    /* XXX Should return an error.  */
-                    err = make_toolong_error (handle, &conn->u.tcp.response);
-                    if (err) {
-                        krb5_klog_syslog(LOG_ERR,
-                                         _("error constructing "
-                                           "KRB_ERR_FIELD_TOOLONG error! %s"),
-                                         error_message(err));
-                        goto kill_tcp_connection;
-                    }
-                    goto have_response;
-                }
-            }
-        } else {
-            /* msglen known. */
-            krb5_data request;
-            krb5_error_code err;
-            struct sockaddr_storage local_saddr;
-            socklen_t local_saddrlen = sizeof(local_saddr);
-            struct sockaddr *local_saddrp = NULL;
-
-            len = conn->u.tcp.msglen - (conn->u.tcp.offset - 4);
-            nread = SOCKET_READ(conn->fd,
-                                conn->u.tcp.buffer + conn->u.tcp.offset, len);
-            if (nread < 0)      /* error */
-                goto kill_tcp_connection;
-            if (nread == 0)     /* eof */
-                goto kill_tcp_connection;
-            conn->u.tcp.offset += nread;
-            if (conn->u.tcp.offset < conn->u.tcp.msglen + 4)
-                return;
-            /* Have a complete message, and exactly one message. */
-            request.length = conn->u.tcp.msglen;
-            request.data = conn->u.tcp.buffer + 4;
-
-            if (getsockname(conn->fd, ss2sa(&local_saddr),
-                            &local_saddrlen) == 0)
-                local_saddrp = ss2sa(&local_saddr);
-
-            err = dispatch(handle, local_saddrp, &conn->u.tcp.faddr,
-                           &request, &conn->u.tcp.response, 1);
-            if (err) {
-                com_err(prog, err, _("while dispatching (tcp)"));
-                goto kill_tcp_connection;
-            }
-            if (conn->u.tcp.response == NULL)
-                goto kill_tcp_connection;
-        have_response:
-            queue_tcp_outgoing_response(conn);
-            FD_CLR(conn->fd, &sstate.rfds);
-        }
-    } else
-        abort();
 
-    return;
+        /* If we still have more data to send, just return so that
+         * the main loop can call this function again when the socket
+         * is ready for more writing. */
+        if (conn->u.tcp.sgnum > 0)
+            return;
+    }
 
-kill_tcp_connection:
-    kill_tcp_or_rpc_connection(handle, conn, isForcedClose);
+    /* Finished sending.  We should go back to reading, though if we
+     * sent a FIELD_TOOLONG error in reply to a length with the high
+     * bit set, RFC 4120 says we have to close the TCP stream. */
+    verto_del(ev);
 }
 
-static void
-service_conn(void *handle, struct connection *conn, const char *prog,
-             int selflags)
+void
+loop_free(verto_ctx *ctx)
 {
-    conn->service(handle, conn, prog, selflags);
+    verto_free(ctx);
+    FREE_SET_DATA(events);
+    FREE_SET_DATA(udp_port_data);
+    FREE_SET_DATA(tcp_port_data);
+    FREE_SET_DATA(rpc_svc_data);
 }
 
 static int
-getcurtime(struct timeval *tvp)
-{
-#ifdef _WIN32
-    struct _timeb tb;
-    _ftime(&tb);
-    tvp->tv_sec = tb.time;
-    tvp->tv_usec = tb.millitm * 1000;
-    return 0;
-#else
-    return gettimeofday(tvp, 0) ? errno : 0;
-#endif
-}
-
-krb5_error_code
-loop_listen_and_process(void *handle, const char *prog, void (*reset)(void))
-{
-    int nfound;
-    /* This struct contains 3 fd_set objects; on some platforms, they
-       can be rather large.  Making this static avoids putting all
-       that junk on the stack.  */
-    static struct select_state sout;
-    size_t i;
-    int sret, netchanged = 0;
-    krb5_error_code err;
-
-    if (conns == (struct connection **) NULL)
-        return KDC5_NONET;
-
-    while (!signal_requests_exit) {
-        if (signal_requests_reset) {
-            krb5_klog_reopen(get_context(handle));
-            reset();
-            signal_requests_reset = 0;
-        }
-
-        if (network_reconfiguration_needed) {
-            /* No point in re-logging what we've just logged. */
-            if (netchanged == 0) {
-                krb5_klog_syslog(LOG_INFO,
-                                 _("network reconfiguration needed"));
-            }
-            /* It might be tidier to add a timer-callback interface to the
-             * control loop, but for this one use, it's not a big deal. */
-            err = getcurtime(&sstate.end_time);
-            if (err) {
-                com_err(prog, err, _("while getting the time"));
-                continue;
-            }
-            sstate.end_time.tv_sec += 3;
-            netchanged = 1;
-        } else
-            sstate.end_time.tv_sec = sstate.end_time.tv_usec = 0;
-
-        err = krb5int_cm_call_select(&sstate, &sout, &sret);
-        if (err) {
-            if (err != EINTR)
-                com_err(prog, err, _("while selecting for network input(1)"));
-            continue;
-        }
-        if (sret == 0 && netchanged) {
-            network_reconfiguration_needed = 0;
-            loop_closedown_network_sockets();
-            err = loop_setup_network(handle, prog, 0);
-            if (err) {
-                com_err(prog, err, _("while reinitializing network"));
-                return err;
-            }
-            netchanged = 0;
-        }
-        if (sret == -1) {
-            if (errno != EINTR) {
-                com_err(prog, errno,
-                        _("while selecting for network input(2)"));
-            }
-            continue;
-        }
-        nfound = sret;
-        for (i=0; i<n_sockets && nfound > 0; i++) {
-            int sflags = 0;
-            if (conns[i]->fd < 0)
-                abort();
-            if (FD_ISSET(conns[i]->fd, &sout.rfds))
-                sflags |= SSF_READ, nfound--;
-            if (FD_ISSET(conns[i]->fd, &sout.wfds))
-                sflags |= SSF_WRITE, nfound--;
-            if (sflags)
-                service_conn(handle, conns[i], prog, sflags);
-        }
-    }
-    krb5_klog_syslog(LOG_INFO, _("shutdown signal received"));
-    return 0;
-}
-
-static void
-loop_closedown_network_sockets()
+have_event_for_fd(int fd)
 {
+    verto_ev *ev;
     int i;
-    struct connection *conn;
-
-    if (conns == (struct connection **) NULL)
-        return;
-
-    FOREACH_ELT (connections, i, conn) {
-        if (conn->fd >= 0) {
-            krb5_klog_syslog(LOG_INFO, _("closing down fd %d"), conn->fd);
-            (void) close(conn->fd);
-            if (conn->type == CONN_RPC) {
-                fd_set fds;
-
-                FD_ZERO(&fds);
-                FD_SET(conn->fd, &fds);
 
-                svc_getreqset(&fds);
-            }
-        }
-        if (conn->type == CONN_RPC_LISTENER) {
-            if (conn->u.rpc.transp != NULL)
-                svc_destroy(conn->u.rpc.transp);
-        }
-        DEL (connections, i);
-        /*
-         * There may also be per-connection data in the tcp structure
-         * (tcp.buffer, tcp.response) that we're not freeing here.  That should
-         * only happen if we quit with a connection in progress.
-         */
-        free(conn);
+    FOREACH_ELT(events, i, ev) {
+        if (verto_get_fd(ev) == fd)
+            return 1;
     }
-}
 
-void
-loop_closedown_network()
-{
-    loop_closedown_network_sockets();
-    FREE_SET_DATA(connections);
-    FREE_SET_DATA(udp_port_data);
-    FREE_SET_DATA(tcp_port_data);
-    FREE_SET_DATA(rpc_svc_data);
+    return 0;
 }
 
 static void
-accept_rpc_connection(void *handle, struct connection *conn, const char *prog,
-                      int selflags)
+accept_rpc_connection(verto_ctx *ctx, verto_ev *ev)
 {
     struct socksetup sockdata;
+    struct connection *conn;
     fd_set fds;
     register int s;
 
-    assert(selflags & SSF_READ);
-
-    if ((selflags & SSF_READ) == 0)
-        return;
+    conn = verto_get_private(ev);
 
-    sockdata.prog = prog;
+    sockdata.ctx = ctx;
+    sockdata.handle = conn->handle;
+    sockdata.prog = conn->prog;
     sockdata.retval = 0;
 
     /* Service the woken RPC listener descriptor. */
     FD_ZERO(&fds);
-    FD_SET(conn->fd, &fds);
-
+    FD_SET(verto_get_fd(ev), &fds);
     svc_getreqset(&fds);
 
     /* Scan svc_fdset for any new connections. */
     for (s = 0; s < FD_SETSIZE; s++) {
-        /* sstate.rfds |= svc_fdset & ~(rpc_listenfds | sstate.rfds) */
-        if (FD_ISSET(s, &svc_fdset) && !FD_ISSET(s, &rpc_listenfds)
-            && !FD_ISSET(s, &sstate.rfds)) {
-            struct connection *newconn;
-            struct sockaddr_storage addr_s;
-            struct sockaddr *addr = (struct sockaddr *)&addr_s;
-            socklen_t addrlen = sizeof(addr_s);
-            char tmpbuf[10];
+        struct sockaddr_storage addr_s;
+        struct sockaddr *addr = (struct sockaddr *) &addr_s;
+        socklen_t addrlen = sizeof(addr_s);
+        struct connection *newconn;
+        char tmpbuf[10];
+        verto_ev *newev;
+
+        /* If we already have this fd, continue. */
+        if (!FD_ISSET(s, &svc_fdset) || have_event_for_fd(s))
+            continue;
 
-            newconn = add_rpc_data_fd(&sockdata, s);
-            if (newconn == NULL)
-                continue;
+        newev = add_rpc_data_fd(&sockdata, s);
+        if (newev == NULL)
+            continue;
+        newconn = verto_get_private(newev);
 
-            set_cloexec_fd(s);
+        set_cloexec_fd(s);
 #if 0
-            setnbio(s), setnolinger(s), setkeepalive(s);
+        setnbio(s), setnolinger(s), setkeepalive(s);
 #endif
 
-            if (getpeername(s, addr, &addrlen) ||
-                getnameinfo(addr, addrlen,
-                            newconn->u.tcp.addrbuf,
-                            sizeof(newconn->u.tcp.addrbuf),
-                            tmpbuf, sizeof(tmpbuf),
-                            NI_NUMERICHOST | NI_NUMERICSERV)) {
-                strlcpy(newconn->u.tcp.addrbuf, "???",
-                        sizeof(newconn->u.tcp.addrbuf));
-            } else {
-                char *p, *end;
-                p = newconn->u.tcp.addrbuf;
-                end = p + sizeof(newconn->u.tcp.addrbuf);
-                p += strlen(p);
-                if ((size_t)(end - p) > 2 + strlen(tmpbuf)) {
-                    *p++ = '.';
-                    strlcpy(p, tmpbuf, end - p);
-                }
+        if (getpeername(s, addr, &addrlen) ||
+            getnameinfo(addr, addrlen,
+                        newconn->u.tcp.addrbuf,
+                        sizeof(newconn->u.tcp.addrbuf),
+                        tmpbuf, sizeof(tmpbuf),
+                        NI_NUMERICHOST | NI_NUMERICSERV)) {
+            strlcpy(newconn->u.tcp.addrbuf, "???",
+                    sizeof(newconn->u.tcp.addrbuf));
+        } else {
+            char *p, *end;
+            p = newconn->u.tcp.addrbuf;
+            end = p + sizeof(newconn->u.tcp.addrbuf);
+            p += strlen(p);
+            if ((size_t)(end - p) > 2 + strlen(tmpbuf)) {
+                *p++ = '.';
+                strlcpy(p, tmpbuf, end - p);
             }
+        }
 #if 0
-            krb5_klog_syslog(LOG_INFO, "accepted RPC connection on socket %d "
-                             "from %s", s, newconn->u.tcp.addrbuf);
+        krb5_klog_syslog(LOG_INFO, _("accepted RPC connection on socket %d "
+                         "from %s"), s, newconn->u.tcp.addrbuf);
 #endif
 
-            newconn->u.tcp.addr_s = addr_s;
-            newconn->u.tcp.addrlen = addrlen;
-            newconn->u.tcp.start_time = time(0);
+        newconn->u.tcp.addr_s = addr_s;
+        newconn->u.tcp.addrlen = addrlen;
+        newconn->u.tcp.start_time = time(0);
 
-            if (++tcp_or_rpc_data_counter > max_tcp_or_rpc_data_connections)
-                kill_lru_tcp_or_rpc_connection(handle, newconn);
+        if (++tcp_or_rpc_data_counter > max_tcp_or_rpc_data_connections)
+            kill_lru_tcp_or_rpc_connection(newconn->handle, newev);
 
-            newconn->u.tcp.faddr.address = &newconn->u.tcp.kaddr;
-            init_addr(&newconn->u.tcp.faddr, ss2sa(&newconn->u.tcp.addr_s));
-
-            FD_SET(s, &sstate.rfds);
-            if (sstate.max <= s)
-                sstate.max = s + 1;
-        }
+        newconn->u.tcp.faddr.address = &newconn->u.tcp.kaddr;
+        init_addr(&newconn->u.tcp.faddr, ss2sa(&newconn->u.tcp.addr_s));
     }
 }
 
 static void
-process_rpc_connection(void *handle, struct connection *conn, const char *prog,
-                       int selflags)
+process_rpc_connection(verto_ctx *ctx, verto_ev *ev)
 {
     fd_set fds;
 
-    assert(selflags & SSF_READ);
-
-    if ((selflags & SSF_READ) == 0)
-        return;
-
     FD_ZERO(&fds);
-    FD_SET(conn->fd, &fds);
-
+    FD_SET(verto_get_fd(ev), &fds);
     svc_getreqset(&fds);
 
-    if (!FD_ISSET(conn->fd, &svc_fdset))
-        kill_tcp_or_rpc_connection(handle, conn, 0);
+    if (!FD_ISSET(verto_get_fd(ev), &svc_fdset))
+        verto_del(ev);
 }
 
 #endif /* INET */