#define BGTAG(bg) \
(((admin_bgop *)(bg))->tag ? ((admin_bgop *)(bg))->tag : "<foreground>")
+/* --- @a_bgfind@ --- *
+ *
+ * Arguments: @admin *a@ = a client block
+ * @const char *tag@ = the requested tag
+ *
+ * Returns: The requested background job, or null.
+ */
+
+static admin_bgop *a_bgfind(admin *a, const char *tag)
+{
+ admin_bgop *bg;
+
+ for (bg = a->bg; bg; bg = bg->next) {
+ if (bg->tag && strcmp(tag, bg->tag) == 0)
+ return (bg);
+ }
+ return (0);
+}
+
/* --- @a_bgrelease@ --- *
*
* Arguments: @admin_bgop *bg@ = backgrounded operation
if (bg->prev) bg->prev->next = bg->next;
else a->bg = bg->next;
xfree(bg);
- if (a->f & AF_CLOSE) a_destroy(a);
+ if (!a->bg && (a->f & AF_CLOSE)) a_destroy(a);
}
/* --- @a_bgok@, @a_bginfo@, @a_bgfail@ --- *
* @const char *tag@ = background tag, or null for foreground
* @void (*cancel)(admin_bgop *)@ = cancel function
*
- * Returns: ---
+ * Returns: Zero for success, nonzero on failure.
*
* Use: Links a background job into the list.
*/
-static void a_bgadd(admin *a, admin_bgop *bg, const char *tag,
- void (*cancel)(admin_bgop *))
+static int a_bgadd(admin *a, admin_bgop *bg, const char *tag,
+ void (*cancel)(admin_bgop *))
{
- if (tag)
+ if (tag) {
+ if (a_bgfind(a, tag)) {
+ a_fail(a, "tag-exists", "%s", tag, A_END);
+ return (-1);
+ }
bg->tag = xstrdup(tag);
+ }
else {
bg->tag = 0;
selbuf_disable(&a->b);
a->bg = bg;
T( trace(T_ADMIN, "admin: add bgop %s", BGTAG(bg)); )
if (tag) a_write(a, "DETACH", tag, A_END);
+ return (0);
}
/*----- Name resolution operations ----------------------------------------*/
* answer straight away.
*/
- a_bgadd(a, &r->bg, tag, a_rescancel);
+ if (a_bgadd(a, &r->bg, tag, a_rescancel))
+ goto fail;
T( trace(T_ADMIN, "admin: %u, resop %s, hostname `%s'",
a->seq, BGTAG(r), r->addr); )
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);
- a_bgadd(a, &pg->bg, tag, a_pingcancel);
+ if (a_bgadd(a, &pg->bg, tag, a_pingcancel))
+ goto fail;
T( trace(T_ADMIN, "admin: ping op %s: %s to %s",
BGTAG(pg), cmd, p_name(p)); )
if (p_pingsend(p, &pg->ping, msg, t, a_pong, pg)) {
bad_syntax:
a_fail(a, "bad-syntax", "%s", cmd, "[OPTIONS] PEER", cmd, A_END);
+fail:
+ if (pg) xfree(pg);
return;
}
}
}
+static void acmd_jobs(admin *a, unsigned ac, char *av[])
+{
+ admin_bgop *bg;
+
+ for (bg = a->bg; bg; bg = bg->next) {
+ assert(bg->tag);
+ a_info(a, "%s", bg->tag, A_END);
+ }
+ a_ok(a);
+}
+
+static void acmd_bgcancel(admin *a, unsigned ac, char *av[])
+{
+ admin_bgop *bg;
+
+ if ((bg = a_bgfind(a, av[0])) == 0)
+ a_fail(a, "unknown-tag", "%s", av[0], A_END);
+ else {
+ bg->cancel(bg);
+ a_bgrelease(bg);
+ a_ok(a);
+ }
+}
+
static void acmd_list(admin *a, unsigned ac, char *av[])
{
peer *p;
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 },
+ { "bgcancel", "TAG", 1, 1, acmd_bgcancel },
{ "checkchal", "CHAL", 1, 1, acmd_checkchal },
{ "daemon", 0, 0, 0, acmd_daemon },
{ "eping", "[OPTIONS] PEER", 1, 0xffff, acmd_eping },
{ "greet", "PEER CHAL", 2, 2, acmd_greet },
{ "help", 0, 0, 0, acmd_help },
{ "ifname", "PEER", 1, 1, acmd_ifname },
+ { "jobs", 0, 0, 0, acmd_jobs },
{ "kill", "PEER", 1, 1, acmd_kill },
{ "list", 0, 0, 0, acmd_list },
{ "notify", "MESSAGE ...", 1, 0xffff, acmd_notify },