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