1*f1b790a5Sclaudio /* $OpenBSD: unwindctl.c,v 1.34 2024/11/21 13:38:15 claudio 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 * 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"; 706371cd0bSbket case RTP_PROPOSAL_PPP: 716371cd0bSbket return "PPP"; 7228d3b5f7Stobhe } 7328d3b5f7Stobhe return "OTHER"; 7428d3b5f7Stobhe } 7528d3b5f7Stobhe 765c077b0fSflorian __dead 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 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); 132*f1b790a5Sclaudio if (imsgbuf_init(ibuf, ctl_sock) == -1) 133*f1b790a5Sclaudio err(1, NULL); 1345c077b0fSflorian done = 0; 1355c077b0fSflorian 1363a1cc939Ssolene /* Check for root-only actions */ 1373a1cc939Ssolene switch (res->action) { 1383a1cc939Ssolene case LOG_DEBUG: 1393a1cc939Ssolene case LOG_VERBOSE: 1403a1cc939Ssolene case LOG_BRIEF: 1413a1cc939Ssolene case RELOAD: 1423a1cc939Ssolene if (geteuid() != 0) 1433a1cc939Ssolene errx(1, "need root privileges"); 1443a1cc939Ssolene break; 1453a1cc939Ssolene default: 1463a1cc939Ssolene break; 1473a1cc939Ssolene } 1483a1cc939Ssolene 1495c077b0fSflorian /* Process user request. */ 1505c077b0fSflorian switch (res->action) { 1515c077b0fSflorian case LOG_DEBUG: 1525c077b0fSflorian verbose |= OPT_VERBOSE2; 1535c077b0fSflorian /* FALLTHROUGH */ 1545c077b0fSflorian case LOG_VERBOSE: 1555c077b0fSflorian verbose |= OPT_VERBOSE; 1565c077b0fSflorian /* FALLTHROUGH */ 1575c077b0fSflorian case LOG_BRIEF: 1585c077b0fSflorian imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1, 1595c077b0fSflorian &verbose, sizeof(verbose)); 1605c077b0fSflorian printf("logging request sent.\n"); 1615c077b0fSflorian done = 1; 1625c077b0fSflorian break; 1635c077b0fSflorian case RELOAD: 1645c077b0fSflorian imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0); 1655c077b0fSflorian printf("reload request sent.\n"); 1665c077b0fSflorian done = 1; 1675c077b0fSflorian break; 1685c077b0fSflorian case STATUS: 169f6a28683Sotto imsg_compose(ibuf, IMSG_CTL_STATUS, 0, 0, -1, NULL, 0); 1702db6800fSotto break; 17115fe126bSflorian case AUTOCONF: 17215fe126bSflorian imsg_compose(ibuf, IMSG_CTL_AUTOCONF, 0, 0, -1, NULL, 0); 17315fe126bSflorian break; 174c071f090Sflorian case MEM: 175c071f090Sflorian imsg_compose(ibuf, IMSG_CTL_MEM, 0, 0, -1, NULL, 0); 176c071f090Sflorian break; 1775c077b0fSflorian default: 1785c077b0fSflorian usage(); 1795c077b0fSflorian } 1805c077b0fSflorian 181dd7efffeSclaudio if (imsgbuf_flush(ibuf) == -1) 1825c077b0fSflorian err(1, "write error"); 1835c077b0fSflorian 1845c077b0fSflorian while (!done) { 185668e5ba9Sclaudio if ((n = imsgbuf_read(ibuf)) == -1) 186ef2e27a1Sclaudio err(1, "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 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 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 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 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 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