sysctlgetmibinfo.c revision 1.6.8.2 1 1.6.8.2 martin /* $NetBSD: sysctlgetmibinfo.c,v 1.6.8.2 2008/04/29 06:53:02 martin Exp $ */
2 1.6.8.2 martin
3 1.6.8.2 martin /*-
4 1.6.8.2 martin * Copyright (c) 2003,2004 The NetBSD Foundation, Inc.
5 1.6.8.2 martin * All rights reserved.
6 1.6.8.2 martin *
7 1.6.8.2 martin * This code is derived from software contributed to The NetBSD Foundation
8 1.6.8.2 martin * by Andrew Brown.
9 1.6.8.2 martin *
10 1.6.8.2 martin * Redistribution and use in source and binary forms, with or without
11 1.6.8.2 martin * modification, are permitted provided that the following conditions
12 1.6.8.2 martin * are met:
13 1.6.8.2 martin * 1. Redistributions of source code must retain the above copyright
14 1.6.8.2 martin * notice, this list of conditions and the following disclaimer.
15 1.6.8.2 martin * 2. Redistributions in binary form must reproduce the above copyright
16 1.6.8.2 martin * notice, this list of conditions and the following disclaimer in the
17 1.6.8.2 martin * documentation and/or other materials provided with the distribution.
18 1.6.8.2 martin *
19 1.6.8.2 martin * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.6.8.2 martin * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.6.8.2 martin * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.6.8.2 martin * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.6.8.2 martin * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.6.8.2 martin * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.6.8.2 martin * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.6.8.2 martin * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.6.8.2 martin * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.6.8.2 martin * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.6.8.2 martin * POSSIBILITY OF SUCH DAMAGE.
30 1.6.8.2 martin */
31 1.6.8.2 martin
32 1.6.8.2 martin #include <sys/cdefs.h>
33 1.6.8.2 martin #if defined(LIBC_SCCS) && !defined(lint)
34 1.6.8.2 martin __RCSID("$NetBSD: sysctlgetmibinfo.c,v 1.6.8.2 2008/04/29 06:53:02 martin Exp $");
35 1.6.8.2 martin #endif /* LIBC_SCCS and not lint */
36 1.6.8.2 martin
37 1.6.8.2 martin #include "namespace.h"
38 1.6.8.2 martin #ifdef _REENTRANT
39 1.6.8.2 martin #include "reentrant.h"
40 1.6.8.2 martin #endif /* _REENTRANT */
41 1.6.8.2 martin #include <sys/param.h>
42 1.6.8.2 martin #include <sys/sysctl.h>
43 1.6.8.2 martin
44 1.6.8.2 martin #include <errno.h>
45 1.6.8.2 martin #include <inttypes.h>
46 1.6.8.2 martin #include <stdlib.h>
47 1.6.8.2 martin #include <string.h>
48 1.6.8.2 martin
49 1.6.8.2 martin #ifdef __weak_alias
50 1.6.8.2 martin __weak_alias(__learn_tree,___learn_tree)
51 1.6.8.2 martin __weak_alias(sysctlgetmibinfo,_sysctlgetmibinfo)
52 1.6.8.2 martin #endif
53 1.6.8.2 martin
54 1.6.8.2 martin /*
55 1.6.8.2 martin * the place where we attach stuff we learn on the fly, not
56 1.6.8.2 martin * necessarily used.
57 1.6.8.2 martin */
58 1.6.8.2 martin static struct sysctlnode sysctl_mibroot = {
59 1.6.8.2 martin #if defined(lint)
60 1.6.8.2 martin /*
61 1.6.8.2 martin * lint doesn't like my initializers
62 1.6.8.2 martin */
63 1.6.8.2 martin 0
64 1.6.8.2 martin #else /* !lint */
65 1.6.8.2 martin .sysctl_flags = SYSCTL_VERSION|CTLFLAG_ROOT|CTLTYPE_NODE,
66 1.6.8.2 martin sysc_init_field(_sysctl_size, sizeof(struct sysctlnode)),
67 1.6.8.2 martin .sysctl_name = "(root)",
68 1.6.8.2 martin #endif /* !lint */
69 1.6.8.2 martin };
70 1.6.8.2 martin
71 1.6.8.2 martin /*
72 1.6.8.2 martin * routines to handle learning and cleanup
73 1.6.8.2 martin */
74 1.6.8.2 martin static int compar(const void *, const void *);
75 1.6.8.2 martin static void free_children(struct sysctlnode *);
76 1.6.8.2 martin static void relearnhead(void);
77 1.6.8.2 martin
78 1.6.8.2 martin /*
79 1.6.8.2 martin * specifically not static since sysctl(8) "borrows" it.
80 1.6.8.2 martin */
81 1.6.8.2 martin int __learn_tree(int *, u_int, struct sysctlnode *);
82 1.6.8.2 martin
83 1.6.8.2 martin /*
84 1.6.8.2 martin * for ordering nodes -- a query may or may not be given them in
85 1.6.8.2 martin * numeric order
86 1.6.8.2 martin */
87 1.6.8.2 martin static int
88 1.6.8.2 martin compar(const void *a, const void *b)
89 1.6.8.2 martin {
90 1.6.8.2 martin
91 1.6.8.2 martin return (((const struct sysctlnode *)a)->sysctl_num -
92 1.6.8.2 martin ((const struct sysctlnode *)b)->sysctl_num);
93 1.6.8.2 martin }
94 1.6.8.2 martin
95 1.6.8.2 martin /*
96 1.6.8.2 martin * recursively nukes a branch or an entire tree from the given node
97 1.6.8.2 martin */
98 1.6.8.2 martin static void
99 1.6.8.2 martin free_children(struct sysctlnode *rnode)
100 1.6.8.2 martin {
101 1.6.8.2 martin struct sysctlnode *node;
102 1.6.8.2 martin
103 1.6.8.2 martin if (rnode == NULL ||
104 1.6.8.2 martin SYSCTL_TYPE(rnode->sysctl_flags) != CTLTYPE_NODE ||
105 1.6.8.2 martin rnode->sysctl_child == NULL)
106 1.6.8.2 martin return;
107 1.6.8.2 martin
108 1.6.8.2 martin for (node = rnode->sysctl_child;
109 1.6.8.2 martin node < &rnode->sysctl_child[rnode->sysctl_clen];
110 1.6.8.2 martin node++) {
111 1.6.8.2 martin free_children(node);
112 1.6.8.2 martin }
113 1.6.8.2 martin free(rnode->sysctl_child);
114 1.6.8.2 martin rnode->sysctl_child = NULL;
115 1.6.8.2 martin }
116 1.6.8.2 martin
117 1.6.8.2 martin /*
118 1.6.8.2 martin * verifies that the head of the tree in the kernel is the same as the
119 1.6.8.2 martin * head of the tree we already got, integrating new stuff and removing
120 1.6.8.2 martin * old stuff, if it's not.
121 1.6.8.2 martin */
122 1.6.8.2 martin static void
123 1.6.8.2 martin relearnhead(void)
124 1.6.8.2 martin {
125 1.6.8.2 martin struct sysctlnode *h, *i, *o, qnode;
126 1.6.8.2 martin size_t si, so;
127 1.6.8.2 martin int rc, name, nlen, olen, ni, oi, t;
128 1.6.8.2 martin
129 1.6.8.2 martin /*
130 1.6.8.2 martin * if there's nothing there, there's no need to expend any
131 1.6.8.2 martin * effort
132 1.6.8.2 martin */
133 1.6.8.2 martin if (sysctl_mibroot.sysctl_child == NULL)
134 1.6.8.2 martin return;
135 1.6.8.2 martin
136 1.6.8.2 martin /*
137 1.6.8.2 martin * attempt to pull out the head of the tree, starting with the
138 1.6.8.2 martin * size we have now, and looping if we need more (or less)
139 1.6.8.2 martin * space
140 1.6.8.2 martin */
141 1.6.8.2 martin si = 0;
142 1.6.8.2 martin so = sysctl_mibroot.sysctl_clen * sizeof(struct sysctlnode);
143 1.6.8.2 martin name = CTL_QUERY;
144 1.6.8.2 martin memset(&qnode, 0, sizeof(qnode));
145 1.6.8.2 martin qnode.sysctl_flags = SYSCTL_VERSION;
146 1.6.8.2 martin do {
147 1.6.8.2 martin si = so;
148 1.6.8.2 martin h = malloc(si);
149 1.6.8.2 martin rc = sysctl(&name, 1, h, &so, &qnode, sizeof(qnode));
150 1.6.8.2 martin if (rc == -1 && errno != ENOMEM)
151 1.6.8.2 martin return;
152 1.6.8.2 martin if (si < so)
153 1.6.8.2 martin free(h);
154 1.6.8.2 martin } while (si < so);
155 1.6.8.2 martin
156 1.6.8.2 martin /*
157 1.6.8.2 martin * order the new copy of the head
158 1.6.8.2 martin */
159 1.6.8.2 martin nlen = so / sizeof(struct sysctlnode);
160 1.6.8.2 martin qsort(h, (size_t)nlen, sizeof(struct sysctlnode), compar);
161 1.6.8.2 martin
162 1.6.8.2 martin /*
163 1.6.8.2 martin * verify that everything is the same. if it is, we don't
164 1.6.8.2 martin * need to do any more work here.
165 1.6.8.2 martin */
166 1.6.8.2 martin olen = sysctl_mibroot.sysctl_clen;
167 1.6.8.2 martin rc = (nlen == olen) ? 0 : 1;
168 1.6.8.2 martin o = sysctl_mibroot.sysctl_child;
169 1.6.8.2 martin for (ni = 0; rc == 0 && ni < nlen; ni++) {
170 1.6.8.2 martin if (h[ni].sysctl_num != o[ni].sysctl_num ||
171 1.6.8.2 martin h[ni].sysctl_ver != o[ni].sysctl_ver)
172 1.6.8.2 martin rc = 1;
173 1.6.8.2 martin }
174 1.6.8.2 martin if (rc == 0) {
175 1.6.8.2 martin free(h);
176 1.6.8.2 martin return;
177 1.6.8.2 martin }
178 1.6.8.2 martin
179 1.6.8.2 martin /*
180 1.6.8.2 martin * something changed. h will become the new head, and we need
181 1.6.8.2 martin * pull over any subtrees we already have if they're the same
182 1.6.8.2 martin * version.
183 1.6.8.2 martin */
184 1.6.8.2 martin i = h;
185 1.6.8.2 martin ni = oi = 0;
186 1.6.8.2 martin while (ni < nlen && oi < olen) {
187 1.6.8.2 martin /*
188 1.6.8.2 martin * something was inserted or deleted
189 1.6.8.2 martin */
190 1.6.8.2 martin if (SYSCTL_TYPE(i[ni].sysctl_flags) == CTLTYPE_NODE)
191 1.6.8.2 martin i[ni].sysctl_child = NULL;
192 1.6.8.2 martin if (i[ni].sysctl_num != o[oi].sysctl_num) {
193 1.6.8.2 martin if (i[ni].sysctl_num < o[oi].sysctl_num) {
194 1.6.8.2 martin ni++;
195 1.6.8.2 martin }
196 1.6.8.2 martin else {
197 1.6.8.2 martin free_children(&o[oi]);
198 1.6.8.2 martin oi++;
199 1.6.8.2 martin }
200 1.6.8.2 martin continue;
201 1.6.8.2 martin }
202 1.6.8.2 martin
203 1.6.8.2 martin /*
204 1.6.8.2 martin * same number, but different version, so throw away
205 1.6.8.2 martin * any accumulated children
206 1.6.8.2 martin */
207 1.6.8.2 martin if (i[ni].sysctl_ver != o[oi].sysctl_ver)
208 1.6.8.2 martin free_children(&o[oi]);
209 1.6.8.2 martin
210 1.6.8.2 martin /*
211 1.6.8.2 martin * this node is the same, but we only need to
212 1.6.8.2 martin * move subtrees.
213 1.6.8.2 martin */
214 1.6.8.2 martin else if (SYSCTL_TYPE(i[ni].sysctl_flags) == CTLTYPE_NODE) {
215 1.6.8.2 martin /*
216 1.6.8.2 martin * move subtree to new parent
217 1.6.8.2 martin */
218 1.6.8.2 martin i[ni].sysctl_clen = o[oi].sysctl_clen;
219 1.6.8.2 martin i[ni].sysctl_csize = o[oi].sysctl_csize;
220 1.6.8.2 martin i[ni].sysctl_child = o[oi].sysctl_child;
221 1.6.8.2 martin /*
222 1.6.8.2 martin * reparent inherited subtree
223 1.6.8.2 martin */
224 1.6.8.2 martin for (t = 0;
225 1.6.8.2 martin i[ni].sysctl_child != NULL &&
226 1.6.8.2 martin t < i[ni].sysctl_clen;
227 1.6.8.2 martin t++)
228 1.6.8.2 martin i[ni].sysctl_child[t].sysctl_parent = &i[ni];
229 1.6.8.2 martin }
230 1.6.8.2 martin ni++;
231 1.6.8.2 martin oi++;
232 1.6.8.2 martin }
233 1.6.8.2 martin
234 1.6.8.2 martin /*
235 1.6.8.2 martin * left over new nodes need to have empty subtrees cleared
236 1.6.8.2 martin */
237 1.6.8.2 martin while (ni < nlen) {
238 1.6.8.2 martin if (SYSCTL_TYPE(i[ni].sysctl_flags) == CTLTYPE_NODE)
239 1.6.8.2 martin i[ni].sysctl_child = NULL;
240 1.6.8.2 martin ni++;
241 1.6.8.2 martin }
242 1.6.8.2 martin
243 1.6.8.2 martin /*
244 1.6.8.2 martin * left over old nodes need to be cleaned out
245 1.6.8.2 martin */
246 1.6.8.2 martin while (oi < olen) {
247 1.6.8.2 martin free_children(&o[oi]);
248 1.6.8.2 martin oi++;
249 1.6.8.2 martin }
250 1.6.8.2 martin
251 1.6.8.2 martin /*
252 1.6.8.2 martin * pop new head in
253 1.6.8.2 martin */
254 1.6.8.2 martin sysctl_mibroot.sysctl_clen = nlen;
255 1.6.8.2 martin sysctl_mibroot.sysctl_csize = nlen;
256 1.6.8.2 martin sysctl_mibroot.sysctl_child = h;
257 1.6.8.2 martin free(o);
258 1.6.8.2 martin }
259 1.6.8.2 martin
260 1.6.8.2 martin /*
261 1.6.8.2 martin * sucks in the children at a given level and attaches it to the tree.
262 1.6.8.2 martin */
263 1.6.8.2 martin int
264 1.6.8.2 martin __learn_tree(int *name, u_int namelen, struct sysctlnode *pnode)
265 1.6.8.2 martin {
266 1.6.8.2 martin struct sysctlnode qnode;
267 1.6.8.2 martin int rc;
268 1.6.8.2 martin size_t sz;
269 1.6.8.2 martin
270 1.6.8.2 martin if (pnode == NULL)
271 1.6.8.2 martin pnode = &sysctl_mibroot;
272 1.6.8.2 martin if (SYSCTL_TYPE(pnode->sysctl_flags) != CTLTYPE_NODE) {
273 1.6.8.2 martin errno = EINVAL;
274 1.6.8.2 martin return (-1);
275 1.6.8.2 martin }
276 1.6.8.2 martin if (pnode->sysctl_child != NULL)
277 1.6.8.2 martin return (0);
278 1.6.8.2 martin
279 1.6.8.2 martin if (pnode->sysctl_clen == 0)
280 1.6.8.2 martin sz = SYSCTL_DEFSIZE * sizeof(struct sysctlnode);
281 1.6.8.2 martin else
282 1.6.8.2 martin sz = pnode->sysctl_clen * sizeof(struct sysctlnode);
283 1.6.8.2 martin pnode->sysctl_child = malloc(sz);
284 1.6.8.2 martin if (pnode->sysctl_child == NULL)
285 1.6.8.2 martin return (-1);
286 1.6.8.2 martin
287 1.6.8.2 martin name[namelen] = CTL_QUERY;
288 1.6.8.2 martin pnode->sysctl_clen = 0;
289 1.6.8.2 martin pnode->sysctl_csize = 0;
290 1.6.8.2 martin memset(&qnode, 0, sizeof(qnode));
291 1.6.8.2 martin qnode.sysctl_flags = SYSCTL_VERSION;
292 1.6.8.2 martin rc = sysctl(name, namelen + 1, pnode->sysctl_child, &sz,
293 1.6.8.2 martin &qnode, sizeof(qnode));
294 1.6.8.2 martin if (sz == 0) {
295 1.6.8.2 martin free(pnode->sysctl_child);
296 1.6.8.2 martin pnode->sysctl_child = NULL;
297 1.6.8.2 martin return (rc);
298 1.6.8.2 martin }
299 1.6.8.2 martin if (rc) {
300 1.6.8.2 martin free(pnode->sysctl_child);
301 1.6.8.2 martin pnode->sysctl_child = NULL;
302 1.6.8.2 martin if ((sz % sizeof(struct sysctlnode)) != 0)
303 1.6.8.2 martin errno = EINVAL;
304 1.6.8.2 martin if (errno != ENOMEM)
305 1.6.8.2 martin return (rc);
306 1.6.8.2 martin }
307 1.6.8.2 martin
308 1.6.8.2 martin if (pnode->sysctl_child == NULL) {
309 1.6.8.2 martin pnode->sysctl_child = malloc(sz);
310 1.6.8.2 martin if (pnode->sysctl_child == NULL)
311 1.6.8.2 martin return (-1);
312 1.6.8.2 martin
313 1.6.8.2 martin rc = sysctl(name, namelen + 1, pnode->sysctl_child, &sz,
314 1.6.8.2 martin &qnode, sizeof(qnode));
315 1.6.8.2 martin if (rc) {
316 1.6.8.2 martin free(pnode->sysctl_child);
317 1.6.8.2 martin pnode->sysctl_child = NULL;
318 1.6.8.2 martin return (rc);
319 1.6.8.2 martin }
320 1.6.8.2 martin }
321 1.6.8.2 martin
322 1.6.8.2 martin /*
323 1.6.8.2 martin * how many did we get?
324 1.6.8.2 martin */
325 1.6.8.2 martin pnode->sysctl_clen = sz / sizeof(struct sysctlnode);
326 1.6.8.2 martin pnode->sysctl_csize = sz / sizeof(struct sysctlnode);
327 1.6.8.2 martin if (pnode->sysctl_clen * sizeof(struct sysctlnode) != sz) {
328 1.6.8.2 martin free(pnode->sysctl_child);
329 1.6.8.2 martin pnode->sysctl_child = NULL;
330 1.6.8.2 martin errno = EINVAL;
331 1.6.8.2 martin return (-1);
332 1.6.8.2 martin }
333 1.6.8.2 martin
334 1.6.8.2 martin /*
335 1.6.8.2 martin * you know, the kernel doesn't really keep them in any
336 1.6.8.2 martin * particular order...just like entries in a directory
337 1.6.8.2 martin */
338 1.6.8.2 martin qsort(pnode->sysctl_child, pnode->sysctl_clen,
339 1.6.8.2 martin sizeof(struct sysctlnode), compar);
340 1.6.8.2 martin
341 1.6.8.2 martin /*
342 1.6.8.2 martin * rearrange parent<->child linkage
343 1.6.8.2 martin */
344 1.6.8.2 martin for (rc = 0; rc < pnode->sysctl_clen; rc++) {
345 1.6.8.2 martin pnode->sysctl_child[rc].sysctl_parent = pnode;
346 1.6.8.2 martin if (SYSCTL_TYPE(pnode->sysctl_child[rc].sysctl_flags) ==
347 1.6.8.2 martin CTLTYPE_NODE) {
348 1.6.8.2 martin /*
349 1.6.8.2 martin * these nodes may have children, but we
350 1.6.8.2 martin * haven't discovered that yet.
351 1.6.8.2 martin */
352 1.6.8.2 martin pnode->sysctl_child[rc].sysctl_child = NULL;
353 1.6.8.2 martin }
354 1.6.8.2 martin pnode->sysctl_child[rc].sysctl_desc = NULL;
355 1.6.8.2 martin }
356 1.6.8.2 martin
357 1.6.8.2 martin return (0);
358 1.6.8.2 martin }
359 1.6.8.2 martin
360 1.6.8.2 martin /*
361 1.6.8.2 martin * that's "given name" as a string, the integer form of the name fit
362 1.6.8.2 martin * to be passed to sysctl(), "canonicalized name" (optional), and a
363 1.6.8.2 martin * pointer to the length of the integer form. oh, and then a pointer
364 1.6.8.2 martin * to the node, in case you (the caller) care. you can leave them all
365 1.6.8.2 martin * NULL except for gname, though that might be rather pointless,
366 1.6.8.2 martin * unless all you wanna do is verify that a given name is acceptable.
367 1.6.8.2 martin *
368 1.6.8.2 martin * returns either 0 (everything was fine) or -1 and sets errno
369 1.6.8.2 martin * accordingly. if errno is set to EAGAIN, we detected a change to
370 1.6.8.2 martin * the mib while parsing, and you should try again. in the case of an
371 1.6.8.2 martin * invalid node name, cname will be set to contain the offending name.
372 1.6.8.2 martin */
373 1.6.8.2 martin #ifdef _REENTRANT
374 1.6.8.2 martin static mutex_t sysctl_mutex = MUTEX_INITIALIZER;
375 1.6.8.2 martin static int sysctlgetmibinfo_unlocked(const char *, int *, u_int *, char *,
376 1.6.8.2 martin size_t *, struct sysctlnode **, int);
377 1.6.8.2 martin #endif /* __REENTRANT */
378 1.6.8.2 martin
379 1.6.8.2 martin int
380 1.6.8.2 martin sysctlgetmibinfo(const char *gname, int *iname, u_int *namelenp,
381 1.6.8.2 martin char *cname, size_t *csz, struct sysctlnode **rnode, int v)
382 1.6.8.2 martin #ifdef _REENTRANT
383 1.6.8.2 martin {
384 1.6.8.2 martin int rc;
385 1.6.8.2 martin
386 1.6.8.2 martin mutex_lock(&sysctl_mutex);
387 1.6.8.2 martin rc = sysctlgetmibinfo_unlocked(gname, iname, namelenp, cname, csz,
388 1.6.8.2 martin rnode, v);
389 1.6.8.2 martin mutex_unlock(&sysctl_mutex);
390 1.6.8.2 martin
391 1.6.8.2 martin return (rc);
392 1.6.8.2 martin }
393 1.6.8.2 martin
394 1.6.8.2 martin static int
395 1.6.8.2 martin sysctlgetmibinfo_unlocked(const char *gname, int *iname, u_int *namelenp,
396 1.6.8.2 martin char *cname, size_t *csz, struct sysctlnode **rnode,
397 1.6.8.2 martin int v)
398 1.6.8.2 martin #endif /* _REENTRANT */
399 1.6.8.2 martin {
400 1.6.8.2 martin struct sysctlnode *pnode, *node;
401 1.6.8.2 martin int name[CTL_MAXNAME], ni, n, haven;
402 1.6.8.2 martin u_int nl;
403 1.6.8.2 martin intmax_t q;
404 1.6.8.2 martin char sep[2], token[SYSCTL_NAMELEN],
405 1.6.8.2 martin pname[SYSCTL_NAMELEN * CTL_MAXNAME + CTL_MAXNAME];
406 1.6.8.2 martin const char *piece, *dot;
407 1.6.8.2 martin char *t;
408 1.6.8.2 martin size_t l;
409 1.6.8.2 martin
410 1.6.8.2 martin if (rnode != NULL) {
411 1.6.8.2 martin if (*rnode == NULL) {
412 1.6.8.2 martin /* XXX later deal with dealing back a sub version */
413 1.6.8.2 martin if (v != SYSCTL_VERSION)
414 1.6.8.2 martin return (EINVAL);
415 1.6.8.2 martin
416 1.6.8.2 martin pnode = &sysctl_mibroot;
417 1.6.8.2 martin }
418 1.6.8.2 martin else {
419 1.6.8.2 martin /* this is just someone being silly */
420 1.6.8.2 martin if (SYSCTL_VERS((*rnode)->sysctl_flags) != v)
421 1.6.8.2 martin return (EINVAL);
422 1.6.8.2 martin
423 1.6.8.2 martin /* XXX later deal with other people's trees */
424 1.6.8.2 martin if (SYSCTL_VERS((*rnode)->sysctl_flags) !=
425 1.6.8.2 martin SYSCTL_VERSION)
426 1.6.8.2 martin return (EINVAL);
427 1.6.8.2 martin
428 1.6.8.2 martin pnode = *rnode;
429 1.6.8.2 martin }
430 1.6.8.2 martin }
431 1.6.8.2 martin else
432 1.6.8.2 martin pnode = &sysctl_mibroot;
433 1.6.8.2 martin
434 1.6.8.2 martin if (pnode == &sysctl_mibroot)
435 1.6.8.2 martin relearnhead();
436 1.6.8.2 martin
437 1.6.8.2 martin nl = ni = 0;
438 1.6.8.2 martin token[0] = '\0';
439 1.6.8.2 martin pname[0] = '\0';
440 1.6.8.2 martin node = NULL;
441 1.6.8.2 martin
442 1.6.8.2 martin /*
443 1.6.8.2 martin * default to using '.' as the separator, but allow '/' as
444 1.6.8.2 martin * well, and then allow a leading separator
445 1.6.8.2 martin */
446 1.6.8.2 martin if ((dot = strpbrk(gname, "./")) == NULL)
447 1.6.8.2 martin sep[0] = '.';
448 1.6.8.2 martin else
449 1.6.8.2 martin sep[0] = dot[0];
450 1.6.8.2 martin sep[1] = '\0';
451 1.6.8.2 martin if (gname[0] == sep[0]) {
452 1.6.8.2 martin strlcat(pname, sep, sizeof(pname));
453 1.6.8.2 martin gname++;
454 1.6.8.2 martin }
455 1.6.8.2 martin
456 1.6.8.2 martin #define COPY_OUT_DATA(t, c, cs, nlp, l) do { \
457 1.6.8.2 martin if ((c) != NULL && (cs) != NULL) \
458 1.6.8.2 martin *(cs) = strlcpy((c), (t), *(cs)); \
459 1.6.8.2 martin else if ((cs) != NULL) \
460 1.6.8.2 martin *(cs) = strlen(t) + 1; \
461 1.6.8.2 martin if ((nlp) != NULL) \
462 1.6.8.2 martin *(nlp) = (l); \
463 1.6.8.2 martin } while (/*CONSTCOND*/0)
464 1.6.8.2 martin
465 1.6.8.2 martin piece = gname;
466 1.6.8.2 martin while (piece != NULL && *piece != '\0') {
467 1.6.8.2 martin /*
468 1.6.8.2 martin * what was i looking for?
469 1.6.8.2 martin */
470 1.6.8.2 martin dot = strchr(piece, sep[0]);
471 1.6.8.2 martin if (dot == NULL) {
472 1.6.8.2 martin l = strlcpy(token, piece, sizeof(token));
473 1.6.8.2 martin if (l > sizeof(token)) {
474 1.6.8.2 martin COPY_OUT_DATA(piece, cname, csz, namelenp, nl);
475 1.6.8.2 martin errno = ENAMETOOLONG;
476 1.6.8.2 martin return (-1);
477 1.6.8.2 martin }
478 1.6.8.2 martin }
479 1.6.8.2 martin else if (dot - piece > sizeof(token) - 1) {
480 1.6.8.2 martin COPY_OUT_DATA(token, cname, csz, namelenp, nl);
481 1.6.8.2 martin errno = ENAMETOOLONG;
482 1.6.8.2 martin return (-1);
483 1.6.8.2 martin }
484 1.6.8.2 martin else {
485 1.6.8.2 martin strncpy(token, piece, (size_t)(dot - piece));
486 1.6.8.2 martin token[dot - piece] = '\0';
487 1.6.8.2 martin }
488 1.6.8.2 martin
489 1.6.8.2 martin /*
490 1.6.8.2 martin * i wonder if this "token" is an integer?
491 1.6.8.2 martin */
492 1.6.8.2 martin errno = 0;
493 1.6.8.2 martin q = strtoimax(token, &t, 0);
494 1.6.8.2 martin n = (int)q;
495 1.6.8.2 martin if (errno != 0 || *t != '\0')
496 1.6.8.2 martin haven = 0;
497 1.6.8.2 martin else if (q < INT_MIN || q > UINT_MAX)
498 1.6.8.2 martin haven = 0;
499 1.6.8.2 martin else
500 1.6.8.2 martin haven = 1;
501 1.6.8.2 martin
502 1.6.8.2 martin /*
503 1.6.8.2 martin * make sure i have something to look at
504 1.6.8.2 martin */
505 1.6.8.2 martin if (SYSCTL_TYPE(pnode->sysctl_flags) != CTLTYPE_NODE) {
506 1.6.8.2 martin if (haven && nl > 0) {
507 1.6.8.2 martin strlcat(pname, sep, sizeof(pname));
508 1.6.8.2 martin goto just_numbers;
509 1.6.8.2 martin }
510 1.6.8.2 martin COPY_OUT_DATA(token, cname, csz, namelenp, nl);
511 1.6.8.2 martin errno = ENOTDIR;
512 1.6.8.2 martin return (-1);
513 1.6.8.2 martin }
514 1.6.8.2 martin if (pnode->sysctl_child == NULL) {
515 1.6.8.2 martin if (__learn_tree(name, nl, pnode) == -1) {
516 1.6.8.2 martin COPY_OUT_DATA(token, cname, csz, namelenp, nl);
517 1.6.8.2 martin return (-1);
518 1.6.8.2 martin }
519 1.6.8.2 martin }
520 1.6.8.2 martin node = pnode->sysctl_child;
521 1.6.8.2 martin if (node == NULL) {
522 1.6.8.2 martin COPY_OUT_DATA(token, cname, csz, namelenp, nl);
523 1.6.8.2 martin errno = ENOENT;
524 1.6.8.2 martin return (-1);
525 1.6.8.2 martin }
526 1.6.8.2 martin
527 1.6.8.2 martin /*
528 1.6.8.2 martin * now...is it there?
529 1.6.8.2 martin */
530 1.6.8.2 martin for (ni = 0; ni < pnode->sysctl_clen; ni++)
531 1.6.8.2 martin if ((haven && ((n == node[ni].sysctl_num) ||
532 1.6.8.2 martin (node[ni].sysctl_flags & CTLFLAG_ANYNUMBER))) ||
533 1.6.8.2 martin strcmp(token, node[ni].sysctl_name) == 0)
534 1.6.8.2 martin break;
535 1.6.8.2 martin if (ni >= pnode->sysctl_clen) {
536 1.6.8.2 martin COPY_OUT_DATA(token, cname, csz, namelenp, nl);
537 1.6.8.2 martin errno = ENOENT;
538 1.6.8.2 martin return (-1);
539 1.6.8.2 martin }
540 1.6.8.2 martin
541 1.6.8.2 martin /*
542 1.6.8.2 martin * ah...it is.
543 1.6.8.2 martin */
544 1.6.8.2 martin pnode = &node[ni];
545 1.6.8.2 martin if (nl > 0)
546 1.6.8.2 martin strlcat(pname, sep, sizeof(pname));
547 1.6.8.2 martin if (haven && n != pnode->sysctl_num) {
548 1.6.8.2 martin just_numbers:
549 1.6.8.2 martin strlcat(pname, token, sizeof(pname));
550 1.6.8.2 martin name[nl] = n;
551 1.6.8.2 martin }
552 1.6.8.2 martin else {
553 1.6.8.2 martin strlcat(pname, pnode->sysctl_name, sizeof(pname));
554 1.6.8.2 martin name[nl] = pnode->sysctl_num;
555 1.6.8.2 martin }
556 1.6.8.2 martin piece = (dot != NULL) ? dot + 1 : NULL;
557 1.6.8.2 martin nl++;
558 1.6.8.2 martin if (nl == CTL_MAXNAME) {
559 1.6.8.2 martin COPY_OUT_DATA(token, cname, csz, namelenp, nl);
560 1.6.8.2 martin errno = ERANGE;
561 1.6.8.2 martin return (-1);
562 1.6.8.2 martin }
563 1.6.8.2 martin }
564 1.6.8.2 martin
565 1.6.8.2 martin if (nl == 0) {
566 1.6.8.2 martin if (namelenp != NULL)
567 1.6.8.2 martin *namelenp = 0;
568 1.6.8.2 martin errno = EINVAL;
569 1.6.8.2 martin return (-1);
570 1.6.8.2 martin }
571 1.6.8.2 martin
572 1.6.8.2 martin COPY_OUT_DATA(pname, cname, csz, namelenp, nl);
573 1.6.8.2 martin if (iname != NULL && namelenp != NULL)
574 1.6.8.2 martin memcpy(iname, &name[0], MIN(nl, *namelenp) * sizeof(int));
575 1.6.8.2 martin if (namelenp != NULL)
576 1.6.8.2 martin *namelenp = nl;
577 1.6.8.2 martin if (rnode != NULL) {
578 1.6.8.2 martin if (*rnode != NULL)
579 1.6.8.2 martin /*
580 1.6.8.2 martin * they gave us a private tree to work in, so
581 1.6.8.2 martin * we give back a pointer into that private
582 1.6.8.2 martin * tree
583 1.6.8.2 martin */
584 1.6.8.2 martin *rnode = pnode;
585 1.6.8.2 martin else {
586 1.6.8.2 martin /*
587 1.6.8.2 martin * they gave us a place to put the node data,
588 1.6.8.2 martin * so give them a copy
589 1.6.8.2 martin */
590 1.6.8.2 martin *rnode = malloc(sizeof(struct sysctlnode));
591 1.6.8.2 martin if (*rnode != NULL) {
592 1.6.8.2 martin **rnode = *pnode;
593 1.6.8.2 martin (*rnode)->sysctl_child = NULL;
594 1.6.8.2 martin (*rnode)->sysctl_parent = NULL;
595 1.6.8.2 martin }
596 1.6.8.2 martin }
597 1.6.8.2 martin }
598 1.6.8.2 martin
599 1.6.8.2 martin return (0);
600 1.6.8.2 martin }
601