chiark / gitweb /
Fix logrotate script.
[tripe] / admin.c
1 /* -*-c-*-
2  *
3  * $Id: admin.c,v 1.9 2004/04/03 10:22:10 mdw Exp $
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 /*----- Revision history --------------------------------------------------* 
30  *
31  * $Log: admin.c,v $
32  * Revision 1.9  2004/04/03 10:22:10  mdw
33  * Don't warn about boring errors from @accept@.
34  *
35  * Revision 1.8  2003/04/06 10:25:17  mdw
36  * Support Linux TUN/TAP device.  Fix some bugs.
37  *
38  * Revision 1.7  2002/01/13 14:57:33  mdw
39  * Track @lbuf@ and @dstr_vputf@ changes in mLib.
40  *
41  * Revision 1.6  2001/02/19 19:11:09  mdw
42  * Output buffering on admin connections.
43  *
44  * Revision 1.5  2001/02/16 21:22:51  mdw
45  * Support for displaying statistics.  Make client connections blocking, so
46  * that things don't get dropped.  (This might change again if I add
47  * buffering.)
48  *
49  * Revision 1.4  2001/02/06 09:34:53  mdw
50  * Change ERR response to FAIL for consistency with other programs.
51  *
52  * Revision 1.3  2001/02/04 01:17:12  mdw
53  * The `DAEMON' notification to stdout is replaced by a warning.  The
54  * `DAEMON' and `QUIT' command send `OK' on successful completion.  Put
55  * assignment of sequence number in a T(...) guard.
56  *
57  * Revision 1.2  2001/02/03 22:40:29  mdw
58  * Put timer information into the entropy pool when packets are received
59  * and on similar events.  Reseed the generator on the interval timer.
60  *
61  * Revision 1.1  2001/02/03 20:26:37  mdw
62  * Initial checkin.
63  *
64  */
65
66 /*----- Header files ------------------------------------------------------*/
67
68 #include "tripe.h"
69
70 /*----- Global variables --------------------------------------------------*/
71
72 #ifndef NTRACE
73
74 const trace_opt tr_opts[] = {
75   { 't',        T_TUNNEL,       "tunnel events" },
76   { 'r',        T_PEER,         "peer events" },
77   { 'a',        T_ADMIN,        "admin interface" },
78   { 'p',        T_PACKET,       "packet contents" },
79   { 'c',        T_CRYPTO,       "crypto details" },
80   { 's',        T_KEYSET,       "symmetric keyset management" },
81   { 'x',        T_KEYEXCH,      "key exchange" },
82   { 'm',        T_KEYMGMT,      "key management" },
83   { 'A',        T_ALL,          "all of the above" },
84   { 0,          0,              0 }
85 };
86
87 unsigned tr_flags = 0;
88 #endif
89
90 /*----- Static variables --------------------------------------------------*/
91
92 static admin *admins;
93 static sel_file sock;
94 static const char *sockname;
95 static unsigned flags = 0;
96 static admin *a_stdin = 0;
97 static sig s_term, s_int, s_hup;
98
99 #define F_DAEMON 1u
100 #define F_INIT 2u
101
102 #define T_RESOLVE SEC(30)
103
104 static void a_destroy(admin */*a*/);
105 static void a_lock(admin */*a*/);
106 static void a_unlock(admin */*a*/);
107
108 /*----- Output functions --------------------------------------------------*/
109
110 /* --- @trywrite@ --- *
111  *
112  * Arguments:   @admin *a@ = pointer to an admin block
113  *              @const char *p@ = pointer to buffer to write
114  *              @size_t sz@ = size of data to write
115  *
116  * Returns:     The number of bytes written, or less than zero on error.
117  *
118  * Use:         Attempts to write data to a client.
119  */
120
121 static ssize_t trywrite(admin *a, const char *p, size_t sz)
122 {
123   ssize_t n, done = 0;
124
125 again:
126   if (!sz)
127     return (done);
128   n = write(a->w.fd, p, sz);
129   if (n > 0) {
130     done += n;
131     p += n;
132     sz -= n;
133     goto again;
134   }
135   if (n < 0) {
136     if (errno == EINTR)
137       goto again;
138     if (errno != EAGAIN && errno != EWOULDBLOCK) {
139       a_destroy(a);
140       a_warn("disconnecting admin client due to write errors: %s",
141              strerror(errno));
142       return (-1);
143     }
144   }
145   return (done);
146 }
147
148 /* --- @dosend@ --- *
149  *
150  * Arguemnts:   @admin *a@ = pointer to an admin block
151  *              @const char *p@ = pointer to buffer to write
152  *              @size_t sz@ = size of data to write
153  *
154  * Returns:     ---
155  *
156  * Use:         Sends data to an admin client.
157  */
158
159 static void dosend(admin *a, const char *p, size_t sz)
160 {
161   ssize_t n;
162   obuf *o;
163
164   if (a->f & AF_DEAD)
165     return;
166
167   /* --- Try to send the data immediately --- */
168
169   if (!a->o_head) {
170     if ((n = trywrite(a, p, sz)) < 0)
171       return;
172     p += n;
173     sz -= n;
174     if (!sz)
175       return;
176   }
177         
178   /* --- Fill buffers with the data until it's all gone --- */
179
180   o = a->o_tail;
181   if (!o)
182     sel_addfile(&a->w);
183   else if (o->p_in < o->buf + OBUFSZ)
184     goto noalloc;
185
186   do {
187     o = xmalloc(sizeof(obuf));
188     o->next = 0;
189     o->p_in = o->p_out = o->buf;
190     if (a->o_tail)
191       a->o_tail->next = o;
192     else
193       a->o_head = o;
194     a->o_tail = o;
195
196   noalloc:
197     n = o->buf + OBUFSZ - o->p_in;
198     if (n > sz)
199       n = sz;
200     memcpy(o->p_in, p, n);
201     o->p_in += n;
202     p += n;
203     sz -= n;
204   } while (sz);
205 }
206
207 /* --- @a_flush@ --- *
208  *
209  * Arguments:   @int fd@ = file descriptor
210  *              @unsigned mode@ = what's happening
211  *              @void *v@ = pointer to my admin block
212  *
213  * Returns:     ---
214  *
215  * Use:         Flushes buffers when a client is ready to read again.
216  */
217
218 static void a_flush(int fd, unsigned mode, void *v)
219 {
220   admin *a = v;
221   obuf *o, *oo;
222   ssize_t n;
223
224   o = a->o_head;
225   while (o) {
226     if ((n = trywrite(a, o->p_out, o->p_in - o->p_out)) < 0)
227       return;
228     o->p_out += n;
229     if (o->p_in < o->p_out)
230       break;
231     oo = o;
232     o = o->next;
233     xfree(oo);
234   }
235   a->o_head = o;
236   if (!o) {
237     a->o_tail = 0;
238     sel_rmfile(&a->w);
239   }
240 }
241
242 /*----- Utility functions -------------------------------------------------*/
243
244 /* --- @a_write@ --- *
245  *
246  * Arguments:   @admin *a@ = admin connection to write to
247  *              @const char *fmt@ = pointer to format string
248  *              @...@ = other arguments
249  *
250  * Returns:     ---
251  *
252  * Use:         Sends a message to an admin connection.
253  */
254
255 static void a_write(admin *a, const char *fmt, ...)
256 {
257   va_list ap;
258   dstr d = DSTR_INIT;
259   va_start(ap, fmt);
260   dstr_vputf(&d, fmt, &ap);
261   va_end(ap);
262   dosend(a, d.buf, d.len);
263   dstr_destroy(&d);
264 }
265
266 /* --- @a_warn@ --- *
267  *
268  * Arguments:   @const char *fmt@ = pointer to format string
269  *              @...@ = other arguments
270  *
271  * Returns:     ---
272  *
273  * Use:         Informs all admin connections of a warning.
274  */
275
276 void a_warn(const char *fmt, ...)
277 {
278   va_list ap;
279   admin *a, *aa;
280   dstr d = DSTR_INIT;
281
282   if (flags & F_INIT)
283     dstr_puts(&d, "WARN ");
284   va_start(ap, fmt);
285   dstr_vputf(&d, fmt, &ap);
286   va_end(ap);
287   if (!(flags & F_INIT))
288     moan("%s", d.buf);
289   else {
290     dstr_putc(&d, '\n');
291     for (a = admins; a; a = aa) {
292       aa = a->next;
293       dosend(a, d.buf, d.len);
294     }
295   }
296   dstr_destroy(&d);
297 }
298
299 /* --- @a_trace@ --- *
300  *
301  * Arguments:   @const char *p@ = pointer to a buffer
302  *              @size_t sz@ = size of the buffer
303  *              @void *v@ = uninteresting pointer
304  *
305  * Returns:     ---
306  *
307  * Use:         Custom trace output handler.
308  */
309
310 #ifndef NTRACE
311 static void a_trace(const char *p, size_t sz, void *v)
312 {
313   dstr d = DSTR_INIT;
314   admin *a, *aa;
315
316   dstr_puts(&d, "TRACE ");
317   dstr_putm(&d, p, sz);
318   dstr_putc(&d, '\n');
319   for (a = admins; a; a = aa) {
320     aa = a->next;
321     dosend(a, d.buf, d.len);
322   }
323   dstr_destroy(&d);  
324 }
325 #endif
326
327 /* --- @a_quit@ --- *
328  *
329  * Arguments:   ---
330  *
331  * Returns:     ---
332  *
333  * Use:         Shuts things down nicely.
334  */
335
336 void a_quit(void)
337 {
338   close(sock.fd);
339   unlink(sockname);
340   exit(0);
341 }
342
343 /* --- @a_sigdie@ --- *
344  *
345  * Arguments:   @int sig@ = signal number
346  *              @void *v@ = an uninteresting argument
347  *
348  * Returns:     ---
349  *
350  * Use          Shuts down on receipt of a fatal signal.
351  */
352
353 static void a_sigdie(int sig, void *v)
354 {
355   char *p;
356   char buf[20];
357
358   switch (sig) {
359     case SIGTERM:       p = "SIGTERM"; break;
360     case SIGINT:        p = "SIGINT"; break;
361     default:
362       sprintf(buf, "signal %i", sig);
363       p = buf;
364       break;
365   }
366   a_warn("shutting down on %s", p);
367   a_quit();
368 }
369
370 /* --- @a_sighup@ --- *
371  *
372  * Arguments:   @int sig@ = signal number
373  *              @void *v@ = an uninteresting argument
374  *
375  * Returns:     ---
376  *
377  * Use          Logs a message about SIGHUP not being useful.
378  */
379
380 static void a_sighup(int sig, void *v)
381 {
382   a_warn("received SIGHUP: ignoring");
383 }
384
385 /*----- Adding peers ------------------------------------------------------*/
386  
387 /* --- @a_resolve@ --- *
388  *
389  * Arguments:   @struct hostent *h@ = pointer to resolved hostname
390  *              @void *v@ = pointer to admin block
391  *
392  * Returns:     ---
393  *
394  * Use:         Handles a completed name resolution.
395  */
396
397 static void a_resolve(struct hostent *h, void *v)
398 {
399   admin *a = v;
400
401   a_lock(a);
402   T( trace(T_ADMIN, "admin: %u resolved", a->seq); )
403   TIMER;
404   sel_rmtimer(&a->t);
405   if (!h)
406     a_write(a, "FAIL couldn't resolve hostname `%s'\n", a->paddr);
407   else if (p_find(a->pname))
408     a_write(a, "FAIL peer `%s' already registered\n", a->pname);
409   else {
410     memcpy(&a->peer.sin.sin_addr, h->h_addr, sizeof(struct in_addr));
411     if (!p_create(a->pname, &a->peer.sa, a->sasz))
412       a_write(a, "FAIL couldn't create peer\n");
413     else
414       a_write(a, "OK\n");
415   }
416   xfree(a->pname);
417   xfree(a->paddr);
418   a->pname = 0;
419   selbuf_enable(&a->b);
420   a_unlock(a);
421 }
422
423 /* --- @a_timer@ --- *
424  *
425  * Arguments:   @struct timeval *tv@ = timer
426  *              @void *v@ = pointer to admin block
427  *
428  * Returns:     ---
429  *
430  * Use:         Times out a resolver.
431  */
432
433 static void a_timer(struct timeval *tv, void *v)
434 {
435   admin *a = v;
436
437   a_lock(a);
438   T( trace(T_ADMIN, "admin: %u resolver timeout", a->seq); )
439   bres_abort(&a->r);
440   a_write(a, "FAIL timeout resolving `%s'\n", a->paddr);
441   xfree(a->pname);
442   xfree(a->paddr);
443   a->pname = 0;
444   selbuf_enable(&a->b);
445   a_unlock(a);
446 }
447
448 /* --- @acmd_add@ --- *
449  *
450  * Arguments:   @admin *a@ = connection which requested the addition
451  *              @unsigned ac@ = argument count
452  *              @char *av[]@ = pointer to the argument list
453  *
454  * Returns:     ---
455  *
456  * Use:         Adds a new peer.
457  */
458
459 static void acmd_add(admin *a, unsigned ac, char *av[])
460 {
461   unsigned long pt;
462   struct timeval tv;
463   char *p;
464
465   /* --- Make sure someone's not got there already --- */
466
467   if (p_find(av[0])) {
468     a_write(a, "FAIL peer `%s' already registered\n", av[0]);
469     return;
470   }
471
472   /* --- Fill in the easy bits of address --- */
473
474   BURN(a->peer);
475   a->peer.sin.sin_family = AF_INET;
476   a->sasz = sizeof(a->peer.sin);
477   pt = strtoul(av[2], &p, 0);
478   if (*p) {
479     struct servent *s = getservbyname(av[2], "udp");
480     if (!s) {
481       a_write(a, "FAIL service `%s' not known\n", av[2]);
482       return;
483     }
484     pt = ntohs(s->s_port);
485   }
486   if (pt == 0 || pt >= 65536) {
487     a_write(a, "FAIL bad port number %lu\n", pt);
488     return;
489   }
490   a->peer.sin.sin_port = htons(pt);
491
492   /* --- If the name is numeric, do it the easy way --- */
493   
494   if (inet_aton(av[1], &a->peer.sin.sin_addr)) {
495     if (!p_create(av[0], &a->peer.sa, a->sasz))
496       a_write(a, "FAIL couldn't create peer\n");
497     else
498       a_write(a, "OK\n");
499     return;
500   }
501
502   /* --- Store everything for later and crank up the resolver --- *
503    *
504    * We disable the line buffer until the resolver completes (or times out).
505    * This prevents other commands on the same connection (though the rest of
506    * the system continues regardless), but makes life simpler for the client.
507    */
508
509   a->pname = xstrdup(av[0]);
510   a->paddr = xstrdup(av[1]);
511   selbuf_disable(&a->b);
512   gettimeofday(&tv, 0);
513   tv.tv_sec += T_RESOLVE;
514   sel_addtimer(&sel, &a->t, &tv, a_timer, a);
515   bres_byname(&a->r, a->paddr, a_resolve, a);
516   T( trace(T_ADMIN, "admin: %u resolving hostname `%s'",
517            a->seq, a->paddr); )
518 }
519
520 /*----- Administration commands -------------------------------------------*/
521
522 /* --- Miscellaneous commands --- */
523
524 #ifndef NTRACE
525
526 static void acmd_trace(admin *a, unsigned ac, char *av[])
527 {
528   if (!ac || strcmp(av[0], "?") == 0) {
529     const trace_opt *t;
530     a_write(a, "INFO Trace options:\n");
531     for (t = tr_opts; t->ch; t++) {
532       a_write(a, "INFO %c %c  %s\n",
533               t->ch, (tr_flags & t->f) == t->f ? '*' : ' ', t->help);
534     }
535   } else {
536     unsigned sense = 1;
537     unsigned f = tr_flags;
538     const trace_opt *tt;
539     char *p = av[0];
540
541     while (*p) {
542       switch (*p) {
543         case '+': sense = 1; break;
544         case '-': sense = 0; break;
545         default:
546           for (tt = tr_opts; tt->ch; tt++) {
547             if (tt->ch == *p) {
548               if (sense) f |= tt->f;
549               else f &= ~tt->f;
550               goto tropt_ok;
551             }
552           }
553           a_write(a, "FAIL unknown trace option `%c'\n", *p);
554           return;
555         tropt_ok:;
556           break;
557       }
558       p++;
559     }
560     tr_flags = f;
561     trace_level(tr_flags);
562   }
563   a_write(a, "OK\n");
564 }
565
566 #endif
567
568 static void acmd_port(admin *a, unsigned ac, char *av[])
569 {
570   a_write(a, "INFO %u\nOK\n", p_port());
571 }
572
573 static void acmd_daemon(admin *a, unsigned ac, char *av[])
574 {
575   if (flags & F_DAEMON)
576     a_write(a, "FAIL already running as a daemon\n");
577   else {
578     a_warn("becoming a daemon");
579     if (a_stdin)
580       a_destroy(a_stdin);
581     if (u_daemon())
582       a_write(a, "FAIL error becoming a daemon: %s", strerror(errno));
583     else {
584       flags |= F_DAEMON;
585       a_write(a, "OK\n");
586     }
587   }
588 }
589
590 static void acmd_list(admin *a, unsigned ac, char *av[])
591 {
592   peer *p;
593   for (p = p_first(); p; p = p_next(p))
594     a_write(a, "INFO %s\n", p_name(p));
595   a_write(a, "OK\n");
596 }
597
598 static void acmd_ifname(admin *a, unsigned ac, char *av[])
599 {
600   peer *p;
601
602   if ((p = p_find(av[0])) == 0)
603     a_write(a, "FAIL peer `%s' not found\n", av[0]);
604   else
605     a_write(a, "INFO %s\nOK\n", p_ifname(p));
606 }
607
608 static void acmd_addr(admin *a, unsigned ac, char *av[])
609 {
610   peer *p;
611   const addr *ad;
612
613   if ((p = p_find(av[0])) == 0)
614     a_write(a, "FAIL peer `%s' not found\n", av[0]);
615   else {
616     ad = p_addr(p);
617     assert(ad->sa.sa_family == AF_INET);
618     a_write(a, "INFO %s %u\nOK\n",
619             inet_ntoa(ad->sin.sin_addr),
620             (unsigned)ntohs(ad->sin.sin_port));
621   }
622 }
623
624 static void acmd_stats(admin *a, unsigned ac, char *av[])
625 {
626   peer *p;
627   stats *st;
628
629   if ((p = p_find(av[0])) == 0)
630     a_write(a, "FAIL peer `%s' not found\n", av[0]);
631   else {
632     st = p_stats(p);
633     a_write(a, "INFO start-time=%s\n", timestr(st->t_start));
634     a_write(a, "INFO last-packet-time=%s\n", timestr(st->t_last));
635     a_write(a, "INFO packets-in=%lu bytes-in=%lu\n", st->n_in, st->sz_in);
636     a_write(a, "INFO packets-out=%lu bytes-out=%lu\n",
637             st->n_out, st->sz_out);
638     a_write(a, "INFO keyexch-packets-in=%lu keyexch-bytes-in=%lu\n",
639             st->n_kxin, st->sz_kxin);
640     a_write(a, "INFO keyexch-packets-out=%lu keyexch-bytes-out=%lu\n",
641             st->n_kxout, st->sz_kxout);
642     a_write(a, "INFO ip-packets-in=%lu ip-bytes-in=%lu\n",
643             st->n_ipin, st->sz_ipin);
644     a_write(a, "INFO ip-packets-out=%lu ip-bytes-out=%lu\n",
645             st->n_ipout, st->sz_ipout);
646     a_write(a, "INFO rejected-packets=%lu\n", st->n_reject);
647     a_write(a, "OK\n");
648   }
649 }
650
651 static void acmd_kill(admin *a, unsigned ac, char *av[])
652 {
653   peer *p;
654   if ((p = p_find(av[0])) == 0)
655     a_write(a, "FAIL peer `%s' not found\n", av[0]);
656   else {
657     p_destroy(p);
658     a_write(a, "OK\n");
659   }
660 }
661
662 static void acmd_quit(admin *a, unsigned ac, char *av[])
663 {
664   a_warn("closing down on admin request");
665   a_write(a, "OK\n");
666   a_quit();
667 }
668
669 /* --- The command table and help --- */
670
671 typedef struct acmd {
672   const char *name;
673   const char *help;
674   unsigned argmin, argmax;
675   void (*func)(admin */*a*/, unsigned /*ac*/, char */*av*/[]);
676 } acmd;
677
678 static void acmd_help(admin */*a*/, unsigned /*ac*/, char */*av*/[]);
679
680 static const acmd acmdtab[] = {
681   { "help",     "HELP",                 0,      0,      acmd_help },
682 #ifndef NTRACE
683   { "trace",    "TRACE [options]",      0,      1,      acmd_trace },
684 #endif
685   { "port",     "PORT",                 0,      0,      acmd_port },
686   { "daemon",   "DAEMON",               0,      0,      acmd_daemon },
687   { "list",     "LIST",                 0,      0,      acmd_list },
688   { "ifname",   "IFNAME peer",          1,      1,      acmd_ifname },
689   { "addr",     "ADDR peer",            1,      1,      acmd_addr },
690   { "stats",    "STATS peer",           1,      1,      acmd_stats },
691   { "kill",     "KILL peer",            1,      1,      acmd_kill },
692   { "add",      "ADD peer addr port",   3,      3,      acmd_add },
693   { "quit",     "QUIT",                 0,      0,      acmd_quit },
694   { 0,          0,                      0,      0,      0 }
695 };
696
697 static void acmd_help(admin *a, unsigned ac, char *av[])
698 {
699   const acmd *c;
700   for (c = acmdtab; c->name; c++)
701     a_write(a, "INFO %s\n", c->help);
702   a_write(a, "OK\n");
703 }
704
705 /*----- Connection handling -----------------------------------------------*/
706
707 /* --- @a_lock@ --- *
708  *
709  * Arguments:   @admin *a@ = pointer to an admin block
710  *
711  * Returns:     ---
712  *
713  * Use:         Locks an admin block so that it won't be destroyed
714  *              immediately.
715  */
716
717 static void a_lock(admin *a) { assert(!(a->f & AF_LOCK)); a->f |= AF_LOCK; }
718
719 /* --- @a_unlock@ --- *
720  *
721  * Arguments:   @admin *a@ = pointer to an admin block
722  *
723  * Returns:     ---
724  *
725  * Use:         Unlocks an admin block, allowing its destruction.  This is
726  *              also the second half of @a_destroy@.
727  */
728
729 static void a_unlock(admin *a)
730 {
731   assert(a->f & AF_LOCK);
732   if (!(a->f & AF_DEAD)) {
733     a->f &= ~AF_LOCK;
734     return;
735   }
736
737   T( trace(T_ADMIN, "admin: completing destruction of connection %u",
738            a->seq); )
739
740   selbuf_destroy(&a->b);
741   if (a->pname) {
742     xfree(a->pname);
743     xfree(a->paddr);
744     bres_abort(&a->r);
745     sel_rmtimer(&a->t);
746   }
747   if (a->b.reader.fd != a->w.fd)
748     close(a->b.reader.fd);
749   close(a->w.fd);
750
751   if (a_stdin == a)
752     a_stdin = 0;
753   if (a->next)
754     a->next->prev = a->prev;
755   if (a->prev)
756     a->prev->next = a->next;
757   else
758     admins = a->next;
759   DESTROY(a);
760 }
761
762 /* --- @a_destroy@ --- *
763  *
764  * Arguments:   @admin *a@ = pointer to an admin block
765  *
766  * Returns:     ---
767  *
768  * Use:         Destroys an admin block.  This requires a certain amount of
769  *              care.
770  */
771
772 static void a_destroy(admin *a)
773 {
774   /* --- Don't multiply destroy admin blocks --- */
775
776   if (a->f & AF_DEAD)
777     return;
778
779   /* --- Make sure nobody expects it to work --- */
780
781   a->f |= AF_DEAD;
782   T( trace(T_ADMIN, "admin: destroying connection %u", a->seq); )
783
784   /* --- Free the output buffers --- */
785
786   if (a->o_head) {
787     obuf *o, *oo;
788     sel_rmfile(&a->w);
789     for (o = a->o_head; o; o = oo) {
790       oo = o->next;
791       xfree(o);
792     }
793     a->o_head = 0;
794   }
795
796   /* --- If the block is locked, that's all we can manage --- */
797
798   if (a->f & AF_LOCK) {
799     T( trace(T_ADMIN, "admin: deferring destruction..."); )
800     return;
801   }
802   a->f |= AF_LOCK;
803   a_unlock(a);
804 }
805
806 /* --- @a_line@ --- *
807  *
808  * Arguments:   @char *p@ = pointer to the line read
809  *              @size_t len@ = length of the line
810  *              @void *vp@ = pointer to my admin block
811  *
812  * Returns:     ---
813  *
814  * Use:         Handles a line of input.
815  */
816
817 static void a_line(char *p, size_t len, void *vp)
818 {
819   admin *a = vp;
820   const acmd *c;
821   char *av[4];
822   size_t ac;
823
824   TIMER;
825   if (a->f & AF_DEAD)
826     return;
827   if (!p) {
828     a_destroy(a);
829     return;
830   }
831   ac = str_qsplit(p, av, 4, 0, STRF_QUOTE);
832   if (!ac)
833     return;
834   for (p = av[0]; *p; p++) *p = tolower((unsigned char)*p);
835   for (c = acmdtab; c->name; c++) {
836     if (strcmp(av[0], c->name) == 0) {
837       ac--;
838       if (c->argmin > ac || ac > c->argmax)
839         a_write(a, "FAIL syntax: %s\n", c->help);
840       else {
841         a_lock(a);
842         c->func(a, ac, av + 1);
843         a_unlock(a);
844       }
845       return;
846     }
847   }
848   a_write(a, "FAIL unknown command `%s'\n", av[0]);
849 }
850
851 /* --- @a_create@ --- *
852  *
853  * Arguments:   @int fd_in, fd_out@ = file descriptors to use
854  *
855  * Returns:     ---
856  *
857  * Use:         Creates a new admin connection.
858  */
859
860 void a_create(int fd_in, int fd_out)
861 {
862   admin *a = CREATE(admin);
863   T( static unsigned seq = 0;
864      a->seq = seq++; )
865   T( trace(T_ADMIN, "admin: accepted connection %u", a->seq); )
866   a->pname = 0;
867   a->f = 0;
868   if (fd_in == STDIN_FILENO)
869     a_stdin = a;
870   fdflags(fd_in, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
871   if (fd_out != fd_in)
872     fdflags(fd_out, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
873   selbuf_init(&a->b, &sel, fd_in, a_line, a);
874   sel_initfile(&sel, &a->w, fd_out, SEL_WRITE, a_flush, a);
875   a->o_head = 0;
876   a->o_tail = 0;
877   a->next = admins;
878   a->prev = 0;
879   if (admins)
880     admins->prev = a;
881   admins = a;
882 }
883
884 /* --- @a_accept@ --- *
885  *
886  * Arguments:   @int fd@ = file descriptor to accept
887  *              @unsigned mode@ = what to do
888  *              @void *v@ = uninteresting pointer
889  *
890  * Returns:     ---
891  *
892  * Use:         Accepts a new admin connection.
893  */
894
895 static void a_accept(int fd, unsigned mode, void *v)
896 {
897   int nfd;
898   struct sockaddr_un sun;
899   size_t sz = sizeof(sun);
900
901   if ((nfd = accept(fd, (struct sockaddr *)&sun, &sz)) < 0) {
902     if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK &&
903         errno != ECONNABORTED && errno != EPROTO)
904       a_warn("accept admin connection failed: %s", strerror(errno));
905     return;
906   }
907   a_create(nfd, nfd);
908 }
909
910 /* --- @a_daemon@ --- *
911  *
912  * Arguments:   ---
913  *
914  * Returns:     ---
915  *
916  * Use:         Informs the admin module that it's a daemon.
917  */
918
919 void a_daemon(void)
920 {
921   flags |= F_DAEMON;
922 }
923
924 /* --- @a_init@ --- *
925  *
926  * Arguments:   @const char *name@ = socket name to create
927  *
928  * Returns:     ---
929  *
930  * Use:         Creates the admin listening socket.
931  */
932
933 void a_init(const char *name)
934 {
935   int fd;
936   int n = 5;
937   struct sockaddr_un sun;
938   struct sigaction sa;
939   size_t sz;
940
941   /* --- Set up the socket address --- */
942
943   sz = strlen(name) + 1;
944   if (sz > sizeof(sun.sun_path))
945     die(EXIT_FAILURE, "socket name `%s' too long", name);
946   BURN(sun);
947   sun.sun_family = AF_UNIX;
948   memcpy(sun.sun_path, name, sz);
949   sz += offsetof(struct sockaddr_un, sun_path);
950
951   /* --- Attempt to bind to the socket --- */
952
953   umask(0077);
954 again:
955   if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
956     die(EXIT_FAILURE, "couldn't create socket: %s", strerror(errno));
957   if (bind(fd, (struct sockaddr *)&sun, sz) < 0) {
958     struct stat st;
959     int e = errno;
960     if (errno != EADDRINUSE) {
961       die(EXIT_FAILURE, "couldn't bind to address `%s': %s",
962           sun.sun_path, strerror(e));
963     }
964     if (!n)
965       die(EXIT_FAILURE, "too many retries; giving up");
966     n--;
967     if (!connect(fd, (struct sockaddr *)&sun, sz)) {
968       die(EXIT_FAILURE, "server already listening on admin socket `%s'",
969           sun.sun_path);
970     }
971     if (errno != ECONNREFUSED)
972       die(EXIT_FAILURE, "couldn't bind to address: %s", strerror(e));
973     if (stat(sun.sun_path, &st)) {
974       die(EXIT_FAILURE, "couldn't stat `%s': %s",
975           sun.sun_path, strerror(errno));
976     }
977     if (!S_ISSOCK(st.st_mode))
978       die(EXIT_FAILURE, "object `%s' isn't a socket", sun.sun_path);
979     T( trace(T_ADMIN, "admin: stale socket found; removing it"); )
980     unlink(sun.sun_path);
981     close(fd);
982     goto again;
983   }
984   chmod(sun.sun_path, 0600);
985   fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
986   if (listen(fd, 5))
987     die(EXIT_FAILURE, "couldn't listen on socket: %s", strerror(errno));
988
989   /* --- Listen to the socket --- */
990
991   sel_initfile(&sel, &sock, fd, SEL_READ, a_accept, 0);
992   sel_addfile(&sock);
993   sockname = name;
994   bres_init(&sel);
995   T( trace_custom(a_trace, 0);
996      trace(T_ADMIN, "admin: enabled custom tracing"); )
997   flags |= F_INIT;
998
999   /* --- Set up signal handlers --- */
1000
1001   sig_add(&s_term, SIGTERM, a_sigdie, 0);
1002   sig_add(&s_hup, SIGHUP, a_sighup, 0);
1003   sigaction(SIGINT, 0, &sa);
1004   if (sa.sa_handler != SIG_IGN)
1005     sig_add(&s_int, SIGINT, a_sigdie, 0);
1006 }
1007
1008 /*----- That's all, folks -------------------------------------------------*/