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