1 | /* $NetBSD: kern_sysctl.c,v 1.258 2015/10/23 01:58:43 pgoyette Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2003, 2007, 2008 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Andrew Brown. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | /*- |
33 | * Copyright (c) 1982, 1986, 1989, 1993 |
34 | * The Regents of the University of California. All rights reserved. |
35 | * |
36 | * This code is derived from software contributed to Berkeley by |
37 | * Mike Karels at Berkeley Software Design, Inc. |
38 | * |
39 | * Redistribution and use in source and binary forms, with or without |
40 | * modification, are permitted provided that the following conditions |
41 | * are met: |
42 | * 1. Redistributions of source code must retain the above copyright |
43 | * notice, this list of conditions and the following disclaimer. |
44 | * 2. Redistributions in binary form must reproduce the above copyright |
45 | * notice, this list of conditions and the following disclaimer in the |
46 | * documentation and/or other materials provided with the distribution. |
47 | * 3. Neither the name of the University nor the names of its contributors |
48 | * may be used to endorse or promote products derived from this software |
49 | * without specific prior written permission. |
50 | * |
51 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
52 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
53 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
54 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
55 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
56 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
57 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
58 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
59 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
60 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
61 | * SUCH DAMAGE. |
62 | * |
63 | * @(#)kern_sysctl.c 8.9 (Berkeley) 5/20/95 |
64 | */ |
65 | |
66 | /* |
67 | * sysctl system call. |
68 | */ |
69 | |
70 | #include <sys/cdefs.h> |
71 | __KERNEL_RCSID(0, "$NetBSD: kern_sysctl.c,v 1.258 2015/10/23 01:58:43 pgoyette Exp $" ); |
72 | |
73 | #ifdef _KERNEL_OPT |
74 | #include "opt_defcorename.h" |
75 | #endif |
76 | |
77 | #include "ksyms.h" |
78 | |
79 | #include <sys/param.h> |
80 | #define __COMPAT_SYSCTL |
81 | #include <sys/sysctl.h> |
82 | #include <sys/systm.h> |
83 | #include <sys/buf.h> |
84 | #include <sys/ksyms.h> |
85 | #include <sys/malloc.h> |
86 | #include <sys/mount.h> |
87 | #include <sys/syscallargs.h> |
88 | #include <sys/kauth.h> |
89 | #include <sys/ktrace.h> |
90 | #include <sys/rndsource.h> |
91 | |
92 | #define MAXDESCLEN 1024 |
93 | MALLOC_DEFINE(M_SYSCTLNODE, "sysctlnode" , "sysctl node structures" ); |
94 | MALLOC_DEFINE(M_SYSCTLDATA, "sysctldata" , "misc sysctl data" ); |
95 | |
96 | static int sysctl_mmap(SYSCTLFN_PROTO); |
97 | static int sysctl_alloc(struct sysctlnode *, int); |
98 | static int sysctl_realloc(struct sysctlnode *); |
99 | |
100 | static int sysctl_cvt_in(struct lwp *, int *, const void *, size_t, |
101 | struct sysctlnode *); |
102 | static int sysctl_cvt_out(struct lwp *, int, const struct sysctlnode *, |
103 | void *, size_t, size_t *); |
104 | |
105 | static int sysctl_log_add(struct sysctllog **, const struct sysctlnode *); |
106 | static int sysctl_log_realloc(struct sysctllog *); |
107 | |
108 | typedef void sysctl_setup_func(struct sysctllog **); |
109 | |
110 | #ifdef SYSCTL_DEBUG |
111 | #define DPRINTF(a) printf a |
112 | #else |
113 | #define DPRINTF(a) |
114 | #endif |
115 | |
116 | struct sysctllog { |
117 | const struct sysctlnode *log_root; |
118 | int *log_num; |
119 | int log_size, log_left; |
120 | }; |
121 | |
122 | /* |
123 | * the "root" of the new sysctl tree |
124 | */ |
125 | struct sysctlnode sysctl_root = { |
126 | .sysctl_flags = SYSCTL_VERSION| |
127 | CTLFLAG_ROOT|CTLFLAG_READWRITE| |
128 | CTLTYPE_NODE, |
129 | .sysctl_num = 0, |
130 | .sysctl_size = sizeof(struct sysctlnode), |
131 | .sysctl_name = "(root)" , |
132 | }; |
133 | |
134 | /* |
135 | * link set of functions that add nodes at boot time (see also |
136 | * sysctl_buildtree()) |
137 | */ |
138 | __link_set_decl(sysctl_funcs, sysctl_setup_func); |
139 | |
140 | /* |
141 | * The `sysctl_treelock' is intended to serialize access to the sysctl |
142 | * tree. XXX This has serious problems; allocating memory and |
143 | * copying data out with the lock held is insane. |
144 | */ |
145 | krwlock_t sysctl_treelock; |
146 | |
147 | kmutex_t sysctl_file_marker_lock; |
148 | |
149 | /* |
150 | * Attributes stored in the kernel. |
151 | */ |
152 | char hostname[MAXHOSTNAMELEN]; |
153 | int hostnamelen; |
154 | |
155 | char domainname[MAXHOSTNAMELEN]; |
156 | int domainnamelen; |
157 | |
158 | long hostid; |
159 | |
160 | #ifndef DEFCORENAME |
161 | #define DEFCORENAME "%n.core" |
162 | #endif |
163 | char defcorename[MAXPATHLEN] = DEFCORENAME; |
164 | |
165 | /* |
166 | * ******************************************************************** |
167 | * Section 0: Some simple glue |
168 | * ******************************************************************** |
169 | * By wrapping copyin(), copyout(), and copyinstr() like this, we can |
170 | * stop caring about who's calling us and simplify some code a bunch. |
171 | * ******************************************************************** |
172 | */ |
173 | int |
174 | sysctl_copyin(struct lwp *l, const void *uaddr, void *kaddr, size_t len) |
175 | { |
176 | int error; |
177 | |
178 | if (l != NULL) { |
179 | error = copyin(uaddr, kaddr, len); |
180 | ktrmibio(-1, UIO_WRITE, uaddr, len, error); |
181 | } else { |
182 | error = kcopy(uaddr, kaddr, len); |
183 | } |
184 | |
185 | return error; |
186 | } |
187 | |
188 | int |
189 | sysctl_copyout(struct lwp *l, const void *kaddr, void *uaddr, size_t len) |
190 | { |
191 | int error; |
192 | |
193 | if (l != NULL) { |
194 | error = copyout(kaddr, uaddr, len); |
195 | ktrmibio(-1, UIO_READ, uaddr, len, error); |
196 | } else { |
197 | error = kcopy(kaddr, uaddr, len); |
198 | } |
199 | |
200 | return error; |
201 | } |
202 | |
203 | int |
204 | sysctl_copyinstr(struct lwp *l, const void *uaddr, void *kaddr, |
205 | size_t len, size_t *done) |
206 | { |
207 | int error; |
208 | |
209 | if (l != NULL) { |
210 | error = copyinstr(uaddr, kaddr, len, done); |
211 | ktrmibio(-1, UIO_WRITE, uaddr, len, error); |
212 | } else { |
213 | error = copystr(uaddr, kaddr, len, done); |
214 | } |
215 | |
216 | return error; |
217 | } |
218 | |
219 | /* |
220 | * ******************************************************************** |
221 | * Initialize sysctl subsystem. |
222 | * ******************************************************************** |
223 | */ |
224 | void |
225 | sysctl_init(void) |
226 | { |
227 | sysctl_setup_func *const *sysctl_setup; |
228 | |
229 | rw_init(&sysctl_treelock); |
230 | |
231 | /* |
232 | * dynamic mib numbers start here |
233 | */ |
234 | sysctl_root.sysctl_num = CREATE_BASE; |
235 | sysctl_basenode_init(); |
236 | |
237 | __link_set_foreach(sysctl_setup, sysctl_funcs) { |
238 | (**sysctl_setup)(NULL); |
239 | } |
240 | |
241 | mutex_init(&sysctl_file_marker_lock, MUTEX_DEFAULT, IPL_NONE); |
242 | } |
243 | |
244 | /* |
245 | * Setting this means no more permanent nodes can be added, |
246 | * trees that claim to be readonly at the root now are, and if |
247 | * the main tree is readonly, *everything* is. |
248 | * |
249 | * Also starts up the PRNG used for the "random" sysctl: it's |
250 | * better to start it later than sooner. |
251 | * |
252 | * Call this at the end of kernel init. |
253 | */ |
254 | void |
255 | sysctl_finalize(void) |
256 | { |
257 | |
258 | sysctl_root.sysctl_flags |= CTLFLAG_PERMANENT; |
259 | } |
260 | |
261 | /* |
262 | * ******************************************************************** |
263 | * The main native sysctl system call itself. |
264 | * ******************************************************************** |
265 | */ |
266 | int |
267 | sys___sysctl(struct lwp *l, const struct sys___sysctl_args *uap, register_t *retval) |
268 | { |
269 | /* { |
270 | syscallarg(const int *) name; |
271 | syscallarg(u_int) namelen; |
272 | syscallarg(void *) old; |
273 | syscallarg(size_t *) oldlenp; |
274 | syscallarg(const void *) new; |
275 | syscallarg(size_t) newlen; |
276 | } */ |
277 | int error, nerror, name[CTL_MAXNAME]; |
278 | size_t oldlen, savelen, *oldlenp; |
279 | |
280 | /* |
281 | * get oldlen |
282 | */ |
283 | oldlen = 0; |
284 | oldlenp = SCARG(uap, oldlenp); |
285 | if (oldlenp != NULL) { |
286 | error = copyin(oldlenp, &oldlen, sizeof(oldlen)); |
287 | if (error) |
288 | return (error); |
289 | } |
290 | savelen = oldlen; |
291 | |
292 | /* |
293 | * top-level sysctl names may or may not be non-terminal, but |
294 | * we don't care |
295 | */ |
296 | if (SCARG(uap, namelen) > CTL_MAXNAME || SCARG(uap, namelen) < 1) |
297 | return (EINVAL); |
298 | error = copyin(SCARG(uap, name), &name, |
299 | SCARG(uap, namelen) * sizeof(int)); |
300 | if (error) |
301 | return (error); |
302 | |
303 | ktrmib(name, SCARG(uap, namelen)); |
304 | |
305 | sysctl_lock(SCARG(uap, newv) != NULL); |
306 | |
307 | /* |
308 | * do sysctl work (NULL means main built-in default tree) |
309 | */ |
310 | error = sysctl_dispatch(&name[0], SCARG(uap, namelen), |
311 | SCARG(uap, oldv), &oldlen, |
312 | SCARG(uap, newv), SCARG(uap, newlen), |
313 | &name[0], l, NULL); |
314 | |
315 | /* |
316 | * release the sysctl lock |
317 | */ |
318 | sysctl_unlock(); |
319 | |
320 | /* |
321 | * set caller's oldlen to new value even in the face of an |
322 | * error (if this gets an error and they didn't have one, they |
323 | * get this one) |
324 | */ |
325 | if (oldlenp) { |
326 | nerror = copyout(&oldlen, oldlenp, sizeof(oldlen)); |
327 | if (error == 0) |
328 | error = nerror; |
329 | } |
330 | |
331 | /* |
332 | * if the only problem is that we weren't given enough space, |
333 | * that's an ENOMEM error |
334 | */ |
335 | if (error == 0 && SCARG(uap, oldv) != NULL && savelen < oldlen) |
336 | error = ENOMEM; |
337 | |
338 | return (error); |
339 | } |
340 | |
341 | /* |
342 | * ******************************************************************** |
343 | * Section 1: How the tree is used |
344 | * ******************************************************************** |
345 | * Implementations of sysctl for emulations should typically need only |
346 | * these three functions in this order: lock the tree, dispatch |
347 | * request into it, unlock the tree. |
348 | * ******************************************************************** |
349 | */ |
350 | void |
351 | sysctl_lock(bool write) |
352 | { |
353 | |
354 | if (write) { |
355 | rw_enter(&sysctl_treelock, RW_WRITER); |
356 | curlwp->l_pflag |= LP_SYSCTLWRITE; |
357 | } else { |
358 | rw_enter(&sysctl_treelock, RW_READER); |
359 | curlwp->l_pflag &= ~LP_SYSCTLWRITE; |
360 | } |
361 | } |
362 | |
363 | void |
364 | sysctl_relock(void) |
365 | { |
366 | |
367 | if ((curlwp->l_pflag & LP_SYSCTLWRITE) != 0) { |
368 | rw_enter(&sysctl_treelock, RW_WRITER); |
369 | } else { |
370 | rw_enter(&sysctl_treelock, RW_READER); |
371 | } |
372 | } |
373 | |
374 | /* |
375 | * ******************************************************************** |
376 | * the main sysctl dispatch routine. scans the given tree and picks a |
377 | * function to call based on what it finds. |
378 | * ******************************************************************** |
379 | */ |
380 | int |
381 | sysctl_dispatch(SYSCTLFN_ARGS) |
382 | { |
383 | int error; |
384 | sysctlfn fn; |
385 | int ni; |
386 | |
387 | KASSERT(rw_lock_held(&sysctl_treelock)); |
388 | |
389 | if (rnode && SYSCTL_VERS(rnode->sysctl_flags) != SYSCTL_VERSION) { |
390 | printf("sysctl_dispatch: rnode %p wrong version\n" , rnode); |
391 | error = EINVAL; |
392 | goto out; |
393 | } |
394 | |
395 | fn = NULL; |
396 | error = sysctl_locate(l, name, namelen, &rnode, &ni); |
397 | |
398 | if (rnode->sysctl_func != NULL) { |
399 | /* |
400 | * the node we ended up at has a function, so call it. it can |
401 | * hand off to query or create if it wants to. |
402 | */ |
403 | fn = rnode->sysctl_func; |
404 | } else if (error == 0) { |
405 | /* |
406 | * we found the node they were looking for, so do a lookup. |
407 | */ |
408 | fn = (sysctlfn)sysctl_lookup; /* XXX may write to rnode */ |
409 | } else if (error == ENOENT && (ni + 1) == namelen && name[ni] < 0) { |
410 | /* |
411 | * prospective parent node found, but the terminal node was |
412 | * not. generic operations associate with the parent. |
413 | */ |
414 | switch (name[ni]) { |
415 | case CTL_QUERY: |
416 | fn = sysctl_query; |
417 | break; |
418 | case CTL_CREATE: |
419 | #if NKSYMS > 0 |
420 | case CTL_CREATESYM: |
421 | #endif /* NKSYMS > 0 */ |
422 | if (newp == NULL) { |
423 | error = EINVAL; |
424 | break; |
425 | } |
426 | KASSERT(rw_write_held(&sysctl_treelock)); |
427 | fn = (sysctlfn)sysctl_create; /* we own the rnode */ |
428 | break; |
429 | case CTL_DESTROY: |
430 | if (newp == NULL) { |
431 | error = EINVAL; |
432 | break; |
433 | } |
434 | KASSERT(rw_write_held(&sysctl_treelock)); |
435 | fn = (sysctlfn)sysctl_destroy; /* we own the rnode */ |
436 | break; |
437 | case CTL_MMAP: |
438 | fn = (sysctlfn)sysctl_mmap; /* we own the rnode */ |
439 | break; |
440 | case CTL_DESCRIBE: |
441 | fn = sysctl_describe; |
442 | break; |
443 | default: |
444 | error = EOPNOTSUPP; |
445 | break; |
446 | } |
447 | } |
448 | |
449 | /* |
450 | * after all of that, maybe we found someone who knows how to |
451 | * get us what we want? |
452 | */ |
453 | if (fn != NULL) |
454 | error = (*fn)(name + ni, namelen - ni, oldp, oldlenp, |
455 | newp, newlen, name, l, rnode); |
456 | else if (error == 0) |
457 | error = EOPNOTSUPP; |
458 | |
459 | out: |
460 | return (error); |
461 | } |
462 | |
463 | /* |
464 | * ******************************************************************** |
465 | * Releases the tree lock. |
466 | * ******************************************************************** |
467 | */ |
468 | void |
469 | sysctl_unlock(void) |
470 | { |
471 | |
472 | rw_exit(&sysctl_treelock); |
473 | } |
474 | |
475 | /* |
476 | * ******************************************************************** |
477 | * Section 2: The main tree interfaces |
478 | * ******************************************************************** |
479 | * This is how sysctl_dispatch() does its work, and you can too, by |
480 | * calling these routines from helpers (though typically only |
481 | * sysctl_lookup() will be used). The tree MUST BE LOCKED when these |
482 | * are called. |
483 | * ******************************************************************** |
484 | */ |
485 | |
486 | /* |
487 | * sysctl_locate -- Finds the node matching the given mib under the |
488 | * given tree (via rv). If no tree is given, we fall back to the |
489 | * native tree. The current process (via l) is used for access |
490 | * control on the tree (some nodes may be traversable only by root) and |
491 | * on return, nip will show how many numbers in the mib were consumed. |
492 | */ |
493 | int |
494 | sysctl_locate(struct lwp *l, const int *name, u_int namelen, |
495 | const struct sysctlnode **rnode, int *nip) |
496 | { |
497 | const struct sysctlnode *node, *pnode; |
498 | int tn, si, ni, error, alias; |
499 | |
500 | KASSERT(rw_lock_held(&sysctl_treelock)); |
501 | |
502 | /* |
503 | * basic checks and setup |
504 | */ |
505 | if (*rnode == NULL) |
506 | *rnode = &sysctl_root; |
507 | if (nip) |
508 | *nip = 0; |
509 | if (namelen == 0) |
510 | return (0); |
511 | |
512 | /* |
513 | * search starts from "root" |
514 | */ |
515 | pnode = *rnode; |
516 | if (SYSCTL_VERS(pnode->sysctl_flags) != SYSCTL_VERSION) { |
517 | printf("sysctl_locate: pnode %p wrong version\n" , pnode); |
518 | return (EINVAL); |
519 | } |
520 | node = pnode->sysctl_child; |
521 | error = 0; |
522 | |
523 | /* |
524 | * scan for node to which new node should be attached |
525 | */ |
526 | for (ni = 0; ni < namelen; ni++) { |
527 | /* |
528 | * walked off bottom of tree |
529 | */ |
530 | if (node == NULL) { |
531 | if (SYSCTL_TYPE(pnode->sysctl_flags) == CTLTYPE_NODE) |
532 | error = ENOENT; |
533 | else |
534 | error = ENOTDIR; |
535 | break; |
536 | } |
537 | /* |
538 | * can anyone traverse this node or only root? |
539 | */ |
540 | if (l != NULL && (pnode->sysctl_flags & CTLFLAG_PRIVATE) && |
541 | (error = kauth_authorize_system(l->l_cred, |
542 | KAUTH_SYSTEM_SYSCTL, KAUTH_REQ_SYSTEM_SYSCTL_PRVT, |
543 | NULL, NULL, NULL)) != 0) |
544 | return (error); |
545 | /* |
546 | * find a child node with the right number |
547 | */ |
548 | tn = name[ni]; |
549 | alias = 0; |
550 | |
551 | si = 0; |
552 | /* |
553 | * Note: ANYNUMBER only matches positive integers. |
554 | * Since ANYNUMBER is only permitted on single-node |
555 | * sub-trees (eg proc), check before the loop and skip |
556 | * it if we can. |
557 | */ |
558 | if ((node[si].sysctl_flags & CTLFLAG_ANYNUMBER) && (tn >= 0)) |
559 | goto foundit; |
560 | for (; si < pnode->sysctl_clen; si++) { |
561 | if (node[si].sysctl_num == tn) { |
562 | if (node[si].sysctl_flags & CTLFLAG_ALIAS) { |
563 | if (alias++ == 4) |
564 | break; |
565 | else { |
566 | tn = node[si].sysctl_alias; |
567 | si = -1; |
568 | } |
569 | } else |
570 | goto foundit; |
571 | } |
572 | } |
573 | /* |
574 | * if we ran off the end, it obviously doesn't exist |
575 | */ |
576 | error = ENOENT; |
577 | break; |
578 | |
579 | /* |
580 | * so far so good, move on down the line |
581 | */ |
582 | foundit: |
583 | pnode = &node[si]; |
584 | if (SYSCTL_TYPE(pnode->sysctl_flags) == CTLTYPE_NODE) |
585 | node = node[si].sysctl_child; |
586 | else |
587 | node = NULL; |
588 | } |
589 | |
590 | *rnode = pnode; |
591 | if (nip) |
592 | *nip = ni; |
593 | |
594 | return (error); |
595 | } |
596 | |
597 | /* |
598 | * sysctl_query -- The auto-discovery engine. Copies out the structs |
599 | * describing nodes under the given node and handles overlay trees. |
600 | */ |
601 | int |
602 | sysctl_query(SYSCTLFN_ARGS) |
603 | { |
604 | int error, ni, elim, v; |
605 | size_t out, left, t; |
606 | const struct sysctlnode *enode, *onode; |
607 | struct sysctlnode qnode; |
608 | |
609 | KASSERT(rw_lock_held(&sysctl_treelock)); |
610 | |
611 | if (SYSCTL_VERS(rnode->sysctl_flags) != SYSCTL_VERSION) { |
612 | printf("sysctl_query: rnode %p wrong version\n" , rnode); |
613 | return (EINVAL); |
614 | } |
615 | |
616 | if (SYSCTL_TYPE(rnode->sysctl_flags) != CTLTYPE_NODE) |
617 | return (ENOTDIR); |
618 | if (namelen != 1 || name[0] != CTL_QUERY) |
619 | return (EINVAL); |
620 | |
621 | error = 0; |
622 | out = 0; |
623 | left = *oldlenp; |
624 | elim = 0; |
625 | enode = NULL; |
626 | |
627 | /* |
628 | * translate the given request to a current node |
629 | */ |
630 | error = sysctl_cvt_in(l, &v, newp, newlen, &qnode); |
631 | if (error) |
632 | return (error); |
633 | |
634 | /* |
635 | * if the request specifies a version, check it |
636 | */ |
637 | if (qnode.sysctl_ver != 0) { |
638 | enode = rnode; |
639 | if (qnode.sysctl_ver != enode->sysctl_ver && |
640 | qnode.sysctl_ver != sysctl_rootof(enode)->sysctl_ver) |
641 | return (EINVAL); |
642 | } |
643 | |
644 | /* |
645 | * process has overlay tree |
646 | */ |
647 | if (l && l->l_proc->p_emul->e_sysctlovly) { |
648 | enode = l->l_proc->p_emul->e_sysctlovly; |
649 | elim = (name - oname); |
650 | error = sysctl_locate(l, oname, elim, &enode, NULL); |
651 | if (error == 0) { |
652 | /* ah, found parent in overlay */ |
653 | elim = enode->sysctl_clen; |
654 | enode = enode->sysctl_child; |
655 | } else { |
656 | error = 0; |
657 | elim = 0; |
658 | enode = NULL; |
659 | } |
660 | } |
661 | |
662 | for (ni = 0; ni < rnode->sysctl_clen; ni++) { |
663 | onode = &rnode->sysctl_child[ni]; |
664 | if (enode && enode->sysctl_num == onode->sysctl_num) { |
665 | if (SYSCTL_TYPE(enode->sysctl_flags) != CTLTYPE_NODE) |
666 | onode = enode; |
667 | if (--elim > 0) |
668 | enode++; |
669 | else |
670 | enode = NULL; |
671 | } |
672 | error = sysctl_cvt_out(l, v, onode, oldp, left, &t); |
673 | if (error) |
674 | return (error); |
675 | if (oldp != NULL) |
676 | oldp = (char*)oldp + t; |
677 | out += t; |
678 | left -= MIN(left, t); |
679 | } |
680 | |
681 | /* |
682 | * overlay trees *MUST* be entirely consumed |
683 | */ |
684 | KASSERT(enode == NULL); |
685 | |
686 | *oldlenp = out; |
687 | |
688 | return (error); |
689 | } |
690 | |
691 | /* |
692 | * sysctl_create -- Adds a node (the description of which is taken |
693 | * from newp) to the tree, returning a copy of it in the space pointed |
694 | * to by oldp. In the event that the requested slot is already taken |
695 | * (either by name or by number), the offending node is returned |
696 | * instead. Yes, this is complex, but we want to make sure everything |
697 | * is proper. |
698 | */ |
699 | #ifdef SYSCTL_DEBUG_CREATE |
700 | int _sysctl_create(SYSCTLFN_ARGS); |
701 | int |
702 | _sysctl_create(SYSCTLFN_ARGS) |
703 | #else |
704 | int |
705 | sysctl_create(SYSCTLFN_ARGS) |
706 | #endif |
707 | { |
708 | struct sysctlnode nnode, *node, *pnode; |
709 | int error, ni, at, nm, type, nsz, sz, flags, anum, v; |
710 | void *own; |
711 | |
712 | KASSERT(rw_write_held(&sysctl_treelock)); |
713 | |
714 | error = 0; |
715 | own = NULL; |
716 | anum = -1; |
717 | |
718 | if (SYSCTL_VERS(rnode->sysctl_flags) != SYSCTL_VERSION) { |
719 | printf("sysctl_create: rnode %p wrong version\n" , rnode); |
720 | return (EINVAL); |
721 | } |
722 | |
723 | if (namelen != 1 || (name[namelen - 1] != CTL_CREATE |
724 | #if NKSYMS > 0 |
725 | && name[namelen - 1] != CTL_CREATESYM |
726 | #endif /* NKSYMS > 0 */ |
727 | )) |
728 | return (EINVAL); |
729 | |
730 | /* |
731 | * processes can only add nodes at securelevel 0, must be |
732 | * root, and can't add nodes to a parent that's not writeable |
733 | */ |
734 | if (l != NULL) { |
735 | #ifndef SYSCTL_DISALLOW_CREATE |
736 | error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_SYSCTL, |
737 | KAUTH_REQ_SYSTEM_SYSCTL_ADD, NULL, NULL, NULL); |
738 | if (error) |
739 | return (error); |
740 | if (!(rnode->sysctl_flags & CTLFLAG_READWRITE)) |
741 | #endif /* SYSCTL_DISALLOW_CREATE */ |
742 | return (EPERM); |
743 | } |
744 | |
745 | /* |
746 | * nothing can add a node if: |
747 | * we've finished initial set up of this tree and |
748 | * (the tree itself is not writeable or |
749 | * the entire sysctl system is not writeable) |
750 | */ |
751 | if ((sysctl_rootof(rnode)->sysctl_flags & CTLFLAG_PERMANENT) && |
752 | (!(sysctl_rootof(rnode)->sysctl_flags & CTLFLAG_READWRITE) || |
753 | !(sysctl_root.sysctl_flags & CTLFLAG_READWRITE))) |
754 | return (EPERM); |
755 | |
756 | /* |
757 | * it must be a "node", not a "int" or something |
758 | */ |
759 | if (SYSCTL_TYPE(rnode->sysctl_flags) != CTLTYPE_NODE) |
760 | return (ENOTDIR); |
761 | if (rnode->sysctl_flags & CTLFLAG_ALIAS) { |
762 | printf("sysctl_create: attempt to add node to aliased " |
763 | "node %p\n" , rnode); |
764 | return (EINVAL); |
765 | } |
766 | pnode = __UNCONST(rnode); /* we are adding children to this node */ |
767 | |
768 | if (newp == NULL) |
769 | return (EINVAL); |
770 | error = sysctl_cvt_in(l, &v, newp, newlen, &nnode); |
771 | if (error) |
772 | return (error); |
773 | |
774 | /* |
775 | * nodes passed in don't *have* parents |
776 | */ |
777 | if (nnode.sysctl_parent != NULL) |
778 | return (EINVAL); |
779 | |
780 | /* |
781 | * if we are indeed adding it, it should be a "good" name and |
782 | * number |
783 | */ |
784 | nm = nnode.sysctl_num; |
785 | #if NKSYMS > 0 |
786 | if (nm == CTL_CREATESYM) |
787 | nm = CTL_CREATE; |
788 | #endif /* NKSYMS > 0 */ |
789 | if (nm < 0 && nm != CTL_CREATE) |
790 | return (EINVAL); |
791 | |
792 | /* |
793 | * the name can't start with a digit |
794 | */ |
795 | if (nnode.sysctl_name[0] >= '0' && |
796 | nnode.sysctl_name[0] <= '9') |
797 | return (EINVAL); |
798 | |
799 | /* |
800 | * the name must be only alphanumerics or - or _, longer than |
801 | * 0 bytes and less than SYSCTL_NAMELEN |
802 | */ |
803 | nsz = 0; |
804 | while (nsz < SYSCTL_NAMELEN && nnode.sysctl_name[nsz] != '\0') { |
805 | if ((nnode.sysctl_name[nsz] >= '0' && |
806 | nnode.sysctl_name[nsz] <= '9') || |
807 | (nnode.sysctl_name[nsz] >= 'A' && |
808 | nnode.sysctl_name[nsz] <= 'Z') || |
809 | (nnode.sysctl_name[nsz] >= 'a' && |
810 | nnode.sysctl_name[nsz] <= 'z') || |
811 | nnode.sysctl_name[nsz] == '-' || |
812 | nnode.sysctl_name[nsz] == '_') |
813 | nsz++; |
814 | else |
815 | return (EINVAL); |
816 | } |
817 | if (nsz == 0 || nsz == SYSCTL_NAMELEN) |
818 | return (EINVAL); |
819 | |
820 | /* |
821 | * various checks revolve around size vs type, etc |
822 | */ |
823 | type = SYSCTL_TYPE(nnode.sysctl_flags); |
824 | flags = SYSCTL_FLAGS(nnode.sysctl_flags); |
825 | sz = nnode.sysctl_size; |
826 | |
827 | /* |
828 | * find out if there's a collision, and if so, let the caller |
829 | * know what they collided with |
830 | */ |
831 | node = pnode->sysctl_child; |
832 | at = 0; |
833 | if (node) { |
834 | if ((flags | node->sysctl_flags) & CTLFLAG_ANYNUMBER) |
835 | /* No siblings for a CTLFLAG_ANYNUMBER node */ |
836 | return EINVAL; |
837 | for (ni = 0; ni < pnode->sysctl_clen; ni++) { |
838 | if (nm == node[ni].sysctl_num || |
839 | strcmp(nnode.sysctl_name, node[ni].sysctl_name) == 0) { |
840 | /* |
841 | * ignore error here, since we |
842 | * are already fixed on EEXIST |
843 | */ |
844 | (void)sysctl_cvt_out(l, v, &node[ni], oldp, |
845 | *oldlenp, oldlenp); |
846 | return (EEXIST); |
847 | } |
848 | if (nm > node[ni].sysctl_num) |
849 | at++; |
850 | } |
851 | } |
852 | |
853 | /* |
854 | * use sysctl_ver to add to the tree iff it hasn't changed |
855 | */ |
856 | if (nnode.sysctl_ver != 0) { |
857 | /* |
858 | * a specified value must match either the parent |
859 | * node's version or the root node's version |
860 | */ |
861 | if (nnode.sysctl_ver != sysctl_rootof(rnode)->sysctl_ver && |
862 | nnode.sysctl_ver != rnode->sysctl_ver) { |
863 | return (EINVAL); |
864 | } |
865 | } |
866 | |
867 | /* |
868 | * only the kernel can assign functions to entries |
869 | */ |
870 | if (l != NULL && nnode.sysctl_func != NULL) |
871 | return (EPERM); |
872 | |
873 | /* |
874 | * only the kernel can create permanent entries, and only then |
875 | * before the kernel is finished setting itself up |
876 | */ |
877 | if (l != NULL && (flags & ~SYSCTL_USERFLAGS)) |
878 | return (EPERM); |
879 | if ((flags & CTLFLAG_PERMANENT) & |
880 | (sysctl_root.sysctl_flags & CTLFLAG_PERMANENT)) |
881 | return (EPERM); |
882 | if ((flags & (CTLFLAG_OWNDATA | CTLFLAG_IMMEDIATE)) == |
883 | (CTLFLAG_OWNDATA | CTLFLAG_IMMEDIATE)) |
884 | return (EINVAL); |
885 | if ((flags & CTLFLAG_IMMEDIATE) && |
886 | type != CTLTYPE_INT && type != CTLTYPE_QUAD && type != CTLTYPE_BOOL) |
887 | return (EINVAL); |
888 | |
889 | /* |
890 | * check size, or set it if unset and we can figure it out. |
891 | * kernel created nodes are allowed to have a function instead |
892 | * of a size (or a data pointer). |
893 | */ |
894 | switch (type) { |
895 | case CTLTYPE_NODE: |
896 | /* |
897 | * only *i* can assert the size of a node |
898 | */ |
899 | if (flags & CTLFLAG_ALIAS) { |
900 | anum = nnode.sysctl_alias; |
901 | if (anum < 0) |
902 | return (EINVAL); |
903 | nnode.sysctl_alias = 0; |
904 | } |
905 | if (sz != 0 || nnode.sysctl_data != NULL) |
906 | return (EINVAL); |
907 | if (nnode.sysctl_csize != 0 || |
908 | nnode.sysctl_clen != 0 || |
909 | nnode.sysctl_child != 0) |
910 | return (EINVAL); |
911 | if (flags & CTLFLAG_OWNDATA) |
912 | return (EINVAL); |
913 | sz = sizeof(struct sysctlnode); |
914 | break; |
915 | case CTLTYPE_INT: |
916 | /* |
917 | * since an int is an int, if the size is not given or |
918 | * is wrong, we can "int-uit" it. |
919 | */ |
920 | if (sz != 0 && sz != sizeof(int)) |
921 | return (EINVAL); |
922 | sz = sizeof(int); |
923 | break; |
924 | case CTLTYPE_STRING: |
925 | /* |
926 | * strings are a little more tricky |
927 | */ |
928 | if (sz == 0) { |
929 | if (l == NULL) { |
930 | if (nnode.sysctl_func == NULL) { |
931 | if (nnode.sysctl_data == NULL) |
932 | return (EINVAL); |
933 | else |
934 | sz = strlen(nnode.sysctl_data) + |
935 | 1; |
936 | } |
937 | } else if (nnode.sysctl_data == NULL && |
938 | flags & CTLFLAG_OWNDATA) { |
939 | return (EINVAL); |
940 | } else { |
941 | char *vp, *e; |
942 | size_t s; |
943 | |
944 | /* |
945 | * we want a rough idea of what the |
946 | * size is now |
947 | */ |
948 | vp = malloc(PAGE_SIZE, M_SYSCTLDATA, |
949 | M_WAITOK|M_CANFAIL); |
950 | if (vp == NULL) |
951 | return (ENOMEM); |
952 | e = nnode.sysctl_data; |
953 | do { |
954 | error = copyinstr(e, vp, PAGE_SIZE, &s); |
955 | if (error) { |
956 | if (error != ENAMETOOLONG) { |
957 | free(vp, M_SYSCTLDATA); |
958 | return (error); |
959 | } |
960 | e += PAGE_SIZE; |
961 | if ((e - 32 * PAGE_SIZE) > |
962 | (char*)nnode.sysctl_data) { |
963 | free(vp, M_SYSCTLDATA); |
964 | return (ERANGE); |
965 | } |
966 | } |
967 | } while (error != 0); |
968 | sz = s + (e - (char*)nnode.sysctl_data); |
969 | free(vp, M_SYSCTLDATA); |
970 | } |
971 | } |
972 | break; |
973 | case CTLTYPE_QUAD: |
974 | if (sz != 0 && sz != sizeof(u_quad_t)) |
975 | return (EINVAL); |
976 | sz = sizeof(u_quad_t); |
977 | break; |
978 | case CTLTYPE_BOOL: |
979 | /* |
980 | * since an bool is an bool, if the size is not given or |
981 | * is wrong, we can "intuit" it. |
982 | */ |
983 | if (sz != 0 && sz != sizeof(bool)) |
984 | return (EINVAL); |
985 | sz = sizeof(bool); |
986 | break; |
987 | case CTLTYPE_STRUCT: |
988 | if (sz == 0) { |
989 | if (l != NULL || nnode.sysctl_func == NULL) |
990 | return (EINVAL); |
991 | if (flags & CTLFLAG_OWNDATA) |
992 | return (EINVAL); |
993 | } |
994 | break; |
995 | default: |
996 | return (EINVAL); |
997 | } |
998 | |
999 | /* |
1000 | * at this point, if sz is zero, we *must* have a |
1001 | * function to go with it and we can't own it. |
1002 | */ |
1003 | |
1004 | /* |
1005 | * l ptr own |
1006 | * 0 0 0 -> EINVAL (if no func) |
1007 | * 0 0 1 -> own |
1008 | * 0 1 0 -> kptr |
1009 | * 0 1 1 -> kptr |
1010 | * 1 0 0 -> EINVAL |
1011 | * 1 0 1 -> own |
1012 | * 1 1 0 -> kptr, no own (fault on lookup) |
1013 | * 1 1 1 -> uptr, own |
1014 | */ |
1015 | if (type != CTLTYPE_NODE) { |
1016 | if (sz != 0) { |
1017 | if (flags & CTLFLAG_OWNDATA) { |
1018 | own = malloc(sz, M_SYSCTLDATA, |
1019 | M_WAITOK|M_CANFAIL); |
1020 | if (own == NULL) |
1021 | return ENOMEM; |
1022 | if (nnode.sysctl_data == NULL) |
1023 | memset(own, 0, sz); |
1024 | else { |
1025 | error = sysctl_copyin(l, |
1026 | nnode.sysctl_data, own, sz); |
1027 | if (error != 0) { |
1028 | free(own, M_SYSCTLDATA); |
1029 | return (error); |
1030 | } |
1031 | } |
1032 | } else if ((nnode.sysctl_data != NULL) && |
1033 | !(flags & CTLFLAG_IMMEDIATE)) { |
1034 | #if NKSYMS > 0 |
1035 | if (name[namelen - 1] == CTL_CREATESYM) { |
1036 | char symname[128]; /* XXX enough? */ |
1037 | u_long symaddr; |
1038 | size_t symlen; |
1039 | |
1040 | error = sysctl_copyinstr(l, |
1041 | nnode.sysctl_data, symname, |
1042 | sizeof(symname), &symlen); |
1043 | if (error) |
1044 | return (error); |
1045 | error = ksyms_getval(NULL, symname, |
1046 | &symaddr, KSYMS_EXTERN); |
1047 | if (error) |
1048 | return (error); /* EINVAL? */ |
1049 | nnode.sysctl_data = (void*)symaddr; |
1050 | } |
1051 | #endif /* NKSYMS > 0 */ |
1052 | /* |
1053 | * Ideally, we'd like to verify here |
1054 | * that this address is acceptable, |
1055 | * but... |
1056 | * |
1057 | * - it might be valid now, only to |
1058 | * become invalid later |
1059 | * |
1060 | * - it might be invalid only for the |
1061 | * moment and valid later |
1062 | * |
1063 | * - or something else. |
1064 | * |
1065 | * Since we can't get a good answer, |
1066 | * we'll just accept the address as |
1067 | * given, and fault on individual |
1068 | * lookups. |
1069 | */ |
1070 | } |
1071 | } else if (nnode.sysctl_func == NULL) |
1072 | return (EINVAL); |
1073 | } |
1074 | |
1075 | /* |
1076 | * a process can't assign a function to a node, and the kernel |
1077 | * can't create a node that has no function or data. |
1078 | * (XXX somewhat redundant check) |
1079 | */ |
1080 | if (l != NULL || nnode.sysctl_func == NULL) { |
1081 | if (type != CTLTYPE_NODE && |
1082 | nnode.sysctl_data == NULL && |
1083 | !(flags & CTLFLAG_IMMEDIATE) && |
1084 | own == NULL) |
1085 | return (EINVAL); |
1086 | } |
1087 | |
1088 | #ifdef SYSCTL_DISALLOW_KWRITE |
1089 | /* |
1090 | * a process can't create a writable node unless it refers to |
1091 | * new data. |
1092 | */ |
1093 | if (l != NULL && own == NULL && type != CTLTYPE_NODE && |
1094 | (flags & CTLFLAG_READWRITE) != CTLFLAG_READONLY && |
1095 | !(flags & CTLFLAG_IMMEDIATE)) |
1096 | return (EPERM); |
1097 | #endif /* SYSCTL_DISALLOW_KWRITE */ |
1098 | |
1099 | /* |
1100 | * make sure there's somewhere to put the new stuff. |
1101 | */ |
1102 | if (pnode->sysctl_child == NULL) { |
1103 | if (flags & CTLFLAG_ANYNUMBER) |
1104 | error = sysctl_alloc(pnode, 1); |
1105 | else |
1106 | error = sysctl_alloc(pnode, 0); |
1107 | if (error) { |
1108 | if (own != NULL) |
1109 | free(own, M_SYSCTLDATA); |
1110 | return (error); |
1111 | } |
1112 | } |
1113 | node = pnode->sysctl_child; |
1114 | |
1115 | /* |
1116 | * no collisions, so pick a good dynamic number if we need to. |
1117 | */ |
1118 | if (nm == CTL_CREATE) { |
1119 | nm = ++sysctl_root.sysctl_num; |
1120 | for (ni = 0; ni < pnode->sysctl_clen; ni++) { |
1121 | if (nm == node[ni].sysctl_num) { |
1122 | nm++; |
1123 | ni = -1; |
1124 | } else if (nm > node[ni].sysctl_num) |
1125 | at = ni + 1; |
1126 | } |
1127 | } |
1128 | |
1129 | /* |
1130 | * oops...ran out of space |
1131 | */ |
1132 | if (pnode->sysctl_clen == pnode->sysctl_csize) { |
1133 | error = sysctl_realloc(pnode); |
1134 | if (error) { |
1135 | if (own != NULL) |
1136 | free(own, M_SYSCTLDATA); |
1137 | return (error); |
1138 | } |
1139 | node = pnode->sysctl_child; |
1140 | } |
1141 | |
1142 | /* |
1143 | * insert new node data |
1144 | */ |
1145 | if (at < pnode->sysctl_clen) { |
1146 | int t; |
1147 | |
1148 | /* |
1149 | * move the nodes that should come after the new one |
1150 | */ |
1151 | memmove(&node[at + 1], &node[at], |
1152 | (pnode->sysctl_clen - at) * sizeof(struct sysctlnode)); |
1153 | memset(&node[at], 0, sizeof(struct sysctlnode)); |
1154 | node[at].sysctl_parent = pnode; |
1155 | /* |
1156 | * and...reparent any children of any moved nodes |
1157 | */ |
1158 | for (ni = at; ni <= pnode->sysctl_clen; ni++) |
1159 | if (node[ni].sysctl_child != NULL) |
1160 | for (t = 0; t < node[ni].sysctl_csize; t++) |
1161 | node[ni].sysctl_child[t].sysctl_parent = |
1162 | &node[ni]; |
1163 | } |
1164 | node = &node[at]; |
1165 | pnode->sysctl_clen++; |
1166 | |
1167 | strlcpy(node->sysctl_name, nnode.sysctl_name, |
1168 | sizeof(node->sysctl_name)); |
1169 | node->sysctl_num = nm; |
1170 | node->sysctl_size = sz; |
1171 | node->sysctl_flags = SYSCTL_VERSION|type|flags; /* XXX other trees */ |
1172 | node->sysctl_csize = 0; |
1173 | node->sysctl_clen = 0; |
1174 | if (own) { |
1175 | node->sysctl_data = own; |
1176 | node->sysctl_flags |= CTLFLAG_OWNDATA; |
1177 | } else if (flags & CTLFLAG_ALIAS) { |
1178 | node->sysctl_alias = anum; |
1179 | } else if (flags & CTLFLAG_IMMEDIATE) { |
1180 | switch (type) { |
1181 | case CTLTYPE_BOOL: |
1182 | node->sysctl_bdata = nnode.sysctl_bdata; |
1183 | break; |
1184 | case CTLTYPE_INT: |
1185 | node->sysctl_idata = nnode.sysctl_idata; |
1186 | break; |
1187 | case CTLTYPE_QUAD: |
1188 | node->sysctl_qdata = nnode.sysctl_qdata; |
1189 | break; |
1190 | } |
1191 | } else { |
1192 | node->sysctl_data = nnode.sysctl_data; |
1193 | node->sysctl_flags &= ~CTLFLAG_OWNDATA; |
1194 | } |
1195 | node->sysctl_func = nnode.sysctl_func; |
1196 | node->sysctl_child = NULL; |
1197 | /* node->sysctl_parent should already be done */ |
1198 | |
1199 | /* |
1200 | * update "version" on path to "root" |
1201 | */ |
1202 | for (; rnode->sysctl_parent != NULL; rnode = rnode->sysctl_parent) |
1203 | ; |
1204 | pnode = node; |
1205 | for (nm = rnode->sysctl_ver + 1; pnode != NULL; |
1206 | pnode = pnode->sysctl_parent) |
1207 | pnode->sysctl_ver = nm; |
1208 | |
1209 | /* If this fails, the node is already added - the user won't know! */ |
1210 | error = sysctl_cvt_out(l, v, node, oldp, *oldlenp, oldlenp); |
1211 | |
1212 | return (error); |
1213 | } |
1214 | |
1215 | /* |
1216 | * ******************************************************************** |
1217 | * A wrapper around sysctl_create() that prints the thing we're trying |
1218 | * to add. |
1219 | * ******************************************************************** |
1220 | */ |
1221 | #ifdef SYSCTL_DEBUG_CREATE |
1222 | int |
1223 | sysctl_create(SYSCTLFN_ARGS) |
1224 | { |
1225 | const struct sysctlnode *node; |
1226 | int k, rc, ni, nl = namelen + (name - oname); |
1227 | |
1228 | node = newp; |
1229 | |
1230 | printf("namelen %d (" , nl); |
1231 | for (ni = 0; ni < nl - 1; ni++) |
1232 | printf(" %d" , oname[ni]); |
1233 | printf(" %d )\t[%s]\tflags %08x (%08x %d %zu)\n" , |
1234 | k = node->sysctl_num, |
1235 | node->sysctl_name, |
1236 | node->sysctl_flags, |
1237 | SYSCTL_FLAGS(node->sysctl_flags), |
1238 | SYSCTL_TYPE(node->sysctl_flags), |
1239 | node->sysctl_size); |
1240 | |
1241 | node = rnode; |
1242 | rc = _sysctl_create(SYSCTLFN_CALL(rnode)); |
1243 | |
1244 | printf("sysctl_create(" ); |
1245 | for (ni = 0; ni < nl - 1; ni++) |
1246 | printf(" %d" , oname[ni]); |
1247 | printf(" %d ) returned %d\n" , k, rc); |
1248 | |
1249 | return (rc); |
1250 | } |
1251 | #endif /* SYSCTL_DEBUG_CREATE */ |
1252 | |
1253 | /* |
1254 | * sysctl_destroy -- Removes a node (as described by newp) from the |
1255 | * given tree, returning (if successful) a copy of the dead node in |
1256 | * oldp. Since we're removing stuff, there's not much to check. |
1257 | */ |
1258 | int |
1259 | sysctl_destroy(SYSCTLFN_ARGS) |
1260 | { |
1261 | struct sysctlnode *node, *pnode, onode, nnode; |
1262 | int ni, error, v; |
1263 | |
1264 | KASSERT(rw_write_held(&sysctl_treelock)); |
1265 | |
1266 | if (SYSCTL_VERS(rnode->sysctl_flags) != SYSCTL_VERSION) { |
1267 | printf("sysctl_destroy: rnode %p wrong version\n" , rnode); |
1268 | return (EINVAL); |
1269 | } |
1270 | |
1271 | error = 0; |
1272 | |
1273 | if (namelen != 1 || name[namelen - 1] != CTL_DESTROY) |
1274 | return (EINVAL); |
1275 | |
1276 | /* |
1277 | * processes can only destroy nodes at securelevel 0, must be |
1278 | * root, and can't remove nodes from a parent that's not |
1279 | * writeable |
1280 | */ |
1281 | if (l != NULL) { |
1282 | #ifndef SYSCTL_DISALLOW_CREATE |
1283 | error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_SYSCTL, |
1284 | KAUTH_REQ_SYSTEM_SYSCTL_DELETE, NULL, NULL, NULL); |
1285 | if (error) |
1286 | return (error); |
1287 | if (!(rnode->sysctl_flags & CTLFLAG_READWRITE)) |
1288 | #endif /* SYSCTL_DISALLOW_CREATE */ |
1289 | return (EPERM); |
1290 | } |
1291 | |
1292 | /* |
1293 | * nothing can remove a node if: |
1294 | * the node is permanent (checked later) or |
1295 | * the tree itself is not writeable or |
1296 | * the entire sysctl system is not writeable |
1297 | * |
1298 | * note that we ignore whether setup is complete or not, |
1299 | * because these rules always apply. |
1300 | */ |
1301 | if (!(sysctl_rootof(rnode)->sysctl_flags & CTLFLAG_READWRITE) || |
1302 | !(sysctl_root.sysctl_flags & CTLFLAG_READWRITE)) |
1303 | return (EPERM); |
1304 | |
1305 | if (newp == NULL) |
1306 | return (EINVAL); |
1307 | error = sysctl_cvt_in(l, &v, newp, newlen, &nnode); |
1308 | if (error) |
1309 | return (error); |
1310 | memset(&onode, 0, sizeof(struct sysctlnode)); |
1311 | |
1312 | node = rnode->sysctl_child; |
1313 | for (ni = 0; ni < rnode->sysctl_clen; ni++) { |
1314 | if (nnode.sysctl_num == node[ni].sysctl_num) { |
1315 | /* |
1316 | * if name specified, must match |
1317 | */ |
1318 | if (nnode.sysctl_name[0] != '\0' && |
1319 | strcmp(nnode.sysctl_name, node[ni].sysctl_name)) |
1320 | continue; |
1321 | /* |
1322 | * if version specified, must match |
1323 | */ |
1324 | if (nnode.sysctl_ver != 0 && |
1325 | nnode.sysctl_ver != node[ni].sysctl_ver) |
1326 | continue; |
1327 | /* |
1328 | * this must be the one |
1329 | */ |
1330 | break; |
1331 | } |
1332 | } |
1333 | if (ni == rnode->sysctl_clen) |
1334 | return (ENOENT); |
1335 | node = &node[ni]; |
1336 | pnode = node->sysctl_parent; |
1337 | |
1338 | /* |
1339 | * if the kernel says permanent, it is, so there. nyah. |
1340 | */ |
1341 | if (SYSCTL_FLAGS(node->sysctl_flags) & CTLFLAG_PERMANENT) |
1342 | return (EPERM); |
1343 | |
1344 | /* |
1345 | * can't delete non-empty nodes |
1346 | */ |
1347 | if (SYSCTL_TYPE(node->sysctl_flags) == CTLTYPE_NODE && |
1348 | node->sysctl_clen != 0) |
1349 | return (ENOTEMPTY); |
1350 | |
1351 | /* |
1352 | * if the node "owns" data, release it now |
1353 | */ |
1354 | if (node->sysctl_flags & CTLFLAG_OWNDATA) { |
1355 | if (node->sysctl_data != NULL) |
1356 | free(node->sysctl_data, M_SYSCTLDATA); |
1357 | node->sysctl_data = NULL; |
1358 | } |
1359 | if (node->sysctl_flags & CTLFLAG_OWNDESC) { |
1360 | if (node->sysctl_desc != NULL) |
1361 | /*XXXUNCONST*/ |
1362 | free(__UNCONST(node->sysctl_desc), M_SYSCTLDATA); |
1363 | node->sysctl_desc = NULL; |
1364 | } |
1365 | |
1366 | /* |
1367 | * if the node to be removed is not the last one on the list, |
1368 | * move the remaining nodes up, and reparent any grandchildren |
1369 | */ |
1370 | onode = *node; |
1371 | if (ni < pnode->sysctl_clen - 1) { |
1372 | int t; |
1373 | |
1374 | memmove(&pnode->sysctl_child[ni], &pnode->sysctl_child[ni + 1], |
1375 | (pnode->sysctl_clen - ni - 1) * |
1376 | sizeof(struct sysctlnode)); |
1377 | for (; ni < pnode->sysctl_clen - 1; ni++) |
1378 | if (SYSCTL_TYPE(pnode->sysctl_child[ni].sysctl_flags) == |
1379 | CTLTYPE_NODE) |
1380 | for (t = 0; |
1381 | t < pnode->sysctl_child[ni].sysctl_clen; |
1382 | t++) |
1383 | pnode->sysctl_child[ni].sysctl_child[t]. |
1384 | sysctl_parent = |
1385 | &pnode->sysctl_child[ni]; |
1386 | ni = pnode->sysctl_clen - 1; |
1387 | node = &pnode->sysctl_child[ni]; |
1388 | } |
1389 | |
1390 | /* |
1391 | * reset the space we just vacated |
1392 | */ |
1393 | memset(node, 0, sizeof(struct sysctlnode)); |
1394 | node->sysctl_parent = pnode; |
1395 | pnode->sysctl_clen--; |
1396 | |
1397 | /* |
1398 | * if this parent just lost its last child, nuke the creche |
1399 | */ |
1400 | if (pnode->sysctl_clen == 0) { |
1401 | free(pnode->sysctl_child, M_SYSCTLNODE); |
1402 | pnode->sysctl_csize = 0; |
1403 | pnode->sysctl_child = NULL; |
1404 | } |
1405 | |
1406 | /* |
1407 | * update "version" on path to "root" |
1408 | */ |
1409 | for (; rnode->sysctl_parent != NULL; rnode = rnode->sysctl_parent) |
1410 | ; |
1411 | for (ni = rnode->sysctl_ver + 1; pnode != NULL; |
1412 | pnode = pnode->sysctl_parent) |
1413 | pnode->sysctl_ver = ni; |
1414 | |
1415 | error = sysctl_cvt_out(l, v, &onode, oldp, *oldlenp, oldlenp); |
1416 | |
1417 | return (error); |
1418 | } |
1419 | |
1420 | /* |
1421 | * sysctl_lookup -- Handles copyin/copyout of new and old values. |
1422 | * Partial reads are globally allowed. Only root can write to things |
1423 | * unless the node says otherwise. |
1424 | */ |
1425 | int |
1426 | sysctl_lookup(SYSCTLFN_ARGS) |
1427 | { |
1428 | int error, rw; |
1429 | size_t sz, len; |
1430 | void *d; |
1431 | |
1432 | KASSERT(rw_lock_held(&sysctl_treelock)); |
1433 | |
1434 | if (SYSCTL_VERS(rnode->sysctl_flags) != SYSCTL_VERSION) { |
1435 | printf("%s: rnode %p wrong version\n" , __func__, rnode); |
1436 | return EINVAL; |
1437 | } |
1438 | |
1439 | if (newlen == 0) |
1440 | newp = NULL; |
1441 | |
1442 | error = 0; |
1443 | |
1444 | /* |
1445 | * you can't "look up" a node. you can "query" it, but you |
1446 | * can't "look it up". |
1447 | */ |
1448 | if (SYSCTL_TYPE(rnode->sysctl_flags) == CTLTYPE_NODE || namelen != 0) { |
1449 | DPRINTF(("%s: can't lookup a node\n" , __func__)); |
1450 | return EINVAL; |
1451 | } |
1452 | |
1453 | /* |
1454 | * some nodes are private, so only root can look into them. |
1455 | */ |
1456 | if (l != NULL && (rnode->sysctl_flags & CTLFLAG_PRIVATE) && |
1457 | (error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_SYSCTL, |
1458 | KAUTH_REQ_SYSTEM_SYSCTL_PRVT, NULL, NULL, NULL)) != 0) { |
1459 | DPRINTF(("%s: private node\n" , __func__)); |
1460 | return error; |
1461 | } |
1462 | |
1463 | /* |
1464 | * if a node wants to be writable according to different rules |
1465 | * other than "only root can write to stuff unless a flag is |
1466 | * set", then it needs its own function which should have been |
1467 | * called and not us. |
1468 | */ |
1469 | if (l != NULL && newp != NULL && |
1470 | !(rnode->sysctl_flags & CTLFLAG_ANYWRITE) && |
1471 | (error = kauth_authorize_system(l->l_cred, |
1472 | KAUTH_SYSTEM_SYSCTL, KAUTH_REQ_SYSTEM_SYSCTL_MODIFY, NULL, NULL, |
1473 | NULL)) != 0) { |
1474 | DPRINTF(("%s: can't modify\n" , __func__)); |
1475 | return error; |
1476 | } |
1477 | |
1478 | /* |
1479 | * is this node supposedly writable? |
1480 | */ |
1481 | rw = (rnode->sysctl_flags & CTLFLAG_READWRITE) ? 1 : 0; |
1482 | |
1483 | /* |
1484 | * it appears not to be writable at this time, so if someone |
1485 | * tried to write to it, we must tell them to go away |
1486 | */ |
1487 | if (!rw && newp != NULL) { |
1488 | DPRINTF(("%s: not writable\n" , __func__)); |
1489 | return EPERM; |
1490 | } |
1491 | |
1492 | /* |
1493 | * step one, copy out the stuff we have presently |
1494 | */ |
1495 | if (rnode->sysctl_flags & CTLFLAG_IMMEDIATE) { |
1496 | /* |
1497 | * note that we discard const here because we are |
1498 | * modifying the contents of the node (which is okay |
1499 | * because it's ours) |
1500 | * |
1501 | * It also doesn't matter which field of the union we pick. |
1502 | */ |
1503 | d = __UNCONST(&rnode->sysctl_qdata); |
1504 | } else |
1505 | d = rnode->sysctl_data; |
1506 | |
1507 | if (SYSCTL_TYPE(rnode->sysctl_flags) == CTLTYPE_STRING) |
1508 | sz = strlen(d) + 1; /* XXX@@@ possible fault here */ |
1509 | else |
1510 | sz = rnode->sysctl_size; |
1511 | if (oldp != NULL) { |
1512 | error = sysctl_copyout(l, d, oldp, MIN(sz, *oldlenp)); |
1513 | if (error) { |
1514 | DPRINTF(("%s: bad copyout %d\n" , __func__, error)); |
1515 | return error; |
1516 | } |
1517 | } |
1518 | *oldlenp = sz; |
1519 | |
1520 | /* |
1521 | * are we done? |
1522 | */ |
1523 | if (newp == NULL) |
1524 | return 0; |
1525 | |
1526 | /* |
1527 | * hmm...not done. must now "copy in" new value. re-adjust |
1528 | * sz to maximum value (strings are "weird"). |
1529 | */ |
1530 | sz = rnode->sysctl_size; |
1531 | switch (SYSCTL_TYPE(rnode->sysctl_flags)) { |
1532 | case CTLTYPE_BOOL: { |
1533 | bool tmp; |
1534 | /* |
1535 | * these data must be *exactly* the same size coming |
1536 | * in. bool may only be true or false. |
1537 | */ |
1538 | if (newlen != sz) { |
1539 | DPRINTF(("%s: bad size %zu != %zu\n" , __func__, newlen, |
1540 | sz)); |
1541 | return EINVAL; |
1542 | } |
1543 | error = sysctl_copyin(l, newp, &tmp, sz); |
1544 | if (error) |
1545 | break; |
1546 | if (tmp != true && tmp != false) { |
1547 | DPRINTF(("%s: tmp %d\n" , __func__, tmp)); |
1548 | return EINVAL; |
1549 | } |
1550 | *(bool *)d = tmp; |
1551 | break; |
1552 | } |
1553 | case CTLTYPE_INT: |
1554 | case CTLTYPE_QUAD: |
1555 | case CTLTYPE_STRUCT: |
1556 | /* |
1557 | * these data must be *exactly* the same size coming |
1558 | * in. |
1559 | */ |
1560 | if (newlen != sz) |
1561 | goto bad_size; |
1562 | error = sysctl_copyin(l, newp, d, sz); |
1563 | rnd_add_data(NULL, d, sz, 0); |
1564 | break; |
1565 | case CTLTYPE_STRING: { |
1566 | /* |
1567 | * strings, on the other hand, can be shorter, and we |
1568 | * let userland be sloppy about the trailing nul. |
1569 | */ |
1570 | char *newbuf; |
1571 | |
1572 | /* |
1573 | * too much new string? |
1574 | */ |
1575 | if (newlen > sz) |
1576 | goto bad_size; |
1577 | |
1578 | /* |
1579 | * temporary copy of new inbound string |
1580 | */ |
1581 | len = MIN(sz, newlen); |
1582 | newbuf = malloc(len, M_SYSCTLDATA, M_WAITOK|M_CANFAIL); |
1583 | if (newbuf == NULL) { |
1584 | DPRINTF(("%s: oomem %zu\n" , __func__, len)); |
1585 | return ENOMEM; |
1586 | } |
1587 | error = sysctl_copyin(l, newp, newbuf, len); |
1588 | if (error) { |
1589 | free(newbuf, M_SYSCTLDATA); |
1590 | DPRINTF(("%s: copyin %d\n" , __func__, error)); |
1591 | return error; |
1592 | } |
1593 | |
1594 | /* |
1595 | * did they NUL terminate it, or do we have space |
1596 | * left to do it ourselves? |
1597 | */ |
1598 | if (newbuf[len - 1] != '\0' && len == sz) { |
1599 | free(newbuf, M_SYSCTLDATA); |
1600 | DPRINTF(("%s: string too long\n" , __func__)); |
1601 | return EINVAL; |
1602 | } |
1603 | |
1604 | /* |
1605 | * looks good, so pop it into place and zero the rest. |
1606 | */ |
1607 | if (len > 0) { |
1608 | memcpy(d, newbuf, len); |
1609 | rnd_add_data(NULL, d, len, 0); |
1610 | } |
1611 | if (sz != len) |
1612 | memset((char*)d + len, 0, sz - len); |
1613 | free(newbuf, M_SYSCTLDATA); |
1614 | break; |
1615 | } |
1616 | default: |
1617 | DPRINTF(("%s: bad type\n" , __func__)); |
1618 | return EINVAL; |
1619 | } |
1620 | if (error) { |
1621 | DPRINTF(("%s: copyin %d\n" , __func__, error)); |
1622 | } |
1623 | |
1624 | return error; |
1625 | |
1626 | bad_size: |
1627 | DPRINTF(("%s: bad size %zu > %zu\n" , __func__, newlen, sz)); |
1628 | return EINVAL; |
1629 | } |
1630 | |
1631 | /* |
1632 | * sysctl_mmap -- Dispatches sysctl mmap requests to those nodes that |
1633 | * purport to handle it. This interface isn't fully fleshed out yet, |
1634 | * unfortunately. |
1635 | */ |
1636 | static int |
1637 | sysctl_mmap(SYSCTLFN_ARGS) |
1638 | { |
1639 | const struct sysctlnode *node; |
1640 | struct sysctlnode nnode; |
1641 | int error; |
1642 | int sysctl_num; |
1643 | |
1644 | if (SYSCTL_VERS(rnode->sysctl_flags) != SYSCTL_VERSION) { |
1645 | printf("sysctl_mmap: rnode %p wrong version\n" , rnode); |
1646 | return (EINVAL); |
1647 | } |
1648 | |
1649 | /* |
1650 | * let's just pretend that didn't happen, m'kay? |
1651 | */ |
1652 | if (l == NULL) |
1653 | return (EPERM); |
1654 | |
1655 | /* |
1656 | * is this a sysctlnode description of an mmap request? |
1657 | */ |
1658 | if (newp == NULL || newlen != sizeof(struct sysctlnode)) |
1659 | return (EINVAL); |
1660 | error = sysctl_copyin(l, newp, &nnode, sizeof(nnode)); |
1661 | if (error) |
1662 | return (error); |
1663 | |
1664 | /* |
1665 | * does the node they asked for exist? |
1666 | */ |
1667 | if (namelen != 1) |
1668 | return (EOPNOTSUPP); |
1669 | node = rnode; |
1670 | sysctl_num = nnode.sysctl_num; |
1671 | error = sysctl_locate(l, &sysctl_num, 1, &node, NULL); |
1672 | if (error) |
1673 | return (error); |
1674 | |
1675 | /* |
1676 | * does this node that we have found purport to handle mmap? |
1677 | */ |
1678 | if (node->sysctl_func == NULL || |
1679 | !(node->sysctl_flags & CTLFLAG_MMAP)) |
1680 | return (EOPNOTSUPP); |
1681 | |
1682 | /* |
1683 | * well...okay, they asked for it. |
1684 | */ |
1685 | return ((*node->sysctl_func)(SYSCTLFN_CALL(node))); |
1686 | } |
1687 | |
1688 | int |
1689 | sysctl_describe(SYSCTLFN_ARGS) |
1690 | { |
1691 | struct sysctldesc *d; |
1692 | void *bf; |
1693 | size_t sz, left, tot; |
1694 | int i, error, v = -1; |
1695 | struct sysctlnode *node; |
1696 | struct sysctlnode dnode; |
1697 | |
1698 | if (SYSCTL_VERS(rnode->sysctl_flags) != SYSCTL_VERSION) { |
1699 | printf("sysctl_query: rnode %p wrong version\n" , rnode); |
1700 | return (EINVAL); |
1701 | } |
1702 | |
1703 | if (SYSCTL_TYPE(rnode->sysctl_flags) != CTLTYPE_NODE) |
1704 | return (ENOTDIR); |
1705 | if (namelen != 1 || name[0] != CTL_DESCRIBE) |
1706 | return (EINVAL); |
1707 | |
1708 | /* |
1709 | * get ready... |
1710 | */ |
1711 | error = 0; |
1712 | d = bf = malloc(MAXDESCLEN, M_TEMP, M_WAITOK|M_CANFAIL); |
1713 | if (bf == NULL) |
1714 | return ENOMEM; |
1715 | tot = 0; |
1716 | node = rnode->sysctl_child; |
1717 | left = *oldlenp; |
1718 | |
1719 | /* |
1720 | * no request -> all descriptions at this level |
1721 | * request with desc unset -> just this node |
1722 | * request with desc set -> set descr for this node |
1723 | */ |
1724 | if (newp != NULL) { |
1725 | error = sysctl_cvt_in(l, &v, newp, newlen, &dnode); |
1726 | if (error) |
1727 | goto out; |
1728 | if (dnode.sysctl_desc != NULL) { |
1729 | /* |
1730 | * processes cannot set descriptions above |
1731 | * securelevel 0. and must be root. blah |
1732 | * blah blah. a couple more checks are made |
1733 | * once we find the node we want. |
1734 | */ |
1735 | if (l != NULL) { |
1736 | #ifndef SYSCTL_DISALLOW_CREATE |
1737 | error = kauth_authorize_system(l->l_cred, |
1738 | KAUTH_SYSTEM_SYSCTL, |
1739 | KAUTH_REQ_SYSTEM_SYSCTL_DESC, NULL, |
1740 | NULL, NULL); |
1741 | if (error) |
1742 | goto out; |
1743 | #else /* SYSCTL_DISALLOW_CREATE */ |
1744 | error = EPERM; |
1745 | goto out; |
1746 | #endif /* SYSCTL_DISALLOW_CREATE */ |
1747 | } |
1748 | |
1749 | /* |
1750 | * find node and try to set the description on it |
1751 | */ |
1752 | for (i = 0; i < rnode->sysctl_clen; i++) |
1753 | if (node[i].sysctl_num == dnode.sysctl_num) |
1754 | break; |
1755 | if (i == rnode->sysctl_clen) { |
1756 | error = ENOENT; |
1757 | goto out; |
1758 | } |
1759 | node = &node[i]; |
1760 | |
1761 | /* |
1762 | * did the caller specify a node version? |
1763 | */ |
1764 | if (dnode.sysctl_ver != 0 && |
1765 | dnode.sysctl_ver != node->sysctl_ver) { |
1766 | error = EINVAL; |
1767 | goto out; |
1768 | } |
1769 | |
1770 | /* |
1771 | * okay...some rules: |
1772 | * (1) if setup is done and the tree is |
1773 | * read-only or the whole system is |
1774 | * read-only |
1775 | * (2) no one can set a description on a |
1776 | * permanent node (it must be set when |
1777 | * using createv) |
1778 | * (3) processes cannot *change* a description |
1779 | * (4) processes *can*, however, set a |
1780 | * description on a read-only node so that |
1781 | * one can be created and then described |
1782 | * in two steps |
1783 | * anything else come to mind? |
1784 | */ |
1785 | if ((sysctl_root.sysctl_flags & CTLFLAG_PERMANENT) && |
1786 | (!(sysctl_rootof(node)->sysctl_flags & |
1787 | CTLFLAG_READWRITE) || |
1788 | !(sysctl_root.sysctl_flags & CTLFLAG_READWRITE))) { |
1789 | error = EPERM; |
1790 | goto out; |
1791 | } |
1792 | if (node->sysctl_flags & CTLFLAG_PERMANENT) { |
1793 | error = EPERM; |
1794 | goto out; |
1795 | } |
1796 | if (l != NULL && node->sysctl_desc != NULL) { |
1797 | error = EPERM; |
1798 | goto out; |
1799 | } |
1800 | |
1801 | /* |
1802 | * right, let's go ahead. the first step is |
1803 | * making the description into something the |
1804 | * node can "own", if need be. |
1805 | */ |
1806 | if (l != NULL || |
1807 | dnode.sysctl_flags & CTLFLAG_OWNDESC) { |
1808 | char *nd, *k; |
1809 | |
1810 | k = malloc(MAXDESCLEN, M_TEMP, |
1811 | M_WAITOK|M_CANFAIL); |
1812 | if (k == NULL) { |
1813 | error = ENOMEM; |
1814 | goto out; |
1815 | } |
1816 | error = sysctl_copyinstr(l, dnode.sysctl_desc, |
1817 | k, MAXDESCLEN, &sz); |
1818 | if (error) { |
1819 | free(k, M_TEMP); |
1820 | goto out; |
1821 | } |
1822 | nd = malloc(sz, M_SYSCTLDATA, |
1823 | M_WAITOK|M_CANFAIL); |
1824 | if (nd == NULL) { |
1825 | free(k, M_TEMP); |
1826 | error = ENOMEM; |
1827 | goto out; |
1828 | } |
1829 | memcpy(nd, k, sz); |
1830 | dnode.sysctl_flags |= CTLFLAG_OWNDESC; |
1831 | dnode.sysctl_desc = nd; |
1832 | free(k, M_TEMP); |
1833 | } |
1834 | |
1835 | /* |
1836 | * now "release" the old description and |
1837 | * attach the new one. ta-da. |
1838 | */ |
1839 | if ((node->sysctl_flags & CTLFLAG_OWNDESC) && |
1840 | node->sysctl_desc != NULL) |
1841 | /*XXXUNCONST*/ |
1842 | free(__UNCONST(node->sysctl_desc), M_SYSCTLDATA); |
1843 | node->sysctl_desc = dnode.sysctl_desc; |
1844 | node->sysctl_flags |= |
1845 | (dnode.sysctl_flags & CTLFLAG_OWNDESC); |
1846 | |
1847 | /* |
1848 | * now we "fall out" and into the loop which |
1849 | * will copy the new description back out for |
1850 | * those interested parties |
1851 | */ |
1852 | } |
1853 | } |
1854 | |
1855 | /* |
1856 | * scan for one description or just retrieve all descriptions |
1857 | */ |
1858 | for (i = 0; i < rnode->sysctl_clen; i++) { |
1859 | /* |
1860 | * did they ask for the description of only one node? |
1861 | */ |
1862 | if (v != -1 && node[i].sysctl_num != dnode.sysctl_num) |
1863 | continue; |
1864 | |
1865 | /* |
1866 | * don't describe "private" nodes to non-suser users |
1867 | */ |
1868 | if ((node[i].sysctl_flags & CTLFLAG_PRIVATE) && (l != NULL) && |
1869 | !(kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_SYSCTL, |
1870 | KAUTH_REQ_SYSTEM_SYSCTL_PRVT, NULL, NULL, NULL))) |
1871 | continue; |
1872 | |
1873 | /* |
1874 | * is this description "valid"? |
1875 | */ |
1876 | memset(bf, 0, MAXDESCLEN); |
1877 | if (node[i].sysctl_desc == NULL) |
1878 | sz = 1; |
1879 | else if (copystr(node[i].sysctl_desc, &d->descr_str[0], |
1880 | MAXDESCLEN - sizeof(*d), &sz) != 0) { |
1881 | /* |
1882 | * erase possible partial description |
1883 | */ |
1884 | memset(bf, 0, MAXDESCLEN); |
1885 | sz = 1; |
1886 | } |
1887 | |
1888 | /* |
1889 | * we've got it, stuff it into the caller's buffer |
1890 | */ |
1891 | d->descr_num = node[i].sysctl_num; |
1892 | d->descr_ver = node[i].sysctl_ver; |
1893 | d->descr_len = sz; /* includes trailing nul */ |
1894 | sz = (char *)NEXT_DESCR(d) - (char *)d; |
1895 | if (oldp != NULL && left >= sz) { |
1896 | error = sysctl_copyout(l, d, oldp, sz); |
1897 | if (error) |
1898 | goto out; |
1899 | left -= sz; |
1900 | oldp = (void *)__sysc_desc_adv(oldp, d->descr_len); |
1901 | } |
1902 | tot += sz; |
1903 | |
1904 | /* |
1905 | * if we get this far with v not "unset", they asked |
1906 | * for a specific node and we found it |
1907 | */ |
1908 | if (v != -1) |
1909 | break; |
1910 | } |
1911 | |
1912 | /* |
1913 | * did we find it after all? |
1914 | */ |
1915 | if (v != -1 && tot == 0) |
1916 | error = ENOENT; |
1917 | else |
1918 | *oldlenp = tot; |
1919 | |
1920 | out: |
1921 | free(bf, M_TEMP); |
1922 | return (error); |
1923 | } |
1924 | |
1925 | /* |
1926 | * ******************************************************************** |
1927 | * Section 3: Create and destroy from inside the kernel |
1928 | * ******************************************************************** |
1929 | * sysctl_createv() and sysctl_destroyv() are simpler-to-use |
1930 | * interfaces for the kernel to fling new entries into the mib and rip |
1931 | * them out later. In the case of sysctl_createv(), the returned copy |
1932 | * of the node (see sysctl_create()) will be translated back into a |
1933 | * pointer to the actual node. |
1934 | * |
1935 | * Note that sysctl_createv() will return 0 if the create request |
1936 | * matches an existing node (ala mkdir -p), and that sysctl_destroyv() |
1937 | * will return 0 if the node to be destroyed already does not exist |
1938 | * (aka rm -f) or if it is a parent of other nodes. |
1939 | * |
1940 | * This allows two (or more) different subsystems to assert sub-tree |
1941 | * existence before populating their own nodes, and to remove their |
1942 | * own nodes without orphaning the others when they are done. |
1943 | * ******************************************************************** |
1944 | */ |
1945 | #undef sysctl_createv |
1946 | int |
1947 | sysctl_createv(struct sysctllog **log, int cflags, |
1948 | const struct sysctlnode **rnode, const struct sysctlnode **cnode, |
1949 | int flags, int type, const char *namep, const char *descr, |
1950 | sysctlfn func, u_quad_t qv, void *newp, size_t newlen, |
1951 | ...) |
1952 | { |
1953 | va_list ap; |
1954 | int error, ni, namelen, name[CTL_MAXNAME]; |
1955 | const struct sysctlnode *root, *pnode; |
1956 | struct sysctlnode nnode, onode, *dnode; |
1957 | size_t sz; |
1958 | |
1959 | /* |
1960 | * where are we putting this? |
1961 | */ |
1962 | if (rnode != NULL && *rnode == NULL) { |
1963 | printf("sysctl_createv: rnode NULL\n" ); |
1964 | return (EINVAL); |
1965 | } |
1966 | root = rnode ? *rnode : NULL; |
1967 | if (cnode != NULL) |
1968 | *cnode = NULL; |
1969 | if (cflags != 0) |
1970 | return (EINVAL); |
1971 | |
1972 | /* |
1973 | * what is it? |
1974 | */ |
1975 | flags = SYSCTL_VERSION|SYSCTL_TYPE(type)|SYSCTL_FLAGS(flags); |
1976 | if (log != NULL) |
1977 | flags &= ~CTLFLAG_PERMANENT; |
1978 | |
1979 | /* |
1980 | * where do we put it? |
1981 | */ |
1982 | va_start(ap, newlen); |
1983 | namelen = 0; |
1984 | error = 0; |
1985 | ni = -1; |
1986 | do { |
1987 | if (++ni == CTL_MAXNAME) { |
1988 | error = ENAMETOOLONG; |
1989 | break; |
1990 | } |
1991 | name[ni] = va_arg(ap, int); |
1992 | /* |
1993 | * sorry, this is not supported from here |
1994 | */ |
1995 | if (name[ni] == CTL_CREATESYM) { |
1996 | error = EINVAL; |
1997 | break; |
1998 | } |
1999 | } while (name[ni] != CTL_EOL && name[ni] != CTL_CREATE); |
2000 | va_end(ap); |
2001 | if (error) |
2002 | return error; |
2003 | namelen = ni + (name[ni] == CTL_CREATE ? 1 : 0); |
2004 | |
2005 | /* |
2006 | * what's it called |
2007 | */ |
2008 | if (strlcpy(nnode.sysctl_name, namep, sizeof(nnode.sysctl_name)) >= |
2009 | sizeof(nnode.sysctl_name)) |
2010 | return (ENAMETOOLONG); |
2011 | |
2012 | /* |
2013 | * cons up the description of the new node |
2014 | */ |
2015 | nnode.sysctl_num = name[namelen - 1]; |
2016 | name[namelen - 1] = CTL_CREATE; |
2017 | nnode.sysctl_size = newlen; |
2018 | nnode.sysctl_flags = flags; |
2019 | if (type == CTLTYPE_NODE) { |
2020 | nnode.sysctl_csize = 0; |
2021 | nnode.sysctl_clen = 0; |
2022 | nnode.sysctl_child = NULL; |
2023 | if (flags & CTLFLAG_ALIAS) |
2024 | nnode.sysctl_alias = qv; |
2025 | } else if (flags & CTLFLAG_IMMEDIATE) { |
2026 | switch (type) { |
2027 | case CTLTYPE_BOOL: |
2028 | nnode.sysctl_bdata = qv; |
2029 | break; |
2030 | case CTLTYPE_INT: |
2031 | nnode.sysctl_idata = qv; |
2032 | break; |
2033 | case CTLTYPE_QUAD: |
2034 | nnode.sysctl_qdata = qv; |
2035 | break; |
2036 | default: |
2037 | return (EINVAL); |
2038 | } |
2039 | } else { |
2040 | nnode.sysctl_data = newp; |
2041 | } |
2042 | nnode.sysctl_func = func; |
2043 | nnode.sysctl_parent = NULL; |
2044 | nnode.sysctl_ver = 0; |
2045 | |
2046 | /* |
2047 | * initialize lock state -- we need locks if the main tree has |
2048 | * been marked as complete, but since we could be called from |
2049 | * either there, or from a device driver (say, at device |
2050 | * insertion), or from a module (at module load time, say), we |
2051 | * don't really want to "wait"... |
2052 | */ |
2053 | sysctl_lock(true); |
2054 | |
2055 | /* |
2056 | * locate the prospective parent of the new node, and if we |
2057 | * find it, add the new node. |
2058 | */ |
2059 | sz = sizeof(onode); |
2060 | pnode = root; |
2061 | error = sysctl_locate(NULL, &name[0], namelen - 1, &pnode, &ni); |
2062 | if (error) { |
2063 | /* |
2064 | * XXX: If you are seeing this printf in early bringup |
2065 | * stages, perhaps your setfault is not functioning and |
2066 | * thus kcopy() is mis-behaving. |
2067 | */ |
2068 | printf("sysctl_createv: sysctl_locate(%s) returned %d\n" , |
2069 | nnode.sysctl_name, error); |
2070 | sysctl_unlock(); |
2071 | return (error); |
2072 | } |
2073 | error = sysctl_create(&name[ni], namelen - ni, &onode, &sz, |
2074 | &nnode, sizeof(nnode), &name[0], NULL, |
2075 | pnode); |
2076 | |
2077 | /* |
2078 | * unfortunately the node we wanted to create is already |
2079 | * there. if the node that's already there is a reasonable |
2080 | * facsimile of the node we wanted to create, just pretend |
2081 | * (for the caller's benefit) that we managed to create the |
2082 | * node they wanted. |
2083 | */ |
2084 | if (error == EEXIST) { |
2085 | /* name is the same as requested... */ |
2086 | if (strcmp(nnode.sysctl_name, onode.sysctl_name) == 0 && |
2087 | /* they want the same function... */ |
2088 | nnode.sysctl_func == onode.sysctl_func && |
2089 | /* number is the same as requested, or... */ |
2090 | (nnode.sysctl_num == onode.sysctl_num || |
2091 | /* they didn't pick a number... */ |
2092 | nnode.sysctl_num == CTL_CREATE)) { |
2093 | /* |
2094 | * collision here from trying to create |
2095 | * something that already existed; let's give |
2096 | * our customers a hand and tell them they got |
2097 | * what they wanted. |
2098 | */ |
2099 | #ifdef SYSCTL_DEBUG_CREATE |
2100 | printf("cleared\n" ); |
2101 | #endif /* SYSCTL_DEBUG_CREATE */ |
2102 | error = 0; |
2103 | } |
2104 | } |
2105 | |
2106 | if (error == 0 && |
2107 | (cnode != NULL || log != NULL || descr != NULL)) { |
2108 | /* |
2109 | * sysctl_create() gave us back a copy of the node, |
2110 | * but we need to know where it actually is... |
2111 | */ |
2112 | pnode = root; |
2113 | error = sysctl_locate(NULL, &name[0], namelen - 1, &pnode, &ni); |
2114 | |
2115 | /* |
2116 | * manual scan of last layer so that aliased nodes |
2117 | * aren't followed. |
2118 | */ |
2119 | if (error == 0) { |
2120 | for (ni = 0; ni < pnode->sysctl_clen; ni++) |
2121 | if (pnode->sysctl_child[ni].sysctl_num == |
2122 | onode.sysctl_num) |
2123 | break; |
2124 | if (ni < pnode->sysctl_clen) |
2125 | pnode = &pnode->sysctl_child[ni]; |
2126 | else |
2127 | error = ENOENT; |
2128 | } |
2129 | |
2130 | /* |
2131 | * not expecting an error here, but... |
2132 | */ |
2133 | if (error == 0) { |
2134 | if (log != NULL) |
2135 | sysctl_log_add(log, pnode); |
2136 | if (cnode != NULL) |
2137 | *cnode = pnode; |
2138 | if (descr != NULL) { |
2139 | /* |
2140 | * allow first caller to *set* a |
2141 | * description actually to set it |
2142 | * |
2143 | * discard const here so we can attach |
2144 | * the description |
2145 | */ |
2146 | dnode = __UNCONST(pnode); |
2147 | if (pnode->sysctl_desc != NULL) |
2148 | /* skip it...we've got one */; |
2149 | else if (flags & CTLFLAG_OWNDESC) { |
2150 | size_t l = strlen(descr) + 1; |
2151 | char *d = malloc(l, M_SYSCTLDATA, |
2152 | M_WAITOK|M_CANFAIL); |
2153 | if (d != NULL) { |
2154 | memcpy(d, descr, l); |
2155 | dnode->sysctl_desc = d; |
2156 | dnode->sysctl_flags |= |
2157 | CTLFLAG_OWNDESC; |
2158 | } |
2159 | } else |
2160 | dnode->sysctl_desc = descr; |
2161 | } |
2162 | } else { |
2163 | printf("sysctl_create succeeded but node not found?!\n" ); |
2164 | /* |
2165 | * confusing, but the create said it |
2166 | * succeeded, so... |
2167 | */ |
2168 | error = 0; |
2169 | } |
2170 | } |
2171 | |
2172 | /* |
2173 | * now it should be safe to release the lock state. note that |
2174 | * the pointer to the newly created node being passed back may |
2175 | * not be "good" for very long. |
2176 | */ |
2177 | sysctl_unlock(); |
2178 | |
2179 | if (error != 0) { |
2180 | printf("sysctl_createv: sysctl_create(%s) returned %d\n" , |
2181 | nnode.sysctl_name, error); |
2182 | #if 0 |
2183 | if (error != ENOENT) |
2184 | sysctl_dump(&onode); |
2185 | #endif |
2186 | } |
2187 | |
2188 | return (error); |
2189 | } |
2190 | |
2191 | int |
2192 | sysctl_destroyv(struct sysctlnode *rnode, ...) |
2193 | { |
2194 | va_list ap; |
2195 | int error, name[CTL_MAXNAME], namelen, ni; |
2196 | const struct sysctlnode *pnode, *node; |
2197 | struct sysctlnode dnode, *onode; |
2198 | size_t sz; |
2199 | |
2200 | va_start(ap, rnode); |
2201 | namelen = 0; |
2202 | ni = 0; |
2203 | do { |
2204 | if (ni == CTL_MAXNAME) { |
2205 | va_end(ap); |
2206 | return (ENAMETOOLONG); |
2207 | } |
2208 | name[ni] = va_arg(ap, int); |
2209 | } while (name[ni++] != CTL_EOL); |
2210 | namelen = ni - 1; |
2211 | va_end(ap); |
2212 | |
2213 | /* |
2214 | * i can't imagine why we'd be destroying a node when the tree |
2215 | * wasn't complete, but who knows? |
2216 | */ |
2217 | sysctl_lock(true); |
2218 | |
2219 | /* |
2220 | * where is it? |
2221 | */ |
2222 | node = rnode; |
2223 | error = sysctl_locate(NULL, &name[0], namelen - 1, &node, &ni); |
2224 | if (error) { |
2225 | /* they want it gone and it's not there, so... */ |
2226 | sysctl_unlock(); |
2227 | return (error == ENOENT ? 0 : error); |
2228 | } |
2229 | |
2230 | /* |
2231 | * set up the deletion |
2232 | */ |
2233 | pnode = node; |
2234 | node = &dnode; |
2235 | memset(&dnode, 0, sizeof(dnode)); |
2236 | dnode.sysctl_flags = SYSCTL_VERSION; |
2237 | dnode.sysctl_num = name[namelen - 1]; |
2238 | |
2239 | /* |
2240 | * we found it, now let's nuke it |
2241 | */ |
2242 | name[namelen - 1] = CTL_DESTROY; |
2243 | sz = 0; |
2244 | error = sysctl_destroy(&name[namelen - 1], 1, NULL, &sz, |
2245 | node, sizeof(*node), &name[0], NULL, |
2246 | pnode); |
2247 | if (error == ENOTEMPTY) { |
2248 | /* |
2249 | * think of trying to delete "foo" when "foo.bar" |
2250 | * (which someone else put there) is still in |
2251 | * existence |
2252 | */ |
2253 | error = 0; |
2254 | |
2255 | /* |
2256 | * dunno who put the description there, but if this |
2257 | * node can ever be removed, we need to make sure the |
2258 | * string doesn't go out of context. that means we |
2259 | * need to find the node that's still there (don't use |
2260 | * sysctl_locate() because that follows aliasing). |
2261 | */ |
2262 | node = pnode->sysctl_child; |
2263 | for (ni = 0; ni < pnode->sysctl_clen; ni++) |
2264 | if (node[ni].sysctl_num == dnode.sysctl_num) |
2265 | break; |
2266 | node = (ni < pnode->sysctl_clen) ? &node[ni] : NULL; |
2267 | |
2268 | /* |
2269 | * if we found it, and this node has a description, |
2270 | * and this node can be released, and it doesn't |
2271 | * already own its own description...sigh. :) |
2272 | */ |
2273 | if (node != NULL && node->sysctl_desc != NULL && |
2274 | !(node->sysctl_flags & CTLFLAG_PERMANENT) && |
2275 | !(node->sysctl_flags & CTLFLAG_OWNDESC)) { |
2276 | char *d; |
2277 | |
2278 | sz = strlen(node->sysctl_desc) + 1; |
2279 | d = malloc(sz, M_SYSCTLDATA, M_WAITOK|M_CANFAIL); |
2280 | if (d != NULL) { |
2281 | /* |
2282 | * discard const so that we can |
2283 | * re-attach the description |
2284 | */ |
2285 | memcpy(d, node->sysctl_desc, sz); |
2286 | onode = __UNCONST(node); |
2287 | onode->sysctl_desc = d; |
2288 | onode->sysctl_flags |= CTLFLAG_OWNDESC; |
2289 | } else { |
2290 | /* |
2291 | * XXX drop the description? be |
2292 | * afraid? don't care? |
2293 | */ |
2294 | } |
2295 | } |
2296 | } |
2297 | |
2298 | sysctl_unlock(); |
2299 | |
2300 | return (error); |
2301 | } |
2302 | |
2303 | /* |
2304 | * ******************************************************************** |
2305 | * Deletes an entire n-ary tree. Not recommended unless you know why |
2306 | * you're doing it. Personally, I don't know why you'd even think |
2307 | * about it. |
2308 | * ******************************************************************** |
2309 | */ |
2310 | void |
2311 | sysctl_free(struct sysctlnode *rnode) |
2312 | { |
2313 | struct sysctlnode *node, *pnode; |
2314 | |
2315 | rw_enter(&sysctl_treelock, RW_WRITER); |
2316 | |
2317 | if (rnode == NULL) |
2318 | rnode = &sysctl_root; |
2319 | |
2320 | if (SYSCTL_VERS(rnode->sysctl_flags) != SYSCTL_VERSION) { |
2321 | printf("sysctl_free: rnode %p wrong version\n" , rnode); |
2322 | rw_exit(&sysctl_treelock); |
2323 | return; |
2324 | } |
2325 | |
2326 | pnode = rnode; |
2327 | |
2328 | node = pnode->sysctl_child; |
2329 | do { |
2330 | while (node != NULL && pnode->sysctl_csize > 0) { |
2331 | while (node < |
2332 | &pnode->sysctl_child[pnode->sysctl_clen] && |
2333 | (SYSCTL_TYPE(node->sysctl_flags) != |
2334 | CTLTYPE_NODE || |
2335 | node->sysctl_csize == 0)) { |
2336 | if (SYSCTL_FLAGS(node->sysctl_flags) & |
2337 | CTLFLAG_OWNDATA) { |
2338 | if (node->sysctl_data != NULL) { |
2339 | free(node->sysctl_data, |
2340 | M_SYSCTLDATA); |
2341 | node->sysctl_data = NULL; |
2342 | } |
2343 | } |
2344 | if (SYSCTL_FLAGS(node->sysctl_flags) & |
2345 | CTLFLAG_OWNDESC) { |
2346 | if (node->sysctl_desc != NULL) { |
2347 | /*XXXUNCONST*/ |
2348 | free(__UNCONST(node->sysctl_desc), |
2349 | M_SYSCTLDATA); |
2350 | node->sysctl_desc = NULL; |
2351 | } |
2352 | } |
2353 | node++; |
2354 | } |
2355 | if (node < &pnode->sysctl_child[pnode->sysctl_clen]) { |
2356 | pnode = node; |
2357 | node = node->sysctl_child; |
2358 | } else |
2359 | break; |
2360 | } |
2361 | if (pnode->sysctl_child != NULL) |
2362 | free(pnode->sysctl_child, M_SYSCTLNODE); |
2363 | pnode->sysctl_clen = 0; |
2364 | pnode->sysctl_csize = 0; |
2365 | pnode->sysctl_child = NULL; |
2366 | node = pnode; |
2367 | pnode = node->sysctl_parent; |
2368 | } while (pnode != NULL && node != rnode); |
2369 | |
2370 | rw_exit(&sysctl_treelock); |
2371 | } |
2372 | |
2373 | void |
2374 | sysctl_log_print(const struct sysctllog *slog) |
2375 | { |
2376 | int i, len; |
2377 | |
2378 | printf("root %p left %d size %d content" , (const void *)slog->log_root, |
2379 | slog->log_left, slog->log_size); |
2380 | |
2381 | for (len = 0, i = slog->log_left; i < slog->log_size; i++) { |
2382 | switch (len) { |
2383 | case 0: |
2384 | len = -1; |
2385 | printf(" version %d" , slog->log_num[i]); |
2386 | break; |
2387 | case -1: |
2388 | len = -2; |
2389 | printf(" type %d" , slog->log_num[i]); |
2390 | break; |
2391 | case -2: |
2392 | len = slog->log_num[i]; |
2393 | printf(" len %d:" , slog->log_num[i]); |
2394 | if (len <= 0) |
2395 | len = -1; |
2396 | break; |
2397 | default: |
2398 | len--; |
2399 | printf(" %d" , slog->log_num[i]); |
2400 | break; |
2401 | } |
2402 | } |
2403 | printf(" end\n" ); |
2404 | } |
2405 | |
2406 | int |
2407 | sysctl_log_add(struct sysctllog **logp, const struct sysctlnode *node) |
2408 | { |
2409 | const int size0 = 16; |
2410 | int name[CTL_MAXNAME], namelen, i; |
2411 | const struct sysctlnode *pnode; |
2412 | struct sysctllog *log; |
2413 | |
2414 | if (node->sysctl_flags & CTLFLAG_PERMANENT) |
2415 | return (0); |
2416 | |
2417 | if (logp == NULL) |
2418 | return (0); |
2419 | |
2420 | if (*logp == NULL) { |
2421 | log = malloc(sizeof(struct sysctllog), |
2422 | M_SYSCTLDATA, M_WAITOK|M_CANFAIL); |
2423 | if (log == NULL) { |
2424 | /* XXX print error message? */ |
2425 | return (-1); |
2426 | } |
2427 | log->log_num = malloc(size0 * sizeof(int), |
2428 | M_SYSCTLDATA, M_WAITOK|M_CANFAIL); |
2429 | if (log->log_num == NULL) { |
2430 | /* XXX print error message? */ |
2431 | free(log, M_SYSCTLDATA); |
2432 | return (-1); |
2433 | } |
2434 | memset(log->log_num, 0, size0 * sizeof(int)); |
2435 | log->log_root = NULL; |
2436 | log->log_size = size0; |
2437 | log->log_left = size0; |
2438 | *logp = log; |
2439 | } else |
2440 | log = *logp; |
2441 | |
2442 | /* |
2443 | * check that the root is proper. it's okay to record the |
2444 | * address of the root of a tree. it's the only thing that's |
2445 | * guaranteed not to shift around as nodes come and go. |
2446 | */ |
2447 | if (log->log_root == NULL) |
2448 | log->log_root = sysctl_rootof(node); |
2449 | else if (log->log_root != sysctl_rootof(node)) { |
2450 | printf("sysctl: log %p root mismatch (%p)\n" , |
2451 | log->log_root, sysctl_rootof(node)); |
2452 | return (-1); |
2453 | } |
2454 | |
2455 | /* |
2456 | * we will copy out name in reverse order |
2457 | */ |
2458 | for (pnode = node, namelen = 0; |
2459 | pnode != NULL && !(pnode->sysctl_flags & CTLFLAG_ROOT); |
2460 | pnode = pnode->sysctl_parent) |
2461 | name[namelen++] = pnode->sysctl_num; |
2462 | |
2463 | /* |
2464 | * do we have space? |
2465 | */ |
2466 | if (log->log_left < (namelen + 3)) |
2467 | sysctl_log_realloc(log); |
2468 | if (log->log_left < (namelen + 3)) |
2469 | return (-1); |
2470 | |
2471 | /* |
2472 | * stuff name in, then namelen, then node type, and finally, |
2473 | * the version for non-node nodes. |
2474 | */ |
2475 | for (i = 0; i < namelen; i++) |
2476 | log->log_num[--log->log_left] = name[i]; |
2477 | log->log_num[--log->log_left] = namelen; |
2478 | log->log_num[--log->log_left] = SYSCTL_TYPE(node->sysctl_flags); |
2479 | if (log->log_num[log->log_left] != CTLTYPE_NODE) |
2480 | log->log_num[--log->log_left] = node->sysctl_ver; |
2481 | else |
2482 | log->log_num[--log->log_left] = 0; |
2483 | |
2484 | return (0); |
2485 | } |
2486 | |
2487 | void |
2488 | sysctl_teardown(struct sysctllog **logp) |
2489 | { |
2490 | const struct sysctlnode *rnode; |
2491 | struct sysctlnode node; |
2492 | struct sysctllog *log; |
2493 | uint namelen; |
2494 | int *name, t, v, error, ni; |
2495 | size_t sz; |
2496 | |
2497 | if (logp == NULL || *logp == NULL) |
2498 | return; |
2499 | log = *logp; |
2500 | |
2501 | rw_enter(&sysctl_treelock, RW_WRITER); |
2502 | memset(&node, 0, sizeof(node)); |
2503 | |
2504 | while (log->log_left < log->log_size) { |
2505 | KASSERT((log->log_left + 3 < log->log_size) && |
2506 | (log->log_left + log->log_num[log->log_left + 2] <= |
2507 | log->log_size)); |
2508 | v = log->log_num[log->log_left++]; |
2509 | t = log->log_num[log->log_left++]; |
2510 | namelen = log->log_num[log->log_left++]; |
2511 | name = &log->log_num[log->log_left]; |
2512 | |
2513 | node.sysctl_num = name[namelen - 1]; |
2514 | node.sysctl_flags = SYSCTL_VERSION|t; |
2515 | node.sysctl_ver = v; |
2516 | |
2517 | rnode = log->log_root; |
2518 | error = sysctl_locate(NULL, &name[0], namelen, &rnode, &ni); |
2519 | if (error == 0) { |
2520 | name[namelen - 1] = CTL_DESTROY; |
2521 | rnode = rnode->sysctl_parent; |
2522 | sz = 0; |
2523 | (void)sysctl_destroy(&name[namelen - 1], 1, NULL, |
2524 | &sz, &node, sizeof(node), |
2525 | &name[0], NULL, rnode); |
2526 | } |
2527 | |
2528 | log->log_left += namelen; |
2529 | } |
2530 | |
2531 | KASSERT(log->log_size == log->log_left); |
2532 | free(log->log_num, M_SYSCTLDATA); |
2533 | free(log, M_SYSCTLDATA); |
2534 | *logp = NULL; |
2535 | |
2536 | rw_exit(&sysctl_treelock); |
2537 | } |
2538 | |
2539 | /* |
2540 | * ******************************************************************** |
2541 | * old_sysctl -- A routine to bridge old-style internal calls to the |
2542 | * new infrastructure. |
2543 | * ******************************************************************** |
2544 | */ |
2545 | int |
2546 | old_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, |
2547 | void *newp, size_t newlen, struct lwp *l) |
2548 | { |
2549 | int error; |
2550 | size_t oldlen = 0; |
2551 | size_t savelen; |
2552 | |
2553 | if (oldlenp) { |
2554 | oldlen = *oldlenp; |
2555 | } |
2556 | savelen = oldlen; |
2557 | |
2558 | sysctl_lock(newp != NULL); |
2559 | error = sysctl_dispatch(name, namelen, oldp, &oldlen, |
2560 | newp, newlen, name, l, NULL); |
2561 | sysctl_unlock(); |
2562 | if (error == 0 && oldp != NULL && savelen < oldlen) |
2563 | error = ENOMEM; |
2564 | if (oldlenp) { |
2565 | *oldlenp = oldlen; |
2566 | } |
2567 | |
2568 | return (error); |
2569 | } |
2570 | |
2571 | /* |
2572 | * ******************************************************************** |
2573 | * Section 4: Generic helper routines |
2574 | * ******************************************************************** |
2575 | * "helper" routines that can do more finely grained access control, |
2576 | * construct structures from disparate information, create the |
2577 | * appearance of more nodes and sub-trees, etc. for example, if |
2578 | * CTL_PROC wanted a helper function, it could respond to a CTL_QUERY |
2579 | * with a dynamically created list of nodes that represented the |
2580 | * currently running processes at that instant. |
2581 | * ******************************************************************** |
2582 | */ |
2583 | |
2584 | /* |
2585 | * first, a few generic helpers that provide: |
2586 | * |
2587 | * sysctl_needfunc() a readonly interface that emits a warning |
2588 | * sysctl_notavail() returns EOPNOTSUPP (generic error) |
2589 | * sysctl_null() an empty return buffer with no error |
2590 | */ |
2591 | int |
2592 | sysctl_needfunc(SYSCTLFN_ARGS) |
2593 | { |
2594 | int error; |
2595 | |
2596 | printf("!!SYSCTL_NEEDFUNC!!\n" ); |
2597 | |
2598 | if (newp != NULL || namelen != 0) |
2599 | return (EOPNOTSUPP); |
2600 | |
2601 | error = 0; |
2602 | if (oldp != NULL) |
2603 | error = sysctl_copyout(l, rnode->sysctl_data, oldp, |
2604 | MIN(rnode->sysctl_size, *oldlenp)); |
2605 | *oldlenp = rnode->sysctl_size; |
2606 | |
2607 | return (error); |
2608 | } |
2609 | |
2610 | int |
2611 | sysctl_notavail(SYSCTLFN_ARGS) |
2612 | { |
2613 | |
2614 | if (namelen == 1 && name[0] == CTL_QUERY) |
2615 | return (sysctl_query(SYSCTLFN_CALL(rnode))); |
2616 | |
2617 | return (EOPNOTSUPP); |
2618 | } |
2619 | |
2620 | int |
2621 | sysctl_null(SYSCTLFN_ARGS) |
2622 | { |
2623 | |
2624 | *oldlenp = 0; |
2625 | |
2626 | return (0); |
2627 | } |
2628 | |
2629 | u_int |
2630 | sysctl_map_flags(const u_int *map, u_int word) |
2631 | { |
2632 | u_int rv; |
2633 | |
2634 | for (rv = 0; *map != 0; map += 2) |
2635 | if ((word & map[0]) != 0) |
2636 | rv |= map[1]; |
2637 | |
2638 | return rv; |
2639 | } |
2640 | |
2641 | /* |
2642 | * ******************************************************************** |
2643 | * Section 5: The machinery that makes it all go |
2644 | * ******************************************************************** |
2645 | * Memory "manglement" routines. Not much to this, eh? |
2646 | * ******************************************************************** |
2647 | */ |
2648 | static int |
2649 | sysctl_alloc(struct sysctlnode *p, int x) |
2650 | { |
2651 | int i; |
2652 | struct sysctlnode *n; |
2653 | |
2654 | assert(p->sysctl_child == NULL); |
2655 | |
2656 | if (x == 1) |
2657 | n = malloc(sizeof(struct sysctlnode), |
2658 | M_SYSCTLNODE, M_WAITOK|M_CANFAIL); |
2659 | else |
2660 | n = malloc(SYSCTL_DEFSIZE * sizeof(struct sysctlnode), |
2661 | M_SYSCTLNODE, M_WAITOK|M_CANFAIL); |
2662 | if (n == NULL) |
2663 | return (ENOMEM); |
2664 | |
2665 | if (x == 1) { |
2666 | memset(n, 0, sizeof(struct sysctlnode)); |
2667 | p->sysctl_csize = 1; |
2668 | } else { |
2669 | memset(n, 0, SYSCTL_DEFSIZE * sizeof(struct sysctlnode)); |
2670 | p->sysctl_csize = SYSCTL_DEFSIZE; |
2671 | } |
2672 | p->sysctl_clen = 0; |
2673 | |
2674 | for (i = 0; i < p->sysctl_csize; i++) |
2675 | n[i].sysctl_parent = p; |
2676 | |
2677 | p->sysctl_child = n; |
2678 | return (0); |
2679 | } |
2680 | |
2681 | static int |
2682 | sysctl_realloc(struct sysctlnode *p) |
2683 | { |
2684 | int i, j, olen; |
2685 | struct sysctlnode *n; |
2686 | |
2687 | assert(p->sysctl_csize == p->sysctl_clen); |
2688 | |
2689 | /* |
2690 | * how many do we have...how many should we make? |
2691 | */ |
2692 | olen = p->sysctl_clen; |
2693 | n = malloc(2 * olen * sizeof(struct sysctlnode), M_SYSCTLNODE, |
2694 | M_WAITOK|M_CANFAIL); |
2695 | if (n == NULL) |
2696 | return (ENOMEM); |
2697 | |
2698 | /* |
2699 | * move old children over...initialize new children |
2700 | */ |
2701 | memcpy(n, p->sysctl_child, olen * sizeof(struct sysctlnode)); |
2702 | memset(&n[olen], 0, olen * sizeof(struct sysctlnode)); |
2703 | p->sysctl_csize = 2 * olen; |
2704 | |
2705 | /* |
2706 | * reattach moved (and new) children to parent; if a moved |
2707 | * child node has children, reattach the parent pointers of |
2708 | * grandchildren |
2709 | */ |
2710 | for (i = 0; i < p->sysctl_csize; i++) { |
2711 | n[i].sysctl_parent = p; |
2712 | if (n[i].sysctl_child != NULL) { |
2713 | for (j = 0; j < n[i].sysctl_csize; j++) |
2714 | n[i].sysctl_child[j].sysctl_parent = &n[i]; |
2715 | } |
2716 | } |
2717 | |
2718 | /* |
2719 | * get out with the old and in with the new |
2720 | */ |
2721 | free(p->sysctl_child, M_SYSCTLNODE); |
2722 | p->sysctl_child = n; |
2723 | |
2724 | return (0); |
2725 | } |
2726 | |
2727 | static int |
2728 | sysctl_log_realloc(struct sysctllog *log) |
2729 | { |
2730 | int *n, s, d; |
2731 | |
2732 | s = log->log_size * 2; |
2733 | d = log->log_size; |
2734 | |
2735 | n = malloc(s * sizeof(int), M_SYSCTLDATA, M_WAITOK|M_CANFAIL); |
2736 | if (n == NULL) |
2737 | return (-1); |
2738 | |
2739 | memset(n, 0, s * sizeof(int)); |
2740 | memcpy(&n[d], log->log_num, d * sizeof(int)); |
2741 | free(log->log_num, M_SYSCTLDATA); |
2742 | log->log_num = n; |
2743 | if (d) |
2744 | log->log_left += d; |
2745 | else |
2746 | log->log_left = s; |
2747 | log->log_size = s; |
2748 | |
2749 | return (0); |
2750 | } |
2751 | |
2752 | /* |
2753 | * ******************************************************************** |
2754 | * Section 6: Conversion between API versions wrt the sysctlnode |
2755 | * ******************************************************************** |
2756 | */ |
2757 | static int |
2758 | sysctl_cvt_in(struct lwp *l, int *vp, const void *i, size_t sz, |
2759 | struct sysctlnode *node) |
2760 | { |
2761 | int error, flags; |
2762 | |
2763 | if (i == NULL || sz < sizeof(flags)) |
2764 | return (EINVAL); |
2765 | |
2766 | error = sysctl_copyin(l, i, &flags, sizeof(flags)); |
2767 | if (error) |
2768 | return (error); |
2769 | |
2770 | #if (SYSCTL_VERSION != SYSCTL_VERS_1) |
2771 | #error sysctl_cvt_in: no support for SYSCTL_VERSION |
2772 | #endif /* (SYSCTL_VERSION != SYSCTL_VERS_1) */ |
2773 | |
2774 | if (sz == sizeof(*node) && |
2775 | SYSCTL_VERS(flags) == SYSCTL_VERSION) { |
2776 | error = sysctl_copyin(l, i, node, sizeof(*node)); |
2777 | if (error) |
2778 | return (error); |
2779 | *vp = SYSCTL_VERSION; |
2780 | return (0); |
2781 | } |
2782 | |
2783 | return (EINVAL); |
2784 | } |
2785 | |
2786 | static int |
2787 | sysctl_cvt_out(struct lwp *l, int v, const struct sysctlnode *i, |
2788 | void *ovp, size_t left, size_t *szp) |
2789 | { |
2790 | size_t sz = sizeof(*i); |
2791 | const void *src = i; |
2792 | int error; |
2793 | |
2794 | switch (v) { |
2795 | case SYSCTL_VERS_0: |
2796 | return (EINVAL); |
2797 | |
2798 | #if (SYSCTL_VERSION != SYSCTL_VERS_1) |
2799 | #error sysctl_cvt_out: no support for SYSCTL_VERSION |
2800 | #endif /* (SYSCTL_VERSION != SYSCTL_VERS_1) */ |
2801 | |
2802 | case SYSCTL_VERSION: |
2803 | /* nothing more to do here */ |
2804 | break; |
2805 | } |
2806 | |
2807 | if (ovp != NULL && left >= sz) { |
2808 | error = sysctl_copyout(l, src, ovp, sz); |
2809 | if (error) |
2810 | return (error); |
2811 | } |
2812 | |
2813 | if (szp != NULL) |
2814 | *szp = sz; |
2815 | |
2816 | return (0); |
2817 | } |
2818 | |