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