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