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