chiark / gitweb /
admin: Option parser macros.
[tripe] / server / admin.c
index b166b853dbdb3de411ad610993cc00f8ed1558f8..1660106c42b27e370b499f445409569f272b04c1 100644 (file)
@@ -62,6 +62,7 @@ static const trace_opt w_opts[] = {
 /*----- Static variables --------------------------------------------------*/
 
 static admin *admins;
+static admin *a_dead;
 static sel_file sock;
 static const char *sockname;
 static unsigned flags = 0;
@@ -75,8 +76,6 @@ static sig s_term, s_int, s_hup;
 #define T_PING SEC(5)
 
 static void a_destroy(admin */*a*/);
-static void a_lock(admin */*a*/);
-static void a_unlock(admin */*a*/);
 
 #define BOOL(x) ((x) ? "t" : "nil")
 
@@ -642,8 +641,7 @@ static void a_bgrelease(admin_bgop *bg)
   if (bg->prev) bg->prev->next = bg->next;
   else a->bg = bg->next;
   xfree(bg);
-  if (a->f & AF_CLOSE) a_destroy(a);
-  a_unlock(a);
+  if (!a->bg && (a->f & AF_CLOSE)) a_destroy(a);
 }
 
 /* --- @a_bgok@, @a_bginfo@, @a_bgfail@ --- *
@@ -703,7 +701,6 @@ static void a_bgadd(admin *a, admin_bgop *bg, const char *tag,
   bg->prev = 0;
   if (a->bg) a->bg->prev = bg;
   a->bg = bg;
-  a_lock(a);
   T( trace(T_ADMIN, "admin: add bgop %s", BGTAG(bg)); )
   if (tag) a_write(a, "DETACH", tag, A_END);
 }
@@ -865,6 +862,44 @@ fail:
   xfree(r);
 }
 
+/*----- Option parsing ----------------------------------------------------*/
+
+#define OPTIONS(argc, argv, guts) do {                                 \
+  char **o_av = argv;                                                  \
+  for (;; o_av++) {                                                    \
+    if (!*o_av)                                                                \
+      break;                                                           \
+    if (mystrieq(*o_av, "--")) {                                       \
+      o_av++;                                                          \
+      break;                                                           \
+    }                                                                  \
+    guts                                                               \
+    if (**o_av == '-')                                                 \
+      goto bad_syntax;                                                 \
+    break;                                                             \
+  }                                                                    \
+  argc -= o_av - argv;                                                 \
+  argv = o_av;                                                         \
+} while (0)
+
+#define OPT(name, guts) if (mystrieq(*o_av, name)) { guts continue; }
+
+#define OPTARG(name, arg, guts) OPT(name, {                            \
+  const char *arg;                                                     \
+  arg = *++o_av;                                                       \
+  if (!arg) goto bad_syntax;                                           \
+  guts                                                                 \
+})
+
+#define OPTTIME(name, arg, guts) OPTARG(name, o_arg, {                 \
+  long arg;                                                            \
+  if ((arg = a_parsetime(o_arg)) < 0) {                                        \
+    a_fail(a, "bad-time-spec", "%s", o_arg, A_END);                    \
+    goto fail;                                                         \
+  }                                                                    \
+  guts                                                                 \
+})
+
 /*----- Adding peers ------------------------------------------------------*/
 
 /* --- @a_doadd@ --- *
@@ -910,72 +945,58 @@ static void a_doadd(admin_resop *r, int rc)
 
 static void acmd_add(admin *a, unsigned ac, char *av[])
 {
-  unsigned i, j;
   const char *tag = 0;
-  admin_addop *add = 0;
+  admin_addop *add;
 
   /* --- Set stuff up --- */
 
   add = xmalloc(sizeof(*add));
-  add->peer.name = xstrdup(av[0]);
+  add->peer.name = 0;
   add->peer.t_ka = 0;
   add->peer.tops = tun_default;
 
-  /* --- Make sure someone's not got there already --- */
-
-  if (p_find(av[0])) {
-    a_fail(a, "peer-exists", "%s", av[0], A_END);
-    goto fail;
-  }
-
   /* --- Parse options --- */
 
