xref: /openbsd/usr.bin/kstat/kstat.c (revision 7b6132c8)
1*7b6132c8Sdlg /* $OpenBSD: kstat.c,v 1.14 2024/03/26 00:54:24 dlg Exp $ */
2ab45bcdeSdlg 
3ab45bcdeSdlg /*
4ab45bcdeSdlg  * Copyright (c) 2020 David Gwynne <dlg@openbsd.org>
5ab45bcdeSdlg  * Permission to use, copy, modify, and distribute this software for any
6ab45bcdeSdlg  * purpose with or without fee is hereby granted, provided that the above
7ab45bcdeSdlg  * copyright notice and this permission notice appear in all copies.
8ab45bcdeSdlg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9ab45bcdeSdlg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10ab45bcdeSdlg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11ab45bcdeSdlg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12ab45bcdeSdlg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13ab45bcdeSdlg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14ab45bcdeSdlg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15ab45bcdeSdlg  */
16ab45bcdeSdlg 
17ab45bcdeSdlg #include <ctype.h>
186f9574e0Scheloha #include <limits.h>
196f9574e0Scheloha #include <signal.h>
20ab45bcdeSdlg #include <stdio.h>
21ab45bcdeSdlg #include <stdlib.h>
22ab45bcdeSdlg #include <stddef.h>
23ab45bcdeSdlg #include <string.h>
24ab45bcdeSdlg #include <inttypes.h>
2559ead937Sdlg #include <fnmatch.h>
26ab45bcdeSdlg #include <fcntl.h>
2762fc0d78Sdlg #include <unistd.h>
28ab45bcdeSdlg #include <errno.h>
29ab45bcdeSdlg #include <err.h>
30ab45bcdeSdlg #include <vis.h>
31ab45bcdeSdlg 
32ab45bcdeSdlg #include <sys/tree.h>
33ab45bcdeSdlg #include <sys/ioctl.h>
34ab45bcdeSdlg #include <sys/time.h>
3559ead937Sdlg #include <sys/queue.h>
36ab45bcdeSdlg 
37ab45bcdeSdlg #include <sys/kstat.h>
38ab45bcdeSdlg 
39ab45bcdeSdlg #ifndef roundup
40ab45bcdeSdlg #define roundup(x, y)		((((x)+((y)-1))/(y))*(y))
41ab45bcdeSdlg #endif
42ab45bcdeSdlg 
4359ead937Sdlg #ifndef nitems
4459ead937Sdlg #define nitems(_a)		(sizeof((_a)) / sizeof((_a)[0]))
4559ead937Sdlg #endif
4659ead937Sdlg 
4759ead937Sdlg #ifndef ISSET
4859ead937Sdlg #define ISSET(_i, _m)		((_i) & (_m))
4959ead937Sdlg #endif
5059ead937Sdlg 
5159ead937Sdlg #ifndef SET
5259ead937Sdlg #define SET(_i, _m)		((_i) |= (_m))
5359ead937Sdlg #endif
5459ead937Sdlg 
5553ff30ddSdlg struct fmt_result {
5653ff30ddSdlg 	uint64_t		val;
5753ff30ddSdlg 	unsigned int		frac;
5853ff30ddSdlg 	unsigned int		exp;
5953ff30ddSdlg };
6053ff30ddSdlg 
6153ff30ddSdlg static void
fmt_thing(struct fmt_result * fr,uint64_t val,uint64_t chunk)6253ff30ddSdlg fmt_thing(struct fmt_result *fr, uint64_t val, uint64_t chunk)
6353ff30ddSdlg {
6453ff30ddSdlg 	unsigned int exp = 0;
6553ff30ddSdlg 	uint64_t rem = 0;
6653ff30ddSdlg 
6753ff30ddSdlg 	while (val > chunk) {
6853ff30ddSdlg 		rem = val % chunk;
6953ff30ddSdlg 		val /= chunk;
7053ff30ddSdlg 		exp++;
7153ff30ddSdlg 	}
7253ff30ddSdlg 
7353ff30ddSdlg 	fr->val = val;
7453ff30ddSdlg 	fr->exp = exp;
7553ff30ddSdlg 	fr->frac = (rem * 1000) / chunk;
7653ff30ddSdlg }
7753ff30ddSdlg 
7859ead937Sdlg #define str_is_empty(_str)	(*(_str) == '\0')
7959ead937Sdlg 
80ab45bcdeSdlg #define DEV_KSTAT "/dev/kstat"
81ab45bcdeSdlg 
8259ead937Sdlg struct kstat_filter {
8359ead937Sdlg 	TAILQ_ENTRY(kstat_filter)	 kf_entry;
8459ead937Sdlg 	const char			*kf_provider;
8559ead937Sdlg 	const char			*kf_name;
8659ead937Sdlg 	unsigned int			 kf_flags;
8759ead937Sdlg #define KSTAT_FILTER_F_INST			(1 << 0)
8859ead937Sdlg #define KSTAT_FILTER_F_UNIT			(1 << 1)
8959ead937Sdlg 	unsigned int			 kf_instance;
9059ead937Sdlg 	unsigned int			 kf_unit;
9159ead937Sdlg };
92ab45bcdeSdlg 
9359ead937Sdlg TAILQ_HEAD(kstat_filters, kstat_filter);
9459ead937Sdlg 
9581e355e4Sdlg struct kstat_entry {
9681e355e4Sdlg 	struct kstat_req	kstat;
9781e355e4Sdlg 	RBT_ENTRY(kstat_entry)	entry;
9881e355e4Sdlg 	int			serrno;
9981e355e4Sdlg };
10081e355e4Sdlg 
10181e355e4Sdlg RBT_HEAD(kstat_tree, kstat_entry);
10281e355e4Sdlg 
10381e355e4Sdlg static inline int
kstat_cmp(const struct kstat_entry * ea,const struct kstat_entry * eb)10481e355e4Sdlg kstat_cmp(const struct kstat_entry *ea, const struct kstat_entry *eb)
10581e355e4Sdlg {
10681e355e4Sdlg 	const struct kstat_req *a = &ea->kstat;
10781e355e4Sdlg 	const struct kstat_req *b = &eb->kstat;
10881e355e4Sdlg 	int rv;
10981e355e4Sdlg 
11081e355e4Sdlg 	rv = strncmp(a->ks_provider, b->ks_provider, sizeof(a->ks_provider));
11181e355e4Sdlg 	if (rv != 0)
11281e355e4Sdlg 		return (rv);
11381e355e4Sdlg 	if (a->ks_instance > b->ks_instance)
11481e355e4Sdlg 		return (1);
11581e355e4Sdlg 	if (a->ks_instance < b->ks_instance)
11681e355e4Sdlg 		return (-1);
11781e355e4Sdlg 
11881e355e4Sdlg 	rv = strncmp(a->ks_name, b->ks_name, sizeof(a->ks_name));
11981e355e4Sdlg 	if (rv != 0)
12081e355e4Sdlg 		return (rv);
12181e355e4Sdlg 	if (a->ks_unit > b->ks_unit)
12281e355e4Sdlg 		return (1);
12381e355e4Sdlg 	if (a->ks_unit < b->ks_unit)
12481e355e4Sdlg 		return (-1);
12581e355e4Sdlg 
12681e355e4Sdlg 	return (0);
12781e355e4Sdlg }
12881e355e4Sdlg 
12981e355e4Sdlg RBT_PROTOTYPE(kstat_tree, kstat_entry, entry, kstat_cmp);
13081e355e4Sdlg RBT_GENERATE(kstat_tree, kstat_entry, entry, kstat_cmp);
13181e355e4Sdlg 
1326f9574e0Scheloha static void handle_alrm(int);
13359ead937Sdlg static struct kstat_filter *
13459ead937Sdlg 		kstat_filter_parse(char *);
13559ead937Sdlg static int	kstat_filter_entry(struct kstat_filters *,
13659ead937Sdlg 		    const struct kstat_req *);
13759ead937Sdlg 
13862fc0d78Sdlg static void	kstat_list(struct kstat_tree *, int, unsigned int,
13962fc0d78Sdlg 		    struct kstat_filters *);
14062fc0d78Sdlg static void	kstat_print(struct kstat_tree *);
1416b04bb45Sdlg static void	kstat_read(struct kstat_tree *, int);
14259ead937Sdlg 
143ab45bcdeSdlg __dead static void
usage(void)144ab45bcdeSdlg usage(void)
145ab45bcdeSdlg {
146ab45bcdeSdlg 	extern char *__progname;
14759ead937Sdlg 
1486b04bb45Sdlg 	fprintf(stderr, "usage: %s [-w wait] "
1498b182afbSkn 	    "[name | provider:instance:name:unit] ...\n", __progname);
15059ead937Sdlg 
151ab45bcdeSdlg 	exit(1);
152ab45bcdeSdlg }
153ab45bcdeSdlg 
154ab45bcdeSdlg int
main(int argc,char * argv[])155ab45bcdeSdlg main(int argc, char *argv[])
156ab45bcdeSdlg {
15759ead937Sdlg 	struct kstat_filters kfs = TAILQ_HEAD_INITIALIZER(kfs);
15862fc0d78Sdlg 	struct kstat_tree kt = RBT_INITIALIZER();
159ab45bcdeSdlg 	unsigned int version;
160ab45bcdeSdlg 	int fd;
1616b04bb45Sdlg 	const char *errstr;
1626b04bb45Sdlg 	int ch;
1636f9574e0Scheloha 	struct itimerval itv;
1646f9574e0Scheloha 	sigset_t empty, mask;
16559ead937Sdlg 	int i;
1666f9574e0Scheloha 	unsigned int wait = 0;
16759ead937Sdlg 
1686b04bb45Sdlg 	while ((ch = getopt(argc, argv, "w:")) != -1) {
1696b04bb45Sdlg 		switch (ch) {
1706b04bb45Sdlg 		case 'w':
1716f9574e0Scheloha 			wait = strtonum(optarg, 1, UINT_MAX, &errstr);
1726b04bb45Sdlg 			if (errstr != NULL)
1736f9574e0Scheloha 				errx(1, "wait is %s: %s", errstr, optarg);
1746b04bb45Sdlg 			break;
1756b04bb45Sdlg 		default:
1766b04bb45Sdlg 			usage();
1776b04bb45Sdlg 		}
1786b04bb45Sdlg 	}
1796b04bb45Sdlg 
1806b04bb45Sdlg 	argc -= optind;
1816b04bb45Sdlg 	argv += optind;
1826b04bb45Sdlg 
1836b04bb45Sdlg 	for (i = 0; i < argc; i++) {
18459ead937Sdlg 		struct kstat_filter *kf = kstat_filter_parse(argv[i]);
18559ead937Sdlg 		TAILQ_INSERT_TAIL(&kfs, kf, kf_entry);
18659ead937Sdlg 	}
187ab45bcdeSdlg 
188ab45bcdeSdlg 	fd = open(DEV_KSTAT, O_RDONLY);
189ab45bcdeSdlg 	if (fd == -1)
190ab45bcdeSdlg 		err(1, "%s", DEV_KSTAT);
191ab45bcdeSdlg 
192ab45bcdeSdlg 	if (ioctl(fd, KSTATIOC_VERSION, &version) == -1)
193ab45bcdeSdlg 		err(1, "kstat version");
194ab45bcdeSdlg 
19562fc0d78Sdlg 	kstat_list(&kt, fd, version, &kfs);
19618bc31b7Sdlg 	kstat_read(&kt, fd);
19762fc0d78Sdlg 	kstat_print(&kt);
19859ead937Sdlg 
1996f9574e0Scheloha 	if (wait == 0)
2006b04bb45Sdlg 		return (0);
2016b04bb45Sdlg 
2026f9574e0Scheloha 	if (signal(SIGALRM, handle_alrm) == SIG_ERR)
2036f9574e0Scheloha 		err(1, "signal");
2046f9574e0Scheloha 	sigemptyset(&empty);
2056f9574e0Scheloha 	sigemptyset(&mask);
2066f9574e0Scheloha 	sigaddset(&mask, SIGALRM);
2076f9574e0Scheloha 	if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
2086f9574e0Scheloha 		err(1, "sigprocmask");
2096b04bb45Sdlg 
2106f9574e0Scheloha 	itv.it_value.tv_sec = wait;
2116f9574e0Scheloha 	itv.it_value.tv_usec = 0;
2126f9574e0Scheloha 	itv.it_interval = itv.it_value;
2136f9574e0Scheloha 	if (setitimer(ITIMER_REAL, &itv, NULL) == -1)
2146f9574e0Scheloha 		err(1, "setitimer");
2156f9574e0Scheloha 
2166f9574e0Scheloha 	for (;;) {
2176f9574e0Scheloha 		sigsuspend(&empty);
2186b04bb45Sdlg 		kstat_read(&kt, fd);
2196b04bb45Sdlg 		kstat_print(&kt);
2206b04bb45Sdlg 	}
2216b04bb45Sdlg 
22259ead937Sdlg 	return (0);
22359ead937Sdlg }
22459ead937Sdlg 
22559ead937Sdlg static struct kstat_filter *
kstat_filter_parse(char * arg)22659ead937Sdlg kstat_filter_parse(char *arg)
22759ead937Sdlg {
22859ead937Sdlg 	struct kstat_filter *kf;
22959ead937Sdlg 	const char *errstr;
23059ead937Sdlg 	char *argv[4];
23159ead937Sdlg 	size_t argc;
23259ead937Sdlg 
23359ead937Sdlg 	for (argc = 0; argc < nitems(argv); argc++) {
23459ead937Sdlg 		char *s = strsep(&arg, ":");
23559ead937Sdlg 		if (s == NULL)
23659ead937Sdlg 			break;
23759ead937Sdlg 
23859ead937Sdlg 		argv[argc] = s;
23959ead937Sdlg 	}
24059ead937Sdlg 	if (arg != NULL)
24159ead937Sdlg 		usage();
24259ead937Sdlg 
24359ead937Sdlg 	kf = malloc(sizeof(*kf));
24459ead937Sdlg 	if (kf == NULL)
24559ead937Sdlg 		err(1, NULL);
24659ead937Sdlg 
24759ead937Sdlg 	memset(kf, 0, sizeof(*kf));
24859ead937Sdlg 
24959ead937Sdlg 	switch (argc) {
25059ead937Sdlg 	case 1:
25159ead937Sdlg 		if (str_is_empty(argv[0]))
25259ead937Sdlg 			errx(1, "empty name");
25359ead937Sdlg 
25459ead937Sdlg 		kf->kf_name = argv[0];
25559ead937Sdlg 		break;
25659ead937Sdlg 	case 4:
25759ead937Sdlg 		if (!str_is_empty(argv[0]))
25859ead937Sdlg 			kf->kf_provider = argv[0];
25959ead937Sdlg 		if (!str_is_empty(argv[1])) {
26059ead937Sdlg 			kf->kf_instance =
26159ead937Sdlg 			    strtonum(argv[1], 0, 0xffffffffU, &errstr);
26259ead937Sdlg 			if (errstr != NULL) {
26359ead937Sdlg 				errx(1, "%s:%s:%s:%s: instance %s: %s",
26459ead937Sdlg 				    argv[0], argv[1], argv[2], argv[3],
26559ead937Sdlg 				    argv[1], errstr);
26659ead937Sdlg 			}
26759ead937Sdlg 			SET(kf->kf_flags, KSTAT_FILTER_F_INST);
26859ead937Sdlg 		}
26959ead937Sdlg 		if (!str_is_empty(argv[2]))
27059ead937Sdlg 			kf->kf_name = argv[2];
27159ead937Sdlg 		if (!str_is_empty(argv[3])) {
27259ead937Sdlg 			kf->kf_unit =
27359ead937Sdlg 			    strtonum(argv[3], 0, 0xffffffffU, &errstr);
27459ead937Sdlg 			if (errstr != NULL) {
275d572c2d2Sdlg 				errx(1, "%s:%s:%s:%s: unit %s: %s",
27659ead937Sdlg 				    argv[0], argv[1], argv[2], argv[3],
277d572c2d2Sdlg 				    argv[3], errstr);
27859ead937Sdlg 			}
279d572c2d2Sdlg 			SET(kf->kf_flags, KSTAT_FILTER_F_UNIT);
28059ead937Sdlg 		}
28159ead937Sdlg 		break;
28259ead937Sdlg 	default:
28359ead937Sdlg 		usage();
28459ead937Sdlg 	}
28559ead937Sdlg 
28659ead937Sdlg 	return (kf);
28759ead937Sdlg }
28859ead937Sdlg 
28959ead937Sdlg static int
kstat_filter_entry(struct kstat_filters * kfs,const struct kstat_req * ksreq)29059ead937Sdlg kstat_filter_entry(struct kstat_filters *kfs, const struct kstat_req *ksreq)
29159ead937Sdlg {
29259ead937Sdlg 	struct kstat_filter *kf;
29359ead937Sdlg 
29459ead937Sdlg 	if (TAILQ_EMPTY(kfs))
29559ead937Sdlg 		return (1);
29659ead937Sdlg 
29759ead937Sdlg 	TAILQ_FOREACH(kf, kfs, kf_entry) {
29859ead937Sdlg 		if (kf->kf_provider != NULL) {
29959ead937Sdlg 			if (fnmatch(kf->kf_provider, ksreq->ks_provider,
30059ead937Sdlg 			    FNM_NOESCAPE | FNM_LEADING_DIR) == FNM_NOMATCH)
30159ead937Sdlg 				continue;
30259ead937Sdlg 		}
30359ead937Sdlg 		if (ISSET(kf->kf_flags, KSTAT_FILTER_F_INST)) {
30459ead937Sdlg 			if (kf->kf_instance != ksreq->ks_instance)
30559ead937Sdlg 				continue;
30659ead937Sdlg 		}
30759ead937Sdlg 		if (kf->kf_name != NULL) {
30859ead937Sdlg 			if (fnmatch(kf->kf_name, ksreq->ks_name,
30959ead937Sdlg 			    FNM_NOESCAPE | FNM_LEADING_DIR) == FNM_NOMATCH)
31059ead937Sdlg 				continue;
31159ead937Sdlg 		}
31259ead937Sdlg 		if (ISSET(kf->kf_flags, KSTAT_FILTER_F_UNIT)) {
31359ead937Sdlg 			if (kf->kf_unit != ksreq->ks_unit)
31459ead937Sdlg 				continue;
31559ead937Sdlg 		}
31659ead937Sdlg 
31759ead937Sdlg 		return (1);
31859ead937Sdlg 	}
319ab45bcdeSdlg 
320ab45bcdeSdlg 	return (0);
321ab45bcdeSdlg }
322ab45bcdeSdlg 
323ab45bcdeSdlg static int
printable(int ch)324ab45bcdeSdlg printable(int ch)
325ab45bcdeSdlg {
326ab45bcdeSdlg 	if (ch == '\0')
327ab45bcdeSdlg 		return ('_');
328ab45bcdeSdlg 	if (!isprint(ch))
329ab45bcdeSdlg 		return ('~');
330ab45bcdeSdlg 	return (ch);
331ab45bcdeSdlg }
332ab45bcdeSdlg 
333ab45bcdeSdlg static void
hexdump(const void * d,size_t datalen)334ab45bcdeSdlg hexdump(const void *d, size_t datalen)
335ab45bcdeSdlg {
336ab45bcdeSdlg 	const uint8_t *data = d;
337ab45bcdeSdlg 	size_t i, j = 0;
338ab45bcdeSdlg 
339ab45bcdeSdlg 	for (i = 0; i < datalen; i += j) {
340ab45bcdeSdlg 		printf("%4zu: ", i);
341ab45bcdeSdlg 
342ab45bcdeSdlg 		for (j = 0; j < 16 && i+j < datalen; j++)
343ab45bcdeSdlg 			printf("%02x ", data[i + j]);
344ab45bcdeSdlg 		while (j++ < 16)
345ab45bcdeSdlg 			printf("   ");
346ab45bcdeSdlg 		printf("|");
347ab45bcdeSdlg 
348ab45bcdeSdlg 		for (j = 0; j < 16 && i+j < datalen; j++)
349ab45bcdeSdlg 			putchar(printable(data[i + j]));
350ab45bcdeSdlg 		printf("|\n");
351ab45bcdeSdlg 	}
352ab45bcdeSdlg }
353ab45bcdeSdlg 
354ab45bcdeSdlg static void
strdump(const void * s,size_t len)355ab45bcdeSdlg strdump(const void *s, size_t len)
356ab45bcdeSdlg {
357ab45bcdeSdlg 	const char *str = s;
358ab45bcdeSdlg 	char dst[8];
359ab45bcdeSdlg 	size_t i;
360ab45bcdeSdlg 
361ab45bcdeSdlg 	for (i = 0; i < len; i++) {
362ab45bcdeSdlg 		char ch = str[i];
363ab45bcdeSdlg 		if (ch == '\0')
364ab45bcdeSdlg 			break;
365ab45bcdeSdlg 
366ab45bcdeSdlg 		vis(dst, ch, VIS_TAB | VIS_NL, 0);
367ab45bcdeSdlg 		printf("%s", dst);
368ab45bcdeSdlg 	}
369ab45bcdeSdlg }
370ab45bcdeSdlg 
371ab45bcdeSdlg static void
strdumpnl(const void * s,size_t len)372ab45bcdeSdlg strdumpnl(const void *s, size_t len)
373ab45bcdeSdlg {
374ab45bcdeSdlg 	strdump(s, len);
375ab45bcdeSdlg 	printf("\n");
376ab45bcdeSdlg }
377ab45bcdeSdlg 
37853ff30ddSdlg static const char *si_prefixes[] = { "", "k", "M", "G", "T", "P", "E" };
37953ff30ddSdlg #ifdef notyet
38053ff30ddSdlg static const char *iec_prefixes[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" };
38153ff30ddSdlg #endif
38253ff30ddSdlg 
383ab45bcdeSdlg static void
kstat_kv(const void * d,ssize_t len)384ab45bcdeSdlg kstat_kv(const void *d, ssize_t len)
385ab45bcdeSdlg {
386ab45bcdeSdlg 	const uint8_t *buf;
387ab45bcdeSdlg 	const struct kstat_kv *kv;
388ab45bcdeSdlg 	ssize_t blen;
389ab45bcdeSdlg 	void (*trailer)(const void *, size_t);
390ab45bcdeSdlg 	double f;
39153ff30ddSdlg 	struct fmt_result fr;
392ab45bcdeSdlg 
393ab45bcdeSdlg 	if (len < (ssize_t)sizeof(*kv)) {
394ab45bcdeSdlg 		warn("short kv (len %zu < size %zu)", len, sizeof(*kv));
395ab45bcdeSdlg 		return;
396ab45bcdeSdlg 	}
397ab45bcdeSdlg 
398ab45bcdeSdlg 	buf = d;
399ab45bcdeSdlg 	do {
400ab45bcdeSdlg 		kv = (const struct kstat_kv *)buf;
401ab45bcdeSdlg 
402ab45bcdeSdlg 		buf += sizeof(*kv);
403ab45bcdeSdlg 		len -= sizeof(*kv);
404ab45bcdeSdlg 
405ab45bcdeSdlg 		blen = 0;
406ab45bcdeSdlg 		trailer = hexdump;
407ab45bcdeSdlg 
408ab45bcdeSdlg 		printf("%16.16s: ", kv->kv_key);
409ab45bcdeSdlg 
410ab45bcdeSdlg 		switch (kv->kv_type) {
411ab45bcdeSdlg 		case KSTAT_KV_T_NULL:
412ab45bcdeSdlg 			printf("null");
413ab45bcdeSdlg 			break;
414ab45bcdeSdlg 		case KSTAT_KV_T_BOOL:
415ab45bcdeSdlg 			printf("%s", kstat_kv_bool(kv) ? "true" : "false");
416ab45bcdeSdlg 			break;
417ab45bcdeSdlg 		case KSTAT_KV_T_COUNTER64:
418ab45bcdeSdlg 		case KSTAT_KV_T_UINT64:
419ab45bcdeSdlg 			printf("%" PRIu64, kstat_kv_u64(kv));
420ab45bcdeSdlg 			break;
421ab45bcdeSdlg 		case KSTAT_KV_T_INT64:
422ab45bcdeSdlg 			printf("%" PRId64, kstat_kv_s64(kv));
423ab45bcdeSdlg 			break;
424ab45bcdeSdlg 		case KSTAT_KV_T_COUNTER32:
425ab45bcdeSdlg 		case KSTAT_KV_T_UINT32:
426ab45bcdeSdlg 			printf("%" PRIu32, kstat_kv_u32(kv));
427ab45bcdeSdlg 			break;
428ab45bcdeSdlg 		case KSTAT_KV_T_INT32:
429ab45bcdeSdlg 			printf("%" PRId32, kstat_kv_s32(kv));
430ab45bcdeSdlg 			break;
4313564cc7fSdlg 		case KSTAT_KV_T_COUNTER16:
4323564cc7fSdlg 		case KSTAT_KV_T_UINT16:
4333564cc7fSdlg 			printf("%" PRIu16, kstat_kv_u16(kv));
4343564cc7fSdlg 			break;
4353564cc7fSdlg 		case KSTAT_KV_T_INT16:
4363564cc7fSdlg 			printf("%" PRId16, kstat_kv_s16(kv));
4373564cc7fSdlg 			break;
438ab45bcdeSdlg 		case KSTAT_KV_T_STR:
439ab45bcdeSdlg 			blen = kstat_kv_len(kv);
440ab45bcdeSdlg 			trailer = strdumpnl;
441ab45bcdeSdlg 			break;
442ab45bcdeSdlg 		case KSTAT_KV_T_BYTES:
443ab45bcdeSdlg 			blen = kstat_kv_len(kv);
444ab45bcdeSdlg 			trailer = hexdump;
445ab45bcdeSdlg 
446ab45bcdeSdlg 			printf("\n");
447ab45bcdeSdlg 			break;
448ab45bcdeSdlg 
449ab45bcdeSdlg 		case KSTAT_KV_T_ISTR:
450ab45bcdeSdlg 			strdump(kstat_kv_istr(kv), sizeof(kstat_kv_istr(kv)));
451ab45bcdeSdlg 			break;
452ab45bcdeSdlg 
453ab45bcdeSdlg 		case KSTAT_KV_T_TEMP:
454ab45bcdeSdlg 			f = kstat_kv_temp(kv);
455ab45bcdeSdlg 			printf("%.2f degC", (f - 273150000.0) / 1000000.0);
456ab45bcdeSdlg 			break;
457ab45bcdeSdlg 
45853ff30ddSdlg 		case KSTAT_KV_T_FREQ:
45953ff30ddSdlg 			fmt_thing(&fr, kstat_kv_freq(kv), 1000);
46053ff30ddSdlg 			printf("%llu", fr.val);
46153ff30ddSdlg 			if (fr.frac > 10)
46253ff30ddSdlg 				printf(".%02u", fr.frac / 10);
46353ff30ddSdlg 			printf(" %sHz", si_prefixes[fr.exp]);
46453ff30ddSdlg 			break;
46553ff30ddSdlg 
46653ff30ddSdlg 		case KSTAT_KV_T_VOLTS_DC: /* uV */
46753ff30ddSdlg 			f = kstat_kv_volts(kv);
46853ff30ddSdlg 			printf("%.2f VDC", f / 1000000.0);
46953ff30ddSdlg 			break;
47053ff30ddSdlg 
47153ff30ddSdlg 		case KSTAT_KV_T_VOLTS_AC: /* uV */
47253ff30ddSdlg 			f = kstat_kv_volts(kv);
47353ff30ddSdlg 			printf("%.2f VAC", f / 1000000.0);
47453ff30ddSdlg 			break;
47553ff30ddSdlg 
476*7b6132c8Sdlg 		case KSTAT_KV_T_AMPS: /* uA */
477*7b6132c8Sdlg 			f = kstat_kv_amps(kv);
478*7b6132c8Sdlg 			printf("%.3f A", f / 1000000.0);
479*7b6132c8Sdlg 			break;
480*7b6132c8Sdlg 
481*7b6132c8Sdlg 		case KSTAT_KV_T_WATTS: /* uW */
482*7b6132c8Sdlg 			f = kstat_kv_watts(kv);
483*7b6132c8Sdlg 			printf("%.3f W", f / 1000000.0);
484*7b6132c8Sdlg 			break;
485*7b6132c8Sdlg 
486ab45bcdeSdlg 		default:
487ab45bcdeSdlg 			printf("unknown type %u, stopping\n", kv->kv_type);
488ab45bcdeSdlg 			return;
489ab45bcdeSdlg 		}
490ab45bcdeSdlg 
491ab45bcdeSdlg 		switch (kv->kv_unit) {
492ab45bcdeSdlg 		case KSTAT_KV_U_NONE:
493ab45bcdeSdlg 			break;
494ab45bcdeSdlg 		case KSTAT_KV_U_PACKETS:
495ab45bcdeSdlg 			printf(" packets");
496ab45bcdeSdlg 			break;
497ab45bcdeSdlg 		case KSTAT_KV_U_BYTES:
498ab45bcdeSdlg 			printf(" bytes");
499ab45bcdeSdlg 			break;
500ab45bcdeSdlg 		case KSTAT_KV_U_CYCLES:
501ab45bcdeSdlg 			printf(" cycles");
502ab45bcdeSdlg 			break;
503ab45bcdeSdlg 
504ab45bcdeSdlg 		default:
505ab45bcdeSdlg 			printf(" unit-type-%u", kv->kv_unit);
506ab45bcdeSdlg 			break;
507ab45bcdeSdlg 		}
508ab45bcdeSdlg 
509ab45bcdeSdlg 		if (blen > 0) {
510ab45bcdeSdlg 			if (blen > len) {
511ab45bcdeSdlg 				blen = len;
512ab45bcdeSdlg 			}
513ab45bcdeSdlg 
514ab45bcdeSdlg 			(*trailer)(buf, blen);
515ab45bcdeSdlg 		} else
516ab45bcdeSdlg 			printf("\n");
517ab45bcdeSdlg 
518ab45bcdeSdlg 		blen = roundup(blen, KSTAT_KV_ALIGN);
519ab45bcdeSdlg 		buf += blen;
520ab45bcdeSdlg 		len -= blen;
521ab45bcdeSdlg 	} while (len >= (ssize_t)sizeof(*kv));
522ab45bcdeSdlg }
523ab45bcdeSdlg 
524ab45bcdeSdlg static void
kstat_list(struct kstat_tree * kt,int fd,unsigned int version,struct kstat_filters * kfs)52562fc0d78Sdlg kstat_list(struct kstat_tree *kt, int fd, unsigned int version,
52662fc0d78Sdlg     struct kstat_filters *kfs)
527ab45bcdeSdlg {
528ab45bcdeSdlg 	struct kstat_entry *kse;
529ab45bcdeSdlg 	struct kstat_req *ksreq;
530ab45bcdeSdlg 	uint64_t id = 0;
531ab45bcdeSdlg 
532ab45bcdeSdlg 	for (;;) {
533ab45bcdeSdlg 		kse = malloc(sizeof(*kse));
534ab45bcdeSdlg 		if (kse == NULL)
535ab45bcdeSdlg 			err(1, NULL);
536ab45bcdeSdlg 
537ab45bcdeSdlg 		memset(kse, 0, sizeof(*kse));
538ab45bcdeSdlg 		ksreq = &kse->kstat;
539ab45bcdeSdlg 		ksreq->ks_version = version;
540ab45bcdeSdlg 		ksreq->ks_id = ++id;
541ab45bcdeSdlg 
542ab45bcdeSdlg 		if (ioctl(fd, KSTATIOC_NFIND_ID, ksreq) == -1) {
543ab45bcdeSdlg 			if (errno == ENOENT) {
544ab45bcdeSdlg 				free(ksreq->ks_data);
545ab45bcdeSdlg 				free(kse);
546ab45bcdeSdlg 				break;
547ab45bcdeSdlg 			}
54859ead937Sdlg 		} else
54959ead937Sdlg 			id = ksreq->ks_id;
55059ead937Sdlg 
55159ead937Sdlg 		if (!kstat_filter_entry(kfs, ksreq)) {
55259ead937Sdlg 			free(ksreq->ks_data);
55359ead937Sdlg 			free(kse);
55459ead937Sdlg 			continue;
555ab45bcdeSdlg 		}
556ab45bcdeSdlg 
55762fc0d78Sdlg 		if (RBT_INSERT(kstat_tree, kt, kse) != NULL)
55859ead937Sdlg 			errx(1, "duplicate kstat entry");
55959ead937Sdlg 
56018bc31b7Sdlg 		ksreq->ks_data = malloc(ksreq->ks_datalen);
561ab45bcdeSdlg 		if (ksreq->ks_data == NULL)
56218bc31b7Sdlg 			err(1, "kstat data alloc");
563ab45bcdeSdlg 	}
56462fc0d78Sdlg }
565ab45bcdeSdlg 
56662fc0d78Sdlg static void
kstat_print(struct kstat_tree * kt)56762fc0d78Sdlg kstat_print(struct kstat_tree *kt)
56862fc0d78Sdlg {
56962fc0d78Sdlg 	struct kstat_entry *kse;
57062fc0d78Sdlg 	struct kstat_req *ksreq;
57162fc0d78Sdlg 
57262fc0d78Sdlg 	RBT_FOREACH(kse, kstat_tree, kt) {
573ab45bcdeSdlg 		ksreq = &kse->kstat;
574ab45bcdeSdlg 		printf("%s:%u:%s:%u\n",
575ab45bcdeSdlg 		    ksreq->ks_provider, ksreq->ks_instance,
576ab45bcdeSdlg 		    ksreq->ks_name, ksreq->ks_unit);
577ab45bcdeSdlg 		if (kse->serrno != 0) {
57818bc31b7Sdlg 			printf("\tkstat read error: %s\n",
57918bc31b7Sdlg 			    strerror(kse->serrno));
580ab45bcdeSdlg 			continue;
581ab45bcdeSdlg 		}
582ab45bcdeSdlg 		switch (ksreq->ks_type) {
583ab45bcdeSdlg 		case KSTAT_T_RAW:
584ab45bcdeSdlg 			hexdump(ksreq->ks_data, ksreq->ks_datalen);
585ab45bcdeSdlg 			break;
586ab45bcdeSdlg 		case KSTAT_T_KV:
587ab45bcdeSdlg 			kstat_kv(ksreq->ks_data, ksreq->ks_datalen);
588ab45bcdeSdlg 			break;
589ab45bcdeSdlg 		default:
590ab45bcdeSdlg 			hexdump(ksreq->ks_data, ksreq->ks_datalen);
591ab45bcdeSdlg 			break;
592ab45bcdeSdlg 		}
593ab45bcdeSdlg 	}
59464ba8693Sdlg 
59564ba8693Sdlg 	fflush(stdout);
596ab45bcdeSdlg }
5976b04bb45Sdlg 
5986b04bb45Sdlg static void
kstat_read(struct kstat_tree * kt,int fd)5996b04bb45Sdlg kstat_read(struct kstat_tree *kt, int fd)
6006b04bb45Sdlg {
6016b04bb45Sdlg 	struct kstat_entry *kse;
6026b04bb45Sdlg 	struct kstat_req *ksreq;
6036b04bb45Sdlg 
6046b04bb45Sdlg 	RBT_FOREACH(kse, kstat_tree, kt) {
60518bc31b7Sdlg 		kse->serrno = 0;
6066b04bb45Sdlg 		ksreq = &kse->kstat;
6076b04bb45Sdlg 		if (ioctl(fd, KSTATIOC_FIND_ID, ksreq) == -1)
60818bc31b7Sdlg 			kse->serrno = errno;
6096b04bb45Sdlg 	}
6106b04bb45Sdlg }
6116f9574e0Scheloha 
6126f9574e0Scheloha static void
handle_alrm(int signo)6136f9574e0Scheloha handle_alrm(int signo)
6146f9574e0Scheloha {
6156f9574e0Scheloha }
616