1 /* $NetBSD: sysctlgetmibinfo.c,v 1.10 2012/03/13 21:13:37 christos 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.10 2012/03/13 21:13:37 christos 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>
53 #include <rump/rump_syscalls.h>
54 #define sysctl(a,b,c,d,e,f) rump_sys___sysctl(a,b,c,d,e,f)
57 __weak_alias(__learn_tree
,___learn_tree
)
58 __weak_alias(sysctlgetmibinfo
,_sysctlgetmibinfo
)
63 * the place where we attach stuff we learn on the fly, not
66 static struct sysctlnode sysctl_mibroot
= {
69 * lint doesn't like my initializers
73 .sysctl_flags
= SYSCTL_VERSION
|CTLFLAG_ROOT
|CTLTYPE_NODE
,
74 sysc_init_field(_sysctl_size
, sizeof(struct sysctlnode
)),
75 .sysctl_name
= "(root)",
80 * routines to handle learning and cleanup
82 static int compar(const void *, const void *);
83 static void free_children(struct sysctlnode
*);
84 static void relearnhead(void);
87 * specifically not static since sysctl(8) "borrows" it.
89 int __learn_tree(int *, u_int
, struct sysctlnode
*);
92 * for ordering nodes -- a query may or may not be given them in
96 compar(const void *a
, const void *b
)
99 return (((const struct sysctlnode
*)a
)->sysctl_num
-
100 ((const struct sysctlnode
*)b
)->sysctl_num
);
104 * recursively nukes a branch or an entire tree from the given node
107 free_children(struct sysctlnode
*rnode
)
109 struct sysctlnode
*node
;
112 SYSCTL_TYPE(rnode
->sysctl_flags
) != CTLTYPE_NODE
||
113 rnode
->sysctl_child
== NULL
)
116 for (node
= rnode
->sysctl_child
;
117 node
< &rnode
->sysctl_child
[rnode
->sysctl_clen
];
121 free(rnode
->sysctl_child
);
122 rnode
->sysctl_child
= NULL
;
126 * verifies that the head of the tree in the kernel is the same as the
127 * head of the tree we already got, integrating new stuff and removing
128 * old stuff, if it's not.
133 struct sysctlnode
*h
, *i
, *o
, qnode
;
136 size_t nlen
, olen
, ni
, oi
;
140 * if there's nothing there, there's no need to expend any
143 if (sysctl_mibroot
.sysctl_child
== NULL
)
147 * attempt to pull out the head of the tree, starting with the
148 * size we have now, and looping if we need more (or less)
152 so
= sysctl_mibroot
.sysctl_clen
* sizeof(struct sysctlnode
);
154 memset(&qnode
, 0, sizeof(qnode
));
155 qnode
.sysctl_flags
= SYSCTL_VERSION
;
159 rc
= sysctl(&name
, 1, h
, &so
, &qnode
, sizeof(qnode
));
160 if (rc
== -1 && errno
!= ENOMEM
)
167 * order the new copy of the head
169 nlen
= so
/ sizeof(struct sysctlnode
);
170 qsort(h
, nlen
, sizeof(struct sysctlnode
), compar
);
173 * verify that everything is the same. if it is, we don't
174 * need to do any more work here.
176 olen
= sysctl_mibroot
.sysctl_clen
;
177 rc
= (nlen
== olen
) ? 0 : 1;
178 o
= sysctl_mibroot
.sysctl_child
;
179 for (ni
= 0; rc
== 0 && ni
< nlen
; ni
++) {
180 if (h
[ni
].sysctl_num
!= o
[ni
].sysctl_num
||
181 h
[ni
].sysctl_ver
!= o
[ni
].sysctl_ver
)
190 * something changed. h will become the new head, and we need
191 * pull over any subtrees we already have if they're the same
196 while (ni
< nlen
&& oi
< olen
) {
198 * something was inserted or deleted
200 if (SYSCTL_TYPE(i
[ni
].sysctl_flags
) == CTLTYPE_NODE
)
201 i
[ni
].sysctl_child
= NULL
;
202 if (i
[ni
].sysctl_num
!= o
[oi
].sysctl_num
) {
203 if (i
[ni
].sysctl_num
< o
[oi
].sysctl_num
) {
207 free_children(&o
[oi
]);
214 * same number, but different version, so throw away
215 * any accumulated children
217 if (i
[ni
].sysctl_ver
!= o
[oi
].sysctl_ver
)
218 free_children(&o
[oi
]);
221 * this node is the same, but we only need to
224 else if (SYSCTL_TYPE(i
[ni
].sysctl_flags
) == CTLTYPE_NODE
) {
226 * move subtree to new parent
228 i
[ni
].sysctl_clen
= o
[oi
].sysctl_clen
;
229 i
[ni
].sysctl_csize
= o
[oi
].sysctl_csize
;
230 i
[ni
].sysctl_child
= o
[oi
].sysctl_child
;
232 * reparent inherited subtree
235 i
[ni
].sysctl_child
!= NULL
&&
236 t
< i
[ni
].sysctl_clen
;
238 i
[ni
].sysctl_child
[t
].sysctl_parent
= &i
[ni
];
245 * left over new nodes need to have empty subtrees cleared
248 if (SYSCTL_TYPE(i
[ni
].sysctl_flags
) == CTLTYPE_NODE
)
249 i
[ni
].sysctl_child
= NULL
;
254 * left over old nodes need to be cleaned out
257 free_children(&o
[oi
]);
264 _DIAGASSERT(__type_fit(uint32_t, nlen
));
265 sysctl_mibroot
.sysctl_csize
=
266 sysctl_mibroot
.sysctl_clen
= (uint32_t)nlen
;
267 sysctl_mibroot
.sysctl_child
= h
;
272 * sucks in the children at a given level and attaches it to the tree.
275 __learn_tree(int *name
, u_int namelen
, struct sysctlnode
*pnode
)
277 struct sysctlnode qnode
;
282 pnode
= &sysctl_mibroot
;
283 if (SYSCTL_TYPE(pnode
->sysctl_flags
) != CTLTYPE_NODE
) {
287 if (pnode
->sysctl_child
!= NULL
)
290 if (pnode
->sysctl_clen
== 0)
291 sz
= SYSCTL_DEFSIZE
* sizeof(struct sysctlnode
);
293 sz
= pnode
->sysctl_clen
* sizeof(struct sysctlnode
);
294 pnode
->sysctl_child
= malloc(sz
);
295 if (pnode
->sysctl_child
== NULL
)
298 name
[namelen
] = CTL_QUERY
;
299 pnode
->sysctl_clen
= 0;
300 pnode
->sysctl_csize
= 0;
301 memset(&qnode
, 0, sizeof(qnode
));
302 qnode
.sysctl_flags
= SYSCTL_VERSION
;
303 rc
= sysctl(name
, namelen
+ 1, pnode
->sysctl_child
, &sz
,
304 &qnode
, sizeof(qnode
));
306 free(pnode
->sysctl_child
);
307 pnode
->sysctl_child
= NULL
;
311 free(pnode
->sysctl_child
);
312 pnode
->sysctl_child
= NULL
;
313 if ((sz
% sizeof(struct sysctlnode
)) != 0)
319 if (pnode
->sysctl_child
== NULL
) {
320 pnode
->sysctl_child
= malloc(sz
);
321 if (pnode
->sysctl_child
== NULL
)
324 rc
= sysctl(name
, namelen
+ 1, pnode
->sysctl_child
, &sz
,
325 &qnode
, sizeof(qnode
));
327 free(pnode
->sysctl_child
);
328 pnode
->sysctl_child
= NULL
;
334 * how many did we get?
336 sz
/= sizeof(struct sysctlnode
);
337 pnode
->sysctl_csize
= pnode
->sysctl_clen
= (uint32_t)sz
;
338 if (pnode
->sysctl_clen
!= sz
) {
339 free(pnode
->sysctl_child
);
340 pnode
->sysctl_child
= NULL
;
346 * you know, the kernel doesn't really keep them in any
347 * particular order...just like entries in a directory
349 qsort(pnode
->sysctl_child
, pnode
->sysctl_clen
,
350 sizeof(struct sysctlnode
), compar
);
353 * rearrange parent<->child linkage
355 for (rc
= 0; rc
< pnode
->sysctl_clen
; rc
++) {
356 pnode
->sysctl_child
[rc
].sysctl_parent
= pnode
;
357 if (SYSCTL_TYPE(pnode
->sysctl_child
[rc
].sysctl_flags
) ==
360 * these nodes may have children, but we
361 * haven't discovered that yet.
363 pnode
->sysctl_child
[rc
].sysctl_child
= NULL
;
365 pnode
->sysctl_child
[rc
].sysctl_desc
= NULL
;
372 * that's "given name" as a string, the integer form of the name fit
373 * to be passed to sysctl(), "canonicalized name" (optional), and a
374 * pointer to the length of the integer form. oh, and then a pointer
375 * to the node, in case you (the caller) care. you can leave them all
376 * NULL except for gname, though that might be rather pointless,
377 * unless all you wanna do is verify that a given name is acceptable.
379 * returns either 0 (everything was fine) or -1 and sets errno
380 * accordingly. if errno is set to EAGAIN, we detected a change to
381 * the mib while parsing, and you should try again. in the case of an
382 * invalid node name, cname will be set to contain the offending name.
385 static mutex_t sysctl_mutex
= MUTEX_INITIALIZER
;
386 static int sysctlgetmibinfo_unlocked(const char *, int *, u_int
*, char *,
387 size_t *, struct sysctlnode
**, int);
388 #endif /* __REENTRANT */
391 sysctlgetmibinfo(const char *gname
, int *iname
, u_int
*namelenp
,
392 char *cname
, size_t *csz
, struct sysctlnode
**rnode
, int v
)
397 mutex_lock(&sysctl_mutex
);
398 rc
= sysctlgetmibinfo_unlocked(gname
, iname
, namelenp
, cname
, csz
,
400 mutex_unlock(&sysctl_mutex
);
406 sysctlgetmibinfo_unlocked(const char *gname
, int *iname
, u_int
*namelenp
,
407 char *cname
, size_t *csz
, struct sysctlnode
**rnode
,
409 #endif /* _REENTRANT */
411 struct sysctlnode
*pnode
, *node
;
412 int name
[CTL_MAXNAME
], n
, haven
;
415 char sep
[2], token
[SYSCTL_NAMELEN
],
416 pname
[SYSCTL_NAMELEN
* CTL_MAXNAME
+ CTL_MAXNAME
];
417 const char *piece
, *dot
;
422 if (*rnode
== NULL
) {
423 /* XXX later deal with dealing back a sub version */
424 if (v
!= SYSCTL_VERSION
)
427 pnode
= &sysctl_mibroot
;
430 /* this is just someone being silly */
431 if (SYSCTL_VERS((*rnode
)->sysctl_flags
) != (uint32_t)v
)
434 /* XXX later deal with other people's trees */
435 if (SYSCTL_VERS((*rnode
)->sysctl_flags
) !=
443 pnode
= &sysctl_mibroot
;
445 if (pnode
== &sysctl_mibroot
)
454 * default to using '.' as the separator, but allow '/' as
455 * well, and then allow a leading separator
457 if ((dot
= strpbrk(gname
, "./")) == NULL
)
462 if (gname
[0] == sep
[0]) {
463 strlcat(pname
, sep
, sizeof(pname
));
467 #define COPY_OUT_DATA(t, c, cs, nlp, l) do { \
468 if ((c) != NULL && (cs) != NULL) \
469 *(cs) = strlcpy((c), (t), *(cs)); \
470 else if ((cs) != NULL) \
471 *(cs) = strlen(t) + 1; \
474 } while (/*CONSTCOND*/0)
477 while (piece
!= NULL
&& *piece
!= '\0') {
479 * what was i looking for?
481 dot
= strchr(piece
, sep
[0]);
483 l
= strlcpy(token
, piece
, sizeof(token
));
484 if (l
> sizeof(token
)) {
485 COPY_OUT_DATA(piece
, cname
, csz
, namelenp
, nl
);
486 errno
= ENAMETOOLONG
;
490 else if (dot
- piece
> (intptr_t)(sizeof(token
) - 1)) {
491 COPY_OUT_DATA(token
, cname
, csz
, namelenp
, nl
);
492 errno
= ENAMETOOLONG
;
496 strncpy(token
, piece
, (size_t)(dot
- piece
));
497 token
[dot
- piece
] = '\0';
501 * i wonder if this "token" is an integer?
504 q
= strtoimax(token
, &t
, 0);
506 if (errno
!= 0 || *t
!= '\0')
508 else if (q
< INT_MIN
|| q
> UINT_MAX
)
514 * make sure i have something to look at
516 if (SYSCTL_TYPE(pnode
->sysctl_flags
) != CTLTYPE_NODE
) {
517 if (haven
&& nl
> 0) {
518 strlcat(pname
, sep
, sizeof(pname
));
521 COPY_OUT_DATA(token
, cname
, csz
, namelenp
, nl
);
525 if (pnode
->sysctl_child
== NULL
) {
526 if (__learn_tree(name
, nl
, pnode
) == -1) {
527 COPY_OUT_DATA(token
, cname
, csz
, namelenp
, nl
);
531 node
= pnode
->sysctl_child
;
533 COPY_OUT_DATA(token
, cname
, csz
, namelenp
, nl
);
541 for (ni
= 0; ni
< pnode
->sysctl_clen
; ni
++)
542 if ((haven
&& ((n
== node
[ni
].sysctl_num
) ||
543 (node
[ni
].sysctl_flags
& CTLFLAG_ANYNUMBER
))) ||
544 strcmp(token
, node
[ni
].sysctl_name
) == 0)
546 if (ni
>= pnode
->sysctl_clen
) {
547 COPY_OUT_DATA(token
, cname
, csz
, namelenp
, nl
);
557 strlcat(pname
, sep
, sizeof(pname
));
558 if (haven
&& n
!= pnode
->sysctl_num
) {
560 strlcat(pname
, token
, sizeof(pname
));
564 strlcat(pname
, pnode
->sysctl_name
, sizeof(pname
));
565 name
[nl
] = pnode
->sysctl_num
;
567 piece
= (dot
!= NULL
) ? dot
+ 1 : NULL
;
569 if (nl
== CTL_MAXNAME
) {
570 COPY_OUT_DATA(token
, cname
, csz
, namelenp
, nl
);
577 if (namelenp
!= NULL
)
583 COPY_OUT_DATA(pname
, cname
, csz
, namelenp
, nl
);
584 if (iname
!= NULL
&& namelenp
!= NULL
)
585 memcpy(iname
, &name
[0], MIN(nl
, *namelenp
) * sizeof(int));
586 if (namelenp
!= NULL
)
591 * they gave us a private tree to work in, so
592 * we give back a pointer into that private
598 * they gave us a place to put the node data,
599 * so give them a copy
601 *rnode
= malloc(sizeof(struct sysctlnode
));
602 if (*rnode
!= NULL
) {
604 (*rnode
)->sysctl_child
= NULL
;
605 (*rnode
)->sysctl_parent
= NULL
;