1 /* $NetBSD: sysctlgetmibinfo.c,v 1.9 2010/12/13 23:10:13 pooka Exp $ */
4 * Copyright (c) 2003,2004 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 #if defined(LIBC_SCCS) && !defined(lint)
34 __RCSID("$NetBSD: sysctlgetmibinfo.c,v 1.9 2010/12/13 23:10:13 pooka Exp $");
35 #endif /* LIBC_SCCS and not lint */
38 #include "namespace.h"
40 #include "reentrant.h"
41 #endif /* _REENTRANT */
42 #endif /* RUMP_ACTION */
43 #include <sys/param.h>
44 #include <sys/sysctl.h>
52 #include <rump/rump_syscalls.h>
53 #define sysctl(a,b,c,d,e,f) rump_sys___sysctl(a,b,c,d,e,f)
56 __weak_alias(__learn_tree
,___learn_tree
)
57 __weak_alias(sysctlgetmibinfo
,_sysctlgetmibinfo
)
62 * the place where we attach stuff we learn on the fly, not
65 static struct sysctlnode sysctl_mibroot
= {
68 * lint doesn't like my initializers
72 .sysctl_flags
= SYSCTL_VERSION
|CTLFLAG_ROOT
|CTLTYPE_NODE
,
73 sysc_init_field(_sysctl_size
, sizeof(struct sysctlnode
)),
74 .sysctl_name
= "(root)",
79 * routines to handle learning and cleanup
81 static int compar(const void *, const void *);
82 static void free_children(struct sysctlnode
*);
83 static void relearnhead(void);
86 * specifically not static since sysctl(8) "borrows" it.
88 int __learn_tree(int *, u_int
, struct sysctlnode
*);
91 * for ordering nodes -- a query may or may not be given them in
95 compar(const void *a
, const void *b
)
98 return (((const struct sysctlnode
*)a
)->sysctl_num
-
99 ((const struct sysctlnode
*)b
)->sysctl_num
);
103 * recursively nukes a branch or an entire tree from the given node
106 free_children(struct sysctlnode
*rnode
)
108 struct sysctlnode
*node
;
111 SYSCTL_TYPE(rnode
->sysctl_flags
) != CTLTYPE_NODE
||
112 rnode
->sysctl_child
== NULL
)
115 for (node
= rnode
->sysctl_child
;
116 node
< &rnode
->sysctl_child
[rnode
->sysctl_clen
];
120 free(rnode
->sysctl_child
);
121 rnode
->sysctl_child
= NULL
;
125 * verifies that the head of the tree in the kernel is the same as the
126 * head of the tree we already got, integrating new stuff and removing
127 * old stuff, if it's not.
132 struct sysctlnode
*h
, *i
, *o
, qnode
;
134 int rc
, name
, nlen
, olen
, ni
, oi
;
138 * if there's nothing there, there's no need to expend any
141 if (sysctl_mibroot
.sysctl_child
== NULL
)
145 * attempt to pull out the head of the tree, starting with the
146 * size we have now, and looping if we need more (or less)
150 so
= sysctl_mibroot
.sysctl_clen
* sizeof(struct sysctlnode
);
152 memset(&qnode
, 0, sizeof(qnode
));
153 qnode
.sysctl_flags
= SYSCTL_VERSION
;
157 rc
= sysctl(&name
, 1, h
, &so
, &qnode
, sizeof(qnode
));
158 if (rc
== -1 && errno
!= ENOMEM
)
165 * order the new copy of the head
167 nlen
= so
/ sizeof(struct sysctlnode
);
168 qsort(h
, (size_t)nlen
, sizeof(struct sysctlnode
), compar
);
171 * verify that everything is the same. if it is, we don't
172 * need to do any more work here.
174 olen
= sysctl_mibroot
.sysctl_clen
;
175 rc
= (nlen
== olen
) ? 0 : 1;
176 o
= sysctl_mibroot
.sysctl_child
;
177 for (ni
= 0; rc
== 0 && ni
< nlen
; ni
++) {
178 if (h
[ni
].sysctl_num
!= o
[ni
].sysctl_num
||
179 h
[ni
].sysctl_ver
!= o
[ni
].sysctl_ver
)
188 * something changed. h will become the new head, and we need
189 * pull over any subtrees we already have if they're the same
194 while (ni
< nlen
&& oi
< olen
) {
196 * something was inserted or deleted
198 if (SYSCTL_TYPE(i
[ni
].sysctl_flags
) == CTLTYPE_NODE
)
199 i
[ni
].sysctl_child
= NULL
;
200 if (i
[ni
].sysctl_num
!= o
[oi
].sysctl_num
) {
201 if (i
[ni
].sysctl_num
< o
[oi
].sysctl_num
) {
205 free_children(&o
[oi
]);
212 * same number, but different version, so throw away
213 * any accumulated children
215 if (i
[ni
].sysctl_ver
!= o
[oi
].sysctl_ver
)
216 free_children(&o
[oi
]);
219 * this node is the same, but we only need to
222 else if (SYSCTL_TYPE(i
[ni
].sysctl_flags
) == CTLTYPE_NODE
) {
224 * move subtree to new parent
226 i
[ni
].sysctl_clen
= o
[oi
].sysctl_clen
;
227 i
[ni
].sysctl_csize
= o
[oi
].sysctl_csize
;
228 i
[ni
].sysctl_child
= o
[oi
].sysctl_child
;
230 * reparent inherited subtree
233 i
[ni
].sysctl_child
!= NULL
&&
234 t
< i
[ni
].sysctl_clen
;
236 i
[ni
].sysctl_child
[t
].sysctl_parent
= &i
[ni
];
243 * left over new nodes need to have empty subtrees cleared
246 if (SYSCTL_TYPE(i
[ni
].sysctl_flags
) == CTLTYPE_NODE
)
247 i
[ni
].sysctl_child
= NULL
;
252 * left over old nodes need to be cleaned out
255 free_children(&o
[oi
]);
262 sysctl_mibroot
.sysctl_clen
= nlen
;
263 sysctl_mibroot
.sysctl_csize
= nlen
;
264 sysctl_mibroot
.sysctl_child
= h
;
269 * sucks in the children at a given level and attaches it to the tree.
272 __learn_tree(int *name
, u_int namelen
, struct sysctlnode
*pnode
)
274 struct sysctlnode qnode
;
279 pnode
= &sysctl_mibroot
;
280 if (SYSCTL_TYPE(pnode
->sysctl_flags
) != CTLTYPE_NODE
) {
284 if (pnode
->sysctl_child
!= NULL
)
287 if (pnode
->sysctl_clen
== 0)
288 sz
= SYSCTL_DEFSIZE
* sizeof(struct sysctlnode
);
290 sz
= pnode
->sysctl_clen
* sizeof(struct sysctlnode
);
291 pnode
->sysctl_child
= malloc(sz
);
292 if (pnode
->sysctl_child
== NULL
)
295 name
[namelen
] = CTL_QUERY
;
296 pnode
->sysctl_clen
= 0;
297 pnode
->sysctl_csize
= 0;
298 memset(&qnode
, 0, sizeof(qnode
));
299 qnode
.sysctl_flags
= SYSCTL_VERSION
;
300 rc
= sysctl(name
, namelen
+ 1, pnode
->sysctl_child
, &sz
,
301 &qnode
, sizeof(qnode
));
303 free(pnode
->sysctl_child
);
304 pnode
->sysctl_child
= NULL
;
308 free(pnode
->sysctl_child
);
309 pnode
->sysctl_child
= NULL
;
310 if ((sz
% sizeof(struct sysctlnode
)) != 0)
316 if (pnode
->sysctl_child
== NULL
) {
317 pnode
->sysctl_child
= malloc(sz
);
318 if (pnode
->sysctl_child
== NULL
)
321 rc
= sysctl(name
, namelen
+ 1, pnode
->sysctl_child
, &sz
,
322 &qnode
, sizeof(qnode
));
324 free(pnode
->sysctl_child
);
325 pnode
->sysctl_child
= NULL
;
331 * how many did we get?
333 pnode
->sysctl_clen
= sz
/ sizeof(struct sysctlnode
);
334 pnode
->sysctl_csize
= sz
/ sizeof(struct sysctlnode
);
335 if (pnode
->sysctl_clen
* sizeof(struct sysctlnode
) != sz
) {
336 free(pnode
->sysctl_child
);
337 pnode
->sysctl_child
= NULL
;
343 * you know, the kernel doesn't really keep them in any
344 * particular order...just like entries in a directory
346 qsort(pnode
->sysctl_child
, pnode
->sysctl_clen
,
347 sizeof(struct sysctlnode
), compar
);
350 * rearrange parent<->child linkage
352 for (rc
= 0; rc
< pnode
->sysctl_clen
; rc
++) {
353 pnode
->sysctl_child
[rc
].sysctl_parent
= pnode
;
354 if (SYSCTL_TYPE(pnode
->sysctl_child
[rc
].sysctl_flags
) ==
357 * these nodes may have children, but we
358 * haven't discovered that yet.
360 pnode
->sysctl_child
[rc
].sysctl_child
= NULL
;
362 pnode
->sysctl_child
[rc
].sysctl_desc
= NULL
;
369 * that's "given name" as a string, the integer form of the name fit
370 * to be passed to sysctl(), "canonicalized name" (optional), and a
371 * pointer to the length of the integer form. oh, and then a pointer
372 * to the node, in case you (the caller) care. you can leave them all
373 * NULL except for gname, though that might be rather pointless,
374 * unless all you wanna do is verify that a given name is acceptable.
376 * returns either 0 (everything was fine) or -1 and sets errno
377 * accordingly. if errno is set to EAGAIN, we detected a change to
378 * the mib while parsing, and you should try again. in the case of an
379 * invalid node name, cname will be set to contain the offending name.
382 static mutex_t sysctl_mutex
= MUTEX_INITIALIZER
;
383 static int sysctlgetmibinfo_unlocked(const char *, int *, u_int
*, char *,
384 size_t *, struct sysctlnode
**, int);
385 #endif /* __REENTRANT */
388 sysctlgetmibinfo(const char *gname
, int *iname
, u_int
*namelenp
,
389 char *cname
, size_t *csz
, struct sysctlnode
**rnode
, int v
)
394 mutex_lock(&sysctl_mutex
);
395 rc
= sysctlgetmibinfo_unlocked(gname
, iname
, namelenp
, cname
, csz
,
397 mutex_unlock(&sysctl_mutex
);
403 sysctlgetmibinfo_unlocked(const char *gname
, int *iname
, u_int
*namelenp
,
404 char *cname
, size_t *csz
, struct sysctlnode
**rnode
,
406 #endif /* _REENTRANT */
408 struct sysctlnode
*pnode
, *node
;
409 int name
[CTL_MAXNAME
], n
, haven
;
412 char sep
[2], token
[SYSCTL_NAMELEN
],
413 pname
[SYSCTL_NAMELEN
* CTL_MAXNAME
+ CTL_MAXNAME
];
414 const char *piece
, *dot
;
419 if (*rnode
== NULL
) {
420 /* XXX later deal with dealing back a sub version */
421 if (v
!= SYSCTL_VERSION
)
424 pnode
= &sysctl_mibroot
;
427 /* this is just someone being silly */
428 if (SYSCTL_VERS((*rnode
)->sysctl_flags
) != (uint32_t)v
)
431 /* XXX later deal with other people's trees */
432 if (SYSCTL_VERS((*rnode
)->sysctl_flags
) !=
440 pnode
= &sysctl_mibroot
;
442 if (pnode
== &sysctl_mibroot
)
451 * default to using '.' as the separator, but allow '/' as
452 * well, and then allow a leading separator
454 if ((dot
= strpbrk(gname
, "./")) == NULL
)
459 if (gname
[0] == sep
[0]) {
460 strlcat(pname
, sep
, sizeof(pname
));
464 #define COPY_OUT_DATA(t, c, cs, nlp, l) do { \
465 if ((c) != NULL && (cs) != NULL) \
466 *(cs) = strlcpy((c), (t), *(cs)); \
467 else if ((cs) != NULL) \
468 *(cs) = strlen(t) + 1; \
471 } while (/*CONSTCOND*/0)
474 while (piece
!= NULL
&& *piece
!= '\0') {
476 * what was i looking for?
478 dot
= strchr(piece
, sep
[0]);
480 l
= strlcpy(token
, piece
, sizeof(token
));
481 if (l
> sizeof(token
)) {
482 COPY_OUT_DATA(piece
, cname
, csz
, namelenp
, nl
);
483 errno
= ENAMETOOLONG
;
487 else if (dot
- piece
> (intptr_t)(sizeof(token
) - 1)) {
488 COPY_OUT_DATA(token
, cname
, csz
, namelenp
, nl
);
489 errno
= ENAMETOOLONG
;
493 strncpy(token
, piece
, (size_t)(dot
- piece
));
494 token
[dot
- piece
] = '\0';
498 * i wonder if this "token" is an integer?
501 q
= strtoimax(token
, &t
, 0);
503 if (errno
!= 0 || *t
!= '\0')
505 else if (q
< INT_MIN
|| q
> UINT_MAX
)
511 * make sure i have something to look at
513 if (SYSCTL_TYPE(pnode
->sysctl_flags
) != CTLTYPE_NODE
) {
514 if (haven
&& nl
> 0) {
515 strlcat(pname
, sep
, sizeof(pname
));
518 COPY_OUT_DATA(token
, cname
, csz
, namelenp
, nl
);
522 if (pnode
->sysctl_child
== NULL
) {
523 if (__learn_tree(name
, nl
, pnode
) == -1) {
524 COPY_OUT_DATA(token
, cname
, csz
, namelenp
, nl
);
528 node
= pnode
->sysctl_child
;
530 COPY_OUT_DATA(token
, cname
, csz
, namelenp
, nl
);
538 for (ni
= 0; ni
< pnode
->sysctl_clen
; ni
++)
539 if ((haven
&& ((n
== node
[ni
].sysctl_num
) ||
540 (node
[ni
].sysctl_flags
& CTLFLAG_ANYNUMBER
))) ||
541 strcmp(token
, node
[ni
].sysctl_name
) == 0)
543 if (ni
>= pnode
->sysctl_clen
) {
544 COPY_OUT_DATA(token
, cname
, csz
, namelenp
, nl
);
554 strlcat(pname
, sep
, sizeof(pname
));
555 if (haven
&& n
!= pnode
->sysctl_num
) {
557 strlcat(pname
, token
, sizeof(pname
));
561 strlcat(pname
, pnode
->sysctl_name
, sizeof(pname
));
562 name
[nl
] = pnode
->sysctl_num
;
564 piece
= (dot
!= NULL
) ? dot
+ 1 : NULL
;
566 if (nl
== CTL_MAXNAME
) {
567 COPY_OUT_DATA(token
, cname
, csz
, namelenp
, nl
);
574 if (namelenp
!= NULL
)
580 COPY_OUT_DATA(pname
, cname
, csz
, namelenp
, nl
);
581 if (iname
!= NULL
&& namelenp
!= NULL
)
582 memcpy(iname
, &name
[0], MIN(nl
, *namelenp
) * sizeof(int));
583 if (namelenp
!= NULL
)
588 * they gave us a private tree to work in, so
589 * we give back a pointer into that private
595 * they gave us a place to put the node data,
596 * so give them a copy
598 *rnode
= malloc(sizeof(struct sysctlnode
));
599 if (*rnode
!= NULL
) {
601 (*rnode
)->sysctl_child
= NULL
;
602 (*rnode
)->sysctl_parent
= NULL
;