kern_sysctl.c revision 62573
11541Srgrimes/*-
21541Srgrimes * Copyright (c) 1982, 1986, 1989, 1993
31541Srgrimes *	The Regents of the University of California.  All rights reserved.
41541Srgrimes *
51541Srgrimes * This code is derived from software contributed to Berkeley by
61541Srgrimes * Mike Karels at Berkeley Software Design, Inc.
71541Srgrimes *
812623Sphk * Quite extensively rewritten by Poul-Henning Kamp of the FreeBSD
912623Sphk * project, to make these variables more userfriendly.
1012623Sphk *
111541Srgrimes * Redistribution and use in source and binary forms, with or without
121541Srgrimes * modification, are permitted provided that the following conditions
131541Srgrimes * are met:
141541Srgrimes * 1. Redistributions of source code must retain the above copyright
151541Srgrimes *    notice, this list of conditions and the following disclaimer.
161541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
171541Srgrimes *    notice, this list of conditions and the following disclaimer in the
181541Srgrimes *    documentation and/or other materials provided with the distribution.
191541Srgrimes * 3. All advertising materials mentioning features or use of this software
201541Srgrimes *    must display the following acknowledgement:
211541Srgrimes *	This product includes software developed by the University of
221541Srgrimes *	California, Berkeley and its contributors.
231541Srgrimes * 4. Neither the name of the University nor the names of its contributors
241541Srgrimes *    may be used to endorse or promote products derived from this software
251541Srgrimes *    without specific prior written permission.
261541Srgrimes *
271541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
281541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
291541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
301541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
311541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
321541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
331541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
341541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
351541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
361541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
371541Srgrimes * SUCH DAMAGE.
381541Srgrimes *
391541Srgrimes *	@(#)kern_sysctl.c	8.4 (Berkeley) 4/14/94
4050477Speter * $FreeBSD: head/sys/kern/kern_sysctl.c 62573 2000-07-04 11:25:35Z phk $
411541Srgrimes */
421541Srgrimes
4331778Seivind#include "opt_compat.h"
4431778Seivind
451541Srgrimes#include <sys/param.h>
4648274Speter#include <sys/systm.h>
4748274Speter#include <sys/kernel.h>
481541Srgrimes#include <sys/sysctl.h>
4912623Sphk#include <sys/malloc.h>
5012662Sdg#include <sys/proc.h>
5115103Sphk#include <sys/sysproto.h>
5212645Sbde#include <vm/vm.h>
5312662Sdg#include <vm/vm_extern.h>
5412645Sbde
5530354Sphkstatic MALLOC_DEFINE(M_SYSCTL, "sysctl", "sysctl internal magic");
5630309Sphk
5712429Sphk/*
5812429Sphk * Locking and stats
5912429Sphk */
6012429Sphkstatic struct sysctl_lock {
6112429Sphk	int	sl_lock;
6212429Sphk	int	sl_want;
6312429Sphk	int	sl_locked;
6412429Sphk} memlock;
6512429Sphk
6662573Sphkstatic int sysctl_root(SYSCTL_HANDLER_ARGS);
6712429Sphk
6844078Sdfrstruct sysctl_oid_list sysctl__children; /* root list */
6912152Sphk
7012623Sphk/*
7112623Sphk * Initialization of the MIB tree.
7212623Sphk *
7344078Sdfr * Order by number in each list.
7412623Sphk */
7512429Sphk
7644078Sdfrvoid sysctl_register_oid(struct sysctl_oid *oidp)
7712152Sphk{
7844078Sdfr	struct sysctl_oid_list *parent = oidp->oid_parent;
7944078Sdfr	struct sysctl_oid *p;
8044078Sdfr	struct sysctl_oid *q;
8144078Sdfr	int n;
8212197Sbde
8344078Sdfr	/*
8444078Sdfr	 * If this oid has a number OID_AUTO, give it a number which
8544078Sdfr	 * is greater than any current oid.  Make sure it is at least
8644078Sdfr	 * 100 to leave space for pre-assigned oid numbers.
8744078Sdfr	 */
8844078Sdfr	if (oidp->oid_number == OID_AUTO) {
8944078Sdfr		/* First, find the highest oid in the parent list >99 */
9044078Sdfr		n = 99;
9144078Sdfr		SLIST_FOREACH(p, parent, oid_link) {
9244078Sdfr			if (p->oid_number > n)
9344078Sdfr				n = p->oid_number;
9444078Sdfr		}
9544078Sdfr		oidp->oid_number = n + 1;
9644078Sdfr	}
9744078Sdfr
9844078Sdfr	/*
9944078Sdfr	 * Insert the oid into the parent's list in order.
10044078Sdfr	 */
10144078Sdfr	q = NULL;
10244078Sdfr	SLIST_FOREACH(p, parent, oid_link) {
10344078Sdfr		if (oidp->oid_number < p->oid_number)
10444078Sdfr			break;
10544078Sdfr		q = p;
10644078Sdfr	}
10744078Sdfr	if (q)
10844078Sdfr		SLIST_INSERT_AFTER(q, oidp, oid_link);
10944078Sdfr	else
11044078Sdfr		SLIST_INSERT_HEAD(parent, oidp, oid_link);
11112152Sphk}
11212131Sphk
11344078Sdfrvoid sysctl_unregister_oid(struct sysctl_oid *oidp)
11412152Sphk{
11560938Sjake	SLIST_REMOVE(oidp->oid_parent, oidp, sysctl_oid, oid_link);
11644078Sdfr}
11712152Sphk
11844078Sdfr/*
11944078Sdfr * Bulk-register all the oids in a linker_set.
12044078Sdfr */
12144078Sdfrvoid sysctl_register_set(struct linker_set *lsp)
12244078Sdfr{
12344078Sdfr	int count = lsp->ls_length;
12444078Sdfr	int i;
12544078Sdfr	for (i = 0; i < count; i++)
12644078Sdfr		sysctl_register_oid((struct sysctl_oid *) lsp->ls_items[i]);
12744078Sdfr}
12812623Sphk
12944078Sdfrvoid sysctl_unregister_set(struct linker_set *lsp)
13044078Sdfr{
13144078Sdfr	int count = lsp->ls_length;
13244078Sdfr	int i;
13344078Sdfr	for (i = 0; i < count; i++)
13444078Sdfr		sysctl_unregister_oid((struct sysctl_oid *) lsp->ls_items[i]);
13512152Sphk}
13612152Sphk
13744078Sdfr/*
13844078Sdfr * Register the kernel's oids on startup.
13944078Sdfr */
14044078Sdfrextern struct linker_set sysctl_set;
14112152Sphk
14244078Sdfrstatic void sysctl_register_all(void *arg)
14338869Sbde{
14444078Sdfr	sysctl_register_set(&sysctl_set);
14538869Sbde}
14638869Sbde
14744078SdfrSYSINIT(sysctl, SI_SUB_KMEM, SI_ORDER_ANY, sysctl_register_all, 0);
14844078Sdfr
14912623Sphk/*
15012623Sphk * "Staff-functions"
15112623Sphk *
15212650Sphk * These functions implement a presently undocumented interface
15312650Sphk * used by the sysctl program to walk the tree, and get the type
15412650Sphk * so it can print the value.
15512650Sphk * This interface is under work and consideration, and should probably
15612650Sphk * be killed with a big axe by the first person who can find the time.
15712650Sphk * (be aware though, that the proper interface isn't as obvious as it
15812650Sphk * may seem, there are various conflicting requirements.
15912650Sphk *
16012623Sphk * {0,0}	printf the entire MIB-tree.
16112623Sphk * {0,1,...}	return the name of the "..." OID.
16242467Sphk * {0,2,...}	return the next OID.
16312623Sphk * {0,3}	return the OID of the name in "new"
16412650Sphk * {0,4,...}	return the kind & format info for the "..." OID.
16512623Sphk */
16612623Sphk
16712152Sphkstatic void
16844078Sdfrsysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i)
16912152Sphk{
17044078Sdfr	int k;
17144078Sdfr	struct sysctl_oid *oidp;
17212152Sphk
17344078Sdfr	SLIST_FOREACH(oidp, l, oid_link) {
17412152Sphk
17512152Sphk		for (k=0; k<i; k++)
17612152Sphk			printf(" ");
17712152Sphk
17844078Sdfr		printf("%d %s ", oidp->oid_number, oidp->oid_name);
17912152Sphk
18012152Sphk		printf("%c%c",
18144078Sdfr			oidp->oid_kind & CTLFLAG_RD ? 'R':' ',
18244078Sdfr			oidp->oid_kind & CTLFLAG_WR ? 'W':' ');
18312152Sphk
18444078Sdfr		if (oidp->oid_handler)
18515241Sphk			printf(" *Handler");
18615241Sphk
18744078Sdfr		switch (oidp->oid_kind & CTLTYPE) {
18812243Sphk			case CTLTYPE_NODE:
18915241Sphk				printf(" Node\n");
19044078Sdfr				if (!oidp->oid_handler) {
19112152Sphk					sysctl_sysctl_debug_dump_node(
19244078Sdfr						oidp->oid_arg1, i+2);
19312152Sphk				}
19412152Sphk				break;
19512152Sphk			case CTLTYPE_INT:    printf(" Int\n"); break;
19612152Sphk			case CTLTYPE_STRING: printf(" String\n"); break;
19712152Sphk			case CTLTYPE_QUAD:   printf(" Quad\n"); break;
19812152Sphk			case CTLTYPE_OPAQUE: printf(" Opaque/struct\n"); break;
19912152Sphk			default:	     printf("\n");
20012152Sphk		}
20112152Sphk
20212152Sphk	}
20312152Sphk}
20412152Sphk
20512152Sphkstatic int
20662573Sphksysctl_sysctl_debug(SYSCTL_HANDLER_ARGS)
20712152Sphk{
20844078Sdfr	sysctl_sysctl_debug_dump_node(&sysctl__children, 0);
20912152Sphk	return ENOENT;
21012152Sphk}
21112152Sphk
21212152SphkSYSCTL_PROC(_sysctl, 0, debug, CTLTYPE_STRING|CTLFLAG_RD,
21312623Sphk	0, 0, sysctl_sysctl_debug, "-", "");
21412152Sphk
21512623Sphkstatic int
21662573Sphksysctl_sysctl_name(SYSCTL_HANDLER_ARGS)
21712623Sphk{
21812623Sphk	int *name = (int *) arg1;
21912623Sphk	u_int namelen = arg2;
22044078Sdfr	int error = 0;
22144078Sdfr	struct sysctl_oid *oid;
22244972Sphk	struct sysctl_oid_list *lsp = &sysctl__children, *lsp2;
22312623Sphk	char buf[10];
22412131Sphk
22512623Sphk	while (namelen) {
22612623Sphk		if (!lsp) {
22741514Sarchie			snprintf(buf,sizeof(buf),"%d",*name);
22812623Sphk			if (req->oldidx)
22912623Sphk				error = SYSCTL_OUT(req, ".", 1);
23012623Sphk			if (!error)
23112623Sphk				error = SYSCTL_OUT(req, buf, strlen(buf));
23212623Sphk			if (error)
23312623Sphk				return (error);
23412623Sphk			namelen--;
23512623Sphk			name++;
23612623Sphk			continue;
23712623Sphk		}
23844972Sphk		lsp2 = 0;
23944078Sdfr		SLIST_FOREACH(oid, lsp, oid_link) {
24044078Sdfr			if (oid->oid_number != *name)
24112623Sphk				continue;
24212131Sphk
24312623Sphk			if (req->oldidx)
24412623Sphk				error = SYSCTL_OUT(req, ".", 1);
24512623Sphk			if (!error)
24644078Sdfr				error = SYSCTL_OUT(req, oid->oid_name,
24744078Sdfr					strlen(oid->oid_name));
24812623Sphk			if (error)
24912623Sphk				return (error);
25012623Sphk
25112623Sphk			namelen--;
25212623Sphk			name++;
25312623Sphk
25444972Sphk			if ((oid->oid_kind & CTLTYPE) != CTLTYPE_NODE)
25512623Sphk				break;
25612623Sphk
25744078Sdfr			if (oid->oid_handler)
25812623Sphk				break;
25912623Sphk
26044972Sphk			lsp2 = (struct sysctl_oid_list *)oid->oid_arg1;
26112623Sphk			break;
26212623Sphk		}
26344972Sphk		lsp = lsp2;
26412623Sphk	}
26512623Sphk	return (SYSCTL_OUT(req, "", 1));
26612623Sphk}
26712623Sphk
26812623SphkSYSCTL_NODE(_sysctl, 1, name, CTLFLAG_RD, sysctl_sysctl_name, "");
26912623Sphk
27012623Sphkstatic int
27144078Sdfrsysctl_sysctl_next_ls (struct sysctl_oid_list *lsp, int *name, u_int namelen,
27244078Sdfr	int *next, int *len, int level, struct sysctl_oid **oidpp)
27312623Sphk{
27444078Sdfr	struct sysctl_oid *oidp;
27512623Sphk
27612623Sphk	*len = level;
27744078Sdfr	SLIST_FOREACH(oidp, lsp, oid_link) {
27844078Sdfr		*next = oidp->oid_number;
27944078Sdfr		*oidpp = oidp;
28012623Sphk
28112623Sphk		if (!namelen) {
28244078Sdfr			if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
28312623Sphk				return 0;
28444078Sdfr			if (oidp->oid_handler)
28512623Sphk				/* We really should call the handler here...*/
28612623Sphk				return 0;
28744078Sdfr			lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
28815241Sphk			if (!sysctl_sysctl_next_ls (lsp, 0, 0, next+1,
28944078Sdfr				len, level+1, oidpp))
29015241Sphk				return 0;
29115241Sphk			goto next;
29212623Sphk		}
29312623Sphk
29444078Sdfr		if (oidp->oid_number < *name)
29512623Sphk			continue;
29612623Sphk
29744078Sdfr		if (oidp->oid_number > *name) {
29844078Sdfr			if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
29912623Sphk				return 0;
30044078Sdfr			if (oidp->oid_handler)
30112623Sphk				return 0;
30244078Sdfr			lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
30312623Sphk			if (!sysctl_sysctl_next_ls (lsp, name+1, namelen-1,
30444078Sdfr				next+1, len, level+1, oidpp))
30512623Sphk				return (0);
30615241Sphk			goto next;
30712623Sphk		}
30844078Sdfr		if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
30912623Sphk			continue;
31012623Sphk
31144078Sdfr		if (oidp->oid_handler)
31212623Sphk			continue;
31312623Sphk
31444078Sdfr		lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
31512623Sphk		if (!sysctl_sysctl_next_ls (lsp, name+1, namelen-1, next+1,
31644078Sdfr			len, level+1, oidpp))
31712623Sphk			return (0);
31815241Sphk	next:
31912623Sphk		namelen = 1;
32012623Sphk		*len = level;
32112623Sphk	}
32212623Sphk	return 1;
32312623Sphk}
32412623Sphk
32512623Sphkstatic int
32662573Sphksysctl_sysctl_next(SYSCTL_HANDLER_ARGS)
32712623Sphk{
32812623Sphk	int *name = (int *) arg1;
32912623Sphk	u_int namelen = arg2;
33012623Sphk	int i, j, error;
33112623Sphk	struct sysctl_oid *oid;
33244078Sdfr	struct sysctl_oid_list *lsp = &sysctl__children;
33312623Sphk	int newoid[CTL_MAXNAME];
33412623Sphk
33512623Sphk	i = sysctl_sysctl_next_ls (lsp, name, namelen, newoid, &j, 1, &oid);
33612623Sphk	if (i)
33712623Sphk		return ENOENT;
33812650Sphk	error = SYSCTL_OUT(req, newoid, j * sizeof (int));
33912623Sphk	return (error);
34012623Sphk}
34112623Sphk
34212623SphkSYSCTL_NODE(_sysctl, 2, next, CTLFLAG_RD, sysctl_sysctl_next, "");
34312623Sphk
34412623Sphkstatic int
34544078Sdfrname2oid (char *name, int *oid, int *len, struct sysctl_oid **oidpp)
34612623Sphk{
34744078Sdfr	int i;
34844078Sdfr	struct sysctl_oid *oidp;
34944078Sdfr	struct sysctl_oid_list *lsp = &sysctl__children;
35012623Sphk	char *p;
35112623Sphk
35212623Sphk	if (!*name)
35312623Sphk		return ENOENT;
35412623Sphk
35512623Sphk	p = name + strlen(name) - 1 ;
35612623Sphk	if (*p == '.')
35712623Sphk		*p = '\0';
35812623Sphk
35912623Sphk	*len = 0;
36012623Sphk
36112623Sphk	for (p = name; *p && *p != '.'; p++)
36212623Sphk		;
36312623Sphk	i = *p;
36412623Sphk	if (i == '.')
36512623Sphk		*p = '\0';
36612623Sphk
36744078Sdfr	oidp = SLIST_FIRST(lsp);
36812623Sphk
36944078Sdfr	while (oidp && *len < CTL_MAXNAME) {
37044078Sdfr		if (strcmp(name, oidp->oid_name)) {
37144078Sdfr			oidp = SLIST_NEXT(oidp, oid_link);
37212623Sphk			continue;
37312623Sphk		}
37444078Sdfr		*oid++ = oidp->oid_number;
37512623Sphk		(*len)++;
37612623Sphk
37712623Sphk		if (!i) {
37844078Sdfr			if (oidpp)
37944078Sdfr				*oidpp = oidp;
38012623Sphk			return (0);
38112623Sphk		}
38212623Sphk
38344078Sdfr		if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
38412623Sphk			break;
38512623Sphk
38644078Sdfr		if (oidp->oid_handler)
38712623Sphk			break;
38812623Sphk
38944078Sdfr		lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
39044078Sdfr		oidp = SLIST_FIRST(lsp);
39112623Sphk		name = p+1;
39212623Sphk		for (p = name; *p && *p != '.'; p++)
39312623Sphk				;
39412623Sphk		i = *p;
39512623Sphk		if (i == '.')
39612623Sphk			*p = '\0';
39712623Sphk	}
39812623Sphk	return ENOENT;
39912623Sphk}
40012623Sphk
40112623Sphkstatic int
40262573Sphksysctl_sysctl_name2oid(SYSCTL_HANDLER_ARGS)
40312623Sphk{
40412623Sphk	char *p;
40512623Sphk	int error, oid[CTL_MAXNAME], len;
40612623Sphk	struct sysctl_oid *op = 0;
40712623Sphk
40812623Sphk	if (!req->newlen)
40912623Sphk		return ENOENT;
41045140Sphk	if (req->newlen >= MAXPATHLEN)	/* XXX arbitrary, undocumented */
41145140Sphk		return (ENAMETOOLONG);
41212623Sphk
41312623Sphk	p = malloc(req->newlen+1, M_SYSCTL, M_WAITOK);
41412623Sphk
41512623Sphk	error = SYSCTL_IN(req, p, req->newlen);
41612623Sphk	if (error) {
41712623Sphk		free(p, M_SYSCTL);
41812623Sphk		return (error);
41912623Sphk	}
42012623Sphk
42112623Sphk	p [req->newlen] = '\0';
42212623Sphk
42312623Sphk	error = name2oid(p, oid, &len, &op);
42412623Sphk
42512623Sphk	free(p, M_SYSCTL);
42612623Sphk
42712623Sphk	if (error)
42812623Sphk		return (error);
42912623Sphk
43012650Sphk	error = SYSCTL_OUT(req, oid, len * sizeof *oid);
43112623Sphk	return (error);
43212623Sphk}
43312623Sphk
43412910SphkSYSCTL_PROC(_sysctl, 3, name2oid, CTLFLAG_RW|CTLFLAG_ANYBODY, 0, 0,
43512623Sphk	sysctl_sysctl_name2oid, "I", "");
43612623Sphk
43712623Sphkstatic int
43862573Sphksysctl_sysctl_oidfmt(SYSCTL_HANDLER_ARGS)
43912623Sphk{
44044078Sdfr	struct sysctl_oid *oid;
44153977Sgreen	int error;
44212623Sphk
44353977Sgreen	error = sysctl_find_oid(arg1, arg2, &oid, NULL, req);
44453977Sgreen	if (error)
44553977Sgreen		return (error);
44612623Sphk
44744078Sdfr	if (!oid->oid_fmt)
44853977Sgreen		return (ENOENT);
44953977Sgreen	error = SYSCTL_OUT(req, &oid->oid_kind, sizeof(oid->oid_kind));
45053977Sgreen	if (error)
45153977Sgreen		return (error);
45253977Sgreen	error = SYSCTL_OUT(req, oid->oid_fmt, strlen(oid->oid_fmt) + 1);
45312650Sphk	return (error);
45412623Sphk}
45512623Sphk
45642467Sphk
45712623SphkSYSCTL_NODE(_sysctl, 4, oidfmt, CTLFLAG_RD, sysctl_sysctl_oidfmt, "");
45812623Sphk
45912243Sphk/*
46012623Sphk * Default "handler" functions.
46112623Sphk */
46212623Sphk
46312623Sphk/*
46442095Sdfr * Handle an int, signed or unsigned.
46512243Sphk * Two cases:
46612243Sphk *     a variable:  point arg1 at it.
46712243Sphk *     a constant:  pass it in arg2.
46812243Sphk */
46912243Sphk
47011865Sphkint
47162573Sphksysctl_handle_int(SYSCTL_HANDLER_ARGS)
47211863Sphk{
47312243Sphk	int error = 0;
47411863Sphk
47512243Sphk	if (arg1)
47612243Sphk		error = SYSCTL_OUT(req, arg1, sizeof(int));
47720506Sbde	else
47812243Sphk		error = SYSCTL_OUT(req, &arg2, sizeof(int));
47911863Sphk
48012243Sphk	if (error || !req->newptr)
48112243Sphk		return (error);
48211863Sphk
48312243Sphk	if (!arg1)
48412243Sphk		error = EPERM;
48512243Sphk	else
48612243Sphk		error = SYSCTL_IN(req, arg1, sizeof(int));
48712243Sphk	return (error);
48811863Sphk}
48911863Sphk
49012243Sphk/*
49145140Sphk * Handle a long, signed or unsigned.  arg1 points to it.
49238517Sdfr */
49338517Sdfr
49438517Sdfrint
49562573Sphksysctl_handle_long(SYSCTL_HANDLER_ARGS)
49638517Sdfr{
49738517Sdfr	int error = 0;
49838517Sdfr
49945140Sphk	if (!arg1)
50045140Sphk		return (EINVAL);
50142095Sdfr	error = SYSCTL_OUT(req, arg1, sizeof(long));
50238517Sdfr
50338517Sdfr	if (error || !req->newptr)
50438517Sdfr		return (error);
50538517Sdfr
50645140Sphk	error = SYSCTL_IN(req, arg1, sizeof(long));
50738517Sdfr	return (error);
50838517Sdfr}
50938517Sdfr
51038517Sdfr/*
51112243Sphk * Handle our generic '\0' terminated 'C' string.
51212243Sphk * Two cases:
51312243Sphk * 	a variable string:  point arg1 at it, arg2 is max length.
51412243Sphk * 	a constant string:  point arg1 at it, arg2 is zero.
51512243Sphk */
51612243Sphk
51711865Sphkint
51862573Sphksysctl_handle_string(SYSCTL_HANDLER_ARGS)
51911863Sphk{
52012243Sphk	int error=0;
52111863Sphk
52212297Sphk	error = SYSCTL_OUT(req, arg1, strlen((char *)arg1)+1);
52311863Sphk
52445140Sphk	if (error || !req->newptr)
52512243Sphk		return (error);
52611863Sphk
52745140Sphk	if ((req->newlen - req->newidx) >= arg2) {
52845140Sphk		error = EINVAL;
52912243Sphk	} else {
53012243Sphk		arg2 = (req->newlen - req->newidx);
53112243Sphk		error = SYSCTL_IN(req, arg1, arg2);
53212243Sphk		((char *)arg1)[arg2] = '\0';
53311863Sphk	}
53412131Sphk
53512131Sphk	return (error);
53611863Sphk}
53711863Sphk
53812243Sphk/*
53912243Sphk * Handle any kind of opaque data.
54012243Sphk * arg1 points to it, arg2 is the size.
54112243Sphk */
54212243Sphk
54311865Sphkint
54462573Sphksysctl_handle_opaque(SYSCTL_HANDLER_ARGS)
54511863Sphk{
54612243Sphk	int error;
54712243Sphk
54812243Sphk	error = SYSCTL_OUT(req, arg1, arg2);
54912243Sphk
55012243Sphk	if (error || !req->newptr)
55112243Sphk		return (error);
55212243Sphk
55312243Sphk	error = SYSCTL_IN(req, arg1, arg2);
55412243Sphk
55512243Sphk	return (error);
55612243Sphk}
55712243Sphk
55812260Sphk/*
55912260Sphk * Transfer functions to/from kernel space.
56012260Sphk * XXX: rather untested at this point
56112260Sphk */
56212260Sphkstatic int
56338517Sdfrsysctl_old_kernel(struct sysctl_req *req, const void *p, size_t l)
56412243Sphk{
56538517Sdfr	size_t i = 0;
56612260Sphk
56712260Sphk	if (req->oldptr) {
56838517Sdfr		i = l;
56938517Sdfr		if (i > req->oldlen - req->oldidx)
57038517Sdfr			i = req->oldlen - req->oldidx;
57112260Sphk		if (i > 0)
57217971Sbde			bcopy(p, (char *)req->oldptr + req->oldidx, i);
57312243Sphk	}
57412260Sphk	req->oldidx += l;
57516282Snate	if (req->oldptr && i != l)
57611863Sphk		return (ENOMEM);
57712260Sphk	return (0);
57812243Sphk}
57912243Sphk
58012260Sphkstatic int
58138517Sdfrsysctl_new_kernel(struct sysctl_req *req, void *p, size_t l)
58212243Sphk{
58312260Sphk	if (!req->newptr)
58412260Sphk		return 0;
58512260Sphk	if (req->newlen - req->newidx < l)
58611863Sphk		return (EINVAL);
58717971Sbde	bcopy((char *)req->newptr + req->newidx, p, l);
58812243Sphk	req->newidx += l;
58912131Sphk	return (0);
59011863Sphk}
59111863Sphk
59216282Snateint
59338517Sdfrkernel_sysctl(struct proc *p, int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen, size_t *retval)
59416282Snate{
59516282Snate	int error = 0;
59616282Snate	struct sysctl_req req;
59716282Snate
59816282Snate	bzero(&req, sizeof req);
59916282Snate
60016282Snate	req.p = p;
60116282Snate
60216282Snate	if (oldlenp) {
60316282Snate		req.oldlen = *oldlenp;
60416282Snate	}
60516282Snate
60616282Snate	if (old) {
60716282Snate		req.oldptr= old;
60816282Snate	}
60916282Snate
61016282Snate	if (newlen) {
61116282Snate		req.newlen = newlen;
61216282Snate		req.newptr = new;
61316282Snate	}
61416282Snate
61516282Snate	req.oldfunc = sysctl_old_kernel;
61616282Snate	req.newfunc = sysctl_new_kernel;
61716282Snate	req.lock = 1;
61816282Snate
61916282Snate	/* XXX this should probably be done in a general way */
62016282Snate	while (memlock.sl_lock) {
62116282Snate		memlock.sl_want = 1;
62216282Snate		(void) tsleep((caddr_t)&memlock, PRIBIO+1, "sysctl", 0);
62316282Snate		memlock.sl_locked++;
62416282Snate	}
62516282Snate	memlock.sl_lock = 1;
62616282Snate
62716282Snate	error = sysctl_root(0, name, namelen, &req);
62816282Snate
62916282Snate	if (req.lock == 2)
63057975Sphk		vsunlock(req.oldptr, req.oldlen);
63116282Snate
63216282Snate	memlock.sl_lock = 0;
63316282Snate
63416282Snate	if (memlock.sl_want) {
63516282Snate		memlock.sl_want = 0;
63616282Snate		wakeup((caddr_t)&memlock);
63716282Snate	}
63816282Snate
63916282Snate	if (error && error != ENOMEM)
64016282Snate		return (error);
64116282Snate
64216282Snate	if (retval) {
64316282Snate		if (req.oldptr && req.oldidx > req.oldlen)
64416282Snate			*retval = req.oldlen;
64516282Snate		else
64616282Snate			*retval = req.oldidx;
64716282Snate	}
64816282Snate	return (error);
64916282Snate}
65016282Snate
65112260Sphk/*
65212260Sphk * Transfer function to/from user space.
65312260Sphk */
65412260Sphkstatic int
65538517Sdfrsysctl_old_user(struct sysctl_req *req, const void *p, size_t l)
65612243Sphk{
65738517Sdfr	int error = 0;
65838517Sdfr	size_t i = 0;
65912243Sphk
66012429Sphk	if (req->lock == 1 && req->oldptr) {
66112429Sphk		vslock(req->oldptr, req->oldlen);
66212429Sphk		req->lock = 2;
66312429Sphk	}
66412260Sphk	if (req->oldptr) {
66538517Sdfr		i = l;
66638517Sdfr		if (i > req->oldlen - req->oldidx)
66738517Sdfr			i = req->oldlen - req->oldidx;
66812260Sphk		if (i > 0)
66917971Sbde			error = copyout(p, (char *)req->oldptr + req->oldidx,
67017971Sbde					i);
67112260Sphk	}
67212260Sphk	req->oldidx += l;
67312243Sphk	if (error)
67412243Sphk		return (error);
67512260Sphk	if (req->oldptr && i < l)
67612243Sphk		return (ENOMEM);
67712260Sphk	return (0);
67812243Sphk}
67912243Sphk
68012260Sphkstatic int
68138517Sdfrsysctl_new_user(struct sysctl_req *req, void *p, size_t l)
68212243Sphk{
68312285Sphk	int error;
68412260Sphk
68512260Sphk	if (!req->newptr)
68612260Sphk		return 0;
68712260Sphk	if (req->newlen - req->newidx < l)
68812243Sphk		return (EINVAL);
68917971Sbde	error = copyin((char *)req->newptr + req->newidx, p, l);
69012243Sphk	req->newidx += l;
69112243Sphk	return (error);
69212243Sphk}
69312243Sphk
6941541Srgrimesint
69553977Sgreensysctl_find_oid(int *name, u_int namelen, struct sysctl_oid **noid,
69653977Sgreen    int *nindx, struct sysctl_req *req)
69712131Sphk{
69844078Sdfr	struct sysctl_oid *oid;
69953977Sgreen	int indx;
70012131Sphk
70153977Sgreen	oid = SLIST_FIRST(&sysctl__children);
70212131Sphk	indx = 0;
70344078Sdfr	while (oid && indx < CTL_MAXNAME) {
70444078Sdfr		if (oid->oid_number == name[indx]) {
70512131Sphk			indx++;
70644078Sdfr			if (oid->oid_kind & CTLFLAG_NOLOCK)
70712429Sphk				req->lock = 0;
70844078Sdfr			if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
70953977Sgreen				if (oid->oid_handler != NULL ||
71053977Sgreen				    indx == namelen) {
71153977Sgreen					*noid = oid;
71253977Sgreen					if (nindx != NULL)
71353977Sgreen						*nindx = indx;
71453977Sgreen					return (0);
71553977Sgreen				}
71653977Sgreen				oid = SLIST_FIRST(
71753977Sgreen				    (struct sysctl_oid_list *)oid->oid_arg1);
71853977Sgreen			} else if (indx == namelen) {
71953977Sgreen				*noid = oid;
72053977Sgreen				if (nindx != NULL)
72153977Sgreen					*nindx = indx;
72253977Sgreen				return (0);
72312131Sphk			} else {
72453977Sgreen				return (ENOTDIR);
72512131Sphk			}
72612131Sphk		} else {
72744078Sdfr			oid = SLIST_NEXT(oid, oid_link);
72812131Sphk		}
72912131Sphk	}
73053977Sgreen	return (ENOENT);
73153977Sgreen}
73253977Sgreen
73353977Sgreen/*
73453977Sgreen * Traverse our tree, and find the right node, execute whatever it points
73553977Sgreen * to, and return the resulting error code.
73653977Sgreen */
73753977Sgreen
73853977Sgreenint
73962573Sphksysctl_root(SYSCTL_HANDLER_ARGS)
74053977Sgreen{
74153977Sgreen	struct sysctl_oid *oid;
74253977Sgreen	int error, indx;
74353977Sgreen
74453977Sgreen	error = sysctl_find_oid(arg1, arg2, &oid, &indx, req);
74553977Sgreen	if (error)
74653977Sgreen		return (error);
74753977Sgreen
74853977Sgreen	if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
74953977Sgreen		/*
75053977Sgreen		 * You can't call a sysctl when it's a node, but has
75153977Sgreen		 * no handler.  Inform the user that it's a node.
75253977Sgreen		 * The indx may or may not be the same as namelen.
75353977Sgreen		 */
75453977Sgreen		if (oid->oid_handler == NULL)
75553977Sgreen			return (EISDIR);
75653977Sgreen	}
75753977Sgreen
75812131Sphk	/* If writing isn't allowed */
75944078Sdfr	if (req->newptr && (!(oid->oid_kind & CTLFLAG_WR) ||
76044078Sdfr	    ((oid->oid_kind & CTLFLAG_SECURE) && securelevel > 0)))
76112131Sphk		return (EPERM);
76212131Sphk
76312910Sphk	/* Most likely only root can write */
76444078Sdfr	if (!(oid->oid_kind & CTLFLAG_ANYBODY) &&
76512910Sphk	    req->newptr && req->p &&
76653977Sgreen	    (error = suser_xxx(0, req->p,
76746155Sphk	    (oid->oid_kind & CTLFLAG_PRISON) ? PRISON_ROOT : 0)))
76853977Sgreen		return (error);
76912910Sphk
77044078Sdfr	if (!oid->oid_handler)
77112131Sphk		return EINVAL;
77212131Sphk
77353977Sgreen	if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE)
77453977Sgreen		error = oid->oid_handler(oid, (int *)arg1 + indx, arg2 - indx,
77553977Sgreen		    req);
77653977Sgreen	else
77753977Sgreen		error = oid->oid_handler(oid, oid->oid_arg1, oid->oid_arg2,
77853977Sgreen		    req);
77953977Sgreen	return (error);
78012131Sphk}
78112131Sphk
78212221Sbde#ifndef _SYS_SYSPROTO_H_
78312171Sphkstruct sysctl_args {
78412171Sphk	int	*name;
78512171Sphk	u_int	namelen;
78612171Sphk	void	*old;
78712171Sphk	size_t	*oldlenp;
78812171Sphk	void	*new;
78912171Sphk	size_t	newlen;
79012171Sphk};
79112221Sbde#endif
79212171Sphk
79312131Sphkint
79430994Sphk__sysctl(struct proc *p, struct sysctl_args *uap)
7951541Srgrimes{
79638517Sdfr	int error, i, name[CTL_MAXNAME];
79738517Sdfr	size_t j;
7981541Srgrimes
7991541Srgrimes	if (uap->namelen > CTL_MAXNAME || uap->namelen < 2)
8001541Srgrimes		return (EINVAL);
80111863Sphk
8023308Sphk 	error = copyin(uap->name, &name, uap->namelen * sizeof(int));
8033308Sphk 	if (error)
8041541Srgrimes		return (error);
8051541Srgrimes
80612260Sphk	error = userland_sysctl(p, name, uap->namelen,
80712171Sphk		uap->old, uap->oldlenp, 0,
80812260Sphk		uap->new, uap->newlen, &j);
80912260Sphk	if (error && error != ENOMEM)
81012260Sphk		return (error);
81112260Sphk	if (uap->oldlenp) {
81212260Sphk		i = copyout(&j, uap->oldlenp, sizeof(j));
81312260Sphk		if (i)
81412260Sphk			return (i);
81512260Sphk	}
81612260Sphk	return (error);
81712171Sphk}
81812171Sphk
81912171Sphk/*
82012171Sphk * This is used from various compatibility syscalls too.  That's why name
82112171Sphk * must be in kernel space.
82212171Sphk */
82312171Sphkint
82438517Sdfruserland_sysctl(struct proc *p, int *name, u_int namelen, void *old, size_t *oldlenp, int inkernel, void *new, size_t newlen, size_t *retval)
82512171Sphk{
82612429Sphk	int error = 0;
82716159Sphk	struct sysctl_req req, req2;
82812171Sphk
82912243Sphk	bzero(&req, sizeof req);
83012243Sphk
83112285Sphk	req.p = p;
83212285Sphk
83312171Sphk	if (oldlenp) {
83412171Sphk		if (inkernel) {
83512243Sphk			req.oldlen = *oldlenp;
83612171Sphk		} else {
83712260Sphk			error = copyin(oldlenp, &req.oldlen, sizeof(*oldlenp));
83812171Sphk			if (error)
83912171Sphk				return (error);
84012171Sphk		}
84112171Sphk	}
84212171Sphk
84312243Sphk	if (old) {
84452644Sphk		if (!useracc(old, req.oldlen, VM_PROT_WRITE))
84512243Sphk			return (EFAULT);
84612243Sphk		req.oldptr= old;
84712243Sphk	}
84812131Sphk
84912171Sphk	if (newlen) {
85052644Sphk		if (!useracc(new, req.newlen, VM_PROT_READ))
85112243Sphk			return (EFAULT);
85212243Sphk		req.newlen = newlen;
85312243Sphk		req.newptr = new;
85411863Sphk	}
85512131Sphk
85612243Sphk	req.oldfunc = sysctl_old_user;
85712243Sphk	req.newfunc = sysctl_new_user;
85812429Sphk	req.lock = 1;
85911863Sphk
86012429Sphk	/* XXX this should probably be done in a general way */
86112429Sphk	while (memlock.sl_lock) {
86212429Sphk		memlock.sl_want = 1;
86312429Sphk		(void) tsleep((caddr_t)&memlock, PRIBIO+1, "sysctl", 0);
86412429Sphk		memlock.sl_locked++;
86512429Sphk	}
86612429Sphk	memlock.sl_lock = 1;
86712429Sphk
86816159Sphk	do {
86916159Sphk	    req2 = req;
87016159Sphk	    error = sysctl_root(0, name, namelen, &req2);
87116159Sphk	} while (error == EAGAIN);
87212243Sphk
87316159Sphk	req = req2;
87412429Sphk	if (req.lock == 2)
87557975Sphk		vsunlock(req.oldptr, req.oldlen);
87612429Sphk
87712429Sphk	memlock.sl_lock = 0;
87812429Sphk
87912429Sphk	if (memlock.sl_want) {
88012429Sphk		memlock.sl_want = 0;
88112429Sphk		wakeup((caddr_t)&memlock);
88212429Sphk	}
88312429Sphk
88412260Sphk	if (error && error != ENOMEM)
88512260Sphk		return (error);
88612260Sphk
88712260Sphk	if (retval) {
88812260Sphk		if (req.oldptr && req.oldidx > req.oldlen)
88912243Sphk			*retval = req.oldlen;
89012260Sphk		else
89112260Sphk			*retval = req.oldidx;
89211863Sphk	}
89312260Sphk	return (error);
8941541Srgrimes}
8951541Srgrimes
8961541Srgrimes#ifdef COMPAT_43
8971541Srgrimes#include <sys/socket.h>
89815103Sphk#include <vm/vm_param.h>
89915103Sphk
9001541Srgrimes#define	KINFO_PROC		(0<<8)
9011541Srgrimes#define	KINFO_RT		(1<<8)
9021541Srgrimes#define	KINFO_VNODE		(2<<8)
9031541Srgrimes#define	KINFO_FILE		(3<<8)
9041541Srgrimes#define	KINFO_METER		(4<<8)
9051541Srgrimes#define	KINFO_LOADAVG		(5<<8)
9061541Srgrimes#define	KINFO_CLOCKRATE		(6<<8)
9071541Srgrimes
9089455Speter/* Non-standard BSDI extension - only present on their 4.3 net-2 releases */
9099455Speter#define	KINFO_BSDI_SYSINFO	(101<<8)
9109455Speter
9119455Speter/*
9129455Speter * XXX this is bloat, but I hope it's better here than on the potentially
9139455Speter * limited kernel stack...  -Peter
9149455Speter */
9159455Speter
91612819Sphkstatic struct {
9179455Speter	int	bsdi_machine;		/* "i386" on BSD/386 */
9189455Speter/*      ^^^ this is an offset to the string, relative to the struct start */
9199455Speter	char	*pad0;
9209455Speter	long	pad1;
9219455Speter	long	pad2;
9229455Speter	long	pad3;
9239455Speter	u_long	pad4;
9249455Speter	u_long	pad5;
9259455Speter	u_long	pad6;
9269455Speter
9279455Speter	int	bsdi_ostype;		/* "BSD/386" on BSD/386 */
9289455Speter	int	bsdi_osrelease;		/* "1.1" on BSD/386 */
9299455Speter	long	pad7;
9309455Speter	long	pad8;
9319455Speter	char	*pad9;
9329455Speter
9339455Speter	long	pad10;
9349455Speter	long	pad11;
9359455Speter	int	pad12;
9369455Speter	long	pad13;
9379455Speter	quad_t	pad14;
9389455Speter	long	pad15;
9399455Speter
9409455Speter	struct	timeval pad16;
9419455Speter	/* we dont set this, because BSDI's uname used gethostname() instead */
9429455Speter	int	bsdi_hostname;		/* hostname on BSD/386 */
9439455Speter
9449455Speter	/* the actual string data is appended here */
9459455Speter
9469455Speter} bsdi_si;
9479455Speter/*
9489455Speter * this data is appended to the end of the bsdi_si structure during copyout.
9499455Speter * The "char *" offsets are relative to the base of the bsdi_si struct.
9509455Speter * This contains "FreeBSD\02.0-BUILT-nnnnnn\0i386\0", and these strings
9519455Speter * should not exceed the length of the buffer here... (or else!! :-)
9529455Speter */
95312819Sphkstatic char bsdi_strings[80];	/* It had better be less than this! */
9549455Speter
95512221Sbde#ifndef _SYS_SYSPROTO_H_
9561541Srgrimesstruct getkerninfo_args {
9571541Srgrimes	int	op;
9581541Srgrimes	char	*where;
95938864Sbde	size_t	*size;
9601541Srgrimes	int	arg;
9611541Srgrimes};
96212221Sbde#endif
9631541Srgrimes
9641549Srgrimesint
96530994Sphkogetkerninfo(struct proc *p, struct getkerninfo_args *uap)
9661541Srgrimes{
96712171Sphk	int error, name[6];
96838517Sdfr	size_t size;
9691541Srgrimes
9701541Srgrimes	switch (uap->op & 0xff00) {
9711541Srgrimes
9721541Srgrimes	case KINFO_RT:
97312171Sphk		name[0] = CTL_NET;
97412171Sphk		name[1] = PF_ROUTE;
97512171Sphk		name[2] = 0;
97612171Sphk		name[3] = (uap->op & 0xff0000) >> 16;
97712171Sphk		name[4] = uap->op & 0xff;
97812171Sphk		name[5] = uap->arg;
97912171Sphk		error = userland_sysctl(p, name, 6, uap->where, uap->size,
98012429Sphk			0, 0, 0, &size);
9811541Srgrimes		break;
9821541Srgrimes
9831541Srgrimes	case KINFO_VNODE:
98412171Sphk		name[0] = CTL_KERN;
98512171Sphk		name[1] = KERN_VNODE;
98612171Sphk		error = userland_sysctl(p, name, 2, uap->where, uap->size,
98712429Sphk			0, 0, 0, &size);
9881541Srgrimes		break;
9891541Srgrimes
9901541Srgrimes	case KINFO_PROC:
99112171Sphk		name[0] = CTL_KERN;
99212171Sphk		name[1] = KERN_PROC;
99312171Sphk		name[2] = uap->op & 0xff;
99412171Sphk		name[3] = uap->arg;
99512171Sphk		error = userland_sysctl(p, name, 4, uap->where, uap->size,
99612429Sphk			0, 0, 0, &size);
9971541Srgrimes		break;
9981541Srgrimes
9991541Srgrimes	case KINFO_FILE:
100012171Sphk		name[0] = CTL_KERN;
100112171Sphk		name[1] = KERN_FILE;
100212171Sphk		error = userland_sysctl(p, name, 2, uap->where, uap->size,
100312429Sphk			0, 0, 0, &size);
10041541Srgrimes		break;
10051541Srgrimes
10061541Srgrimes	case KINFO_METER:
100712171Sphk		name[0] = CTL_VM;
100812171Sphk		name[1] = VM_METER;
100912171Sphk		error = userland_sysctl(p, name, 2, uap->where, uap->size,
101012429Sphk			0, 0, 0, &size);
10111541Srgrimes		break;
10121541Srgrimes
10131541Srgrimes	case KINFO_LOADAVG:
101412171Sphk		name[0] = CTL_VM;
101512171Sphk		name[1] = VM_LOADAVG;
101612171Sphk		error = userland_sysctl(p, name, 2, uap->where, uap->size,
101712429Sphk			0, 0, 0, &size);
10181541Srgrimes		break;
10191541Srgrimes
10201541Srgrimes	case KINFO_CLOCKRATE:
102112171Sphk		name[0] = CTL_KERN;
102212171Sphk		name[1] = KERN_CLOCKRATE;
102312171Sphk		error = userland_sysctl(p, name, 2, uap->where, uap->size,
102412429Sphk			0, 0, 0, &size);
10251541Srgrimes		break;
10261541Srgrimes
10279455Speter	case KINFO_BSDI_SYSINFO: {
10289455Speter		/*
10299455Speter		 * this is pretty crude, but it's just enough for uname()
10309455Speter		 * from BSDI's 1.x libc to work.
10319455Speter		 *
10329455Speter		 * In particular, it doesn't return the same results when
10339455Speter		 * the supplied buffer is too small.  BSDI's version apparently
10349455Speter		 * will return the amount copied, and set the *size to how
10359455Speter		 * much was needed.  The emulation framework here isn't capable
10369455Speter		 * of that, so we just set both to the amount copied.
10379455Speter		 * BSDI's 2.x product apparently fails with ENOMEM in this
10389455Speter		 * scenario.
10399455Speter		 */
10409455Speter
10419455Speter		u_int needed;
10429455Speter		u_int left;
10439455Speter		char *s;
10449455Speter
10459455Speter		bzero((char *)&bsdi_si, sizeof(bsdi_si));
10469455Speter		bzero(bsdi_strings, sizeof(bsdi_strings));
10479455Speter
10489455Speter		s = bsdi_strings;
10499455Speter
10509455Speter		bsdi_si.bsdi_ostype = (s - bsdi_strings) + sizeof(bsdi_si);
10519455Speter		strcpy(s, ostype);
10529455Speter		s += strlen(s) + 1;
10539455Speter
10549455Speter		bsdi_si.bsdi_osrelease = (s - bsdi_strings) + sizeof(bsdi_si);
10559455Speter		strcpy(s, osrelease);
10569455Speter		s += strlen(s) + 1;
10579455Speter
10589455Speter		bsdi_si.bsdi_machine = (s - bsdi_strings) + sizeof(bsdi_si);
10599455Speter		strcpy(s, machine);
10609455Speter		s += strlen(s) + 1;
10619455Speter
10629455Speter		needed = sizeof(bsdi_si) + (s - bsdi_strings);
10639455Speter
10649455Speter		if (uap->where == NULL) {
10659455Speter			/* process is asking how much buffer to supply.. */
10669455Speter			size = needed;
10679455Speter			error = 0;
10689455Speter			break;
10699455Speter		}
10709455Speter
10719455Speter
10729455Speter		/* if too much buffer supplied, trim it down */
10739455Speter		if (size > needed)
10749455Speter			size = needed;
10759455Speter
10769455Speter		/* how much of the buffer is remaining */
10779455Speter		left = size;
10789455Speter
10799455Speter		if ((error = copyout((char *)&bsdi_si, uap->where, left)) != 0)
10809455Speter			break;
10819455Speter
10829455Speter		/* is there any point in continuing? */
10839455Speter		if (left > sizeof(bsdi_si)) {
10849455Speter			left -= sizeof(bsdi_si);
10859455Speter			error = copyout(&bsdi_strings,
10869455Speter					uap->where + sizeof(bsdi_si), left);
10879455Speter		}
10889455Speter		break;
10899455Speter	}
10909455Speter
10911541Srgrimes	default:
10921541Srgrimes		return (EOPNOTSUPP);
10931541Srgrimes	}
10941541Srgrimes	if (error)
10951541Srgrimes		return (error);
109630994Sphk	p->p_retval[0] = size;
10971541Srgrimes	if (uap->size)
10981541Srgrimes		error = copyout((caddr_t)&size, (caddr_t)uap->size,
10991541Srgrimes		    sizeof(size));
11001541Srgrimes	return (error);
11011541Srgrimes}
11021541Srgrimes#endif /* COMPAT_43 */
1103