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