]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_newsysctl.c
eaa2ce293ae61476b2eee4a4b975a30c124030fc
[apple/xnu.git] / bsd / kern / kern_newsysctl.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30 /*-
31 * Copyright (c) 1982, 1986, 1989, 1993
32 * The Regents of the University of California. All rights reserved.
33 *
34 * This code is derived from software contributed to Berkeley by
35 * Mike Karels at Berkeley Software Design, Inc.
36 *
37 * Quite extensively rewritten by Poul-Henning Kamp of the FreeBSD
38 * project, to make these variables more userfriendly.
39 *
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
42 * are met:
43 * 1. Redistributions of source code must retain the above copyright
44 * notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 * notice, this list of conditions and the following disclaimer in the
47 * documentation and/or other materials provided with the distribution.
48 * 3. All advertising materials mentioning features or use of this software
49 * must display the following acknowledgement:
50 * This product includes software developed by the University of
51 * California, Berkeley and its contributors.
52 * 4. Neither the name of the University nor the names of its contributors
53 * may be used to endorse or promote products derived from this software
54 * without specific prior written permission.
55 *
56 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
57 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
58 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
59 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
60 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
61 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
62 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
63 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
64 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
65 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
66 * SUCH DAMAGE.
67 *
68 * @(#)kern_sysctl.c 8.4 (Berkeley) 4/14/94
69 */
70
71
72 #include <sys/param.h>
73 #include <sys/buf.h>
74 #include <sys/kernel.h>
75 #include <sys/sysctl.h>
76 #include <sys/malloc.h>
77 #include <sys/proc_internal.h>
78 #include <sys/systm.h>
79
80 #include <bsm/audit_kernel.h>
81
82 /*
83 struct sysctl_oid_list sysctl__debug_children;
84 struct sysctl_oid_list sysctl__kern_children;
85 struct sysctl_oid_list sysctl__net_children;
86 struct sysctl_oid_list sysctl__sysctl_children;
87 */
88
89 extern struct sysctl_oid *newsysctl_list[];
90 extern struct sysctl_oid *machdep_sysctl_list[];
91
92
93 static void
94 sysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i);
95
96
97
98 /*
99 * Locking and stats
100 */
101 static struct sysctl_lock {
102 int sl_lock;
103 int sl_want;
104 int sl_locked;
105 } memlock;
106
107 /*
108 * XXX this does not belong here
109 */
110 static funnel_t *
111 spl_kernel_funnel(void)
112 {
113 funnel_t *cfunnel;
114
115 cfunnel = thread_funnel_get();
116 if (cfunnel != kernel_flock) {
117 if (cfunnel != NULL)
118 thread_funnel_set(cfunnel, FALSE);
119 thread_funnel_set(kernel_flock, TRUE);
120 }
121 return(cfunnel);
122 }
123
124 static void
125 splx_kernel_funnel(funnel_t *saved)
126 {
127 if (saved != kernel_flock) {
128 thread_funnel_set(kernel_flock, FALSE);
129 if (saved != NULL)
130 thread_funnel_set(saved, TRUE);
131 }
132 }
133
134 static int sysctl_root SYSCTL_HANDLER_ARGS;
135
136 struct sysctl_oid_list sysctl__children; /* root list */
137
138 /*
139 * Initialization of the MIB tree.
140 *
141 * Order by number in each list.
142 */
143
144 void sysctl_register_oid(struct sysctl_oid *oidp)
145 {
146 struct sysctl_oid_list *parent = oidp->oid_parent;
147 struct sysctl_oid *p;
148 struct sysctl_oid *q;
149 int n;
150 funnel_t *fnl;
151
152 fnl = spl_kernel_funnel();
153
154 /*
155 * If this oid has a number OID_AUTO, give it a number which
156 * is greater than any current oid. Make sure it is at least
157 * 100 to leave space for pre-assigned oid numbers.
158 */
159 /* sysctl_sysctl_debug_dump_node(parent, 3); */
160 if (oidp->oid_number == OID_AUTO) {
161 /* First, find the highest oid in the parent list >99 */
162 n = 99;
163 SLIST_FOREACH(p, parent, oid_link) {
164 if (p->oid_number > n)
165 n = p->oid_number;
166 }
167 oidp->oid_number = n + 1;
168 }
169
170 /*
171 * Insert the oid into the parent's list in order.
172 */
173 q = NULL;
174 SLIST_FOREACH(p, parent, oid_link) {
175 if (oidp->oid_number < p->oid_number)
176 break;
177 q = p;
178 }
179 if (q)
180 SLIST_INSERT_AFTER(q, oidp, oid_link);
181 else
182 SLIST_INSERT_HEAD(parent, oidp, oid_link);
183
184 splx_kernel_funnel(fnl);
185 }
186
187 void sysctl_unregister_oid(struct sysctl_oid *oidp)
188 {
189 funnel_t *fnl;
190
191 fnl = spl_kernel_funnel();
192 SLIST_REMOVE(oidp->oid_parent, oidp, sysctl_oid, oid_link);
193 splx_kernel_funnel(fnl);
194 }
195
196 /*
197 * Bulk-register all the oids in a linker_set.
198 */
199 void sysctl_register_set(struct linker_set *lsp)
200 {
201 int count = lsp->ls_length;
202 int i;
203 for (i = 0; i < count; i++)
204 sysctl_register_oid((struct sysctl_oid *) lsp->ls_items[i]);
205 }
206
207 void sysctl_unregister_set(struct linker_set *lsp)
208 {
209 int count = lsp->ls_length;
210 int i;
211 for (i = 0; i < count; i++)
212 sysctl_unregister_oid((struct sysctl_oid *) lsp->ls_items[i]);
213 }
214
215
216 /*
217 * Register OID's from fixed list
218 */
219
220 void sysctl_register_fixed()
221 {
222 int i;
223
224 for (i=0; newsysctl_list[i]; i++) {
225 sysctl_register_oid(newsysctl_list[i]);
226 }
227 for (i=0; machdep_sysctl_list[i]; i++) {
228 sysctl_register_oid(machdep_sysctl_list[i]);
229 }
230 }
231
232 /*
233 * Register the kernel's oids on startup.
234 */
235 struct linker_set sysctl_set;
236
237 void sysctl_register_all(void *arg)
238 {
239 sysctl_register_set(&sysctl_set);
240 }
241
242 SYSINIT(sysctl, SI_SUB_KMEM, SI_ORDER_ANY, sysctl_register_all, 0);
243
244 /*
245 * "Staff-functions"
246 *
247 * These functions implement a presently undocumented interface
248 * used by the sysctl program to walk the tree, and get the type
249 * so it can print the value.
250 * This interface is under work and consideration, and should probably
251 * be killed with a big axe by the first person who can find the time.
252 * (be aware though, that the proper interface isn't as obvious as it
253 * may seem, there are various conflicting requirements.
254 *
255 * {0,0} printf the entire MIB-tree.
256 * {0,1,...} return the name of the "..." OID.
257 * {0,2,...} return the next OID.
258 * {0,3} return the OID of the name in "new"
259 * {0,4,...} return the kind & format info for the "..." OID.
260 */
261
262 static void
263 sysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i)
264 {
265 int k;
266 struct sysctl_oid *oidp;
267
268 SLIST_FOREACH(oidp, l, oid_link) {
269
270 for (k=0; k<i; k++)
271 printf(" ");
272
273 printf("%d %s ", oidp->oid_number, oidp->oid_name);
274
275 printf("%c%c",
276 oidp->oid_kind & CTLFLAG_RD ? 'R':' ',
277 oidp->oid_kind & CTLFLAG_WR ? 'W':' ');
278
279 if (oidp->oid_handler)
280 printf(" *Handler");
281
282 switch (oidp->oid_kind & CTLTYPE) {
283 case CTLTYPE_NODE:
284 printf(" Node\n");
285 if (!oidp->oid_handler) {
286 sysctl_sysctl_debug_dump_node(
287 oidp->oid_arg1, i+2);
288 }
289 break;
290 case CTLTYPE_INT: printf(" Int\n"); break;
291 case CTLTYPE_STRING: printf(" String\n"); break;
292 case CTLTYPE_QUAD: printf(" Quad\n"); break;
293 case CTLTYPE_OPAQUE: printf(" Opaque/struct\n"); break;
294 default: printf("\n");
295 }
296
297 }
298 }
299
300 static int
301 sysctl_sysctl_debug SYSCTL_HANDLER_ARGS
302 {
303 sysctl_sysctl_debug_dump_node(&sysctl__children, 0);
304 return ENOENT;
305 }
306
307 SYSCTL_PROC(_sysctl, 0, debug, CTLTYPE_STRING|CTLFLAG_RD,
308 0, 0, sysctl_sysctl_debug, "-", "");
309
310 static int
311 sysctl_sysctl_name SYSCTL_HANDLER_ARGS
312 {
313 int *name = (int *) arg1;
314 u_int namelen = arg2;
315 int error = 0;
316 struct sysctl_oid *oid;
317 struct sysctl_oid_list *lsp = &sysctl__children, *lsp2;
318 char tempbuf[10];
319
320 while (namelen) {
321 if (!lsp) {
322 snprintf(tempbuf,sizeof(tempbuf),"%d",*name);
323 if (req->oldidx)
324 error = SYSCTL_OUT(req, ".", 1);
325 if (!error)
326 error = SYSCTL_OUT(req, tempbuf, strlen(tempbuf));
327 if (error)
328 return (error);
329 namelen--;
330 name++;
331 continue;
332 }
333 lsp2 = 0;
334 SLIST_FOREACH(oid, lsp, oid_link) {
335 if (oid->oid_number != *name)
336 continue;
337
338 if (req->oldidx)
339 error = SYSCTL_OUT(req, ".", 1);
340 if (!error)
341 error = SYSCTL_OUT(req, oid->oid_name,
342 strlen(oid->oid_name));
343 if (error)
344 return (error);
345
346 namelen--;
347 name++;
348
349 if ((oid->oid_kind & CTLTYPE) != CTLTYPE_NODE)
350 break;
351
352 if (oid->oid_handler)
353 break;
354
355 lsp2 = (struct sysctl_oid_list *)oid->oid_arg1;
356 break;
357 }
358 lsp = lsp2;
359 }
360 return (SYSCTL_OUT(req, "", 1));
361 }
362
363 SYSCTL_NODE(_sysctl, 1, name, CTLFLAG_RD, sysctl_sysctl_name, "");
364
365 static int
366 sysctl_sysctl_next_ls (struct sysctl_oid_list *lsp, int *name, u_int namelen,
367 int *next, int *len, int level, struct sysctl_oid **oidpp)
368 {
369 struct sysctl_oid *oidp;
370
371 *len = level;
372 SLIST_FOREACH(oidp, lsp, oid_link) {
373 *next = oidp->oid_number;
374 *oidpp = oidp;
375
376 if (!namelen) {
377 if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
378 return 0;
379 if (oidp->oid_handler)
380 /* We really should call the handler here...*/
381 return 0;
382 lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
383 if (!sysctl_sysctl_next_ls (lsp, 0, 0, next+1,
384 len, level+1, oidpp))
385 return 0;
386 goto next;
387 }
388
389 if (oidp->oid_number < *name)
390 continue;
391
392 if (oidp->oid_number > *name) {
393 if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
394 return 0;
395 if (oidp->oid_handler)
396 return 0;
397 lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
398 if (!sysctl_sysctl_next_ls (lsp, name+1, namelen-1,
399 next+1, len, level+1, oidpp))
400 return (0);
401 goto next;
402 }
403 if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
404 continue;
405
406 if (oidp->oid_handler)
407 continue;
408
409 lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
410 if (!sysctl_sysctl_next_ls (lsp, name+1, namelen-1, next+1,
411 len, level+1, oidpp))
412 return (0);
413 next:
414 namelen = 1;
415 *len = level;
416 }
417 return 1;
418 }
419
420 static int
421 sysctl_sysctl_next SYSCTL_HANDLER_ARGS
422 {
423 int *name = (int *) arg1;
424 u_int namelen = arg2;
425 int i, j, error;
426 struct sysctl_oid *oid;
427 struct sysctl_oid_list *lsp = &sysctl__children;
428 int newoid[CTL_MAXNAME];
429
430 i = sysctl_sysctl_next_ls (lsp, name, namelen, newoid, &j, 1, &oid);
431 if (i)
432 return ENOENT;
433 error = SYSCTL_OUT(req, newoid, j * sizeof (int));
434 return (error);
435 }
436
437 SYSCTL_NODE(_sysctl, 2, next, CTLFLAG_RD, sysctl_sysctl_next, "");
438
439 static int
440 name2oid (char *name, int *oid, int *len, struct sysctl_oid **oidpp)
441 {
442 int i;
443 struct sysctl_oid *oidp;
444 struct sysctl_oid_list *lsp = &sysctl__children;
445 char *p;
446
447 if (!*name)
448 return ENOENT;
449
450 p = name + strlen(name) - 1 ;
451 if (*p == '.')
452 *p = '\0';
453
454 *len = 0;
455
456 for (p = name; *p && *p != '.'; p++)
457 ;
458 i = *p;
459 if (i == '.')
460 *p = '\0';
461
462 oidp = SLIST_FIRST(lsp);
463
464 while (oidp && *len < CTL_MAXNAME) {
465 if (strcmp(name, oidp->oid_name)) {
466 oidp = SLIST_NEXT(oidp, oid_link);
467 continue;
468 }
469 *oid++ = oidp->oid_number;
470 (*len)++;
471
472 if (!i) {
473 if (oidpp)
474 *oidpp = oidp;
475 return (0);
476 }
477
478 if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
479 break;
480
481 if (oidp->oid_handler)
482 break;
483
484 lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
485 oidp = SLIST_FIRST(lsp);
486 name = p+1;
487 for (p = name; *p && *p != '.'; p++)
488 ;
489 i = *p;
490 if (i == '.')
491 *p = '\0';
492 }
493 return ENOENT;
494 }
495
496 static int
497 sysctl_sysctl_name2oid SYSCTL_HANDLER_ARGS
498 {
499 char *p;
500 int error, oid[CTL_MAXNAME], len;
501 struct sysctl_oid *op = 0;
502
503 if (!req->newlen)
504 return ENOENT;
505 if (req->newlen >= MAXPATHLEN) /* XXX arbitrary, undocumented */
506 return (ENAMETOOLONG);
507
508 MALLOC(p, char *,req->newlen+1, M_TEMP, M_WAITOK);
509 if (!p)
510 return ENOMEM;
511
512 error = SYSCTL_IN(req, p, req->newlen);
513 if (error) {
514 FREE(p, M_TEMP);
515 return (error);
516 }
517
518 p [req->newlen] = '\0';
519
520 error = name2oid(p, oid, &len, &op);
521
522 FREE(p, M_TEMP);
523
524 if (error)
525 return (error);
526
527 error = SYSCTL_OUT(req, oid, len * sizeof *oid);
528 return (error);
529 }
530
531 SYSCTL_PROC(_sysctl, 3, name2oid, CTLFLAG_RW|CTLFLAG_ANYBODY|CTLFLAG_KERN, 0, 0,
532 sysctl_sysctl_name2oid, "I", "");
533
534 static int
535 sysctl_sysctl_oidfmt SYSCTL_HANDLER_ARGS
536 {
537 int *name = (int *) arg1, error;
538 u_int namelen = arg2;
539 int indx;
540 struct sysctl_oid *oid;
541 struct sysctl_oid_list *lsp = &sysctl__children;
542
543 oid = SLIST_FIRST(lsp);
544
545 indx = 0;
546 while (oid && indx < CTL_MAXNAME) {
547 if (oid->oid_number == name[indx]) {
548 indx++;
549 if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
550 if (oid->oid_handler)
551 goto found;
552 if (indx == namelen)
553 goto found;
554 lsp = (struct sysctl_oid_list *)oid->oid_arg1;
555 oid = SLIST_FIRST(lsp);
556 } else {
557 if (indx != namelen)
558 return EISDIR;
559 goto found;
560 }
561 } else {
562 oid = SLIST_NEXT(oid, oid_link);
563 }
564 }
565 return ENOENT;
566 found:
567 if (!oid->oid_fmt)
568 return ENOENT;
569 error = SYSCTL_OUT(req,
570 &oid->oid_kind, sizeof(oid->oid_kind));
571 if (!error)
572 error = SYSCTL_OUT(req, oid->oid_fmt,
573 strlen(oid->oid_fmt)+1);
574 return (error);
575 }
576
577
578 SYSCTL_NODE(_sysctl, 4, oidfmt, CTLFLAG_RD, sysctl_sysctl_oidfmt, "");
579
580 /*
581 * Default "handler" functions.
582 */
583
584 /*
585 * Handle an int, signed or unsigned.
586 * Two cases:
587 * a variable: point arg1 at it.
588 * a constant: pass it in arg2.
589 */
590
591 int
592 sysctl_handle_int SYSCTL_HANDLER_ARGS
593 {
594 int error = 0;
595
596 if (arg1)
597 error = SYSCTL_OUT(req, arg1, sizeof(int));
598 else
599 error = SYSCTL_OUT(req, &arg2, sizeof(int));
600
601 if (error || !req->newptr)
602 return (error);
603
604 if (!arg1)
605 error = EPERM;
606 else
607 error = SYSCTL_IN(req, arg1, sizeof(int));
608
609 if (error == 0)
610 AUDIT_ARG(value, *(int *)arg1);
611 return (error);
612 }
613
614 /*
615 * Handle a long, signed or unsigned. arg1 points to it.
616 */
617
618 int
619 sysctl_handle_long SYSCTL_HANDLER_ARGS
620 {
621 int error = 0;
622
623 if (!arg1)
624 return (EINVAL);
625 error = SYSCTL_OUT(req, arg1, sizeof(long));
626
627 if (error || !req->newptr)
628 return (error);
629
630 error = SYSCTL_IN(req, arg1, sizeof(long));
631 return (error);
632 }
633
634 /*
635 * Handle a quad, signed or unsigned. arg1 points to it.
636 */
637
638 int
639 sysctl_handle_quad SYSCTL_HANDLER_ARGS
640 {
641 int error = 0;
642
643 if (!arg1)
644 return (EINVAL);
645 error = SYSCTL_OUT(req, arg1, sizeof(long long));
646
647 if (error || !req->newptr)
648 return (error);
649
650 error = SYSCTL_IN(req, arg1, sizeof(long long));
651 return (error);
652 }
653
654 /*
655 * Expose an int value as a quad.
656 *
657 * This interface allows us to support interfaces defined
658 * as using quad values while the implementation is still
659 * using ints.
660 */
661 int
662 sysctl_handle_int2quad SYSCTL_HANDLER_ARGS
663 {
664 int error = 0;
665 long long val;
666 int newval;
667
668 if (!arg1)
669 return (EINVAL);
670 val = (long long)*(int *)arg1;
671 error = SYSCTL_OUT(req, &val, sizeof(long long));
672
673 if (error || !req->newptr)
674 return (error);
675
676 error = SYSCTL_IN(req, &val, sizeof(long long));
677 if (!error) {
678 /*
679 * Value must be representable; check by
680 * casting and then casting back.
681 */
682 newval = (int)val;
683 if ((long long)newval != val) {
684 error = ERANGE;
685 } else {
686 *(int *)arg1 = newval;
687 }
688 }
689 return (error);
690 }
691
692 /*
693 * Handle our generic '\0' terminated 'C' string.
694 * Two cases:
695 * a variable string: point arg1 at it, arg2 is max length.
696 * a constant string: point arg1 at it, arg2 is zero.
697 */
698
699 int
700 sysctl_handle_string SYSCTL_HANDLER_ARGS
701 {
702 int error=0;
703
704 error = SYSCTL_OUT(req, arg1, strlen((char *)arg1)+1);
705
706 if (error || !req->newptr)
707 return (error);
708
709 if ((req->newlen - req->newidx) >= arg2) {
710 error = EINVAL;
711 } else {
712 arg2 = (req->newlen - req->newidx);
713 error = SYSCTL_IN(req, arg1, arg2);
714 ((char *)arg1)[arg2] = '\0';
715 }
716
717 return (error);
718 }
719
720 /*
721 * Handle any kind of opaque data.
722 * arg1 points to it, arg2 is the size.
723 */
724
725 int
726 sysctl_handle_opaque SYSCTL_HANDLER_ARGS
727 {
728 int error;
729
730 error = SYSCTL_OUT(req, arg1, arg2);
731
732 if (error || !req->newptr)
733 return (error);
734
735 error = SYSCTL_IN(req, arg1, arg2);
736
737 return (error);
738 }
739
740 /*
741 * Transfer functions to/from kernel space.
742 */
743 static int
744 sysctl_old_kernel(struct sysctl_req *req, const void *p, size_t l)
745 {
746 size_t i = 0;
747
748 if (req->oldptr) {
749 i = l;
750 if (i > req->oldlen - req->oldidx)
751 i = req->oldlen - req->oldidx;
752 if (i > 0)
753 bcopy((void*)p, CAST_DOWN(char *, (req->oldptr + req->oldidx)), i);
754 }
755 req->oldidx += l;
756 if (req->oldptr && i != l)
757 return (ENOMEM);
758 return (0);
759 }
760
761 static int
762 sysctl_new_kernel(struct sysctl_req *req, void *p, size_t l)
763 {
764 if (!req->newptr)
765 return 0;
766 if (req->newlen - req->newidx < l)
767 return (EINVAL);
768 bcopy(CAST_DOWN(char *, (req->newptr + req->newidx)), p, l);
769 req->newidx += l;
770 return (0);
771 }
772
773 int
774 kernel_sysctl(struct proc *p, int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen)
775 {
776 int error = 0;
777 struct sysctl_req req;
778 funnel_t *fnl;
779
780 /*
781 * Construct request.
782 */
783 bzero(&req, sizeof req);
784 req.p = p;
785 if (oldlenp)
786 req.oldlen = *oldlenp;
787 if (old)
788 req.oldptr = CAST_USER_ADDR_T(old);
789 if (newlen) {
790 req.newlen = newlen;
791 req.newptr = CAST_USER_ADDR_T(new);
792 }
793 req.oldfunc = sysctl_old_kernel;
794 req.newfunc = sysctl_new_kernel;
795 req.lock = 1;
796
797 /*
798 * Locking. Tree traversal always begins with the kernel funnel held.
799 */
800 fnl = spl_kernel_funnel();
801
802 /* XXX this should probably be done in a general way */
803 while (memlock.sl_lock) {
804 memlock.sl_want = 1;
805 (void) tsleep((caddr_t)&memlock, PRIBIO+1, "sysctl", 0);
806 memlock.sl_locked++;
807 }
808 memlock.sl_lock = 1;
809
810 /* make the request */
811 error = sysctl_root(0, name, namelen, &req);
812
813 /* unlock memory if required */
814 if (req.lock == 2)
815 vsunlock(req.oldptr, (user_size_t)req.oldlen, B_WRITE);
816
817 memlock.sl_lock = 0;
818
819 if (memlock.sl_want) {
820 memlock.sl_want = 0;
821 wakeup((caddr_t)&memlock);
822 }
823
824 /*
825 * Undo locking.
826 */
827 splx_kernel_funnel(fnl);
828
829 if (error && error != ENOMEM)
830 return (error);
831
832 if (oldlenp)
833 *oldlenp = req.oldidx;
834
835 return (error);
836 }
837
838 /*
839 * Transfer function to/from user space.
840 */
841 static int
842 sysctl_old_user(struct sysctl_req *req, const void *p, size_t l)
843 {
844 int error = 0;
845 size_t i = 0;
846
847 if (req->oldptr) {
848 if (req->oldlen - req->oldidx < l)
849 return (ENOMEM);
850 i = l;
851 if (i > req->oldlen - req->oldidx)
852 i = req->oldlen - req->oldidx;
853 if (i > 0)
854 error = copyout((void*)p, (req->oldptr + req->oldidx), i);
855 }
856 req->oldidx += l;
857 if (error)
858 return (error);
859 if (req->oldptr && i < l)
860 return (ENOMEM);
861 return (0);
862 }
863
864 static int
865 sysctl_new_user(struct sysctl_req *req, void *p, size_t l)
866 {
867 int error;
868
869 if (!req->newptr)
870 return 0;
871 if (req->newlen - req->newidx < l)
872 return (EINVAL);
873 error = copyin((req->newptr + req->newidx), p, l);
874 req->newidx += l;
875 return (error);
876 }
877
878 /*
879 * Traverse our tree, and find the right node, execute whatever it points
880 * at, and return the resulting error code.
881 */
882
883 int
884 sysctl_root SYSCTL_HANDLER_ARGS
885 {
886 int *name = (int *) arg1;
887 u_int namelen = arg2;
888 int indx, i;
889 struct sysctl_oid *oid;
890 struct sysctl_oid_list *lsp = &sysctl__children;
891 int error;
892
893 oid = SLIST_FIRST(lsp);
894
895 indx = 0;
896 while (oid && indx < CTL_MAXNAME) {
897 if (oid->oid_number == name[indx]) {
898 indx++;
899 if (oid->oid_kind & CTLFLAG_NOLOCK)
900 req->lock = 0;
901 if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
902 if (oid->oid_handler)
903 goto found;
904 if (indx == namelen)
905 return ENOENT;
906 lsp = (struct sysctl_oid_list *)oid->oid_arg1;
907 oid = SLIST_FIRST(lsp);
908 } else {
909 if (indx != namelen)
910 return EISDIR;
911 goto found;
912 }
913 } else {
914 oid = SLIST_NEXT(oid, oid_link);
915 }
916 }
917 return ENOENT;
918 found:
919 /* If writing isn't allowed */
920 if (req->newptr && (!(oid->oid_kind & CTLFLAG_WR) ||
921 ((oid->oid_kind & CTLFLAG_SECURE) && securelevel > 0))) {
922 return (EPERM);
923 }
924
925 /*
926 * If we're inside the kernel, the OID must be marked as kernel-valid.
927 * XXX This mechanism for testing is bad.
928 */
929 if ((req->oldfunc == sysctl_old_kernel) && !(oid->oid_kind & CTLFLAG_KERN))
930 return(EPERM);
931
932 /* Most likely only root can write */
933 if (!(oid->oid_kind & CTLFLAG_ANYBODY) &&
934 req->newptr && req->p &&
935 (error = suser(req->p->p_ucred, &req->p->p_acflag)))
936 return (error);
937
938 if (!oid->oid_handler) {
939 return EINVAL;
940 }
941
942
943 if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
944 i = (oid->oid_handler) (oid,
945 name + indx, namelen - indx,
946 req);
947 } else {
948 i = (oid->oid_handler) (oid,
949 oid->oid_arg1, oid->oid_arg2,
950 req);
951 }
952
953 return (i);
954 }
955
956 #ifndef _SYS_SYSPROTO_H_
957 struct sysctl_args {
958 int *name;
959 u_int namelen;
960 void *old;
961 size_t *oldlenp;
962 void *new;
963 size_t newlen;
964 };
965 #endif
966
967 int
968 /* __sysctl(struct proc *p, struct sysctl_args *uap) */
969 new_sysctl(struct proc *p, struct sysctl_args *uap)
970 {
971 int error, i, name[CTL_MAXNAME];
972 size_t j;
973
974 if (uap->namelen > CTL_MAXNAME || uap->namelen < 2)
975 return (EINVAL);
976
977 error = copyin(CAST_USER_ADDR_T(uap->name), &name, uap->namelen * sizeof(int));
978 if (error)
979 return (error);
980
981 error = userland_sysctl(p, name, uap->namelen,
982 CAST_USER_ADDR_T(uap->old), uap->oldlenp, 0,
983 CAST_USER_ADDR_T(uap->new), uap->newlen, &j);
984 if (error && error != ENOMEM)
985 return (error);
986 if (uap->oldlenp) {
987 i = copyout(&j, CAST_USER_ADDR_T(uap->oldlenp), sizeof(j));
988 if (i)
989 return (i);
990 }
991 return (error);
992 }
993
994 /*
995 * This is used from various compatibility syscalls too. That's why name
996 * must be in kernel space.
997 */
998 int
999 userland_sysctl(struct proc *p, int *name, u_int namelen, user_addr_t oldp,
1000 size_t *oldlenp, int inkernel, user_addr_t newp, size_t newlen,
1001 size_t *retval)
1002 {
1003 int error = 0;
1004 struct sysctl_req req, req2;
1005
1006 bzero(&req, sizeof req);
1007
1008 req.p = p;
1009
1010 if (oldlenp) {
1011 if (inkernel) {
1012 req.oldlen = *oldlenp;
1013 } else {
1014 error = copyin(CAST_USER_ADDR_T(oldlenp), &req.oldlen, sizeof(*oldlenp));
1015 if (error)
1016 return (error);
1017 }
1018 }
1019
1020 if (oldp) {
1021 req.oldptr = oldp;
1022 }
1023
1024 if (newlen) {
1025 req.newlen = newlen;
1026 req.newptr = newp;
1027 }
1028
1029 req.oldfunc = sysctl_old_user;
1030 req.newfunc = sysctl_new_user;
1031 req.lock = 1;
1032
1033 do {
1034 req2 = req;
1035 error = sysctl_root(0, name, namelen, &req2);
1036 } while (error == EAGAIN);
1037
1038 req = req2;
1039
1040 if (error && error != ENOMEM)
1041 return (error);
1042
1043 if (retval) {
1044 if (req.oldptr && req.oldidx > req.oldlen)
1045 *retval = req.oldlen;
1046 else
1047 *retval = req.oldidx;
1048 }
1049 return (error);
1050 }
1051
1052 /* Non-standard BSDI extension - only present on their 4.3 net-2 releases */
1053 #define KINFO_BSDI_SYSINFO (101<<8)
1054
1055 /*
1056 * Kernel versions of the userland sysctl helper functions.
1057 *
1058 * These allow sysctl to be used in the same fashion in both
1059 * userland and the kernel.
1060 *
1061 * Note that some sysctl handlers use copyin/copyout, which
1062 * may not work correctly.
1063 */
1064
1065 static int
1066 sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen)
1067 {
1068
1069 return(kernel_sysctl(current_proc(), name, namelen, oldp, oldlenp, newp, newlen));
1070 }
1071
1072 static int
1073 sysctlnametomib(const char *name, int *mibp, size_t *sizep)
1074 {
1075 int oid[2];
1076 int error;
1077
1078 /* magic service node */
1079 oid[0] = 0;
1080 oid[1] = 3;
1081
1082 /* look up OID for name */
1083 *sizep *= sizeof(int);
1084 error = sysctl(oid, 2, mibp, sizep, (void *)name, strlen(name));
1085 *sizep /= sizeof(int);
1086 return(error);
1087 }
1088
1089 int
1090 sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen)
1091 {
1092 int oid[CTL_MAXNAME + 2];
1093 int error;
1094 size_t oidlen;
1095
1096 /* look up the OID */
1097 oidlen = CTL_MAXNAME;
1098 error = sysctlnametomib(name, oid, &oidlen);
1099
1100 /* now use the OID */
1101 if (error == 0)
1102 error = sysctl(oid, oidlen, oldp, oldlenp, newp, newlen);
1103 return(error);
1104 }
1105