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