1 /* $OpenBSD: unwindctl.c,v 1.27 2021/02/27 10:32:29 florian Exp $ */ 2 3 /* 4 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> 5 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 6 * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 21 #include <sys/types.h> 22 #include <sys/queue.h> 23 #include <sys/socket.h> 24 #include <sys/un.h> 25 #include <netinet/in.h> 26 #include <arpa/inet.h> 27 #include <net/if.h> 28 #include <net/if_media.h> 29 #include <net/if_types.h> 30 #include <net/route.h> 31 32 #include <err.h> 33 #include <errno.h> 34 #include <event.h> 35 #include <imsg.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 41 #include "unwind.h" 42 #include "frontend.h" 43 #include "resolver.h" 44 #include "parser.h" 45 46 __dead void usage(void); 47 int show_status_msg(struct imsg *); 48 int show_autoconf_msg(struct imsg *); 49 int show_mem_msg(struct imsg *); 50 void histogram_header(void); 51 void print_histogram(const char *name, int64_t[], size_t); 52 53 struct imsgbuf *ibuf; 54 int info_cnt; 55 struct ctl_resolver_info info[UW_RES_NONE]; 56 57 __dead void 58 usage(void) 59 { 60 extern char *__progname; 61 62 fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n", 63 __progname); 64 exit(1); 65 } 66 67 int 68 main(int argc, char *argv[]) 69 { 70 struct sockaddr_un sun; 71 struct parse_result *res; 72 struct imsg imsg; 73 struct ctl_resolver_info *cri; 74 int ctl_sock; 75 int done = 0; 76 int i, j, k, n, verbose = 0; 77 int ch, column_offset; 78 char *sockname; 79 80 sockname = _PATH_UNWIND_SOCKET; 81 while ((ch = getopt(argc, argv, "s:")) != -1) { 82 switch (ch) { 83 case 's': 84 sockname = optarg; 85 break; 86 default: 87 usage(); 88 } 89 } 90 argc -= optind; 91 argv += optind; 92 93 /* Parse command line. */ 94 if ((res = parse(argc, argv)) == NULL) 95 exit(1); 96 97 /* Connect to control socket. */ 98 if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 99 err(1, "socket"); 100 101 memset(&sun, 0, sizeof(sun)); 102 sun.sun_family = AF_UNIX; 103 strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path)); 104 105 if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) 106 err(1, "connect: %s", sockname); 107 108 if (pledge("stdio", NULL) == -1) 109 err(1, "pledge"); 110 111 if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) 112 err(1, NULL); 113 imsg_init(ibuf, ctl_sock); 114 done = 0; 115 116 /* Check for root-only actions */ 117 switch (res->action) { 118 case LOG_DEBUG: 119 case LOG_VERBOSE: 120 case LOG_BRIEF: 121 case RELOAD: 122 if (geteuid() != 0) 123 errx(1, "need root privileges"); 124 break; 125 default: 126 break; 127 } 128 129 /* Process user request. */ 130 switch (res->action) { 131 case LOG_DEBUG: 132 verbose |= OPT_VERBOSE2; 133 /* FALLTHROUGH */ 134 case LOG_VERBOSE: 135 verbose |= OPT_VERBOSE; 136 /* FALLTHROUGH */ 137 case LOG_BRIEF: 138 imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1, 139 &verbose, sizeof(verbose)); 140 printf("logging request sent.\n"); 141 done = 1; 142 break; 143 case RELOAD: 144 imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0); 145 printf("reload request sent.\n"); 146 done = 1; 147 break; 148 case STATUS: 149 imsg_compose(ibuf, IMSG_CTL_STATUS, 0, 0, -1, NULL, 0); 150 break; 151 case AUTOCONF: 152 imsg_compose(ibuf, IMSG_CTL_AUTOCONF, 0, 0, -1, NULL, 0); 153 break; 154 case MEM: 155 imsg_compose(ibuf, IMSG_CTL_MEM, 0, 0, -1, NULL, 0); 156 break; 157 default: 158 usage(); 159 } 160 161 while (ibuf->w.queued) 162 if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) 163 err(1, "write error"); 164 165 while (!done) { 166 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 167 errx(1, "imsg_read error"); 168 if (n == 0) 169 errx(1, "pipe closed"); 170 171 while (!done) { 172 if ((n = imsg_get(ibuf, &imsg)) == -1) 173 errx(1, "imsg_get error"); 174 if (n == 0) 175 break; 176 177 switch (res->action) { 178 case STATUS: 179 done = show_status_msg(&imsg); 180 break; 181 case AUTOCONF: 182 done = show_autoconf_msg(&imsg); 183 break; 184 case MEM: 185 done = show_mem_msg(&imsg); 186 break; 187 default: 188 break; 189 } 190 imsg_free(&imsg); 191 } 192 } 193 close(ctl_sock); 194 free(ibuf); 195 196 column_offset = info_cnt / 2; 197 if (info_cnt % 2 == 1) 198 column_offset++; 199 200 for (i = 0; i < column_offset; i++) { 201 for (j = 0; j < 2; j++) { 202 k = i + j * column_offset; 203 if (k >= info_cnt) 204 break; 205 206 cri = &info[k]; 207 printf("%d. %-15s %10s, ", k + 1, 208 uw_resolver_type_str[cri->type], 209 uw_resolver_state_str[cri->state]); 210 if (cri->median == 0) 211 printf("%5s", "N/A"); 212 else if (cri->median == INT64_MAX) 213 printf("%5s", "Inf"); 214 else 215 printf("%3lldms", cri->median); 216 if (j == 0) 217 printf(" "); 218 } 219 printf("\n"); 220 } 221 222 if (info_cnt) 223 histogram_header(); 224 for (i = 0; i < info_cnt; i++) { 225 cri = &info[i]; 226 print_histogram(uw_resolver_type_short[cri->type], 227 cri->histogram, nitems(cri->histogram)); 228 print_histogram("", cri->latest_histogram, 229 nitems(cri->latest_histogram)); 230 } 231 return (0); 232 } 233 234 int 235 show_status_msg(struct imsg *imsg) 236 { 237 static char fwd_line[80]; 238 239 switch (imsg->hdr.type) { 240 case IMSG_CTL_RESOLVER_INFO: 241 memcpy(&info[info_cnt++], imsg->data, sizeof(info[0])); 242 break; 243 case IMSG_CTL_END: 244 if (fwd_line[0] != '\0') 245 printf("%s\n", fwd_line); 246 return (1); 247 default: 248 break; 249 } 250 251 return (0); 252 } 253 254 int 255 show_autoconf_msg(struct imsg *imsg) 256 { 257 static int autoconf_forwarders, last_src; 258 static int label_len, line_len; 259 static uint32_t last_if_index; 260 static char fwd_line[80]; 261 struct ctl_forwarder_info *cfi; 262 char ifnamebuf[IFNAMSIZ]; 263 char *if_name; 264 265 switch (imsg->hdr.type) { 266 case IMSG_CTL_AUTOCONF_RESOLVER_INFO: 267 cfi = imsg->data; 268 if (!autoconf_forwarders++) 269 printf("autoconfiguration forwarders:\n"); 270 if (cfi->if_index != last_if_index || cfi->src != last_src) { 271 if_name = if_indextoname(cfi->if_index, ifnamebuf); 272 if (fwd_line[0] != '\0') { 273 printf("%s\n", fwd_line); 274 fwd_line[0] = '\0'; 275 } 276 label_len = snprintf(fwd_line, sizeof(fwd_line), 277 "%s[%s]:", cfi->src == RTP_PROPOSAL_DHCLIENT ? 278 " DHCP" : "SLAAC", if_name ? if_name : "unknown"); 279 line_len = label_len; 280 last_if_index = cfi->if_index; 281 last_src = cfi->src; 282 } 283 284 if (line_len + 1 + strlen(cfi->ip) > sizeof(fwd_line)) { 285 printf("%s\n", fwd_line); 286 snprintf(fwd_line, sizeof(fwd_line), "%*s", label_len, 287 " "); 288 } 289 strlcat(fwd_line, " ", sizeof(fwd_line)); 290 line_len = strlcat(fwd_line, cfi->ip, sizeof(fwd_line)); 291 break; 292 case IMSG_CTL_END: 293 if (fwd_line[0] != '\0') 294 printf("%s\n", fwd_line); 295 return (1); 296 default: 297 break; 298 } 299 300 return (0); 301 } 302 303 void 304 histogram_header(void) 305 { 306 const char head[] = "histograms: lifetime[ms], decaying[ms]"; 307 char buf[10]; 308 size_t i; 309 310 printf("\n%*s%*s\n%*s", 5, "", 311 (int)(72/2 + (sizeof(head)-1)/2), head, 6, ""); 312 for(i = 0; i < nitems(histogram_limits) - 1; i++) { 313 snprintf(buf, sizeof(buf), "<%lld", histogram_limits[i]); 314 printf("%6s", buf); 315 } 316 printf("%6s\n", ">"); 317 } 318 319 void 320 print_histogram(const char *name, int64_t histogram[], size_t n) 321 { 322 size_t i; 323 324 printf("%5s ", name); 325 for(i = 0; i < n; i++) 326 printf("%6lld", histogram[i]); 327 printf("\n"); 328 } 329 330 int 331 show_mem_msg(struct imsg *imsg) 332 { 333 struct ctl_mem_info *cmi; 334 335 switch (imsg->hdr.type) { 336 case IMSG_CTL_MEM_INFO: 337 cmi = imsg->data; 338 printf("msg-cache: %zu / %zu (%.2f%%)\n", cmi->msg_cache_used, 339 cmi->msg_cache_max, 100.0 * cmi->msg_cache_used / 340 cmi->msg_cache_max); 341 printf("rrset-cache: %zu / %zu (%.2f%%)\n", 342 cmi->rrset_cache_used, cmi->rrset_cache_max, 100.0 * 343 cmi->rrset_cache_used / cmi->rrset_cache_max); 344 printf("key-cache: %zu / %zu (%.2f%%)\n", cmi->key_cache_used, 345 cmi->key_cache_max, 100.0 * cmi->key_cache_used / 346 cmi->key_cache_max); 347 printf("neg-cache: %zu / %zu (%.2f%%)\n", cmi->neg_cache_used, 348 cmi->neg_cache_max, 100.0 * cmi->neg_cache_used / 349 cmi->neg_cache_max); 350 break; 351 default: 352 break; 353 } 354 355 return 1; 356 } 357