chiark / gitweb /
uslip/uslip.c: Be consistent about `VERB_NOUN' function naming.
[tripe] / server / admin.c
1 /* -*-c-*-
2  *
3  * Admin interface for configuration
4  *
5  * (c) 2001 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of Trivial IP Encryption (TrIPE).
11  *
12  * TrIPE is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * TrIPE is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with TrIPE; if not, write to the Free Software Foundation,
24  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25  */
26
27 /*----- Header files ------------------------------------------------------*/
28
29 #include "tripe.h"
30
31 /*----- Global variables --------------------------------------------------*/
32
33 #ifndef NTRACE
34
35 const trace_opt tr_opts[] = {
36   { 't',        T_TUNNEL,       "tunnel events" },
37   { 'r',        T_PEER,         "peer events" },
38   { 'a',        T_ADMIN,        "admin interface" },
39   { 's',        T_KEYSET,       "symmetric keyset management" },
40   { 'x',        T_KEYEXCH,      "key exchange" },
41   { 'm',        T_KEYMGMT,      "key management" },
42   { 'l',        T_CHAL,         "challenge management" },
43   { 'v',        T_PRIVSEP,      "privilege separation" },
44   { 'p',        T_PACKET,       "packet contents" },
45   { 'c',        T_CRYPTO,       "crypto details" },
46   { 'A',        T_ALL,          "all of the above" },
47   { 0,          0,              0 }
48 };
49
50 unsigned tr_flags = 0;
51 #endif
52
53 static const trace_opt w_opts[] = {
54 #ifndef NTRACE
55   { 't',        AF_TRACE,       "trace messages" },
56 #endif
57   { 'n',        AF_NOTE,        "asynchronous notifications" },
58   { 'w',        AF_WARN,        "warnings" },
59   { 'A',        AF_ALLMSGS,     "all of the above" },
60   { 0,          0,              0 }
61 };
62
63 /*----- Static variables --------------------------------------------------*/
64
65 static admin *admins;
66 static admin *a_dead;
67 static sel_file sock;
68 static const char *sockname;
69 static sym_table a_svcs;
70 static unsigned flags = 0;
71 static admin *a_stdin = 0;
72 static sig s_term, s_int, s_hup;
73
74 #define F_DAEMON 1u
75 #define F_INIT 2u
76 #define F_FOREGROUND 4u
77
78 #define T_RESOLVE SEC(30)
79 #define T_PING SEC(5)
80
81 static void a_destroy(admin */*a*/);
82
83 #define BOOL(x) ((x) ? "t" : "nil")
84
85 /*----- Output functions --------------------------------------------------*/
86
87 /* --- @trywrite@ --- *
88  *
89  * Arguments:   @admin *a@ = pointer to an admin block
90  *              @const char *p@ = pointer to buffer to write
91  *              @size_t sz@ = size of data to write
92  *
93  * Returns:     The number of bytes written, or less than zero on error.
94  *
95  * Use:         Attempts to write data to a client.
96  */
97
98 static ssize_t trywrite(admin *a, const char *p, size_t sz)
99 {
100   ssize_t n, done = 0;
101
102 again:
103   if (!sz)
104     return (done);
105   n = write(a->w.fd, p, sz);
106   if (n > 0) {
107     done += n;
108     p += n;
109     sz -= n;
110     goto again;
111   }
112   if (n < 0) {
113     if (errno == EINTR)
114       goto again;
115     if (errno != EAGAIN && errno != EWOULDBLOCK) {
116       a_destroy(a);
117       a_warn("ADMIN", "client-write-error", "?ERRNO", A_END);
118       return (-1);
119     }
120   }
121   return (done);
122 }
123
124 /* --- @doqueue@ -- *
125  *
126  * Arguments:   @oqueue *q@ = pointer to output queue
127  *              @const char *p@ = pointer to buffer to write
128  *              @size_t sz@ = size of buffer
129  *
130  * Returns:     Nonzero if the queue was previously empty.
131  *
132  * Use:         Queues data to be written later.
133  */
134
135 static int doqueue(oqueue *q, const char *p, size_t sz)
136 {
137   obuf *o;
138   int rc = 0;
139   size_t n;
140
141   o = q->tl;
142   if (!o)
143     rc = 1;
144   else if (o->p_in < o->buf + OBUFSZ)
145     goto noalloc;
146
147   do {
148     o = xmalloc(sizeof(obuf));
149     o->next = 0;
150     o->p_in = o->p_out = o->buf;
151     if (q->tl)
152       q->tl->next = o;
153     else
154       q->hd = o;
155     q->tl = o;
156
157   noalloc:
158     n = o->buf + OBUFSZ - o->p_in;
159     if (n > sz)
160       n = sz;
161     memcpy(o->p_in, p, n);
162     o->p_in += n;
163     p += n;
164     sz -= n;
165   } while (sz);
166
167   return (rc);
168 }
169
170 /* --- @dosend@ --- *
171  *
172  * Arguemnts:   @admin *a@ = pointer to an admin block
173  *              @const char *p@ = pointer to buffer to write
174  *              @size_t sz@ = size of data to write
175  *
176  * Returns:     ---
177  *
178  * Use:         Sends data to an admin client.
179  */
180
181 static void dosend(admin *a, const char *p, size_t sz)
182 {
183   ssize_t n;
184
185   if (a->f & AF_DEAD)
186     return;
187   if (!a->out.hd) {
188     if ((n = trywrite(a, p, sz)) < 0)
189       return;
190     p += n;
191     sz -= n;
192     if (!sz)
193       return;
194   }
195   if (doqueue(&a->out, p, sz))
196     sel_addfile(&a->w);
197 }
198
199 /* --- @a_flush@ --- *
200  *
201  * Arguments:   @int fd@ = file descriptor
202  *              @unsigned mode@ = what's happening
203  *              @void *v@ = pointer to my admin block
204  *
205  * Returns:     ---
206  *
207  * Use:         Flushes buffers when a client is ready to read again.
208  */
209
210 static void a_flush(int fd, unsigned mode, void *v)
211 {
212   admin *a = v;
213   obuf *o, *oo;
214   ssize_t n;
215
216   o = a->out.hd;
217   while (o) {
218     if ((n = trywrite(a, o->p_out, o->p_in - o->p_out)) < 0)
219       return;
220     o->p_out += n;
221     if (o->p_in < o->p_out)
222       break;
223     oo = o;
224     o = o->next;
225     xfree(oo);
226   }
227   a->out.hd = o;
228   if (!o) {
229     a->out.tl = 0;
230     sel_rmfile(&a->w);
231   }
232 }
233
234 /*----- Utility functions -------------------------------------------------*/
235
236 /* --- @a_vformat@ --- *
237  *
238  * Arguments:   @dstr *d@ = where to leave the formatted message
239  *              @const char *fmt@ = pointer to format string
240  *              @va_list *ap@ = arguments in list
241  *
242  * Returns:     ---
243  *
244  * Use:         Main message token formatting driver.  The arguments are
245  *              interleaved formatting tokens and their parameters, finally
246  *              terminated by an entry @A_END@.
247  *
248  *              Tokens recognized:
249  *
250  *                * "*..." ... -- pretokenized @dstr_putf@-like string
251  *
252  *                * "?ADDR" SOCKADDR -- a socket address, to be converted
253  *
254  *                * "?B64" BUFFER SIZE -- binary data to be base64-encoded
255  *
256  *                * "?TOKENS" VECTOR -- null-terminated vector of tokens
257  *
258  *                * "?PEER" PEER -- peer's name
259  *
260  *                * "?ERRNO" ERRNO -- system error code
261  *
262  *                * "[!]..." ... -- @dstr_putf@-like string as single token
263  */
264
265 void a_vformat(dstr *d, const char *fmt, va_list *ap)
266 {
267   dstr dd = DSTR_INIT;
268
269   while (fmt) {
270     if (*fmt == '*') {
271       if (d->len) dstr_putc(d, ' ');
272       dstr_vputf(d, fmt + 1, ap);
273     } else if (*fmt == '?') {
274       if (strcmp(fmt, "?ADDR") == 0) {
275         const addr *a = va_arg(*ap, const addr *);
276         switch (a->sa.sa_family) {
277           case AF_INET:
278             u_quotify(d, "INET");
279             u_quotify(d, inet_ntoa(a->sin.sin_addr));
280             dstr_putf(d, " %u", (unsigned)ntohs(a->sin.sin_port));
281             break;
282           default:
283             abort();
284         }
285       } else if (strcmp(fmt, "?B64") == 0) {
286         const octet *p = va_arg(*ap, const octet *);
287         size_t n = va_arg(*ap, size_t);
288         base64_ctx b64;
289         dstr_putc(d, ' ');
290         base64_init(&b64);
291         b64.indent = "";
292         b64.maxline = 0;
293         base64_encode(&b64, p, n, d);
294         base64_encode(&b64, 0, 0, d);
295         while (d->len && d->buf[d->len - 1] == '=') d->len--;
296       } else if (strcmp(fmt, "?TOKENS") == 0) {
297         const char *const *av = va_arg(*ap, const char *const *);
298         while (*av) u_quotify(d, *av++);
299       } else if (strcmp(fmt, "?PEER") == 0)
300         u_quotify(d, p_name(va_arg(*ap, peer *)));
301       else if (strcmp(fmt, "?ERRNO") == 0) {
302         dstr_putf(d, " E%d", errno);
303         u_quotify(d, strerror(errno));
304       } else
305         abort();
306     } else {
307       if (*fmt == '!') fmt++;
308       DRESET(&dd);
309       dstr_vputf(&dd, fmt, ap);
310       u_quotify(d, dd.buf);
311     }
312     fmt = va_arg(*ap, const char *);
313   }
314   dstr_putz(d);
315
316   dstr_destroy(&dd);
317 }
318
319 /* --- @a_format@ --- *
320  *
321  * Arguments:   @dstr *d@ = where to leave the formatted message
322  *              @const char *fmt@ = pointer to format string
323  *
324  * Returns:     ---
325  *
326  * Use:         Writes a tokenized message into a string, for later
327  *              presentation.
328  */
329
330 void a_format(dstr *d, const char *fmt, ...)
331 {
332   va_list ap;
333
334   va_start(ap, fmt);
335   a_vformat(d, fmt, &ap);
336   va_end(ap);
337 }
338
339 /* --- @a_write@, @a_vwrite@ --- *
340  *
341  * Arguments:   @admin *a@ = admin connection to write to
342  *              @const char *status@ = status code to report
343  *              @const char *tag@ = tag string, or null
344  *              @const char *fmt@ = pointer to format string
345  *              @va_list *ap@ = arguments in list
346  *              @...@ = other arguments
347  *
348  * Returns:     ---
349  *
350  * Use:         Sends a message to an admin connection.
351  */
352
353 static void a_vwrite(admin *a, const char *status, const char *tag,
354                      const char *fmt, va_list *ap)
355 {
356   dstr d = DSTR_INIT;
357
358   if (tag) dstr_puts(&d, "BG");
359   dstr_puts(&d, status);
360   if (tag) u_quotify(&d, tag);
361   a_vformat(&d, fmt, ap);
362   dstr_putc(&d, '\n');
363   dosend(a, d.buf, d.len);
364   dstr_destroy(&d);
365 }
366
367 static void a_write(admin *a, const char *status, const char *tag,
368                     const char *fmt, ...)
369 {
370   va_list ap;
371
372   va_start(ap, fmt);
373   a_vwrite(a, status, tag, fmt, &ap);
374   va_end(ap);
375 }
376
377 /* --- @a_ok@, @a_info@, @a_fail@ --- *
378  *
379  * Arguments:   @admin *a@ = connection
380  *              @const char *fmt@ = format string
381  *              @...@ = other arguments
382  *
383  * Returns:     ---
384  *
385  * Use:         Convenience functions for @a_write@.
386  */
387
388 static void a_ok(admin *a) { a_write(a, "OK", 0, A_END); }
389
390 static void a_info(admin *a, const char *fmt, ...)
391 {
392   va_list ap;
393
394   va_start(ap, fmt);
395   a_vwrite(a, "INFO", 0, fmt, &ap);
396   va_end(ap);
397 }
398
399 static void a_fail(admin *a, const char *fmt, ...)
400 {
401   va_list ap;
402
403   va_start(ap, fmt);
404   a_vwrite(a, "FAIL", 0, fmt, &ap);
405   va_end(ap);
406 }
407
408 /* --- @a_alert@, @a_valert@, @a_rawalert@ --- *
409  *
410  * Arguments:   @unsigned f_and, f_eq@ = filter for connections
411  *              @const char *status@ = status string
412  *              @const char *fmt@ = pointer to format string
413  *              @const char *p@ = pointer to raw string
414  *              @size_t sz@ = size of raw string
415  *              @va_list *ap@ = arguments in list
416  *              @...@ = other arguments
417  *
418  * Returns:     ---
419  *
420  * Use:         Write a message to all admin connections matched by the given
421  *              filter.
422  */
423
424 static void a_rawalert(unsigned f_and, unsigned f_eq, const char *status,
425                        const char *p, size_t sz)
426 {
427   admin *a, *aa;
428   dstr d = DSTR_INIT;
429
430   if (!(flags & F_INIT))
431     return;
432   dstr_puts(&d, status);
433   if (p) {
434     dstr_putc(&d, ' ');
435     dstr_putm(&d, p, sz);
436   }
437   dstr_putc(&d, '\n');
438   p = d.buf;
439   sz = d.len;
440   for (a = admins; a; a = aa) {
441     aa = a->next;
442     if ((a->f & f_and) == f_eq)
443       dosend(a, d.buf, d.len);
444   }
445   dstr_destroy(&d);
446 }
447
448 static void a_valert(unsigned f_and, unsigned f_eq, const char *status,
449                      const char *fmt, va_list *ap)
450 {
451   dstr d = DSTR_INIT;
452
453   if (!(flags & F_INIT))
454     return;
455   a_vformat(&d, fmt, ap);
456   a_rawalert(f_and, f_eq, status, fmt ? d.buf : 0, fmt ? d.len : 0);
457   dstr_destroy(&d);
458 }
459
460 static void a_alert(unsigned f_and, unsigned f_eq, const char *status,
461                     const char *fmt, ...)
462 {
463   va_list ap;
464
465   va_start(ap, fmt);
466   a_valert(f_and, f_eq, status, fmt, &ap);
467   va_end(ap);
468 }
469
470 /* --- @a_warn@ --- *
471  *
472  * Arguments:   @const char *fmt@ = pointer to format string
473  *              @...@ = other arguments
474  *
475  * Returns:     ---
476  *
477  * Use:         Informs all admin connections of a warning.
478  */
479
480 void a_warn(const char *fmt, ...)
481 {
482   va_list ap;
483
484   va_start(ap, fmt);
485   if (flags & F_INIT)
486     a_valert(0, 0, "WARN", fmt, &ap);
487   else {
488     dstr d = DSTR_INIT;
489     fprintf(stderr, "%s: ", QUIS);
490     a_vformat(&d, fmt, &ap);
491     dstr_putc(&d, '\n');
492     dstr_write(&d, stderr);
493     dstr_destroy(&d);
494   }
495   va_end(ap);
496 }
497
498 /* --- @a_trace@ --- *
499  *
500  * Arguments:   @const char *p@ = pointer to a buffer
501  *              @size_t sz@ = size of the buffer
502  *              @void *v@ = uninteresting pointer
503  *
504  * Returns:     ---
505  *
506  * Use:         Custom trace output handler.  Sends trace messages to
507  *              interested admin connections.
508  */
509
510 #ifndef NTRACE
511 static void a_trace(const char *p, size_t sz, void *v)
512   { a_rawalert(AF_TRACE, AF_TRACE, "TRACE", p, sz); }
513 #endif
514
515 /* --- @a_notify@ --- *
516  *
517  * Arguments:   @const char *fmt@ = pointer to format string
518  *              @...@ = other arguments
519  *
520  * Returns:     ---
521  *
522  * Use:         Sends a notification to interested admin connections.
523  */
524
525 void a_notify(const char *fmt, ...)
526 {
527   va_list ap;
528
529   va_start(ap, fmt);
530   a_valert(AF_NOTE, AF_NOTE, "NOTE", fmt, &ap);
531   va_end(ap);
532 }
533
534 /* --- @a_quit@ --- *
535  *
536  * Arguments:   ---
537  *
538  * Returns:     ---
539  *
540  * Use:         Shuts things down nicely.
541  */
542
543 void a_quit(void)
544 {
545   close(sock.fd);
546   unlink(sockname);
547   FOREACH_PEER(p, { p_destroy(p); });
548   ps_quit();
549   exit(0);
550 }
551
552 /* --- @a_sigdie@ --- *
553  *
554  * Arguments:   @int sig@ = signal number
555  *              @void *v@ = an uninteresting argument
556  *
557  * Returns:     ---
558  *
559  * Use          Shuts down on receipt of a fatal signal.
560  */
561
562 static void a_sigdie(int sig, void *v)
563 {
564   char *p;
565   char buf[20];
566
567   switch (sig) {
568     case SIGTERM:       p = "SIGTERM"; break;
569     case SIGINT:        p = "SIGINT"; break;
570     default:
571       sprintf(buf, "%i", sig);
572       p = buf;
573       break;
574   }
575   a_warn("SERVER", "quit", "signal", "%s", p, A_END);
576   a_quit();
577 }
578
579 /* --- @a_sighup@ --- *
580  *
581  * Arguments:   @int sig@ = signal number
582  *              @void *v@ = an uninteresting argument
583  *
584  * Returns:     ---
585  *
586  * Use          Logs a message about SIGHUP not being useful.
587  */
588
589 static void a_sighup(int sig, void *v)
590   { a_warn("SERVER", "ignore", "signal", "SIGHUP", A_END); }
591
592 /* --- @a_parsetime@ --- *
593  *
594  * Arguments;   @const char *p@ = time string to parse
595  *
596  * Returns:     Time in seconds, or @< 0@ on error.
597  */
598
599 static long a_parsetime(const char *p)
600 {
601   char *q;
602   long t = strtol(p, &q, 0);
603
604   switch (*q) {
605     case 'd': t *= 24;
606     case 'h': t *= 60;
607     case 'm': t *= 60;
608     case 's': if (q[1] != 0)
609     default:    t = -1;
610     case 0:   break;
611   }
612   return (t);
613 }
614
615 /* --- @a_findpeer@ --- *
616  *
617  * Arguments:   @admin *a@ = admin connection
618  *              @const char *pn@ = peer name
619  *
620  * Returns:     The peer, or null if not there.
621  *
622  * Use:         Finds a peer, reporting an error if it failed.
623  */
624
625 static peer *a_findpeer(admin *a, const char *pn)
626 {
627   peer *p;
628
629   if ((p = p_find(pn)) == 0)
630     a_fail(a, "unknown-peer", "%s", pn, A_END);
631   return (p);
632 }
633
634 /*----- Backgrounded operations -------------------------------------------*/
635
636 #define BGTAG(bg)                                                       \
637   (((admin_bgop *)(bg))->tag ? ((admin_bgop *)(bg))->tag : "<foreground>")
638
639 /* --- @a_bgfind@ --- *
640  *
641  * Arguments:   @admin *a@ = a client block
642  *              @const char *tag@ = the requested tag
643  *
644  * Returns:     The requested background job, or null.
645  */
646
647 static admin_bgop *a_bgfind(admin *a, const char *tag)
648 {
649   admin_bgop *bg;
650
651   for (bg = a->bg; bg; bg = bg->next) {
652     if (bg->tag && strcmp(tag, bg->tag) == 0)
653       return (bg);
654   }
655   return (0);
656 }
657
658 /* --- @a_bgrelease@ --- *
659  *
660  * Arguments:   @admin_bgop *bg@ = backgrounded operation
661  *
662  * Returns:     ---
663  *
664  * Use:         Removes a backgrounded operation from the queue, since
665  *              (presumably) it's done.
666  */
667
668 static void a_bgrelease(admin_bgop *bg)
669 {
670   admin *a = bg->a;
671
672   T( trace(T_ADMIN, "admin: release bgop %s", BGTAG(bg)); )
673   if (bg->tag) xfree(bg->tag);
674   else selbuf_enable(&a->b);
675   if (bg->next) bg->next->prev = bg->prev;
676   if (bg->prev) bg->prev->next = bg->next;
677   else a->bg = bg->next;
678   xfree(bg);
679   if (!a->bg && (a->f & AF_CLOSE)) a_destroy(a);
680 }
681
682 /* --- @a_bgok@, @a_bginfo@, @a_bgfail@ --- *
683  *
684  * Arguments:   @admin_bgop *bg@ = backgrounded operation
685  *              @const char *fmt@ = format string
686  *              @...@ = other arguments
687  *
688  * Returns:     ---
689  *
690  * Use:         Convenience functions for @a_write@.
691  */
692
693 static void a_bgok(admin_bgop *bg)
694   { a_write(bg->a, "OK", bg->tag, A_END); }
695
696 static void a_bginfo(admin_bgop *bg, const char *fmt, ...)
697 {
698   va_list ap;
699   va_start(ap, fmt);
700   a_vwrite(bg->a, "INFO", bg->tag, fmt, &ap);
701   va_end(ap);
702 }
703
704 static void a_bgfail(admin_bgop *bg, const char *fmt, ...)
705 {
706   va_list ap;
707   va_start(ap, fmt);
708   a_vwrite(bg->a, "FAIL", bg->tag, fmt, &ap);
709   va_end(ap);
710 }
711
712 /* --- @a_bgadd@ --- *
713  *
714  * Arguments:   @admin *a@ = administration connection
715  *              @admin_bgop *bg@ = pointer to background operation
716  *              @const char *tag@ = background tag, or null for foreground
717  *              @void (*cancel)(admin_bgop *)@ = cancel function
718  *
719  * Returns:     Zero for success, nonzero on failure.
720  *
721  * Use:         Links a background job into the list.
722  */
723
724 static int a_bgadd(admin *a, admin_bgop *bg, const char *tag,
725                    void (*cancel)(admin_bgop *))
726 {
727   if (tag) {
728     if (a_bgfind(a, tag)) {
729       a_fail(a, "tag-exists", "%s", tag, A_END);
730       return (-1);
731     }
732     bg->tag = xstrdup(tag);
733   }
734   else {
735     bg->tag = 0;
736     selbuf_disable(&a->b);
737   }
738   bg->a = a;
739   bg->cancel = cancel;
740   bg->next = a->bg;
741   bg->prev = 0;
742   if (a->bg) a->bg->prev = bg;
743   a->bg = bg;
744   T( trace(T_ADMIN, "admin: add bgop %s", BGTAG(bg)); )
745   if (tag) a_write(a, "DETACH", tag, A_END);
746   return (0);
747 }
748
749 /*----- Job table manipulation --------------------------------------------*/
750
751 #define JOB_SHIFT 16
752 #define JOB_INDEXMASK ((1ul << JOB_SHIFT) - 1)
753 #define JOB_SEQMASK ((1ul << (32 - JOB_SHIFT)) - 1)
754
755 #define JOB_END 0xfffffffful
756
757 static unsigned long a_joboffset;
758
759 /* --- @a_jobidencode@ --- *
760  *
761  * Arguments:   @admin_svcop *svc@ = pointer to a service operation
762  *
763  * Returns:     A jobid for this job, in an internal static buffer.
764  *
765  * Use:         Constructs a jobid.  In order to dissuade people from
766  *              predicting jobids, we obfuscate them.
767  *
768  *              A `raw' jobid consists of two 16-bit fields.  The low 16 bits
769  *              are an index into a big array.  The high 16 bits are a
770  *              sequence number recording how many times that slot has been
771  *              reused.
772  *
773  *              This `raw' jobid is then obfuscated by adding a randomly-
774  *              generated offset, and multiplying (mod %$2^{32}$%) by a fixed
775  *              odd constant.
776  */
777
778 static const char *a_jobidencode(admin_svcop *svc)
779 {
780   admin_jobtable *j = &svc->prov->j;
781   static char buf[10];
782   unsigned long pre;
783   unsigned seq;
784
785   assert(svc->index <= JOB_INDEXMASK);
786   seq = j->v[svc->index].seq;
787   assert(seq <= JOB_SEQMASK);
788   pre = (unsigned long)svc->index | ((unsigned long)seq << JOB_SHIFT);
789   sprintf(buf, "J%08lx", ((pre + a_joboffset) * 0x0f87a7a3ul) & 0xffffffff);
790   return (buf);
791 }
792
793 /* --- @a_jobiddecode@ --- *
794  *
795  * Arguments:   @admin_jobtable *j@ = pointer to a job table
796  *              @const char *jid@ = pointer to a jobid string
797  *
798  * Returns:     A pointer to the job's @svcop@ structure.
799  */
800
801 static admin_svcop *a_jobiddecode(admin_jobtable *j, const char *jid)
802 {
803   unsigned i;
804   unsigned long pre;
805
806   if (jid[0] != 'J')
807     return (0);
808   for (i = 1; i < 9; i++) {
809     if (!isxdigit((unsigned char)jid[i]))
810       return (0);
811   }
812   if (jid[9] != 0)
813     return (0);
814   pre = strtoul(jid + 1, 0, 16);
815   pre = ((pre * 0xbd11c40bul) - a_joboffset) & 0xffffffff;
816   i = pre & JOB_INDEXMASK;
817   if (i >= j->n || j->v[i].seq != (pre >> JOB_SHIFT))
818     return (0);
819   return (j->v[i].u.op);
820 }
821
822 /* --- @a_jobcreate@ --- *
823  *
824  * Arguments:   @admin *a@ = pointer to administration client
825  *
826  * Returns:     A pointer to a freshly-allocated @svcop@, or null.
827  *
828  * Use:         Allocates a fresh @svcop@ and links it into a job table.
829  */
830
831 static admin_svcop *a_jobcreate(admin *a)
832 {
833   admin_svcop *svc;
834   unsigned i;
835   unsigned sz;
836   admin_jobtable *j = &a->j;
837
838   if (j->free != JOB_END) {
839     i = j->free;
840     j->free = j->v[i].u.next;
841   } else {
842     if (j->n == j->sz) {
843       if (j->sz > JOB_INDEXMASK)
844         return (0);
845       sz = j->sz;
846       if (!sz) {
847         j->sz = 16;
848         j->v = xmalloc(j->sz * sizeof(*j->v));
849       } else {
850         j->sz = sz << 1;
851         j->v = xrealloc(j->v, j->sz * sizeof(*j->v), sz * sizeof(*j->v));
852       }
853     }
854     i = j->n++;
855     j->v[i].seq = 0;
856   }
857   svc = xmalloc(sizeof(*svc));
858   svc->index = i;
859   svc->prov = a;
860   svc->next = j->active;
861   svc->prev = 0;
862   if (j->active) j->active->prev = svc;
863   j->active = svc;
864   j->v[i].u.op = svc;
865   IF_TRACING(T_ADMIN, {
866     trace(T_ADMIN, "admin: created job %s (%u)", a_jobidencode(svc), i);
867   })
868   return (svc);
869 }
870
871 /* --- @a_jobdestroy@ --- *
872  *
873  * Arguments:   @admin_svcop *svc@ = pointer to job block
874  *
875  * Returns:     ---
876  *
877  * Use:         Frees up a completed (or cancelled) job.
878  */
879
880 static void a_jobdestroy(admin_svcop *svc)
881 {
882   admin *a = svc->prov;
883   admin_jobtable *j = &a->j;
884   unsigned i = svc->index;
885
886   IF_TRACING(T_ADMIN, {
887     trace(T_ADMIN, "admin: destroying job %s (%u)", a_jobidencode(svc), i);
888   })
889   assert(j->v[i].u.op = svc);
890   j->v[i].u.next = j->free;
891   j->v[i].seq++;
892   j->free = i;
893   if (svc->next) svc->next->prev = svc->prev;
894   if (svc->prev) svc->prev->next = svc->next;
895   else j->active = svc->next;
896 }
897
898 /* --- @a_jobtableinit@ --- *
899  *
900  * Arguments:   @admin_jobtable *j@ = pointer to job table
901  *
902  * Returns:     ---
903  *
904  * Use:         Initializes a job table.
905  */
906
907 static void a_jobtableinit(admin_jobtable *j)
908 {
909   if (!a_joboffset)
910     a_joboffset = GR_RANGE(&rand_global, 0xffffffff) + 1;
911   j->n = j->sz = 0;
912   j->active = 0;
913   j->free = JOB_END;
914   j->v = 0;
915 }
916
917 /* --- @a_jobtablefinal@ --- *
918  *
919  * Arguments:   @admin_jobtable *j@ = pointer to job table
920  *
921  * Returns:     ---
922  *
923  * Use:         Closes down a job table.
924  */
925
926 static void a_jobtablefinal(admin_jobtable *j)
927 {
928   admin_svcop *svc, *ssvc;
929
930   for (svc = j->active; svc; svc = ssvc) {
931     ssvc = svc->next;
932     a_bgfail(&svc->bg, "provider-failed", A_END);
933     a_bgrelease(&svc->bg);
934   }
935   if (j->v) xfree(j->v);
936 }
937
938 /*----- Services infrastructure -------------------------------------------*/
939
940 /* --- @a_svcfind@ --- *
941  *
942  * Arguments:   @admin *a@ = the requesting client
943  *              @const char *name@ = service name wanted
944  *
945  * Returns:     The service requested, or null.
946  *
947  * Use:         Finds a service; reports an error if the service couldn't be
948  *              found.
949  */
950
951 static admin_service *a_svcfind(admin *a, const char *name)
952 {
953   admin_service *svc;
954
955   if ((svc = sym_find(&a_svcs, name, -1, 0, 0)) == 0) {
956     a_fail(a, "unknown-service", "%s", name, A_END);
957     return (0);
958   }
959   return (svc);
960 }
961
962 /* --- @a_svcunlink@ --- *
963  *
964  * Arguments:   @admin_service *svc@ = pointer to service structure
965  *
966  * Returns:     ---
967  *
968  * Use:         Unlinks the service from its provider, without issuing a
969  *              message or freeing the structure.  The version string is
970  *              freed, however.
971  */
972
973 static void a_svcunlink(admin_service *svc)
974 {
975   if (svc->next)
976     svc->next->prev = svc->prev;
977   if (svc->prev)
978     svc->prev->next = svc->next;
979   else
980     svc->prov->svcs = svc->next;
981   xfree(svc->version);
982 }
983
984 /* --- @a_svcrelease@ --- *
985  *
986  * Arguments:   @admin_service *svc@ = pointer to service structure
987  *
988  * Returns:     ---
989  *
990  * Use:         Releases a service and frees its structure.
991  */
992
993 static void a_svcrelease(admin_service *svc)
994 {
995   a_notify("SVCRELEASE", "%s", SYM_NAME(svc), A_END);
996   a_svcunlink(svc);
997   sym_remove(&a_svcs, svc);
998 }
999
1000 /*----- Name resolution operations ----------------------------------------*/
1001
1002 /* --- @a_resolved@ --- *
1003  *
1004  * Arguments:   @struct hostent *h@ = pointer to resolved hostname
1005  *              @void *v@ = pointer to resolver operation
1006  *
1007  * Returns:     ---
1008  *
1009  * Use:         Handles a completed name resolution.
1010  */
1011
1012 static void a_resolved(struct hostent *h, void *v)
1013 {
1014   admin_resop *r = v;
1015
1016   T( trace(T_ADMIN, "admin: resop %s resolved", BGTAG(r)); )
1017   QUICKRAND;
1018   if (!h) {
1019     a_bgfail(&r->bg, "resolve-error", "%s", r->addr, A_END);
1020     r->func(r, ARES_FAIL);
1021   } else {
1022     memcpy(&r->sa.sin.sin_addr, h->h_addr, sizeof(struct in_addr));
1023     r->func(r, ARES_OK);
1024   }
1025   sel_rmtimer(&r->t);
1026   xfree(r->addr);
1027   a_bgrelease(&r->bg);
1028 }
1029
1030 /* --- @a_restimer@ --- *
1031  *
1032  * Arguments:   @struct timeval *tv@ = timer
1033  *              @void *v@ = pointer to resolver operation
1034  *
1035  * Returns:     ---
1036  *
1037  * Use:         Times out a resolver.
1038  */
1039
1040 static void a_restimer(struct timeval *tv, void *v)
1041 {
1042   admin_resop *r = v;
1043
1044   T( trace(T_ADMIN, "admin: resop %s timeout", BGTAG(r)); )
1045   a_bgfail(&r->bg, "resolver-timeout", "%s", r->addr, A_END);
1046   r->func(r, ARES_FAIL);
1047   bres_abort(&r->r);
1048   xfree(r->addr);
1049   a_bgrelease(&r->bg);
1050 }
1051
1052 /* --- @a_rescancel@ --- *
1053  *
1054  * Arguments:   @admin_bgop *bg@ = background operation
1055  *
1056  * Returns:     ---
1057  *
1058  * Use:         Cancels an add operation.
1059  */
1060
1061 static void a_rescancel(admin_bgop *bg)
1062 {
1063   admin_resop *r = (admin_resop *)bg;
1064
1065   T( trace(T_ADMIN, "admin: cancel resop %s", BGTAG(r)); )
1066   r->func(r, ARES_FAIL);
1067   sel_rmtimer(&r->t);
1068   xfree(r->addr);
1069   bres_abort(&r->r);
1070 }
1071
1072 /* --- @a_resolve@ --- *
1073  *
1074  * Arguments:   @admin *a@ = administration connection
1075  *              @admin_resop *r@ = resolver operation to run
1076  *              @const char *tag@ = background operation tag
1077  *              @void (*func)(struct admin_resop *, int@ = handler function
1078  *              @unsigned ac@ = number of remaining arguments
1079  *              @char *av[]@ = pointer to remaining arguments
1080  *
1081  * Returns:     ---
1082  *
1083  * Use:         Cranks up a resolver job.
1084  */
1085
1086 static void a_resolve(admin *a, admin_resop *r, const char *tag,
1087                       void (*func)(struct admin_resop *, int),
1088                       unsigned ac, char *av[])
1089 {
1090   struct timeval tv;
1091   unsigned long pt;
1092   char *p;
1093   int i = 0;
1094
1095   /* --- Fill in the easy bits of address --- */
1096
1097   r->bg.tag = "<starting>";
1098   r->addr = 0;
1099   r->func = func;
1100   if (mystrieq(av[i], "inet")) i++;
1101   if (ac - i != 1 && ac - i != 2) {
1102     a_fail(a, "bad-addr-syntax", "[inet] ADDRESS [PORT]", A_END);
1103     goto fail;
1104   }
1105   r->sa.sin.sin_family = AF_INET;
1106   r->sasz = sizeof(r->sa.sin);
1107   r->addr = xstrdup(av[i]);
1108   if (!av[i + 1])
1109     pt = TRIPE_PORT;
1110   else {
1111     pt = strtoul(av[i + 1], &p, 0);
1112     if (*p) {
1113       struct servent *s = getservbyname(av[i + 1], "udp");
1114       if (!s) {
1115         a_fail(a, "unknown-service", "%s", av[i + 1], A_END);
1116         goto fail;
1117       }
1118       pt = ntohs(s->s_port);
1119     }
1120   }
1121   if (pt == 0 || pt >= 65536) {
1122     a_fail(a, "invalid-port", "%lu", pt, A_END);
1123     goto fail;
1124   }
1125   r->sa.sin.sin_port = htons(pt);
1126
1127   /* --- Report backgrounding --- *
1128    *
1129    * Do this for consistency of interface, even if we're going to get the
1130    * answer straight away.
1131    */
1132
1133   if (a_bgadd(a, &r->bg, tag, a_rescancel))
1134     goto fail;
1135   T( trace(T_ADMIN, "admin: %u, resop %s, hostname `%s'",
1136            a->seq, BGTAG(r), r->addr); )
1137
1138   /* --- If the name is numeric, do it the easy way --- */
1139
1140   if (inet_aton(av[i], &r->sa.sin.sin_addr)) {
1141     T( trace(T_ADMIN, "admin: resop %s done the easy way", BGTAG(r)); )
1142     func(r, ARES_OK);
1143     xfree(r->addr);
1144     a_bgrelease(&r->bg);
1145     return;
1146   }
1147
1148   /* --- Store everything for later and crank up the resolver --- */
1149
1150   gettimeofday(&tv, 0);
1151   tv.tv_sec += T_RESOLVE;
1152   sel_addtimer(&sel, &r->t, &tv, a_restimer, r);
1153   bres_byname(&r->r, r->addr, a_resolved, r);
1154   return;
1155
1156 fail:
1157   func(r, ARES_FAIL);
1158   if (r->addr) xfree(r->addr);
1159   xfree(r);
1160 }
1161
1162 /*----- Option parsing ----------------------------------------------------*/
1163
1164 #define OPTIONS(argc, argv, guts) do {                                  \
1165   char **o_av = argv;                                                   \
1166   for (;; o_av++) {                                                     \
1167     if (!*o_av)                                                         \
1168       break;                                                            \
1169     if (mystrieq(*o_av, "--")) {                                        \
1170       o_av++;                                                           \
1171       break;                                                            \
1172     }                                                                   \
1173     guts                                                                \
1174     if (**o_av == '-')                                                  \
1175       goto bad_syntax;                                                  \
1176     break;                                                              \
1177   }                                                                     \
1178   argc -= o_av - argv;                                                  \
1179   argv = o_av;                                                          \
1180 } while (0)
1181
1182 #define OPT(name, guts) if (mystrieq(*o_av, name)) { guts continue; }
1183
1184 #define OPTARG(name, arg, guts) OPT(name, {                             \
1185   const char *arg;                                                      \
1186   arg = *++o_av;                                                        \
1187   if (!arg) goto bad_syntax;                                            \
1188   guts                                                                  \
1189 })
1190
1191 #define OPTTIME(name, arg, guts) OPTARG(name, o_arg, {                  \
1192   long arg;                                                             \
1193   if ((arg = a_parsetime(o_arg)) < 0) {                                 \
1194     a_fail(a, "bad-time-spec", "%s", o_arg, A_END);                     \
1195     goto fail;                                                          \
1196   }                                                                     \
1197   guts                                                                  \
1198 })
1199
1200 /*----- Adding peers ------------------------------------------------------*/
1201
1202 /* --- @a_doadd@ --- *
1203  *
1204  * Arguments:   @admin_resop *r@ = resolver operation
1205  *              @int rc@ = how it worked
1206  *
1207  * Returns:     ---
1208  *
1209  * Use:         Handles a completed resolution.
1210  */
1211
1212 static void a_doadd(admin_resop *r, int rc)
1213 {
1214   admin_addop *add = (admin_addop *)r;
1215
1216   T( trace(T_ADMIN, "admin: done add op %s", BGTAG(add)); )
1217
1218   if (rc == ARES_OK) {
1219     add->peer.sasz = add->r.sasz;
1220     add->peer.sa = add->r.sa;
1221     if (p_findbyaddr(&add->r.sa))
1222       a_bgfail(&add->r.bg, "peer-addr-exists", "?ADDR", &add->r.sa, A_END);
1223     else if (p_find(add->peer.name))
1224       a_bgfail(&add->r.bg, "peer-exists", "%s", add->peer.name, A_END);
1225     else if (!p_create(&add->peer))
1226       a_bgfail(&add->r.bg, "peer-create-fail", "%s", add->peer.name, A_END);
1227     else
1228       a_bgok(&add->r.bg);
1229   }
1230
1231   if (add->peer.tag) xfree(add->peer.tag);
1232   xfree(add->peer.name);
1233 }
1234
1235 /* --- @acmd_add@ --- *
1236  *
1237  * Arguments:   @admin *a@ = connection which requested the addition
1238  *              @unsigned ac@ = argument count
1239  *              @char *av[]@ = pointer to the argument list
1240  *
1241  * Returns:     ---
1242  *
1243  * Use:         Adds a new peer.
1244  */
1245
1246 static void acmd_add(admin *a, unsigned ac, char *av[])
1247 {
1248   const char *tag = 0;
1249   admin_addop *add;
1250
1251   /* --- Set stuff up --- */
1252
1253   add = xmalloc(sizeof(*add));
1254   add->peer.name = 0;
1255   add->peer.tag = 0;
1256   add->peer.privtag = 0;
1257   add->peer.t_ka = 0;
1258   add->peer.tops = tun_default;
1259   add->peer.f = 0;
1260
1261   /* --- Parse options --- */
1262
1263   OPTIONS(ac, av, {
1264     OPTARG("-background", arg, { tag = arg; })
1265     OPTARG("-tunnel", arg, {
1266       unsigned i;
1267       for (i = 0;; i++) {
1268         if (!tunnels[i]) {
1269           a_fail(a, "unknown-tunnel", "%s", arg, A_END);
1270           goto fail;
1271         }
1272         if (mystrieq(arg, tunnels[i]->name)) {
1273           add->peer.tops = tunnels[i];
1274           break;
1275         }
1276       }
1277     })
1278     OPTTIME("-keepalive", t, { add->peer.t_ka = t; })
1279     OPT("-cork", { add->peer.f |= KXF_CORK; })
1280     OPTARG("-key", arg, {
1281       if (add->peer.tag)
1282         xfree(add->peer.tag);
1283       add->peer.tag = xstrdup(arg);
1284     })
1285     OPT("-mobile", { add->peer.f |= PSF_MOBILE; })
1286     OPTARG("-priv", arg, {
1287       if (add->peer.privtag)
1288         xfree(add->peer.privtag);
1289       add->peer.privtag = xstrdup(arg);
1290     })
1291   });
1292
1293   /* --- Make sure someone's not got there already --- */
1294
1295   if (!av[0] || !av[1])
1296     goto bad_syntax;
1297   if (p_find(*av)) {
1298     a_fail(a, "peer-exists", "%s", *av, A_END);
1299     goto fail;
1300   }
1301   add->peer.name = xstrdup(*av++);
1302   ac--;
1303
1304   /* --- Crank up the resolver --- */
1305
1306   a_resolve(a, &add->r, tag, a_doadd, ac, av);
1307   return;
1308
1309   /* --- Clearing up --- */
1310
1311 bad_syntax:
1312   a_fail(a, "bad-syntax", "add", "[OPTIONS] PEER ADDR ...", A_END);
1313 fail:
1314   if (add->peer.name) xfree(add->peer.name);
1315   if (add->peer.tag) xfree(add->peer.tag);
1316   if (add->peer.privtag) xfree(add->peer.privtag);
1317   xfree(add);
1318   return;
1319 }
1320
1321 /*----- Ping --------------------------------------------------------------*/
1322
1323 /* --- @a_pingcancel@ --- *
1324  *
1325  * Arguments:   @admin_bgop *bg@ = background operation block
1326  *
1327  * Returns:     ---
1328  *
1329  * Use:         Cancels a running ping.
1330  */
1331
1332 static void a_pingcancel(admin_bgop *bg)
1333 {
1334   admin_pingop *pg = (admin_pingop *)bg;
1335   T( trace(T_ADMIN, "admin: cancel ping op %s", BGTAG(pg)); )
1336   p_pingdone(&pg->ping, PING_NONOTIFY);
1337 }
1338
1339 /* --- @a_pong@ --- *
1340  *
1341  * Arguments:   @int rc@ = return code
1342  *              @void *v@ = ping operation block
1343  *
1344  * Returns:     ---
1345  *
1346  * Use:         Collects what happened to a ping message.
1347  */
1348
1349 static void a_pong(int rc, void *v)
1350 {
1351   admin_pingop *pg = v;
1352   struct timeval tv;
1353   double millis;
1354
1355   switch (rc) {
1356     case PING_OK:
1357       gettimeofday(&tv, 0);
1358       tv_sub(&tv, &tv, &pg->pingtime);
1359       millis = (double)tv.tv_sec * 1000 + (double)tv.tv_usec/1000;
1360       a_bginfo(&pg->bg, "ping-ok", "%.1f", millis, A_END);
1361       a_bgok(&pg->bg);
1362       break;
1363     case PING_TIMEOUT:
1364       a_bginfo(&pg->bg, "ping-timeout", A_END);
1365       a_bgok(&pg->bg);
1366       break;
1367     case PING_PEERDIED:
1368       a_bginfo(&pg->bg, "ping-peer-died", A_END);
1369       a_bgok(&pg->bg);
1370       break;
1371     default:
1372       abort();
1373   }
1374   T( trace(T_ADMIN, "admin: ponged ping op %s", BGTAG(pg)); )
1375   a_bgrelease(&pg->bg);
1376 }
1377
1378 /* --- @acmd_ping@, @acmd_eping@ --- *
1379  *
1380  * Arguments:   @admin *a@ = connection which requested the ping
1381  *              @unsigned ac@ = argument count
1382  *              @char *av[]@ = pointer to the argument list
1383  *
1384  * Returns:     ---
1385  *
1386  * Use:         Pings a peer.
1387  */
1388
1389 static void a_ping(admin *a, unsigned ac, char *av[],
1390                    const char *cmd, unsigned msg)
1391 {
1392   long t = T_PING;
1393   peer *p;
1394   admin_pingop *pg = 0;
1395   const char *tag = 0;
1396
1397   OPTIONS(ac, av, {
1398     OPTARG("-background", arg, { tag = arg; })
1399     OPTTIME("-timeout", arg, { t = arg; })
1400   });
1401   if (!*av || av[1]) goto bad_syntax;
1402   if ((p = a_findpeer(a, *av)) == 0)
1403     return;
1404   pg = xmalloc(sizeof(*pg));
1405   gettimeofday(&pg->pingtime, 0);
1406   if (a_bgadd(a, &pg->bg, tag, a_pingcancel))
1407     goto fail;
1408   T( trace(T_ADMIN, "admin: ping op %s: %s to %s",
1409            BGTAG(pg), cmd, p_name(p)); )
1410   if (p_pingsend(p, &pg->ping, msg, t, a_pong, pg)) {
1411     a_bgfail(&pg->bg, "ping-send-failed", A_END);
1412     a_bgrelease(&pg->bg);
1413   }
1414   return;
1415
1416 bad_syntax:
1417   a_fail(a, "bad-syntax", "%s", cmd, "[OPTIONS] PEER", cmd, A_END);
1418 fail:
1419   if (pg) xfree(pg);
1420   return;
1421 }
1422
1423 static void acmd_ping(admin *a, unsigned ac, char *av[])
1424   { a_ping(a, ac, av, "ping", MISC_PING); }
1425 static void acmd_eping(admin *a, unsigned ac, char *av[])
1426   { a_ping(a, ac, av, "eping", MISC_EPING); }
1427
1428 /*----- Service commands --------------------------------------------------*/
1429
1430 static void acmd_svcclaim(admin *a, unsigned ac, char *av[])
1431 {
1432   admin_service *svc;
1433   unsigned f;
1434
1435   svc = sym_find(&a_svcs, av[0], -1, sizeof(*svc), &f);
1436   if (f) {
1437     if (versioncmp(av[1], svc->version) <= 0) {
1438       a_fail(a,
1439              "service-exists",
1440              "%s", SYM_NAME(svc),
1441              "%s", svc->version,
1442              A_END);
1443       return;
1444     }
1445     a_write(svc->prov, "SVCCLAIM", 0, "%s", av[0], "%s", av[1], A_END);
1446     a_svcunlink(svc);
1447   }
1448   svc->prov = a;
1449   svc->version = xstrdup(av[1]);
1450   svc->next = a->svcs;
1451   svc->prev = 0;
1452   if (a->svcs) a->svcs->prev = svc;
1453   a->svcs = svc;
1454   a_notify("SVCCLAIM", "%s", SYM_NAME(svc), "%s", svc->version, A_END);
1455   a_ok(a);
1456 }
1457
1458 static void acmd_svcrelease(admin *a, unsigned ac, char *av[])
1459 {
1460   admin_service *svc;
1461
1462   if ((svc = a_svcfind(a, av[0])) == 0)
1463     return;
1464   if (svc->prov != a) {
1465     a_fail(a, "not-service-provider", "%s", SYM_NAME(svc), A_END);
1466     return;
1467   }
1468   a_svcrelease(svc);
1469   a_ok(a);
1470 }
1471
1472 static void acmd_svcensure(admin *a, unsigned ac, char *av[])
1473 {
1474   admin_service *svc;
1475
1476   if ((svc = a_svcfind(a, av[0])) == 0)
1477     return;
1478   if (av[1] && versioncmp(svc->version, av[1]) < 0) {
1479     a_fail(a, "service-too-old",
1480            "%s", SYM_NAME(svc),
1481            "%s", svc->version,
1482            A_END);
1483     return;
1484   }
1485   a_ok(a);
1486 }
1487
1488 static void acmd_svcquery(admin *a, unsigned ac, char *av[])
1489 {
1490   admin_service *svc;
1491
1492   if ((svc = a_svcfind(a, av[0])) != 0) {
1493     a_info(a, "name=%s", SYM_NAME(svc), "version=%s", svc->version, A_END);
1494     a_ok(a);
1495   }
1496 }
1497
1498 static void acmd_svclist(admin *a, unsigned ac, char *av[])
1499 {
1500   admin_service *svc;
1501   sym_iter i;
1502
1503   for (sym_mkiter(&i, &a_svcs); (svc = sym_next(&i)) != 0; )
1504     a_info(a, "%s", SYM_NAME(svc), "%s", svc->version, A_END);
1505   a_ok(a);
1506 }
1507
1508 static void a_canceljob(admin_bgop *bg)
1509 {
1510   admin_svcop *svc = (admin_svcop *)bg;
1511
1512   a_write(svc->prov, "SVCCANCEL", 0, "%s", a_jobidencode(svc), A_END);
1513   a_jobdestroy(svc);
1514 }
1515
1516 static void acmd_svcsubmit(admin *a, unsigned ac, char *av[])
1517 {
1518   const char *tag = 0;
1519   admin_service *svc;
1520   admin_svcop *svcop;
1521   const char *ver = 0;
1522   dstr d = DSTR_INIT;
1523
1524   OPTIONS(ac, av, {
1525     OPTARG("-background", arg, { tag = arg; })
1526     OPTARG("-version", arg, { ver = arg; })
1527   });
1528   if (!*av) goto bad_syntax;
1529   if ((svc = a_svcfind(a, *av)) == 0) goto fail;
1530   if (ver && versioncmp(svc->version, ver) < 0) {
1531     a_fail(a, "service-too-old",
1532            "%s", SYM_NAME(svc),
1533            "%s", svc->version,
1534            A_END);
1535     goto fail;
1536   }
1537   if ((svcop = a_jobcreate(svc->prov)) == 0) {
1538     a_fail(a, "provider-overloaded", A_END);
1539     goto fail;
1540   }
1541   if (a_bgadd(a, &svcop->bg, tag, a_canceljob)) {
1542     a_jobdestroy(svcop);
1543     goto fail;
1544   }
1545   a_write(svc->prov, "SVCJOB", 0,
1546           "%s", a_jobidencode(svcop),
1547           "?TOKENS", av,
1548           A_END);
1549   goto done;
1550
1551 bad_syntax:
1552   a_fail(a, "bad-syntax", "svcsubmit", "[OPTIONS] SERVICE ARGS...", A_END);
1553 fail:
1554 done:
1555   dstr_destroy(&d);
1556   return;
1557 }
1558
1559 static void a_replycmd(admin *a, const char *status, int donep, char *av[])
1560 {
1561   admin_svcop *svc;
1562
1563   if ((svc = a_jobiddecode(&a->j, av[0])) == 0) {
1564     a_fail(a, "unknown-jobid", av[0], A_END);
1565     return;
1566   }
1567   a_write(svc->bg.a, status, svc->bg.tag, "?TOKENS", av + 1, A_END);
1568   if (donep) {
1569     a_jobdestroy(svc);
1570     a_bgrelease(&svc->bg);
1571   }
1572   a_ok(a);
1573 }
1574
1575 static void acmd_svcok(admin *a, unsigned ac, char *av[])
1576   { a_replycmd(a, "OK", 1, av); }
1577 static void acmd_svcinfo(admin *a, unsigned ac, char *av[])
1578   { a_replycmd(a, "INFO", 0, av); }
1579 static void acmd_svcfail(admin *a, unsigned ac, char *av[])
1580   { a_replycmd(a, "FAIL", 1, av); }
1581
1582 /*----- Administration commands -------------------------------------------*/
1583
1584 /* --- Miscellaneous commands --- */
1585
1586 /* --- @traceish@ --- *
1587  *
1588  * Arguments:   @admin *a@ = connection to complain on
1589  *              @unsigned ac@ = number of arguments
1590  *              @char *av[]@ = vector of arguments
1591  *              @const char *what@ = what we're messing with
1592  *              @const trace_opt *tt@ = options table
1593  *              @unsigned *ff@ = where the flags are
1594  *
1595  * Returns:     Nonzero if anything changed.
1596  *
1597  * Use:         Guts of trace-ish commands like `trace' and `watch'.
1598  */
1599
1600 static int traceish(admin *a, unsigned ac, char *av[],
1601                     const char *what, const trace_opt *tt, unsigned *ff)
1602 {
1603   int ch = 0;
1604
1605   if (!ac || strcmp(av[0], "?") == 0) {
1606     const trace_opt *t;
1607     for (t = tt; t->ch; t++) {
1608       a_info(a, "*%c%c %s",
1609              t->ch, (*ff & t->f) == t->f ? '+' : ' ', t->help, A_END);
1610     }
1611   } else {
1612     unsigned sense = 1;
1613     unsigned f = *ff;
1614     const trace_opt *t;
1615     char *p = av[0];
1616
1617     while (*p) {
1618       switch (*p) {
1619         case '+': sense = 1; break;
1620         case '-': sense = 0; break;
1621         default:
1622           for (t = tt; t->ch; t++) {
1623             if (t->ch == *p) {
1624               if (sense) f |= t->f;
1625               else f &= ~t->f;
1626               goto tropt_ok;
1627             }
1628           }
1629           a_fail(a, "bad-%s-option", what, "%c", *p, A_END);
1630           return (0);
1631         tropt_ok:;
1632           break;
1633       }
1634       p++;
1635     }
1636     *ff = f;
1637     ch = 1;
1638   }
1639   a_ok(a);
1640   return (ch);
1641 }
1642
1643 #ifndef NTRACE
1644
1645 static void acmd_trace(admin *a, unsigned ac, char *av[])
1646 {
1647   if (traceish(a, ac, av, "trace", tr_opts, &tr_flags))
1648     trace_level(tr_flags);
1649 }
1650
1651 #endif
1652
1653 static void acmd_watch(admin *a, unsigned ac, char *av[])
1654   { traceish(a, ac, av, "watch", w_opts, &a->f); }
1655
1656 static void alertcmd(admin *a, unsigned f_and, unsigned f_eq,
1657                      const char *status, char *av[])
1658   { a_alert(f_and, f_eq, status, "USER", "?TOKENS", av, A_END); a_ok(a); }
1659 static void acmd_notify(admin *a, unsigned ac, char *av[])
1660   { alertcmd(a, AF_NOTE, AF_NOTE, "NOTE", av); }
1661 static void acmd_warn(admin *a, unsigned ac, char *av[])
1662   { alertcmd(a, AF_WARN, AF_WARN, "WARN", av); }
1663
1664 static void acmd_port(admin *a, unsigned ac, char *av[])
1665   { a_info(a, "%u", p_port(), A_END); a_ok(a); }
1666
1667 static void acmd_daemon(admin *a, unsigned ac, char *av[])
1668 {
1669   if (flags & F_DAEMON)
1670     a_fail(a, "already-daemon", A_END);
1671   else {
1672     a_notify("DAEMON", A_END);
1673     if (a_stdin)
1674       a_destroy(a_stdin);
1675     if (daemonize())
1676       a_fail(a, "daemon-error", "?ERRNO", A_END);
1677     else {
1678       flags |= F_DAEMON;
1679       a_ok(a);
1680     }
1681   }
1682 }
1683
1684 static void acmd_jobs(admin *a, unsigned ac, char *av[])
1685 {
1686   admin_bgop *bg;
1687
1688   for (bg = a->bg; bg; bg = bg->next) {
1689     assert(bg->tag);
1690     a_info(a, "%s", bg->tag, A_END);
1691   }
1692   a_ok(a);
1693 }
1694
1695 static void acmd_bgcancel(admin *a, unsigned ac, char *av[])
1696 {
1697   admin_bgop *bg;
1698
1699   if ((bg = a_bgfind(a, av[0])) == 0)
1700     a_fail(a, "unknown-tag", "%s", av[0], A_END);
1701   else {
1702     bg->cancel(bg);
1703     a_bgrelease(bg);
1704     a_ok(a);
1705   }
1706 }
1707
1708 static void acmd_algs(admin *a, unsigned ac, char *av[])
1709 {
1710   peer *p;
1711   const kdata *kd;
1712   const group *g;
1713   const algswitch *algs;
1714
1715   if (!ac)
1716     kd = master;
1717   else {
1718     if ((p = a_findpeer(a, av[0])) == 0) return;
1719     kd = p->kx.kpriv;
1720   }
1721   g = kd->g;
1722   algs = &kd->algs;
1723
1724   a_info(a,
1725          "kx-group=%s", g->ops->name,
1726          "kx-group-order-bits=%lu", (unsigned long)mp_bits(g->r),
1727          "kx-group-elt-bits=%lu", (unsigned long)g->nbits,
1728          A_END);
1729   a_info(a,
1730          "hash=%s", algs->h->name,
1731          "mgf=%s", algs->mgf->name,
1732          "hash-sz=%lu", (unsigned long)algs->h->hashsz,
1733          A_END);
1734   a_info(a,
1735          "bulk-transform=%s", algs->bulk->name,
1736          "bulk-overhead=%lu", (unsigned long)algs->bulk->overhead(algs),
1737          A_END);
1738   if (algs->c) {
1739     a_info(a,
1740            "cipher=%s", algs->c->name,
1741            "cipher-keysz=%lu", (unsigned long)algs->cksz,
1742            "cipher-blksz=%lu", (unsigned long)algs->c->blksz,
1743            A_END);
1744   }
1745   a_info(a,
1746          "cipher-data-limit=%lu", (unsigned long)algs->expsz,
1747          A_END);
1748   if (algs->m) {
1749     a_info(a,
1750            "mac=%s", algs->m->name,
1751            "mac-keysz=%lu", (unsigned long)algs->mksz,
1752            "mac-tagsz=%lu", (unsigned long)algs->tagsz,
1753            A_END);
1754   }
1755   if (algs->b) {
1756     a_info(a,
1757            "blkc=%.*s", strlen(algs->b->name) - 4, algs->b->name,
1758            "blkc-keysz=%lu", (unsigned long)algs->bksz,
1759            "blkc-blksz=%lu", (unsigned long)algs->b->blksz,
1760            A_END);
1761   }
1762   a_ok(a);
1763 }
1764
1765 static void acmd_list(admin *a, unsigned ac, char *av[])
1766 {
1767   FOREACH_PEER(p, {  a_info(a, "%s", p_name(p), A_END); });
1768   a_ok(a);
1769 }
1770
1771 static void acmd_ifname(admin *a, unsigned ac, char *av[])
1772 {
1773   peer *p;
1774
1775   if ((p = a_findpeer(a, av[0])) != 0) {
1776     a_info(a, "%s", p_ifname(p), A_END);
1777     a_ok(a);
1778   }
1779 }
1780
1781 static void acmd_setifname(admin *a, unsigned ac, char *av[])
1782 {
1783   peer *p;
1784
1785   if ((p = a_findpeer(a, av[0])) != 0) {
1786     a_notify("NEWIFNAME", "?PEER", p, "%s", p_ifname(p), "%s", av[1], A_END);
1787     p_setifname(p, av[1]);
1788     a_ok(a);
1789   }
1790 }
1791
1792 static void acmd_getchal(admin *a, unsigned ac, char *av[])
1793 {
1794   buf b;
1795
1796   buf_init(&b, buf_i, PKBUFSZ);
1797   c_new(&b);
1798   a_info(a, "?B64", BBASE(&b), (size_t)BLEN(&b), A_END);
1799   a_ok(a);
1800 }
1801
1802 static void acmd_checkchal(admin *a, unsigned ac, char *av[])
1803 {
1804   base64_ctx b64;
1805   buf b;
1806   dstr d = DSTR_INIT;
1807
1808   base64_init(&b64);
1809   base64_decode(&b64, av[0], strlen(av[0]), &d);
1810   base64_decode(&b64, 0, 0, &d);
1811   buf_init(&b, d.buf, d.len);
1812   if (c_check(&b) || BBAD(&b) || BLEFT(&b))
1813     a_fail(a, "invalid-challenge", A_END);
1814   else
1815     a_ok(a);
1816   dstr_destroy(&d);
1817 }
1818
1819 static void acmd_greet(admin *a, unsigned ac, char *av[])
1820 {
1821   peer *p;
1822   base64_ctx b64;
1823   dstr d = DSTR_INIT;
1824
1825   if ((p = a_findpeer(a, av[0])) != 0) {
1826     base64_init(&b64);
1827     base64_decode(&b64, av[1], strlen(av[1]), &d);
1828     base64_decode(&b64, 0, 0, &d);
1829     p_greet(p, d.buf, d.len);
1830     dstr_destroy(&d);
1831     a_ok(a);
1832   }
1833 }
1834
1835 static void acmd_addr(admin *a, unsigned ac, char *av[])
1836 {
1837   peer *p;
1838   const addr *ad;
1839
1840   if ((p = a_findpeer(a, av[0])) != 0) {
1841     ad = p_addr(p);
1842     assert(ad->sa.sa_family == AF_INET);
1843     a_info(a, "?ADDR", ad, A_END);
1844     a_ok(a);
1845   }
1846 }
1847
1848 static void acmd_peerinfo(admin *a, unsigned ac, char *av[])
1849 {
1850   peer *p;
1851   const peerspec *ps;
1852   const char *ptag;
1853
1854   if ((p = a_findpeer(a, av[0])) != 0) {
1855     ps = p_spec(p);
1856     a_info(a, "tunnel=%s", ps->tops->name, A_END);
1857     a_info(a, "key=%s", p_tag(p),
1858            "current-key=%s", p->kx.kpub->tag, A_END);
1859     if ((ptag = p_privtag(p)) == 0) ptag = "(default)";
1860     a_info(a, "private-key=%s", ptag,
1861            "current-private-key=%s", p->kx.kpriv->tag, A_END);
1862     a_info(a, "keepalive=%lu", ps->t_ka, A_END);
1863     a_ok(a);
1864   }
1865 }
1866
1867 static void acmd_servinfo(admin *a, unsigned ac, char *av[])
1868 {
1869   a_info(a, "implementation=edgeware-tripe", A_END);
1870   a_info(a, "version=%s", VERSION, A_END);
1871   a_info(a, "daemon=%s", BOOL(flags & F_DAEMON), A_END);
1872   a_ok(a);
1873 }
1874
1875 static void acmd_stats(admin *a, unsigned ac, char *av[])
1876 {
1877   peer *p;
1878   stats *st;
1879
1880   if ((p = a_findpeer(a, av[0])) == 0)
1881     return;
1882
1883   st = p_stats(p);
1884   a_info(a, "start-time=%s", timestr(st->t_start), A_END);
1885   a_info(a, "last-packet-time=%s", timestr(st->t_last), A_END);
1886   a_info(a, "last-keyexch-time=%s", timestr(st->t_kx), A_END);
1887   a_info(a, "packets-in=%lu", st->n_in, "bytes-in=%lu", st->sz_in, A_END);
1888   a_info(a,
1889          "packets-out=%lu", st->n_out,
1890          "bytes-out=%lu", st->sz_out,
1891          A_END);
1892   a_info(a,
1893          "keyexch-packets-in=%lu", st->n_kxin,
1894          "keyexch-bytes-in=%lu", st->sz_kxin,
1895          A_END);
1896   a_info(a,
1897          "keyexch-packets-out=%lu", st->n_kxout,
1898          "keyexch-bytes-out=%lu", st->sz_kxout,
1899          A_END);
1900   a_info(a,
1901          "ip-packets-in=%lu", st->n_ipin,
1902          "ip-bytes-in=%lu", st->sz_ipin,
1903          A_END);
1904   a_info(a,
1905          "ip-packets-out=%lu", st->n_ipout,
1906          "ip-bytes-out=%lu", st->sz_ipout,
1907          A_END);
1908   a_info(a, "rejected-packets=%lu", st->n_reject, A_END);
1909   a_ok(a);
1910 }
1911
1912 static void acmd_kill(admin *a, unsigned ac, char *av[])
1913 {
1914   peer *p;
1915
1916   if ((p = a_findpeer(a, av[0])) != 0) {
1917     p_destroy(p);
1918     a_ok(a);
1919   }
1920 }
1921
1922 static void acmd_forcekx(admin *a, unsigned ac, char *av[])
1923 {
1924   peer *p;
1925
1926   if ((p = a_findpeer(a, av[0])) != 0) {
1927     kx_start(&p->kx, 1);
1928     a_ok(a);
1929   }
1930 }
1931
1932 static void acmd_reload(admin *a, unsigned ac, char *av[])
1933   { p_keyreload(); a_ok(a); }
1934
1935 static void acmd_quit(admin *a, unsigned ac, char *av[])
1936 {
1937   a_warn("SERVER", "quit", "admin-request", A_END);
1938   a_ok(a);
1939   a_quit();
1940 }
1941
1942 static void acmd_version(admin *a, unsigned ac, char *av[])
1943 {
1944   a_info(a, "%s", PACKAGE, "%s", VERSION, A_END);
1945   a_ok(a);
1946 }
1947
1948 static void acmd_tunnels(admin *a, unsigned ac, char *av[])
1949 {
1950   int i;
1951
1952   for (i = 0; tunnels[i]; i++)
1953     a_info(a, "%s", tunnels[i]->name, A_END);
1954   a_ok(a);
1955 }
1956
1957 /* --- The command table and help --- */
1958
1959 typedef struct acmd {
1960   const char *name;
1961   const char *help;
1962   unsigned argmin, argmax;
1963   void (*func)(admin */*a*/, unsigned /*ac*/, char */*av*/[]);
1964 } acmd;
1965
1966 static void acmd_help(admin */*a*/, unsigned /*ac*/, char */*av*/[]);
1967
1968 static const acmd acmdtab[] = {
1969   { "add",      "[OPTIONS] PEER ADDR ...", 2,   0xffff, acmd_add },
1970   { "addr",     "PEER",                 1,      1,      acmd_addr },
1971   { "algs",     "[PEER]",               0,      1,      acmd_algs },
1972   { "bgcancel", "TAG",                  1,      1,      acmd_bgcancel },
1973   { "checkchal", "CHAL",                1,      1,      acmd_checkchal },
1974   { "daemon",   0,                      0,      0,      acmd_daemon },
1975   { "eping",    "[OPTIONS] PEER",       1,      0xffff, acmd_eping },
1976   { "forcekx",  "PEER",                 1,      1,      acmd_forcekx },
1977   { "getchal",  0,                      0,      0,      acmd_getchal },
1978   { "greet",    "PEER CHAL",            2,      2,      acmd_greet },
1979   { "help",     0,                      0,      0,      acmd_help },
1980   { "ifname",   "PEER",                 1,      1,      acmd_ifname },
1981   { "jobs",     0,                      0,      0,      acmd_jobs },
1982   { "kill",     "PEER",                 1,      1,      acmd_kill },
1983   { "list",     0,                      0,      0,      acmd_list },
1984   { "notify",   "MESSAGE ...",          1,      0xffff, acmd_notify },
1985   { "peerinfo", "PEER",                 1,      1,      acmd_peerinfo },
1986   { "ping",     "[OPTIONS] PEER",       1,      0xffff, acmd_ping },
1987   { "port",     0,                      0,      0,      acmd_port },
1988   { "quit",     0,                      0,      0,      acmd_quit },
1989   { "reload",   0,                      0,      0,      acmd_reload },
1990   { "servinfo", 0,                      0,      0,      acmd_servinfo },
1991   { "setifname", "PEER NEW-NAME",       2,      2,      acmd_setifname },
1992   { "svcclaim", "SERVICE VERSION",      2,      2,      acmd_svcclaim },
1993   { "svcensure", "SERVICE [VERSION]",   1,      2,      acmd_svcensure },
1994   { "svcfail",  "JOBID TOKENS...",      1,      0xffff, acmd_svcfail },
1995   { "svcinfo",  "JOBID TOKENS...",      1,      0xffff, acmd_svcinfo },
1996   { "svclist",  0,                      0,      0,      acmd_svclist },
1997   { "svcok",    "JOBID",                1,      1,      acmd_svcok },
1998   { "svcquery", "SERVICE",              1,      1,      acmd_svcquery },
1999   { "svcrelease", "SERVICE",            1,      1,      acmd_svcrelease },
2000   { "svcsubmit", "[OPTIONS] SERVICE TOKENS...",
2001                                         2,      0xffff, acmd_svcsubmit },
2002   { "stats",    "PEER",                 1,      1,      acmd_stats },
2003 #ifndef NTRACE
2004   { "trace",    "[OPTIONS]",            0,      1,      acmd_trace },
2005 #endif
2006   { "tunnels",  0,                      0,      0,      acmd_tunnels },
2007   { "version",  0,                      0,      0,      acmd_version },
2008   { "warn",     "MESSAGE ...",          1,      0xffff, acmd_warn },
2009   { "watch",    "[OPTIONS]",            0,      1,      acmd_watch },
2010   { 0,          0,                      0,      0,      0 }
2011 };
2012
2013 static void acmd_help(admin *a, unsigned ac, char *av[])
2014 {
2015   const acmd *c;
2016
2017   for (c = acmdtab; c->name; c++) {
2018     if (c->help)
2019       a_info(a, "%s", c->name, "*%s", c->help, A_END);
2020     else
2021       a_info(a, "%s", c->name, A_END);
2022   }
2023   a_ok(a);
2024 }
2025
2026 /*----- Connection handling -----------------------------------------------*/
2027
2028 /* --- @a_destroypending@ --- *
2029  *
2030  * Arguments:   ---
2031  *
2032  * Returns:     ---
2033  *
2034  * Use:         Destroys pending admin connections at a safe time.
2035  */
2036
2037 static void a_destroypending(void)
2038 {
2039   admin *a, *aa, *head;
2040   admin_bgop *bg, *bbg;
2041   admin_service *svc, *ssvc;
2042
2043   /* --- Destroy connections marked as pending --- *
2044    *
2045    * Slightly messy.  Killing clients may cause others to finally die.  Make
2046    * sure that they can be put on the list without clobbering anything or
2047    * getting lost.
2048    */
2049
2050   while (a_dead) {
2051     head = a_dead;
2052     a_dead = 0;
2053     for (a = head; a; a = aa) {
2054       aa = a->next;
2055       assert(a->f & AF_DEAD);
2056
2057       /* --- Report what we're doing --- */
2058
2059       T( trace(T_ADMIN, "admin: completing destruction of connection %u",
2060                a->seq); )
2061
2062       /* --- If this is the foreground client then shut down --- */
2063
2064       if (a->f & AF_FOREGROUND) {
2065         T( trace(T_ADMIN, "admin: foreground client quit: shutting down"); )
2066         a_warn("SERVER", "quit", "foreground-eof", A_END);
2067         a_quit();
2068       }
2069
2070       /* --- Abort any background jobs in progress --- */
2071
2072       for (bg = a->bg; bg; bg = bbg) {
2073         bbg = bg->next;
2074         bg->cancel(bg);
2075         if (bg->tag) xfree(bg->tag);
2076         xfree(bg);
2077       }
2078
2079       /* --- Release services I hold, and abort pending jobs --- */
2080
2081       for (svc = a->svcs; svc; svc = ssvc) {
2082         ssvc = svc->next;
2083         a_svcrelease(svc);
2084       }
2085       a_jobtablefinal(&a->j);
2086
2087       /* --- Close file descriptors and selectory --- */
2088
2089       selbuf_destroy(&a->b);
2090       if (a->b.reader.fd != a->w.fd) close(a->b.reader.fd);
2091       close(a->w.fd);
2092       if (a_stdin == a) a_stdin = 0;
2093
2094       /* --- Done --- */
2095
2096       DESTROY(a);
2097     }
2098   }
2099 }
2100
2101 /* --- @a_destroy@ --- *
2102  *
2103  * Arguments:   @admin *a@ = pointer to an admin block
2104  *
2105  * Returns:     ---
2106  *
2107  * Use:         Destroys an admin block.  This requires a certain amount of
2108  *              care.
2109  */
2110
2111 static void freequeue(oqueue *q)
2112 {
2113   obuf *o, *oo;
2114
2115   for (o = q->hd; o; o = oo) {
2116     oo = o->next;
2117     xfree(o);
2118   }
2119   q->hd = q->tl = 0;
2120 }
2121
2122 static void a_destroy(admin *a)
2123 {
2124   if (a->f & AF_DEAD)
2125     return;
2126
2127   if (a->next) a->next->prev = a->prev;
2128   if (a->prev) a->prev->next = a->next;
2129   else admins = a->next;
2130
2131   if (a->out.hd) sel_rmfile(&a->w);
2132   freequeue(&a->out);
2133
2134   a->f |= AF_DEAD;
2135   a->next = a_dead;
2136   a_dead = a;
2137
2138   T( trace(T_ADMIN, "admin: killing connection %u", a->seq); )
2139 }
2140
2141 /* --- @a_line@ --- *
2142  *
2143  * Arguments:   @char *p@ = pointer to the line read
2144  *              @size_t len@ = length of the line
2145  *              @void *vp@ = pointer to my admin block
2146  *
2147  * Returns:     ---
2148  *
2149  * Use:         Handles a line of input.
2150  */
2151
2152 static void a_line(char *p, size_t len, void *vp)
2153 {
2154   admin *a = vp;
2155   const acmd *c;
2156   char *av[16 + 1];
2157   size_t ac;
2158
2159   QUICKRAND;
2160   if (a->f & AF_DEAD)
2161     return;
2162   if (!p) {
2163     if (!a->bg)
2164       a_destroy(a);
2165     else {
2166       a->f |= AF_CLOSE;
2167       selbuf_disable(&a->b);
2168     }
2169     return;
2170   }
2171   ac = str_qsplit(p, av, N(av) - 1, 0, STRF_QUOTE);
2172   if (!ac)
2173     return;
2174   av[ac] = 0;
2175   for (c = acmdtab; c->name; c++) {
2176     if (mystrieq(av[0], c->name)) {
2177       ac--;
2178       if (c->argmin > ac || ac > c->argmax) {
2179         if (!c->help)
2180           a_fail(a, "bad-syntax", "%s", c->name, "", A_END);
2181         else
2182           a_fail(a, "bad-syntax", "%s", c->name, "%s", c->help, A_END);
2183       } else
2184         c->func(a, ac, av + 1);
2185       return;
2186     }
2187   }
2188   a_fail(a, "unknown-command", "%s", av[0], A_END);
2189 }
2190
2191 /* --- @a_create@ --- *
2192  *
2193  * Arguments:   @int fd_in, fd_out@ = file descriptors to use
2194  *              @unsigned f@ = initial flags to set
2195  *
2196  * Returns:     ---
2197  *
2198  * Use:         Creates a new admin connection.
2199  */
2200
2201 void a_create(int fd_in, int fd_out, unsigned f)
2202 {
2203   admin *a = CREATE(admin);
2204
2205   T( static unsigned seq = 0;
2206      a->seq = seq++; )
2207   T( trace(T_ADMIN, "admin: accepted connection %u", a->seq); )
2208   a->bg = 0;
2209   a->ref = 0;
2210   a->svcs = 0;
2211   a_jobtableinit(&a->j);
2212   a->f = f;
2213   if (fd_in == STDIN_FILENO) a_stdin = a;
2214   fdflags(fd_in, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
2215   if (fd_out != fd_in)
2216     fdflags(fd_out, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
2217   selbuf_init(&a->b, &sel, fd_in, a_line, a);
2218   sel_initfile(&sel, &a->w, fd_out, SEL_WRITE, a_flush, a);
2219   a->out.hd = a->out.tl = 0;
2220   a->next = admins;
2221   a->prev = 0;
2222   if (admins) admins->prev = a;
2223   admins = a;
2224 }
2225
2226 /* --- @a_accept@ --- *
2227  *
2228  * Arguments:   @int fd@ = file descriptor to accept
2229  *              @unsigned mode@ = what to do
2230  *              @void *v@ = uninteresting pointer
2231  *
2232  * Returns:     ---
2233  *
2234  * Use:         Accepts a new admin connection.
2235  */
2236
2237 static void a_accept(int fd, unsigned mode, void *v)
2238 {
2239   int nfd;
2240   struct sockaddr_un sun;
2241   socklen_t sz = sizeof(sun);
2242
2243   if ((nfd = accept(fd, (struct sockaddr *)&sun, &sz)) < 0) {
2244     if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK &&
2245         errno != ECONNABORTED && errno != EPROTO)
2246       a_warn("ADMIN", "accept-error", "?ERRNO", A_END);
2247     return;
2248   }
2249   a_create(nfd, nfd, 0);
2250 }
2251
2252 /* --- @a_preselect@ --- *
2253  *
2254  * Arguments:   ---
2255  *
2256  * Returns:     ---
2257  *
2258  * Use:         Informs the admin module that we're about to select again,
2259  *              and that it should do cleanup things it has delayed until a
2260  *              `safe' time.
2261  */
2262
2263 void a_preselect(void) { if (a_dead) a_destroypending(); }
2264
2265 /* --- @a_daemon@ --- *
2266  *
2267  * Arguments:   ---
2268  *
2269  * Returns:     ---
2270  *
2271  * Use:         Informs the admin module that it's a daemon.
2272  */
2273
2274 void a_daemon(void) { flags |= F_DAEMON; }
2275
2276 /* --- @a_init@ --- *
2277  *
2278  * Arguments:   @const char *name@ = socket name to create
2279  *              @uid_t u@ = user to own the socket
2280  *              @gid_t g@ = group to own the socket
2281  *              @mode_t m@ = permissions to set on the socket
2282  *
2283  * Returns:     ---
2284  *
2285  * Use:         Creates the admin listening socket.
2286  */
2287
2288 void a_init(const char *name, uid_t u, gid_t g, mode_t m)
2289 {
2290   int fd;
2291   int n = 5;
2292   struct sockaddr_un sun;
2293   struct sigaction sa;
2294   size_t sz;
2295   mode_t omask;
2296
2297   /* --- Create services table --- */
2298
2299   sym_create(&a_svcs);
2300
2301   /* --- Set up the socket address --- */
2302
2303   sz = strlen(name) + 1;
2304   if (sz > sizeof(sun.sun_path))
2305     die(EXIT_FAILURE, "socket name `%s' too long", name);
2306   BURN(sun);
2307   sun.sun_family = AF_UNIX;
2308   memcpy(sun.sun_path, name, sz);
2309   sz += offsetof(struct sockaddr_un, sun_path);
2310
2311   /* --- Attempt to bind to the socket --- */
2312
2313   omask = umask(0077);
2314 again:
2315   if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
2316     die(EXIT_FAILURE, "couldn't create socket: %s", strerror(errno));
2317   if (bind(fd, (struct sockaddr *)&sun, sz) < 0) {
2318     struct stat st;
2319     int e = errno;
2320     if (errno != EADDRINUSE) {
2321       die(EXIT_FAILURE, "couldn't bind to address `%s': %s",
2322           sun.sun_path, strerror(e));
2323     }
2324     if (!n)
2325       die(EXIT_FAILURE, "too many retries; giving up");
2326     n--;
2327     if (!connect(fd, (struct sockaddr *)&sun, sz)) {
2328       die(EXIT_FAILURE, "server already listening on admin socket `%s'",
2329           sun.sun_path);
2330     }
2331     if (errno != ECONNREFUSED)
2332       die(EXIT_FAILURE, "couldn't bind to address: %s", strerror(e));
2333     if (stat(sun.sun_path, &st)) {
2334       die(EXIT_FAILURE, "couldn't stat `%s': %s",
2335           sun.sun_path, strerror(errno));
2336     }
2337     if (!S_ISSOCK(st.st_mode))
2338       die(EXIT_FAILURE, "object `%s' isn't a socket", sun.sun_path);
2339     T( trace(T_ADMIN, "admin: stale socket found; removing it"); )
2340     unlink(sun.sun_path);
2341     close(fd);
2342     goto again;
2343   }
2344   if (chown(sun.sun_path, u, g)) {
2345     die(EXIT_FAILURE, "failed to set socket owner: %s",
2346         strerror(errno));
2347   }
2348   if (chmod(sun.sun_path, m)) {
2349     die(EXIT_FAILURE, "failed to set socket permissions: %s",
2350         strerror(errno));
2351   }
2352   umask(omask);
2353   fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
2354   if (listen(fd, 5))
2355     die(EXIT_FAILURE, "couldn't listen on socket: %s", strerror(errno));
2356
2357   /* --- Listen to the socket --- */
2358
2359   sel_initfile(&sel, &sock, fd, SEL_READ, a_accept, 0);
2360   sel_addfile(&sock);
2361   sockname = name;
2362   bres_init(&sel);
2363   T( trace_custom(a_trace, 0);
2364      trace(T_ADMIN, "admin: enabled custom tracing"); )
2365   flags |= F_INIT;
2366
2367   /* --- Set up signal handlers --- */
2368
2369   sig_add(&s_term, SIGTERM, a_sigdie, 0);
2370   sig_add(&s_hup, SIGHUP, a_sighup, 0);
2371   signal(SIGPIPE, SIG_IGN);
2372   sigaction(SIGINT, 0, &sa);
2373   if (sa.sa_handler != SIG_IGN)
2374     sig_add(&s_int, SIGINT, a_sigdie, 0);
2375 }
2376
2377 /*----- That's all, folks -------------------------------------------------*/