kern_sysctl.c revision 16159
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.62 1996/04/13 13:28:54 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, req2;
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	do {
784	    req2 = req;
785	    error = sysctl_root(0, name, namelen, &req2);
786	} while (error == EAGAIN);
787
788	req = req2;
789	if (req.lock == 2)
790		vsunlock(req.oldptr, req.oldlen, B_WRITE);
791
792	memlock.sl_lock = 0;
793
794	if (memlock.sl_want) {
795		memlock.sl_want = 0;
796		wakeup((caddr_t)&memlock);
797	}
798
799	if (error && error != ENOMEM)
800		return (error);
801
802	if (retval) {
803		if (req.oldptr && req.oldidx > req.oldlen)
804			*retval = req.oldlen;
805		else
806			*retval = req.oldidx;
807	}
808	return (error);
809}
810
811#ifdef COMPAT_43
812#include <sys/socket.h>
813#include <vm/vm_param.h>
814
815#define	KINFO_PROC		(0<<8)
816#define	KINFO_RT		(1<<8)
817#define	KINFO_VNODE		(2<<8)
818#define	KINFO_FILE		(3<<8)
819#define	KINFO_METER		(4<<8)
820#define	KINFO_LOADAVG		(5<<8)
821#define	KINFO_CLOCKRATE		(6<<8)
822
823/* Non-standard BSDI extension - only present on their 4.3 net-2 releases */
824#define	KINFO_BSDI_SYSINFO	(101<<8)
825
826/*
827 * XXX this is bloat, but I hope it's better here than on the potentially
828 * limited kernel stack...  -Peter
829 */
830
831static struct {
832	int	bsdi_machine;		/* "i386" on BSD/386 */
833/*      ^^^ this is an offset to the string, relative to the struct start */
834	char	*pad0;
835	long	pad1;
836	long	pad2;
837	long	pad3;
838	u_long	pad4;
839	u_long	pad5;
840	u_long	pad6;
841
842	int	bsdi_ostype;		/* "BSD/386" on BSD/386 */
843	int	bsdi_osrelease;		/* "1.1" on BSD/386 */
844	long	pad7;
845	long	pad8;
846	char	*pad9;
847
848	long	pad10;
849	long	pad11;
850	int	pad12;
851	long	pad13;
852	quad_t	pad14;
853	long	pad15;
854
855	struct	timeval pad16;
856	/* we dont set this, because BSDI's uname used gethostname() instead */
857	int	bsdi_hostname;		/* hostname on BSD/386 */
858
859	/* the actual string data is appended here */
860
861} bsdi_si;
862/*
863 * this data is appended to the end of the bsdi_si structure during copyout.
864 * The "char *" offsets are relative to the base of the bsdi_si struct.
865 * This contains "FreeBSD\02.0-BUILT-nnnnnn\0i386\0", and these strings
866 * should not exceed the length of the buffer here... (or else!! :-)
867 */
868static char bsdi_strings[80];	/* It had better be less than this! */
869
870#ifndef _SYS_SYSPROTO_H_
871struct getkerninfo_args {
872	int	op;
873	char	*where;
874	int	*size;
875	int	arg;
876};
877#endif
878
879int
880ogetkerninfo(struct proc *p, struct getkerninfo_args *uap, int *retval)
881{
882	int error, name[6];
883	u_int size;
884
885	switch (uap->op & 0xff00) {
886
887	case KINFO_RT:
888		name[0] = CTL_NET;
889		name[1] = PF_ROUTE;
890		name[2] = 0;
891		name[3] = (uap->op & 0xff0000) >> 16;
892		name[4] = uap->op & 0xff;
893		name[5] = uap->arg;
894		error = userland_sysctl(p, name, 6, uap->where, uap->size,
895			0, 0, 0, &size);
896		break;
897
898	case KINFO_VNODE:
899		name[0] = CTL_KERN;
900		name[1] = KERN_VNODE;
901		error = userland_sysctl(p, name, 2, uap->where, uap->size,
902			0, 0, 0, &size);
903		break;
904
905	case KINFO_PROC:
906		name[0] = CTL_KERN;
907		name[1] = KERN_PROC;
908		name[2] = uap->op & 0xff;
909		name[3] = uap->arg;
910		error = userland_sysctl(p, name, 4, uap->where, uap->size,
911			0, 0, 0, &size);
912		break;
913
914	case KINFO_FILE:
915		name[0] = CTL_KERN;
916		name[1] = KERN_FILE;
917		error = userland_sysctl(p, name, 2, uap->where, uap->size,
918			0, 0, 0, &size);
919		break;
920
921	case KINFO_METER:
922		name[0] = CTL_VM;
923		name[1] = VM_METER;
924		error = userland_sysctl(p, name, 2, uap->where, uap->size,
925			0, 0, 0, &size);
926		break;
927
928	case KINFO_LOADAVG:
929		name[0] = CTL_VM;
930		name[1] = VM_LOADAVG;
931		error = userland_sysctl(p, name, 2, uap->where, uap->size,
932			0, 0, 0, &size);
933		break;
934
935	case KINFO_CLOCKRATE:
936		name[0] = CTL_KERN;
937		name[1] = KERN_CLOCKRATE;
938		error = userland_sysctl(p, name, 2, uap->where, uap->size,
939			0, 0, 0, &size);
940		break;
941
942	case KINFO_BSDI_SYSINFO: {
943		/*
944		 * this is pretty crude, but it's just enough for uname()
945		 * from BSDI's 1.x libc to work.
946		 *
947		 * In particular, it doesn't return the same results when
948		 * the supplied buffer is too small.  BSDI's version apparently
949		 * will return the amount copied, and set the *size to how
950		 * much was needed.  The emulation framework here isn't capable
951		 * of that, so we just set both to the amount copied.
952		 * BSDI's 2.x product apparently fails with ENOMEM in this
953		 * scenario.
954		 */
955
956		u_int needed;
957		u_int left;
958		char *s;
959
960		bzero((char *)&bsdi_si, sizeof(bsdi_si));
961		bzero(bsdi_strings, sizeof(bsdi_strings));
962
963		s = bsdi_strings;
964
965		bsdi_si.bsdi_ostype = (s - bsdi_strings) + sizeof(bsdi_si);
966		strcpy(s, ostype);
967		s += strlen(s) + 1;
968
969		bsdi_si.bsdi_osrelease = (s - bsdi_strings) + sizeof(bsdi_si);
970		strcpy(s, osrelease);
971		s += strlen(s) + 1;
972
973		bsdi_si.bsdi_machine = (s - bsdi_strings) + sizeof(bsdi_si);
974		strcpy(s, machine);
975		s += strlen(s) + 1;
976
977		needed = sizeof(bsdi_si) + (s - bsdi_strings);
978
979		if (uap->where == NULL) {
980			/* process is asking how much buffer to supply.. */
981			size = needed;
982			error = 0;
983			break;
984		}
985
986
987		/* if too much buffer supplied, trim it down */
988		if (size > needed)
989			size = needed;
990
991		/* how much of the buffer is remaining */
992		left = size;
993
994		if ((error = copyout((char *)&bsdi_si, uap->where, left)) != 0)
995			break;
996
997		/* is there any point in continuing? */
998		if (left > sizeof(bsdi_si)) {
999			left -= sizeof(bsdi_si);
1000			error = copyout(&bsdi_strings,
1001					uap->where + sizeof(bsdi_si), left);
1002		}
1003		break;
1004	}
1005
1006	default:
1007		return (EOPNOTSUPP);
1008	}
1009	if (error)
1010		return (error);
1011	*retval = size;
1012	if (uap->size)
1013		error = copyout((caddr_t)&size, (caddr_t)uap->size,
1014		    sizeof(size));
1015	return (error);
1016}
1017#endif /* COMPAT_43 */
1018