Remove building with NOCRYPTO option
[minix.git] / minix / usr.bin / trace / service / mib.c
blob9734a0e034006083d4a5426211d8a87747924163
2 #include "inc.h"
4 #include <sys/time.h>
5 #include <sys/sysctl.h>
6 #include <sys/sched.h>
7 #include <sys/resource.h>
9 struct sysctl_tab {
10 int id;
11 size_t size;
12 const struct sysctl_tab *tab;
13 int (*proc)(struct trace_proc *, const char *, int, const void *,
14 vir_bytes, size_t);
16 #define NODE(i,t) { .id = i, .size = __arraycount(t), .tab = t }
17 #define PROC(i,s,p) { .id = i, .size = s, .proc = p }
20 * Print CTL_KERN KERN_CLOCKRATE.
22 static int
23 put_kern_clockrate(struct trace_proc * proc, const char * name,
24 int type __unused, const void * ptr, vir_bytes addr __unused,
25 size_t size __unused)
27 const struct clockinfo *ci;
29 ci = (const struct clockinfo *)ptr;
31 put_value(proc, "hz", "%d", ci->hz);
32 put_value(proc, "tick", "%d", ci->tick);
33 if (verbose > 0) {
34 put_value(proc, "tickadj", "%d", ci->tickadj);
35 put_value(proc, "stathz", "%d", ci->stathz);
36 put_value(proc, "profhz", "%d", ci->profhz);
37 return TRUE;
38 } else
39 return FALSE;
43 * Print CTL_KERN KERN_PROC2.
45 static int
46 put_kern_proc2(struct trace_proc * proc, const char * name, int type,
47 const void * ptr, vir_bytes addr, size_t size)
49 const int *mib;
50 const char *text;
51 unsigned int i;
53 if (type == ST_NAME) {
54 mib = (const int *)ptr;
56 for (i = 0; i < size; i++) {
57 text = NULL;
59 if (i == 0) {
60 switch (mib[i]) {
61 case KERN_PROC_ALL: text = "<all>"; break;
62 case KERN_PROC_PID: text = "<pid>"; break;
63 case KERN_PROC_PGRP: text = "<pgrp>"; break;
64 case KERN_PROC_SESSION:
65 text = "<session>"; break;
66 case KERN_PROC_TTY: text = "<tty>"; break;
67 case KERN_PROC_UID: text = "<uid>"; break;
68 case KERN_PROC_RUID: text = "<ruid>"; break;
69 case KERN_PROC_GID: text = "<gid>"; break;
70 case KERN_PROC_RGID: text = "<rgid>"; break;
72 } else if (i == 1 && mib[0] == KERN_PROC_TTY) {
73 switch ((dev_t)mib[i]) {
74 case KERN_PROC_TTY_NODEV:
75 text = "<nodev>"; break;
76 case KERN_PROC_TTY_REVOKE:
77 text = "<revoke>"; break;
81 if (!valuesonly && text != NULL)
82 put_field(proc, NULL, text);
83 else
84 put_value(proc, NULL, "%d", mib[i]);
88 * Save the requested structure length, so that we can later
89 * determine how many elements were returned (see below).
91 proc->sctl_arg = (size == 4) ? mib[2] : 0;
93 return 0;
96 if (proc->sctl_arg > 0) {
97 /* TODO: optionally dump struct kinfo_drivers array */
98 put_open(proc, name, 0, "[", ", ");
99 if (size > 0)
100 put_tail(proc, size / proc->sctl_arg, 0);
101 put_close(proc, "]");
102 } else
103 put_ptr(proc, name, addr);
105 return TRUE;
109 * Print CTL_KERN KERN_PROC_ARGS.
111 static int
112 put_kern_proc_args(struct trace_proc * proc, const char * name, int type,
113 const void * ptr, vir_bytes addr, size_t size)
115 const int *mib;
116 const char *text;
117 unsigned int i;
118 int v;
120 if (type == ST_NAME) {
121 mib = (const int *)ptr;
123 for (i = 0; i < size; i++) {
124 text = NULL;
126 if (i == 1) {
127 switch (mib[i]) {
128 case KERN_PROC_ARGV: text = "<argv>"; break;
129 case KERN_PROC_ENV: text = "<env>"; break;
130 case KERN_PROC_NARGV: text = "<nargv>"; break;
131 case KERN_PROC_NENV: text = "<nenv>"; break;
135 if (!valuesonly && text != NULL)
136 put_field(proc, NULL, text);
137 else
138 put_value(proc, NULL, "%d", mib[i]);
141 /* Save the subrequest, so that we can later print data. */
142 proc->sctl_arg = (size == 2) ? mib[1] : -999;
144 return 0;
147 if ((proc->sctl_arg == KERN_PROC_NARGV ||
148 proc->sctl_arg == KERN_PROC_NENV) && size == sizeof(v) &&
149 mem_get_data(proc->pid, addr, &v, sizeof(v)) >= 0) {
150 put_open(proc, name, PF_NONAME, "{", ", ");
152 put_value(proc, NULL, "%d", v);
154 put_close(proc, "}");
155 } else
156 put_ptr(proc, name, addr);
158 return TRUE;
162 * Print CTL_KERN KERN_CP_TIME.
164 static int
165 put_kern_cp_time(struct trace_proc * proc, const char * name __unused,
166 int type, const void * ptr, vir_bytes addr __unused, size_t size)
168 const uint64_t *p;
169 unsigned int i;
170 const int *mib;
172 if (type == ST_NAME) {
173 mib = (const int *)ptr;
174 for (i = 0; i < size; i++)
175 put_value(proc, NULL, "%d", mib[i]);
177 return 0;
180 p = (const uint64_t *)ptr;
182 /* TODO: support for multi-CPU results */
183 for (i = 0; i < CPUSTATES; i++)
184 put_value(proc, NULL, "%"PRIu64, p[i]);
186 return TRUE;
190 * Print CTL_KERN KERN_CONSDEV.
192 static int
193 put_kern_consdev(struct trace_proc * proc, const char * name,
194 int type __unused, const void * ptr, vir_bytes addr __unused,
195 size_t size __unused)
198 put_dev(proc, NULL, *(const dev_t *)ptr);
200 return TRUE;
204 * Print CTL_KERN KERN_DRIVERS.
206 static int
207 put_kern_drivers(struct trace_proc * proc, const char * name,
208 int type __unused, const void * ptr __unused, vir_bytes addr __unused,
209 size_t size)
212 /* TODO: optionally dump struct kinfo_drivers array */
213 put_open(proc, name, 0, "[", ", ");
214 if (size > 0)
215 put_tail(proc, size / sizeof(struct kinfo_drivers), 0);
216 put_close(proc, "]");
218 return TRUE;
222 * Print CTL_KERN KERN_BOOTTIME.
224 static int
225 put_kern_boottime(struct trace_proc * proc, const char * name,
226 int type __unused, const void * ptr __unused, vir_bytes addr,
227 size_t size)
230 if (size == sizeof(struct timeval))
231 put_struct_timeval(proc, name, 0, addr);
232 else
233 put_ptr(proc, name, addr);
235 return TRUE;
239 * Print CTL_KERN KERN_SYSVIPC KERN_SYSVIPC_INFO.
241 static int
242 put_kern_sysvipc_info(struct trace_proc * proc, const char * name,
243 int type, const void * ptr, vir_bytes addr, size_t size)
245 const int *mib;
246 const char *text;
247 unsigned int i;
250 * TODO: print the obtained structure(s). For now we are just
251 * concerned with the name components.
253 if (type != ST_NAME) {
254 put_ptr(proc, name, addr);
256 return TRUE;
259 mib = (const int *)ptr;
261 for (i = 0; i < size; i++) {
262 text = NULL;
264 if (i == 0) {
265 switch (mib[i]) {
266 case KERN_SYSVIPC_SEM_INFO: text = "<sem>"; break;
267 case KERN_SYSVIPC_SHM_INFO: text = "<shm>"; break;
268 case KERN_SYSVIPC_MSG_INFO: text = "<msg>"; break;
272 if (!valuesonly && text != NULL)
273 put_field(proc, NULL, text);
274 else
275 put_value(proc, NULL, "%d", mib[i]);
278 return 0;
281 /* The CTL_KERN KERN_SYSVIPC table. */
282 static const struct sysctl_tab kern_sysvipc_tab[] = {
283 PROC(KERN_SYSVIPC_INFO, 0, put_kern_sysvipc_info),
286 /* The CTL_KERN table. */
287 static const struct sysctl_tab kern_tab[] = {
288 PROC(KERN_CLOCKRATE, sizeof(struct clockinfo), put_kern_clockrate),
289 PROC(KERN_PROC2, 0, put_kern_proc2),
290 PROC(KERN_PROC_ARGS, 0, put_kern_proc_args),
291 PROC(KERN_CP_TIME, sizeof(uint64_t) * CPUSTATES, put_kern_cp_time),
292 PROC(KERN_CONSDEV, sizeof(dev_t), put_kern_consdev),
293 PROC(KERN_DRIVERS, 0, put_kern_drivers),
294 NODE(KERN_SYSVIPC, kern_sysvipc_tab),
295 PROC(KERN_BOOTTIME, 0, put_kern_boottime),
299 * Print CTL_VM VM_LOADAVG.
301 static int
302 put_vm_loadavg(struct trace_proc * proc, const char * name __unused,
303 int type __unused, const void * ptr, vir_bytes addr __unused,
304 size_t size __unused)
306 const struct loadavg *loadavg;
307 unsigned int i;
309 loadavg = (const struct loadavg *)ptr;
311 put_open(proc, "ldavg", 0, "{", ", ");
313 for (i = 0; i < __arraycount(loadavg->ldavg); i++)
314 put_value(proc, NULL, "%"PRIu32, loadavg->ldavg[i]);
316 put_close(proc, "}");
318 if (verbose > 0) {
319 put_value(proc, "fscale", "%ld", loadavg->fscale);
321 return TRUE;
322 } else
323 return FALSE;
326 /* The CTL_VM table. */
327 static const struct sysctl_tab vm_tab[] = {
328 PROC(VM_LOADAVG, sizeof(struct loadavg), put_vm_loadavg),
332 * Print CTL_NET PF_ROUTE 0.
334 static int
335 put_net_route_rtable(struct trace_proc * proc, const char * name,
336 int type, const void * ptr, vir_bytes addr, size_t size)
338 const int *mib;
339 const char *text;
340 unsigned int i;
343 * TODO: print the obtained structure(s). For now we are just
344 * concerned with the name components.
346 if (type != ST_NAME) {
347 put_ptr(proc, name, addr);
349 return TRUE;
352 mib = (const int *)ptr;
354 for (i = 0; i < size; i++) {
355 text = NULL;
357 switch (i) {
358 case 0:
359 switch (mib[i]) {
360 case AF_UNSPEC: text = "<all>"; break;
361 case AF_LINK: text = "<link>"; break;
362 case AF_INET: text = "<inet>"; break;
363 case AF_INET6: text = "<inet6>"; break;
364 /* TODO: add more address families here */
366 break;
367 case 1:
368 switch (mib[i]) {
369 case NET_RT_DUMP: text = "<dump>"; break;
370 case NET_RT_FLAGS: text = "<flags>"; break;
371 case NET_RT_IFLIST: text = "<iflist>"; break;
373 break;
374 case 2:
375 if (mib[1] == NET_RT_IFLIST && mib[i] == 0)
376 text = "<all>";
379 if (!valuesonly && text != NULL)
380 put_field(proc, NULL, text);
381 else
382 put_value(proc, NULL, "%d", mib[i]);
385 return 0;
388 /* The CTL_NET PF_ROUTE table. */
389 static const struct sysctl_tab net_route_tab[] = {
390 PROC(0, 0, put_net_route_rtable),
393 /* The CTL_NET table. */
394 static const struct sysctl_tab net_tab[] = {
395 NODE(PF_ROUTE, net_route_tab),
398 /* The top-level table, which is indexed by identifier. */
399 static const struct sysctl_tab root_tab[] = {
400 [CTL_KERN] = NODE(0, kern_tab),
401 [CTL_VM] = NODE(0, vm_tab),
402 [CTL_NET] = NODE(0, net_tab),
406 * This buffer should be large enough to avoid having to perform dynamic
407 * allocation in all but highly exceptional cases. The CTL_KERN subtree is
408 * currently the largest, so we base the buffer size on its length.
409 * TODO: merge this buffer with ioctlbuf.
411 static char sysctlbuf[sizeof(struct sysctlnode) * KERN_MAXID];
413 static const struct flags sysctl_flags[] = {
414 FLAG_MASK(SYSCTL_VERS_MASK, SYSCTL_VERS_0),
415 FLAG_MASK(SYSCTL_VERS_MASK, SYSCTL_VERSION),
416 #define SYSCTL_VER_ENTRIES 2 /* the first N entries are for SYSCTL_VERS_MASK */
417 FLAG(CTLFLAG_UNSIGNED),
418 FLAG(CTLFLAG_OWNDESC),
419 FLAG(CTLFLAG_MMAP),
420 FLAG(CTLFLAG_ALIAS),
421 FLAG(CTLFLAG_ANYNUMBER),
422 FLAG(CTLFLAG_ROOT),
423 FLAG(CTLFLAG_HEX),
424 FLAG(CTLFLAG_IMMEDIATE),
425 FLAG(CTLFLAG_OWNDATA),
426 FLAG(CTLFLAG_HIDDEN),
427 FLAG(CTLFLAG_PERMANENT),
428 FLAG(CTLFLAG_PRIVATE),
429 FLAG(CTLFLAG_ANYWRITE),
430 FLAG_MASK(CTLFLAG_READWRITE, CTLFLAG_READONLY),
431 FLAG_MASK(CTLFLAG_READWRITE, CTLFLAG_READWRITE),
432 FLAG_MASK(SYSCTL_TYPEMASK, CTLTYPE_NODE),
433 FLAG_MASK(SYSCTL_TYPEMASK, CTLTYPE_INT),
434 FLAG_MASK(SYSCTL_TYPEMASK, CTLTYPE_STRING),
435 FLAG_MASK(SYSCTL_TYPEMASK, CTLTYPE_QUAD),
436 FLAG_MASK(SYSCTL_TYPEMASK, CTLTYPE_STRUCT),
437 FLAG_MASK(SYSCTL_TYPEMASK, CTLTYPE_BOOL),
441 * Print the immediate value of a sysctl node.
443 static void
444 put_sysctl_imm(struct trace_proc * proc, struct sysctlnode * scn, int use_name)
446 const char *name;
448 name = NULL;
450 switch (SYSCTL_TYPE(scn->sysctl_flags)) {
451 case CTLTYPE_INT:
452 if (use_name)
453 name = "sysctl_idata";
454 if (scn->sysctl_flags & CTLFLAG_HEX)
455 put_value(proc, name, "0x%x", scn->sysctl_idata);
456 else if (scn->sysctl_flags & CTLFLAG_UNSIGNED)
457 put_value(proc, name, "%u", scn->sysctl_idata);
458 else
459 put_value(proc, name, "%d", scn->sysctl_idata);
460 break;
461 case CTLTYPE_BOOL:
462 if (use_name)
463 name = "sysctl_bdata";
464 put_field(proc, name, (scn->sysctl_bdata) ? "true" : "false");
465 break;
466 case CTLTYPE_QUAD:
467 if (use_name)
468 name = "sysctl_qdata";
469 if (scn->sysctl_flags & CTLFLAG_HEX)
470 put_value(proc, name, "0x%"PRIx64, scn->sysctl_qdata);
471 else
472 put_value(proc, name, "%"PRIu64, scn->sysctl_qdata);
473 break;
478 * Printer for CTL_QUERY data.
480 static int
481 put_sysctl_query(struct trace_proc * proc, const char * name, int type,
482 const void * data __unused, vir_bytes addr, size_t size)
484 struct sysctlnode scn;
486 if (type == ST_NEWP) {
487 if (!put_open_struct(proc, name, 0, addr, &scn, sizeof(scn)))
488 return TRUE;
490 /* Print just the protocol version, that's all there is. */
491 if (verbose > 1)
492 put_flags(proc, "sysctl_flags", sysctl_flags,
493 SYSCTL_VER_ENTRIES, "0x%x", scn.sysctl_flags);
495 put_close_struct(proc, FALSE /*all*/);
496 } else {
497 /* TODO: optionally dump struct sysctlnode array */
498 put_open(proc, name, 0, "[", ", ");
499 if (size > 0)
500 put_tail(proc, size / sizeof(scn), 0);
501 put_close(proc, "]");
504 return TRUE;
508 * Printer for CTL_CREATE data.
510 static int
511 put_sysctl_create(struct trace_proc * proc, const char * name, int type,
512 const void * data __unused, vir_bytes addr, size_t size)
514 struct sysctlnode scn;
516 if (!put_open_struct(proc, name, 0, addr, &scn, sizeof(scn)))
517 return TRUE;
519 if (type == ST_NEWP)
520 put_flags(proc, "sysctl_flags", sysctl_flags,
521 COUNT(sysctl_flags), "0x%x", scn.sysctl_flags);
523 if (scn.sysctl_num == CTL_CREATE && type == ST_NEWP && !valuesonly)
524 put_field(proc, "sysctl_num", "CTL_CREATE");
525 else
526 put_value(proc, "sysctl_num", "%d", scn.sysctl_num);
528 if (type == ST_NEWP) {
529 put_buf(proc, "sysctl_name", PF_LOCADDR | PF_STRING,
530 (vir_bytes)scn.sysctl_name, sizeof(scn.sysctl_name));
532 if (scn.sysctl_ver != 0 && verbose > 0)
533 put_value(proc, "sysctl_ver", "%u", scn.sysctl_ver);
535 if (type == ST_NEWP) {
536 if (scn.sysctl_flags & CTLFLAG_IMMEDIATE)
537 put_sysctl_imm(proc, &scn, TRUE /*use_name*/);
539 switch (SYSCTL_TYPE(scn.sysctl_flags)) {
540 case CTLTYPE_NODE:
541 break;
542 case CTLTYPE_STRING:
543 if (scn.sysctl_data != NULL)
544 put_buf(proc, "sysctl_data", PF_STRING,
545 (vir_bytes)scn.sysctl_data,
546 (scn.sysctl_size > 0) ? scn.sysctl_size :
547 SSIZE_MAX /* hopefully it stops early */);
548 if (scn.sysctl_data != NULL || verbose == 0)
549 break;
550 /* FALLTHROUGH */
551 default:
552 if (!(scn.sysctl_flags & CTLFLAG_IMMEDIATE) &&
553 verbose > 0)
554 put_ptr(proc, "sysctl_data",
555 (vir_bytes)scn.sysctl_data);
556 break;
559 if (SYSCTL_TYPE(scn.sysctl_flags) == CTLTYPE_STRUCT ||
560 verbose > 0)
561 put_value(proc, "sysctl_size", "%zu", scn.sysctl_size);
564 put_close_struct(proc, FALSE /*all*/);
566 return TRUE;
570 * Printer for CTL_DESTROY data.
572 static int
573 put_sysctl_destroy(struct trace_proc * proc, const char * name, int type,
574 const void * data __unused, vir_bytes addr, size_t size)
576 struct sysctlnode scn;
578 if (!put_open_struct(proc, name, 0, addr, &scn, sizeof(scn)))
579 return TRUE;
581 if (type == ST_NEWP) {
582 put_value(proc, "sysctl_num", "%d", scn.sysctl_num);
583 if (scn.sysctl_name[0] != '\0')
584 put_buf(proc, "sysctl_name", PF_LOCADDR | PF_STRING,
585 (vir_bytes)scn.sysctl_name,
586 sizeof(scn.sysctl_name));
587 if (scn.sysctl_ver != 0 && verbose > 0)
588 put_value(proc, "sysctl_ver", "%u", scn.sysctl_ver);
591 put_close_struct(proc, FALSE /*all*/);
593 return TRUE;
597 * Printer for CTL_CREATE data.
599 static int
600 put_sysctl_describe(struct trace_proc * proc, const char * name, int type,
601 const void * data __unused, vir_bytes addr, size_t size)
603 struct sysctlnode scn;
605 if (type == ST_NEWP) {
606 if (!put_open_struct(proc, name, 0, addr, &scn, sizeof(scn)))
607 return TRUE;
609 /* Print just the protocol version, that's all there is. */
610 if (verbose > 1)
611 put_flags(proc, "sysctl_flags", sysctl_flags,
612 SYSCTL_VER_ENTRIES, "0x%x", scn.sysctl_flags);
614 put_value(proc, "sysctl_num", "%d", scn.sysctl_num);
616 if (scn.sysctl_desc != NULL)
617 put_buf(proc, "sysctl_desc", PF_STRING,
618 (vir_bytes)scn.sysctl_desc, 1024 /*no constant!*/);
619 else if (verbose > 0)
620 put_ptr(proc, "sysctl_desc",
621 (vir_bytes)scn.sysctl_desc);
623 put_close_struct(proc, FALSE /*all*/);
624 } else {
625 /* TODO: optionally dump struct sysctldesc array */
626 put_field(proc, name, (size == 0) ? "[]" : "[..]");
629 return TRUE;
633 * Printer for generic data, using the node flags stored in proc->sysctl_flags.
635 static int
636 put_sysctl_generic(struct trace_proc * proc, const char * name, int type,
637 const void * data __unused, vir_bytes addr, size_t size)
639 struct sysctlnode scn;
640 void *ptr;
641 size_t len;
643 switch (SYSCTL_TYPE(proc->sctl_flags)) {
644 case CTLTYPE_STRING:
645 put_buf(proc, name, PF_STRING, addr, size);
646 return TRUE;
647 case CTLTYPE_INT:
648 ptr = &scn.sysctl_idata;
649 len = sizeof(scn.sysctl_idata);
650 break;
651 case CTLTYPE_BOOL:
652 ptr = &scn.sysctl_bdata;
653 len = sizeof(scn.sysctl_bdata);
654 break;
655 case CTLTYPE_QUAD:
656 ptr = &scn.sysctl_qdata;
657 len = sizeof(scn.sysctl_qdata);
658 break;
659 case CTLTYPE_STRUCT:
660 default:
661 ptr = NULL;
662 len = 0;
663 break;
666 if (ptr == NULL || len != size ||
667 mem_get_data(proc->pid, addr, ptr, len) < 0) {
668 put_ptr(proc, name, addr);
669 return TRUE;
672 put_open(proc, name, PF_NONAME, "{", ", ");
674 scn.sysctl_flags = proc->sctl_flags;
676 put_sysctl_imm(proc, &scn, FALSE);
678 put_close(proc, "}");
680 return TRUE;
684 * Obtain information about a particular node 'id' in the node directory
685 * identified by the MIB path 'name' (length 'namelen'). Return TRUE if the
686 * node was found, in which case it is copied into 'scnp'. Return FALSE if the
687 * node was not found or another error occurred.
689 static int
690 get_sysctl_node(const int * name, unsigned int namelen, int id,
691 struct sysctlnode * scnp)
693 struct sysctlnode *scn, *escn, *fscn;
694 char *buf;
695 size_t len, elen;
696 int r, mib[CTL_MAXNAME];
698 assert(namelen < CTL_MAXNAME);
699 assert(id >= 0);
701 /* Query the parent, first using our static buffer for the results. */
702 memcpy(mib, name, sizeof(mib[0]) * namelen);
703 mib[namelen] = CTL_QUERY;
704 len = sizeof(sysctlbuf);
705 r = sysctl(mib, namelen + 1, sysctlbuf, &len, NULL, 0);
706 if (r == -1 && (errno != ENOMEM || len == 0))
707 return FALSE;
709 /* Even with partial results, check if we already found the node. */
710 elen = MIN(len, sizeof(sysctlbuf));
711 scn = (struct sysctlnode *)sysctlbuf;
712 escn = (struct sysctlnode *)&sysctlbuf[elen];
713 fscn = NULL; /* pointer to the node once found, NULL until then */
714 for (; scn < escn && fscn == NULL; scn++)
715 if (scn->sysctl_num == id)
716 fscn = scn;
718 /* If our buffer was too small, use a temporary buffer. */
719 if (fscn == NULL && r == -1) {
720 if ((buf = malloc(len)) == NULL)
721 return FALSE;
722 if (sysctl(mib, namelen, buf, &len, NULL, 0) == 0) {
723 scn = (struct sysctlnode *)sysctlbuf;
724 escn = (struct sysctlnode *)&sysctlbuf[len];
725 for (; scn < escn && fscn != NULL; scn++)
726 if (scn->sysctl_num == id)
727 fscn = scn;
729 free(buf);
732 if (fscn != NULL) {
733 memcpy(scnp, fscn, sizeof(*scnp));
734 return TRUE;
735 } else
736 return FALSE;
740 * Print the name string of one level of a sysctl(2) name, while also gathering
741 * information about the target node. Return 1 if name interpretation should
742 * continue as before, meaning this function will also be called for the next
743 * name component (if any). Return 0 if the rest of the name should be printed
744 * as numbers, without interpretation. Return -1 if printing the name is now
745 * complete.
747 static int
748 put_sysctl_namestr(struct trace_proc * proc, const int * name,
749 unsigned int namelen, unsigned int n, int all,
750 const struct sysctl_tab ** sctp)
752 const struct sysctl_tab *sct;
753 struct sysctlnode scn;
754 const char *namestr;
755 int i, r, id, is_last;
757 assert(n < namelen);
759 id = name[n];
760 is_last = (n == namelen - 1 && all);
761 namestr = NULL;
763 /* Negative identifiers are meta-identifiers. */
764 if (id < 0) {
765 switch (id) {
766 case CTL_EOL: namestr = "<eol>"; break;
767 case CTL_QUERY: namestr = "<query>"; break;
768 case CTL_CREATE: namestr = "<create>"; break;
769 case CTL_CREATESYM: namestr = "<createsym>"; break;
770 case CTL_DESTROY: namestr = "<destroy>"; break;
771 case CTL_MMAP: namestr = "<mmap>"; break;
772 case CTL_DESCRIBE: namestr = "<describe>"; break;
775 /* For some of them, we can print their parameters. */
776 if (is_last) {
777 switch (id) {
778 case CTL_QUERY:
779 proc->sctl_proc = put_sysctl_query;
780 break;
781 case CTL_CREATE:
782 proc->sctl_proc = put_sysctl_create;
783 break;
784 case CTL_DESTROY:
785 proc->sctl_proc = put_sysctl_destroy;
786 break;
787 case CTL_DESCRIBE:
788 proc->sctl_proc = put_sysctl_describe;
789 break;
794 * Meta-identifiers are allowed only at the very end of a name,
795 * so if anything follows a meta-identifier, there is no good
796 * way to interpret it. We just print numbers.
798 r = 0;
799 } else if (get_sysctl_node(name, n, id, &scn)) {
801 * For regular identifiers, first see if we have a callback
802 * function that does the interpretation. The use of the
803 * callback function depends on whether the current node is of
804 * type CTLTYPE_NODE: if it is, the callback function is
805 * responsible for printing the rest of the name (and we return
806 * -1 here after we are done, #1); if it isn't, then we just
807 * use the callback function to interpret the node value (#2).
808 * If we do not have a callback function, but the current node
809 * is of type CTLTYPE_NODE *and* has a non-NULL callback
810 * function registered in the MIB service, the remote callback
811 * function would interpret the rest of the name, so we simply
812 * print the rest of the name as numbers (returning 0 once we
813 * are done, #3). Without a MIB-service callback function,
814 * such nodes are just taken as path components and thus we
815 * return 1 to continue resolution (#4). Finally, if we do not
816 * have a callback function, and the current node is a data
817 * node (i.e., *not* of type CTLTYPE_NODE), we try to interpret
818 * it generically if it is the last component (#5), or we give
819 * up and just print numbers otherwise (#6).
822 /* Okay, so start by looking up the node in our own tables. */
823 sct = NULL;
824 if (n == 0) {
825 /* The top level is ID-indexed for performance. */
826 if ((unsigned int)id < __arraycount(root_tab))
827 *sctp = &root_tab[id];
828 else
829 *sctp = NULL;
830 } else if (*sctp != NULL) {
831 /* Other levels are searched, because of sparseness. */
832 sct = (*sctp)->tab; /* NULL if missing or leaf */
833 for (i = (int)(*sctp)->size; sct != NULL && i > 0;
834 i--, sct++)
835 if (sct->id == id)
836 break;
837 if (i == 0)
838 sct = NULL;
839 *sctp = sct;
842 /* Now determine what to do. */
843 if (SYSCTL_TYPE(scn.sysctl_flags) == CTLTYPE_NODE) {
844 if (sct != NULL && sct->proc != NULL) {
845 proc->sctl_size = sct->size;
846 proc->sctl_proc = sct->proc;
847 r = -1; /* #1 */
848 } else if (scn.sysctl_func != NULL)
849 r = 0; /* #3 */
850 else
851 r = 1; /* #4 */
852 } else {
853 if (!is_last)
854 r = 0; /* #6 */
855 else if (sct != NULL && sct->proc != NULL) {
856 /* A nonzero size must match the node size. */
857 if (sct->size == 0 ||
858 sct->size == scn.sysctl_size) {
859 proc->sctl_size = sct->size;
860 proc->sctl_proc = sct->proc;
862 r = 0; /* #2 */
863 } else {
864 proc->sctl_flags = scn.sysctl_flags;
865 proc->sctl_proc = put_sysctl_generic;
866 r = 0; /* #5 */
870 namestr = scn.sysctl_name;
871 } else {
873 * The node was not found. This basically means that we will
874 * not be able to get any information about deeper nodes
875 * either. We do not even try: just print numbers.
877 r = 0;
880 if (!valuesonly && namestr != NULL)
881 put_field(proc, NULL, namestr);
882 else
883 put_value(proc, NULL, "%d", id);
886 * Did we determine that the rest of the name should be printed by the
887 * callback function? Then we might as well make that happen. The
888 * abuse of the parameter types is not great, oh well.
890 if (r == -1)
891 (void)proc->sctl_proc(proc, NULL, ST_NAME, &name[n + 1], 0,
892 namelen - n - 1);
894 return r;
898 * Print the sysctl(2) name parameter, and gather information needed to print
899 * the oldp and newp parameters later.
901 static void
902 put_sysctl_name(struct trace_proc * proc, const char * name, int flags,
903 vir_bytes addr, unsigned int namelen)
905 const struct sysctl_tab *sct = NULL;
906 int r, all, namebuf[CTL_MAXNAME];
907 unsigned int n;
909 if (namelen > CTL_MAXNAME) {
910 namelen = CTL_MAXNAME;
911 all = 0;
912 } else
913 all = 1;
915 if ((flags & PF_FAILED) || valuesonly > 1 || namelen > CTL_MAXNAME ||
916 (namelen > 0 && !(flags & PF_LOCADDR) &&
917 mem_get_data(proc->pid, addr, namebuf,
918 namelen * sizeof(namebuf[0])) < 0)) {
919 if (flags & PF_LOCADDR)
920 put_field(proc, name, "&..");
921 else
922 put_ptr(proc, name, addr);
923 return;
924 } else if (namelen > 0 && (flags & PF_LOCADDR))
925 memcpy(namebuf, (void *)addr, sizeof(namebuf[0]) * namelen);
928 * Print the path name of the node as possible, and find information
929 * about the target node as we go along. See put_sysctl_namestr() for
930 * the meaning of 'r'.
932 put_open(proc, name, PF_NONAME, "[", ".");
933 for (n = 0, r = 1; n < namelen; n++) {
934 if (r == 1) {
935 if ((r = put_sysctl_namestr(proc, namebuf, namelen, n,
936 all, &sct)) < 0)
937 break;
938 } else
939 put_value(proc, NULL, "%d", namebuf[n]);
941 if (!all)
942 put_field(proc, NULL, "..");
943 put_close(proc, "]");
947 * Print the sysctl(2) oldp or newp parameter. PF_ALT means that the given
948 * parameter is newp rather than oldp, in which case PF_FAILED will not be set.
950 static void
951 put_sysctl_data(struct trace_proc * proc, const char * name, int flags,
952 vir_bytes addr, size_t len)
954 char *ptr;
955 int type, all;
957 if ((flags & PF_FAILED) || addr == 0 || valuesonly > 1 ||
958 proc->sctl_proc == NULL || proc->sctl_size > sizeof(sysctlbuf) ||
959 (proc->sctl_size > 0 && (proc->sctl_size != len ||
960 mem_get_data(proc->pid, addr, sysctlbuf, proc->sctl_size) < 0))) {
961 put_ptr(proc, name, addr);
962 return;
965 type = (flags & PF_ALT) ? ST_NEWP : ST_OLDP;
966 ptr = (proc->sctl_size > 0) ? sysctlbuf : NULL;
969 * The rough idea here: we have a "simple" mode and a "flexible" mode,
970 * depending on whether a size was specified in our table. For the
971 * simple mode, we only call the callback function when we have been
972 * able to copy in the data. A surrounding {} block will be printed
973 * automatically, the callback function only has to print the data
974 * fields. The simple mode is basically for structures. In contrast,
975 * the flexible mode leaves both the copying and the printing entirely
976 * to the callback function, which thus may print the pointer on copy
977 * failure (in which case the surrounding {}s would get in the way).
979 if (ptr != NULL)
980 put_open(proc, name, 0, "{", ", ");
982 all = proc->sctl_proc(proc, name, type, ptr, addr, len);
984 if (ptr != NULL) {
985 if (all == FALSE)
986 put_field(proc, NULL, "..");
987 put_close(proc, "}");
991 static int
992 mib_sysctl_out(struct trace_proc * proc, const message * m_out)
994 unsigned int namelen;
996 /* Reset the sysctl-related state. */
997 proc->sctl_flags = 0;
998 proc->sctl_size = 0;
999 proc->sctl_proc = NULL;
1000 proc->sctl_arg = 0;
1002 namelen = m_out->m_lc_mib_sysctl.namelen;
1004 /* As part of processing the name, we initialize the state. */
1005 if (namelen <= CTL_SHORTNAME)
1006 put_sysctl_name(proc, "name", PF_LOCADDR,
1007 (vir_bytes)&m_out->m_lc_mib_sysctl.name, namelen);
1008 else
1009 put_sysctl_name(proc, "name", 0, m_out->m_lc_mib_sysctl.namep,
1010 namelen);
1012 put_value(proc, "namelen", "%u", namelen);
1014 if (m_out->m_lc_mib_sysctl.oldp == 0 || valuesonly > 1) {
1015 put_sysctl_data(proc, "oldp", 0,
1016 m_out->m_lc_mib_sysctl.oldp,
1017 m_out->m_lc_mib_sysctl.oldlen);
1018 /* If oldp is NULL, oldlen may contain garbage; don't print. */
1019 if (m_out->m_lc_mib_sysctl.oldp != 0)
1020 put_value(proc, "oldlen", "%zu", /* {%zu} is more */
1021 m_out->m_lc_mib_sysctl.oldlen); /* correct.. */
1022 else
1023 put_value(proc, "oldlen", "%d", 0);
1024 put_sysctl_data(proc, "newp", PF_ALT,
1025 m_out->m_lc_mib_sysctl.newp,
1026 m_out->m_lc_mib_sysctl.newlen);
1027 put_value(proc, "newlen", "%zu",
1028 m_out->m_lc_mib_sysctl.newlen);
1029 return CT_DONE;
1030 } else
1031 return CT_NOTDONE;
1034 static void
1035 mib_sysctl_in(struct trace_proc * proc, const message * m_out,
1036 const message * m_in, int failed)
1038 int err;
1040 if (m_out->m_lc_mib_sysctl.oldp != 0 && valuesonly <= 1) {
1041 put_sysctl_data(proc, "oldp", failed,
1042 m_out->m_lc_mib_sysctl.oldp,
1043 m_in->m_mib_lc_sysctl.oldlen /* the returned length */);
1044 put_value(proc, "oldlen", "%zu", /* {%zu} is more correct.. */
1045 m_out->m_lc_mib_sysctl.oldlen);
1046 put_sysctl_data(proc, "newp", PF_ALT,
1047 m_out->m_lc_mib_sysctl.newp,
1048 m_out->m_lc_mib_sysctl.newlen);
1049 put_value(proc, "newlen", "%zu",
1050 m_out->m_lc_mib_sysctl.newlen);
1051 put_equals(proc);
1054 put_result(proc);
1057 * We want to print the returned old length in the following cases:
1058 * 1. the call succeeded, the old pointer was NULL, and no new data was
1059 * supplied;
1060 * 2. the call succeeded, the old pointer was not NULL, and the
1061 * returned old length is different from the supplied old length.
1062 * 3. the call failed with ENOMEM or EEXIST, and the old pointer was
1063 * not NULL (an undocumented NetBSD feature, used by sysctl(8)).
1065 if (/*#1*/ (!failed && m_out->m_lc_mib_sysctl.oldp == 0 &&
1066 (m_out->m_lc_mib_sysctl.newp == 0 ||
1067 m_out->m_lc_mib_sysctl.newlen == 0)) ||
1068 /*#2*/ (!failed && m_out->m_lc_mib_sysctl.oldp != 0 &&
1069 m_out->m_lc_mib_sysctl.oldlen != m_in->m_mib_lc_sysctl.oldlen) ||
1070 /*#3*/ (failed && call_errno(proc, &err) &&
1071 (err == ENOMEM || err == EEXIST) &&
1072 m_out->m_lc_mib_sysctl.oldp != 0)) {
1073 put_open(proc, NULL, 0, "(", ", ");
1074 put_value(proc, "oldlen", "%zu", m_in->m_mib_lc_sysctl.oldlen);
1075 put_close(proc, ")");
1079 #define MIB_CALL(c) [((MIB_ ## c) - MIB_BASE)]
1081 static const struct call_handler mib_map[] = {
1082 MIB_CALL(SYSCTL) = HANDLER("sysctl", mib_sysctl_out, mib_sysctl_in),
1085 const struct calls mib_calls = {
1086 .endpt = MIB_PROC_NR,
1087 .base = MIB_BASE,
1088 .map = mib_map,
1089 .count = COUNT(mib_map)