#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
* @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); )
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 const acmd acmdtab[] = {
{ "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 },