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