#include <sys/types.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/route.h>
#include <err.h>
#include <errno.h>
#include <event.h>
#include <imsg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "unwind.h"
#include "frontend.h"
#include "resolver.h"
#include "parser.h"
__dead void usage(void);
int show_status_msg(struct imsg *);
int show_autoconf_msg(struct imsg *);
int show_mem_msg(struct imsg *);
void histogram_header(void);
void print_histogram(const char *name, int64_t[], size_t);
const char *prio2str(int);
struct imsgbuf *ibuf;
int info_cnt;
struct ctl_resolver_info info[UW_RES_NONE];
const char *
prio2str(int prio)
{
switch(prio) {
case RTP_PROPOSAL_DHCLIENT:
return "DHCP";
case RTP_PROPOSAL_SLAAC:
return "SLAAC";
case RTP_PROPOSAL_STATIC:
return "STATIC";
case RTP_PROPOSAL_UMB:
return "UMB";
case RTP_PROPOSAL_PPP:
return "PPP";
}
return "OTHER";
}
__dead void
usage(void)
{
extern char *__progname;
fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n",
__progname);
exit(1);
}
int
main(int argc, char *argv[])
{
struct sockaddr_un sun;
struct parse_result *res;
struct imsg imsg;
struct ctl_resolver_info *cri;
int ctl_sock;
int done = 0;
int i, j, k, n, verbose = 0;
int ch, column_offset;
char *sockname;
sockname = _PATH_UNWIND_SOCKET;
while ((ch = getopt(argc, argv, "s:")) != -1) {
switch (ch) {
case 's':
sockname = optarg;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if ((res = parse(argc, argv)) == NULL)
exit(1);
if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
err(1, "socket");
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path));
if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
err(1, "connect: %s", sockname);
if (pledge("stdio", NULL) == -1)
err(1, "pledge");
if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
err(1, NULL);
if (imsgbuf_init(ibuf, ctl_sock) == -1)
err(1, NULL);
done = 0;
switch (res->action) {
case LOG_DEBUG:
case LOG_VERBOSE:
case LOG_BRIEF:
case RELOAD:
if (geteuid() != 0)
errx(1, "need root privileges");
break;
default:
break;
}
switch (res->action) {
case LOG_DEBUG:
verbose |= OPT_VERBOSE2;
case LOG_VERBOSE:
verbose |= OPT_VERBOSE;
case LOG_BRIEF:
imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
&verbose, sizeof(verbose));
printf("logging request sent.\n");
done = 1;
break;
case RELOAD:
imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
printf("reload request sent.\n");
done = 1;
break;
case STATUS:
imsg_compose(ibuf, IMSG_CTL_STATUS, 0, 0, -1, NULL, 0);
break;
case AUTOCONF:
imsg_compose(ibuf, IMSG_CTL_AUTOCONF, 0, 0, -1, NULL, 0);
break;
case MEM:
imsg_compose(ibuf, IMSG_CTL_MEM, 0, 0, -1, NULL, 0);
break;
default:
usage();
}
if (imsgbuf_flush(ibuf) == -1)
err(1, "write error");
while (!done) {
if ((n = imsgbuf_read(ibuf)) == -1)
err(1, "read error");
if (n == 0)
errx(1, "pipe closed");
while (!done) {
if ((n = imsg_get(ibuf, &imsg)) == -1)
errx(1, "imsg_get error");
if (n == 0)
break;
switch (res->action) {
case STATUS:
done = show_status_msg(&imsg);
break;
case AUTOCONF:
done = show_autoconf_msg(&imsg);
break;
case MEM:
done = show_mem_msg(&imsg);
break;
default:
break;
}
imsg_free(&imsg);
}
}
close(ctl_sock);
free(ibuf);
column_offset = info_cnt / 2;
if (info_cnt % 2 == 1)
column_offset++;
for (i = 0; i < column_offset; i++) {
for (j = 0; j < 2; j++) {
k = i + j * column_offset;
if (k >= info_cnt)
break;
cri = &info[k];
printf("%d. %-15s %10s, ", k + 1,
uw_resolver_type_str[cri->type],
uw_resolver_state_str[cri->state]);
if (cri->median == 0)
printf("%5s", "N/A");
else if (cri->median == INT64_MAX)
printf("%5s", "Inf");
else
printf("%3lldms", cri->median);
if (j == 0)
printf(" ");
}
printf("\n");
}
if (info_cnt)
histogram_header();
for (i = 0; i < info_cnt; i++) {
cri = &info[i];
print_histogram(uw_resolver_type_short[cri->type],
cri->histogram, nitems(cri->histogram));
print_histogram("", cri->latest_histogram,
nitems(cri->latest_histogram));
}
return (0);
}
int
show_status_msg(struct imsg *imsg)
{
static char fwd_line[80];
switch (imsg->hdr.type) {
case IMSG_CTL_RESOLVER_INFO:
memcpy(&info[info_cnt++], imsg->data, sizeof(info[0]));
break;
case IMSG_CTL_END:
if (fwd_line[0] != '\0')
printf("%s\n", fwd_line);
return (1);
default:
break;
}
return (0);
}
int
show_autoconf_msg(struct imsg *imsg)
{
static int autoconf_forwarders, last_src;
static int label_len, line_len;
static uint32_t last_if_index;
static char fwd_line[80];
struct ctl_forwarder_info *cfi;
char ifnamebuf[IFNAMSIZ];
char *if_name;
switch (imsg->hdr.type) {
case IMSG_CTL_AUTOCONF_RESOLVER_INFO:
cfi = imsg->data;
if (!autoconf_forwarders++)
printf("autoconfiguration forwarders:\n");
if (cfi->if_index != last_if_index || cfi->src != last_src) {
if_name = if_indextoname(cfi->if_index, ifnamebuf);
if (fwd_line[0] != '\0') {
printf("%s\n", fwd_line);
fwd_line[0] = '\0';
}
label_len = snprintf(fwd_line, sizeof(fwd_line),
"%6s[%s]:", prio2str(cfi->src),
if_name ? if_name : "unknown");
line_len = label_len;
last_if_index = cfi->if_index;
last_src = cfi->src;
}
if (line_len + 1 + strlen(cfi->ip) > sizeof(fwd_line)) {
printf("%s\n", fwd_line);
snprintf(fwd_line, sizeof(fwd_line), "%*s", label_len,
" ");
}
strlcat(fwd_line, " ", sizeof(fwd_line));
line_len = strlcat(fwd_line, cfi->ip, sizeof(fwd_line));
break;
case IMSG_CTL_END:
if (fwd_line[0] != '\0')
printf("%s\n", fwd_line);
return (1);
default:
break;
}
return (0);
}
void
histogram_header(void)
{
const char head[] = "histograms: lifetime[ms], decaying[ms]";
char buf[10];
size_t i;
printf("\n%*s%*s\n%*s", 5, "",
(int)(72/2 + (sizeof(head)-1)/2), head, 6, "");
for(i = 0; i < nitems(histogram_limits) - 1; i++) {
snprintf(buf, sizeof(buf), "<%lld", histogram_limits[i]);
printf("%6s", buf);
}
printf("%6s\n", ">");
}
void
print_histogram(const char *name, int64_t histogram[], size_t n)
{
size_t i;
printf("%5s ", name);
for(i = 0; i < n; i++)
printf("%6lld", histogram[i]);
printf("\n");
}
int
show_mem_msg(struct imsg *imsg)
{
struct ctl_mem_info *cmi;
switch (imsg->hdr.type) {
case IMSG_CTL_MEM_INFO:
cmi = imsg->data;
printf("msg-cache: %zu / %zu (%.2f%%)\n", cmi->msg_cache_used,
cmi->msg_cache_max, 100.0 * cmi->msg_cache_used /
cmi->msg_cache_max);
printf("rrset-cache: %zu / %zu (%.2f%%)\n",
cmi->rrset_cache_used, cmi->rrset_cache_max, 100.0 *
cmi->rrset_cache_used / cmi->rrset_cache_max);
printf("key-cache: %zu / %zu (%.2f%%)\n", cmi->key_cache_used,
cmi->key_cache_max, 100.0 * cmi->key_cache_used /
cmi->key_cache_max);
printf("neg-cache: %zu / %zu (%.2f%%)\n", cmi->neg_cache_used,
cmi->neg_cache_max, 100.0 * cmi->neg_cache_used /
cmi->neg_cache_max);
break;
default:
break;
}
return 1;
}