1*6371cd0bSbket /* $OpenBSD: unwindctl.c,v 1.29 2021/11/10 20:24:22 bket Exp $ */
25c077b0fSflorian
35c077b0fSflorian /*
45c077b0fSflorian * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
55c077b0fSflorian * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
65c077b0fSflorian * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
75c077b0fSflorian *
85c077b0fSflorian * Permission to use, copy, modify, and distribute this software for any
95c077b0fSflorian * purpose with or without fee is hereby granted, provided that the above
105c077b0fSflorian * copyright notice and this permission notice appear in all copies.
115c077b0fSflorian *
125c077b0fSflorian * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
135c077b0fSflorian * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
145c077b0fSflorian * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
155c077b0fSflorian * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
165c077b0fSflorian * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
175c077b0fSflorian * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
185c077b0fSflorian * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
195c077b0fSflorian */
205c077b0fSflorian
215c077b0fSflorian #include <sys/types.h>
225c077b0fSflorian #include <sys/queue.h>
235c077b0fSflorian #include <sys/socket.h>
245c077b0fSflorian #include <sys/un.h>
255c077b0fSflorian #include <netinet/in.h>
265c077b0fSflorian #include <arpa/inet.h>
275c077b0fSflorian #include <net/if.h>
285c077b0fSflorian #include <net/if_media.h>
295c077b0fSflorian #include <net/if_types.h>
3028ba4729Sflorian #include <net/route.h>
315c077b0fSflorian
325c077b0fSflorian #include <err.h>
335c077b0fSflorian #include <errno.h>
345c077b0fSflorian #include <event.h>
355c077b0fSflorian #include <imsg.h>
365c077b0fSflorian #include <stdio.h>
375c077b0fSflorian #include <stdlib.h>
385c077b0fSflorian #include <string.h>
395c077b0fSflorian #include <unistd.h>
405c077b0fSflorian
415c077b0fSflorian #include "unwind.h"
425c077b0fSflorian #include "frontend.h"
435c077b0fSflorian #include "resolver.h"
445c077b0fSflorian #include "parser.h"
455c077b0fSflorian
465c077b0fSflorian __dead void usage(void);
475c077b0fSflorian int show_status_msg(struct imsg *);
4815fe126bSflorian int show_autoconf_msg(struct imsg *);
49c071f090Sflorian int show_mem_msg(struct imsg *);
502db6800fSotto void histogram_header(void);
512db6800fSotto void print_histogram(const char *name, int64_t[], size_t);
5228d3b5f7Stobhe const char *prio2str(int);
535c077b0fSflorian
545c077b0fSflorian struct imsgbuf *ibuf;
55608e5d72Sotto int info_cnt;
562db6800fSotto struct ctl_resolver_info info[UW_RES_NONE];
575c077b0fSflorian
5828d3b5f7Stobhe const char *
prio2str(int prio)5928d3b5f7Stobhe prio2str(int prio)
6028d3b5f7Stobhe {
6128d3b5f7Stobhe switch(prio) {
6228d3b5f7Stobhe case RTP_PROPOSAL_DHCLIENT:
6328d3b5f7Stobhe return "DHCP";
6428d3b5f7Stobhe case RTP_PROPOSAL_SLAAC:
6528d3b5f7Stobhe return "SLAAC";
6628d3b5f7Stobhe case RTP_PROPOSAL_STATIC:
6728d3b5f7Stobhe return "STATIC";
6828d3b5f7Stobhe case RTP_PROPOSAL_UMB:
6928d3b5f7Stobhe return "UMB";
70*6371cd0bSbket case RTP_PROPOSAL_PPP:
71*6371cd0bSbket return "PPP";
7228d3b5f7Stobhe }
7328d3b5f7Stobhe return "OTHER";
7428d3b5f7Stobhe }
7528d3b5f7Stobhe
765c077b0fSflorian __dead void
usage(void)775c077b0fSflorian usage(void)
785c077b0fSflorian {
795c077b0fSflorian extern char *__progname;
805c077b0fSflorian
815c077b0fSflorian fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n",
825c077b0fSflorian __progname);
835c077b0fSflorian exit(1);
845c077b0fSflorian }
855c077b0fSflorian
865c077b0fSflorian int
main(int argc,char * argv[])875c077b0fSflorian main(int argc, char *argv[])
885c077b0fSflorian {
895c077b0fSflorian struct sockaddr_un sun;
905c077b0fSflorian struct parse_result *res;
915c077b0fSflorian struct imsg imsg;
92608e5d72Sotto struct ctl_resolver_info *cri;
935c077b0fSflorian int ctl_sock;
945c077b0fSflorian int done = 0;
95608e5d72Sotto int i, j, k, n, verbose = 0;
96608e5d72Sotto int ch, column_offset;
97608e5d72Sotto char *sockname;
985c077b0fSflorian
99ee5be195Sflorian sockname = _PATH_UNWIND_SOCKET;
1005c077b0fSflorian while ((ch = getopt(argc, argv, "s:")) != -1) {
1015c077b0fSflorian switch (ch) {
1025c077b0fSflorian case 's':
1035c077b0fSflorian sockname = optarg;
1045c077b0fSflorian break;
1055c077b0fSflorian default:
1065c077b0fSflorian usage();
1075c077b0fSflorian }
1085c077b0fSflorian }
1095c077b0fSflorian argc -= optind;
1105c077b0fSflorian argv += optind;
1115c077b0fSflorian
1125c077b0fSflorian /* Parse command line. */
1135c077b0fSflorian if ((res = parse(argc, argv)) == NULL)
1145c077b0fSflorian exit(1);
1155c077b0fSflorian
1165c077b0fSflorian /* Connect to control socket. */
1175c077b0fSflorian if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
1185c077b0fSflorian err(1, "socket");
1195c077b0fSflorian
1205c077b0fSflorian memset(&sun, 0, sizeof(sun));
1215c077b0fSflorian sun.sun_family = AF_UNIX;
1225c077b0fSflorian strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path));
123cee51daaSflorian
1245c077b0fSflorian if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
1255c077b0fSflorian err(1, "connect: %s", sockname);
1265c077b0fSflorian
1275c077b0fSflorian if (pledge("stdio", NULL) == -1)
1285c077b0fSflorian err(1, "pledge");
1295c077b0fSflorian
1305c077b0fSflorian if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
1315c077b0fSflorian err(1, NULL);
1325c077b0fSflorian imsg_init(ibuf, ctl_sock);
1335c077b0fSflorian done = 0;
1345c077b0fSflorian
1353a1cc939Ssolene /* Check for root-only actions */
1363a1cc939Ssolene switch (res->action) {
1373a1cc939Ssolene case LOG_DEBUG:
1383a1cc939Ssolene case LOG_VERBOSE:
1393a1cc939Ssolene case LOG_BRIEF:
1403a1cc939Ssolene case RELOAD:
1413a1cc939Ssolene if (geteuid() != 0)
1423a1cc939Ssolene errx(1, "need root privileges");
1433a1cc939Ssolene break;
1443a1cc939Ssolene default:
1453a1cc939Ssolene break;
1463a1cc939Ssolene }
1473a1cc939Ssolene
1485c077b0fSflorian /* Process user request. */
1495c077b0fSflorian switch (res->action) {
1505c077b0fSflorian case LOG_DEBUG:
1515c077b0fSflorian verbose |= OPT_VERBOSE2;
1525c077b0fSflorian /* FALLTHROUGH */
1535c077b0fSflorian case LOG_VERBOSE:
1545c077b0fSflorian verbose |= OPT_VERBOSE;
1555c077b0fSflorian /* FALLTHROUGH */
1565c077b0fSflorian case LOG_BRIEF:
1575c077b0fSflorian imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
1585c077b0fSflorian &verbose, sizeof(verbose));
1595c077b0fSflorian printf("logging request sent.\n");
1605c077b0fSflorian done = 1;
1615c077b0fSflorian break;
1625c077b0fSflorian case RELOAD:
1635c077b0fSflorian imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
1645c077b0fSflorian printf("reload request sent.\n");
1655c077b0fSflorian done = 1;
1665c077b0fSflorian break;
1675c077b0fSflorian case STATUS:
168f6a28683Sotto imsg_compose(ibuf, IMSG_CTL_STATUS, 0, 0, -1, NULL, 0);
1692db6800fSotto break;
17015fe126bSflorian case AUTOCONF:
17115fe126bSflorian imsg_compose(ibuf, IMSG_CTL_AUTOCONF, 0, 0, -1, NULL, 0);
17215fe126bSflorian break;
173c071f090Sflorian case MEM:
174c071f090Sflorian imsg_compose(ibuf, IMSG_CTL_MEM, 0, 0, -1, NULL, 0);
175c071f090Sflorian break;
1765c077b0fSflorian default:
1775c077b0fSflorian usage();
1785c077b0fSflorian }
1795c077b0fSflorian
1805c077b0fSflorian while (ibuf->w.queued)
1815c077b0fSflorian if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
1825c077b0fSflorian err(1, "write error");
1835c077b0fSflorian
1845c077b0fSflorian while (!done) {
1855c077b0fSflorian if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
1865c077b0fSflorian errx(1, "imsg_read error");
1875c077b0fSflorian if (n == 0)
1885c077b0fSflorian errx(1, "pipe closed");
1895c077b0fSflorian
1905c077b0fSflorian while (!done) {
1915c077b0fSflorian if ((n = imsg_get(ibuf, &imsg)) == -1)
1925c077b0fSflorian errx(1, "imsg_get error");
1935c077b0fSflorian if (n == 0)
1945c077b0fSflorian break;
1955c077b0fSflorian
1965c077b0fSflorian switch (res->action) {
1975c077b0fSflorian case STATUS:
1985c077b0fSflorian done = show_status_msg(&imsg);
1995c077b0fSflorian break;
20015fe126bSflorian case AUTOCONF:
20115fe126bSflorian done = show_autoconf_msg(&imsg);
20215fe126bSflorian break;
203c071f090Sflorian case MEM:
204c071f090Sflorian done = show_mem_msg(&imsg);
205c071f090Sflorian break;
2065c077b0fSflorian default:
2075c077b0fSflorian break;
2085c077b0fSflorian }
2095c077b0fSflorian imsg_free(&imsg);
2105c077b0fSflorian }
2115c077b0fSflorian }
2125c077b0fSflorian close(ctl_sock);
2135c077b0fSflorian free(ibuf);
2145c077b0fSflorian
215608e5d72Sotto column_offset = info_cnt / 2;
216608e5d72Sotto if (info_cnt % 2 == 1)
217608e5d72Sotto column_offset++;
218608e5d72Sotto
219608e5d72Sotto for (i = 0; i < column_offset; i++) {
220608e5d72Sotto for (j = 0; j < 2; j++) {
221608e5d72Sotto k = i + j * column_offset;
222608e5d72Sotto if (k >= info_cnt)
22315fe126bSflorian break;
224608e5d72Sotto
225608e5d72Sotto cri = &info[k];
226608e5d72Sotto printf("%d. %-15s %10s, ", k + 1,
227608e5d72Sotto uw_resolver_type_str[cri->type],
228608e5d72Sotto uw_resolver_state_str[cri->state]);
229608e5d72Sotto if (cri->median == 0)
230608e5d72Sotto printf("%5s", "N/A");
231608e5d72Sotto else if (cri->median == INT64_MAX)
232608e5d72Sotto printf("%5s", "Inf");
233608e5d72Sotto else
234608e5d72Sotto printf("%3lldms", cri->median);
235608e5d72Sotto if (j == 0)
236608e5d72Sotto printf(" ");
23715fe126bSflorian }
238608e5d72Sotto printf("\n");
239608e5d72Sotto }
240608e5d72Sotto
241608e5d72Sotto if (info_cnt)
242608e5d72Sotto histogram_header();
243608e5d72Sotto for (i = 0; i < info_cnt; i++) {
244608e5d72Sotto cri = &info[i];
245608e5d72Sotto print_histogram(uw_resolver_type_short[cri->type],
246608e5d72Sotto cri->histogram, nitems(cri->histogram));
247608e5d72Sotto print_histogram("", cri->latest_histogram,
248608e5d72Sotto nitems(cri->latest_histogram));
2492db6800fSotto }
2505c077b0fSflorian return (0);
2515c077b0fSflorian }
2525c077b0fSflorian
2535c077b0fSflorian int
show_status_msg(struct imsg * imsg)2545c077b0fSflorian show_status_msg(struct imsg *imsg)
2555c077b0fSflorian {
2561ceebf76Sflorian static char fwd_line[80];
2575c077b0fSflorian
2585c077b0fSflorian switch (imsg->hdr.type) {
2595c077b0fSflorian case IMSG_CTL_RESOLVER_INFO:
260608e5d72Sotto memcpy(&info[info_cnt++], imsg->data, sizeof(info[0]));
2615c077b0fSflorian break;
26215fe126bSflorian case IMSG_CTL_END:
26315fe126bSflorian if (fwd_line[0] != '\0')
26415fe126bSflorian printf("%s\n", fwd_line);
26515fe126bSflorian return (1);
26615fe126bSflorian default:
26715fe126bSflorian break;
26815fe126bSflorian }
26915fe126bSflorian
27015fe126bSflorian return (0);
27115fe126bSflorian }
27215fe126bSflorian
27315fe126bSflorian int
show_autoconf_msg(struct imsg * imsg)27415fe126bSflorian show_autoconf_msg(struct imsg *imsg)
27515fe126bSflorian {
27615fe126bSflorian static int autoconf_forwarders, last_src;
27715fe126bSflorian static int label_len, line_len;
27815fe126bSflorian static uint32_t last_if_index;
27915fe126bSflorian static char fwd_line[80];
28015fe126bSflorian struct ctl_forwarder_info *cfi;
28115fe126bSflorian char ifnamebuf[IFNAMSIZ];
28215fe126bSflorian char *if_name;
28315fe126bSflorian
28415fe126bSflorian switch (imsg->hdr.type) {
285ecfbee2cSflorian case IMSG_CTL_AUTOCONF_RESOLVER_INFO:
286ecfbee2cSflorian cfi = imsg->data;
287ecfbee2cSflorian if (!autoconf_forwarders++)
28815fe126bSflorian printf("autoconfiguration forwarders:\n");
2891ceebf76Sflorian if (cfi->if_index != last_if_index || cfi->src != last_src) {
2906158ed34Sflorian if_name = if_indextoname(cfi->if_index, ifnamebuf);
2911ceebf76Sflorian if (fwd_line[0] != '\0') {
2921ceebf76Sflorian printf("%s\n", fwd_line);
2931ceebf76Sflorian fwd_line[0] = '\0';
2941ceebf76Sflorian }
2951ceebf76Sflorian label_len = snprintf(fwd_line, sizeof(fwd_line),
29628d3b5f7Stobhe "%6s[%s]:", prio2str(cfi->src),
29728d3b5f7Stobhe if_name ? if_name : "unknown");
2981ceebf76Sflorian line_len = label_len;
2991ceebf76Sflorian last_if_index = cfi->if_index;
3001ceebf76Sflorian last_src = cfi->src;
3011ceebf76Sflorian }
3021ceebf76Sflorian
3031ceebf76Sflorian if (line_len + 1 + strlen(cfi->ip) > sizeof(fwd_line)) {
3041ceebf76Sflorian printf("%s\n", fwd_line);
3051ceebf76Sflorian snprintf(fwd_line, sizeof(fwd_line), "%*s", label_len,
3061ceebf76Sflorian " ");
3071ceebf76Sflorian }
3081ceebf76Sflorian strlcat(fwd_line, " ", sizeof(fwd_line));
3091ceebf76Sflorian line_len = strlcat(fwd_line, cfi->ip, sizeof(fwd_line));
310ecfbee2cSflorian break;
3115c077b0fSflorian case IMSG_CTL_END:
3121ceebf76Sflorian if (fwd_line[0] != '\0')
3131ceebf76Sflorian printf("%s\n", fwd_line);
3145c077b0fSflorian return (1);
3155c077b0fSflorian default:
3165c077b0fSflorian break;
3175c077b0fSflorian }
3185c077b0fSflorian
3195c077b0fSflorian return (0);
3205c077b0fSflorian }
3215c077b0fSflorian
3225c077b0fSflorian void
histogram_header(void)3232db6800fSotto histogram_header(void)
3245c077b0fSflorian {
3254b642e9eSotto const char head[] = "histograms: lifetime[ms], decaying[ms]";
3265c077b0fSflorian char buf[10];
3272db6800fSotto size_t i;
3285c077b0fSflorian
3294b642e9eSotto printf("\n%*s%*s\n%*s", 5, "",
330608e5d72Sotto (int)(72/2 + (sizeof(head)-1)/2), head, 6, "");
331943f8c8cSotto for(i = 0; i < nitems(histogram_limits) - 1; i++) {
3325c077b0fSflorian snprintf(buf, sizeof(buf), "<%lld", histogram_limits[i]);
3335c077b0fSflorian printf("%6s", buf);
3345c077b0fSflorian }
3355c077b0fSflorian printf("%6s\n", ">");
3362db6800fSotto }
3372db6800fSotto
3382db6800fSotto void
print_histogram(const char * name,int64_t histogram[],size_t n)3392db6800fSotto print_histogram(const char *name, int64_t histogram[], size_t n)
3402db6800fSotto {
3412db6800fSotto size_t i;
3422db6800fSotto
343608e5d72Sotto printf("%5s ", name);
3442db6800fSotto for(i = 0; i < n; i++)
3455c077b0fSflorian printf("%6lld", histogram[i]);
3465c077b0fSflorian printf("\n");
3475c077b0fSflorian }
348c071f090Sflorian
349c071f090Sflorian int
show_mem_msg(struct imsg * imsg)350c071f090Sflorian show_mem_msg(struct imsg *imsg)
351c071f090Sflorian {
352c071f090Sflorian struct ctl_mem_info *cmi;
353c071f090Sflorian
354c071f090Sflorian switch (imsg->hdr.type) {
355c071f090Sflorian case IMSG_CTL_MEM_INFO:
356c071f090Sflorian cmi = imsg->data;
357c071f090Sflorian printf("msg-cache: %zu / %zu (%.2f%%)\n", cmi->msg_cache_used,
358c071f090Sflorian cmi->msg_cache_max, 100.0 * cmi->msg_cache_used /
359c071f090Sflorian cmi->msg_cache_max);
360c071f090Sflorian printf("rrset-cache: %zu / %zu (%.2f%%)\n",
361c071f090Sflorian cmi->rrset_cache_used, cmi->rrset_cache_max, 100.0 *
362c071f090Sflorian cmi->rrset_cache_used / cmi->rrset_cache_max);
363c071f090Sflorian printf("key-cache: %zu / %zu (%.2f%%)\n", cmi->key_cache_used,
364c071f090Sflorian cmi->key_cache_max, 100.0 * cmi->key_cache_used /
365c071f090Sflorian cmi->key_cache_max);
366c071f090Sflorian printf("neg-cache: %zu / %zu (%.2f%%)\n", cmi->neg_cache_used,
367c071f090Sflorian cmi->neg_cache_max, 100.0 * cmi->neg_cache_used /
368c071f090Sflorian cmi->neg_cache_max);
369c071f090Sflorian break;
370c071f090Sflorian default:
371c071f090Sflorian break;
372c071f090Sflorian }
373c071f090Sflorian
374c071f090Sflorian return 1;
375c071f090Sflorian }
376