kern_sysctl.c revision 83968
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 83968 2001-09-26 19:51:25Z rwatson $
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>
5182746Sdillon#include <sys/lock.h>
5282746Sdillon#include <sys/mutex.h>
5315103Sphk#include <sys/sysproto.h>
5412645Sbde#include <vm/vm.h>
5512662Sdg#include <vm/vm_extern.h>
5612645Sbde
5730354Sphkstatic MALLOC_DEFINE(M_SYSCTL, "sysctl", "sysctl internal magic");
5863212Sabialstatic MALLOC_DEFINE(M_SYSCTLOID, "sysctloid", "sysctl dynamic oids");
5930309Sphk
6012429Sphk/*
6112429Sphk * Locking and stats
6212429Sphk */
6312429Sphkstatic struct sysctl_lock {
6412429Sphk	int	sl_lock;
6512429Sphk	int	sl_want;
6612429Sphk	int	sl_locked;
6712429Sphk} memlock;
6812429Sphk
6962573Sphkstatic int sysctl_root(SYSCTL_HANDLER_ARGS);
7012429Sphk
7144078Sdfrstruct sysctl_oid_list sysctl__children; /* root list */
7212152Sphk
7363212Sabialstatic struct sysctl_oid *
7463212Sabialsysctl_find_oidname(const char *name, struct sysctl_oid_list *list)
7563212Sabial{
7663212Sabial	struct sysctl_oid *oidp;
7763212Sabial
7863212Sabial	SLIST_FOREACH(oidp, list, oid_link) {
7963212Sabial		if (strcmp(oidp->oid_name, name) == 0) {
8063212Sabial			return (oidp);
8163212Sabial		}
8263212Sabial	}
8363212Sabial	return (NULL);
8463212Sabial}
8563212Sabial
8612623Sphk/*
8712623Sphk * Initialization of the MIB tree.
8812623Sphk *
8944078Sdfr * Order by number in each list.
9012623Sphk */
9112429Sphk
9280338Sroamvoid
9380338Sroamsysctl_register_oid(struct sysctl_oid *oidp)
9412152Sphk{
9544078Sdfr	struct sysctl_oid_list *parent = oidp->oid_parent;
9644078Sdfr	struct sysctl_oid *p;
9744078Sdfr	struct sysctl_oid *q;
9812197Sbde
9944078Sdfr	/*
10063212Sabial	 * First check if another oid with the same name already
10163212Sabial	 * exists in the parent's list.
10263212Sabial	 */
10363212Sabial	p = sysctl_find_oidname(oidp->oid_name, parent);
10463212Sabial	if (p != NULL) {
10563212Sabial		if ((p->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
10663212Sabial			p->oid_refcnt++;
10763212Sabial			return;
10863212Sabial		} else {
10963212Sabial			printf("can't re-use a leaf (%s)!\n", p->oid_name);
11063212Sabial			return;
11163212Sabial		}
11263212Sabial	}
11363212Sabial	/*
11444078Sdfr	 * If this oid has a number OID_AUTO, give it a number which
11580339Sroam	 * is greater than any current oid.
11680339Sroam	 * NOTE: DO NOT change the starting value here, change it in
11780339Sroam	 * <sys/sysctl.h>, and make sure it is at least 256 to
11880339Sroam	 * accomodate e.g. net.inet.raw as a static sysctl node.
11944078Sdfr	 */
12044078Sdfr	if (oidp->oid_number == OID_AUTO) {
12180339Sroam		static int newoid = CTL_AUTO_START;
12271510Smckusick
12371510Smckusick		oidp->oid_number = newoid++;
12471510Smckusick		if (newoid == 0x7fffffff)
12571510Smckusick			panic("out of oids");
12680339Sroam	} else if (oidp->oid_number >= CTL_AUTO_START) {
12780339Sroam		panic("static sysctl oid too high: %d", oidp->oid_number);
12844078Sdfr	}
12944078Sdfr
13044078Sdfr	/*
13144078Sdfr	 * Insert the oid into the parent's list in order.
13244078Sdfr	 */
13344078Sdfr	q = NULL;
13444078Sdfr	SLIST_FOREACH(p, parent, oid_link) {
13544078Sdfr		if (oidp->oid_number < p->oid_number)
13644078Sdfr			break;
13744078Sdfr		q = p;
13844078Sdfr	}
13944078Sdfr	if (q)
14044078Sdfr		SLIST_INSERT_AFTER(q, oidp, oid_link);
14144078Sdfr	else
14244078Sdfr		SLIST_INSERT_HEAD(parent, oidp, oid_link);
14312152Sphk}
14412131Sphk
14580338Sroamvoid
14680338Sroamsysctl_unregister_oid(struct sysctl_oid *oidp)
14712152Sphk{
14860938Sjake	SLIST_REMOVE(oidp->oid_parent, oidp, sysctl_oid, oid_link);
14944078Sdfr}
15012152Sphk
15163212Sabial/* Initialize a new context to keep track of dynamically added sysctls. */
15263212Sabialint
15363212Sabialsysctl_ctx_init(struct sysctl_ctx_list *c)
15463212Sabial{
15563212Sabial
15663212Sabial	if (c == NULL) {
15763212Sabial		return (EINVAL);
15863212Sabial	}
15963212Sabial	TAILQ_INIT(c);
16063212Sabial	return (0);
16163212Sabial}
16263212Sabial
16363212Sabial/* Free the context, and destroy all dynamic oids registered in this context */
16463212Sabialint
16563212Sabialsysctl_ctx_free(struct sysctl_ctx_list *clist)
16663212Sabial{
16763212Sabial	struct sysctl_ctx_entry *e, *e1;
16863212Sabial	int error;
16963212Sabial
17063212Sabial	error = 0;
17163212Sabial	/*
17263212Sabial	 * First perform a "dry run" to check if it's ok to remove oids.
17363212Sabial	 * XXX FIXME
17463212Sabial	 * XXX This algorithm is a hack. But I don't know any
17563212Sabial	 * XXX better solution for now...
17663212Sabial	 */
17763212Sabial	TAILQ_FOREACH(e, clist, link) {
17863212Sabial		error = sysctl_remove_oid(e->entry, 0, 0);
17963212Sabial		if (error)
18063212Sabial			break;
18163212Sabial	}
18263212Sabial	/*
18363212Sabial	 * Restore deregistered entries, either from the end,
18463212Sabial	 * or from the place where error occured.
18563212Sabial	 * e contains the entry that was not unregistered
18663212Sabial	 */
18763212Sabial	if (error)
18863212Sabial		e1 = TAILQ_PREV(e, sysctl_ctx_list, link);
18963212Sabial	else
19063212Sabial		e1 = TAILQ_LAST(clist, sysctl_ctx_list);
19163212Sabial	while (e1 != NULL) {
19263212Sabial		sysctl_register_oid(e1->entry);
19363212Sabial		e1 = TAILQ_PREV(e1, sysctl_ctx_list, link);
19463212Sabial	}
19563212Sabial	if (error)
19663212Sabial		return(EBUSY);
19763212Sabial	/* Now really delete the entries */
19863212Sabial	e = TAILQ_FIRST(clist);
19963212Sabial	while (e != NULL) {
20063212Sabial		e1 = TAILQ_NEXT(e, link);
20163212Sabial		error = sysctl_remove_oid(e->entry, 1, 0);
20263212Sabial		if (error)
20363212Sabial			panic("sysctl_remove_oid: corrupt tree, entry: %s",
20463212Sabial			    e->entry->oid_name);
20563212Sabial		free(e, M_SYSCTLOID);
20663212Sabial		e = e1;
20763212Sabial	}
20863212Sabial	return (error);
20963212Sabial}
21063212Sabial
21163212Sabial/* Add an entry to the context */
21263212Sabialstruct sysctl_ctx_entry *
21363212Sabialsysctl_ctx_entry_add(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp)
21463212Sabial{
21563212Sabial	struct sysctl_ctx_entry *e;
21663212Sabial
21763212Sabial	if (clist == NULL || oidp == NULL)
21863212Sabial		return(NULL);
21963978Speter	e = malloc(sizeof(struct sysctl_ctx_entry), M_SYSCTLOID, M_WAITOK);
22063212Sabial	e->entry = oidp;
22163212Sabial	TAILQ_INSERT_HEAD(clist, e, link);
22263212Sabial	return (e);
22363212Sabial}
22463212Sabial
22563212Sabial/* Find an entry in the context */
22663212Sabialstruct sysctl_ctx_entry *
22763212Sabialsysctl_ctx_entry_find(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp)
22863212Sabial{
22963212Sabial	struct sysctl_ctx_entry *e;
23063212Sabial
23163212Sabial	if (clist == NULL || oidp == NULL)
23263212Sabial		return(NULL);
23371999Sphk	TAILQ_FOREACH(e, clist, link) {
23463212Sabial		if(e->entry == oidp)
23563212Sabial			return(e);
23663212Sabial	}
23763212Sabial	return (e);
23863212Sabial}
23963212Sabial
24044078Sdfr/*
24163212Sabial * Delete an entry from the context.
24263212Sabial * NOTE: this function doesn't free oidp! You have to remove it
24363212Sabial * with sysctl_remove_oid().
24463212Sabial */
24563212Sabialint
24663212Sabialsysctl_ctx_entry_del(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp)
24763212Sabial{
24863212Sabial	struct sysctl_ctx_entry *e;
24963212Sabial
25063212Sabial	if (clist == NULL || oidp == NULL)
25163212Sabial		return (EINVAL);
25263212Sabial	e = sysctl_ctx_entry_find(clist, oidp);
25363212Sabial	if (e != NULL) {
25463212Sabial		TAILQ_REMOVE(clist, e, link);
25563212Sabial		free(e, M_SYSCTLOID);
25663212Sabial		return (0);
25763212Sabial	} else
25863212Sabial		return (ENOENT);
25963212Sabial}
26063212Sabial
26163212Sabial/*
26263212Sabial * Remove dynamically created sysctl trees.
26363212Sabial * oidp - top of the tree to be removed
26463212Sabial * del - if 0 - just deregister, otherwise free up entries as well
26563212Sabial * recurse - if != 0 traverse the subtree to be deleted
26663212Sabial */
26763212Sabialint
26863212Sabialsysctl_remove_oid(struct sysctl_oid *oidp, int del, int recurse)
26963212Sabial{
27063212Sabial	struct sysctl_oid *p;
27163212Sabial	int error;
27263212Sabial
27363212Sabial	if (oidp == NULL)
27463212Sabial		return(EINVAL);
27563212Sabial	if ((oidp->oid_kind & CTLFLAG_DYN) == 0) {
27663212Sabial		printf("can't remove non-dynamic nodes!\n");
27763212Sabial		return (EINVAL);
27863212Sabial	}
27963212Sabial	/*
28063212Sabial	 * WARNING: normal method to do this should be through
28163212Sabial	 * sysctl_ctx_free(). Use recursing as the last resort
28263212Sabial	 * method to purge your sysctl tree of leftovers...
28363212Sabial	 * However, if some other code still references these nodes,
28463212Sabial	 * it will panic.
28563212Sabial	 */
28663212Sabial	if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
28763212Sabial		if (oidp->oid_refcnt == 1) {
28863212Sabial			SLIST_FOREACH(p, SYSCTL_CHILDREN(oidp), oid_link) {
28963212Sabial				if (!recurse)
29063212Sabial					return (ENOTEMPTY);
29163212Sabial				error = sysctl_remove_oid(p, del, recurse);
29263212Sabial				if (error)
29363212Sabial					return (error);
29463212Sabial			}
29563212Sabial			if (del)
29663212Sabial				free(SYSCTL_CHILDREN(oidp), M_SYSCTLOID);
29763212Sabial		}
29863212Sabial	}
29963212Sabial	if (oidp->oid_refcnt > 1 ) {
30063212Sabial		oidp->oid_refcnt--;
30163212Sabial	} else {
30263212Sabial		if (oidp->oid_refcnt == 0) {
30363212Sabial			printf("Warning: bad oid_refcnt=%u (%s)!\n",
30463212Sabial				oidp->oid_refcnt, oidp->oid_name);
30563212Sabial			return (EINVAL);
30663212Sabial		}
30763212Sabial		sysctl_unregister_oid(oidp);
30863212Sabial		if (del) {
30963978Speter			free((void *)(uintptr_t)(const void *)oidp->oid_name,
31063978Speter			     M_SYSCTLOID);
31163212Sabial			free(oidp, M_SYSCTLOID);
31263212Sabial		}
31363212Sabial	}
31463212Sabial	return (0);
31563212Sabial}
31663212Sabial
31763212Sabial/*
31863212Sabial * Create new sysctls at run time.
31963212Sabial * clist may point to a valid context initialized with sysctl_ctx_init().
32063212Sabial */
32163212Sabialstruct sysctl_oid *
32263212Sabialsysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent,
32370679Sjhb	int number, const char *name, int kind, void *arg1, int arg2,
32470679Sjhb	int (*handler)(SYSCTL_HANDLER_ARGS), const char *fmt, const char *descr)
32563212Sabial{
32663212Sabial	struct sysctl_oid *oidp;
32763212Sabial	ssize_t len;
32863978Speter	char *newname;
32963212Sabial
33063212Sabial	/* You have to hook up somewhere.. */
33163212Sabial	if (parent == NULL)
33263212Sabial		return(NULL);
33363212Sabial	/* Check if the node already exists, otherwise create it */
33463212Sabial	oidp = sysctl_find_oidname(name, parent);
33563212Sabial	if (oidp != NULL) {
33663212Sabial		if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
33763212Sabial			oidp->oid_refcnt++;
33863212Sabial			/* Update the context */
33963212Sabial			if (clist != NULL)
34063212Sabial				sysctl_ctx_entry_add(clist, oidp);
34163212Sabial			return (oidp);
34263212Sabial		} else {
34363212Sabial			printf("can't re-use a leaf (%s)!\n", name);
34463212Sabial			return (NULL);
34563212Sabial		}
34663212Sabial	}
34769781Sdwmalone	oidp = malloc(sizeof(struct sysctl_oid), M_SYSCTLOID, M_WAITOK|M_ZERO);
34863212Sabial	oidp->oid_parent = parent;
34963212Sabial	SLIST_NEXT(oidp, oid_link) = NULL;
35063212Sabial	oidp->oid_number = number;
35163212Sabial	oidp->oid_refcnt = 1;
35263212Sabial	len = strlen(name);
35363978Speter	newname = malloc(len + 1, M_SYSCTLOID, M_WAITOK);
35463978Speter	bcopy(name, newname, len + 1);
35563978Speter	newname[len] = '\0';
35663978Speter	oidp->oid_name = newname;
35763212Sabial	oidp->oid_handler = handler;
35863212Sabial	oidp->oid_kind = CTLFLAG_DYN | kind;
35963212Sabial	if ((kind & CTLTYPE) == CTLTYPE_NODE) {
36063212Sabial		/* Allocate space for children */
36163212Sabial		SYSCTL_CHILDREN(oidp) = malloc(sizeof(struct sysctl_oid_list),
36263212Sabial		    M_SYSCTLOID, M_WAITOK);
36363212Sabial		SLIST_INIT(SYSCTL_CHILDREN(oidp));
36463212Sabial	} else {
36563212Sabial		oidp->oid_arg1 = arg1;
36663212Sabial		oidp->oid_arg2 = arg2;
36763212Sabial	}
36863212Sabial	oidp->oid_fmt = fmt;
36963212Sabial	/* Update the context, if used */
37063212Sabial	if (clist != NULL)
37163212Sabial		sysctl_ctx_entry_add(clist, oidp);
37263212Sabial	/* Register this oid */
37363212Sabial	sysctl_register_oid(oidp);
37463212Sabial	return (oidp);
37563212Sabial}
37663212Sabial
37763212Sabial/*
37844078Sdfr * Register the kernel's oids on startup.
37944078Sdfr */
38078161SpeterSET_DECLARE(sysctl_set, struct sysctl_oid);
38112152Sphk
38280338Sroamstatic void
38380338Sroamsysctl_register_all(void *arg)
38438869Sbde{
38578161Speter	struct sysctl_oid **oidp;
38678161Speter
38778161Speter	SET_FOREACH(oidp, sysctl_set)
38878161Speter		sysctl_register_oid(*oidp);
38938869Sbde}
39044078SdfrSYSINIT(sysctl, SI_SUB_KMEM, SI_ORDER_ANY, sysctl_register_all, 0);
39144078Sdfr
39212623Sphk/*
39312623Sphk * "Staff-functions"
39412623Sphk *
39512650Sphk * These functions implement a presently undocumented interface
39612650Sphk * used by the sysctl program to walk the tree, and get the type
39712650Sphk * so it can print the value.
39812650Sphk * This interface is under work and consideration, and should probably
39912650Sphk * be killed with a big axe by the first person who can find the time.
40012650Sphk * (be aware though, that the proper interface isn't as obvious as it
40112650Sphk * may seem, there are various conflicting requirements.
40212650Sphk *
40312623Sphk * {0,0}	printf the entire MIB-tree.
40412623Sphk * {0,1,...}	return the name of the "..." OID.
40542467Sphk * {0,2,...}	return the next OID.
40612623Sphk * {0,3}	return the OID of the name in "new"
40712650Sphk * {0,4,...}	return the kind & format info for the "..." OID.
40812623Sphk */
40912623Sphk
41012152Sphkstatic void
41144078Sdfrsysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i)
41212152Sphk{
41344078Sdfr	int k;
41444078Sdfr	struct sysctl_oid *oidp;
41512152Sphk
41644078Sdfr	SLIST_FOREACH(oidp, l, oid_link) {
41712152Sphk
41812152Sphk		for (k=0; k<i; k++)
41912152Sphk			printf(" ");
42012152Sphk
42144078Sdfr		printf("%d %s ", oidp->oid_number, oidp->oid_name);
42212152Sphk
42312152Sphk		printf("%c%c",
42444078Sdfr			oidp->oid_kind & CTLFLAG_RD ? 'R':' ',
42544078Sdfr			oidp->oid_kind & CTLFLAG_WR ? 'W':' ');
42612152Sphk
42744078Sdfr		if (oidp->oid_handler)
42815241Sphk			printf(" *Handler");
42915241Sphk
43044078Sdfr		switch (oidp->oid_kind & CTLTYPE) {
43112243Sphk			case CTLTYPE_NODE:
43215241Sphk				printf(" Node\n");
43344078Sdfr				if (!oidp->oid_handler) {
43412152Sphk					sysctl_sysctl_debug_dump_node(
43544078Sdfr						oidp->oid_arg1, i+2);
43612152Sphk				}
43712152Sphk				break;
43812152Sphk			case CTLTYPE_INT:    printf(" Int\n"); break;
43912152Sphk			case CTLTYPE_STRING: printf(" String\n"); break;
44012152Sphk			case CTLTYPE_QUAD:   printf(" Quad\n"); break;
44112152Sphk			case CTLTYPE_OPAQUE: printf(" Opaque/struct\n"); break;
44212152Sphk			default:	     printf("\n");
44312152Sphk		}
44412152Sphk
44512152Sphk	}
44612152Sphk}
44712152Sphk
44812152Sphkstatic int
44962573Sphksysctl_sysctl_debug(SYSCTL_HANDLER_ARGS)
45012152Sphk{
45144078Sdfr	sysctl_sysctl_debug_dump_node(&sysctl__children, 0);
45212152Sphk	return ENOENT;
45312152Sphk}
45412152Sphk
45512152SphkSYSCTL_PROC(_sysctl, 0, debug, CTLTYPE_STRING|CTLFLAG_RD,
45612623Sphk	0, 0, sysctl_sysctl_debug, "-", "");
45712152Sphk
45812623Sphkstatic int
45962573Sphksysctl_sysctl_name(SYSCTL_HANDLER_ARGS)
46012623Sphk{
46112623Sphk	int *name = (int *) arg1;
46212623Sphk	u_int namelen = arg2;
46344078Sdfr	int error = 0;
46444078Sdfr	struct sysctl_oid *oid;
46544972Sphk	struct sysctl_oid_list *lsp = &sysctl__children, *lsp2;
46612623Sphk	char buf[10];
46712131Sphk
46812623Sphk	while (namelen) {
46912623Sphk		if (!lsp) {
47041514Sarchie			snprintf(buf,sizeof(buf),"%d",*name);
47112623Sphk			if (req->oldidx)
47212623Sphk				error = SYSCTL_OUT(req, ".", 1);
47312623Sphk			if (!error)
47412623Sphk				error = SYSCTL_OUT(req, buf, strlen(buf));
47512623Sphk			if (error)
47612623Sphk				return (error);
47712623Sphk			namelen--;
47812623Sphk			name++;
47912623Sphk			continue;
48012623Sphk		}
48144972Sphk		lsp2 = 0;
48244078Sdfr		SLIST_FOREACH(oid, lsp, oid_link) {
48344078Sdfr			if (oid->oid_number != *name)
48412623Sphk				continue;
48512131Sphk
48612623Sphk			if (req->oldidx)
48712623Sphk				error = SYSCTL_OUT(req, ".", 1);
48812623Sphk			if (!error)
48944078Sdfr				error = SYSCTL_OUT(req, oid->oid_name,
49044078Sdfr					strlen(oid->oid_name));
49112623Sphk			if (error)
49212623Sphk				return (error);
49312623Sphk
49412623Sphk			namelen--;
49512623Sphk			name++;
49612623Sphk
49744972Sphk			if ((oid->oid_kind & CTLTYPE) != CTLTYPE_NODE)
49812623Sphk				break;
49912623Sphk
50044078Sdfr			if (oid->oid_handler)
50112623Sphk				break;
50212623Sphk
50344972Sphk			lsp2 = (struct sysctl_oid_list *)oid->oid_arg1;
50412623Sphk			break;
50512623Sphk		}
50644972Sphk		lsp = lsp2;
50712623Sphk	}
50812623Sphk	return (SYSCTL_OUT(req, "", 1));
50912623Sphk}
51012623Sphk
51112623SphkSYSCTL_NODE(_sysctl, 1, name, CTLFLAG_RD, sysctl_sysctl_name, "");
51212623Sphk
51312623Sphkstatic int
51463978Spetersysctl_sysctl_next_ls(struct sysctl_oid_list *lsp, int *name, u_int namelen,
51544078Sdfr	int *next, int *len, int level, struct sysctl_oid **oidpp)
51612623Sphk{
51744078Sdfr	struct sysctl_oid *oidp;
51812623Sphk
51912623Sphk	*len = level;
52044078Sdfr	SLIST_FOREACH(oidp, lsp, oid_link) {
52144078Sdfr		*next = oidp->oid_number;
52244078Sdfr		*oidpp = oidp;
52312623Sphk
52412623Sphk		if (!namelen) {
52544078Sdfr			if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
52612623Sphk				return 0;
52744078Sdfr			if (oidp->oid_handler)
52812623Sphk				/* We really should call the handler here...*/
52912623Sphk				return 0;
53044078Sdfr			lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
53163978Speter			if (!sysctl_sysctl_next_ls(lsp, 0, 0, next+1,
53244078Sdfr				len, level+1, oidpp))
53315241Sphk				return 0;
53415241Sphk			goto next;
53512623Sphk		}
53612623Sphk
53744078Sdfr		if (oidp->oid_number < *name)
53812623Sphk			continue;
53912623Sphk
54044078Sdfr		if (oidp->oid_number > *name) {
54144078Sdfr			if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
54212623Sphk				return 0;
54344078Sdfr			if (oidp->oid_handler)
54412623Sphk				return 0;
54544078Sdfr			lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
54663978Speter			if (!sysctl_sysctl_next_ls(lsp, name+1, namelen-1,
54744078Sdfr				next+1, len, level+1, oidpp))
54812623Sphk				return (0);
54915241Sphk			goto next;
55012623Sphk		}
55144078Sdfr		if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
55212623Sphk			continue;
55312623Sphk
55444078Sdfr		if (oidp->oid_handler)
55512623Sphk			continue;
55612623Sphk
55744078Sdfr		lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
55863978Speter		if (!sysctl_sysctl_next_ls(lsp, name+1, namelen-1, next+1,
55944078Sdfr			len, level+1, oidpp))
56012623Sphk			return (0);
56115241Sphk	next:
56212623Sphk		namelen = 1;
56312623Sphk		*len = level;
56412623Sphk	}
56512623Sphk	return 1;
56612623Sphk}
56712623Sphk
56812623Sphkstatic int
56962573Sphksysctl_sysctl_next(SYSCTL_HANDLER_ARGS)
57012623Sphk{
57112623Sphk	int *name = (int *) arg1;
57212623Sphk	u_int namelen = arg2;
57312623Sphk	int i, j, error;
57412623Sphk	struct sysctl_oid *oid;
57544078Sdfr	struct sysctl_oid_list *lsp = &sysctl__children;
57612623Sphk	int newoid[CTL_MAXNAME];
57712623Sphk
57863978Speter	i = sysctl_sysctl_next_ls(lsp, name, namelen, newoid, &j, 1, &oid);
57912623Sphk	if (i)
58012623Sphk		return ENOENT;
58112650Sphk	error = SYSCTL_OUT(req, newoid, j * sizeof (int));
58212623Sphk	return (error);
58312623Sphk}
58412623Sphk
58512623SphkSYSCTL_NODE(_sysctl, 2, next, CTLFLAG_RD, sysctl_sysctl_next, "");
58612623Sphk
58712623Sphkstatic int
58844078Sdfrname2oid (char *name, int *oid, int *len, struct sysctl_oid **oidpp)
58912623Sphk{
59044078Sdfr	int i;
59144078Sdfr	struct sysctl_oid *oidp;
59244078Sdfr	struct sysctl_oid_list *lsp = &sysctl__children;
59312623Sphk	char *p;
59412623Sphk
59512623Sphk	if (!*name)
59612623Sphk		return ENOENT;
59712623Sphk
59812623Sphk	p = name + strlen(name) - 1 ;
59912623Sphk	if (*p == '.')
60012623Sphk		*p = '\0';
60112623Sphk
60212623Sphk	*len = 0;
60312623Sphk
60412623Sphk	for (p = name; *p && *p != '.'; p++)
60512623Sphk		;
60612623Sphk	i = *p;
60712623Sphk	if (i == '.')
60812623Sphk		*p = '\0';
60912623Sphk
61044078Sdfr	oidp = SLIST_FIRST(lsp);
61112623Sphk
61244078Sdfr	while (oidp && *len < CTL_MAXNAME) {
61344078Sdfr		if (strcmp(name, oidp->oid_name)) {
61444078Sdfr			oidp = SLIST_NEXT(oidp, oid_link);
61512623Sphk			continue;
61612623Sphk		}
61744078Sdfr		*oid++ = oidp->oid_number;
61812623Sphk		(*len)++;
61912623Sphk
62012623Sphk		if (!i) {
62144078Sdfr			if (oidpp)
62244078Sdfr				*oidpp = oidp;
62312623Sphk			return (0);
62412623Sphk		}
62512623Sphk
62644078Sdfr		if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
62712623Sphk			break;
62812623Sphk
62944078Sdfr		if (oidp->oid_handler)
63012623Sphk			break;
63112623Sphk
63244078Sdfr		lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
63344078Sdfr		oidp = SLIST_FIRST(lsp);
63412623Sphk		name = p+1;
63512623Sphk		for (p = name; *p && *p != '.'; p++)
63612623Sphk				;
63712623Sphk		i = *p;
63812623Sphk		if (i == '.')
63912623Sphk			*p = '\0';
64012623Sphk	}
64112623Sphk	return ENOENT;
64212623Sphk}
64312623Sphk
64412623Sphkstatic int
64562573Sphksysctl_sysctl_name2oid(SYSCTL_HANDLER_ARGS)
64612623Sphk{
64712623Sphk	char *p;
64812623Sphk	int error, oid[CTL_MAXNAME], len;
64912623Sphk	struct sysctl_oid *op = 0;
65012623Sphk
65112623Sphk	if (!req->newlen)
65212623Sphk		return ENOENT;
65345140Sphk	if (req->newlen >= MAXPATHLEN)	/* XXX arbitrary, undocumented */
65445140Sphk		return (ENAMETOOLONG);
65512623Sphk
65612623Sphk	p = malloc(req->newlen+1, M_SYSCTL, M_WAITOK);
65712623Sphk
65812623Sphk	error = SYSCTL_IN(req, p, req->newlen);
65912623Sphk	if (error) {
66012623Sphk		free(p, M_SYSCTL);
66112623Sphk		return (error);
66212623Sphk	}
66312623Sphk
66412623Sphk	p [req->newlen] = '\0';
66512623Sphk
66612623Sphk	error = name2oid(p, oid, &len, &op);
66712623Sphk
66812623Sphk	free(p, M_SYSCTL);
66912623Sphk
67012623Sphk	if (error)
67112623Sphk		return (error);
67212623Sphk
67312650Sphk	error = SYSCTL_OUT(req, oid, len * sizeof *oid);
67412623Sphk	return (error);
67512623Sphk}
67612623Sphk
67712910SphkSYSCTL_PROC(_sysctl, 3, name2oid, CTLFLAG_RW|CTLFLAG_ANYBODY, 0, 0,
67812623Sphk	sysctl_sysctl_name2oid, "I", "");
67912623Sphk
68012623Sphkstatic int
68162573Sphksysctl_sysctl_oidfmt(SYSCTL_HANDLER_ARGS)
68212623Sphk{
68344078Sdfr	struct sysctl_oid *oid;
68453977Sgreen	int error;
68512623Sphk
68653977Sgreen	error = sysctl_find_oid(arg1, arg2, &oid, NULL, req);
68753977Sgreen	if (error)
68853977Sgreen		return (error);
68912623Sphk
69044078Sdfr	if (!oid->oid_fmt)
69153977Sgreen		return (ENOENT);
69253977Sgreen	error = SYSCTL_OUT(req, &oid->oid_kind, sizeof(oid->oid_kind));
69353977Sgreen	if (error)
69453977Sgreen		return (error);
69553977Sgreen	error = SYSCTL_OUT(req, oid->oid_fmt, strlen(oid->oid_fmt) + 1);
69612650Sphk	return (error);
69712623Sphk}
69812623Sphk
69942467Sphk
70012623SphkSYSCTL_NODE(_sysctl, 4, oidfmt, CTLFLAG_RD, sysctl_sysctl_oidfmt, "");
70112623Sphk
70212243Sphk/*
70312623Sphk * Default "handler" functions.
70412623Sphk */
70512623Sphk
70612623Sphk/*
70742095Sdfr * Handle an int, signed or unsigned.
70812243Sphk * Two cases:
70912243Sphk *     a variable:  point arg1 at it.
71012243Sphk *     a constant:  pass it in arg2.
71112243Sphk */
71212243Sphk
71311865Sphkint
71462573Sphksysctl_handle_int(SYSCTL_HANDLER_ARGS)
71511863Sphk{
71612243Sphk	int error = 0;
71711863Sphk
71812243Sphk	if (arg1)
71912243Sphk		error = SYSCTL_OUT(req, arg1, sizeof(int));
72020506Sbde	else
72112243Sphk		error = SYSCTL_OUT(req, &arg2, sizeof(int));
72211863Sphk
72312243Sphk	if (error || !req->newptr)
72412243Sphk		return (error);
72511863Sphk
72612243Sphk	if (!arg1)
72712243Sphk		error = EPERM;
72812243Sphk	else
72912243Sphk		error = SYSCTL_IN(req, arg1, sizeof(int));
73012243Sphk	return (error);
73111863Sphk}
73211863Sphk
73312243Sphk/*
73445140Sphk * Handle a long, signed or unsigned.  arg1 points to it.
73538517Sdfr */
73638517Sdfr
73738517Sdfrint
73862573Sphksysctl_handle_long(SYSCTL_HANDLER_ARGS)
73938517Sdfr{
74038517Sdfr	int error = 0;
74138517Sdfr
74245140Sphk	if (!arg1)
74345140Sphk		return (EINVAL);
74442095Sdfr	error = SYSCTL_OUT(req, arg1, sizeof(long));
74538517Sdfr
74638517Sdfr	if (error || !req->newptr)
74738517Sdfr		return (error);
74838517Sdfr
74945140Sphk	error = SYSCTL_IN(req, arg1, sizeof(long));
75038517Sdfr	return (error);
75138517Sdfr}
75238517Sdfr
75338517Sdfr/*
75412243Sphk * Handle our generic '\0' terminated 'C' string.
75512243Sphk * Two cases:
75612243Sphk * 	a variable string:  point arg1 at it, arg2 is max length.
75712243Sphk * 	a constant string:  point arg1 at it, arg2 is zero.
75812243Sphk */
75912243Sphk
76011865Sphkint
76162573Sphksysctl_handle_string(SYSCTL_HANDLER_ARGS)
76211863Sphk{
76312243Sphk	int error=0;
76411863Sphk
76512297Sphk	error = SYSCTL_OUT(req, arg1, strlen((char *)arg1)+1);
76611863Sphk
76745140Sphk	if (error || !req->newptr)
76812243Sphk		return (error);
76911863Sphk
77045140Sphk	if ((req->newlen - req->newidx) >= arg2) {
77145140Sphk		error = EINVAL;
77212243Sphk	} else {
77312243Sphk		arg2 = (req->newlen - req->newidx);
77412243Sphk		error = SYSCTL_IN(req, arg1, arg2);
77512243Sphk		((char *)arg1)[arg2] = '\0';
77611863Sphk	}
77712131Sphk
77812131Sphk	return (error);
77911863Sphk}
78011863Sphk
78112243Sphk/*
78212243Sphk * Handle any kind of opaque data.
78312243Sphk * arg1 points to it, arg2 is the size.
78412243Sphk */
78512243Sphk
78611865Sphkint
78762573Sphksysctl_handle_opaque(SYSCTL_HANDLER_ARGS)
78811863Sphk{
78912243Sphk	int error;
79012243Sphk
79112243Sphk	error = SYSCTL_OUT(req, arg1, arg2);
79212243Sphk
79312243Sphk	if (error || !req->newptr)
79412243Sphk		return (error);
79512243Sphk
79612243Sphk	error = SYSCTL_IN(req, arg1, arg2);
79712243Sphk
79812243Sphk	return (error);
79912243Sphk}
80012243Sphk
80112260Sphk/*
80212260Sphk * Transfer functions to/from kernel space.
80312260Sphk * XXX: rather untested at this point
80412260Sphk */
80512260Sphkstatic int
80638517Sdfrsysctl_old_kernel(struct sysctl_req *req, const void *p, size_t l)
80712243Sphk{
80838517Sdfr	size_t i = 0;
80912260Sphk
81012260Sphk	if (req->oldptr) {
81138517Sdfr		i = l;
81273971Stmm		if (req->oldlen <= req->oldidx)
81373971Stmm			i = 0;
81473971Stmm		else
81573971Stmm			if (i > req->oldlen - req->oldidx)
81673971Stmm				i = req->oldlen - req->oldidx;
81712260Sphk		if (i > 0)
81817971Sbde			bcopy(p, (char *)req->oldptr + req->oldidx, i);
81912243Sphk	}
82012260Sphk	req->oldidx += l;
82116282Snate	if (req->oldptr && i != l)
82211863Sphk		return (ENOMEM);
82312260Sphk	return (0);
82412243Sphk}
82512243Sphk
82612260Sphkstatic int
82738517Sdfrsysctl_new_kernel(struct sysctl_req *req, void *p, size_t l)
82812243Sphk{
82912260Sphk	if (!req->newptr)
83012260Sphk		return 0;
83112260Sphk	if (req->newlen - req->newidx < l)
83211863Sphk		return (EINVAL);
83317971Sbde	bcopy((char *)req->newptr + req->newidx, p, l);
83412243Sphk	req->newidx += l;
83512131Sphk	return (0);
83611863Sphk}
83711863Sphk
83816282Snateint
83983366Sjuliankernel_sysctl(struct thread *td, int *name, u_int namelen, void *old,
84076834Sjlemon    size_t *oldlenp, void *new, size_t newlen, size_t *retval)
84116282Snate{
84216282Snate	int error = 0;
84316282Snate	struct sysctl_req req;
84416282Snate
84516282Snate	bzero(&req, sizeof req);
84616282Snate
84783366Sjulian	req.p = td->td_proc;
84816282Snate
84916282Snate	if (oldlenp) {
85016282Snate		req.oldlen = *oldlenp;
85116282Snate	}
85216282Snate
85316282Snate	if (old) {
85416282Snate		req.oldptr= old;
85516282Snate	}
85616282Snate
85777646Sdd	if (new != NULL) {
85816282Snate		req.newlen = newlen;
85916282Snate		req.newptr = new;
86016282Snate	}
86116282Snate
86216282Snate	req.oldfunc = sysctl_old_kernel;
86316282Snate	req.newfunc = sysctl_new_kernel;
86416282Snate	req.lock = 1;
86516282Snate
86616282Snate	/* XXX this should probably be done in a general way */
86716282Snate	while (memlock.sl_lock) {
86816282Snate		memlock.sl_want = 1;
86916282Snate		(void) tsleep((caddr_t)&memlock, PRIBIO+1, "sysctl", 0);
87016282Snate		memlock.sl_locked++;
87116282Snate	}
87216282Snate	memlock.sl_lock = 1;
87316282Snate
87416282Snate	error = sysctl_root(0, name, namelen, &req);
87516282Snate
87616282Snate	if (req.lock == 2)
87757975Sphk		vsunlock(req.oldptr, req.oldlen);
87816282Snate
87916282Snate	memlock.sl_lock = 0;
88016282Snate
88116282Snate	if (memlock.sl_want) {
88216282Snate		memlock.sl_want = 0;
88316282Snate		wakeup((caddr_t)&memlock);
88416282Snate	}
88516282Snate
88616282Snate	if (error && error != ENOMEM)
88716282Snate		return (error);
88816282Snate
88916282Snate	if (retval) {
89016282Snate		if (req.oldptr && req.oldidx > req.oldlen)
89116282Snate			*retval = req.oldlen;
89216282Snate		else
89316282Snate			*retval = req.oldidx;
89416282Snate	}
89516282Snate	return (error);
89616282Snate}
89716282Snate
89876834Sjlemonint
89983366Sjuliankernel_sysctlbyname(struct thread *td, char *name, void *old, size_t *oldlenp,
90076834Sjlemon    void *new, size_t newlen, size_t *retval)
90176834Sjlemon{
90276834Sjlemon        int oid[CTL_MAXNAME];
90378620Smjacob        size_t oidlen, plen;
90478620Smjacob	int error;
90576834Sjlemon
90676834Sjlemon	oid[0] = 0;		/* sysctl internal magic */
90776834Sjlemon	oid[1] = 3;		/* name2oid */
90876834Sjlemon	oidlen = sizeof(oid);
90976834Sjlemon
91083366Sjulian	error = kernel_sysctl(td, oid, 2, oid, &oidlen,
91176834Sjlemon	    (void *)name, strlen(name), &plen);
91276834Sjlemon	if (error)
91376834Sjlemon		return (error);
91476834Sjlemon
91583366Sjulian	error = kernel_sysctl(td, oid, plen / sizeof(int), old, oldlenp,
91676834Sjlemon	    new, newlen, retval);
91776834Sjlemon	return (error);
91876834Sjlemon}
91976834Sjlemon
92012260Sphk/*
92112260Sphk * Transfer function to/from user space.
92212260Sphk */
92312260Sphkstatic int
92438517Sdfrsysctl_old_user(struct sysctl_req *req, const void *p, size_t l)
92512243Sphk{
92638517Sdfr	int error = 0;
92738517Sdfr	size_t i = 0;
92812243Sphk
92912429Sphk	if (req->lock == 1 && req->oldptr) {
93012429Sphk		vslock(req->oldptr, req->oldlen);
93112429Sphk		req->lock = 2;
93212429Sphk	}
93312260Sphk	if (req->oldptr) {
93438517Sdfr		i = l;
93573971Stmm		if (req->oldlen <= req->oldidx)
93673971Stmm			i = 0;
93773971Stmm		else
93873971Stmm			if (i > req->oldlen - req->oldidx)
93973971Stmm				i = req->oldlen - req->oldidx;
94012260Sphk		if (i > 0)
94117971Sbde			error = copyout(p, (char *)req->oldptr + req->oldidx,
94217971Sbde					i);
94312260Sphk	}
94412260Sphk	req->oldidx += l;
94512243Sphk	if (error)
94612243Sphk		return (error);
94712260Sphk	if (req->oldptr && i < l)
94812243Sphk		return (ENOMEM);
94912260Sphk	return (0);
95012243Sphk}
95112243Sphk
95212260Sphkstatic int
95338517Sdfrsysctl_new_user(struct sysctl_req *req, void *p, size_t l)
95412243Sphk{
95512285Sphk	int error;
95612260Sphk
95712260Sphk	if (!req->newptr)
95812260Sphk		return 0;
95912260Sphk	if (req->newlen - req->newidx < l)
96012243Sphk		return (EINVAL);
96117971Sbde	error = copyin((char *)req->newptr + req->newidx, p, l);
96212243Sphk	req->newidx += l;
96312243Sphk	return (error);
96412243Sphk}
96512243Sphk
9661541Srgrimesint
96753977Sgreensysctl_find_oid(int *name, u_int namelen, struct sysctl_oid **noid,
96853977Sgreen    int *nindx, struct sysctl_req *req)
96912131Sphk{
97044078Sdfr	struct sysctl_oid *oid;
97153977Sgreen	int indx;
97212131Sphk
97353977Sgreen	oid = SLIST_FIRST(&sysctl__children);
97412131Sphk	indx = 0;
97544078Sdfr	while (oid && indx < CTL_MAXNAME) {
97644078Sdfr		if (oid->oid_number == name[indx]) {
97712131Sphk			indx++;
97844078Sdfr			if (oid->oid_kind & CTLFLAG_NOLOCK)
97912429Sphk				req->lock = 0;
98044078Sdfr			if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
98153977Sgreen				if (oid->oid_handler != NULL ||
98253977Sgreen				    indx == namelen) {
98353977Sgreen					*noid = oid;
98453977Sgreen					if (nindx != NULL)
98553977Sgreen						*nindx = indx;
98653977Sgreen					return (0);
98753977Sgreen				}
98853977Sgreen				oid = SLIST_FIRST(
98953977Sgreen				    (struct sysctl_oid_list *)oid->oid_arg1);
99053977Sgreen			} else if (indx == namelen) {
99153977Sgreen				*noid = oid;
99253977Sgreen				if (nindx != NULL)
99353977Sgreen					*nindx = indx;
99453977Sgreen				return (0);
99512131Sphk			} else {
99653977Sgreen				return (ENOTDIR);
99712131Sphk			}
99812131Sphk		} else {
99944078Sdfr			oid = SLIST_NEXT(oid, oid_link);
100012131Sphk		}
100112131Sphk	}
100253977Sgreen	return (ENOENT);
100353977Sgreen}
100453977Sgreen
100553977Sgreen/*
100653977Sgreen * Traverse our tree, and find the right node, execute whatever it points
100753977Sgreen * to, and return the resulting error code.
100853977Sgreen */
100953977Sgreen
101053977Sgreenint
101162573Sphksysctl_root(SYSCTL_HANDLER_ARGS)
101253977Sgreen{
101353977Sgreen	struct sysctl_oid *oid;
101453977Sgreen	int error, indx;
101553977Sgreen
101653977Sgreen	error = sysctl_find_oid(arg1, arg2, &oid, &indx, req);
101753977Sgreen	if (error)
101853977Sgreen		return (error);
101953977Sgreen
102053977Sgreen	if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
102153977Sgreen		/*
102253977Sgreen		 * You can't call a sysctl when it's a node, but has
102353977Sgreen		 * no handler.  Inform the user that it's a node.
102453977Sgreen		 * The indx may or may not be the same as namelen.
102553977Sgreen		 */
102653977Sgreen		if (oid->oid_handler == NULL)
102753977Sgreen			return (EISDIR);
102853977Sgreen	}
102953977Sgreen
103083968Srwatson	/* Is this sysctl writable? */
103183968Srwatson	if (req->newptr && !(oid->oid_kind & CTLFLAG_WR))
103212131Sphk		return (EPERM);
103312131Sphk
103483968Srwatson	/* Is this sysctl sensitive to securelevels? */
103583968Srwatson	if (req->newptr && (oid->oid_kind & CTLFLAG_SECURE)) {
103683968Srwatson		if (req->p == NULL) {
103783968Srwatson			error = securelevel_gt(NULL, 0);	/* XXX */
103883968Srwatson			if (error)
103983968Srwatson				return (error);
104083968Srwatson		} else {
104183968Srwatson			error = securelevel_gt(req->p->p_ucred, 0);
104283968Srwatson			if (error)
104383968Srwatson				return (error);
104483968Srwatson		}
104583968Srwatson	}
104612910Sphk
104783968Srwatson	/* Is this sysctl writable by only privileged users? */
104883968Srwatson	if (req->newptr && !(oid->oid_kind & CTLFLAG_ANYBODY)) {
104983968Srwatson		if (req->p != NULL) {
105083968Srwatson			int flags;
105183968Srwatson
105283968Srwatson			if (oid->oid_kind & CTLFLAG_PRISON)
105383968Srwatson				flags = PRISON_ROOT;
105483968Srwatson			else
105583968Srwatson				flags = 0;
105683968Srwatson			error = suser_xxx(NULL, req->p, flags);
105783968Srwatson			if (error)
105883968Srwatson				return (error);
105983968Srwatson		}
106083968Srwatson	}
106183968Srwatson
106244078Sdfr	if (!oid->oid_handler)
106312131Sphk		return EINVAL;
106412131Sphk
106553977Sgreen	if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE)
106653977Sgreen		error = oid->oid_handler(oid, (int *)arg1 + indx, arg2 - indx,
106753977Sgreen		    req);
106853977Sgreen	else
106953977Sgreen		error = oid->oid_handler(oid, oid->oid_arg1, oid->oid_arg2,
107053977Sgreen		    req);
107153977Sgreen	return (error);
107212131Sphk}
107312131Sphk
107412221Sbde#ifndef _SYS_SYSPROTO_H_
107512171Sphkstruct sysctl_args {
107612171Sphk	int	*name;
107712171Sphk	u_int	namelen;
107812171Sphk	void	*old;
107912171Sphk	size_t	*oldlenp;
108012171Sphk	void	*new;
108112171Sphk	size_t	newlen;
108212171Sphk};
108312221Sbde#endif
108412171Sphk
108582746Sdillon/*
108682746Sdillon * MPSAFE
108782746Sdillon */
108812131Sphkint
108983366Sjulian__sysctl(struct thread *td, struct sysctl_args *uap)
10901541Srgrimes{
109182746Sdillon	int error, name[CTL_MAXNAME];
109238517Sdfr	size_t j;
10931541Srgrimes
10941541Srgrimes	if (uap->namelen > CTL_MAXNAME || uap->namelen < 2)
10951541Srgrimes		return (EINVAL);
109611863Sphk
10973308Sphk 	error = copyin(uap->name, &name, uap->namelen * sizeof(int));
10983308Sphk 	if (error)
10991541Srgrimes		return (error);
11001541Srgrimes
110182746Sdillon	mtx_lock(&Giant);
110282746Sdillon
110383366Sjulian	error = userland_sysctl(td, name, uap->namelen,
110412171Sphk		uap->old, uap->oldlenp, 0,
110512260Sphk		uap->new, uap->newlen, &j);
110612260Sphk	if (error && error != ENOMEM)
110782746Sdillon		goto done2;
110812260Sphk	if (uap->oldlenp) {
110982746Sdillon		int i = copyout(&j, uap->oldlenp, sizeof(j));
111012260Sphk		if (i)
111182746Sdillon			error = i;
111212260Sphk	}
111382746Sdillondone2:
111482746Sdillon	mtx_unlock(&Giant);
111512260Sphk	return (error);
111612171Sphk}
111712171Sphk
111812171Sphk/*
111912171Sphk * This is used from various compatibility syscalls too.  That's why name
112012171Sphk * must be in kernel space.
112112171Sphk */
112212171Sphkint
112383366Sjulianuserland_sysctl(struct thread *td, int *name, u_int namelen, void *old,
112480338Sroam    size_t *oldlenp, int inkernel, void *new, size_t newlen, size_t *retval)
112512171Sphk{
112612429Sphk	int error = 0;
112716159Sphk	struct sysctl_req req, req2;
112812171Sphk
112912243Sphk	bzero(&req, sizeof req);
113012243Sphk
113183366Sjulian	req.p = td->td_proc;
113212285Sphk
113312171Sphk	if (oldlenp) {
113412171Sphk		if (inkernel) {
113512243Sphk			req.oldlen = *oldlenp;
113612171Sphk		} else {
113712260Sphk			error = copyin(oldlenp, &req.oldlen, sizeof(*oldlenp));
113812171Sphk			if (error)
113912171Sphk				return (error);
114012171Sphk		}
114112171Sphk	}
114212171Sphk
114312243Sphk	if (old) {
114452644Sphk		if (!useracc(old, req.oldlen, VM_PROT_WRITE))
114512243Sphk			return (EFAULT);
114612243Sphk		req.oldptr= old;
114712243Sphk	}
114812131Sphk
114977646Sdd	if (new != NULL) {
115052644Sphk		if (!useracc(new, req.newlen, VM_PROT_READ))
115112243Sphk			return (EFAULT);
115212243Sphk		req.newlen = newlen;
115312243Sphk		req.newptr = new;
115411863Sphk	}
115512131Sphk
115612243Sphk	req.oldfunc = sysctl_old_user;
115712243Sphk	req.newfunc = sysctl_new_user;
115812429Sphk	req.lock = 1;
115911863Sphk
116012429Sphk	/* XXX this should probably be done in a general way */
116112429Sphk	while (memlock.sl_lock) {
116212429Sphk		memlock.sl_want = 1;
116312429Sphk		(void) tsleep((caddr_t)&memlock, PRIBIO+1, "sysctl", 0);
116412429Sphk		memlock.sl_locked++;
116512429Sphk	}
116612429Sphk	memlock.sl_lock = 1;
116712429Sphk
116816159Sphk	do {
116916159Sphk	    req2 = req;
117016159Sphk	    error = sysctl_root(0, name, namelen, &req2);
117116159Sphk	} while (error == EAGAIN);
117212243Sphk
117316159Sphk	req = req2;
117412429Sphk	if (req.lock == 2)
117557975Sphk		vsunlock(req.oldptr, req.oldlen);
117612429Sphk
117712429Sphk	memlock.sl_lock = 0;
117812429Sphk
117912429Sphk	if (memlock.sl_want) {
118012429Sphk		memlock.sl_want = 0;
118112429Sphk		wakeup((caddr_t)&memlock);
118212429Sphk	}
118312429Sphk
118412260Sphk	if (error && error != ENOMEM)
118512260Sphk		return (error);
118612260Sphk
118712260Sphk	if (retval) {
118812260Sphk		if (req.oldptr && req.oldidx > req.oldlen)
118912243Sphk			*retval = req.oldlen;
119012260Sphk		else
119112260Sphk			*retval = req.oldidx;
119211863Sphk	}
119312260Sphk	return (error);
11941541Srgrimes}
11951541Srgrimes
11961541Srgrimes#ifdef COMPAT_43
11971541Srgrimes#include <sys/socket.h>
119815103Sphk#include <vm/vm_param.h>
119915103Sphk
12001541Srgrimes#define	KINFO_PROC		(0<<8)
12011541Srgrimes#define	KINFO_RT		(1<<8)
12021541Srgrimes#define	KINFO_VNODE		(2<<8)
12031541Srgrimes#define	KINFO_FILE		(3<<8)
12041541Srgrimes#define	KINFO_METER		(4<<8)
12051541Srgrimes#define	KINFO_LOADAVG		(5<<8)
12061541Srgrimes#define	KINFO_CLOCKRATE		(6<<8)
12071541Srgrimes
12089455Speter/* Non-standard BSDI extension - only present on their 4.3 net-2 releases */
12099455Speter#define	KINFO_BSDI_SYSINFO	(101<<8)
12109455Speter
12119455Speter/*
12129455Speter * XXX this is bloat, but I hope it's better here than on the potentially
12139455Speter * limited kernel stack...  -Peter
12149455Speter */
12159455Speter
121612819Sphkstatic struct {
12179455Speter	int	bsdi_machine;		/* "i386" on BSD/386 */
12189455Speter/*      ^^^ this is an offset to the string, relative to the struct start */
12199455Speter	char	*pad0;
12209455Speter	long	pad1;
12219455Speter	long	pad2;
12229455Speter	long	pad3;
12239455Speter	u_long	pad4;
12249455Speter	u_long	pad5;
12259455Speter	u_long	pad6;
12269455Speter
12279455Speter	int	bsdi_ostype;		/* "BSD/386" on BSD/386 */
12289455Speter	int	bsdi_osrelease;		/* "1.1" on BSD/386 */
12299455Speter	long	pad7;
12309455Speter	long	pad8;
12319455Speter	char	*pad9;
12329455Speter
12339455Speter	long	pad10;
12349455Speter	long	pad11;
12359455Speter	int	pad12;
12369455Speter	long	pad13;
12379455Speter	quad_t	pad14;
12389455Speter	long	pad15;
12399455Speter
12409455Speter	struct	timeval pad16;
12419455Speter	/* we dont set this, because BSDI's uname used gethostname() instead */
12429455Speter	int	bsdi_hostname;		/* hostname on BSD/386 */
12439455Speter
12449455Speter	/* the actual string data is appended here */
12459455Speter
12469455Speter} bsdi_si;
12479455Speter/*
12489455Speter * this data is appended to the end of the bsdi_si structure during copyout.
12499455Speter * The "char *" offsets are relative to the base of the bsdi_si struct.
12509455Speter * This contains "FreeBSD\02.0-BUILT-nnnnnn\0i386\0", and these strings
12519455Speter * should not exceed the length of the buffer here... (or else!! :-)
12529455Speter */
125312819Sphkstatic char bsdi_strings[80];	/* It had better be less than this! */
12549455Speter
125512221Sbde#ifndef _SYS_SYSPROTO_H_
12561541Srgrimesstruct getkerninfo_args {
12571541Srgrimes	int	op;
12581541Srgrimes	char	*where;
125938864Sbde	size_t	*size;
12601541Srgrimes	int	arg;
12611541Srgrimes};
126212221Sbde#endif
12631541Srgrimes
126482746Sdillon/*
126582746Sdillon * MPSAFE
126682746Sdillon */
12671549Srgrimesint
126883366Sjulianogetkerninfo(struct thread *td, struct getkerninfo_args *uap)
12691541Srgrimes{
127012171Sphk	int error, name[6];
127138517Sdfr	size_t size;
127282494Speter	u_int needed = 0;
12731541Srgrimes
127482746Sdillon	mtx_lock(&Giant);
127582746Sdillon
12761541Srgrimes	switch (uap->op & 0xff00) {
12771541Srgrimes
12781541Srgrimes	case KINFO_RT:
127912171Sphk		name[0] = CTL_NET;
128012171Sphk		name[1] = PF_ROUTE;
128112171Sphk		name[2] = 0;
128212171Sphk		name[3] = (uap->op & 0xff0000) >> 16;
128312171Sphk		name[4] = uap->op & 0xff;
128412171Sphk		name[5] = uap->arg;
128583366Sjulian		error = userland_sysctl(td, name, 6, uap->where, uap->size,
128612429Sphk			0, 0, 0, &size);
12871541Srgrimes		break;
12881541Srgrimes
12891541Srgrimes	case KINFO_VNODE:
129012171Sphk		name[0] = CTL_KERN;
129112171Sphk		name[1] = KERN_VNODE;
129283366Sjulian		error = userland_sysctl(td, name, 2, uap->where, uap->size,
129312429Sphk			0, 0, 0, &size);
12941541Srgrimes		break;
12951541Srgrimes
12961541Srgrimes	case KINFO_PROC:
129712171Sphk		name[0] = CTL_KERN;
129812171Sphk		name[1] = KERN_PROC;
129912171Sphk		name[2] = uap->op & 0xff;
130012171Sphk		name[3] = uap->arg;
130183366Sjulian		error = userland_sysctl(td, name, 4, uap->where, uap->size,
130212429Sphk			0, 0, 0, &size);
13031541Srgrimes		break;
13041541Srgrimes
13051541Srgrimes	case KINFO_FILE:
130612171Sphk		name[0] = CTL_KERN;
130712171Sphk		name[1] = KERN_FILE;
130883366Sjulian		error = userland_sysctl(td, name, 2, uap->where, uap->size,
130912429Sphk			0, 0, 0, &size);
13101541Srgrimes		break;
13111541Srgrimes
13121541Srgrimes	case KINFO_METER:
131312171Sphk		name[0] = CTL_VM;
131412171Sphk		name[1] = VM_METER;
131583366Sjulian		error = userland_sysctl(td, name, 2, uap->where, uap->size,
131612429Sphk			0, 0, 0, &size);
13171541Srgrimes		break;
13181541Srgrimes
13191541Srgrimes	case KINFO_LOADAVG:
132012171Sphk		name[0] = CTL_VM;
132112171Sphk		name[1] = VM_LOADAVG;
132283366Sjulian		error = userland_sysctl(td, name, 2, uap->where, uap->size,
132312429Sphk			0, 0, 0, &size);
13241541Srgrimes		break;
13251541Srgrimes
13261541Srgrimes	case KINFO_CLOCKRATE:
132712171Sphk		name[0] = CTL_KERN;
132812171Sphk		name[1] = KERN_CLOCKRATE;
132983366Sjulian		error = userland_sysctl(td, name, 2, uap->where, uap->size,
133012429Sphk			0, 0, 0, &size);
13311541Srgrimes		break;
13321541Srgrimes
13339455Speter	case KINFO_BSDI_SYSINFO: {
13349455Speter		/*
13359455Speter		 * this is pretty crude, but it's just enough for uname()
13369455Speter		 * from BSDI's 1.x libc to work.
13379455Speter		 *
133882494Speter		 * *size gives the size of the buffer before the call, and
133982494Speter		 * the amount of data copied after a successful call.
134082494Speter		 * If successful, the return value is the amount of data
134182494Speter		 * available, which can be larger than *size.
134282494Speter		 *
134382494Speter		 * BSDI's 2.x product apparently fails with ENOMEM if *size
134482494Speter		 * is too small.
13459455Speter		 */
13469455Speter
13479455Speter		u_int left;
13489455Speter		char *s;
13499455Speter
13509455Speter		bzero((char *)&bsdi_si, sizeof(bsdi_si));
13519455Speter		bzero(bsdi_strings, sizeof(bsdi_strings));
13529455Speter
13539455Speter		s = bsdi_strings;
13549455Speter
13559455Speter		bsdi_si.bsdi_ostype = (s - bsdi_strings) + sizeof(bsdi_si);
13569455Speter		strcpy(s, ostype);
13579455Speter		s += strlen(s) + 1;
13589455Speter
13599455Speter		bsdi_si.bsdi_osrelease = (s - bsdi_strings) + sizeof(bsdi_si);
13609455Speter		strcpy(s, osrelease);
13619455Speter		s += strlen(s) + 1;
13629455Speter
13639455Speter		bsdi_si.bsdi_machine = (s - bsdi_strings) + sizeof(bsdi_si);
13649455Speter		strcpy(s, machine);
13659455Speter		s += strlen(s) + 1;
13669455Speter
13679455Speter		needed = sizeof(bsdi_si) + (s - bsdi_strings);
13689455Speter
136982494Speter		if ((uap->where == NULL) || (uap->size == NULL)) {
13709455Speter			/* process is asking how much buffer to supply.. */
13719455Speter			size = needed;
13729455Speter			error = 0;
13739455Speter			break;
13749455Speter		}
13759455Speter
137682494Speter		if ((error = copyin(uap->size, &size, sizeof(size))) != 0)
137782494Speter			break;
13789455Speter
13799455Speter		/* if too much buffer supplied, trim it down */
13809455Speter		if (size > needed)
13819455Speter			size = needed;
13829455Speter
13839455Speter		/* how much of the buffer is remaining */
13849455Speter		left = size;
13859455Speter
13869455Speter		if ((error = copyout((char *)&bsdi_si, uap->where, left)) != 0)
13879455Speter			break;
13889455Speter
13899455Speter		/* is there any point in continuing? */
13909455Speter		if (left > sizeof(bsdi_si)) {
13919455Speter			left -= sizeof(bsdi_si);
13929455Speter			error = copyout(&bsdi_strings,
13939455Speter					uap->where + sizeof(bsdi_si), left);
13949455Speter		}
13959455Speter		break;
13969455Speter	}
13979455Speter
13981541Srgrimes	default:
139982746Sdillon		error = EOPNOTSUPP;
140082746Sdillon		break;
14011541Srgrimes	}
140282746Sdillon	if (error == 0) {
140383366Sjulian		td->td_retval[0] = needed ? needed : size;
140482746Sdillon		if (uap->size) {
140582746Sdillon			error = copyout((caddr_t)&size, (caddr_t)uap->size,
140682746Sdillon				    sizeof(size));
140782746Sdillon		}
140882746Sdillon	}
140982746Sdillon	mtx_unlock(&Giant);
14101541Srgrimes	return (error);
14111541Srgrimes}
14121541Srgrimes#endif /* COMPAT_43 */
1413