kern_sysctl.c revision 16282
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.63 1996/06/06 17:17: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 (req->oldptr && i != l)
575		return (ENOMEM);
576	return (0);
577}
578
579static int
580sysctl_new_kernel(struct sysctl_req *req, void *p, int l)
581{
582	if (!req->newptr)
583		return 0;
584	if (req->newlen - req->newidx < l)
585		return (EINVAL);
586	bcopy(req->newptr + req->newidx, p, l);
587	req->newidx += l;
588	return (0);
589}
590
591int
592kernel_sysctl(struct proc *p, int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen, int *retval)
593{
594	int error = 0;
595	struct sysctl_req req;
596
597	bzero(&req, sizeof req);
598
599	req.p = p;
600
601	if (oldlenp) {
602		req.oldlen = *oldlenp;
603	}
604
605	if (old) {
606		req.oldptr= old;
607	}
608
609	if (newlen) {
610		req.newlen = newlen;
611		req.newptr = new;
612	}
613
614	req.oldfunc = sysctl_old_kernel;
615	req.newfunc = sysctl_new_kernel;
616	req.lock = 1;
617
618	/* XXX this should probably be done in a general way */
619	while (memlock.sl_lock) {
620		memlock.sl_want = 1;
621		(void) tsleep((caddr_t)&memlock, PRIBIO+1, "sysctl", 0);
622		memlock.sl_locked++;
623	}
624	memlock.sl_lock = 1;
625
626	error = sysctl_root(0, name, namelen, &req);
627
628	if (req.lock == 2)
629		vsunlock(req.oldptr, req.oldlen, B_WRITE);
630
631	memlock.sl_lock = 0;
632
633	if (memlock.sl_want) {
634		memlock.sl_want = 0;
635		wakeup((caddr_t)&memlock);
636	}
637
638	if (error && error != ENOMEM)
639		return (error);
640
641	if (retval) {
642		if (req.oldptr && req.oldidx > req.oldlen)
643			*retval = req.oldlen;
644		else
645			*retval = req.oldidx;
646	}
647	return (error);
648}
649
650/*
651 * Transfer function to/from user space.
652 */
653static int
654sysctl_old_user(struct sysctl_req *req, const void *p, int l)
655{
656	int error = 0, i = 0;
657
658	if (req->lock == 1 && req->oldptr) {
659		vslock(req->oldptr, req->oldlen);
660		req->lock = 2;
661	}
662	if (req->oldptr) {
663		i = min(req->oldlen - req->oldidx, l);
664		if (i > 0)
665			error  = copyout(p, req->oldptr + req->oldidx, i);
666	}
667	req->oldidx += l;
668	if (error)
669		return (error);
670	if (req->oldptr && i < l)
671		return (ENOMEM);
672	return (0);
673}
674
675static int
676sysctl_new_user(struct sysctl_req *req, void *p, int l)
677{
678	int error;
679
680	if (!req->newptr)
681		return 0;
682	if (req->newlen - req->newidx < l)
683		return (EINVAL);
684	error = copyin(req->newptr + req->newidx, p, l);
685	req->newidx += l;
686	return (error);
687}
688
689/*
690 * Traverse our tree, and find the right node, execute whatever it points
691 * at, and return the resulting error code.
692 */
693
694int
695sysctl_root SYSCTL_HANDLER_ARGS
696{
697	int *name = (int *) arg1;
698	u_int namelen = arg2;
699	int indx, i, j;
700	struct sysctl_oid **oidpp;
701	struct linker_set *lsp = &sysctl_;
702
703	j = lsp->ls_length;
704	oidpp = (struct sysctl_oid **) lsp->ls_items;
705
706	indx = 0;
707	while (j-- && indx < CTL_MAXNAME) {
708		if (*oidpp && ((*oidpp)->oid_number == name[indx])) {
709			indx++;
710			if ((*oidpp)->oid_kind & CTLFLAG_NOLOCK)
711				req->lock = 0;
712			if (((*oidpp)->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
713				if ((*oidpp)->oid_handler)
714					goto found;
715				if (indx == namelen)
716					return ENOENT;
717				lsp = (struct linker_set*)(*oidpp)->oid_arg1;
718				j = lsp->ls_length;
719				oidpp = (struct sysctl_oid **)lsp->ls_items;
720			} else {
721				if (indx != namelen)
722					return EISDIR;
723				goto found;
724			}
725		} else {
726			oidpp++;
727		}
728	}
729	return ENOENT;
730found:
731	/* If writing isn't allowed */
732	if (req->newptr && !((*oidpp)->oid_kind & CTLFLAG_WR))
733		return (EPERM);
734
735	/* Most likely only root can write */
736	if (!((*oidpp)->oid_kind & CTLFLAG_ANYBODY) &&
737	    req->newptr && req->p &&
738	    (i = suser(req->p->p_ucred, &req->p->p_acflag)))
739		return (i);
740
741	if (!(*oidpp)->oid_handler)
742		return EINVAL;
743
744	if (((*oidpp)->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
745		i = ((*oidpp)->oid_handler) (*oidpp,
746					name + indx, namelen - indx,
747					req);
748	} else {
749		i = ((*oidpp)->oid_handler) (*oidpp,
750					(*oidpp)->oid_arg1, (*oidpp)->oid_arg2,
751					req);
752	}
753	return (i);
754}
755
756#ifndef _SYS_SYSPROTO_H_
757struct sysctl_args {
758	int	*name;
759	u_int	namelen;
760	void	*old;
761	size_t	*oldlenp;
762	void	*new;
763	size_t	newlen;
764};
765#endif
766
767int
768__sysctl(struct proc *p, struct sysctl_args *uap, int *retval)
769{
770	int error, i, j, name[CTL_MAXNAME];
771
772	if (uap->namelen > CTL_MAXNAME || uap->namelen < 2)
773		return (EINVAL);
774
775 	error = copyin(uap->name, &name, uap->namelen * sizeof(int));
776 	if (error)
777		return (error);
778
779	error = userland_sysctl(p, name, uap->namelen,
780		uap->old, uap->oldlenp, 0,
781		uap->new, uap->newlen, &j);
782	if (error && error != ENOMEM)
783		return (error);
784	if (uap->oldlenp) {
785		i = copyout(&j, uap->oldlenp, sizeof(j));
786		if (i)
787			return (i);
788	}
789	return (error);
790}
791
792/*
793 * This is used from various compatibility syscalls too.  That's why name
794 * must be in kernel space.
795 */
796int
797userland_sysctl(struct proc *p, int *name, u_int namelen, void *old, size_t *oldlenp, int inkernel, void *new, size_t newlen, int *retval)
798{
799	int error = 0;
800	struct sysctl_req req, req2;
801
802	bzero(&req, sizeof req);
803
804	req.p = p;
805
806	if (oldlenp) {
807		if (inkernel) {
808			req.oldlen = *oldlenp;
809		} else {
810			error = copyin(oldlenp, &req.oldlen, sizeof(*oldlenp));
811			if (error)
812				return (error);
813		}
814	}
815
816	if (old) {
817		if (!useracc(old, req.oldlen, B_WRITE))
818			return (EFAULT);
819		req.oldptr= old;
820	}
821
822	if (newlen) {
823		if (!useracc(new, req.newlen, B_READ))
824			return (EFAULT);
825		req.newlen = newlen;
826		req.newptr = new;
827	}
828
829	req.oldfunc = sysctl_old_user;
830	req.newfunc = sysctl_new_user;
831	req.lock = 1;
832
833	/* XXX this should probably be done in a general way */
834	while (memlock.sl_lock) {
835		memlock.sl_want = 1;
836		(void) tsleep((caddr_t)&memlock, PRIBIO+1, "sysctl", 0);
837		memlock.sl_locked++;
838	}
839	memlock.sl_lock = 1;
840
841	do {
842	    req2 = req;
843	    error = sysctl_root(0, name, namelen, &req2);
844	} while (error == EAGAIN);
845
846	req = req2;
847	if (req.lock == 2)
848		vsunlock(req.oldptr, req.oldlen, B_WRITE);
849
850	memlock.sl_lock = 0;
851
852	if (memlock.sl_want) {
853		memlock.sl_want = 0;
854		wakeup((caddr_t)&memlock);
855	}
856
857	if (error && error != ENOMEM)
858		return (error);
859
860	if (retval) {
861		if (req.oldptr && req.oldidx > req.oldlen)
862			*retval = req.oldlen;
863		else
864			*retval = req.oldidx;
865	}
866	return (error);
867}
868
869#ifdef COMPAT_43
870#include <sys/socket.h>
871#include <vm/vm_param.h>
872
873#define	KINFO_PROC		(0<<8)
874#define	KINFO_RT		(1<<8)
875#define	KINFO_VNODE		(2<<8)
876#define	KINFO_FILE		(3<<8)
877#define	KINFO_METER		(4<<8)
878#define	KINFO_LOADAVG		(5<<8)
879#define	KINFO_CLOCKRATE		(6<<8)
880
881/* Non-standard BSDI extension - only present on their 4.3 net-2 releases */
882#define	KINFO_BSDI_SYSINFO	(101<<8)
883
884/*
885 * XXX this is bloat, but I hope it's better here than on the potentially
886 * limited kernel stack...  -Peter
887 */
888
889static struct {
890	int	bsdi_machine;		/* "i386" on BSD/386 */
891/*      ^^^ this is an offset to the string, relative to the struct start */
892	char	*pad0;
893	long	pad1;
894	long	pad2;
895	long	pad3;
896	u_long	pad4;
897	u_long	pad5;
898	u_long	pad6;
899
900	int	bsdi_ostype;		/* "BSD/386" on BSD/386 */
901	int	bsdi_osrelease;		/* "1.1" on BSD/386 */
902	long	pad7;
903	long	pad8;
904	char	*pad9;
905
906	long	pad10;
907	long	pad11;
908	int	pad12;
909	long	pad13;
910	quad_t	pad14;
911	long	pad15;
912
913	struct	timeval pad16;
914	/* we dont set this, because BSDI's uname used gethostname() instead */
915	int	bsdi_hostname;		/* hostname on BSD/386 */
916
917	/* the actual string data is appended here */
918
919} bsdi_si;
920/*
921 * this data is appended to the end of the bsdi_si structure during copyout.
922 * The "char *" offsets are relative to the base of the bsdi_si struct.
923 * This contains "FreeBSD\02.0-BUILT-nnnnnn\0i386\0", and these strings
924 * should not exceed the length of the buffer here... (or else!! :-)
925 */
926static char bsdi_strings[80];	/* It had better be less than this! */
927
928#ifndef _SYS_SYSPROTO_H_
929struct getkerninfo_args {
930	int	op;
931	char	*where;
932	int	*size;
933	int	arg;
934};
935#endif
936
937int
938ogetkerninfo(struct proc *p, struct getkerninfo_args *uap, int *retval)
939{
940	int error, name[6];
941	u_int size;
942
943	switch (uap->op & 0xff00) {
944
945	case KINFO_RT:
946		name[0] = CTL_NET;
947		name[1] = PF_ROUTE;
948		name[2] = 0;
949		name[3] = (uap->op & 0xff0000) >> 16;
950		name[4] = uap->op & 0xff;
951		name[5] = uap->arg;
952		error = userland_sysctl(p, name, 6, uap->where, uap->size,
953			0, 0, 0, &size);
954		break;
955
956	case KINFO_VNODE:
957		name[0] = CTL_KERN;
958		name[1] = KERN_VNODE;
959		error = userland_sysctl(p, name, 2, uap->where, uap->size,
960			0, 0, 0, &size);
961		break;
962
963	case KINFO_PROC:
964		name[0] = CTL_KERN;
965		name[1] = KERN_PROC;
966		name[2] = uap->op & 0xff;
967		name[3] = uap->arg;
968		error = userland_sysctl(p, name, 4, uap->where, uap->size,
969			0, 0, 0, &size);
970		break;
971
972	case KINFO_FILE:
973		name[0] = CTL_KERN;
974		name[1] = KERN_FILE;
975		error = userland_sysctl(p, name, 2, uap->where, uap->size,
976			0, 0, 0, &size);
977		break;
978
979	case KINFO_METER:
980		name[0] = CTL_VM;
981		name[1] = VM_METER;
982		error = userland_sysctl(p, name, 2, uap->where, uap->size,
983			0, 0, 0, &size);
984		break;
985
986	case KINFO_LOADAVG:
987		name[0] = CTL_VM;
988		name[1] = VM_LOADAVG;
989		error = userland_sysctl(p, name, 2, uap->where, uap->size,
990			0, 0, 0, &size);
991		break;
992
993	case KINFO_CLOCKRATE:
994		name[0] = CTL_KERN;
995		name[1] = KERN_CLOCKRATE;
996		error = userland_sysctl(p, name, 2, uap->where, uap->size,
997			0, 0, 0, &size);
998		break;
999
1000	case KINFO_BSDI_SYSINFO: {
1001		/*
1002		 * this is pretty crude, but it's just enough for uname()
1003		 * from BSDI's 1.x libc to work.
1004		 *
1005		 * In particular, it doesn't return the same results when
1006		 * the supplied buffer is too small.  BSDI's version apparently
1007		 * will return the amount copied, and set the *size to how
1008		 * much was needed.  The emulation framework here isn't capable
1009		 * of that, so we just set both to the amount copied.
1010		 * BSDI's 2.x product apparently fails with ENOMEM in this
1011		 * scenario.
1012		 */
1013
1014		u_int needed;
1015		u_int left;
1016		char *s;
1017
1018		bzero((char *)&bsdi_si, sizeof(bsdi_si));
1019		bzero(bsdi_strings, sizeof(bsdi_strings));
1020
1021		s = bsdi_strings;
1022
1023		bsdi_si.bsdi_ostype = (s - bsdi_strings) + sizeof(bsdi_si);
1024		strcpy(s, ostype);
1025		s += strlen(s) + 1;
1026
1027		bsdi_si.bsdi_osrelease = (s - bsdi_strings) + sizeof(bsdi_si);
1028		strcpy(s, osrelease);
1029		s += strlen(s) + 1;
1030
1031		bsdi_si.bsdi_machine = (s - bsdi_strings) + sizeof(bsdi_si);
1032		strcpy(s, machine);
1033		s += strlen(s) + 1;
1034
1035		needed = sizeof(bsdi_si) + (s - bsdi_strings);
1036
1037		if (uap->where == NULL) {
1038			/* process is asking how much buffer to supply.. */
1039			size = needed;
1040			error = 0;
1041			break;
1042		}
1043
1044
1045		/* if too much buffer supplied, trim it down */
1046		if (size > needed)
1047			size = needed;
1048
1049		/* how much of the buffer is remaining */
1050		left = size;
1051
1052		if ((error = copyout((char *)&bsdi_si, uap->where, left)) != 0)
1053			break;
1054
1055		/* is there any point in continuing? */
1056		if (left > sizeof(bsdi_si)) {
1057			left -= sizeof(bsdi_si);
1058			error = copyout(&bsdi_strings,
1059					uap->where + sizeof(bsdi_si), left);
1060		}
1061		break;
1062	}
1063
1064	default:
1065		return (EOPNOTSUPP);
1066	}
1067	if (error)
1068		return (error);
1069	*retval = size;
1070	if (uap->size)
1071		error = copyout((caddr_t)&size, (caddr_t)uap->size,
1072		    sizeof(size));
1073	return (error);
1074}
1075#endif /* COMPAT_43 */
1076