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