kern_sysctl.c revision 62454
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: head/sys/kern/kern_sysctl.c 62454 2000-07-03 09:35:31Z phk $
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
55static MALLOC_DEFINE(M_SYSCTL, "sysctl", "sysctl internal magic");
56
57/*
58 * Locking and stats
59 */
60static struct sysctl_lock {
61	int	sl_lock;
62	int	sl_want;
63	int	sl_locked;
64} memlock;
65
66static int sysctl_root (SYSCTL_HANDLER_ARGS);
67
68struct 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
76void 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
113void 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 */
121void 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
129void 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 */
140extern struct linker_set sysctl_set;
141
142static void sysctl_register_all(void *arg)
143{
144	sysctl_register_set(&sysctl_set);
145}
146
147SYSINIT(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
167static void
168sysctl_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
205static int
206sysctl_sysctl_debug (SYSCTL_HANDLER_ARGS)
207{
208	sysctl_sysctl_debug_dump_node(&sysctl__children, 0);
209	return ENOENT;
210}
211
212SYSCTL_PROC(_sysctl, 0, debug, CTLTYPE_STRING|CTLFLAG_RD,
213	0, 0, sysctl_sysctl_debug, "-", "");
214
215static int
216sysctl_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
268SYSCTL_NODE(_sysctl, 1, name, CTLFLAG_RD, sysctl_sysctl_name, "");
269
270static int
271sysctl_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
325static int
326sysctl_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
342SYSCTL_NODE(_sysctl, 2, next, CTLFLAG_RD, sysctl_sysctl_next, "");
343
344static int
345name2oid (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
401static int
402sysctl_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
434SYSCTL_PROC(_sysctl, 3, name2oid, CTLFLAG_RW|CTLFLAG_ANYBODY, 0, 0,
435	sysctl_sysctl_name2oid, "I", "");
436
437static int
438sysctl_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
457SYSCTL_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
470int
471sysctl_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
494int
495sysctl_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
517int
518sysctl_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
543int
544sysctl_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 */
562static int
563sysctl_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
580static int
581sysctl_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
592int
593kernel_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 */
654static int
655sysctl_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
680static int
681sysctl_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
694int
695sysctl_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
738int
739sysctl_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_
783struct 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
793int
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 */
823int
824userland_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
916static 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 */
953static char bsdi_strings[80];	/* It had better be less than this! */
954
955#ifndef _SYS_SYSPROTO_H_
956struct getkerninfo_args {
957	int	op;
958	char	*where;
959	size_t	*size;
960	int	arg;
961};
962#endif
963
964int
965ogetkerninfo(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