| File: | src/usr.bin/kstat/kstat.c |
| Warning: | line 196, column 2 Potential leak of memory pointed to by 'kfs.tqh_first' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: kstat.c,v 1.13 2023/11/16 03:17:34 dlg Exp $ */ | |||
| 2 | ||||
| 3 | /* | |||
| 4 | * Copyright (c) 2020 David Gwynne <dlg@openbsd.org> | |||
| 5 | * Permission to use, copy, modify, and distribute this software for any | |||
| 6 | * purpose with or without fee is hereby granted, provided that the above | |||
| 7 | * copyright notice and this permission notice appear in all copies. | |||
| 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| 15 | */ | |||
| 16 | ||||
| 17 | #include <ctype.h> | |||
| 18 | #include <limits.h> | |||
| 19 | #include <signal.h> | |||
| 20 | #include <stdio.h> | |||
| 21 | #include <stdlib.h> | |||
| 22 | #include <stddef.h> | |||
| 23 | #include <string.h> | |||
| 24 | #include <inttypes.h> | |||
| 25 | #include <fnmatch.h> | |||
| 26 | #include <fcntl.h> | |||
| 27 | #include <unistd.h> | |||
| 28 | #include <errno(*__errno()).h> | |||
| 29 | #include <err.h> | |||
| 30 | #include <vis.h> | |||
| 31 | ||||
| 32 | #include <sys/tree.h> | |||
| 33 | #include <sys/ioctl.h> | |||
| 34 | #include <sys/time.h> | |||
| 35 | #include <sys/queue.h> | |||
| 36 | ||||
| 37 | #include <sys/kstat.h> | |||
| 38 | ||||
| 39 | #ifndef roundup | |||
| 40 | #define roundup(x, y)((((x)+((y)-1))/(y))*(y)) ((((x)+((y)-1))/(y))*(y)) | |||
| 41 | #endif | |||
| 42 | ||||
| 43 | #ifndef nitems | |||
| 44 | #define nitems(_a)(sizeof((_a)) / sizeof((_a)[0])) (sizeof((_a)) / sizeof((_a)[0])) | |||
| 45 | #endif | |||
| 46 | ||||
| 47 | #ifndef ISSET | |||
| 48 | #define ISSET(_i, _m)((_i) & (_m)) ((_i) & (_m)) | |||
| 49 | #endif | |||
| 50 | ||||
| 51 | #ifndef SET | |||
| 52 | #define SET(_i, _m)((_i) |= (_m)) ((_i) |= (_m)) | |||
| 53 | #endif | |||
| 54 | ||||
| 55 | struct fmt_result { | |||
| 56 | uint64_t val; | |||
| 57 | unsigned int frac; | |||
| 58 | unsigned int exp; | |||
| 59 | }; | |||
| 60 | ||||
| 61 | static void | |||
| 62 | fmt_thing(struct fmt_result *fr, uint64_t val, uint64_t chunk) | |||
| 63 | { | |||
| 64 | unsigned int exp = 0; | |||
| 65 | uint64_t rem = 0; | |||
| 66 | ||||
| 67 | while (val > chunk) { | |||
| 68 | rem = val % chunk; | |||
| 69 | val /= chunk; | |||
| 70 | exp++; | |||
| 71 | } | |||
| 72 | ||||
| 73 | fr->val = val; | |||
| 74 | fr->exp = exp; | |||
| 75 | fr->frac = (rem * 1000) / chunk; | |||
| 76 | } | |||
| 77 | ||||
| 78 | #define str_is_empty(_str)(*(_str) == '\0') (*(_str) == '\0') | |||
| 79 | ||||
| 80 | #define DEV_KSTAT"/dev/kstat" "/dev/kstat" | |||
| 81 | ||||
| 82 | struct kstat_filter { | |||
| 83 | TAILQ_ENTRY(kstat_filter)struct { struct kstat_filter *tqe_next; struct kstat_filter * *tqe_prev; } kf_entry; | |||
| 84 | const char *kf_provider; | |||
| 85 | const char *kf_name; | |||
| 86 | unsigned int kf_flags; | |||
| 87 | #define KSTAT_FILTER_F_INST(1 << 0) (1 << 0) | |||
| 88 | #define KSTAT_FILTER_F_UNIT(1 << 1) (1 << 1) | |||
| 89 | unsigned int kf_instance; | |||
| 90 | unsigned int kf_unit; | |||
| 91 | }; | |||
| 92 | ||||
| 93 | TAILQ_HEAD(kstat_filters, kstat_filter)struct kstat_filters { struct kstat_filter *tqh_first; struct kstat_filter **tqh_last; }; | |||
| 94 | ||||
| 95 | struct kstat_entry { | |||
| 96 | struct kstat_req kstat; | |||
| 97 | RBT_ENTRY(kstat_entry)struct rb_entry entry; | |||
| 98 | int serrno; | |||
| 99 | }; | |||
| 100 | ||||
| 101 | RBT_HEAD(kstat_tree, kstat_entry)struct kstat_tree { struct rb_tree rbh_root; }; | |||
| 102 | ||||
| 103 | static inline int | |||
| 104 | kstat_cmp(const struct kstat_entry *ea, const struct kstat_entry *eb) | |||
| 105 | { | |||
| 106 | const struct kstat_req *a = &ea->kstat; | |||
| 107 | const struct kstat_req *b = &eb->kstat; | |||
| 108 | int rv; | |||
| 109 | ||||
| 110 | rv = strncmp(a->ks_provider, b->ks_provider, sizeof(a->ks_provider)); | |||
| 111 | if (rv != 0) | |||
| 112 | return (rv); | |||
| 113 | if (a->ks_instance > b->ks_instance) | |||
| 114 | return (1); | |||
| 115 | if (a->ks_instance < b->ks_instance) | |||
| 116 | return (-1); | |||
| 117 | ||||
| 118 | rv = strncmp(a->ks_name, b->ks_name, sizeof(a->ks_name)); | |||
| 119 | if (rv != 0) | |||
| 120 | return (rv); | |||
| 121 | if (a->ks_unit > b->ks_unit) | |||
| 122 | return (1); | |||
| 123 | if (a->ks_unit < b->ks_unit) | |||
| 124 | return (-1); | |||
| 125 | ||||
| 126 | return (0); | |||
| 127 | } | |||
| 128 | ||||
| 129 | RBT_PROTOTYPE(kstat_tree, kstat_entry, entry, kstat_cmp)extern const struct rb_type *const kstat_tree_RBT_TYPE; __attribute__ ((__unused__)) static inline void kstat_tree_RBT_INIT(struct kstat_tree *head) { _rb_init(&head->rbh_root); } __attribute__(( __unused__)) static inline struct kstat_entry * kstat_tree_RBT_INSERT (struct kstat_tree *head, struct kstat_entry *elm) { return _rb_insert (kstat_tree_RBT_TYPE, &head->rbh_root, elm); } __attribute__ ((__unused__)) static inline struct kstat_entry * kstat_tree_RBT_REMOVE (struct kstat_tree *head, struct kstat_entry *elm) { return _rb_remove (kstat_tree_RBT_TYPE, &head->rbh_root, elm); } __attribute__ ((__unused__)) static inline struct kstat_entry * kstat_tree_RBT_FIND (struct kstat_tree *head, const struct kstat_entry *key) { return _rb_find(kstat_tree_RBT_TYPE, &head->rbh_root, key); } __attribute__((__unused__)) static inline struct kstat_entry * kstat_tree_RBT_NFIND(struct kstat_tree *head, const struct kstat_entry *key) { return _rb_nfind(kstat_tree_RBT_TYPE, & head->rbh_root, key); } __attribute__((__unused__)) static inline struct kstat_entry * kstat_tree_RBT_ROOT(struct kstat_tree *head) { return _rb_root(kstat_tree_RBT_TYPE, &head-> rbh_root); } __attribute__((__unused__)) static inline int kstat_tree_RBT_EMPTY (struct kstat_tree *head) { return _rb_empty(&head->rbh_root ); } __attribute__((__unused__)) static inline struct kstat_entry * kstat_tree_RBT_MIN(struct kstat_tree *head) { return _rb_min (kstat_tree_RBT_TYPE, &head->rbh_root); } __attribute__ ((__unused__)) static inline struct kstat_entry * kstat_tree_RBT_MAX (struct kstat_tree *head) { return _rb_max(kstat_tree_RBT_TYPE , &head->rbh_root); } __attribute__((__unused__)) static inline struct kstat_entry * kstat_tree_RBT_NEXT(struct kstat_entry *elm) { return _rb_next(kstat_tree_RBT_TYPE, elm); } __attribute__ ((__unused__)) static inline struct kstat_entry * kstat_tree_RBT_PREV (struct kstat_entry *elm) { return _rb_prev(kstat_tree_RBT_TYPE , elm); } __attribute__((__unused__)) static inline struct kstat_entry * kstat_tree_RBT_LEFT(struct kstat_entry *elm) { return _rb_left (kstat_tree_RBT_TYPE, elm); } __attribute__((__unused__)) static inline struct kstat_entry * kstat_tree_RBT_RIGHT(struct kstat_entry *elm) { return _rb_right(kstat_tree_RBT_TYPE, elm); } __attribute__ ((__unused__)) static inline struct kstat_entry * kstat_tree_RBT_PARENT (struct kstat_entry *elm) { return _rb_parent(kstat_tree_RBT_TYPE , elm); } __attribute__((__unused__)) static inline void kstat_tree_RBT_SET_LEFT (struct kstat_entry *elm, struct kstat_entry *left) { _rb_set_left (kstat_tree_RBT_TYPE, elm, left); } __attribute__((__unused__ )) static inline void kstat_tree_RBT_SET_RIGHT(struct kstat_entry *elm, struct kstat_entry *right) { _rb_set_right(kstat_tree_RBT_TYPE , elm, right); } __attribute__((__unused__)) static inline void kstat_tree_RBT_SET_PARENT(struct kstat_entry *elm, struct kstat_entry *parent) { _rb_set_parent(kstat_tree_RBT_TYPE, elm, parent); } __attribute__((__unused__)) static inline void kstat_tree_RBT_POISON (struct kstat_entry *elm, unsigned long poison) { _rb_poison( kstat_tree_RBT_TYPE, elm, poison); } __attribute__((__unused__ )) static inline int kstat_tree_RBT_CHECK(struct kstat_entry * elm, unsigned long poison) { return _rb_check(kstat_tree_RBT_TYPE , elm, poison); }; | |||
| 130 | RBT_GENERATE(kstat_tree, kstat_entry, entry, kstat_cmp)static int kstat_tree_RBT_COMPARE(const void *lptr, const void *rptr) { const struct kstat_entry *l = lptr, *r = rptr; return kstat_cmp(l, r); } static const struct rb_type kstat_tree_RBT_INFO = { kstat_tree_RBT_COMPARE, ((void *)0), __builtin_offsetof( struct kstat_entry, entry), }; const struct rb_type *const kstat_tree_RBT_TYPE = &kstat_tree_RBT_INFO; | |||
| 131 | ||||
| 132 | static void handle_alrm(int); | |||
| 133 | static struct kstat_filter * | |||
| 134 | kstat_filter_parse(char *); | |||
| 135 | static int kstat_filter_entry(struct kstat_filters *, | |||
| 136 | const struct kstat_req *); | |||
| 137 | ||||
| 138 | static void kstat_list(struct kstat_tree *, int, unsigned int, | |||
| 139 | struct kstat_filters *); | |||
| 140 | static void kstat_print(struct kstat_tree *); | |||
| 141 | static void kstat_read(struct kstat_tree *, int); | |||
| 142 | ||||
| 143 | __dead__attribute__((__noreturn__)) static void | |||
| 144 | usage(void) | |||
| 145 | { | |||
| 146 | extern char *__progname; | |||
| 147 | ||||
| 148 | fprintf(stderr(&__sF[2]), "usage: %s [-w wait] " | |||
| 149 | "[name | provider:instance:name:unit] ...\n", __progname); | |||
| 150 | ||||
| 151 | exit(1); | |||
| 152 | } | |||
| 153 | ||||
| 154 | int | |||
| 155 | main(int argc, char *argv[]) | |||
| 156 | { | |||
| 157 | struct kstat_filters kfs = TAILQ_HEAD_INITIALIZER(kfs){ ((void *)0), &(kfs).tqh_first }; | |||
| 158 | struct kstat_tree kt = RBT_INITIALIZER(){ { ((void *)0) } }; | |||
| 159 | unsigned int version; | |||
| 160 | int fd; | |||
| 161 | const char *errstr; | |||
| 162 | int ch; | |||
| 163 | struct itimerval itv; | |||
| 164 | sigset_t empty, mask; | |||
| 165 | int i; | |||
| 166 | unsigned int wait = 0; | |||
| 167 | ||||
| 168 | while ((ch = getopt(argc, argv, "w:")) != -1) { | |||
| ||||
| 169 | switch (ch) { | |||
| 170 | case 'w': | |||
| 171 | wait = strtonum(optarg, 1, UINT_MAX0xffffffffU, &errstr); | |||
| 172 | if (errstr != NULL((void *)0)) | |||
| 173 | errx(1, "wait is %s: %s", errstr, optarg); | |||
| 174 | break; | |||
| 175 | default: | |||
| 176 | usage(); | |||
| 177 | } | |||
| 178 | } | |||
| 179 | ||||
| 180 | argc -= optind; | |||
| 181 | argv += optind; | |||
| 182 | ||||
| 183 | for (i = 0; i < argc; i++) { | |||
| 184 | struct kstat_filter *kf = kstat_filter_parse(argv[i]); | |||
| 185 | TAILQ_INSERT_TAIL(&kfs, kf, kf_entry)do { (kf)->kf_entry.tqe_next = ((void *)0); (kf)->kf_entry .tqe_prev = (&kfs)->tqh_last; *(&kfs)->tqh_last = (kf); (&kfs)->tqh_last = &(kf)->kf_entry.tqe_next ; } while (0); | |||
| 186 | } | |||
| 187 | ||||
| 188 | fd = open(DEV_KSTAT"/dev/kstat", O_RDONLY0x0000); | |||
| 189 | if (fd == -1) | |||
| 190 | err(1, "%s", DEV_KSTAT"/dev/kstat"); | |||
| 191 | ||||
| 192 | if (ioctl(fd, KSTATIOC_VERSION((unsigned long)0x40000000 | ((sizeof(unsigned int) & 0x1fff ) << 16) | ((('k')) << 8) | ((1))), &version) == -1) | |||
| 193 | err(1, "kstat version"); | |||
| 194 | ||||
| 195 | kstat_list(&kt, fd, version, &kfs); | |||
| 196 | kstat_read(&kt, fd); | |||
| ||||
| 197 | kstat_print(&kt); | |||
| 198 | ||||
| 199 | if (wait == 0) | |||
| 200 | return (0); | |||
| 201 | ||||
| 202 | if (signal(SIGALRM14, handle_alrm) == SIG_ERR(void (*)(int))-1) | |||
| 203 | err(1, "signal"); | |||
| 204 | sigemptyset(&empty); | |||
| 205 | sigemptyset(&mask); | |||
| 206 | sigaddset(&mask, SIGALRM14); | |||
| 207 | if (sigprocmask(SIG_BLOCK1, &mask, NULL((void *)0)) == -1) | |||
| 208 | err(1, "sigprocmask"); | |||
| 209 | ||||
| 210 | itv.it_value.tv_sec = wait; | |||
| 211 | itv.it_value.tv_usec = 0; | |||
| 212 | itv.it_interval = itv.it_value; | |||
| 213 | if (setitimer(ITIMER_REAL0, &itv, NULL((void *)0)) == -1) | |||
| 214 | err(1, "setitimer"); | |||
| 215 | ||||
| 216 | for (;;) { | |||
| 217 | sigsuspend(&empty); | |||
| 218 | kstat_read(&kt, fd); | |||
| 219 | kstat_print(&kt); | |||
| 220 | } | |||
| 221 | ||||
| 222 | return (0); | |||
| 223 | } | |||
| 224 | ||||
| 225 | static struct kstat_filter * | |||
| 226 | kstat_filter_parse(char *arg) | |||
| 227 | { | |||
| 228 | struct kstat_filter *kf; | |||
| 229 | const char *errstr; | |||
| 230 | char *argv[4]; | |||
| 231 | size_t argc; | |||
| 232 | ||||
| 233 | for (argc = 0; argc < nitems(argv)(sizeof((argv)) / sizeof((argv)[0])); argc++) { | |||
| 234 | char *s = strsep(&arg, ":"); | |||
| 235 | if (s == NULL((void *)0)) | |||
| 236 | break; | |||
| 237 | ||||
| 238 | argv[argc] = s; | |||
| 239 | } | |||
| 240 | if (arg != NULL((void *)0)) | |||
| 241 | usage(); | |||
| 242 | ||||
| 243 | kf = malloc(sizeof(*kf)); | |||
| 244 | if (kf == NULL((void *)0)) | |||
| 245 | err(1, NULL((void *)0)); | |||
| 246 | ||||
| 247 | memset(kf, 0, sizeof(*kf)); | |||
| 248 | ||||
| 249 | switch (argc) { | |||
| 250 | case 1: | |||
| 251 | if (str_is_empty(argv[0])(*(argv[0]) == '\0')) | |||
| 252 | errx(1, "empty name"); | |||
| 253 | ||||
| 254 | kf->kf_name = argv[0]; | |||
| 255 | break; | |||
| 256 | case 4: | |||
| 257 | if (!str_is_empty(argv[0])(*(argv[0]) == '\0')) | |||
| 258 | kf->kf_provider = argv[0]; | |||
| 259 | if (!str_is_empty(argv[1])(*(argv[1]) == '\0')) { | |||
| 260 | kf->kf_instance = | |||
| 261 | strtonum(argv[1], 0, 0xffffffffU, &errstr); | |||
| 262 | if (errstr != NULL((void *)0)) { | |||
| 263 | errx(1, "%s:%s:%s:%s: instance %s: %s", | |||
| 264 | argv[0], argv[1], argv[2], argv[3], | |||
| 265 | argv[1], errstr); | |||
| 266 | } | |||
| 267 | SET(kf->kf_flags, KSTAT_FILTER_F_INST)((kf->kf_flags) |= ((1 << 0))); | |||
| 268 | } | |||
| 269 | if (!str_is_empty(argv[2])(*(argv[2]) == '\0')) | |||
| 270 | kf->kf_name = argv[2]; | |||
| 271 | if (!str_is_empty(argv[3])(*(argv[3]) == '\0')) { | |||
| 272 | kf->kf_unit = | |||
| 273 | strtonum(argv[3], 0, 0xffffffffU, &errstr); | |||
| 274 | if (errstr != NULL((void *)0)) { | |||
| 275 | errx(1, "%s:%s:%s:%s: unit %s: %s", | |||
| 276 | argv[0], argv[1], argv[2], argv[3], | |||
| 277 | argv[3], errstr); | |||
| 278 | } | |||
| 279 | SET(kf->kf_flags, KSTAT_FILTER_F_UNIT)((kf->kf_flags) |= ((1 << 1))); | |||
| 280 | } | |||
| 281 | break; | |||
| 282 | default: | |||
| 283 | usage(); | |||
| 284 | } | |||
| 285 | ||||
| 286 | return (kf); | |||
| 287 | } | |||
| 288 | ||||
| 289 | static int | |||
| 290 | kstat_filter_entry(struct kstat_filters *kfs, const struct kstat_req *ksreq) | |||
| 291 | { | |||
| 292 | struct kstat_filter *kf; | |||
| 293 | ||||
| 294 | if (TAILQ_EMPTY(kfs)(((kfs)->tqh_first) == ((void *)0))) | |||
| 295 | return (1); | |||
| 296 | ||||
| 297 | TAILQ_FOREACH(kf, kfs, kf_entry)for((kf) = ((kfs)->tqh_first); (kf) != ((void *)0); (kf) = ((kf)->kf_entry.tqe_next)) { | |||
| 298 | if (kf->kf_provider != NULL((void *)0)) { | |||
| 299 | if (fnmatch(kf->kf_provider, ksreq->ks_provider, | |||
| 300 | FNM_NOESCAPE0x01 | FNM_LEADING_DIR0x08) == FNM_NOMATCH1) | |||
| 301 | continue; | |||
| 302 | } | |||
| 303 | if (ISSET(kf->kf_flags, KSTAT_FILTER_F_INST)((kf->kf_flags) & ((1 << 0)))) { | |||
| 304 | if (kf->kf_instance != ksreq->ks_instance) | |||
| 305 | continue; | |||
| 306 | } | |||
| 307 | if (kf->kf_name != NULL((void *)0)) { | |||
| 308 | if (fnmatch(kf->kf_name, ksreq->ks_name, | |||
| 309 | FNM_NOESCAPE0x01 | FNM_LEADING_DIR0x08) == FNM_NOMATCH1) | |||
| 310 | continue; | |||
| 311 | } | |||
| 312 | if (ISSET(kf->kf_flags, KSTAT_FILTER_F_UNIT)((kf->kf_flags) & ((1 << 1)))) { | |||
| 313 | if (kf->kf_unit != ksreq->ks_unit) | |||
| 314 | continue; | |||
| 315 | } | |||
| 316 | ||||
| 317 | return (1); | |||
| 318 | } | |||
| 319 | ||||
| 320 | return (0); | |||
| 321 | } | |||
| 322 | ||||
| 323 | static int | |||
| 324 | printable(int ch) | |||
| 325 | { | |||
| 326 | if (ch == '\0') | |||
| 327 | return ('_'); | |||
| 328 | if (!isprint(ch)) | |||
| 329 | return ('~'); | |||
| 330 | return (ch); | |||
| 331 | } | |||
| 332 | ||||
| 333 | static void | |||
| 334 | hexdump(const void *d, size_t datalen) | |||
| 335 | { | |||
| 336 | const uint8_t *data = d; | |||
| 337 | size_t i, j = 0; | |||
| 338 | ||||
| 339 | for (i = 0; i < datalen; i += j) { | |||
| 340 | printf("%4zu: ", i); | |||
| 341 | ||||
| 342 | for (j = 0; j < 16 && i+j < datalen; j++) | |||
| 343 | printf("%02x ", data[i + j]); | |||
| 344 | while (j++ < 16) | |||
| 345 | printf(" "); | |||
| 346 | printf("|"); | |||
| 347 | ||||
| 348 | for (j = 0; j < 16 && i+j < datalen; j++) | |||
| 349 | putchar(printable(data[i + j]))(!__isthreaded ? __sputc(printable(data[i + j]), (&__sF[1 ])) : (putc)(printable(data[i + j]), (&__sF[1]))); | |||
| 350 | printf("|\n"); | |||
| 351 | } | |||
| 352 | } | |||
| 353 | ||||
| 354 | static void | |||
| 355 | strdump(const void *s, size_t len) | |||
| 356 | { | |||
| 357 | const char *str = s; | |||
| 358 | char dst[8]; | |||
| 359 | size_t i; | |||
| 360 | ||||
| 361 | for (i = 0; i < len; i++) { | |||
| 362 | char ch = str[i]; | |||
| 363 | if (ch == '\0') | |||
| 364 | break; | |||
| 365 | ||||
| 366 | vis(dst, ch, VIS_TAB0x08 | VIS_NL0x10, 0); | |||
| 367 | printf("%s", dst); | |||
| 368 | } | |||
| 369 | } | |||
| 370 | ||||
| 371 | static void | |||
| 372 | strdumpnl(const void *s, size_t len) | |||
| 373 | { | |||
| 374 | strdump(s, len); | |||
| 375 | printf("\n"); | |||
| 376 | } | |||
| 377 | ||||
| 378 | static const char *si_prefixes[] = { "", "k", "M", "G", "T", "P", "E" }; | |||
| 379 | #ifdef notyet | |||
| 380 | static const char *iec_prefixes[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" }; | |||
| 381 | #endif | |||
| 382 | ||||
| 383 | static void | |||
| 384 | kstat_kv(const void *d, ssize_t len) | |||
| 385 | { | |||
| 386 | const uint8_t *buf; | |||
| 387 | const struct kstat_kv *kv; | |||
| 388 | ssize_t blen; | |||
| 389 | void (*trailer)(const void *, size_t); | |||
| 390 | double f; | |||
| 391 | struct fmt_result fr; | |||
| 392 | ||||
| 393 | if (len < (ssize_t)sizeof(*kv)) { | |||
| 394 | warn("short kv (len %zu < size %zu)", len, sizeof(*kv)); | |||
| 395 | return; | |||
| 396 | } | |||
| 397 | ||||
| 398 | buf = d; | |||
| 399 | do { | |||
| 400 | kv = (const struct kstat_kv *)buf; | |||
| 401 | ||||
| 402 | buf += sizeof(*kv); | |||
| 403 | len -= sizeof(*kv); | |||
| 404 | ||||
| 405 | blen = 0; | |||
| 406 | trailer = hexdump; | |||
| 407 | ||||
| 408 | printf("%16.16s: ", kv->kv_key); | |||
| 409 | ||||
| 410 | switch (kv->kv_type) { | |||
| 411 | case KSTAT_KV_T_NULL: | |||
| 412 | printf("null"); | |||
| 413 | break; | |||
| 414 | case KSTAT_KV_T_BOOL: | |||
| 415 | printf("%s", kstat_kv_bool(kv)(kv)->kv_v.v_bool ? "true" : "false"); | |||
| 416 | break; | |||
| 417 | case KSTAT_KV_T_COUNTER64: | |||
| 418 | case KSTAT_KV_T_UINT64: | |||
| 419 | printf("%" PRIu64"llu", kstat_kv_u64(kv)(kv)->kv_v.v_u64); | |||
| 420 | break; | |||
| 421 | case KSTAT_KV_T_INT64: | |||
| 422 | printf("%" PRId64"lld", kstat_kv_s64(kv)(kv)->kv_v.v_s64); | |||
| 423 | break; | |||
| 424 | case KSTAT_KV_T_COUNTER32: | |||
| 425 | case KSTAT_KV_T_UINT32: | |||
| 426 | printf("%" PRIu32"u", kstat_kv_u32(kv)(kv)->kv_v.v_u32); | |||
| 427 | break; | |||
| 428 | case KSTAT_KV_T_INT32: | |||
| 429 | printf("%" PRId32"d", kstat_kv_s32(kv)(kv)->kv_v.v_s32); | |||
| 430 | break; | |||
| 431 | case KSTAT_KV_T_COUNTER16: | |||
| 432 | case KSTAT_KV_T_UINT16: | |||
| 433 | printf("%" PRIu16"u", kstat_kv_u16(kv)(kv)->kv_v.v_u16); | |||
| 434 | break; | |||
| 435 | case KSTAT_KV_T_INT16: | |||
| 436 | printf("%" PRId16"d", kstat_kv_s16(kv)(kv)->kv_v.v_s16); | |||
| 437 | break; | |||
| 438 | case KSTAT_KV_T_STR: | |||
| 439 | blen = kstat_kv_len(kv)(kv)->kv_v.v_len; | |||
| 440 | trailer = strdumpnl; | |||
| 441 | break; | |||
| 442 | case KSTAT_KV_T_BYTES: | |||
| 443 | blen = kstat_kv_len(kv)(kv)->kv_v.v_len; | |||
| 444 | trailer = hexdump; | |||
| 445 | ||||
| 446 | printf("\n"); | |||
| 447 | break; | |||
| 448 | ||||
| 449 | case KSTAT_KV_T_ISTR: | |||
| 450 | strdump(kstat_kv_istr(kv)(kv)->kv_v.v_istr, sizeof(kstat_kv_istr(kv)(kv)->kv_v.v_istr)); | |||
| 451 | break; | |||
| 452 | ||||
| 453 | case KSTAT_KV_T_TEMP: | |||
| 454 | f = kstat_kv_temp(kv)(kv)->kv_v.v_u64; | |||
| 455 | printf("%.2f degC", (f - 273150000.0) / 1000000.0); | |||
| 456 | break; | |||
| 457 | ||||
| 458 | case KSTAT_KV_T_FREQ: | |||
| 459 | fmt_thing(&fr, kstat_kv_freq(kv)(kv)->kv_v.v_u64, 1000); | |||
| 460 | printf("%llu", fr.val); | |||
| 461 | if (fr.frac > 10) | |||
| 462 | printf(".%02u", fr.frac / 10); | |||
| 463 | printf(" %sHz", si_prefixes[fr.exp]); | |||
| 464 | break; | |||
| 465 | ||||
| 466 | case KSTAT_KV_T_VOLTS_DC: /* uV */ | |||
| 467 | f = kstat_kv_volts(kv)(kv)->kv_v.v_u64; | |||
| 468 | printf("%.2f VDC", f / 1000000.0); | |||
| 469 | break; | |||
| 470 | ||||
| 471 | case KSTAT_KV_T_VOLTS_AC: /* uV */ | |||
| 472 | f = kstat_kv_volts(kv)(kv)->kv_v.v_u64; | |||
| 473 | printf("%.2f VAC", f / 1000000.0); | |||
| 474 | break; | |||
| 475 | ||||
| 476 | default: | |||
| 477 | printf("unknown type %u, stopping\n", kv->kv_type); | |||
| 478 | return; | |||
| 479 | } | |||
| 480 | ||||
| 481 | switch (kv->kv_unit) { | |||
| 482 | case KSTAT_KV_U_NONE: | |||
| 483 | break; | |||
| 484 | case KSTAT_KV_U_PACKETS: | |||
| 485 | printf(" packets"); | |||
| 486 | break; | |||
| 487 | case KSTAT_KV_U_BYTES: | |||
| 488 | printf(" bytes"); | |||
| 489 | break; | |||
| 490 | case KSTAT_KV_U_CYCLES: | |||
| 491 | printf(" cycles"); | |||
| 492 | break; | |||
| 493 | ||||
| 494 | default: | |||
| 495 | printf(" unit-type-%u", kv->kv_unit); | |||
| 496 | break; | |||
| 497 | } | |||
| 498 | ||||
| 499 | if (blen > 0) { | |||
| 500 | if (blen > len) { | |||
| 501 | blen = len; | |||
| 502 | } | |||
| 503 | ||||
| 504 | (*trailer)(buf, blen); | |||
| 505 | } else | |||
| 506 | printf("\n"); | |||
| 507 | ||||
| 508 | blen = roundup(blen, KSTAT_KV_ALIGN)((((blen)+((sizeof(uint64_t))-1))/(sizeof(uint64_t)))*(sizeof (uint64_t))); | |||
| 509 | buf += blen; | |||
| 510 | len -= blen; | |||
| 511 | } while (len >= (ssize_t)sizeof(*kv)); | |||
| 512 | } | |||
| 513 | ||||
| 514 | static void | |||
| 515 | kstat_list(struct kstat_tree *kt, int fd, unsigned int version, | |||
| 516 | struct kstat_filters *kfs) | |||
| 517 | { | |||
| 518 | struct kstat_entry *kse; | |||
| 519 | struct kstat_req *ksreq; | |||
| 520 | uint64_t id = 0; | |||
| 521 | ||||
| 522 | for (;;) { | |||
| 523 | kse = malloc(sizeof(*kse)); | |||
| 524 | if (kse == NULL((void *)0)) | |||
| 525 | err(1, NULL((void *)0)); | |||
| 526 | ||||
| 527 | memset(kse, 0, sizeof(*kse)); | |||
| 528 | ksreq = &kse->kstat; | |||
| 529 | ksreq->ks_version = version; | |||
| 530 | ksreq->ks_id = ++id; | |||
| 531 | ||||
| 532 | if (ioctl(fd, KSTATIOC_NFIND_ID(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct kstat_req) & 0x1fff) << 16) | ((('k')) << 8) | ((3))), ksreq) == -1) { | |||
| 533 | if (errno(*__errno()) == ENOENT2) { | |||
| 534 | free(ksreq->ks_data); | |||
| 535 | free(kse); | |||
| 536 | break; | |||
| 537 | } | |||
| 538 | } else | |||
| 539 | id = ksreq->ks_id; | |||
| 540 | ||||
| 541 | if (!kstat_filter_entry(kfs, ksreq)) { | |||
| 542 | free(ksreq->ks_data); | |||
| 543 | free(kse); | |||
| 544 | continue; | |||
| 545 | } | |||
| 546 | ||||
| 547 | if (RBT_INSERT(kstat_tree, kt, kse)kstat_tree_RBT_INSERT(kt, kse) != NULL((void *)0)) | |||
| 548 | errx(1, "duplicate kstat entry"); | |||
| 549 | ||||
| 550 | ksreq->ks_data = malloc(ksreq->ks_datalen); | |||
| 551 | if (ksreq->ks_data == NULL((void *)0)) | |||
| 552 | err(1, "kstat data alloc"); | |||
| 553 | } | |||
| 554 | } | |||
| 555 | ||||
| 556 | static void | |||
| 557 | kstat_print(struct kstat_tree *kt) | |||
| 558 | { | |||
| 559 | struct kstat_entry *kse; | |||
| 560 | struct kstat_req *ksreq; | |||
| 561 | ||||
| 562 | RBT_FOREACH(kse, kstat_tree, kt)for ((kse) = kstat_tree_RBT_MIN((kt)); (kse) != ((void *)0); ( kse) = kstat_tree_RBT_NEXT((kse))) { | |||
| 563 | ksreq = &kse->kstat; | |||
| 564 | printf("%s:%u:%s:%u\n", | |||
| 565 | ksreq->ks_provider, ksreq->ks_instance, | |||
| 566 | ksreq->ks_name, ksreq->ks_unit); | |||
| 567 | if (kse->serrno != 0) { | |||
| 568 | printf("\tkstat read error: %s\n", | |||
| 569 | strerror(kse->serrno)); | |||
| 570 | continue; | |||
| 571 | } | |||
| 572 | switch (ksreq->ks_type) { | |||
| 573 | case KSTAT_T_RAW0: | |||
| 574 | hexdump(ksreq->ks_data, ksreq->ks_datalen); | |||
| 575 | break; | |||
| 576 | case KSTAT_T_KV1: | |||
| 577 | kstat_kv(ksreq->ks_data, ksreq->ks_datalen); | |||
| 578 | break; | |||
| 579 | default: | |||
| 580 | hexdump(ksreq->ks_data, ksreq->ks_datalen); | |||
| 581 | break; | |||
| 582 | } | |||
| 583 | } | |||
| 584 | ||||
| 585 | fflush(stdout(&__sF[1])); | |||
| 586 | } | |||
| 587 | ||||
| 588 | static void | |||
| 589 | kstat_read(struct kstat_tree *kt, int fd) | |||
| 590 | { | |||
| 591 | struct kstat_entry *kse; | |||
| 592 | struct kstat_req *ksreq; | |||
| 593 | ||||
| 594 | RBT_FOREACH(kse, kstat_tree, kt)for ((kse) = kstat_tree_RBT_MIN((kt)); (kse) != ((void *)0); ( kse) = kstat_tree_RBT_NEXT((kse))) { | |||
| 595 | kse->serrno = 0; | |||
| 596 | ksreq = &kse->kstat; | |||
| 597 | if (ioctl(fd, KSTATIOC_FIND_ID(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct kstat_req) & 0x1fff) << 16) | ((('k')) << 8) | ((2))), ksreq) == -1) | |||
| 598 | kse->serrno = errno(*__errno()); | |||
| 599 | } | |||
| 600 | } | |||
| 601 | ||||
| 602 | static void | |||
| 603 | handle_alrm(int signo) | |||
| 604 | { | |||
| 605 | } |