1 /* $NetBSD: sysctlgetmibinfo.c,v 1.6 2008/04/29 06:53:01 martin 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.6 2008/04/29 06:53:01 martin Exp $");
35 #endif /* LIBC_SCCS and not lint */
37 #include "namespace.h"
39 #include "reentrant.h"
40 #endif /* _REENTRANT */
41 #include <sys/param.h>
42 #include <sys/sysctl.h>
50 __weak_alias(__learn_tree
,___learn_tree
)
51 __weak_alias(sysctlgetmibinfo
,_sysctlgetmibinfo
)
55 * the place where we attach stuff we learn on the fly, not
58 static struct sysctlnode sysctl_mibroot
= {
61 * lint doesn't like my initializers
65 .sysctl_flags
= SYSCTL_VERSION
|CTLFLAG_ROOT
|CTLTYPE_NODE
,
66 sysc_init_field(_sysctl_size
, sizeof(struct sysctlnode
)),
67 .sysctl_name
= "(root)",
72 * routines to handle learning and cleanup
74 static int compar(const void *, const void *);
75 static void free_children(struct sysctlnode
*);
76 static void relearnhead(void);
79 * specifically not static since sysctl(8) "borrows" it.
81 int __learn_tree(int *, u_int
, struct sysctlnode
*);
84 * for ordering nodes -- a query may or may not be given them in
88 compar(const void *a
, const void *b
)
91 return (((const struct sysctlnode
*)a
)->sysctl_num
-
92 ((const struct sysctlnode
*)b
)->sysctl_num
);
96 * recursively nukes a branch or an entire tree from the given node
99 free_children(struct sysctlnode
*rnode
)
101 struct sysctlnode
*node
;
104 SYSCTL_TYPE(rnode
->sysctl_flags
) != CTLTYPE_NODE
||
105 rnode
->sysctl_child
== NULL
)
108 for (node
= rnode
->sysctl_child
;
109 node
< &rnode
->sysctl_child
[rnode
->sysctl_clen
];
113 free(rnode
->sysctl_child
);
114 rnode
->sysctl_child
= NULL
;
118 * verifies that the head of the tree in the kernel is the same as the
119 * head of the tree we already got, integrating new stuff and removing
120 * old stuff, if it's not.
125 struct sysctlnode
*h
, *i
, *o
, qnode
;
127 int rc
, name
, nlen
, olen
, ni
, oi
;
131 * if there's nothing there, there's no need to expend any
134 if (sysctl_mibroot
.sysctl_child
== NULL
)
138 * attempt to pull out the head of the tree, starting with the
139 * size we have now, and looping if we need more (or less)
143 so
= sysctl_mibroot
.sysctl_clen
* sizeof(struct sysctlnode
);
145 memset(&qnode
, 0, sizeof(qnode
));
146 qnode
.sysctl_flags
= SYSCTL_VERSION
;
150 rc
= sysctl(&name
, 1, h
, &so
, &qnode
, sizeof(qnode
));
151 if (rc
== -1 && errno
!= ENOMEM
)
158 * order the new copy of the head
160 nlen
= so
/ sizeof(struct sysctlnode
);
161 qsort(h
, (size_t)nlen
, sizeof(struct sysctlnode
), compar
);
164 * verify that everything is the same. if it is, we don't
165 * need to do any more work here.
167 olen
= sysctl_mibroot
.sysctl_clen
;
168 rc
= (nlen
== olen
) ? 0 : 1;
169 o
= sysctl_mibroot
.sysctl_child
;
170 for (ni
= 0; rc
== 0 && ni
< nlen
; ni
++) {
171 if (h
[ni
].sysctl_num
!= o
[ni
].sysctl_num
||
172 h
[ni
].sysctl_ver
!= o
[ni
].sysctl_ver
)
181 * something changed. h will become the new head, and we need
182 * pull over any subtrees we already have if they're the same
187 while (ni
< nlen
&& oi
< olen
) {
189 * something was inserted or deleted
191 if (SYSCTL_TYPE(i
[ni
].sysctl_flags
) == CTLTYPE_NODE
)
192 i
[ni
].sysctl_child
= NULL
;
193 if (i
[ni
].sysctl_num
!= o
[oi
].sysctl_num
) {
194 if (i
[ni
].sysctl_num
< o
[oi
].sysctl_num
) {
198 free_children(&o
[oi
]);
205 * same number, but different version, so throw away
206 * any accumulated children
208 if (i
[ni
].sysctl_ver
!= o
[oi
].sysctl_ver
)
209 free_children(&o
[oi
]);
212 * this node is the same, but we only need to
215 else if (SYSCTL_TYPE(i
[ni
].sysctl_flags
) == CTLTYPE_NODE
) {
217 * move subtree to new parent
219 i
[ni
].sysctl_clen
= o
[oi
].sysctl_clen
;
220 i
[ni
].sysctl_csize
= o
[oi
].sysctl_csize
;
221 i
[ni
].sysctl_child
= o
[oi
].sysctl_child
;
223 * reparent inherited subtree
226 i
[ni
].sysctl_child
!= NULL
&&
227 t
< i
[ni
].sysctl_clen
;
229 i
[ni
].sysctl_child
[t
].sysctl_parent
= &i
[ni
];
236 * left over new nodes need to have empty subtrees cleared
239 if (SYSCTL_TYPE(i
[ni
].sysctl_flags
) == CTLTYPE_NODE
)
240 i
[ni
].sysctl_child
= NULL
;
245 * left over old nodes need to be cleaned out
248 free_children(&o
[oi
]);
255 sysctl_mibroot
.sysctl_clen
= nlen
;
256 sysctl_mibroot
.sysctl_csize
= nlen
;
257 sysctl_mibroot
.sysctl_child
= h
;
262 * sucks in the children at a given level and attaches it to the tree.
265 __learn_tree(int *name
, u_int namelen
, struct sysctlnode
*pnode
)
267 struct sysctlnode qnode
;
272 pnode
= &sysctl_mibroot
;
273 if (SYSCTL_TYPE(pnode
->sysctl_flags
) != CTLTYPE_NODE
) {
277 if (pnode
->sysctl_child
!= NULL
)
280 if (pnode
->sysctl_clen
== 0)
281 sz
= SYSCTL_DEFSIZE
* sizeof(struct sysctlnode
);
283 sz
= pnode
->sysctl_clen
* sizeof(struct sysctlnode
);
284 pnode
->sysctl_child
= malloc(sz
);
285 if (pnode
->sysctl_child
== NULL
)
288 name
[namelen
] = CTL_QUERY
;
289 pnode
->sysctl_clen
= 0;
290 pnode
->sysctl_csize
= 0;
291 memset(&qnode
, 0, sizeof(qnode
));
292 qnode
.sysctl_flags
= SYSCTL_VERSION
;
293 rc
= sysctl(name
, namelen
+ 1, pnode
->sysctl_child
, &sz
,
294 &qnode
, sizeof(qnode
));
296 free(pnode
->sysctl_child
);
297 pnode
->sysctl_child
= NULL
;
301 free(pnode
->sysctl_child
);
302 pnode
->sysctl_child
= NULL
;
303 if ((sz
% sizeof(struct sysctlnode
)) != 0)
309 if (pnode
->sysctl_child
== NULL
) {
310 pnode
->sysctl_child
= malloc(sz
);
311 if (pnode
->sysctl_child
== NULL
)
314 rc
= sysctl(name
, namelen
+ 1, pnode
->sysctl_child
, &sz
,
315 &qnode
, sizeof(qnode
));
317 free(pnode
->sysctl_child
);
318 pnode
->sysctl_child
= NULL
;
324 * how many did we get?
326 pnode
->sysctl_clen
= sz
/ sizeof(struct sysctlnode
);
327 pnode
->sysctl_csize
= sz
/ sizeof(struct sysctlnode
);
328 if (pnode
->sysctl_clen
* sizeof(struct sysctlnode
) != sz
) {
329 free(pnode
->sysctl_child
);
330 pnode
->sysctl_child
= NULL
;
336 * you know, the kernel doesn't really keep them in any
337 * particular order...just like entries in a directory
339 qsort(pnode
->sysctl_child
, pnode
->sysctl_clen
,
340 sizeof(struct sysctlnode
), compar
);
343 * rearrange parent<->child linkage
345 for (rc
= 0; rc
< pnode
->sysctl_clen
; rc
++) {
346 pnode
->sysctl_child
[rc
].sysctl_parent
= pnode
;
347 if (SYSCTL_TYPE(pnode
->sysctl_child
[rc
].sysctl_flags
) ==
350 * these nodes may have children, but we
351 * haven't discovered that yet.
353 pnode
->sysctl_child
[rc
].sysctl_child
= NULL
;
355 pnode
->sysctl_child
[rc
].sysctl_desc
= NULL
;
362 * that's "given name" as a string, the integer form of the name fit
363 * to be passed to sysctl(), "canonicalized name" (optional), and a
364 * pointer to the length of the integer form. oh, and then a pointer
365 * to the node, in case you (the caller) care. you can leave them all
366 * NULL except for gname, though that might be rather pointless,
367 * unless all you wanna do is verify that a given name is acceptable.
369 * returns either 0 (everything was fine) or -1 and sets errno
370 * accordingly. if errno is set to EAGAIN, we detected a change to
371 * the mib while parsing, and you should try again. in the case of an
372 * invalid node name, cname will be set to contain the offending name.
375 static mutex_t sysctl_mutex
= MUTEX_INITIALIZER
;
376 static int sysctlgetmibinfo_unlocked(const char *, int *, u_int
*, char *,
377 size_t *, struct sysctlnode
**, int);
378 #endif /* __REENTRANT */
381 sysctlgetmibinfo(const char *gname
, int *iname
, u_int
*namelenp
,
382 char *cname
, size_t *csz
, struct sysctlnode
**rnode
, int v
)
387 mutex_lock(&sysctl_mutex
);
388 rc
= sysctlgetmibinfo_unlocked(gname
, iname
, namelenp
, cname
, csz
,
390 mutex_unlock(&sysctl_mutex
);
396 sysctlgetmibinfo_unlocked(const char *gname
, int *iname
, u_int
*namelenp
,
397 char *cname
, size_t *csz
, struct sysctlnode
**rnode
,
399 #endif /* _REENTRANT */
401 struct sysctlnode
*pnode
, *node
;
402 int name
[CTL_MAXNAME
], n
, haven
;
405 char sep
[2], token
[SYSCTL_NAMELEN
],
406 pname
[SYSCTL_NAMELEN
* CTL_MAXNAME
+ CTL_MAXNAME
];
407 const char *piece
, *dot
;
412 if (*rnode
== NULL
) {
413 /* XXX later deal with dealing back a sub version */
414 if (v
!= SYSCTL_VERSION
)
417 pnode
= &sysctl_mibroot
;
420 /* this is just someone being silly */
421 if (SYSCTL_VERS((*rnode
)->sysctl_flags
) != (uint32_t)v
)
424 /* XXX later deal with other people's trees */
425 if (SYSCTL_VERS((*rnode
)->sysctl_flags
) !=
433 pnode
= &sysctl_mibroot
;
435 if (pnode
== &sysctl_mibroot
)
444 * default to using '.' as the separator, but allow '/' as
445 * well, and then allow a leading separator
447 if ((dot
= strpbrk(gname
, "./")) == NULL
)
452 if (gname
[0] == sep
[0]) {
453 strlcat(pname
, sep
, sizeof(pname
));
457 #define COPY_OUT_DATA(t, c, cs, nlp, l) do { \
458 if ((c) != NULL && (cs) != NULL) \
459 *(cs) = strlcpy((c), (t), *(cs)); \
460 else if ((cs) != NULL) \
461 *(cs) = strlen(t) + 1; \
464 } while (/*CONSTCOND*/0)
467 while (piece
!= NULL
&& *piece
!= '\0') {
469 * what was i looking for?
471 dot
= strchr(piece
, sep
[0]);
473 l
= strlcpy(token
, piece
, sizeof(token
));
474 if (l
> sizeof(token
)) {
475 COPY_OUT_DATA(piece
, cname
, csz
, namelenp
, nl
);
476 errno
= ENAMETOOLONG
;
480 else if (dot
- piece
> (intptr_t)(sizeof(token
) - 1)) {
481 COPY_OUT_DATA(token
, cname
, csz
, namelenp
, nl
);
482 errno
= ENAMETOOLONG
;
486 strncpy(token
, piece
, (size_t)(dot
- piece
));
487 token
[dot
- piece
] = '\0';
491 * i wonder if this "token" is an integer?
494 q
= strtoimax(token
, &t
, 0);
496 if (errno
!= 0 || *t
!= '\0')
498 else if (q
< INT_MIN
|| q
> UINT_MAX
)
504 * make sure i have something to look at
506 if (SYSCTL_TYPE(pnode
->sysctl_flags
) != CTLTYPE_NODE
) {
507 if (haven
&& nl
> 0) {
508 strlcat(pname
, sep
, sizeof(pname
));
511 COPY_OUT_DATA(token
, cname
, csz
, namelenp
, nl
);
515 if (pnode
->sysctl_child
== NULL
) {
516 if (__learn_tree(name
, nl
, pnode
) == -1) {
517 COPY_OUT_DATA(token
, cname
, csz
, namelenp
, nl
);
521 node
= pnode
->sysctl_child
;
523 COPY_OUT_DATA(token
, cname
, csz
, namelenp
, nl
);
531 for (ni
= 0; ni
< pnode
->sysctl_clen
; ni
++)
532 if ((haven
&& ((n
== node
[ni
].sysctl_num
) ||
533 (node
[ni
].sysctl_flags
& CTLFLAG_ANYNUMBER
))) ||
534 strcmp(token
, node
[ni
].sysctl_name
) == 0)
536 if (ni
>= pnode
->sysctl_clen
) {
537 COPY_OUT_DATA(token
, cname
, csz
, namelenp
, nl
);
547 strlcat(pname
, sep
, sizeof(pname
));
548 if (haven
&& n
!= pnode
->sysctl_num
) {
550 strlcat(pname
, token
, sizeof(pname
));
554 strlcat(pname
, pnode
->sysctl_name
, sizeof(pname
));
555 name
[nl
] = pnode
->sysctl_num
;
557 piece
= (dot
!= NULL
) ? dot
+ 1 : NULL
;
559 if (nl
== CTL_MAXNAME
) {
560 COPY_OUT_DATA(token
, cname
, csz
, namelenp
, nl
);
567 if (namelenp
!= NULL
)
573 COPY_OUT_DATA(pname
, cname
, csz
, namelenp
, nl
);
574 if (iname
!= NULL
&& namelenp
!= NULL
)
575 memcpy(iname
, &name
[0], MIN(nl
, *namelenp
) * sizeof(int));
576 if (namelenp
!= NULL
)
581 * they gave us a private tree to work in, so
582 * we give back a pointer into that private
588 * they gave us a place to put the node data,
589 * so give them a copy
591 *rnode
= malloc(sizeof(struct sysctlnode
));
592 if (*rnode
!= NULL
) {
594 (*rnode
)->sysctl_child
= NULL
;
595 (*rnode
)->sysctl_parent
= NULL
;