-  i = 1;
-  for (;;) {
-    if (!av[i])
-      goto bad_syntax;
-    if (mystrieq(av[i], "-background")) {
-      if (!av[++i]) goto bad_syntax;
-      tag = av[i];
-    } else if (mystrieq(av[i], "-tunnel")) {
-      if (!av[++i]) goto bad_syntax;
-      for (j = 0;; j++) {
-       if (!tunnels[j]) {
-         a_fail(a, "unknown-tunnel", "%s", av[i], A_END);
+  OPTIONS(ac, av, {
+    OPTARG("-background", arg, { tag = arg; })
+    OPTARG("-tunnel", arg, {
+      unsigned i;
+      for (i = 0;; i++) {
+       if (!tunnels[i]) {
+         a_fail(a, "unknown-tunnel", "%s", arg, A_END);
          goto fail;
        }
-       if (mystrieq(av[i], tunnels[j]->name)) {
-         add->peer.tops = tunnels[j];
+       if (mystrieq(arg, tunnels[i]->name)) {
+         add->peer.tops = tunnels[i];
          break;
        }
       }
-    } else if (mystrieq(av[i], "-keepalive")) {
-      long t;
-      if (!av[++i]) goto bad_syntax;
-      if ((t = a_parsetime(av[i])) < 0) {
-       a_fail(a, "bad-time-spec", "%s", av[i], A_END);
-       goto fail;
-      }
-      add->peer.t_ka = t;
-    } else if (mystrieq(av[i], "--")) {
-      i++;
-      break;
-    } else
-      break;
-    i++;
+    })
+    OPTTIME("-keepalive", t, { add->peer.t_ka = t; })
+  });
+
+  /* --- Make sure someone's not got there already --- */
+
+  if (!*av)
+    goto bad_syntax;
+  if (p_find(*av)) {
+    a_fail(a, "peer-exists", "%s", *av, A_END);
+    goto fail;
   }
+  add->peer.name = xstrdup(*av++);
+  ac--;
 
   /* --- Crank up the resolver --- */
 
-  a_resolve(a, &add->r, tag, a_doadd, ac - i, av + i);
+  a_resolve(a, &add->r, tag, a_doadd, ac, av);
   return;
 
   /* --- Clearing up --- */
 
 bad_syntax:
-  a_fail(a, "bad-syntax", "add", "PEER [OPTIONS] ADDR ...", A_END);
+  a_fail(a, "bad-syntax", "add", "[OPTIONS] PEER ADDR ...", A_END);
 fail:
-  xfree(add->peer.name);
+  if (add->peer.name) xfree(add->peer.name);
   xfree(add);
   return;
 }
@@ -1052,34 +1073,16 @@ static void a_ping(admin *a, unsigned ac, char *av[],
                   const char *cmd, unsigned msg)
 {
   long t = T_PING;
-  int i;
   peer *p;
   admin_pingop *pg = 0;
   const char *tag = 0;
 
-  i = 0;
-  for (;;) {
-    if (!av[i])
-      goto bad_syntax;
-    if (mystrieq(av[i], "-background")) {
-      if (!av[++i]) goto bad_syntax;
-      tag = av[i];
-    } else if (mystrieq(av[i], "-timeout")) {
-      if (!av[++i]) goto bad_syntax;
-      if ((t = a_parsetime(av[i])) < 0) {
-       a_fail(a, "bad-time-spec", "%s", av[i], A_END);
-       return;
-      }
-    } else if (mystrieq(av[i], "--")) {
-      i++;
-      break;
-    } else
-      break;
-    i++;
-  }
-
-  if (!av[i]) goto bad_syntax;
-  if ((p = a_findpeer(a, av[i])) == 0)
+  OPTIONS(ac, av, {
+    OPTARG("-background", arg, { tag = arg; })
+    OPTTIME("-timeout", arg, { t = arg; })
+  });
+  if (!*av || av[1]) goto bad_syntax;
+  if ((p = a_findpeer(a, *av)) == 0)
     return;
   pg = xmalloc(sizeof(*pg));
   gettimeofday(&pg->pingtime, 0);
@@ -1094,6 +1097,7 @@ static void a_ping(admin *a, unsigned ac, char *av[],
     
 bad_syntax:
   a_fail(a, "bad-syntax", "%s", cmd, "[OPTIONS] PEER", cmd, A_END);
+fail:
   return;
 }
 
@@ -1379,7 +1383,6 @@ static void acmd_quit(admin *a, unsigned ac, char *av[])
 {
   a_warn("SERVER", "quit", "admin-request", A_END);
   a_ok(a);
-  a_unlock(a);
   a_quit();
 }
 
@@ -1409,7 +1412,7 @@ typedef struct acmd {
 static void acmd_help(admin */*a*/, unsigned /*ac*/, char */*av*/[]);
 
 static const acmd acmdtab[] = {
-  { "add",     "PEER [OPTIONS] ADDR ...", 2,   0xffff, acmd_add },
+  { "add",     "[OPTIONS] PEER ADDR ...", 2,   0xffff, acmd_add },
   { "addr",    "PEER",                 1,      1,      acmd_addr },
   { "checkchal", "CHAL",               1,      1,      acmd_checkchal },
   { "daemon",  0,                      0,      0,      acmd_daemon },
@@ -1454,70 +1457,55 @@ static void acmd_help(admin *a, unsigned ac, char *av[])
 
 /*----- Connection handling -----------------------------------------------*/
 
-/* --- @a_lock@ --- *
+/* --- @a_destroypending@ --- *
  *
- * Arguments:  @admin *a@ = pointer to an admin block
- *
- * Returns:    ---
- *
- * Use:                Locks an admin block so that it won't be destroyed
- *             immediately.
- */
-
-static void a_lock(admin *a) { a->ref++; }
-
-/* --- @a_dodestroy@ --- *
- *
- * Arguments:  @admin *a@ = pointer to an admin block
+ * Arguments:  ---
  *
  * Returns:    ---
  *
- * Use:                Actually does the legwork of destroying an admin block.
+ * Use:                Destroys pending admin connections at a safe time.
  */
 
-static void a_dodestroy(admin *a)
+static void a_destroypending(void)
 {
+  admin *a, *aa;
   admin_bgop *bg, *bbg;
 
-  T( trace(T_ADMIN, "admin: completing destruction of connection %u",
-          a->seq); )
+  /* --- Destroy connections marked as pending --- */
+
+  for (a = a_dead; a; a = aa) {
+    aa = a->next;
+    assert(a->f & AF_DEAD);
 
-  selbuf_destroy(&a->b);
-  for (bg = a->bg; bg; bg = bbg) {
-    bbg = bg->next;
-    bg->cancel(bg);
-    if (bg->tag) xfree(bg->tag);
-    xfree(bg);
+    /* --- Report what we're doing --- */
+
+    T( trace(T_ADMIN, "admin: completing destruction of connection %u",
+            a->seq); )
+
+    /* --- Abort any background jobs in progress --- */
+
+    for (bg = a->bg; bg; bg = bbg) {
+      bbg = bg->next;
+      bg->cancel(bg);
+      if (bg->tag) xfree(bg->tag);
+      xfree(bg);
+    }
+
+    /* --- Close file descriptors and selectory --- */
+
+    selbuf_destroy(&a->b);
+    if (a->b.reader.fd != a->w.fd) close(a->b.reader.fd);
+    close(a->w.fd);
+    if (a_stdin == a) a_stdin = 0;
+
+    /* --- Done --- */
+
+    DESTROY(a);
   }
-  if (a->b.reader.fd != a->w.fd) close(a->b.reader.fd);
-  close(a->w.fd);
-
-  if (a_stdin == a)
-    a_stdin = 0;
-  if (a->next)
-    a->next->prev = a->prev;
-  if (a->prev)
-    a->prev->next = a->next;
-  else
-    admins = a->next;
-  DESTROY(a);
-}
 
-/* --- @a_unlock@ --- *
- *
- * Arguments:  @admin *a@ = pointer to an admin block
- *
- * Returns:    ---
- *
- * Use:                Unlocks an admin block, allowing its destruction.  This is
- *             also the second half of @a_destroy@.
- */
+  /* --- All pending destruction completed --- */
 
-static void a_unlock(admin *a)
-{
-  assert(a->ref);
-  if (!--a->ref && (a->f & AF_DEAD))
-    a_dodestroy(a);
+  a_dead = 0;
 }
 
 /* --- @a_destroy@ --- *
@@ -1543,28 +1531,21 @@ static void freequeue(oqueue *q)
 
 static void a_destroy(admin *a)
 {
-  /* --- Don't multiply destroy admin blocks --- */
-
   if (a->f & AF_DEAD)
     return;
 
-  /* --- Make sure nobody expects it to work --- */
-
-  a->f |= AF_DEAD;
-  T( trace(T_ADMIN, "admin: destroying connection %u", a->seq); )
-
-  /* --- Free the output buffers --- */
+  if (a->next) a->next->prev = a->prev;
+  if (a->prev) a->prev->next = a->next;
+  else admins = a->next;
 
-  if (a->out.hd)
-    sel_rmfile(&a->w);
+  if (a->out.hd) sel_rmfile(&a->w);
   freequeue(&a->out);
 
-  /* --- If the block is locked, that's all we can manage --- */
+  a->f |= AF_DEAD;
+  a->next = a_dead;
+  a_dead = a;
 
-  if (!a->ref)
-    a_dodestroy(a);
-  T( else 
-     trace(T_ADMIN, "admin: deferring destruction..."); )
+  T( trace(T_ADMIN, "admin: killing connection %u", a->seq); )
 }
 
 /* --- @a_line@ --- *
@@ -1608,11 +1589,8 @@ static void a_line(char *p, size_t len, void *vp)
          a_fail(a, "bad-syntax", "%s", c->name, "", A_END);
        else 
          a_fail(a, "bad-syntax", "%s", c->name, "%s", c->help, A_END);
-      } else {
-       a_lock(a);
+      } else
        c->func(a, ac, av + 1);
-       a_unlock(a);
-      }
       return;
     }
   }
@@ -1678,6 +1656,19 @@ static void a_accept(int fd, unsigned mode, void *v)
   a_create(nfd, nfd, 0);
 }
 
+/* --- @a_preselect@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Informs the admin module that we're about to select again,
+ *             and that it should do cleanup things it has delayed until a
+ *             `safe' time.
+ */
+
+void a_preselect(void) { if (a_dead) a_destroypending(); }
+
 /* --- @a_daemon@ --- *
  *
  * Arguments:  ---