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