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