yaid.c: Delay destruction of selbuf objects.
authorMark Wooding <mdw@distorted.org.uk>
Mon, 22 Dec 2014 20:32:58 +0000 (20:32 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Sun, 18 Jan 2015 16:34:54 +0000 (16:34 +0000)
It's not safe to destroy them from inside the per-line callback
function, so make chains of defunct objects and arrange to destroy them
properly in the main loop.

yaid.c

diff --git a/yaid.c b/yaid.c
index cc3bb47..da015e1 100644 (file)
--- a/yaid.c
+++ b/yaid.c
@@ -61,6 +61,7 @@ struct client {
   struct listen *l;                    /* Back to the listener (and ops) */
   struct writebuf wb;                  /* Write buffer for our reply */
   struct proxy *px;                    /* Proxy if conn goes via NAT */
+  struct client *next;                 /* Next in a chain of clients */
 };
 
 /* A proxy connection. */
@@ -71,6 +72,7 @@ struct proxy {
   selbuf b;                            /* Accumulate the response line */
   struct writebuf wb;                  /* Write buffer for query */
   char nat[ADDRLEN];                   /* Server address, as text */
+  struct proxy *next;                  /* Next in a chain of proxies */
 };
 
 /*----- Static variables --------------------------------------------------*/
@@ -88,6 +90,9 @@ static unsigned char tokenbuf[4096];  /* Random-ish data for tokens */
 static size_t tokenptr = sizeof(tokenbuf); /* Current read position */
 static int randfd;                     /* File descriptor for random data */
 
+static struct client *dead_clients = 0;        /* List of defunct clients */
+static struct proxy *dead_proxies = 0; /* List of defunct proxies */
+
 static unsigned flags = 0;             /* Various interesting flags */
 #define F_SYSLOG 1u                    /*   Use syslog for logging */
 #define F_RUNNING 2u                   /*   Running properly now */
@@ -362,12 +367,28 @@ static void cancel_proxy(struct proxy *px)
     conn_kill(&px->cn);
   else {
     close(px->fd);
-    selbuf_destroy(&px->b);
-    free_writebuf(&px->wb);
+    selbuf_disable(&px->b);
   }
   px->c->px = 0;
   selbuf_enable(&px->c->b);
-  xfree(px);
+  px->next = dead_proxies;
+  dead_proxies = px;
+}
+
+/* Delayed destruction of unsafe parts of proxies. */
+static void reap_dead_proxies(void)
+{
+  struct proxy *px, *pp;
+
+  for (px = dead_proxies; px; px = pp) {
+    pp = px->next;
+    if (px->fd != -1) {
+      selbuf_destroy(&px->b);
+      free_writebuf(&px->wb);
+    }
+    xfree(px);
+  }
+  dead_proxies = 0;
 }
 
 /* Notification that a line (presumably a reply) has been received from the
@@ -543,12 +564,27 @@ err_0:
 /* Disconnect a client, freeing up any associated resources. */
 static void disconnect_client(struct client *c)
 {
+  selbuf_disable(&c->b);
   close(c->fd);
-  selbuf_destroy(&c->b);
   sel_rmtimer(&c->t);
   free_writebuf(&c->wb);
   if (c->px) cancel_proxy(c->px);
-  xfree(c);
+  c->next = dead_clients;
+  dead_clients = c;
+}
+
+/* Throw away dead clients now that we've reached a safe point in the
+ * program.
+ */
+static void reap_dead_clients(void)
+{
+  struct client *c, *cc;
+  for (c = dead_clients; c; c = cc) {
+    cc = c->next;
+    selbuf_destroy(&c->b);
+    xfree(c);
+  }
+  dead_clients = 0;
 }
 
 /* Time out a client because it's been idle for too long. */
@@ -1108,6 +1144,8 @@ int main(int argc, char *argv[])
   for (;;) {
     if (sel_select(&sel) && errno != EINTR)
       die(1, "select failed: %s", strerror(errno));
+    reap_dead_proxies();
+    reap_dead_clients();
   }
 
   /* This just keeps the compiler happy. */