chiark / gitweb /
987de7abd385a98685beae6d4bd02bfdafa9f49c
[tripe] / admin.c
1 /* -*-c-*-
2  *
3  * $Id$
4  *
5  * Admin interface for configuration
6  *
7  * (c) 2001 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of Trivial IP Encryption (TrIPE).
13  *
14  * TrIPE is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  * 
19  * TrIPE is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * 
24  * You should have received a copy of the GNU General Public License
25  * along with TrIPE; if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 /*----- Header files ------------------------------------------------------*/
30
31 #include "tripe.h"
32
33 /*----- Global variables --------------------------------------------------*/
34
35 #ifndef NTRACE
36
37 const trace_opt tr_opts[] = {
38   { 't',        T_TUNNEL,       "tunnel events" },
39   { 'r',        T_PEER,         "peer events" },
40   { 'a',        T_ADMIN,        "admin interface" },
41   { 'p',        T_PACKET,       "packet contents" },
42   { 'c',        T_CRYPTO,       "crypto details" },
43   { 's',        T_KEYSET,       "symmetric keyset management" },
44   { 'x',        T_KEYEXCH,      "key exchange" },
45   { 'm',        T_KEYMGMT,      "key management" },
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   { 't',        AF_TRACE,       "trace messages" },
55   { 'n',        AF_NOTE,        "asynchronous notifications" },
56   { 'w',        AF_WARN,        "warnings" },
57   { 'A',        AF_ALLMSGS,     "all of the above" },
58   { 0,          0,              0 }
59 };
60
61 /*----- Static variables --------------------------------------------------*/
62
63 static admin *admins;
64 static sel_file sock;
65 static const char *sockname;
66 static unsigned flags = 0;
67 static admin *a_stdin = 0;
68 static sig s_term, s_int, s_hup;
69
70 #define F_DAEMON 1u
71 #define F_INIT 2u
72
73 #define T_RESOLVE SEC(30)
74 #define T_PING SEC(5)
75
76 static void a_destroy(admin */*a*/);
77 static void a_lock(admin */*a*/);
78 static void a_unlock(admin */*a*/);
79
80 #define BOOL(x) ((x) ? "t" : "nil")
81
82 /*----- Output functions --------------------------------------------------*/
83
84 /* --- @trywrite@ --- *
85  *
86  * Arguments:   @admin *a@ = pointer to an admin block
87  *              @const char *p@ = pointer to buffer to write
88  *              @size_t sz@ = size of data to write
89  *
90  * Returns:     The number of bytes written, or less than zero on error.
91  *
92  * Use:         Attempts to write data to a client.
93  */
94
95 static ssize_t trywrite(admin *a, const char *p, size_t sz)
96 {
97   ssize_t n, done = 0;
98
99 again:
100   if (!sz)
101     return (done);
102   n = write(a->w.fd, p, sz);
103   if (n > 0) {
104     done += n;
105     p += n;
106     sz -= n;
107     goto again;
108   }
109   if (n < 0) {
110     if (errno == EINTR)
111       goto again;
112     if (errno != EAGAIN && errno != EWOULDBLOCK) {
113       a_destroy(a);
114       a_warn("ADMIN client-read-error -- %s", strerror(errno));
115       return (-1);
116     }
117   }
118   return (done);
119 }
120
121 /* --- @doqueue@ -- *
122  *
123  * Arguments:   @oqueue *q@ = pointer to output queue
124  *              @const char *p@ = pointer to buffer to write
125  *              @size_t sz@ = size of buffer
126  *
127  * Returns:     Nonzero if the queue was previously empty.
128  *
129  * Use:         Queues data to be written later.
130  */
131
132 static int doqueue(oqueue *q, const char *p, size_t sz)
133 {
134   obuf *o;
135   int rc = 0;
136   size_t n;
137
138   o = q->tl;
139   if (!o)
140     rc = 1;
141   else if (o->p_in < o->buf + OBUFSZ)
142     goto noalloc;
143
144   do {
145     o = xmalloc(sizeof(obuf));
146     o->next = 0;
147     o->p_in = o->p_out = o->buf;
148     if (q->tl)
149       q->tl->next = o;
150     else
151       q->hd = o;
152     q->tl = o;
153
154   noalloc:
155     n = o->buf + OBUFSZ - o->p_in;
156     if (n > sz)
157       n = sz;
158     memcpy(o->p_in, p, n);
159     o->p_in += n;
160     p += n;
161     sz -= n;
162   } while (sz);
163
164   return (rc);
165 }
166
167 /* --- @dosend@ --- *
168  *
169  * Arguemnts:   @admin *a@ = pointer to an admin block
170  *              @const char *p@ = pointer to buffer to write
171  *              @size_t sz@ = size of data to write
172  *
173  * Returns:     ---
174  *
175  * Use:         Sends data to an admin client.
176  */
177
178 static void dosend(admin *a, const char *p, size_t sz)
179 {
180   ssize_t n;
181
182   if (a->f & AF_DEAD)
183     return;
184   if (!a->out.hd) {
185     if ((n = trywrite(a, p, sz)) < 0)
186       return;
187     p += n;
188     sz -= n;
189     if (!sz)
190       return;
191   }
192   if (doqueue(&a->out, p, sz))
193     sel_addfile(&a->w);
194 }
195
196 /* --- @a_flush@ --- *
197  *
198  * Arguments:   @int fd@ = file descriptor
199  *              @unsigned mode@ = what's happening
200  *              @void *v@ = pointer to my admin block
201  *
202  * Returns:     ---
203  *
204  * Use:         Flushes buffers when a client is ready to read again.
205  */
206
207 static void a_flush(int fd, unsigned mode, void *v)
208 {
209   admin *a = v;
210   obuf *o, *oo;
211   ssize_t n;
212
213   o = a->out.hd;
214   while (o) {
215     if ((n = trywrite(a, o->p_out, o->p_in - o->p_out)) < 0)
216       return;
217     o->p_out += n;
218     if (o->p_in < o->p_out)
219       break;
220     oo = o;
221     o = o->next;
222     xfree(oo);
223   }
224   a->out.hd = o;
225   if (!o) {
226     a->out.tl = 0;
227     sel_rmfile(&a->w);
228   }
229 }
230
231 /*----- Utility functions -------------------------------------------------*/
232
233 /* --- @a_write@, @a_vwrite@ --- *
234  *
235  * Arguments:   @admin *a@ = admin connection to write to
236  *              @const char *status@ = status code to report
237  *              @const char *tag@ = tag string, or null
238  *              @const char *fmt@ = pointer to format string
239  *              @va_list ap@ = arguments in list
240  *              @...@ = other arguments
241  *
242  * Returns:     ---
243  *
244  * Use:         Sends a message to an admin connection.
245  */
246
247 static void a_vwrite(admin *a, const char *status, const char *tag,
248                      const char *fmt, va_list ap)
249 {
250   dstr d = DSTR_INIT;
251
252   if (tag) dstr_puts(&d, "BG");
253   dstr_puts(&d, status);
254   if (tag) {
255     dstr_putc(&d, ' ');
256     dstr_puts(&d, tag);
257   }
258   if (fmt) {
259     dstr_putc(&d, ' ');
260     dstr_vputf(&d, fmt, &ap);
261   }
262   dstr_putc(&d, '\n');
263   dosend(a, d.buf, d.len);
264   dstr_destroy(&d);
265 }
266
267 static void a_write(admin *a, const char *status, const char *tag,
268                     const char *fmt, ...)
269 {
270   va_list ap;
271   va_start(ap, fmt);
272   a_vwrite(a, status, tag, fmt, ap);
273   va_end(ap);
274 }
275
276 /* --- @a_ok@, @a_info@, @a_fail@ --- *
277  *
278  * Arguments:   @admin *a@ = connection
279  *              @const char *fmt@ = format string
280  *              @...@ = other arguments
281  *
282  * Returns:     ---
283  *
284  * Use:         Convenience functions for @a_write@.
285  */
286
287 static void a_ok(admin *a) { a_write(a, "OK", 0, 0); }
288
289 static void a_info(admin *a, const char *fmt, ...)
290 {
291   va_list ap;
292   va_start(ap, fmt);
293   a_vwrite(a, "INFO", 0, fmt, ap);
294   va_end(ap);
295 }
296
297 static void a_fail(admin *a, const char *fmt, ...)
298 {
299   va_list ap;
300   va_start(ap, fmt);
301   a_vwrite(a, "FAIL", 0, fmt, ap);
302   va_end(ap);
303 }
304
305 /* --- @a_alert@, @a_valert@, @a_rawalert@ --- *
306  *
307  * Arguments:   @unsigned f_and, f_eq@ = filter for connections
308  *              @const char *status@ = status string
309  *              @const char *fmt@ = pointer to format string
310  *              @const char *p@ = pointer to raw string
311  *              @size_t sz@ = size of raw string
312  *              @va_list ap@ = arguments in list
313  *              @...@ = other arguments
314  *
315  * Returns:     ---
316  *
317  * Use:         Write a message to all admin connections matched by the given
318  *              filter.
319  */
320
321 static void a_rawalert(unsigned f_and, unsigned f_eq, const char *status,
322                        const char *p, size_t sz)
323 {
324   admin *a, *aa;
325   dstr d = DSTR_INIT;
326   
327   if (!(flags & F_INIT))
328     return;
329   dstr_puts(&d, status);
330   if (p) {
331     dstr_putc(&d, ' ');
332     dstr_putm(&d, p, sz);
333   }
334   dstr_putc(&d, '\n');
335   p = d.buf;
336   sz = d.len;
337   for (a = admins; a; a = aa) {
338     aa = a->next;
339     if ((a->f & f_and) == f_eq)
340       dosend(a, d.buf, d.len);
341   }
342   dstr_destroy(&d);
343 }
344
345 static void a_valert(unsigned f_and, unsigned f_eq, const char *tag,
346                      const char *fmt, va_list ap)
347 {
348   dstr d = DSTR_INIT;
349
350   if (!(flags & F_INIT))
351     return;
352   if (fmt)
353     dstr_vputf(&d, fmt, &ap);
354   a_rawalert(f_and, f_eq, tag, fmt ? d.buf : 0, fmt ? d.len : 0);
355   dstr_destroy(&d);
356 }
357
358 #if 0 /*unused*/
359 static void a_alert(unsigned f_and, unsigned f_eq, const char *tag,
360                     const char *fmt, ...)
361 {
362   va_list ap;
363   va_start(ap, fmt);
364   a_valert(f_and, f_eq, tag, fmt, ap);
365   va_end(ap);
366 }
367 #endif
368
369 /* --- @a_warn@ --- *
370  *
371  * Arguments:   @const char *fmt@ = pointer to format string
372  *              @...@ = other arguments
373  *
374  * Returns:     ---
375  *
376  * Use:         Informs all admin connections of a warning.
377  */
378
379 void a_warn(const char *fmt, ...)
380 {
381   va_list ap;
382
383   va_start(ap, fmt);
384   if (flags & F_INIT)
385     a_valert(0, 0, "WARN", fmt, ap);
386   else {
387     fprintf(stderr, "%s: ", QUIS);
388     vfprintf(stderr, fmt, ap);
389     fputc('\n', stderr);
390   }
391   va_end(ap);
392 }
393
394 /* --- @a_trace@ --- *
395  *
396  * Arguments:   @const char *p@ = pointer to a buffer
397  *              @size_t sz@ = size of the buffer
398  *              @void *v@ = uninteresting pointer
399  *
400  * Returns:     ---
401  *
402  * Use:         Custom trace output handler.  Sends trace messages to
403  *              interested admin connections.
404  */
405
406 #ifndef NTRACE
407 static void a_trace(const char *p, size_t sz, void *v)
408   { a_rawalert(AF_TRACE, AF_TRACE, "TRACE", p, sz); }
409 #endif
410
411 /* --- @a_notify@ --- *
412  *
413  * Arguments:   @const char *fmt@ = pointer to format string
414  *              @...@ = other arguments
415  *
416  * Returns:     ---
417  *
418  * Use:         Sends a notification to interested admin connections.
419  */
420
421 void a_notify(const char *fmt, ...)
422 {
423   va_list ap;
424
425   va_start(ap, fmt);
426   a_valert(AF_NOTE, AF_NOTE, "NOTE", fmt, ap);
427   va_end(ap);
428 }
429
430 /* --- @a_quit@ --- *
431  *
432  * Arguments:   ---
433  *
434  * Returns:     ---
435  *
436  * Use:         Shuts things down nicely.
437  */
438
439 void a_quit(void)
440 {
441   peer *p;
442
443   close(sock.fd);
444   unlink(sockname);
445   while ((p = p_first()) != 0)
446     p_destroy(p);
447   exit(0);
448 }
449
450 /* --- @a_sigdie@ --- *
451  *
452  * Arguments:   @int sig@ = signal number
453  *              @void *v@ = an uninteresting argument
454  *
455  * Returns:     ---
456  *
457  * Use          Shuts down on receipt of a fatal signal.
458  */
459
460 static void a_sigdie(int sig, void *v)
461 {
462   char *p;
463   char buf[20];
464
465   switch (sig) {
466     case SIGTERM:       p = "SIGTERM"; break;
467     case SIGINT:        p = "SIGINT"; break;
468     default:
469       sprintf(buf, "%i", sig);
470       p = buf;
471       break;
472   }
473   a_warn("SERVER quit signal %s", p);
474   a_quit();
475 }
476
477 /* --- @a_sighup@ --- *
478  *
479  * Arguments:   @int sig@ = signal number
480  *              @void *v@ = an uninteresting argument
481  *
482  * Returns:     ---
483  *
484  * Use          Logs a message about SIGHUP not being useful.
485  */
486
487 static void a_sighup(int sig, void *v)
488 {
489   a_warn("SERVER ignore signal SIGHUP");
490 }
491
492 /* --- @a_parsetime@ --- *
493  *
494  * Arguments;   @const char *p@ = time string to parse
495  *
496  * Returns:     Time in seconds, or @< 0@ on error.
497  */
498
499 static long a_parsetime(const char *p)
500 {
501   char *q;
502   long t = strtol(p, &q, 0);
503
504   switch (*q) {
505     case 'd': t *= 24;
506     case 'h': t *= 60;
507     case 'm': t *= 60;
508     case 's': if (q[1] != 0)
509     default:    t = -1;
510     case 0:   break;
511   }
512   return (t);    
513 }
514
515 /*----- Backgrounded operations -------------------------------------------*/
516
517 #define BGTAG(bg)                                                       \
518   (((admin_bgop *)(bg))->tag ? ((admin_bgop *)(bg))->tag : "<foreground>")
519
520 /* --- @a_bgrelease@ --- *
521  *
522  * Arguments:   @admin_bgop *bg@ = backgrounded operation
523  *
524  * Returns:     ---
525  *
526  * Use:         Removes a backgrounded operation from the queue, since
527  *              (presumably) it's done.
528  */
529
530 static void a_bgrelease(admin_bgop *bg)
531 {
532   admin *a = bg->a;
533
534   T( trace(T_ADMIN, "admin: release bgop %s", BGTAG(bg)); )
535   if (bg->tag) xfree(bg->tag);
536   else selbuf_enable(&a->b);
537   if (bg->next) bg->next->prev = bg->prev;
538   if (bg->prev) bg->prev->next = bg->next;
539   else a->bg = bg->next;
540   xfree(bg);
541   if (a->f & AF_CLOSE) a_destroy(a);
542   a_unlock(a);
543 }
544
545 /* --- @a_bgok@, @a_bginfo@, @a_bgfail@ --- *
546  *
547  * Arguments:   @admin_bgop *bg@ = backgrounded operation
548  *              @const char *fmt@ = format string
549  *              @...@ = other arguments
550  *
551  * Returns:     ---
552  *
553  * Use:         Convenience functions for @a_write@.
554  */
555
556 static void a_bgok(admin_bgop *bg)
557   { a_write(bg->a, "OK", bg->tag, 0); }
558
559 static void a_bginfo(admin_bgop *bg, const char *fmt, ...)
560 {
561   va_list ap;
562   va_start(ap, fmt);
563   a_vwrite(bg->a, "INFO", bg->tag, fmt, ap);
564   va_end(ap);
565 }
566
567 static void a_bgfail(admin_bgop *bg, const char *fmt, ...)
568 {
569   va_list ap;
570   va_start(ap, fmt);
571   a_vwrite(bg->a, "FAIL", bg->tag, fmt, ap);
572   va_end(ap);
573 }
574
575 /* --- @a_bgadd@ --- *
576  *
577  * Arguments:   @admin *a@ = administration connection
578  *              @admin_bgop *bg@ = pointer to background operation
579  *              @const char *tag@ = background tag, or null for foreground
580  *              @void (*cancel)(admin_bgop *)@ = cancel function
581  *
582  * Returns:     ---
583  *
584  * Use:         Links a background job into the list.
585  */
586
587 static void a_bgadd(admin *a, admin_bgop *bg, const char *tag,
588                     void (*cancel)(admin_bgop *))
589 {
590   if (tag)
591     bg->tag = xstrdup(tag);
592   else {
593     bg->tag = 0;
594     selbuf_disable(&a->b);
595   }
596   bg->a = a;
597   bg->cancel = cancel;
598   bg->next = a->bg;
599   bg->prev = 0;
600   if (a->bg) a->bg->prev = bg;
601   a->bg = bg;
602   a_lock(a);
603   T( trace(T_ADMIN, "admin: add bgop %s", BGTAG(bg)); )
604   if (tag) a_write(a, "DETACH", tag, 0);
605 }
606
607 /*----- Adding peers ------------------------------------------------------*/
608
609 /* --- @a_addfree@ --- *
610  *
611  * Arguments:   @admin_addop *add@ = operation block
612  *
613  * Returns:     ---
614  *
615  * Use:         Frees an add operation.
616  */
617
618 static void a_addfree(admin_addop *add)
619 {
620   T( trace(T_ADMIN, "admin: free add op %s", BGTAG(add)); )
621   if (add->peer.name) xfree(add->peer.name);
622   if (add->paddr) xfree(add->paddr);
623 }
624
625 /* --- @a_addcancel@ --- *
626  *
627  * Arguments:   @admin_bgop *bg@ = background operation
628  *
629  * Returns:     ---
630  *
631  * Use:         Cancels an add operation.
632  */
633
634 static void a_addcancel(admin_bgop *bg)
635 {
636   admin_addop *add = (admin_addop *)bg;
637
638   T( trace(T_ADMIN, "admin: cancel add op %s", BGTAG(add)); )
639   sel_rmtimer(&add->t);
640   bres_abort(&add->r);
641   a_addfree(add);
642 }
643
644 /* --- @a_doadd@ --- *
645  *
646  * Arguments:   @admin_addop *add@ = operation block
647  *
648  * Returns:     ---
649  *
650  * Use:         Does the peer add thing.
651  */
652
653 static void a_doadd(admin_addop *add)
654 {
655   if (p_find(add->peer.name))
656     a_bgfail(&add->bg, "peer-exists %s", add->peer.name);
657   else if (!p_create(&add->peer))
658     a_bgfail(&add->bg, "peer-create-fail %s", add->peer.name);
659   else
660     a_bgok(&add->bg);
661 }
662  
663 /* --- @a_addresolve@ --- *
664  *
665  * Arguments:   @struct hostent *h@ = pointer to resolved hostname
666  *              @void *v@ = pointer to add operation
667  *
668  * Returns:     ---
669  *
670  * Use:         Handles a completed name resolution.
671  */
672
673 static void a_addresolve(struct hostent *h, void *v)
674 {
675   admin_addop *add = v;
676
677   T( trace(T_ADMIN, "admin: add op %s resolved", BGTAG(add)); )
678   TIMER;
679   if (!h)
680     a_bgfail(&add->bg, "resolve-error %s", add->paddr);
681   else {
682     memcpy(&add->peer.sa.sin.sin_addr, h->h_addr, sizeof(struct in_addr));
683     a_doadd(add);
684   }
685   sel_rmtimer(&add->t);
686   a_addfree(add);
687   a_bgrelease(&add->bg);
688 }
689
690 /* --- @a_addtimer@ --- *
691  *
692  * Arguments:   @struct timeval *tv@ = timer
693  *              @void *v@ = pointer to add operation
694  *
695  * Returns:     ---
696  *
697  * Use:         Times out a resolver.
698  */
699
700 static void a_addtimer(struct timeval *tv, void *v)
701 {
702   admin_addop *add = v;
703
704   T( trace(T_ADMIN, "admin: add op %s timeout", BGTAG(add)); )
705   a_bgfail(&add->bg, "resolver-timeout %s\n", add->paddr);
706   bres_abort(&add->r);
707   a_addfree(add);
708   a_bgrelease(&add->bg);
709 }
710
711 /* --- @acmd_add@ --- *
712  *
713  * Arguments:   @admin *a@ = connection which requested the addition
714  *              @unsigned ac@ = argument count
715  *              @char *av[]@ = pointer to the argument list
716  *
717  * Returns:     ---
718  *
719  * Use:         Adds a new peer.
720  */
721
722 static void acmd_add(admin *a, unsigned ac, char *av[])
723 {
724   unsigned long pt;
725   struct timeval tv;
726   unsigned i, j;
727   char *p;
728   const char *tag = 0;
729   admin_addop *add = 0;
730
731   /* --- Make sure someone's not got there already --- */
732
733   if (p_find(av[0])) {
734     a_fail(a, "peer-exists %s", av[0]);
735     goto fail;
736   }
737
738   /* --- Set stuff up --- */
739
740   add = xmalloc(sizeof(*add));
741   add->peer.name = xstrdup(av[0]);
742   add->peer.t_ka = 0;
743   add->peer.tops = tun_default;
744   add->paddr = 0;
745
746   /* --- Parse options --- */
747
748   i = 1;
749   for (;;) {
750     if (!av[i])
751       goto bad_syntax;
752     if (mystrieq(av[i], "-background")) {
753       if (!av[++i]) goto bad_syntax;
754       tag = av[i];
755     } else if (mystrieq(av[i], "-tunnel")) {
756       if (!av[++i]) goto bad_syntax;
757       for (j = 0;; j++) {
758         if (!tunnels[j]) {
759           a_fail(a, "unknown-tunnel %s", av[i]);
760           return;
761         }
762         if (mystrieq(av[i], tunnels[j]->name)) {
763           add->peer.tops = tunnels[j];
764           break;
765         }
766       }
767     } else if (mystrieq(av[i], "-keepalive")) {
768       long t;
769       if (!av[++i]) goto bad_syntax;
770       if ((t = a_parsetime(av[i])) < 0) {
771         a_fail(a, "bad-time-spec %s", av[i]);
772         return;
773       }
774       add->peer.t_ka = t;
775     } else if (mystrieq(av[i], "--")) {
776       i++;
777       break;
778     } else
779       break;
780     i++;
781   }
782
783   /* --- Fill in the easy bits of address --- */
784
785   if (mystrieq(av[i], "inet")) i++;
786   if (ac - i != 2) {
787     a_fail(a, "bad-syntax -- add PEER [OPTIONS] [inet] ADDRESS PORT");
788     goto fail;
789   }
790   add->peer.sa.sin.sin_family = AF_INET;
791   add->peer.sasz = sizeof(add->peer.sa.sin);
792   add->paddr = xstrdup(av[i]);
793   pt = strtoul(av[i + 1], &p, 0);
794   if (*p) {
795     struct servent *s = getservbyname(av[i + 1], "udp");
796     if (!s) {
797       a_fail(a, "unknown-service %s", av[i + 1]);
798       goto fail;
799     }
800     pt = ntohs(s->s_port);
801   }
802   if (pt == 0 || pt >= 65536) {
803     a_fail(a, "invalid-port %lu", pt);
804     goto fail;
805   }
806   add->peer.sa.sin.sin_port = htons(pt);
807
808   /* --- Report backgrounding --- *
809    *
810    * Do this for consistency of interface, even if we're going to get the
811    * answer straight away.
812    */
813
814   a_bgadd(a, &add->bg, tag, a_addcancel);
815   T( trace(T_ADMIN, "admin: %u, add op %s resolving hostname `%s'",
816            a->seq, BGTAG(add), add->paddr); )
817
818   /* --- If the name is numeric, do it the easy way --- */
819   
820   if (inet_aton(av[i], &add->peer.sa.sin.sin_addr)) {
821     T( trace(T_ADMIN, "admin: add op %s done the easy way", BGTAG(add)); )
822     a_doadd(add);
823     a_addfree(add);
824     a_bgrelease(&add->bg);
825     return;
826   }
827
828   /* --- Store everything for later and crank up the resolver --- */
829
830   gettimeofday(&tv, 0);
831   tv.tv_sec += T_RESOLVE;
832   sel_addtimer(&sel, &add->t, &tv, a_addtimer, add);
833   bres_byname(&add->r, add->paddr, a_addresolve, add);
834   return;
835
836 bad_syntax:
837   a_fail(a, "bad-syntax -- add PEER [OPTIONS] ADDR ...");
838 fail:
839   if (add) {
840     a_addfree(add);
841     xfree(add);
842   }
843   return;
844 }
845
846 /*----- Ping --------------------------------------------------------------*/
847
848 /* --- @a_pingcancel@ --- *
849  *
850  * Arguments:   @admin_bgop *bg@ = background operation block
851  *
852  * Returns:     ---
853  *
854  * Use:         Cancels a running ping.
855  */
856
857 static void a_pingcancel(admin_bgop *bg)
858 {
859   admin_pingop *pg = (admin_pingop *)bg;
860   T( trace(T_ADMIN, "admin: cancel ping op %s", BGTAG(pg)); )
861   p_pingdone(&pg->ping, PING_NONOTIFY);
862 }
863
864 /* --- @a_pong@ --- *
865  *
866  * Arguments:   @int rc@ = return code
867  *              @void *v@ = ping operation block
868  *
869  * Returns:     ---
870  *
871  * Use:         Collects what happened to a ping message.
872  */
873
874 static void a_pong(int rc, void *v)
875 {
876   admin_pingop *pg = v;
877   struct timeval tv;
878   double millis;
879
880   switch (rc) {
881     case PING_OK:
882       gettimeofday(&tv, 0);
883       tv_sub(&tv, &tv, &pg->pingtime);
884       millis = (double)tv.tv_sec * 1000 + (double)tv.tv_usec/1000;
885       a_bginfo(&pg->bg, "ping-ok %.1f", millis);
886       a_bgok(&pg->bg);
887       break;
888     case PING_TIMEOUT:
889       a_bginfo(&pg->bg, "ping-timeout");
890       a_bgok(&pg->bg);
891       break;
892     case PING_PEERDIED:
893       a_bginfo(&pg->bg, "ping-peer-died");
894       a_bgok(&pg->bg);
895       break;
896     default:
897       abort();
898   }
899   T( trace(T_ADMIN, "admin: ponged ping op %s", BGTAG(pg)); )
900   a_bgrelease(&pg->bg);
901 }
902
903 /* --- @acmd_ping@, @acmd_eping@ --- *
904  *
905  * Arguments:   @admin *a@ = connection which requested the ping
906  *              @unsigned ac@ = argument count
907  *              @char *av[]@ = pointer to the argument list
908  *
909  * Returns:     ---
910  *
911  * Use:         Pings a peer.
912  */
913
914 static void a_ping(admin *a, unsigned ac, char *av[],
915                    const char *cmd, unsigned msg)
916 {
917   long t = T_PING;
918   int i;
919   peer *p;
920   admin_pingop *pg = 0;
921   const char *tag = 0;
922
923   i = 0;
924   for (;;) {
925     if (!av[i])
926       goto bad_syntax;
927     if (mystrieq(av[i], "-background")) {
928       if (!av[++i]) goto bad_syntax;
929       tag = av[i];
930     } else if (mystrieq(av[i], "-timeout")) {
931       if (!av[++i]) goto bad_syntax;
932       if ((t = a_parsetime(av[i])) < 0) {
933         a_fail(a, "bad-time-spec %s", av[i]);
934         return;
935       }
936     } else if (mystrieq(av[i], "--")) {
937       i++;
938       break;
939     } else
940       break;
941     i++;
942   }
943
944   if (!av[i]) goto bad_syntax;
945   if ((p = p_find(av[i])) == 0) {
946     a_fail(a, "unknown-peer %s", av[i]);
947     return;
948   }
949   pg = xmalloc(sizeof(*pg));
950   gettimeofday(&pg->pingtime, 0);
951   a_bgadd(a, &pg->bg, tag, a_pingcancel);
952   T( trace(T_ADMIN, "admin: ping op %s: %s to %s",
953            BGTAG(pg), cmd, p_name(p)); )
954   if (p_pingsend(p, &pg->ping, msg, t, a_pong, pg)) {
955     a_bgfail(&pg->bg, "ping-send-failed");
956     a_bgrelease(&pg->bg);
957   }
958   return;
959     
960 bad_syntax:
961   a_fail(a, "bad-syntax -- %s [OPTIONS] PEER", cmd);
962   return;
963 }
964
965 static void acmd_ping(admin *a, unsigned ac, char *av[])
966   { a_ping(a, ac, av, "ping", MISC_PING); }
967 static void acmd_eping(admin *a, unsigned ac, char *av[])
968   { a_ping(a, ac, av, "eping", MISC_EPING); }
969
970 /*----- Administration commands -------------------------------------------*/
971
972 /* --- Miscellaneous commands --- */
973
974 /* --- @traceish@ --- *
975  *
976  * Arguments:   @admin *a@ = connection to complain on
977  *              @unsigned ac@ = number of arguments
978  *              @char *av[]@ = vector of arguments
979  *              @const char *what@ = what we're messing with
980  *              @const trace_opt *tt@ = options table
981  *              @unsigned *ff@ = where the flags are
982  *
983  * Returns:     Nonzero if anything changed.
984  *
985  * Use:         Guts of trace-ish commands like `trace' and `watch'.
986  */
987
988 static int traceish(admin *a, unsigned ac, char *av[],
989                     const char *what, const trace_opt *tt, unsigned *ff)
990 {
991   int ch = 0;
992
993   if (!ac || strcmp(av[0], "?") == 0) {
994     const trace_opt *t;
995     for (t = tt; t->ch; t++) {
996       a_info(a, "%c%c %s",
997              t->ch, (*ff & t->f) == t->f ? '+' : ' ', t->help);
998     }
999   } else {
1000     unsigned sense = 1;
1001     unsigned f = *ff;
1002     const trace_opt *t;
1003     char *p = av[0];
1004
1005     while (*p) {
1006       switch (*p) {
1007         case '+': sense = 1; break;
1008         case '-': sense = 0; break;
1009         default:
1010           for (t = tt; t->ch; t++) {
1011             if (t->ch == *p) {
1012               if (sense) f |= t->f;
1013               else f &= ~t->f;
1014               goto tropt_ok;
1015             }
1016           }
1017           a_fail(a, "bad-%s-option %c", what, *p);
1018           return (0);
1019         tropt_ok:;
1020           break;
1021       }
1022       p++;
1023     }
1024     *ff = f;
1025     ch = 1;
1026   }
1027   a_ok(a);
1028   return (ch);
1029 }
1030
1031 #ifndef NTRACE
1032
1033 static void acmd_trace(admin *a, unsigned ac, char *av[])
1034 {
1035   if (traceish(a, ac, av, "trace", tr_opts, &tr_flags))
1036     trace_level(tr_flags);
1037 }
1038
1039 #endif
1040
1041 static void acmd_watch(admin *a, unsigned ac, char *av[])
1042 {
1043   traceish(a, ac, av, "watch", w_opts, &a->f);
1044 }
1045
1046 static void quotify(dstr *d, const char *p)
1047 {
1048   if (d->len)
1049     dstr_putc(d, ' ');
1050   if (*p && !p[strcspn(p, "\"' \t\n\v")])
1051     dstr_puts(d, p);
1052   else {
1053     dstr_putc(d, '\"');
1054     while (*p) {
1055       if (*p == '\\' || *p == '\"')
1056         dstr_putc(d, '\\');
1057       dstr_putc(d, *p++);
1058     }
1059     dstr_putc(d, '\"');
1060   }
1061 }
1062
1063 static void alertcmd(admin *a, unsigned f_and, unsigned f_eq,
1064                      const char *tag, unsigned ac, char *av[])
1065 {
1066   dstr d = DSTR_INIT;
1067   unsigned i;
1068
1069   dstr_puts(&d, "USER");
1070   for (i = 0; i < ac; i++)
1071     quotify(&d, av[i]);
1072   dstr_putz(&d);
1073   a_rawalert(f_and, f_eq, tag, d.buf, d.len);
1074   dstr_destroy(&d);
1075   a_ok(a);
1076 }
1077
1078 static void acmd_notify(admin *a, unsigned ac, char *av[])
1079   { alertcmd(a, AF_NOTE, AF_NOTE, "NOTE", ac, av); }
1080 static void acmd_warn(admin *a, unsigned ac, char *av[])
1081   { alertcmd(a, AF_WARN, AF_WARN, "WARN", ac, av); }
1082
1083 static void acmd_port(admin *a, unsigned ac, char *av[])
1084 {
1085   a_info(a, "%u", p_port());
1086   a_ok(a);
1087 }
1088
1089 static void acmd_daemon(admin *a, unsigned ac, char *av[])
1090 {
1091   if (flags & F_DAEMON)
1092     a_fail(a, "already-daemon");
1093   else {
1094     a_notify("DAEMON");
1095     if (a_stdin)
1096       a_destroy(a_stdin);
1097     if (u_daemon())
1098       a_fail(a, "daemon-error -- %s", strerror(errno));
1099     else {
1100       flags |= F_DAEMON;
1101       a_ok(a);
1102     }
1103   }
1104 }
1105
1106 static void acmd_list(admin *a, unsigned ac, char *av[])
1107 {
1108   peer *p;
1109   for (p = p_first(); p; p = p_next(p))
1110     a_info(a, "%s", p_name(p));
1111   a_ok(a);
1112 }
1113
1114 static void acmd_ifname(admin *a, unsigned ac, char *av[])
1115 {
1116   peer *p;
1117
1118   if ((p = p_find(av[0])) == 0)
1119     a_fail(a, "unknown-peer %s", av[0]);
1120   else {
1121     a_info(a, "%s", p_ifname(p));
1122     a_ok(a);
1123   }
1124 }
1125
1126 static void acmd_addr(admin *a, unsigned ac, char *av[])
1127 {
1128   peer *p;
1129   const addr *ad;
1130
1131   if ((p = p_find(av[0])) == 0)
1132     a_fail(a, "unknown-peer %s", av[0]);
1133   else {
1134     ad = p_addr(p);
1135     assert(ad->sa.sa_family == AF_INET);
1136     a_info(a, "INET %s %u",
1137             inet_ntoa(ad->sin.sin_addr),
1138             (unsigned)ntohs(ad->sin.sin_port));
1139     a_ok(a);
1140   }
1141 }
1142
1143 static void acmd_peerinfo(admin *a, unsigned ac, char *av[])
1144 {
1145   peer *p;
1146   const peerspec *ps;
1147
1148   if ((p = p_find(av[0])) == 0) {
1149     a_fail(a, "unknown-peer %s", av[0]);
1150     return;
1151   }
1152
1153   ps = p_spec(p);
1154   a_info(a, "tunnel=%s", ps->tops->name);
1155   a_info(a, "keepalive=%lu", ps->t_ka);
1156   a_ok(a);
1157 }
1158
1159 static void acmd_servinfo(admin *a, unsigned ac, char *av[])
1160 {
1161   a_info(a, "implementation=edgeware-tripe");
1162   a_info(a, "version=%s", VERSION);
1163   a_info(a, "daemon=%s", BOOL(flags & F_DAEMON));
1164   a_ok(a);
1165 }
1166
1167 static void acmd_stats(admin *a, unsigned ac, char *av[])
1168 {
1169   peer *p;
1170   stats *st;
1171
1172   if ((p = p_find(av[0])) == 0) {
1173     a_fail(a, "unknown-peer %s", av[0]);
1174     return;
1175   }
1176
1177   st = p_stats(p);
1178   a_info(a, "start-time=%s", timestr(st->t_start));
1179   a_info(a, "last-packet-time=%s", timestr(st->t_last));
1180   a_info(a, "last-keyexch-time=%s", timestr(st->t_kx));
1181   a_info(a, "packets-in=%lu bytes-in=%lu", st->n_in, st->sz_in);
1182   a_info(a, "packets-out=%lu bytes-out=%lu",
1183          st->n_out, st->sz_out);
1184   a_info(a, "keyexch-packets-in=%lu keyexch-bytes-in=%lu",
1185          st->n_kxin, st->sz_kxin);
1186   a_info(a, "keyexch-packets-out=%lu keyexch-bytes-out=%lu",
1187          st->n_kxout, st->sz_kxout);
1188   a_info(a, "ip-packets-in=%lu ip-bytes-in=%lu",
1189          st->n_ipin, st->sz_ipin);
1190   a_info(a, "ip-packets-out=%lu ip-bytes-out=%lu",
1191          st->n_ipout, st->sz_ipout);
1192   a_info(a, "rejected-packets=%lu", st->n_reject);
1193   a_ok(a);
1194 }
1195
1196 static void acmd_kill(admin *a, unsigned ac, char *av[])
1197 {
1198   peer *p;
1199   if ((p = p_find(av[0])) == 0)
1200     a_fail(a, "unknown-peer %s", av[0]);
1201   else {
1202     p_destroy(p);
1203     a_ok(a);
1204   }
1205 }
1206
1207 static void acmd_forcekx(admin *a, unsigned ac, char *av[])
1208 {
1209   peer *p;
1210   if ((p = p_find(av[0])) == 0)
1211     a_fail(a, "unknown-peer %s", av[0]);
1212   else {
1213     kx_start(&p->kx, 1);
1214     a_ok(a);
1215   }
1216 }
1217
1218 static void acmd_reload(admin *a, unsigned ac, char *av[])
1219   { p_keyreload(); a_ok(a); }
1220
1221 static void acmd_quit(admin *a, unsigned ac, char *av[])
1222 {
1223   a_warn("SERVER quit admin-request");
1224   a_ok(a);
1225   a_unlock(a);
1226   a_quit();
1227 }
1228
1229 static void acmd_version(admin *a, unsigned ac, char *av[])
1230 {
1231   a_info(a, "%s %s", PACKAGE, VERSION);
1232   a_ok(a);
1233 }
1234
1235 static void acmd_tunnels(admin *a, unsigned ac, char *av[])
1236 {
1237   int i;
1238   for (i = 0; tunnels[i]; i++)
1239     a_info(a, "%s", tunnels[i]->name);
1240   a_ok(a);
1241 }
1242
1243 /* --- The command table and help --- */
1244
1245 typedef struct acmd {
1246   const char *name;
1247   const char *help;
1248   unsigned argmin, argmax;
1249   void (*func)(admin */*a*/, unsigned /*ac*/, char */*av*/[]);
1250 } acmd;
1251
1252 static void acmd_help(admin */*a*/, unsigned /*ac*/, char */*av*/[]);
1253
1254 static const acmd acmdtab[] = {
1255   { "add",      "add PEER [OPTIONS] ADDR ...",
1256                                         2,      0xffff, acmd_add },
1257   { "addr",     "addr PEER",            1,      1,      acmd_addr },
1258   { "daemon",   "daemon",               0,      0,      acmd_daemon },
1259   { "eping",    "eping [OPTIONS] PEER", 1,      0xffff, acmd_eping },
1260   { "forcekx",  "forcekx PEER",         1,      1,      acmd_forcekx },
1261   { "help",     "help",                 0,      0,      acmd_help },
1262   { "ifname",   "ifname PEER",          1,      1,      acmd_ifname },
1263   { "kill",     "kill PEER",            1,      1,      acmd_kill },
1264   { "list",     "list",                 0,      0,      acmd_list },
1265   { "notify",   "notify MESSAGE ...",   1,      0xffff, acmd_notify },
1266   { "peerinfo", "peerinfo PEER",        1,      1,      acmd_peerinfo },
1267   { "ping",     "ping [OPTIONS] PEER",  1,      0xffff, acmd_ping },
1268   { "port",     "port",                 0,      0,      acmd_port },
1269   { "quit",     "quit",                 0,      0,      acmd_quit },
1270   { "reload",   "reload",               0,      0,      acmd_reload },
1271   { "servinfo", "servinfo",             0,      0,      acmd_servinfo },
1272   { "stats",    "stats PEER",           1,      1,      acmd_stats },
1273 #ifndef NTRACE
1274   { "trace",    "trace [OPTIONS]",      0,      1,      acmd_trace },
1275 #endif
1276   { "tunnels",  "tunnels",              0,      0,      acmd_tunnels },
1277   { "version",  "version",              0,      0,      acmd_version },
1278   { "warn",     "warn MESSAGE ...",     1,      0xffff, acmd_warn },
1279   { "watch",    "watch [OPTIONS]",      0,      1,      acmd_watch },
1280   { 0,          0,                      0,      0,      0 }
1281 };
1282
1283 static void acmd_help(admin *a, unsigned ac, char *av[])
1284 {
1285   const acmd *c;
1286   for (c = acmdtab; c->name; c++)
1287     a_info(a, "%s", c->help);
1288   a_ok(a);
1289 }
1290
1291 /*----- Connection handling -----------------------------------------------*/
1292
1293 /* --- @a_lock@ --- *
1294  *
1295  * Arguments:   @admin *a@ = pointer to an admin block
1296  *
1297  * Returns:     ---
1298  *
1299  * Use:         Locks an admin block so that it won't be destroyed
1300  *              immediately.
1301  */
1302
1303 static void a_lock(admin *a) { a->ref++; }
1304
1305 /* --- @a_dodestroy@ --- *
1306  *
1307  * Arguments:   @admin *a@ = pointer to an admin block
1308  *
1309  * Returns:     ---
1310  *
1311  * Use:         Actually does the legwork of destroying an admin block.
1312  */
1313
1314 static void a_dodestroy(admin *a)
1315 {
1316   admin_bgop *bg, *bbg;
1317
1318   T( trace(T_ADMIN, "admin: completing destruction of connection %u",
1319            a->seq); )
1320
1321   selbuf_destroy(&a->b);
1322   for (bg = a->bg; bg; bg = bbg) {
1323     bbg = bg->next;
1324     bg->cancel(bg);
1325     if (bg->tag) xfree(bg->tag);
1326     xfree(bg);
1327   }
1328   if (a->b.reader.fd != a->w.fd) close(a->b.reader.fd);
1329   close(a->w.fd);
1330
1331   if (a_stdin == a)
1332     a_stdin = 0;
1333   if (a->next)
1334     a->next->prev = a->prev;
1335   if (a->prev)
1336     a->prev->next = a->next;
1337   else
1338     admins = a->next;
1339   DESTROY(a);
1340 }
1341
1342 /* --- @a_unlock@ --- *
1343  *
1344  * Arguments:   @admin *a@ = pointer to an admin block
1345  *
1346  * Returns:     ---
1347  *
1348  * Use:         Unlocks an admin block, allowing its destruction.  This is
1349  *              also the second half of @a_destroy@.
1350  */
1351
1352 static void a_unlock(admin *a)
1353 {
1354   assert(a->ref);
1355   if (!--a->ref && (a->f & AF_DEAD))
1356     a_dodestroy(a);
1357 }
1358
1359 /* --- @a_destroy@ --- *
1360  *
1361  * Arguments:   @admin *a@ = pointer to an admin block
1362  *
1363  * Returns:     ---
1364  *
1365  * Use:         Destroys an admin block.  This requires a certain amount of
1366  *              care.
1367  */
1368
1369 static void freequeue(oqueue *q)
1370 {
1371   obuf *o, *oo;
1372
1373   for (o = q->hd; o; o = oo) {
1374     oo = o->next;
1375     xfree(o);
1376   }
1377   q->hd = q->tl = 0;
1378 }
1379
1380 static void a_destroy(admin *a)
1381 {
1382   /* --- Don't multiply destroy admin blocks --- */
1383
1384   if (a->f & AF_DEAD)
1385     return;
1386
1387   /* --- Make sure nobody expects it to work --- */
1388
1389   a->f |= AF_DEAD;
1390   T( trace(T_ADMIN, "admin: destroying connection %u", a->seq); )
1391
1392   /* --- Free the output buffers --- */
1393
1394   if (a->out.hd)
1395     sel_rmfile(&a->w);
1396   freequeue(&a->out);
1397
1398   /* --- If the block is locked, that's all we can manage --- */
1399
1400   if (!a->ref)
1401     a_dodestroy(a);
1402   T( else 
1403      trace(T_ADMIN, "admin: deferring destruction..."); )
1404 }
1405
1406 /* --- @a_line@ --- *
1407  *
1408  * Arguments:   @char *p@ = pointer to the line read
1409  *              @size_t len@ = length of the line
1410  *              @void *vp@ = pointer to my admin block
1411  *
1412  * Returns:     ---
1413  *
1414  * Use:         Handles a line of input.
1415  */
1416
1417 static void a_line(char *p, size_t len, void *vp)
1418 {
1419   admin *a = vp;
1420   const acmd *c;
1421   char *av[16];
1422   size_t ac;
1423
1424   TIMER;
1425   if (a->f & AF_DEAD)
1426     return;
1427   if (!p) {
1428     if (!a->bg)
1429       a_destroy(a);
1430     else {
1431       a->f |= AF_CLOSE;
1432       selbuf_disable(&a->b);
1433     }
1434     return;
1435   }
1436   ac = str_qsplit(p, av, 16, 0, STRF_QUOTE);
1437   if (!ac)
1438     return;
1439   for (c = acmdtab; c->name; c++) {
1440     if (mystrieq(av[0], c->name)) {
1441       ac--;
1442       if (c->argmin > ac || ac > c->argmax)
1443         a_fail(a, "bad-syntax -- %s", c->help);
1444       else {
1445         a_lock(a);
1446         c->func(a, ac, av + 1);
1447         a_unlock(a);
1448       }
1449       return;
1450     }
1451   }
1452   a_fail(a, "unknown-command %s", av[0]);
1453 }
1454
1455 /* --- @a_create@ --- *
1456  *
1457  * Arguments:   @int fd_in, fd_out@ = file descriptors to use
1458  *              @unsigned f@ = initial flags to set
1459  *
1460  * Returns:     ---
1461  *
1462  * Use:         Creates a new admin connection.
1463  */
1464
1465 void a_create(int fd_in, int fd_out, unsigned f)
1466 {
1467   admin *a = CREATE(admin);
1468
1469   T( static unsigned seq = 0;
1470      a->seq = seq++; )
1471   T( trace(T_ADMIN, "admin: accepted connection %u", a->seq); )
1472   a->bg = 0;
1473   a->f = f;
1474   if (fd_in == STDIN_FILENO) a_stdin = a;
1475   fdflags(fd_in, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
1476   if (fd_out != fd_in)
1477     fdflags(fd_out, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
1478   selbuf_init(&a->b, &sel, fd_in, a_line, a);
1479   sel_initfile(&sel, &a->w, fd_out, SEL_WRITE, a_flush, a);
1480   a->out.hd = a->out.tl = 0;
1481   a->next = admins;
1482   a->prev = 0;
1483   if (admins) admins->prev = a;
1484   admins = a;
1485 }
1486
1487 /* --- @a_accept@ --- *
1488  *
1489  * Arguments:   @int fd@ = file descriptor to accept
1490  *              @unsigned mode@ = what to do
1491  *              @void *v@ = uninteresting pointer
1492  *
1493  * Returns:     ---
1494  *
1495  * Use:         Accepts a new admin connection.
1496  */
1497
1498 static void a_accept(int fd, unsigned mode, void *v)
1499 {
1500   int nfd;
1501   struct sockaddr_un sun;
1502   size_t sz = sizeof(sun);
1503
1504   if ((nfd = accept(fd, (struct sockaddr *)&sun, &sz)) < 0) {
1505     if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK &&
1506         errno != ECONNABORTED && errno != EPROTO)
1507       a_warn("ADMIN accept-error -- %s", strerror(errno));
1508     return;
1509   }
1510   a_create(nfd, nfd, 0);
1511 }
1512
1513 /* --- @a_daemon@ --- *
1514  *
1515  * Arguments:   ---
1516  *
1517  * Returns:     ---
1518  *
1519  * Use:         Informs the admin module that it's a daemon.
1520  */
1521
1522 void a_daemon(void)
1523 {
1524   flags |= F_DAEMON;
1525 }
1526
1527 /* --- @a_init@ --- *
1528  *
1529  * Arguments:   @const char *name@ = socket name to create
1530  *
1531  * Returns:     ---
1532  *
1533  * Use:         Creates the admin listening socket.
1534  */
1535
1536 void a_init(const char *name)
1537 {
1538   int fd;
1539   int n = 5;
1540   struct sockaddr_un sun;
1541   struct sigaction sa;
1542   size_t sz;
1543
1544   /* --- Set up the socket address --- */
1545
1546   sz = strlen(name) + 1;
1547   if (sz > sizeof(sun.sun_path))
1548     die(EXIT_FAILURE, "socket name `%s' too long", name);
1549   BURN(sun);
1550   sun.sun_family = AF_UNIX;
1551   memcpy(sun.sun_path, name, sz);
1552   sz += offsetof(struct sockaddr_un, sun_path);
1553
1554   /* --- Attempt to bind to the socket --- */
1555
1556   umask(0077);
1557 again:
1558   if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
1559     die(EXIT_FAILURE, "couldn't create socket: %s", strerror(errno));
1560   if (bind(fd, (struct sockaddr *)&sun, sz) < 0) {
1561     struct stat st;
1562     int e = errno;
1563     if (errno != EADDRINUSE) {
1564       die(EXIT_FAILURE, "couldn't bind to address `%s': %s",
1565           sun.sun_path, strerror(e));
1566     }
1567     if (!n)
1568       die(EXIT_FAILURE, "too many retries; giving up");
1569     n--;
1570     if (!connect(fd, (struct sockaddr *)&sun, sz)) {
1571       die(EXIT_FAILURE, "server already listening on admin socket `%s'",
1572           sun.sun_path);
1573     }
1574     if (errno != ECONNREFUSED)
1575       die(EXIT_FAILURE, "couldn't bind to address: %s", strerror(e));
1576     if (stat(sun.sun_path, &st)) {
1577       die(EXIT_FAILURE, "couldn't stat `%s': %s",
1578           sun.sun_path, strerror(errno));
1579     }
1580     if (!S_ISSOCK(st.st_mode))
1581       die(EXIT_FAILURE, "object `%s' isn't a socket", sun.sun_path);
1582     T( trace(T_ADMIN, "admin: stale socket found; removing it"); )
1583     unlink(sun.sun_path);
1584     close(fd);
1585     goto again;
1586   }
1587   chmod(sun.sun_path, 0600);
1588   fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
1589   if (listen(fd, 5))
1590     die(EXIT_FAILURE, "couldn't listen on socket: %s", strerror(errno));
1591
1592   /* --- Listen to the socket --- */
1593
1594   sel_initfile(&sel, &sock, fd, SEL_READ, a_accept, 0);
1595   sel_addfile(&sock);
1596   sockname = name;
1597   bres_init(&sel);
1598   T( trace_custom(a_trace, 0);
1599      trace(T_ADMIN, "admin: enabled custom tracing"); )
1600   flags |= F_INIT;
1601
1602   /* --- Set up signal handlers --- */
1603
1604   sig_add(&s_term, SIGTERM, a_sigdie, 0);
1605   sig_add(&s_hup, SIGHUP, a_sighup, 0);
1606   signal(SIGPIPE, SIG_IGN);
1607   sigaction(SIGINT, 0, &sa);
1608   if (sa.sa_handler != SIG_IGN)
1609     sig_add(&s_int, SIGINT, a_sigdie, 0);
1610 }
1611
1612 /*----- That's all, folks -------------------------------------------------*/