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