+ va_list ap;
+
+ va_start(ap, fmt);
+ a_valert(AF_NOTE, AF_NOTE, "NOTE", fmt, ap);
+ va_end(ap);
+}
+
+/* --- @a_quit@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Shuts things down nicely.
+ */
+
+void a_quit(void)
+{
+ peer *p;
+
+ close(sock.fd);
+ unlink(sockname);
+ while ((p = p_first()) != 0)
+ p_destroy(p);
+ exit(0);
+}
+
+/* --- @a_sigdie@ --- *
+ *
+ * Arguments: @int sig@ = signal number
+ * @void *v@ = an uninteresting argument
+ *
+ * Returns: ---
+ *
+ * Use Shuts down on receipt of a fatal signal.
+ */
+
+static void a_sigdie(int sig, void *v)
+{
+ char *p;
+ char buf[20];
+
+ switch (sig) {
+ case SIGTERM: p = "SIGTERM"; break;
+ case SIGINT: p = "SIGINT"; break;
+ default:
+ sprintf(buf, "%i", sig);
+ p = buf;
+ break;
+ }
+ a_warn("SERVER", "quit", "signal", "%s", p, A_END);
+ a_quit();
+}
+
+/* --- @a_sighup@ --- *
+ *
+ * Arguments: @int sig@ = signal number
+ * @void *v@ = an uninteresting argument
+ *
+ * Returns: ---
+ *
+ * Use Logs a message about SIGHUP not being useful.
+ */
+
+static void a_sighup(int sig, void *v)
+{
+ a_warn("SERVER", "ignore", "signal", "SIGHUP", A_END);
+}
+
+/* --- @a_parsetime@ --- *
+ *
+ * Arguments; @const char *p@ = time string to parse
+ *
+ * Returns: Time in seconds, or @< 0@ on error.
+ */
+
+static long a_parsetime(const char *p)
+{
+ char *q;
+ long t = strtol(p, &q, 0);
+
+ switch (*q) {
+ case 'd': t *= 24;
+ case 'h': t *= 60;
+ case 'm': t *= 60;
+ case 's': if (q[1] != 0)
+ default: t = -1;
+ case 0: break;
+ }
+ return (t);
+}
+
+/* --- @a_findpeer@ --- *
+ *
+ * Arguments: @admin *a@ = admin connection
+ * @const char *pn@ = peer name
+ *
+ * Returns: The peer, or null if not there.
+ *
+ * Use: Finds a peer, reporting an error if it failed.
+ */
+
+static peer *a_findpeer(admin *a, const char *pn)
+{
+ peer *p;
+
+ if ((p = p_find(pn)) == 0)
+ a_fail(a, "unknown-peer", "%s", pn, A_END);
+ return (p);
+}
+
+/*----- Backgrounded operations -------------------------------------------*/
+
+#define BGTAG(bg) \
+ (((admin_bgop *)(bg))->tag ? ((admin_bgop *)(bg))->tag : "<foreground>")
+
+/* --- @a_bgrelease@ --- *
+ *
+ * Arguments: @admin_bgop *bg@ = backgrounded operation
+ *
+ * Returns: ---
+ *
+ * Use: Removes a backgrounded operation from the queue, since
+ * (presumably) it's done.
+ */
+
+static void a_bgrelease(admin_bgop *bg)
+{
+ admin *a = bg->a;
+
+ T( trace(T_ADMIN, "admin: release bgop %s", BGTAG(bg)); )
+ if (bg->tag) xfree(bg->tag);
+ else selbuf_enable(&a->b);
+ if (bg->next) bg->next->prev = bg->prev;
+ 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);
+}
+
+/* --- @a_bgok@, @a_bginfo@, @a_bgfail@ --- *
+ *
+ * Arguments: @admin_bgop *bg@ = backgrounded operation
+ * @const char *fmt@ = format string
+ * @...@ = other arguments
+ *
+ * Returns: ---
+ *
+ * Use: Convenience functions for @a_write@.
+ */
+
+static void a_bgok(admin_bgop *bg)
+ { a_write(bg->a, "OK", bg->tag, A_END); }
+
+static void a_bginfo(admin_bgop *bg, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ a_vwrite(bg->a, "INFO", bg->tag, fmt, ap);
+ va_end(ap);
+}
+
+static void a_bgfail(admin_bgop *bg, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ a_vwrite(bg->a, "FAIL", bg->tag, fmt, ap);
+ va_end(ap);
+}
+
+/* --- @a_bgadd@ --- *
+ *
+ * Arguments: @admin *a@ = administration connection
+ * @admin_bgop *bg@ = pointer to background operation
+ * @const char *tag@ = background tag, or null for foreground
+ * @void (*cancel)(admin_bgop *)@ = cancel function
+ *
+ * Returns: ---
+ *
+ * Use: Links a background job into the list.
+ */
+
+static void a_bgadd(admin *a, admin_bgop *bg, const char *tag,
+ void (*cancel)(admin_bgop *))
+{
+ if (tag)
+ bg->tag = xstrdup(tag);
+ else {
+ bg->tag = 0;
+ selbuf_disable(&a->b);
+ }
+ bg->a = a;
+ bg->cancel = cancel;
+ bg->next = a->bg;
+ 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);
+}
+
+/*----- Name resolution operations ----------------------------------------*/
+
+/* --- @a_resolved@ --- *
+ *
+ * Arguments: @struct hostent *h@ = pointer to resolved hostname
+ * @void *v@ = pointer to resolver operation
+ *
+ * Returns: ---
+ *
+ * Use: Handles a completed name resolution.
+ */
+
+static void a_resolved(struct hostent *h, void *v)
+{
+ admin_resop *r = v;
+
+ T( trace(T_ADMIN, "admin: resop %s resolved", BGTAG(r)); )
+ TIMER;
+ if (!h) {
+ a_bgfail(&r->bg, "resolve-error", "%s", r->addr, A_END);
+ r->func(r, ARES_FAIL);
+ } else {
+ memcpy(&r->sa.sin.sin_addr, h->h_addr, sizeof(struct in_addr));
+ r->func(r, ARES_OK);
+ }
+ sel_rmtimer(&r->t);
+ xfree(r->addr);
+ a_bgrelease(&r->bg);
+}
+
+/* --- @a_restimer@ --- *
+ *
+ * Arguments: @struct timeval *tv@ = timer
+ * @void *v@ = pointer to resolver operation
+ *
+ * Returns: ---
+ *
+ * Use: Times out a resolver.
+ */
+
+static void a_restimer(struct timeval *tv, void *v)
+{
+ admin_resop *r = v;
+
+ T( trace(T_ADMIN, "admin: resop %s timeout", BGTAG(r)); )
+ a_bgfail(&r->bg, "resolver-timeout", "%s", r->addr, A_END);
+ r->func(r, ARES_FAIL);
+ bres_abort(&r->r);
+ xfree(r->addr);
+ a_bgrelease(&r->bg);
+}
+
+/* --- @a_rescancel@ --- *
+ *
+ * Arguments: @admin_bgop *bg@ = background operation
+ *
+ * Returns: ---
+ *
+ * Use: Cancels an add operation.
+ */
+
+static void a_rescancel(admin_bgop *bg)
+{
+ admin_resop *r = (admin_resop *)bg;
+
+ T( trace(T_ADMIN, "admin: cancel resop %s", BGTAG(r)); )
+ r->func(r, ARES_FAIL);
+ sel_rmtimer(&r->t);
+ xfree(r->addr);
+ bres_abort(&r->r);
+}
+
+/* --- @a_resolve@ --- *
+ *
+ * Arguments: @admin *a@ = administration connection
+ * @admin_resop *r@ = resolver operation to run
+ * @const char *tag@ = background operation tag
+ * @void (*func)(struct admin_resop *, int@ = handler function
+ * @unsigned ac@ = number of remaining arguments
+ * @char *av[]@ = pointer to remaining arguments
+ *
+ * Returns: ---
+ *
+ * Use: Cranks up a resolver job.
+ */
+
+static void a_resolve(admin *a, admin_resop *r, const char *tag,
+ void (*func)(struct admin_resop *, int),
+ unsigned ac, char *av[])
+{
+ struct timeval tv;
+ unsigned long pt;
+ char *p;
+ int i = 0;
+
+ /* --- Fill in the easy bits of address --- */
+
+ r->bg.tag = "<starting>";
+ r->addr = 0;
+ r->func = func;
+ if (mystrieq(av[i], "inet")) i++;
+ if (ac - i != 2) {
+ a_fail(a, "bad-addr-syntax", "[inet] ADDRESS PORT", A_END);
+ goto fail;
+ }
+ r->sa.sin.sin_family = AF_INET;
+ r->sasz = sizeof(r->sa.sin);
+ r->addr = xstrdup(av[i]);
+ pt = strtoul(av[i + 1], &p, 0);
+ if (*p) {
+ struct servent *s = getservbyname(av[i + 1], "udp");
+ if (!s) {
+ a_fail(a, "unknown-service", "%s", av[i + 1], A_END);
+ goto fail;
+ }
+ pt = ntohs(s->s_port);
+ }
+ if (pt == 0 || pt >= 65536) {
+ a_fail(a, "invalid-port", "%lu", pt, A_END);
+ goto fail;
+ }
+ r->sa.sin.sin_port = htons(pt);
+
+ /* --- Report backgrounding --- *
+ *
+ * Do this for consistency of interface, even if we're going to get the
+ * answer straight away.
+ */
+
+ a_bgadd(a, &r->bg, tag, a_rescancel);
+ T( trace(T_ADMIN, "admin: %u, resop %s, hostname `%s'",
+ a->seq, BGTAG(r), r->addr); )
+
+ /* --- If the name is numeric, do it the easy way --- */
+
+ if (inet_aton(av[i], &r->sa.sin.sin_addr)) {
+ T( trace(T_ADMIN, "admin: resop %s done the easy way", BGTAG(r)); )
+ func(r, ARES_OK);
+ xfree(r->addr);
+ a_bgrelease(&r->bg);
+ return;
+ }