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