X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/tripe/blobdiff_plain/64cf222377512d2cbdc8d750420e11cc5ddecdbd..e7a4d919c7949baff584b17fe604f1a9973cf00f:/server/admin.c diff --git a/server/admin.c b/server/admin.c index b166b853..1660106c 100644 --- a/server/admin.c +++ b/server/admin.c @@ -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: ---