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