1*0a6a1f1dSLionel Sambuc /* $NetBSD: sysctlgetmibinfo.c,v 1.11 2014/05/16 12:22:32 martin Exp $ */
22fe8fb19SBen Gras
32fe8fb19SBen Gras /*-
42fe8fb19SBen Gras * Copyright (c) 2003,2004 The NetBSD Foundation, Inc.
52fe8fb19SBen Gras * All rights reserved.
62fe8fb19SBen Gras *
72fe8fb19SBen Gras * This code is derived from software contributed to The NetBSD Foundation
82fe8fb19SBen Gras * by Andrew Brown.
92fe8fb19SBen Gras *
102fe8fb19SBen Gras * Redistribution and use in source and binary forms, with or without
112fe8fb19SBen Gras * modification, are permitted provided that the following conditions
122fe8fb19SBen Gras * are met:
132fe8fb19SBen Gras * 1. Redistributions of source code must retain the above copyright
142fe8fb19SBen Gras * notice, this list of conditions and the following disclaimer.
152fe8fb19SBen Gras * 2. Redistributions in binary form must reproduce the above copyright
162fe8fb19SBen Gras * notice, this list of conditions and the following disclaimer in the
172fe8fb19SBen Gras * documentation and/or other materials provided with the distribution.
182fe8fb19SBen Gras *
192fe8fb19SBen Gras * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
202fe8fb19SBen Gras * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
212fe8fb19SBen Gras * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
222fe8fb19SBen Gras * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
232fe8fb19SBen Gras * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
242fe8fb19SBen Gras * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
252fe8fb19SBen Gras * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
262fe8fb19SBen Gras * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
272fe8fb19SBen Gras * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
282fe8fb19SBen Gras * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
292fe8fb19SBen Gras * POSSIBILITY OF SUCH DAMAGE.
302fe8fb19SBen Gras */
312fe8fb19SBen Gras
322fe8fb19SBen Gras #include <sys/cdefs.h>
332fe8fb19SBen Gras #if defined(LIBC_SCCS) && !defined(lint)
34*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: sysctlgetmibinfo.c,v 1.11 2014/05/16 12:22:32 martin Exp $");
352fe8fb19SBen Gras #endif /* LIBC_SCCS and not lint */
362fe8fb19SBen Gras
372fe8fb19SBen Gras #ifndef RUMP_ACTION
382fe8fb19SBen Gras #include "namespace.h"
392fe8fb19SBen Gras #ifdef _REENTRANT
402fe8fb19SBen Gras #include "reentrant.h"
412fe8fb19SBen Gras #endif /* _REENTRANT */
422fe8fb19SBen Gras #endif /* RUMP_ACTION */
432fe8fb19SBen Gras #include <sys/param.h>
442fe8fb19SBen Gras #include <sys/sysctl.h>
452fe8fb19SBen Gras
46f14fb602SLionel Sambuc #include <assert.h>
472fe8fb19SBen Gras #include <errno.h>
482fe8fb19SBen Gras #include <inttypes.h>
492fe8fb19SBen Gras #include <stdlib.h>
502fe8fb19SBen Gras #include <string.h>
512fe8fb19SBen Gras
522fe8fb19SBen Gras #ifdef RUMP_ACTION
532fe8fb19SBen Gras #include <rump/rump_syscalls.h>
542fe8fb19SBen Gras #define sysctl(a,b,c,d,e,f) rump_sys___sysctl(a,b,c,d,e,f)
552fe8fb19SBen Gras #else
562fe8fb19SBen Gras #ifdef __weak_alias
572fe8fb19SBen Gras __weak_alias(__learn_tree,___learn_tree)
582fe8fb19SBen Gras __weak_alias(sysctlgetmibinfo,_sysctlgetmibinfo)
592fe8fb19SBen Gras #endif
602fe8fb19SBen Gras #endif
612fe8fb19SBen Gras
622fe8fb19SBen Gras /*
632fe8fb19SBen Gras * the place where we attach stuff we learn on the fly, not
642fe8fb19SBen Gras * necessarily used.
652fe8fb19SBen Gras */
662fe8fb19SBen Gras static struct sysctlnode sysctl_mibroot = {
672fe8fb19SBen Gras #if defined(lint)
682fe8fb19SBen Gras /*
692fe8fb19SBen Gras * lint doesn't like my initializers
702fe8fb19SBen Gras */
712fe8fb19SBen Gras 0
722fe8fb19SBen Gras #else /* !lint */
732fe8fb19SBen Gras .sysctl_flags = SYSCTL_VERSION|CTLFLAG_ROOT|CTLTYPE_NODE,
74*0a6a1f1dSLionel Sambuc .sysctl_size = sizeof(struct sysctlnode),
752fe8fb19SBen Gras .sysctl_name = "(root)",
762fe8fb19SBen Gras #endif /* !lint */
772fe8fb19SBen Gras };
782fe8fb19SBen Gras
792fe8fb19SBen Gras /*
802fe8fb19SBen Gras * routines to handle learning and cleanup
812fe8fb19SBen Gras */
822fe8fb19SBen Gras static int compar(const void *, const void *);
832fe8fb19SBen Gras static void free_children(struct sysctlnode *);
842fe8fb19SBen Gras static void relearnhead(void);
852fe8fb19SBen Gras
862fe8fb19SBen Gras /*
872fe8fb19SBen Gras * specifically not static since sysctl(8) "borrows" it.
882fe8fb19SBen Gras */
892fe8fb19SBen Gras int __learn_tree(int *, u_int, struct sysctlnode *);
902fe8fb19SBen Gras
912fe8fb19SBen Gras /*
922fe8fb19SBen Gras * for ordering nodes -- a query may or may not be given them in
932fe8fb19SBen Gras * numeric order
942fe8fb19SBen Gras */
952fe8fb19SBen Gras static int
compar(const void * a,const void * b)962fe8fb19SBen Gras compar(const void *a, const void *b)
972fe8fb19SBen Gras {
982fe8fb19SBen Gras
992fe8fb19SBen Gras return (((const struct sysctlnode *)a)->sysctl_num -
1002fe8fb19SBen Gras ((const struct sysctlnode *)b)->sysctl_num);
1012fe8fb19SBen Gras }
1022fe8fb19SBen Gras
1032fe8fb19SBen Gras /*
1042fe8fb19SBen Gras * recursively nukes a branch or an entire tree from the given node
1052fe8fb19SBen Gras */
1062fe8fb19SBen Gras static void
free_children(struct sysctlnode * rnode)1072fe8fb19SBen Gras free_children(struct sysctlnode *rnode)
1082fe8fb19SBen Gras {
1092fe8fb19SBen Gras struct sysctlnode *node;
1102fe8fb19SBen Gras
1112fe8fb19SBen Gras if (rnode == NULL ||
1122fe8fb19SBen Gras SYSCTL_TYPE(rnode->sysctl_flags) != CTLTYPE_NODE ||
1132fe8fb19SBen Gras rnode->sysctl_child == NULL)
1142fe8fb19SBen Gras return;
1152fe8fb19SBen Gras
1162fe8fb19SBen Gras for (node = rnode->sysctl_child;
1172fe8fb19SBen Gras node < &rnode->sysctl_child[rnode->sysctl_clen];
1182fe8fb19SBen Gras node++) {
1192fe8fb19SBen Gras free_children(node);
1202fe8fb19SBen Gras }
1212fe8fb19SBen Gras free(rnode->sysctl_child);
1222fe8fb19SBen Gras rnode->sysctl_child = NULL;
1232fe8fb19SBen Gras }
1242fe8fb19SBen Gras
1252fe8fb19SBen Gras /*
1262fe8fb19SBen Gras * verifies that the head of the tree in the kernel is the same as the
1272fe8fb19SBen Gras * head of the tree we already got, integrating new stuff and removing
1282fe8fb19SBen Gras * old stuff, if it's not.
1292fe8fb19SBen Gras */
1302fe8fb19SBen Gras static void
relearnhead(void)1312fe8fb19SBen Gras relearnhead(void)
1322fe8fb19SBen Gras {
1332fe8fb19SBen Gras struct sysctlnode *h, *i, *o, qnode;
1342fe8fb19SBen Gras size_t si, so;
135f14fb602SLionel Sambuc int rc, name;
136f14fb602SLionel Sambuc size_t nlen, olen, ni, oi;
1372fe8fb19SBen Gras uint32_t t;
1382fe8fb19SBen Gras
1392fe8fb19SBen Gras /*
1402fe8fb19SBen Gras * if there's nothing there, there's no need to expend any
1412fe8fb19SBen Gras * effort
1422fe8fb19SBen Gras */
1432fe8fb19SBen Gras if (sysctl_mibroot.sysctl_child == NULL)
1442fe8fb19SBen Gras return;
1452fe8fb19SBen Gras
1462fe8fb19SBen Gras /*
1472fe8fb19SBen Gras * attempt to pull out the head of the tree, starting with the
1482fe8fb19SBen Gras * size we have now, and looping if we need more (or less)
1492fe8fb19SBen Gras * space
1502fe8fb19SBen Gras */
1512fe8fb19SBen Gras si = 0;
1522fe8fb19SBen Gras so = sysctl_mibroot.sysctl_clen * sizeof(struct sysctlnode);
1532fe8fb19SBen Gras name = CTL_QUERY;
1542fe8fb19SBen Gras memset(&qnode, 0, sizeof(qnode));
1552fe8fb19SBen Gras qnode.sysctl_flags = SYSCTL_VERSION;
1562fe8fb19SBen Gras do {
1572fe8fb19SBen Gras si = so;
1582fe8fb19SBen Gras h = malloc(si);
1592fe8fb19SBen Gras rc = sysctl(&name, 1, h, &so, &qnode, sizeof(qnode));
1602fe8fb19SBen Gras if (rc == -1 && errno != ENOMEM)
1612fe8fb19SBen Gras return;
1622fe8fb19SBen Gras if (si < so)
1632fe8fb19SBen Gras free(h);
1642fe8fb19SBen Gras } while (si < so);
1652fe8fb19SBen Gras
1662fe8fb19SBen Gras /*
1672fe8fb19SBen Gras * order the new copy of the head
1682fe8fb19SBen Gras */
1692fe8fb19SBen Gras nlen = so / sizeof(struct sysctlnode);
170f14fb602SLionel Sambuc qsort(h, nlen, sizeof(struct sysctlnode), compar);
1712fe8fb19SBen Gras
1722fe8fb19SBen Gras /*
1732fe8fb19SBen Gras * verify that everything is the same. if it is, we don't
1742fe8fb19SBen Gras * need to do any more work here.
1752fe8fb19SBen Gras */
1762fe8fb19SBen Gras olen = sysctl_mibroot.sysctl_clen;
1772fe8fb19SBen Gras rc = (nlen == olen) ? 0 : 1;
1782fe8fb19SBen Gras o = sysctl_mibroot.sysctl_child;
1792fe8fb19SBen Gras for (ni = 0; rc == 0 && ni < nlen; ni++) {
1802fe8fb19SBen Gras if (h[ni].sysctl_num != o[ni].sysctl_num ||
1812fe8fb19SBen Gras h[ni].sysctl_ver != o[ni].sysctl_ver)
1822fe8fb19SBen Gras rc = 1;
1832fe8fb19SBen Gras }
1842fe8fb19SBen Gras if (rc == 0) {
1852fe8fb19SBen Gras free(h);
1862fe8fb19SBen Gras return;
1872fe8fb19SBen Gras }
1882fe8fb19SBen Gras
1892fe8fb19SBen Gras /*
1902fe8fb19SBen Gras * something changed. h will become the new head, and we need
1912fe8fb19SBen Gras * pull over any subtrees we already have if they're the same
1922fe8fb19SBen Gras * version.
1932fe8fb19SBen Gras */
1942fe8fb19SBen Gras i = h;
1952fe8fb19SBen Gras ni = oi = 0;
1962fe8fb19SBen Gras while (ni < nlen && oi < olen) {
1972fe8fb19SBen Gras /*
1982fe8fb19SBen Gras * something was inserted or deleted
1992fe8fb19SBen Gras */
2002fe8fb19SBen Gras if (SYSCTL_TYPE(i[ni].sysctl_flags) == CTLTYPE_NODE)
2012fe8fb19SBen Gras i[ni].sysctl_child = NULL;
2022fe8fb19SBen Gras if (i[ni].sysctl_num != o[oi].sysctl_num) {
2032fe8fb19SBen Gras if (i[ni].sysctl_num < o[oi].sysctl_num) {
2042fe8fb19SBen Gras ni++;
2052fe8fb19SBen Gras }
2062fe8fb19SBen Gras else {
2072fe8fb19SBen Gras free_children(&o[oi]);
2082fe8fb19SBen Gras oi++;
2092fe8fb19SBen Gras }
2102fe8fb19SBen Gras continue;
2112fe8fb19SBen Gras }
2122fe8fb19SBen Gras
2132fe8fb19SBen Gras /*
2142fe8fb19SBen Gras * same number, but different version, so throw away
2152fe8fb19SBen Gras * any accumulated children
2162fe8fb19SBen Gras */
2172fe8fb19SBen Gras if (i[ni].sysctl_ver != o[oi].sysctl_ver)
2182fe8fb19SBen Gras free_children(&o[oi]);
2192fe8fb19SBen Gras
2202fe8fb19SBen Gras /*
2212fe8fb19SBen Gras * this node is the same, but we only need to
2222fe8fb19SBen Gras * move subtrees.
2232fe8fb19SBen Gras */
2242fe8fb19SBen Gras else if (SYSCTL_TYPE(i[ni].sysctl_flags) == CTLTYPE_NODE) {
2252fe8fb19SBen Gras /*
2262fe8fb19SBen Gras * move subtree to new parent
2272fe8fb19SBen Gras */
2282fe8fb19SBen Gras i[ni].sysctl_clen = o[oi].sysctl_clen;
2292fe8fb19SBen Gras i[ni].sysctl_csize = o[oi].sysctl_csize;
2302fe8fb19SBen Gras i[ni].sysctl_child = o[oi].sysctl_child;
2312fe8fb19SBen Gras /*
2322fe8fb19SBen Gras * reparent inherited subtree
2332fe8fb19SBen Gras */
2342fe8fb19SBen Gras for (t = 0;
2352fe8fb19SBen Gras i[ni].sysctl_child != NULL &&
2362fe8fb19SBen Gras t < i[ni].sysctl_clen;
2372fe8fb19SBen Gras t++)
2382fe8fb19SBen Gras i[ni].sysctl_child[t].sysctl_parent = &i[ni];
2392fe8fb19SBen Gras }
2402fe8fb19SBen Gras ni++;
2412fe8fb19SBen Gras oi++;
2422fe8fb19SBen Gras }
2432fe8fb19SBen Gras
2442fe8fb19SBen Gras /*
2452fe8fb19SBen Gras * left over new nodes need to have empty subtrees cleared
2462fe8fb19SBen Gras */
2472fe8fb19SBen Gras while (ni < nlen) {
2482fe8fb19SBen Gras if (SYSCTL_TYPE(i[ni].sysctl_flags) == CTLTYPE_NODE)
2492fe8fb19SBen Gras i[ni].sysctl_child = NULL;
2502fe8fb19SBen Gras ni++;
2512fe8fb19SBen Gras }
2522fe8fb19SBen Gras
2532fe8fb19SBen Gras /*
2542fe8fb19SBen Gras * left over old nodes need to be cleaned out
2552fe8fb19SBen Gras */
2562fe8fb19SBen Gras while (oi < olen) {
2572fe8fb19SBen Gras free_children(&o[oi]);
2582fe8fb19SBen Gras oi++;
2592fe8fb19SBen Gras }
2602fe8fb19SBen Gras
2612fe8fb19SBen Gras /*
2622fe8fb19SBen Gras * pop new head in
2632fe8fb19SBen Gras */
264f14fb602SLionel Sambuc _DIAGASSERT(__type_fit(uint32_t, nlen));
265f14fb602SLionel Sambuc sysctl_mibroot.sysctl_csize =
266f14fb602SLionel Sambuc sysctl_mibroot.sysctl_clen = (uint32_t)nlen;
2672fe8fb19SBen Gras sysctl_mibroot.sysctl_child = h;
2682fe8fb19SBen Gras free(o);
2692fe8fb19SBen Gras }
2702fe8fb19SBen Gras
2712fe8fb19SBen Gras /*
2722fe8fb19SBen Gras * sucks in the children at a given level and attaches it to the tree.
2732fe8fb19SBen Gras */
2742fe8fb19SBen Gras int
__learn_tree(int * name,u_int namelen,struct sysctlnode * pnode)2752fe8fb19SBen Gras __learn_tree(int *name, u_int namelen, struct sysctlnode *pnode)
2762fe8fb19SBen Gras {
2772fe8fb19SBen Gras struct sysctlnode qnode;
2782fe8fb19SBen Gras uint32_t rc;
2792fe8fb19SBen Gras size_t sz;
2802fe8fb19SBen Gras
2812fe8fb19SBen Gras if (pnode == NULL)
2822fe8fb19SBen Gras pnode = &sysctl_mibroot;
2832fe8fb19SBen Gras if (SYSCTL_TYPE(pnode->sysctl_flags) != CTLTYPE_NODE) {
2842fe8fb19SBen Gras errno = EINVAL;
2852fe8fb19SBen Gras return (-1);
2862fe8fb19SBen Gras }
2872fe8fb19SBen Gras if (pnode->sysctl_child != NULL)
2882fe8fb19SBen Gras return (0);
2892fe8fb19SBen Gras
2902fe8fb19SBen Gras if (pnode->sysctl_clen == 0)
2912fe8fb19SBen Gras sz = SYSCTL_DEFSIZE * sizeof(struct sysctlnode);
2922fe8fb19SBen Gras else
2932fe8fb19SBen Gras sz = pnode->sysctl_clen * sizeof(struct sysctlnode);
2942fe8fb19SBen Gras pnode->sysctl_child = malloc(sz);
2952fe8fb19SBen Gras if (pnode->sysctl_child == NULL)
2962fe8fb19SBen Gras return (-1);
2972fe8fb19SBen Gras
2982fe8fb19SBen Gras name[namelen] = CTL_QUERY;
2992fe8fb19SBen Gras pnode->sysctl_clen = 0;
3002fe8fb19SBen Gras pnode->sysctl_csize = 0;
3012fe8fb19SBen Gras memset(&qnode, 0, sizeof(qnode));
3022fe8fb19SBen Gras qnode.sysctl_flags = SYSCTL_VERSION;
3032fe8fb19SBen Gras rc = sysctl(name, namelen + 1, pnode->sysctl_child, &sz,
3042fe8fb19SBen Gras &qnode, sizeof(qnode));
3052fe8fb19SBen Gras if (sz == 0) {
3062fe8fb19SBen Gras free(pnode->sysctl_child);
3072fe8fb19SBen Gras pnode->sysctl_child = NULL;
3082fe8fb19SBen Gras return (rc);
3092fe8fb19SBen Gras }
3102fe8fb19SBen Gras if (rc) {
3112fe8fb19SBen Gras free(pnode->sysctl_child);
3122fe8fb19SBen Gras pnode->sysctl_child = NULL;
3132fe8fb19SBen Gras if ((sz % sizeof(struct sysctlnode)) != 0)
3142fe8fb19SBen Gras errno = EINVAL;
3152fe8fb19SBen Gras if (errno != ENOMEM)
3162fe8fb19SBen Gras return (rc);
3172fe8fb19SBen Gras }
3182fe8fb19SBen Gras
3192fe8fb19SBen Gras if (pnode->sysctl_child == NULL) {
3202fe8fb19SBen Gras pnode->sysctl_child = malloc(sz);
3212fe8fb19SBen Gras if (pnode->sysctl_child == NULL)
3222fe8fb19SBen Gras return (-1);
3232fe8fb19SBen Gras
3242fe8fb19SBen Gras rc = sysctl(name, namelen + 1, pnode->sysctl_child, &sz,
3252fe8fb19SBen Gras &qnode, sizeof(qnode));
3262fe8fb19SBen Gras if (rc) {
3272fe8fb19SBen Gras free(pnode->sysctl_child);
3282fe8fb19SBen Gras pnode->sysctl_child = NULL;
3292fe8fb19SBen Gras return (rc);
3302fe8fb19SBen Gras }
3312fe8fb19SBen Gras }
3322fe8fb19SBen Gras
3332fe8fb19SBen Gras /*
3342fe8fb19SBen Gras * how many did we get?
3352fe8fb19SBen Gras */
336f14fb602SLionel Sambuc sz /= sizeof(struct sysctlnode);
337f14fb602SLionel Sambuc pnode->sysctl_csize = pnode->sysctl_clen = (uint32_t)sz;
338f14fb602SLionel Sambuc if (pnode->sysctl_clen != sz) {
3392fe8fb19SBen Gras free(pnode->sysctl_child);
3402fe8fb19SBen Gras pnode->sysctl_child = NULL;
3412fe8fb19SBen Gras errno = EINVAL;
3422fe8fb19SBen Gras return (-1);
3432fe8fb19SBen Gras }
3442fe8fb19SBen Gras
3452fe8fb19SBen Gras /*
3462fe8fb19SBen Gras * you know, the kernel doesn't really keep them in any
3472fe8fb19SBen Gras * particular order...just like entries in a directory
3482fe8fb19SBen Gras */
3492fe8fb19SBen Gras qsort(pnode->sysctl_child, pnode->sysctl_clen,
3502fe8fb19SBen Gras sizeof(struct sysctlnode), compar);
3512fe8fb19SBen Gras
3522fe8fb19SBen Gras /*
3532fe8fb19SBen Gras * rearrange parent<->child linkage
3542fe8fb19SBen Gras */
3552fe8fb19SBen Gras for (rc = 0; rc < pnode->sysctl_clen; rc++) {
3562fe8fb19SBen Gras pnode->sysctl_child[rc].sysctl_parent = pnode;
3572fe8fb19SBen Gras if (SYSCTL_TYPE(pnode->sysctl_child[rc].sysctl_flags) ==
3582fe8fb19SBen Gras CTLTYPE_NODE) {
3592fe8fb19SBen Gras /*
3602fe8fb19SBen Gras * these nodes may have children, but we
3612fe8fb19SBen Gras * haven't discovered that yet.
3622fe8fb19SBen Gras */
3632fe8fb19SBen Gras pnode->sysctl_child[rc].sysctl_child = NULL;
3642fe8fb19SBen Gras }
3652fe8fb19SBen Gras pnode->sysctl_child[rc].sysctl_desc = NULL;
3662fe8fb19SBen Gras }
3672fe8fb19SBen Gras
3682fe8fb19SBen Gras return (0);
3692fe8fb19SBen Gras }
3702fe8fb19SBen Gras
3712fe8fb19SBen Gras /*
3722fe8fb19SBen Gras * that's "given name" as a string, the integer form of the name fit
3732fe8fb19SBen Gras * to be passed to sysctl(), "canonicalized name" (optional), and a
3742fe8fb19SBen Gras * pointer to the length of the integer form. oh, and then a pointer
3752fe8fb19SBen Gras * to the node, in case you (the caller) care. you can leave them all
3762fe8fb19SBen Gras * NULL except for gname, though that might be rather pointless,
3772fe8fb19SBen Gras * unless all you wanna do is verify that a given name is acceptable.
3782fe8fb19SBen Gras *
3792fe8fb19SBen Gras * returns either 0 (everything was fine) or -1 and sets errno
3802fe8fb19SBen Gras * accordingly. if errno is set to EAGAIN, we detected a change to
3812fe8fb19SBen Gras * the mib while parsing, and you should try again. in the case of an
3822fe8fb19SBen Gras * invalid node name, cname will be set to contain the offending name.
3832fe8fb19SBen Gras */
3842fe8fb19SBen Gras #ifdef _REENTRANT
3852fe8fb19SBen Gras static mutex_t sysctl_mutex = MUTEX_INITIALIZER;
3862fe8fb19SBen Gras static int sysctlgetmibinfo_unlocked(const char *, int *, u_int *, char *,
3872fe8fb19SBen Gras size_t *, struct sysctlnode **, int);
3882fe8fb19SBen Gras #endif /* __REENTRANT */
3892fe8fb19SBen Gras
3902fe8fb19SBen Gras int
sysctlgetmibinfo(const char * gname,int * iname,u_int * namelenp,char * cname,size_t * csz,struct sysctlnode ** rnode,int v)3912fe8fb19SBen Gras sysctlgetmibinfo(const char *gname, int *iname, u_int *namelenp,
3922fe8fb19SBen Gras char *cname, size_t *csz, struct sysctlnode **rnode, int v)
3932fe8fb19SBen Gras #ifdef _REENTRANT
3942fe8fb19SBen Gras {
3952fe8fb19SBen Gras int rc;
3962fe8fb19SBen Gras
3972fe8fb19SBen Gras mutex_lock(&sysctl_mutex);
3982fe8fb19SBen Gras rc = sysctlgetmibinfo_unlocked(gname, iname, namelenp, cname, csz,
3992fe8fb19SBen Gras rnode, v);
4002fe8fb19SBen Gras mutex_unlock(&sysctl_mutex);
4012fe8fb19SBen Gras
4022fe8fb19SBen Gras return (rc);
4032fe8fb19SBen Gras }
4042fe8fb19SBen Gras
4052fe8fb19SBen Gras static int
sysctlgetmibinfo_unlocked(const char * gname,int * iname,u_int * namelenp,char * cname,size_t * csz,struct sysctlnode ** rnode,int v)4062fe8fb19SBen Gras sysctlgetmibinfo_unlocked(const char *gname, int *iname, u_int *namelenp,
4072fe8fb19SBen Gras char *cname, size_t *csz, struct sysctlnode **rnode,
4082fe8fb19SBen Gras int v)
4092fe8fb19SBen Gras #endif /* _REENTRANT */
4102fe8fb19SBen Gras {
4112fe8fb19SBen Gras struct sysctlnode *pnode, *node;
4122fe8fb19SBen Gras int name[CTL_MAXNAME], n, haven;
4132fe8fb19SBen Gras u_int ni, nl;
4142fe8fb19SBen Gras intmax_t q;
4152fe8fb19SBen Gras char sep[2], token[SYSCTL_NAMELEN],
4162fe8fb19SBen Gras pname[SYSCTL_NAMELEN * CTL_MAXNAME + CTL_MAXNAME];
4172fe8fb19SBen Gras const char *piece, *dot;
4182fe8fb19SBen Gras char *t;
4192fe8fb19SBen Gras size_t l;
4202fe8fb19SBen Gras
4212fe8fb19SBen Gras if (rnode != NULL) {
4222fe8fb19SBen Gras if (*rnode == NULL) {
4232fe8fb19SBen Gras /* XXX later deal with dealing back a sub version */
4242fe8fb19SBen Gras if (v != SYSCTL_VERSION)
4252fe8fb19SBen Gras return (EINVAL);
4262fe8fb19SBen Gras
4272fe8fb19SBen Gras pnode = &sysctl_mibroot;
4282fe8fb19SBen Gras }
4292fe8fb19SBen Gras else {
4302fe8fb19SBen Gras /* this is just someone being silly */
4312fe8fb19SBen Gras if (SYSCTL_VERS((*rnode)->sysctl_flags) != (uint32_t)v)
4322fe8fb19SBen Gras return (EINVAL);
4332fe8fb19SBen Gras
4342fe8fb19SBen Gras /* XXX later deal with other people's trees */
4352fe8fb19SBen Gras if (SYSCTL_VERS((*rnode)->sysctl_flags) !=
4362fe8fb19SBen Gras SYSCTL_VERSION)
4372fe8fb19SBen Gras return (EINVAL);
4382fe8fb19SBen Gras
4392fe8fb19SBen Gras pnode = *rnode;
4402fe8fb19SBen Gras }
4412fe8fb19SBen Gras }
4422fe8fb19SBen Gras else
4432fe8fb19SBen Gras pnode = &sysctl_mibroot;
4442fe8fb19SBen Gras
4452fe8fb19SBen Gras if (pnode == &sysctl_mibroot)
4462fe8fb19SBen Gras relearnhead();
4472fe8fb19SBen Gras
4482fe8fb19SBen Gras nl = ni = 0;
4492fe8fb19SBen Gras token[0] = '\0';
4502fe8fb19SBen Gras pname[0] = '\0';
4512fe8fb19SBen Gras node = NULL;
4522fe8fb19SBen Gras
4532fe8fb19SBen Gras /*
4542fe8fb19SBen Gras * default to using '.' as the separator, but allow '/' as
4552fe8fb19SBen Gras * well, and then allow a leading separator
4562fe8fb19SBen Gras */
4572fe8fb19SBen Gras if ((dot = strpbrk(gname, "./")) == NULL)
4582fe8fb19SBen Gras sep[0] = '.';
4592fe8fb19SBen Gras else
4602fe8fb19SBen Gras sep[0] = dot[0];
4612fe8fb19SBen Gras sep[1] = '\0';
4622fe8fb19SBen Gras if (gname[0] == sep[0]) {
4632fe8fb19SBen Gras strlcat(pname, sep, sizeof(pname));
4642fe8fb19SBen Gras gname++;
4652fe8fb19SBen Gras }
4662fe8fb19SBen Gras
4672fe8fb19SBen Gras #define COPY_OUT_DATA(t, c, cs, nlp, l) do { \
4682fe8fb19SBen Gras if ((c) != NULL && (cs) != NULL) \
4692fe8fb19SBen Gras *(cs) = strlcpy((c), (t), *(cs)); \
4702fe8fb19SBen Gras else if ((cs) != NULL) \
4712fe8fb19SBen Gras *(cs) = strlen(t) + 1; \
4722fe8fb19SBen Gras if ((nlp) != NULL) \
4732fe8fb19SBen Gras *(nlp) = (l); \
4742fe8fb19SBen Gras } while (/*CONSTCOND*/0)
4752fe8fb19SBen Gras
4762fe8fb19SBen Gras piece = gname;
4772fe8fb19SBen Gras while (piece != NULL && *piece != '\0') {
4782fe8fb19SBen Gras /*
4792fe8fb19SBen Gras * what was i looking for?
4802fe8fb19SBen Gras */
4812fe8fb19SBen Gras dot = strchr(piece, sep[0]);
4822fe8fb19SBen Gras if (dot == NULL) {
4832fe8fb19SBen Gras l = strlcpy(token, piece, sizeof(token));
4842fe8fb19SBen Gras if (l > sizeof(token)) {
4852fe8fb19SBen Gras COPY_OUT_DATA(piece, cname, csz, namelenp, nl);
4862fe8fb19SBen Gras errno = ENAMETOOLONG;
4872fe8fb19SBen Gras return (-1);
4882fe8fb19SBen Gras }
4892fe8fb19SBen Gras }
4902fe8fb19SBen Gras else if (dot - piece > (intptr_t)(sizeof(token) - 1)) {
4912fe8fb19SBen Gras COPY_OUT_DATA(token, cname, csz, namelenp, nl);
4922fe8fb19SBen Gras errno = ENAMETOOLONG;
4932fe8fb19SBen Gras return (-1);
4942fe8fb19SBen Gras }
4952fe8fb19SBen Gras else {
4962fe8fb19SBen Gras strncpy(token, piece, (size_t)(dot - piece));
4972fe8fb19SBen Gras token[dot - piece] = '\0';
4982fe8fb19SBen Gras }
4992fe8fb19SBen Gras
5002fe8fb19SBen Gras /*
5012fe8fb19SBen Gras * i wonder if this "token" is an integer?
5022fe8fb19SBen Gras */
5032fe8fb19SBen Gras errno = 0;
5042fe8fb19SBen Gras q = strtoimax(token, &t, 0);
5052fe8fb19SBen Gras n = (int)q;
5062fe8fb19SBen Gras if (errno != 0 || *t != '\0')
5072fe8fb19SBen Gras haven = 0;
5082fe8fb19SBen Gras else if (q < INT_MIN || q > UINT_MAX)
5092fe8fb19SBen Gras haven = 0;
5102fe8fb19SBen Gras else
5112fe8fb19SBen Gras haven = 1;
5122fe8fb19SBen Gras
5132fe8fb19SBen Gras /*
5142fe8fb19SBen Gras * make sure i have something to look at
5152fe8fb19SBen Gras */
5162fe8fb19SBen Gras if (SYSCTL_TYPE(pnode->sysctl_flags) != CTLTYPE_NODE) {
5172fe8fb19SBen Gras if (haven && nl > 0) {
5182fe8fb19SBen Gras strlcat(pname, sep, sizeof(pname));
5192fe8fb19SBen Gras goto just_numbers;
5202fe8fb19SBen Gras }
5212fe8fb19SBen Gras COPY_OUT_DATA(token, cname, csz, namelenp, nl);
5222fe8fb19SBen Gras errno = ENOTDIR;
5232fe8fb19SBen Gras return (-1);
5242fe8fb19SBen Gras }
5252fe8fb19SBen Gras if (pnode->sysctl_child == NULL) {
5262fe8fb19SBen Gras if (__learn_tree(name, nl, pnode) == -1) {
5272fe8fb19SBen Gras COPY_OUT_DATA(token, cname, csz, namelenp, nl);
5282fe8fb19SBen Gras return (-1);
5292fe8fb19SBen Gras }
5302fe8fb19SBen Gras }
5312fe8fb19SBen Gras node = pnode->sysctl_child;
5322fe8fb19SBen Gras if (node == NULL) {
5332fe8fb19SBen Gras COPY_OUT_DATA(token, cname, csz, namelenp, nl);
5342fe8fb19SBen Gras errno = ENOENT;
5352fe8fb19SBen Gras return (-1);
5362fe8fb19SBen Gras }
5372fe8fb19SBen Gras
5382fe8fb19SBen Gras /*
5392fe8fb19SBen Gras * now...is it there?
5402fe8fb19SBen Gras */
5412fe8fb19SBen Gras for (ni = 0; ni < pnode->sysctl_clen; ni++)
5422fe8fb19SBen Gras if ((haven && ((n == node[ni].sysctl_num) ||
5432fe8fb19SBen Gras (node[ni].sysctl_flags & CTLFLAG_ANYNUMBER))) ||
5442fe8fb19SBen Gras strcmp(token, node[ni].sysctl_name) == 0)
5452fe8fb19SBen Gras break;
5462fe8fb19SBen Gras if (ni >= pnode->sysctl_clen) {
5472fe8fb19SBen Gras COPY_OUT_DATA(token, cname, csz, namelenp, nl);
5482fe8fb19SBen Gras errno = ENOENT;
5492fe8fb19SBen Gras return (-1);
5502fe8fb19SBen Gras }
5512fe8fb19SBen Gras
5522fe8fb19SBen Gras /*
5532fe8fb19SBen Gras * ah...it is.
5542fe8fb19SBen Gras */
5552fe8fb19SBen Gras pnode = &node[ni];
5562fe8fb19SBen Gras if (nl > 0)
5572fe8fb19SBen Gras strlcat(pname, sep, sizeof(pname));
5582fe8fb19SBen Gras if (haven && n != pnode->sysctl_num) {
5592fe8fb19SBen Gras just_numbers:
5602fe8fb19SBen Gras strlcat(pname, token, sizeof(pname));
5612fe8fb19SBen Gras name[nl] = n;
5622fe8fb19SBen Gras }
5632fe8fb19SBen Gras else {
5642fe8fb19SBen Gras strlcat(pname, pnode->sysctl_name, sizeof(pname));
5652fe8fb19SBen Gras name[nl] = pnode->sysctl_num;
5662fe8fb19SBen Gras }
5672fe8fb19SBen Gras piece = (dot != NULL) ? dot + 1 : NULL;
5682fe8fb19SBen Gras nl++;
5692fe8fb19SBen Gras if (nl == CTL_MAXNAME) {
5702fe8fb19SBen Gras COPY_OUT_DATA(token, cname, csz, namelenp, nl);
5712fe8fb19SBen Gras errno = ERANGE;
5722fe8fb19SBen Gras return (-1);
5732fe8fb19SBen Gras }
5742fe8fb19SBen Gras }
5752fe8fb19SBen Gras
5762fe8fb19SBen Gras if (nl == 0) {
5772fe8fb19SBen Gras if (namelenp != NULL)
5782fe8fb19SBen Gras *namelenp = 0;
5792fe8fb19SBen Gras errno = EINVAL;
5802fe8fb19SBen Gras return (-1);
5812fe8fb19SBen Gras }
5822fe8fb19SBen Gras
5832fe8fb19SBen Gras COPY_OUT_DATA(pname, cname, csz, namelenp, nl);
5842fe8fb19SBen Gras if (iname != NULL && namelenp != NULL)
5852fe8fb19SBen Gras memcpy(iname, &name[0], MIN(nl, *namelenp) * sizeof(int));
5862fe8fb19SBen Gras if (namelenp != NULL)
5872fe8fb19SBen Gras *namelenp = nl;
5882fe8fb19SBen Gras if (rnode != NULL) {
5892fe8fb19SBen Gras if (*rnode != NULL)
5902fe8fb19SBen Gras /*
5912fe8fb19SBen Gras * they gave us a private tree to work in, so
5922fe8fb19SBen Gras * we give back a pointer into that private
5932fe8fb19SBen Gras * tree
5942fe8fb19SBen Gras */
5952fe8fb19SBen Gras *rnode = pnode;
5962fe8fb19SBen Gras else {
5972fe8fb19SBen Gras /*
5982fe8fb19SBen Gras * they gave us a place to put the node data,
5992fe8fb19SBen Gras * so give them a copy
6002fe8fb19SBen Gras */
6012fe8fb19SBen Gras *rnode = malloc(sizeof(struct sysctlnode));
6022fe8fb19SBen Gras if (*rnode != NULL) {
6032fe8fb19SBen Gras **rnode = *pnode;
6042fe8fb19SBen Gras (*rnode)->sysctl_child = NULL;
6052fe8fb19SBen Gras (*rnode)->sysctl_parent = NULL;
6062fe8fb19SBen Gras }
6072fe8fb19SBen Gras }
6082fe8fb19SBen Gras }
6092fe8fb19SBen Gras
6102fe8fb19SBen Gras return (0);
6112fe8fb19SBen Gras }
612