]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_newsysctl.c
d24a34b5139f7cc34bcb14eb5827247789cd7983
[apple/xnu.git] / bsd / kern / kern_newsysctl.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * 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.
11 *
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
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
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.
19 *
20 * @APPLE_LICENSE_HEADER_END@
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>
69 #include <sys/proc.h>
70 #include <sys/systm.h>
71
72 /*
73 struct sysctl_oid_list sysctl__debug_children;
74 struct sysctl_oid_list sysctl__kern_children;
75 struct sysctl_oid_list sysctl__net_children;
76 struct sysctl_oid_list sysctl__sysctl_children;
77 */
78
79 extern struct sysctl_oid *newsysctl_list[];
80
81
82 static void
83 sysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i);
84
85
86
87 /*
88 * Locking and stats
89 */
90 static struct sysctl_lock {
91 int sl_lock;
92 int sl_want;
93 int sl_locked;
94 } memlock;
95
96 static int sysctl_root SYSCTL_HANDLER_ARGS;
97
98 struct sysctl_oid_list sysctl__children; /* root list */
99
100 /*
101 * Initialization of the MIB tree.
102 *
103 * Order by number in each list.
104 */
105
106 void sysctl_register_oid(struct sysctl_oid *oidp)
107 {
108 struct sysctl_oid_list *parent = oidp->oid_parent;
109 struct sysctl_oid *p;
110 struct sysctl_oid *q;
111 int n;
112
113 /*
114 * If this oid has a number OID_AUTO, give it a number which
115 * is greater than any current oid. Make sure it is at least
116 * 100 to leave space for pre-assigned oid numbers.
117 */
118 /* sysctl_sysctl_debug_dump_node(parent, 3); */
119 if (oidp->oid_number == OID_AUTO) {
120 /* First, find the highest oid in the parent list >99 */
121 n = 99;
122 SLIST_FOREACH(p, parent, oid_link) {
123 if (p->oid_number > n)
124 n = p->oid_number;
125 }
126 oidp->oid_number = n + 1;
127 }
128
129 /*
130 * Insert the oid into the parent's list in order.
131 */
132 q = NULL;
133 SLIST_FOREACH(p, parent, oid_link) {
134 if (oidp->oid_number < p->oid_number)
135 break;
136 q = p;
137 }
138 if (q)
139 SLIST_INSERT_AFTER(q, oidp, oid_link);
140 else
141 SLIST_INSERT_HEAD(parent, oidp, oid_link);
142 }
143
144 void sysctl_unregister_oid(struct sysctl_oid *oidp)
145 {
146 SLIST_REMOVE(oidp->oid_parent, oidp, sysctl_oid, oid_link);
147 }
148
149 /*
150 * Bulk-register all the oids in a linker_set.
151 */
152 void sysctl_register_set(struct linker_set *lsp)
153 {
154 int count = lsp->ls_length;
155 int i;
156 for (i = 0; i < count; i++)
157 sysctl_register_oid((struct sysctl_oid *) lsp->ls_items[i]);
158 }
159
160 void sysctl_unregister_set(struct linker_set *lsp)
161 {
162 int count = lsp->ls_length;
163 int i;
164 for (i = 0; i < count; i++)
165 sysctl_unregister_oid((struct sysctl_oid *) lsp->ls_items[i]);
166 }
167
168
169 /*
170 * Register OID's from fixed list
171 */
172
173 void sysctl_register_fixed()
174 {
175 int i = 0;
176
177
178 while (newsysctl_list[i]) {
179 /* printf("Registering %d\n", i); */
180 sysctl_register_oid(newsysctl_list[i++]);
181 }
182 }
183
184 /*
185 * Register the kernel's oids on startup.
186 */
187 struct linker_set sysctl_set;
188
189 void sysctl_register_all(void *arg)
190 {
191 sysctl_register_set(&sysctl_set);
192 }
193
194 SYSINIT(sysctl, SI_SUB_KMEM, SI_ORDER_ANY, sysctl_register_all, 0);
195
196 /*
197 * "Staff-functions"
198 *
199 * These functions implement a presently undocumented interface
200 * used by the sysctl program to walk the tree, and get the type
201 * so it can print the value.
202 * This interface is under work and consideration, and should probably
203 * be killed with a big axe by the first person who can find the time.
204 * (be aware though, that the proper interface isn't as obvious as it
205 * may seem, there are various conflicting requirements.
206 *
207 * {0,0} printf the entire MIB-tree.
208 * {0,1,...} return the name of the "..." OID.
209 * {0,2,...} return the next OID.
210 * {0,3} return the OID of the name in "new"
211 * {0,4,...} return the kind & format info for the "..." OID.
212 */
213
214 static void
215 sysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i)
216 {
217 int k;
218 struct sysctl_oid *oidp;
219
220 SLIST_FOREACH(oidp, l, oid_link) {
221
222 for (k=0; k<i; k++)
223 printf(" ");
224
225 printf("%d %s ", oidp->oid_number, oidp->oid_name);
226
227 printf("%c%c",
228 oidp->oid_kind & CTLFLAG_RD ? 'R':' ',
229 oidp->oid_kind & CTLFLAG_WR ? 'W':' ');
230
231 if (oidp->oid_handler)
232 printf(" *Handler");
233
234 switch (oidp->oid_kind & CTLTYPE) {
235 case CTLTYPE_NODE:
236 printf(" Node\n");
237 if (!oidp->oid_handler) {
238 sysctl_sysctl_debug_dump_node(
239 oidp->oid_arg1, i+2);
240 }
241 break;
242 case CTLTYPE_INT: printf(" Int\n"); break;
243 case CTLTYPE_STRING: printf(" String\n"); break;
244 case CTLTYPE_QUAD: printf(" Quad\n"); break;
245 case CTLTYPE_OPAQUE: printf(" Opaque/struct\n"); break;
246 default: printf("\n");
247 }
248
249 }
250 }
251
252 static int
253 sysctl_sysctl_debug SYSCTL_HANDLER_ARGS
254 {
255 sysctl_sysctl_debug_dump_node(&sysctl__children, 0);
256 return ENOENT;
257 }
258
259 SYSCTL_PROC(_sysctl, 0, debug, CTLTYPE_STRING|CTLFLAG_RD,
260 0, 0, sysctl_sysctl_debug, "-", "");
261
262 static int
263 sysctl_sysctl_name SYSCTL_HANDLER_ARGS
264 {
265 int *name = (int *) arg1;
266 u_int namelen = arg2;
267 int error = 0;
268 struct sysctl_oid *oid;
269 struct sysctl_oid_list *lsp = &sysctl__children, *lsp2;
270 char buf[10];
271
272 while (namelen) {
273 if (!lsp) {
274 snprintf(buf,sizeof(buf),"%d",*name);
275 if (req->oldidx)
276 error = SYSCTL_OUT(req, ".", 1);
277 if (!error)
278 error = SYSCTL_OUT(req, buf, strlen(buf));
279 if (error)
280 return (error);
281 namelen--;
282 name++;
283 continue;
284 }
285 lsp2 = 0;
286 SLIST_FOREACH(oid, lsp, oid_link) {
287 if (oid->oid_number != *name)
288 continue;
289
290 if (req->oldidx)
291 error = SYSCTL_OUT(req, ".", 1);
292 if (!error)
293 error = SYSCTL_OUT(req, oid->oid_name,
294 strlen(oid->oid_name));
295 if (error)
296 return (error);
297
298 namelen--;
299 name++;
300
301 if ((oid->oid_kind & CTLTYPE) != CTLTYPE_NODE)
302 break;
303
304 if (oid->oid_handler)
305 break;
306
307 lsp2 = (struct sysctl_oid_list *)oid->oid_arg1;
308 break;
309 }
310 lsp = lsp2;
311 }
312 return (SYSCTL_OUT(req, "", 1));
313 }
314
315 SYSCTL_NODE(_sysctl, 1, name, CTLFLAG_RD, sysctl_sysctl_name, "");
316
317 static int
318 sysctl_sysctl_next_ls (struct sysctl_oid_list *lsp, int *name, u_int namelen,
319 int *next, int *len, int level, struct sysctl_oid **oidpp)
320 {
321 struct sysctl_oid *oidp;
322
323 *len = level;
324 SLIST_FOREACH(oidp, lsp, oid_link) {
325 *next = oidp->oid_number;
326 *oidpp = oidp;
327
328 if (!namelen) {
329 if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
330 return 0;
331 if (oidp->oid_handler)
332 /* We really should call the handler here...*/
333 return 0;
334 lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
335 if (!sysctl_sysctl_next_ls (lsp, 0, 0, next+1,
336 len, level+1, oidpp))
337 return 0;
338 goto next;
339 }
340
341 if (oidp->oid_number < *name)
342 continue;
343
344 if (oidp->oid_number > *name) {
345 if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
346 return 0;
347 if (oidp->oid_handler)
348 return 0;
349 lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
350 if (!sysctl_sysctl_next_ls (lsp, name+1, namelen-1,
351 next+1, len, level+1, oidpp))
352 return (0);
353 goto next;
354 }
355 if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
356 continue;
357
358 if (oidp->oid_handler)
359 continue;
360
361 lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
362 if (!sysctl_sysctl_next_ls (lsp, name+1, namelen-1, next+1,
363 len, level+1, oidpp))
364 return (0);
365 next:
366 namelen = 1;
367 *len = level;
368 }
369 return 1;
370 }
371
372 static int
373 sysctl_sysctl_next SYSCTL_HANDLER_ARGS
374 {
375 int *name = (int *) arg1;
376 u_int namelen = arg2;
377 int i, j, error;
378 struct sysctl_oid *oid;
379 struct sysctl_oid_list *lsp = &sysctl__children;
380 int newoid[CTL_MAXNAME];
381
382 i = sysctl_sysctl_next_ls (lsp, name, namelen, newoid, &j, 1, &oid);
383 if (i)
384 return ENOENT;
385 error = SYSCTL_OUT(req, newoid, j * sizeof (int));
386 return (error);
387 }
388
389 SYSCTL_NODE(_sysctl, 2, next, CTLFLAG_RD, sysctl_sysctl_next, "");
390
391 static int
392 name2oid (char *name, int *oid, int *len, struct sysctl_oid **oidpp)
393 {
394 int i;
395 struct sysctl_oid *oidp;
396 struct sysctl_oid_list *lsp = &sysctl__children;
397 char *p;
398
399 if (!*name)
400 return ENOENT;
401
402 p = name + strlen(name) - 1 ;
403 if (*p == '.')
404 *p = '\0';
405
406 *len = 0;
407
408 for (p = name; *p && *p != '.'; p++)
409 ;
410 i = *p;
411 if (i == '.')
412 *p = '\0';
413
414 oidp = SLIST_FIRST(lsp);
415
416 while (oidp && *len < CTL_MAXNAME) {
417 if (strcmp(name, oidp->oid_name)) {
418 oidp = SLIST_NEXT(oidp, oid_link);
419 continue;
420 }
421 *oid++ = oidp->oid_number;
422 (*len)++;
423
424 if (!i) {
425 if (oidpp)
426 *oidpp = oidp;
427 return (0);
428 }
429
430 if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
431 break;
432
433 if (oidp->oid_handler)
434 break;
435
436 lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
437 oidp = SLIST_FIRST(lsp);
438 name = p+1;
439 for (p = name; *p && *p != '.'; p++)
440 ;
441 i = *p;
442 if (i == '.')
443 *p = '\0';
444 }
445 return ENOENT;
446 }
447
448 static int
449 sysctl_sysctl_name2oid SYSCTL_HANDLER_ARGS
450 {
451 char *p;
452 int error, oid[CTL_MAXNAME], len;
453 struct sysctl_oid *op = 0;
454
455 if (!req->newlen)
456 return ENOENT;
457 if (req->newlen >= MAXPATHLEN) /* XXX arbitrary, undocumented */
458 return (ENAMETOOLONG);
459
460 p = _MALLOC(req->newlen+1, M_TEMP, M_WAITOK);
461
462 if (!p)
463 return ENOMEM;
464
465 error = SYSCTL_IN(req, p, req->newlen);
466 if (error) {
467 FREE(p, M_TEMP);
468 return (error);
469 }
470
471 p [req->newlen] = '\0';
472
473 error = name2oid(p, oid, &len, &op);
474
475 FREE(p, M_TEMP);
476
477 if (error)
478 return (error);
479
480 error = SYSCTL_OUT(req, oid, len * sizeof *oid);
481 return (error);
482 }
483
484 SYSCTL_PROC(_sysctl, 3, name2oid, CTLFLAG_RW|CTLFLAG_ANYBODY, 0, 0,
485 sysctl_sysctl_name2oid, "I", "");
486
487 static int
488 sysctl_sysctl_oidfmt SYSCTL_HANDLER_ARGS
489 {
490 int *name = (int *) arg1, error;
491 u_int namelen = arg2;
492 int indx;
493 struct sysctl_oid *oid;
494 struct sysctl_oid_list *lsp = &sysctl__children;
495
496 oid = SLIST_FIRST(lsp);
497
498 indx = 0;
499 while (oid && indx < CTL_MAXNAME) {
500 if (oid->oid_number == name[indx]) {
501 indx++;
502 if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
503 if (oid->oid_handler)
504 goto found;
505 if (indx == namelen)
506 goto found;
507 lsp = (struct sysctl_oid_list *)oid->oid_arg1;
508 oid = SLIST_FIRST(lsp);
509 } else {
510 if (indx != namelen)
511 return EISDIR;
512 goto found;
513 }
514 } else {
515 oid = SLIST_NEXT(oid, oid_link);
516 }
517 }
518 return ENOENT;
519 found:
520 if (!oid->oid_fmt)
521 return ENOENT;
522 error = SYSCTL_OUT(req,
523 &oid->oid_kind, sizeof(oid->oid_kind));
524 if (!error)
525 error = SYSCTL_OUT(req, oid->oid_fmt,
526 strlen(oid->oid_fmt)+1);
527 return (error);
528 }
529
530
531 SYSCTL_NODE(_sysctl, 4, oidfmt, CTLFLAG_RD, sysctl_sysctl_oidfmt, "");
532
533 /*
534 * Default "handler" functions.
535 */
536
537 /*
538 * Handle an int, signed or unsigned.
539 * Two cases:
540 * a variable: point arg1 at it.
541 * a constant: pass it in arg2.
542 */
543
544 int
545 sysctl_handle_int SYSCTL_HANDLER_ARGS
546 {
547 int error = 0;
548
549 if (arg1)
550 error = SYSCTL_OUT(req, arg1, sizeof(int));
551 else
552 error = SYSCTL_OUT(req, &arg2, sizeof(int));
553
554 if (error || !req->newptr)
555 return (error);
556
557 if (!arg1)
558 error = EPERM;
559 else
560 error = SYSCTL_IN(req, arg1, sizeof(int));
561 return (error);
562 }
563
564 /*
565 * Handle a long, signed or unsigned. arg1 points to it.
566 */
567
568 int
569 sysctl_handle_long SYSCTL_HANDLER_ARGS
570 {
571 int error = 0;
572
573 if (!arg1)
574 return (EINVAL);
575 error = SYSCTL_OUT(req, arg1, sizeof(long));
576
577 if (error || !req->newptr)
578 return (error);
579
580 error = SYSCTL_IN(req, arg1, sizeof(long));
581 return (error);
582 }
583
584 /*
585 * Handle our generic '\0' terminated 'C' string.
586 * Two cases:
587 * a variable string: point arg1 at it, arg2 is max length.
588 * a constant string: point arg1 at it, arg2 is zero.
589 */
590
591 int
592 sysctl_handle_string SYSCTL_HANDLER_ARGS
593 {
594 int error=0;
595
596 error = SYSCTL_OUT(req, arg1, strlen((char *)arg1)+1);
597
598 if (error || !req->newptr)
599 return (error);
600
601 if ((req->newlen - req->newidx) >= arg2) {
602 error = EINVAL;
603 } else {
604 arg2 = (req->newlen - req->newidx);
605 error = SYSCTL_IN(req, arg1, arg2);
606 ((char *)arg1)[arg2] = '\0';
607 }
608
609 return (error);
610 }
611
612 /*
613 * Handle any kind of opaque data.
614 * arg1 points to it, arg2 is the size.
615 */
616
617 int
618 sysctl_handle_opaque SYSCTL_HANDLER_ARGS
619 {
620 int error;
621
622 error = SYSCTL_OUT(req, arg1, arg2);
623
624 if (error || !req->newptr)
625 return (error);
626
627 error = SYSCTL_IN(req, arg1, arg2);
628
629 return (error);
630 }
631
632 /*
633 * Transfer functions to/from kernel space.
634 * XXX: rather untested at this point
635 */
636 static int
637 sysctl_old_kernel(struct sysctl_req *req, const void *p, size_t l)
638 {
639 size_t i = 0;
640 int error = 0;
641
642 if (req->oldptr) {
643 i = l;
644 if (i > req->oldlen - req->oldidx)
645 i = req->oldlen - req->oldidx;
646 if (i > 0) {
647 error = copyout(p, (char *)req->oldptr + req->oldidx, i);
648 if (error)
649 return error;
650 }
651 }
652 req->oldidx += l;
653 if (req->oldptr && i != l)
654 return (ENOMEM);
655 return (0);
656 }
657
658 static int
659 sysctl_new_kernel(struct sysctl_req *req, void *p, size_t l)
660 {
661 if (!req->newptr)
662 return 0;
663 if (req->newlen - req->newidx < l)
664 return (EINVAL);
665 copyin((char *)req->newptr + req->newidx, p, l);
666 req->newidx += l;
667 return (0);
668 }
669
670 int
671 kernel_sysctl(struct proc *p, int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen, size_t *retval)
672 {
673 int error = 0;
674 struct sysctl_req req;
675
676 bzero(&req, sizeof req);
677
678 req.p = p;
679
680 if (oldlenp) {
681 req.oldlen = *oldlenp;
682 }
683
684 if (old) {
685 req.oldptr= old;
686 }
687
688 if (newlen) {
689 req.newlen = newlen;
690 req.newptr = new;
691 }
692
693 req.oldfunc = sysctl_old_kernel;
694 req.newfunc = sysctl_new_kernel;
695 req.lock = 1;
696
697 /* XXX this should probably be done in a general way */
698 while (memlock.sl_lock) {
699 memlock.sl_want = 1;
700 (void) tsleep((caddr_t)&memlock, PRIBIO+1, "sysctl", 0);
701 memlock.sl_locked++;
702 }
703 memlock.sl_lock = 1;
704
705 error = sysctl_root(0, name, namelen, &req);
706
707 if (req.lock == 2)
708 vsunlock(req.oldptr, req.oldlen, B_WRITE);
709
710 memlock.sl_lock = 0;
711
712 if (memlock.sl_want) {
713 memlock.sl_want = 0;
714 wakeup((caddr_t)&memlock);
715 }
716
717 if (error && error != ENOMEM)
718 return (error);
719
720 if (retval) {
721 if (req.oldptr && req.oldidx > req.oldlen)
722 *retval = req.oldlen;
723 else
724 *retval = req.oldidx;
725 }
726 return (error);
727 }
728
729 /*
730 * Transfer function to/from user space.
731 */
732 static int
733 sysctl_old_user(struct sysctl_req *req, const void *p, size_t l)
734 {
735 int error = 0;
736 size_t i = 0;
737
738 if (req->oldptr) {
739 if (req->oldlen - req->oldidx < l)
740 return (ENOMEM);
741 i = l;
742 if (i > req->oldlen - req->oldidx)
743 i = req->oldlen - req->oldidx;
744 if (i > 0)
745 error = copyout(p, (char *)req->oldptr + req->oldidx,
746 i);
747 }
748 req->oldidx += l;
749 if (error)
750 return (error);
751 if (req->oldptr && i < l)
752 return (ENOMEM);
753 return (0);
754 }
755
756 static int
757 sysctl_new_user(struct sysctl_req *req, void *p, size_t l)
758 {
759 int error;
760
761 if (!req->newptr)
762 return 0;
763 if (req->newlen - req->newidx < l)
764 return (EINVAL);
765 error = copyin((char *)req->newptr + req->newidx, p, l);
766 req->newidx += l;
767 return (error);
768 }
769
770 /*
771 * Traverse our tree, and find the right node, execute whatever it points
772 * at, and return the resulting error code.
773 */
774
775 int
776 sysctl_root SYSCTL_HANDLER_ARGS
777 {
778 int *name = (int *) arg1;
779 u_int namelen = arg2;
780 int indx, i;
781 struct sysctl_oid *oid;
782 struct sysctl_oid_list *lsp = &sysctl__children;
783 int error;
784
785 oid = SLIST_FIRST(lsp);
786
787 indx = 0;
788 while (oid && indx < CTL_MAXNAME) {
789 if (oid->oid_number == name[indx]) {
790 indx++;
791 if (oid->oid_kind & CTLFLAG_NOLOCK)
792 req->lock = 0;
793 if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
794 if (oid->oid_handler)
795 goto found;
796 if (indx == namelen)
797 return ENOENT;
798 lsp = (struct sysctl_oid_list *)oid->oid_arg1;
799 oid = SLIST_FIRST(lsp);
800 } else {
801 if (indx != namelen)
802 return EISDIR;
803 goto found;
804 }
805 } else {
806 oid = SLIST_NEXT(oid, oid_link);
807 }
808 }
809 return ENOENT;
810 found:
811 /* If writing isn't allowed */
812 if (req->newptr && (!(oid->oid_kind & CTLFLAG_WR) ||
813 ((oid->oid_kind & CTLFLAG_SECURE) && securelevel > 0))) {
814 return (EPERM);
815 }
816
817 /* Most likely only root can write */
818 if (!(oid->oid_kind & CTLFLAG_ANYBODY) &&
819 req->newptr && req->p &&
820 (error = suser(req->p->p_ucred, &req->p->p_acflag)))
821 return (error);
822
823 if (!oid->oid_handler) {
824 return EINVAL;
825 }
826
827 /*
828 * Switch to the NETWORK funnel for CTL_NET and KERN_IPC sysctls
829 */
830
831 if (((name[0] == CTL_NET) || ((name[0] == CTL_KERN) &&
832 (name[1] == KERN_IPC))))
833 thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
834
835 if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
836 i = (oid->oid_handler) (oid,
837 name + indx, namelen - indx,
838 req);
839 } else {
840 i = (oid->oid_handler) (oid,
841 oid->oid_arg1, oid->oid_arg2,
842 req);
843 }
844
845 /*
846 * Switch back to the KERNEL funnel, if necessary
847 */
848
849 if (((name[0] == CTL_NET) || ((name[0] == CTL_KERN) &&
850 (name[1] == KERN_IPC))))
851 thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
852
853 return (i);
854 }
855
856 #ifndef _SYS_SYSPROTO_H_
857 struct sysctl_args {
858 int *name;
859 u_int namelen;
860 void *old;
861 size_t *oldlenp;
862 void *new;
863 size_t newlen;
864 };
865 #endif
866
867 int
868 /* __sysctl(struct proc *p, struct sysctl_args *uap) */
869 new_sysctl(struct proc *p, struct sysctl_args *uap)
870 {
871 int error, i, name[CTL_MAXNAME];
872 size_t j;
873
874 if (uap->namelen > CTL_MAXNAME || uap->namelen < 2)
875 return (EINVAL);
876
877 error = copyin(uap->name, &name, uap->namelen * sizeof(int));
878 if (error)
879 return (error);
880
881 error = userland_sysctl(p, name, uap->namelen,
882 uap->old, uap->oldlenp, 0,
883 uap->new, uap->newlen, &j);
884 if (error && error != ENOMEM)
885 return (error);
886 if (uap->oldlenp) {
887 i = copyout(&j, uap->oldlenp, sizeof(j));
888 if (i)
889 return (i);
890 }
891 return (error);
892 }
893
894 /*
895 * This is used from various compatibility syscalls too. That's why name
896 * must be in kernel space.
897 */
898 int
899 userland_sysctl(struct proc *p, int *name, u_int namelen, void *old, size_t *oldlenp, int inkernel, void *new, size_t newlen, size_t *retval)
900 {
901 int error = 0;
902 struct sysctl_req req, req2;
903
904 bzero(&req, sizeof req);
905
906 req.p = p;
907
908 if (oldlenp) {
909 if (inkernel) {
910 req.oldlen = *oldlenp;
911 } else {
912 error = copyin(oldlenp, &req.oldlen, sizeof(*oldlenp));
913 if (error)
914 return (error);
915 }
916 }
917
918 if (old) {
919 req.oldptr= old;
920 }
921
922 if (newlen) {
923 req.newlen = newlen;
924 req.newptr = new;
925 }
926
927 req.oldfunc = sysctl_old_user;
928 req.newfunc = sysctl_new_user;
929 req.lock = 1;
930
931 do {
932 req2 = req;
933 error = sysctl_root(0, name, namelen, &req2);
934 } while (error == EAGAIN);
935
936 req = req2;
937
938 if (error && error != ENOMEM)
939 return (error);
940
941 if (retval) {
942 if (req.oldptr && req.oldidx > req.oldlen)
943 *retval = req.oldlen;
944 else
945 *retval = req.oldidx;
946 }
947 return (error);
948 }
949 #if 0
950
951 #if COMPAT_43
952 #include <sys/socket.h>
953 #include <vm/vm_param.h>
954
955 #define KINFO_PROC (0<<8)
956 #define KINFO_RT (1<<8)
957 #define KINFO_VNODE (2<<8)
958 #define KINFO_FILE (3<<8)
959 #define KINFO_METER (4<<8)
960 #define KINFO_LOADAVG (5<<8)
961 #define KINFO_CLOCKRATE (6<<8)
962
963 /* Non-standard BSDI extension - only present on their 4.3 net-2 releases */
964 #define KINFO_BSDI_SYSINFO (101<<8)
965
966 /*
967 * XXX this is bloat, but I hope it's better here than on the potentially
968 * limited kernel stack... -Peter
969 */
970
971 static struct {
972 int bsdi_machine; /* "i386" on BSD/386 */
973 /* ^^^ this is an offset to the string, relative to the struct start */
974 char *pad0;
975 long pad1;
976 long pad2;
977 long pad3;
978 u_long pad4;
979 u_long pad5;
980 u_long pad6;
981
982 int bsdi_ostype; /* "BSD/386" on BSD/386 */
983 int bsdi_osrelease; /* "1.1" on BSD/386 */
984 long pad7;
985 long pad8;
986 char *pad9;
987
988 long pad10;
989 long pad11;
990 int pad12;
991 long pad13;
992 quad_t pad14;
993 long pad15;
994
995 struct timeval pad16;
996 /* we dont set this, because BSDI's uname used gethostname() instead */
997 int bsdi_hostname; /* hostname on BSD/386 */
998
999 /* the actual string data is appended here */
1000
1001 } bsdi_si;
1002 /*
1003 * this data is appended to the end of the bsdi_si structure during copyout.
1004 * The "char *" offsets are relative to the base of the bsdi_si struct.
1005 * This contains "FreeBSD\02.0-BUILT-nnnnnn\0i386\0", and these strings
1006 * should not exceed the length of the buffer here... (or else!! :-)
1007 */
1008 static char bsdi_strings[80]; /* It had better be less than this! */
1009
1010 #ifndef _SYS_SYSPROTO_H_
1011 struct getkerninfo_args {
1012 int op;
1013 char *where;
1014 size_t *size;
1015 int arg;
1016 };
1017 #endif
1018
1019 int
1020 ogetkerninfo(struct proc *p, struct getkerninfo_args *uap)
1021 {
1022 int error, name[6];
1023 size_t size;
1024
1025 switch (uap->op & 0xff00) {
1026
1027 case KINFO_RT:
1028 name[0] = CTL_NET;
1029 name[1] = PF_ROUTE;
1030 name[2] = 0;
1031 name[3] = (uap->op & 0xff0000) >> 16;
1032 name[4] = uap->op & 0xff;
1033 name[5] = uap->arg;
1034 error = userland_sysctl(p, name, 6, uap->where, uap->size,
1035 0, 0, 0, &size);
1036 break;
1037
1038 case KINFO_VNODE:
1039 name[0] = CTL_KERN;
1040 name[1] = KERN_VNODE;
1041 error = userland_sysctl(p, name, 2, uap->where, uap->size,
1042 0, 0, 0, &size);
1043 break;
1044
1045 case KINFO_PROC:
1046 name[0] = CTL_KERN;
1047 name[1] = KERN_PROC;
1048 name[2] = uap->op & 0xff;
1049 name[3] = uap->arg;
1050 error = userland_sysctl(p, name, 4, uap->where, uap->size,
1051 0, 0, 0, &size);
1052 break;
1053
1054 case KINFO_FILE:
1055 name[0] = CTL_KERN;
1056 name[1] = KERN_FILE;
1057 error = userland_sysctl(p, name, 2, uap->where, uap->size,
1058 0, 0, 0, &size);
1059 break;
1060
1061 case KINFO_METER:
1062 name[0] = CTL_VM;
1063 name[1] = VM_METER;
1064 error = userland_sysctl(p, name, 2, uap->where, uap->size,
1065 0, 0, 0, &size);
1066 break;
1067
1068 case KINFO_LOADAVG:
1069 name[0] = CTL_VM;
1070 name[1] = VM_LOADAVG;
1071 error = userland_sysctl(p, name, 2, uap->where, uap->size,
1072 0, 0, 0, &size);
1073 break;
1074
1075 case KINFO_CLOCKRATE:
1076 name[0] = CTL_KERN;
1077 name[1] = KERN_CLOCKRATE;
1078 error = userland_sysctl(p, name, 2, uap->where, uap->size,
1079 0, 0, 0, &size);
1080 break;
1081
1082 case KINFO_BSDI_SYSINFO: {
1083 /*
1084 * this is pretty crude, but it's just enough for uname()
1085 * from BSDI's 1.x libc to work.
1086 *
1087 * In particular, it doesn't return the same results when
1088 * the supplied buffer is too small. BSDI's version apparently
1089 * will return the amount copied, and set the *size to how
1090 * much was needed. The emulation framework here isn't capable
1091 * of that, so we just set both to the amount copied.
1092 * BSDI's 2.x product apparently fails with ENOMEM in this
1093 * scenario.
1094 */
1095
1096 u_int needed;
1097 u_int left;
1098 char *s;
1099
1100 bzero((char *)&bsdi_si, sizeof(bsdi_si));
1101 bzero(bsdi_strings, sizeof(bsdi_strings));
1102
1103 s = bsdi_strings;
1104
1105 bsdi_si.bsdi_ostype = (s - bsdi_strings) + sizeof(bsdi_si);
1106 strcpy(s, ostype);
1107 s += strlen(s) + 1;
1108
1109 bsdi_si.bsdi_osrelease = (s - bsdi_strings) + sizeof(bsdi_si);
1110 strcpy(s, osrelease);
1111 s += strlen(s) + 1;
1112
1113 bsdi_si.bsdi_machine = (s - bsdi_strings) + sizeof(bsdi_si);
1114 strcpy(s, machine);
1115 s += strlen(s) + 1;
1116
1117 needed = sizeof(bsdi_si) + (s - bsdi_strings);
1118
1119 if (uap->where == NULL) {
1120 /* process is asking how much buffer to supply.. */
1121 size = needed;
1122 error = 0;
1123 break;
1124 }
1125
1126
1127 /* if too much buffer supplied, trim it down */
1128 if (size > needed)
1129 size = needed;
1130
1131 /* how much of the buffer is remaining */
1132 left = size;
1133
1134 if ((error = copyout((char *)&bsdi_si, uap->where, left)) != 0)
1135 break;
1136
1137 /* is there any point in continuing? */
1138 if (left > sizeof(bsdi_si)) {
1139 left -= sizeof(bsdi_si);
1140 error = copyout(&bsdi_strings,
1141 uap->where + sizeof(bsdi_si), left);
1142 }
1143 break;
1144 }
1145
1146 default:
1147 return (EOPNOTSUPP);
1148 }
1149 if (error)
1150 return (error);
1151 p->p_retval[0] = size;
1152 if (uap->size)
1153 error = copyout((caddr_t)&size, (caddr_t)uap->size,
1154 sizeof(size));
1155 return (error);
1156 }
1157 #endif /* COMPAT_43 */
1158
1159 #endif