chiark / gitweb /
Change key exchange message interface. Maintain statistics.
[tripe] / admin.c
1 /* -*-c-*-
2  *
3  * $Id: admin.c,v 1.5 2001/02/16 21:22:51 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.5  2001/02/16 21:22:51  mdw
33  * Support for displaying statistics.  Make client connections blocking, so
34  * that things don't get dropped.  (This might change again if I add
35  * buffering.)
36  *
37  * Revision 1.4  2001/02/06 09:34:53  mdw
38  * Change ERR response to FAIL for consistency with other programs.
39  *
40  * Revision 1.3  2001/02/04 01:17:12  mdw
41  * The `DAEMON' notification to stdout is replaced by a warning.  The
42  * `DAEMON' and `QUIT' command send `OK' on successful completion.  Put
43  * assignment of sequence number in a T(...) guard.
44  *
45  * Revision 1.2  2001/02/03 22:40:29  mdw
46  * Put timer information into the entropy pool when packets are received
47  * and on similar events.  Reseed the generator on the interval timer.
48  *
49  * Revision 1.1  2001/02/03 20:26:37  mdw
50  * Initial checkin.
51  *
52  */
53
54 /*----- Header files ------------------------------------------------------*/
55
56 #include "tripe.h"
57
58 /*----- Global variables --------------------------------------------------*/
59
60 #ifndef NTRACE
61
62 const trace_opt tr_opts[] = {
63   { 't',        T_TUNNEL,       "tunnel events" },
64   { 'r',        T_PEER,         "peer events" },
65   { 'a',        T_ADMIN,        "admin interface" },
66   { 'p',        T_PACKET,       "packet contents" },
67   { 'c',        T_CRYPTO,       "crypto details" },
68   { 's',        T_KEYSET,       "symmetric keyset management" },
69   { 'x',        T_KEYEXCH,      "key exchange" },
70   { 'm',        T_KEYMGMT,      "key management" },
71   { 'A',        T_ALL,          "all of the above" },
72   { 0,          0,              0 }
73 };
74
75 unsigned tr_flags = 0;
76 #endif
77
78 /*----- Static variables --------------------------------------------------*/
79
80 static admin *admins;
81 static sel_file sock;
82 static const char *sockname;
83 static unsigned flags = 0;
84 static admin *a_stdin = 0;
85 static sig s_term, s_int, s_hup;
86
87 #define F_DAEMON 1u
88 #define F_INIT 2u
89
90 #define T_RESOLVE SEC(30)
91
92 /*----- Utility functions -------------------------------------------------*/
93
94 /* --- @a_write@ --- *
95  *
96  * Arguments:   @admin *a@ = admin connection to write to
97  *              @const char *fmt@ = pointer to format string
98  *              @...@ = other arguments
99  *
100  * Returns:     ---
101  *
102  * Use:         Sends a message to an admin connection.
103  */
104
105 static void a_write(admin *a, const char *fmt, ...)
106 {
107   va_list ap;
108   dstr d = DSTR_INIT;
109   va_start(ap, fmt);
110   dstr_vputf(&d, fmt, ap);
111   va_end(ap);
112   write(a->fd, d.buf, d.len);
113   dstr_destroy(&d);
114 }
115
116 /* --- @a_warn@ --- *
117  *
118  * Arguments:   @const char *fmt@ = pointer to format string
119  *              @...@ = other arguments
120  *
121  * Returns:     ---
122  *
123  * Use:         Informs all admin connections of a warning.
124  */
125
126 void a_warn(const char *fmt, ...)
127 {
128   va_list ap;
129   admin *a;
130   dstr d = DSTR_INIT;
131
132   if (flags & F_INIT)
133     dstr_puts(&d, "WARN ");
134   va_start(ap, fmt);
135   dstr_vputf(&d, fmt, ap);
136   va_end(ap);
137   if (!(flags & F_INIT))
138     moan("%s", d.buf);
139   else {
140     dstr_putc(&d, '\n');
141     for (a = admins; a; a = a->next)
142       write(a->fd, d.buf, d.len);
143   }
144   dstr_destroy(&d);
145 }
146
147 /* --- @a_trace@ --- *
148  *
149  * Arguments:   @const char *p@ = pointer to a buffer
150  *              @size_t sz@ = size of the buffer
151  *              @void *v@ = uninteresting pointer
152  *
153  * Returns:     ---
154  *
155  * Use:         Custom trace output handler.
156  */
157
158 #ifndef NTRACE
159 static void a_trace(const char *p, size_t sz, void *v)
160 {
161   dstr d = DSTR_INIT;
162   admin *a;
163
164   dstr_puts(&d, "TRACE ");
165   dstr_putm(&d, p, sz);
166   dstr_putc(&d, '\n');
167   for (a = admins; a; a = a->next)
168     write(a->fd, d.buf, d.len);
169   dstr_destroy(&d);  
170 }
171 #endif
172
173 /* --- @a_quit@ --- *
174  *
175  * Arguments:   ---
176  *
177  * Returns:     ---
178  *
179  * Use:         Shuts things down nicely.
180  */
181
182 void a_quit(void)
183 {
184   close(sock.fd);
185   unlink(sockname);
186   exit(0);
187 }
188
189 /* --- @a_sigdie@ --- *
190  *
191  * Arguments:   @int sig@ = signal number
192  *              @void *v@ = an uninteresting argument
193  *
194  * Returns:     ---
195  *
196  * Use          Shuts down on receipt of a fatal signal.
197  */
198
199 static void a_sigdie(int sig, void *v)
200 {
201   char *p;
202   char buf[20];
203
204   switch (sig) {
205     case SIGTERM:       p = "SIGTERM"; break;
206     case SIGINT:        p = "SIGINT"; break;
207     default:
208       sprintf(buf, "signal %i", sig);
209       p = buf;
210       break;
211   }
212   a_warn("shutting down on %s", p);
213   a_quit();
214 }
215
216 /* --- @a_sighup@ --- *
217  *
218  * Arguments:   @int sig@ = signal number
219  *              @void *v@ = an uninteresting argument
220  *
221  * Returns:     ---
222  *
223  * Use          Logs a message about SIGHUP not being useful.
224  */
225
226 static void a_sighup(int sig, void *v)
227 {
228   a_warn("received SIGHUP: ignoring");
229 }
230
231 /*----- Adding peers ------------------------------------------------------*/
232  
233 /* --- @a_resolve@ --- *
234  *
235  * Arguments:   @struct hostent *h@ = pointer to resolved hostname
236  *              @void *v@ = pointer to admin block
237  *
238  * Returns:     ---
239  *
240  * Use:         Handles a completed name resolution.
241  */
242
243 static void a_resolve(struct hostent *h, void *v)
244 {
245   admin *a = v;
246   T( trace(T_ADMIN, "admin: %u resolved", a->seq); )
247   TIMER;
248   sel_rmtimer(&a->t);
249   if (!h)
250     a_write(a, "FAIL couldn't resolve hostname `%s'\n", a->paddr);
251   else if (p_find(a->pname))
252     a_write(a, "FAIL peer `%s' already registered\n", a->pname);
253   else {
254     memcpy(&a->peer.sin.sin_addr, h->h_addr, sizeof(struct in_addr));
255     if (!p_create(a->pname, &a->peer.sa, a->sasz))
256       a_write(a, "FAIL couldn't create peer\n");
257     else
258       a_write(a, "OK\n");
259   }
260   xfree(a->pname);
261   xfree(a->paddr);
262   a->pname = 0;
263   selbuf_enable(&a->b);
264 }
265
266 /* --- @a_timer@ --- *
267  *
268  * Arguments:   @struct timeval *tv@ = timer
269  *              @void *v@ = pointer to admin block
270  *
271  * Returns:     ---
272  *
273  * Use:         Times out a resolver.
274  */
275
276 static void a_timer(struct timeval *tv, void *v)
277 {
278   admin *a = v;
279   T( trace(T_ADMIN, "admin: %u resolver timeout", a->seq); )
280   bres_abort(&a->r);
281   a_write(a, "FAIL timeout resolving `%s'\n", a->paddr);
282   xfree(a->pname);
283   xfree(a->paddr);
284   a->pname = 0;
285   selbuf_enable(&a->b);
286 }
287
288 /* --- @acmd_add@ --- *
289  *
290  * Arguments:   @admin *a@ = connection which requested the addition
291  *              @unsigned ac@ = argument count
292  *              @char *av[]@ = pointer to the argument list
293  *
294  * Returns:     ---
295  *
296  * Use:         Adds a new peer.
297  */
298
299 static void acmd_add(admin *a, unsigned ac, char *av[])
300 {
301   unsigned long pt;
302   struct timeval tv;
303   char *p;
304
305   /* --- Make sure someone's not got there already --- */
306
307   if (p_find(av[0])) {
308     a_write(a, "FAIL peer `%s' already registered\n", av[0]);
309     return;
310   }
311
312   /* --- Fill in the easy bits of address --- */
313
314   BURN(a->peer);
315   a->peer.sin.sin_family = AF_INET;
316   a->sasz = sizeof(a->peer.sin);
317   pt = strtoul(av[2], &p, 0);
318   if (*p) {
319     struct servent *s = getservbyname(av[2], "udp");
320     if (!s) {
321       a_write(a, "FAIL service `%s' not known\n", av[2]);
322       return;
323     }
324     pt = ntohs(s->s_port);
325   }
326   if (pt == 0 || pt >= 65536) {
327     a_write(a, "FAIL bad port number %lu\n", pt);
328     return;
329   }
330   a->peer.sin.sin_port = htons(pt);
331
332   /* --- If the name is numeric, do it the easy way --- */
333   
334   if (inet_aton(av[1], &a->peer.sin.sin_addr)) {
335     if (!p_create(av[0], &a->peer.sa, a->sasz))
336       a_write(a, "FAIL couldn't create peer\n");
337     else
338       a_write(a, "OK\n");
339     return;
340   }
341
342   /* --- Store everything for later and crank up the resolver --- *
343    *
344    * We disable the line buffer until the resolver completes (or times out).
345    * This prevents other commands on the same connection (though the rest of
346    * the system continues regardless), but makes life simpler for the client.
347    */
348
349   a->pname = xstrdup(av[0]);
350   a->paddr = xstrdup(av[1]);
351   selbuf_disable(&a->b);
352   gettimeofday(&tv, 0);
353   tv.tv_sec += T_RESOLVE;
354   sel_addtimer(&sel, &a->t, &tv, a_timer, a);
355   bres_byname(&a->r, a->paddr, a_resolve, a);
356   T( trace(T_ADMIN, "admin: %u resolving hostname `%s'",
357            a->seq, a->paddr); )
358 }
359
360 /*----- Administration commands -------------------------------------------*/
361
362 /* --- Miscellaneous commands --- */
363
364 #ifndef NTRACE
365
366 static void acmd_trace(admin *a, unsigned ac, char *av[])
367 {
368   if (!ac || strcmp(av[0], "?") == 0) {
369     const trace_opt *t;
370     a_write(a, "INFO Trace options:\n");
371     for (t = tr_opts; t->ch; t++) {
372       a_write(a, "INFO %c %c  %s\n",
373               t->ch, (tr_flags & t->f) == t->f ? '*' : ' ', t->help);
374     }
375   } else {
376     unsigned sense = 1;
377     unsigned f = tr_flags;
378     const trace_opt *tt;
379     char *p = av[0];
380
381     while (*p) {
382       switch (*p) {
383         case '+': sense = 1; break;
384         case '-': sense = 0; break;
385         default:
386           for (tt = tr_opts; tt->ch; tt++) {
387             if (tt->ch == *p) {
388               if (sense) f |= tt->f;
389               else f &= ~tt->f;
390               goto tropt_ok;
391             }
392           }
393           a_write(a, "FAIL unknown trace option `%c'\n", *p);
394           return;
395         tropt_ok:;
396           break;
397       }
398       p++;
399     }
400     tr_flags = f;
401     trace_level(tr_flags);
402   }
403   a_write(a, "OK\n");
404 }
405
406 #endif
407
408 static void acmd_port(admin *a, unsigned ac, char *av[])
409 {
410   a_write(a, "INFO %u\nOK\n", p_port());
411 }
412
413 static void a_destroy(admin */*a*/);
414
415 static void acmd_daemon(admin *a, unsigned ac, char *av[])
416 {
417   if (flags & F_DAEMON)
418     a_write(a, "FAIL already running as a daemon\n");
419   else {
420     a_warn("becoming a daemon");
421     if (a_stdin)
422       a_destroy(a_stdin);
423     if (u_daemon())
424       a_write(a, "FAIL error becoming a daemon: %s", strerror(errno));
425     else {
426       flags |= F_DAEMON;
427       a_write(a, "OK\n");
428     }
429   }
430 }
431
432 static void acmd_list(admin *a, unsigned ac, char *av[])
433 {
434   peer *p;
435   for (p = p_first(); p; p = p_next(p))
436     a_write(a, "INFO %s\n", p_name(p));
437   a_write(a, "OK\n");
438 }
439
440 static void acmd_ifname(admin *a, unsigned ac, char *av[])
441 {
442   peer *p;
443
444   if ((p = p_find(av[0])) == 0)
445     a_write(a, "FAIL peer `%s' not found\n", av[0]);
446   else
447     a_write(a, "INFO %s\nOK\n", p_ifname(p));
448 }
449
450 static void acmd_addr(admin *a, unsigned ac, char *av[])
451 {
452   peer *p;
453   const addr *ad;
454
455   if ((p = p_find(av[0])) == 0)
456     a_write(a, "FAIL peer `%s' not found\n", av[0]);
457   else {
458     ad = p_addr(p);
459     assert(ad->sa.sa_family == AF_INET);
460     a_write(a, "INFO %s %u\nOK\n",
461             inet_ntoa(ad->sin.sin_addr),
462             (unsigned)ntohs(ad->sin.sin_port));
463   }
464 }
465
466 static void acmd_stats(admin *a, unsigned ac, char *av[])
467 {
468   peer *p;
469   stats *st;
470
471   if ((p = p_find(av[0])) == 0)
472     a_write(a, "FAIL peer `%s' not found\n", av[0]);
473   else {
474     st = p_stats(p);
475     a_write(a, "INFO start-time=%s\n", timestr(st->t_start));
476     a_write(a, "INFO last-packet-time=%s\n", timestr(st->t_last));
477     a_write(a, "INFO packets-in=%lu bytes-in=%lu\n", st->n_in, st->sz_in);
478     a_write(a, "INFO packets-out=%lu bytes-out=%lu\n",
479             st->n_out, st->sz_out);
480     a_write(a, "INFO keyexch-packets-in=%lu keyexch-bytes-in=%lu\n",
481             st->n_kxin, st->sz_kxin);
482     a_write(a, "INFO keyexch-packets-out=%lu keyexch-bytes-out=%lu\n",
483             st->n_kxout, st->sz_kxout);
484     a_write(a, "INFO ip-packets-in=%lu ip-bytes-in=%lu\n",
485             st->n_ipin, st->sz_ipin);
486     a_write(a, "INFO ip-packets-out=%lu ip-bytes-out=%lu\n",
487             st->n_ipout, st->sz_ipout);
488     a_write(a, "INFO rejected-packets=%lu\n", st->n_reject);
489     a_write(a, "OK\n");
490   }
491 }
492
493 static void acmd_kill(admin *a, unsigned ac, char *av[])
494 {
495   peer *p;
496   if ((p = p_find(av[0])) == 0)
497     a_write(a, "FAIL peer `%s' not found\n", av[0]);
498   else {
499     p_destroy(p);
500     a_write(a, "OK\n");
501   }
502 }
503
504 static void acmd_quit(admin *a, unsigned ac, char *av[])
505 {
506   a_warn("closing down on admin request");
507   a_write(a, "OK\n");
508   a_quit();
509 }
510
511 /* --- The command table and help --- */
512
513 typedef struct acmd {
514   const char *name;
515   const char *help;
516   unsigned argmin, argmax;
517   void (*func)(admin */*a*/, unsigned /*ac*/, char */*av*/[]);
518 } acmd;
519
520 static void acmd_help(admin */*a*/, unsigned /*ac*/, char */*av*/[]);
521
522 static const acmd acmdtab[] = {
523   { "help",     "HELP",                 0,      0,      acmd_help },
524 #ifndef NTRACE
525   { "trace",    "TRACE [options]",      0,      1,      acmd_trace },
526 #endif
527   { "port",     "PORT",                 0,      0,      acmd_port },
528   { "daemon",   "DAEMON",               0,      0,      acmd_daemon },
529   { "list",     "LIST",                 0,      0,      acmd_list },
530   { "ifname",   "IFNAME peer",          1,      1,      acmd_ifname },
531   { "addr",     "ADDR peer",            1,      1,      acmd_addr },
532   { "stats",    "STATS peer",           1,      1,      acmd_stats },
533   { "kill",     "KILL peer",            1,      1,      acmd_kill },
534   { "add",      "ADD peer addr port",   3,      3,      acmd_add },
535   { "quit",     "QUIT",                 0,      0,      acmd_quit },
536   { 0,          0,                      0,      0,      0 }
537 };
538
539 static void acmd_help(admin *a, unsigned ac, char *av[])
540 {
541   const acmd *c;
542   for (c = acmdtab; c->name; c++)
543     a_write(a, "INFO %s\n", c->help);
544   a_write(a, "OK\n");
545 }
546
547 /*----- Connection handling -----------------------------------------------*/
548
549 /* --- @a_destroy@ --- *
550  *
551  * Arguments:   @admin *a@ = pointer to an admin block
552  *
553  * Returns:     ---
554  *
555  * Use:         Destroys an admin block.
556  */
557
558 static void a_destroy(admin *a)
559 {
560   T( trace(T_ADMIN, "admin: destroying connection %u", a->seq); )
561   selbuf_destroy(&a->b);
562   if (a->b.reader.fd != a->fd)
563     close(a->b.reader.fd);
564   close(a->fd);
565   if (a->pname) {
566     xfree(a->pname);
567     xfree(a->paddr);
568     bres_abort(&a->r);
569     sel_rmtimer(&a->t);
570   }
571   if (a->next)
572     a->next->prev = a->prev;
573   if (a->prev)
574     a->prev->next = a->next;
575   else
576     admins = a->next;
577   if (a_stdin == a)
578     a_stdin = 0;
579   DESTROY(a);
580 }
581
582 /* --- @a_line@ --- *
583  *
584  * Arguments:   @char *p@ = pointer to the line read
585  *              @void *vp@ = pointer to my admin block
586  *
587  * Returns:     ---
588  *
589  * Use:         Handles a line of input.
590  */
591
592 static void a_line(char *p, void *vp)
593 {
594   admin *a = vp;
595   const acmd *c;
596   char *av[4];
597   size_t ac;
598
599   TIMER;
600   if (!p) {
601     a_destroy(a);
602     return;
603   }
604   ac = str_qsplit(p, av, 4, 0, STRF_QUOTE);
605   if (!ac)
606     return;
607   for (p = av[0]; *p; p++) *p = tolower((unsigned char)*p);
608   for (c = acmdtab; c->name; c++) {
609     if (strcmp(av[0], c->name) == 0) {
610       ac--;
611       if (c->argmin > ac || ac > c->argmax)
612         a_write(a, "FAIL syntax: %s\n", c->help);
613       else
614         c->func(a, ac, av + 1);
615       return;
616     }
617   }
618   a_write(a, "FAIL unknown command `%s'\n", av[0]);
619 }
620
621 /* --- @a_create@ --- *
622  *
623  * Arguments:   @int fd_in, fd_out@ = file descriptors to use
624  *
625  * Returns:     ---
626  *
627  * Use:         Creates a new admin connection.
628  */
629
630 void a_create(int fd_in, int fd_out)
631 {
632   admin *a = CREATE(admin);
633   T( static unsigned seq = 0;
634      a->seq = seq++; )
635   T( trace(T_ADMIN, "admin: accepted connection %u", a->seq); )
636   a->pname = 0;
637   if (fd_in == STDIN_FILENO)
638     a_stdin = a;
639   fdflags(fd_in, O_NONBLOCK, 0, FD_CLOEXEC, FD_CLOEXEC);
640   if (fd_out != fd_in)
641     fdflags(fd_out, O_NONBLOCK, 0, FD_CLOEXEC, FD_CLOEXEC);
642   a->fd = fd_out;
643   selbuf_init(&a->b, &sel, fd_in, a_line, a);
644   a->next = admins;
645   a->prev = 0;
646   if (admins)
647     admins->prev = a;
648   admins = a;
649 }
650
651 /* --- @a_accept@ --- *
652  *
653  * Arguments:   @int fd@ = file descriptor to accept
654  *              @unsigned mode@ = what to do
655  *              @void *v@ = uninteresting pointer
656  *
657  * Returns:     ---
658  *
659  * Use:         Accepts a new admin connection.
660  */
661
662 static void a_accept(int fd, unsigned mode, void *v)
663 {
664   int nfd;
665   struct sockaddr_un sun;
666   size_t sz = sizeof(sun);
667
668   if ((nfd = accept(fd, (struct sockaddr *)&sun, &sz)) < 0) {
669     a_warn("accept admin connection failed: %s", strerror(errno));
670     return;
671   }
672   a_create(nfd, nfd);
673 }
674
675 /* --- @a_daemon@ --- *
676  *
677  * Arguments:   ---
678  *
679  * Returns:     ---
680  *
681  * Use:         Informs the admin module that it's a daemon.
682  */
683
684 void a_daemon(void)
685 {
686   flags |= F_DAEMON;
687 }
688
689 /* --- @a_init@ --- *
690  *
691  * Arguments:   @const char *name@ = socket name to create
692  *
693  * Returns:     ---
694  *
695  * Use:         Creates the admin listening socket.
696  */
697
698 void a_init(const char *name)
699 {
700   int fd;
701   int n = 5;
702   struct sockaddr_un sun;
703   struct sigaction sa;
704   size_t sz;
705
706   /* --- Set up the socket address --- */
707
708   sz = strlen(name) + 1;
709   if (sz > sizeof(sun.sun_path))
710     die(EXIT_FAILURE, "socket name `%s' too long", name);
711   BURN(sun);
712   sun.sun_family = AF_UNIX;
713   memcpy(sun.sun_path, name, sz);
714   sz += offsetof(struct sockaddr_un, sun_path);
715
716   /* --- Attempt to bind to the socket --- */
717
718   umask(0077);
719 again:
720   if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
721     die(EXIT_FAILURE, "couldn't create socket: %s", strerror(errno));
722   if (bind(fd, (struct sockaddr *)&sun, sz) < 0) {
723     struct stat st;
724     int e = errno;
725     if (errno != EADDRINUSE) {
726       die(EXIT_FAILURE, "couldn't bind to address `%s': %s",
727           sun.sun_path, strerror(e));
728     }
729     if (!n)
730       die(EXIT_FAILURE, "too many retries; giving up");
731     n--;
732     if (!connect(fd, (struct sockaddr *)&sun, sz)) {
733       die(EXIT_FAILURE, "server already listening on admin socket `%s'",
734           sun.sun_path);
735     }
736     if (errno != ECONNREFUSED)
737       die(EXIT_FAILURE, "couldn't bind to address: %s", strerror(e));
738     if (stat(sun.sun_path, &st)) {
739       die(EXIT_FAILURE, "couldn't stat `%s': %s",
740           sun.sun_path, strerror(errno));
741     }
742     if (!S_ISSOCK(st.st_mode))
743       die(EXIT_FAILURE, "object `%s' isn't a socket", sun.sun_path);
744     T( trace(T_ADMIN, "admin: stale socket found; removing it"); )
745     unlink(sun.sun_path);
746     close(fd);
747     goto again;
748   }
749   chmod(sun.sun_path, 0600);
750   fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
751   if (listen(fd, 5))
752     die(EXIT_FAILURE, "couldn't listen on socket: %s", strerror(errno));
753
754   /* --- Listen to the socket --- */
755
756   sel_initfile(&sel, &sock, fd, SEL_READ, a_accept, 0);
757   sel_addfile(&sock);
758   sockname = name;
759   bres_init(&sel);
760   T( trace_custom(a_trace, 0);
761      trace(T_ADMIN, "admin: enabled custom tracing"); )
762   flags |= F_INIT;
763
764   /* --- Set up signal handlers --- */
765
766   sig_add(&s_term, SIGTERM, a_sigdie, 0);
767   sig_add(&s_hup, SIGHUP, a_sighup, 0);
768   sigaction(SIGINT, 0, &sa);
769   if (sa.sa_handler != SIG_IGN)
770     sig_add(&s_int, SIGINT, a_sigdie, 0);
771 }
772
773 /*----- That's all, folks -------------------------------------------------*/