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