/*----- Static variables --------------------------------------------------*/
static admin *admins;
+static admin *a_dead;
static sel_file sock;
static const char *sockname;
static unsigned flags = 0;
#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")
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@ --- *
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);
}
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@ --- *
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;
}
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);
bad_syntax:
a_fail(a, "bad-syntax", "%s", cmd, "[OPTIONS] PEER", cmd, A_END);
+fail:
return;
}
}
}
+static void acmd_setifname(admin *a, unsigned ac, char *av[])
+{
+ peer *p;
+
+ if ((p = a_findpeer(a, av[0])) != 0) {
+ a_notify("NEWIFNAME", "?PEER", p, "%s", p_ifname(p), "%s", av[1], A_END);
+ p_setifname(p, av[1]);
+ a_ok(a);
+ }
+}
+
static void acmd_getchal(admin *a, unsigned ac, char *av[])
{
buf b;
{
a_warn("SERVER", "quit", "admin-request", A_END);
a_ok(a);
- a_unlock(a);
a_quit();
}
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 },
{ "quit", 0, 0, 0, acmd_quit },
{ "reload", 0, 0, 0, acmd_reload },
{ "servinfo", 0, 0, 0, acmd_servinfo },
+ { "setifname", "PEER NEW-NAME", 2, 2, acmd_setifname },
{ "stats", "PEER", 1, 1, acmd_stats },
#ifndef NTRACE
{ "trace", "[OPTIONS]", 0, 1, acmd_trace },
/*----- 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);
+
+ /* --- 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);
- for (bg = a->bg; bg; bg = bbg) {
- bbg = bg->next;
- bg->cancel(bg);
- if (bg->tag) xfree(bg->tag);
- xfree(bg);
+ 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@ --- *
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); )
+ if (a->next) a->next->prev = a->prev;
+ if (a->prev) a->prev->next = a->next;
+ else admins = a->next;
- /* --- Free the output buffers --- */
-
- 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@ --- *
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;
}
}
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: ---