xref: /minix3/lib/libc/gen/sysctlgetmibinfo.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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