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 | } |