kern_sysctl.c revision 104094
1292920Sdim/*-
2292920Sdim * Copyright (c) 1982, 1986, 1989, 1993
3353358Sdim *	The Regents of the University of California.  All rights reserved.
4353358Sdim *
5353358Sdim * This code is derived from software contributed to Berkeley by
6292920Sdim * Mike Karels at Berkeley Software Design, Inc.
7292920Sdim *
8292920Sdim * Quite extensively rewritten by Poul-Henning Kamp of the FreeBSD
9292920Sdim * project, to make these variables more userfriendly.
10292920Sdim *
11292920Sdim * Redistribution and use in source and binary forms, with or without
12292920Sdim * modification, are permitted provided that the following conditions
13292920Sdim * are met:
14292920Sdim * 1. Redistributions of source code must retain the above copyright
15292920Sdim *    notice, this list of conditions and the following disclaimer.
16292920Sdim * 2. Redistributions in binary form must reproduce the above copyright
17292920Sdim *    notice, this list of conditions and the following disclaimer in the
18360784Sdim *    documentation and/or other materials provided with the distribution.
19292920Sdim * 3. All advertising materials mentioning features or use of this software
20292920Sdim *    must display the following acknowledgement:
21292920Sdim *	This product includes software developed by the University of
22292920Sdim *	California, Berkeley and its contributors.
23292920Sdim * 4. Neither the name of the University nor the names of its contributors
24292920Sdim *    may be used to endorse or promote products derived from this software
25292920Sdim *    without specific prior written permission.
26353358Sdim *
27353358Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28353358Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29353358Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30353358Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31353358Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32353358Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33353358Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34353358Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35353358Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36353358Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37353358Sdim * SUCH DAMAGE.
38353358Sdim *
39353358Sdim *	@(#)kern_sysctl.c	8.4 (Berkeley) 4/14/94
40353358Sdim * $FreeBSD: head/sys/kern/kern_sysctl.c 104094 2002-09-28 17:15:38Z phk $
41353358Sdim */
42353358Sdim
43353358Sdim#include "opt_compat.h"
44353358Sdim
45360784Sdim#include <sys/param.h>
46360784Sdim#include <sys/systm.h>
47360784Sdim#include <sys/kernel.h>
48360784Sdim#include <sys/sysctl.h>
49360784Sdim#include <sys/malloc.h>
50360784Sdim#include <sys/proc.h>
51360784Sdim#include <sys/lock.h>
52360784Sdim#include <sys/mutex.h>
53360784Sdim#include <sys/sx.h>
54360784Sdim#include <sys/sysproto.h>
55360784Sdim#include <vm/vm.h>
56360784Sdim#include <vm/vm_extern.h>
57360784Sdim
58360784Sdimstatic MALLOC_DEFINE(M_SYSCTL, "sysctl", "sysctl internal magic");
59360784Sdimstatic MALLOC_DEFINE(M_SYSCTLOID, "sysctloid", "sysctl dynamic oids");
60360784Sdimstatic MALLOC_DEFINE(M_SYSCTLTMP, "sysctltmp", "sysctl temp output buffer");
61360784Sdim
62360784Sdim/*
63360784Sdim * Locking - this locks the sysctl tree in memory.
64360784Sdim */
65360784Sdimstatic struct sx sysctllock;
66360784Sdim
67360784Sdim#define	SYSCTL_LOCK()		sx_xlock(&sysctllock)
68360784Sdim#define	SYSCTL_UNLOCK()	sx_xunlock(&sysctllock)
69360784Sdim#define	SYSCTL_INIT()		sx_init(&sysctllock, "sysctl sysctllock")
70360784Sdim
71360784Sdimstatic int sysctl_root(SYSCTL_HANDLER_ARGS);
72360784Sdim
73360784Sdimstruct sysctl_oid_list sysctl__children; /* root list */
74360784Sdim
75360784Sdimstatic struct sysctl_oid *
76360784Sdimsysctl_find_oidname(const char *name, struct sysctl_oid_list *list)
77360784Sdim{
78360784Sdim	struct sysctl_oid *oidp;
79360784Sdim
80360784Sdim	SLIST_FOREACH(oidp, list, oid_link) {
81360784Sdim		if (strcmp(oidp->oid_name, name) == 0) {
82360784Sdim			return (oidp);
83360784Sdim		}
84360784Sdim	}
85360784Sdim	return (NULL);
86360784Sdim}
87360784Sdim
88360784Sdim/*
89360784Sdim * Initialization of the MIB tree.
90360784Sdim *
91360784Sdim * Order by number in each list.
92360784Sdim */
93360784Sdim
94360784Sdimvoid
95360784Sdimsysctl_register_oid(struct sysctl_oid *oidp)
96360784Sdim{
97360784Sdim	struct sysctl_oid_list *parent = oidp->oid_parent;
98360784Sdim	struct sysctl_oid *p;
99360784Sdim	struct sysctl_oid *q;
100360784Sdim
101360784Sdim	/*
102360784Sdim	 * First check if another oid with the same name already
103360784Sdim	 * exists in the parent's list.
104360784Sdim	 */
105360784Sdim	p = sysctl_find_oidname(oidp->oid_name, parent);
106360784Sdim	if (p != NULL) {
107360784Sdim		if ((p->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
108360784Sdim			p->oid_refcnt++;
109360784Sdim			return;
110360784Sdim		} else {
111360784Sdim			printf("can't re-use a leaf (%s)!\n", p->oid_name);
112360784Sdim			return;
113292920Sdim		}
114292920Sdim	}
115292920Sdim	/*
116292920Sdim	 * If this oid has a number OID_AUTO, give it a number which
117292920Sdim	 * is greater than any current oid.
118292920Sdim	 * NOTE: DO NOT change the starting value here, change it in
119292920Sdim	 * <sys/sysctl.h>, and make sure it is at least 256 to
120292920Sdim	 * accomodate e.g. net.inet.raw as a static sysctl node.
121292920Sdim	 */
122292920Sdim	if (oidp->oid_number == OID_AUTO) {
123292920Sdim		static int newoid = CTL_AUTO_START;
124292920Sdim
125292920Sdim		oidp->oid_number = newoid++;
126292920Sdim		if (newoid == 0x7fffffff)
127292920Sdim			panic("out of oids");
128292920Sdim	}
129292920Sdim#if 0
130292920Sdim	else if (oidp->oid_number >= CTL_AUTO_START) {
131292920Sdim		/* do not panic; this happens when unregistering sysctl sets */
132292920Sdim		printf("static sysctl oid too high: %d", oidp->oid_number);
133292920Sdim	}
134292920Sdim#endif
135292920Sdim
136292920Sdim	/*
137292920Sdim	 * Insert the oid into the parent's list in order.
138292920Sdim	 */
139292920Sdim	q = NULL;
140292920Sdim	SLIST_FOREACH(p, parent, oid_link) {
141292920Sdim		if (oidp->oid_number < p->oid_number)
142292920Sdim			break;
143292920Sdim		q = p;
144360784Sdim	}
145360784Sdim	if (q)
146360784Sdim		SLIST_INSERT_AFTER(q, oidp, oid_link);
147360784Sdim	else
148360784Sdim		SLIST_INSERT_HEAD(parent, oidp, oid_link);
149360784Sdim}
150360784Sdim
151360784Sdimvoid
152360784Sdimsysctl_unregister_oid(struct sysctl_oid *oidp)
153360784Sdim{
154360784Sdim	SLIST_REMOVE(oidp->oid_parent, oidp, sysctl_oid, oid_link);
155360784Sdim}
156360784Sdim
157360784Sdim/* Initialize a new context to keep track of dynamically added sysctls. */
158360784Sdimint
159360784Sdimsysctl_ctx_init(struct sysctl_ctx_list *c)
160360784Sdim{
161360784Sdim
162360784Sdim	if (c == NULL) {
163292920Sdim		return (EINVAL);
164292920Sdim	}
165292920Sdim	TAILQ_INIT(c);
166309124Sdim	return (0);
167314564Sdim}
168292920Sdim
169292920Sdim/* Free the context, and destroy all dynamic oids registered in this context */
170292920Sdimint
171292920Sdimsysctl_ctx_free(struct sysctl_ctx_list *clist)
172292920Sdim{
173292920Sdim	struct sysctl_ctx_entry *e, *e1;
174292920Sdim	int error;
175292920Sdim
176292920Sdim	error = 0;
177292920Sdim	/*
178292920Sdim	 * First perform a "dry run" to check if it's ok to remove oids.
179292920Sdim	 * XXX FIXME
180292920Sdim	 * XXX This algorithm is a hack. But I don't know any
181309124Sdim	 * XXX better solution for now...
182314564Sdim	 */
183292920Sdim	TAILQ_FOREACH(e, clist, link) {
184292920Sdim		error = sysctl_remove_oid(e->entry, 0, 0);
185292920Sdim		if (error)
186292920Sdim			break;
187292920Sdim	}
188292920Sdim	/*
189292920Sdim	 * Restore deregistered entries, either from the end,
190292920Sdim	 * or from the place where error occured.
191292920Sdim	 * e contains the entry that was not unregistered
192292920Sdim	 */
193314564Sdim	if (error)
194292920Sdim		e1 = TAILQ_PREV(e, sysctl_ctx_list, link);
195292920Sdim	else
196292920Sdim		e1 = TAILQ_LAST(clist, sysctl_ctx_list);
197292920Sdim	while (e1 != NULL) {
198292920Sdim		sysctl_register_oid(e1->entry);
199292920Sdim		e1 = TAILQ_PREV(e1, sysctl_ctx_list, link);
200292920Sdim	}
201292920Sdim	if (error)
202292920Sdim		return(EBUSY);
203292920Sdim	/* Now really delete the entries */
204292920Sdim	e = TAILQ_FIRST(clist);
205292920Sdim	while (e != NULL) {
206292920Sdim		e1 = TAILQ_NEXT(e, link);
207292920Sdim		error = sysctl_remove_oid(e->entry, 1, 0);
208292920Sdim		if (error)
209292920Sdim			panic("sysctl_remove_oid: corrupt tree, entry: %s",
210292920Sdim			    e->entry->oid_name);
211292920Sdim		free(e, M_SYSCTLOID);
212292920Sdim		e = e1;
213360784Sdim	}
214360784Sdim	return (error);
215360784Sdim}
216309124Sdim
217292920Sdim/* Add an entry to the context */
218292920Sdimstruct sysctl_ctx_entry *
219292920Sdimsysctl_ctx_entry_add(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp)
220292920Sdim{
221292920Sdim	struct sysctl_ctx_entry *e;
222292920Sdim
223292920Sdim	if (clist == NULL || oidp == NULL)
224314564Sdim		return(NULL);
225292920Sdim	e = malloc(sizeof(struct sysctl_ctx_entry), M_SYSCTLOID, M_WAITOK);
226292920Sdim	e->entry = oidp;
227292920Sdim	TAILQ_INSERT_HEAD(clist, e, link);
228292920Sdim	return (e);
229292920Sdim}
230292920Sdim
231292920Sdim/* Find an entry in the context */
232292920Sdimstruct sysctl_ctx_entry *
233292920Sdimsysctl_ctx_entry_find(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp)
234292920Sdim{
235292920Sdim	struct sysctl_ctx_entry *e;
236314564Sdim
237292920Sdim	if (clist == NULL || oidp == NULL)
238292920Sdim		return(NULL);
239292920Sdim	TAILQ_FOREACH(e, clist, link) {
240292920Sdim		if(e->entry == oidp)
241292920Sdim			return(e);
242292920Sdim	}
243292920Sdim	return (e);
244292920Sdim}
245292920Sdim
246292920Sdim/*
247292920Sdim * Delete an entry from the context.
248292920Sdim * NOTE: this function doesn't free oidp! You have to remove it
249292920Sdim * with sysctl_remove_oid().
250292920Sdim */
251292920Sdimint
252292920Sdimsysctl_ctx_entry_del(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp)
253292920Sdim{
254292920Sdim	struct sysctl_ctx_entry *e;
255292920Sdim
256292920Sdim	if (clist == NULL || oidp == NULL)
257292920Sdim		return (EINVAL);
258309124Sdim	e = sysctl_ctx_entry_find(clist, oidp);
259292920Sdim	if (e != NULL) {
260292920Sdim		TAILQ_REMOVE(clist, e, link);
261292920Sdim		free(e, M_SYSCTLOID);
262292920Sdim		return (0);
263292920Sdim	} else
264360784Sdim		return (ENOENT);
265360784Sdim}
266360784Sdim
267309124Sdim/*
268292920Sdim * Remove dynamically created sysctl trees.
269292920Sdim * oidp - top of the tree to be removed
270292920Sdim * del - if 0 - just deregister, otherwise free up entries as well
271292920Sdim * recurse - if != 0 traverse the subtree to be deleted
272292920Sdim */
273292920Sdimint
274292920Sdimsysctl_remove_oid(struct sysctl_oid *oidp, int del, int recurse)
275292920Sdim{
276314564Sdim	struct sysctl_oid *p;
277292920Sdim	int error;
278292920Sdim
279292920Sdim	if (oidp == NULL)
280292920Sdim		return(EINVAL);
281292920Sdim	if ((oidp->oid_kind & CTLFLAG_DYN) == 0) {
282292920Sdim		printf("can't remove non-dynamic nodes!\n");
283292920Sdim		return (EINVAL);
284292920Sdim	}
285292920Sdim	/*
286292920Sdim	 * WARNING: normal method to do this should be through
287292920Sdim	 * sysctl_ctx_free(). Use recursing as the last resort
288309124Sdim	 * method to purge your sysctl tree of leftovers...
289314564Sdim	 * However, if some other code still references these nodes,
290292920Sdim	 * it will panic.
291292920Sdim	 */
292292920Sdim	if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
293292920Sdim		if (oidp->oid_refcnt == 1) {
294292920Sdim			SLIST_FOREACH(p, SYSCTL_CHILDREN(oidp), oid_link) {
295292920Sdim				if (!recurse)
296292920Sdim					return (ENOTEMPTY);
297292920Sdim				error = sysctl_remove_oid(p, del, recurse);
298292920Sdim				if (error)
299292920Sdim					return (error);
300292920Sdim			}
301292920Sdim			if (del)
302292920Sdim				free(SYSCTL_CHILDREN(oidp), M_SYSCTLOID);
303292920Sdim		}
304292920Sdim	}
305292920Sdim	if (oidp->oid_refcnt > 1 ) {
306292920Sdim		oidp->oid_refcnt--;
307292920Sdim	} else {
308292920Sdim		if (oidp->oid_refcnt == 0) {
309292920Sdim			printf("Warning: bad oid_refcnt=%u (%s)!\n",
310292920Sdim				oidp->oid_refcnt, oidp->oid_name);
311309124Sdim			return (EINVAL);
312292920Sdim		}
313292920Sdim		sysctl_unregister_oid(oidp);
314292920Sdim		if (del) {
315292920Sdim			if (oidp->descr)
316292920Sdim				free((void *)(uintptr_t)(const void *)oidp->descr, M_SYSCTLOID);
317360784Sdim			free((void *)(uintptr_t)(const void *)oidp->oid_name,
318360784Sdim			     M_SYSCTLOID);
319360784Sdim			free(oidp, M_SYSCTLOID);
320309124Sdim		}
321292920Sdim	}
322292920Sdim	return (0);
323292920Sdim}
324292920Sdim
325292920Sdim/*
326292920Sdim * Create new sysctls at run time.
327292920Sdim * clist may point to a valid context initialized with sysctl_ctx_init().
328309124Sdim */
329314564Sdimstruct sysctl_oid *
330292920Sdimsysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent,
331292920Sdim	int number, const char *name, int kind, void *arg1, int arg2,
332292920Sdim	int (*handler)(SYSCTL_HANDLER_ARGS), const char *fmt, const char *descr)
333292920Sdim{
334292920Sdim	struct sysctl_oid *oidp;
335292920Sdim	ssize_t len;
336292920Sdim	char *newname;
337292920Sdim
338292920Sdim	/* You have to hook up somewhere.. */
339309124Sdim	if (parent == NULL)
340314564Sdim		return(NULL);
341292920Sdim	/* Check if the node already exists, otherwise create it */
342292920Sdim	oidp = sysctl_find_oidname(name, parent);
343292920Sdim	if (oidp != NULL) {
344292920Sdim		if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
345292920Sdim			oidp->oid_refcnt++;
346292920Sdim			/* Update the context */
347292920Sdim			if (clist != NULL)
348292920Sdim				sysctl_ctx_entry_add(clist, oidp);
349292920Sdim			return (oidp);
350292920Sdim		} else {
351292920Sdim			printf("can't re-use a leaf (%s)!\n", name);
352292920Sdim			return (NULL);
353292920Sdim		}
354309124Sdim	}
355314564Sdim	oidp = malloc(sizeof(struct sysctl_oid), M_SYSCTLOID, M_WAITOK|M_ZERO);
356292920Sdim	oidp->oid_parent = parent;
357292920Sdim	SLIST_NEXT(oidp, oid_link) = NULL;
358292920Sdim	oidp->oid_number = number;
359292920Sdim	oidp->oid_refcnt = 1;
360292920Sdim	len = strlen(name);
361292920Sdim	newname = malloc(len + 1, M_SYSCTLOID, M_WAITOK);
362292920Sdim	bcopy(name, newname, len + 1);
363292920Sdim	newname[len] = '\0';
364292920Sdim	oidp->oid_name = newname;
365292920Sdim	oidp->oid_handler = handler;
366314564Sdim	oidp->oid_kind = CTLFLAG_DYN | kind;
367292920Sdim	if ((kind & CTLTYPE) == CTLTYPE_NODE) {
368292920Sdim		/* Allocate space for children */
369292920Sdim		SYSCTL_CHILDREN(oidp) = malloc(sizeof(struct sysctl_oid_list),
370292920Sdim		    M_SYSCTLOID, M_WAITOK);
371292920Sdim		SLIST_INIT(SYSCTL_CHILDREN(oidp));
372292920Sdim	} else {
373292920Sdim		oidp->oid_arg1 = arg1;
374292920Sdim		oidp->oid_arg2 = arg2;
375292920Sdim	}
376314564Sdim	oidp->oid_fmt = fmt;
377292920Sdim	if (descr) {
378292920Sdim		int len = strlen(descr) + 1;
379292920Sdim		oidp->descr = malloc(len, M_SYSCTLOID, M_WAITOK);
380292920Sdim		if (oidp->descr)
381292920Sdim			strcpy((char *)(uintptr_t)(const void *)oidp->descr, descr);
382292920Sdim	}
383292920Sdim	/* Update the context, if used */
384292920Sdim	if (clist != NULL)
385292920Sdim		sysctl_ctx_entry_add(clist, oidp);
386309124Sdim	/* Register this oid */
387314564Sdim	sysctl_register_oid(oidp);
388292920Sdim	return (oidp);
389292920Sdim}
390292920Sdim
391292920Sdim/*
392292920Sdim * Register the kernel's oids on startup.
393292920Sdim */
394292920SdimSET_DECLARE(sysctl_set, struct sysctl_oid);
395292920Sdim
396292920Sdimstatic void
397292920Sdimsysctl_register_all(void *arg)
398292920Sdim{
399292920Sdim	struct sysctl_oid **oidp;
400309124Sdim
401314564Sdim	SYSCTL_INIT();
402292920Sdim	SET_FOREACH(oidp, sysctl_set)
403292920Sdim		sysctl_register_oid(*oidp);
404292920Sdim}
405292920SdimSYSINIT(sysctl, SI_SUB_KMEM, SI_ORDER_ANY, sysctl_register_all, 0);
406292920Sdim
407292920Sdim/*
408292920Sdim * "Staff-functions"
409292920Sdim *
410292920Sdim * These functions implement a presently undocumented interface
411314564Sdim * used by the sysctl program to walk the tree, and get the type
412292920Sdim * so it can print the value.
413292920Sdim * This interface is under work and consideration, and should probably
414292920Sdim * be killed with a big axe by the first person who can find the time.
415292920Sdim * (be aware though, that the proper interface isn't as obvious as it
416292920Sdim * may seem, there are various conflicting requirements.
417292920Sdim *
418292920Sdim * {0,0}	printf the entire MIB-tree.
419292920Sdim * {0,1,...}	return the name of the "..." OID.
420314564Sdim * {0,2,...}	return the next OID.
421292920Sdim * {0,3}	return the OID of the name in "new"
422292920Sdim * {0,4,...}	return the kind & format info for the "..." OID.
423292920Sdim * {0,5,...}	return the description the "..." OID.
424292920Sdim */
425292920Sdim
426292920Sdimstatic void
427292920Sdimsysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i)
428292920Sdim{
429309124Sdim	int k;
430314564Sdim	struct sysctl_oid *oidp;
431292920Sdim
432292920Sdim	SLIST_FOREACH(oidp, l, oid_link) {
433292920Sdim
434292920Sdim		for (k=0; k<i; k++)
435292920Sdim			printf(" ");
436292920Sdim
437292920Sdim		printf("%d %s ", oidp->oid_number, oidp->oid_name);
438292920Sdim
439292920Sdim		printf("%c%c",
440292920Sdim			oidp->oid_kind & CTLFLAG_RD ? 'R':' ',
441292920Sdim			oidp->oid_kind & CTLFLAG_WR ? 'W':' ');
442292920Sdim
443309124Sdim		if (oidp->oid_handler)
444314564Sdim			printf(" *Handler");
445292920Sdim
446292920Sdim		switch (oidp->oid_kind & CTLTYPE) {
447292920Sdim			case CTLTYPE_NODE:
448292920Sdim				printf(" Node\n");
449292920Sdim				if (!oidp->oid_handler) {
450292920Sdim					sysctl_sysctl_debug_dump_node(
451292920Sdim						oidp->oid_arg1, i+2);
452292920Sdim				}
453292920Sdim				break;
454314564Sdim			case CTLTYPE_INT:    printf(" Int\n"); break;
455314564Sdim			case CTLTYPE_STRING: printf(" String\n"); break;
456292920Sdim			case CTLTYPE_QUAD:   printf(" Quad\n"); break;
457292920Sdim			case CTLTYPE_OPAQUE: printf(" Opaque/struct\n"); break;
458292920Sdim			default:	     printf("\n");
459292920Sdim		}
460292920Sdim
461292920Sdim	}
462292920Sdim}
463292920Sdim
464292920Sdimstatic int
465292920Sdimsysctl_sysctl_debug(SYSCTL_HANDLER_ARGS)
466292920Sdim{
467292920Sdim	int error;
468292920Sdim
469292920Sdim	error = suser(req->td);
470292920Sdim	if (error)
471292920Sdim		return error;
472292920Sdim	sysctl_sysctl_debug_dump_node(&sysctl__children, 0);
473292920Sdim	return ENOENT;
474292920Sdim}
475292920Sdim
476292920SdimSYSCTL_PROC(_sysctl, 0, debug, CTLTYPE_STRING|CTLFLAG_RD,
477309124Sdim	0, 0, sysctl_sysctl_debug, "-", "");
478292920Sdim
479292920Sdimstatic int
480292920Sdimsysctl_sysctl_name(SYSCTL_HANDLER_ARGS)
481292920Sdim{
482292920Sdim	int *name = (int *) arg1;
483360784Sdim	u_int namelen = arg2;
484360784Sdim	int error = 0;
485360784Sdim	struct sysctl_oid *oid;
486309124Sdim	struct sysctl_oid_list *lsp = &sysctl__children, *lsp2;
487292920Sdim	char buf[10];
488292920Sdim
489292920Sdim	while (namelen) {
490292920Sdim		if (!lsp) {
491292920Sdim			snprintf(buf,sizeof(buf),"%d",*name);
492292920Sdim			if (req->oldidx)
493292920Sdim				error = SYSCTL_OUT(req, ".", 1);
494314564Sdim			if (!error)
495314564Sdim				error = SYSCTL_OUT(req, buf, strlen(buf));
496292920Sdim			if (error)
497292920Sdim				return (error);
498292920Sdim			namelen--;
499292920Sdim			name++;
500292920Sdim			continue;
501292920Sdim		}
502292920Sdim		lsp2 = 0;
503292920Sdim		SLIST_FOREACH(oid, lsp, oid_link) {
504292920Sdim			if (oid->oid_number != *name)
505292920Sdim				continue;
506314564Sdim
507314564Sdim			if (req->oldidx)
508292920Sdim				error = SYSCTL_OUT(req, ".", 1);
509292920Sdim			if (!error)
510292920Sdim				error = SYSCTL_OUT(req, oid->oid_name,
511292920Sdim					strlen(oid->oid_name));
512292920Sdim			if (error)
513292920Sdim				return (error);
514292920Sdim
515292920Sdim			namelen--;
516292920Sdim			name++;
517292920Sdim
518292920Sdim			if ((oid->oid_kind & CTLTYPE) != CTLTYPE_NODE)
519292920Sdim				break;
520292920Sdim
521292920Sdim			if (oid->oid_handler)
522292920Sdim				break;
523292920Sdim
524292920Sdim			lsp2 = (struct sysctl_oid_list *)oid->oid_arg1;
525292920Sdim			break;
526292920Sdim		}
527292920Sdim		lsp = lsp2;
528292920Sdim	}
529309124Sdim	return (SYSCTL_OUT(req, "", 1));
530292920Sdim}
531292920Sdim
532292920SdimSYSCTL_NODE(_sysctl, 1, name, CTLFLAG_RD, sysctl_sysctl_name, "");
533292920Sdim
534292920Sdimstatic int
535360784Sdimsysctl_sysctl_next_ls(struct sysctl_oid_list *lsp, int *name, u_int namelen,
536360784Sdim	int *next, int *len, int level, struct sysctl_oid **oidpp)
537360784Sdim{
538309124Sdim	struct sysctl_oid *oidp;
539292920Sdim
540292920Sdim	*len = level;
541292920Sdim	SLIST_FOREACH(oidp, lsp, oid_link) {
542292920Sdim		*next = oidp->oid_number;
543292920Sdim		*oidpp = oidp;
544292920Sdim
545292920Sdim		if (oidp->oid_kind & CTLFLAG_SKIP)
546314564Sdim			continue;
547314564Sdim
548292920Sdim		if (!namelen) {
549292920Sdim			if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
550292920Sdim				return 0;
551292920Sdim			if (oidp->oid_handler)
552292920Sdim				/* We really should call the handler here...*/
553292920Sdim				return 0;
554360784Sdim			lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
555360784Sdim			if (!sysctl_sysctl_next_ls(lsp, 0, 0, next+1,
556360784Sdim				len, level+1, oidpp))
557360784Sdim				return 0;
558360784Sdim			goto next;
559360784Sdim		}
560360784Sdim
561360784Sdim		if (oidp->oid_number < *name)
562360784Sdim			continue;
563360784Sdim
564360784Sdim		if (oidp->oid_number > *name) {
565360784Sdim			if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
566360784Sdim				return 0;
567360784Sdim			if (oidp->oid_handler)
568360784Sdim				return 0;
569360784Sdim			lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
570360784Sdim			if (!sysctl_sysctl_next_ls(lsp, name+1, namelen-1,
571360784Sdim				next+1, len, level+1, oidpp))
572360784Sdim				return (0);
573360784Sdim			goto next;
574360784Sdim		}
575360784Sdim		if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
576360784Sdim			continue;
577360784Sdim
578292920Sdim		if (oidp->oid_handler)
579292920Sdim			continue;
580292920Sdim
581314564Sdim		lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
582314564Sdim		if (!sysctl_sysctl_next_ls(lsp, name+1, namelen-1, next+1,
583292920Sdim			len, level+1, oidpp))
584292920Sdim			return (0);
585292920Sdim	next:
586292920Sdim		namelen = 1;
587292920Sdim		*len = level;
588292920Sdim	}
589292920Sdim	return 1;
590292920Sdim}
591292920Sdim
592292920Sdimstatic int
593292920Sdimsysctl_sysctl_next(SYSCTL_HANDLER_ARGS)
594292920Sdim{
595292920Sdim	int *name = (int *) arg1;
596314564Sdim	u_int namelen = arg2;
597314564Sdim	int i, j, error;
598292920Sdim	struct sysctl_oid *oid;
599292920Sdim	struct sysctl_oid_list *lsp = &sysctl__children;
600292920Sdim	int newoid[CTL_MAXNAME];
601292920Sdim
602292920Sdim	i = sysctl_sysctl_next_ls(lsp, name, namelen, newoid, &j, 1, &oid);
603292920Sdim	if (i)
604292920Sdim		return ENOENT;
605292920Sdim	error = SYSCTL_OUT(req, newoid, j * sizeof (int));
606292920Sdim	return (error);
607314564Sdim}
608292920Sdim
609292920SdimSYSCTL_NODE(_sysctl, 2, next, CTLFLAG_RD, sysctl_sysctl_next, "");
610292920Sdim
611292920Sdimstatic int
612292920Sdimname2oid (char *name, int *oid, int *len, struct sysctl_oid **oidpp)
613292920Sdim{
614292920Sdim	int i;
615292920Sdim	struct sysctl_oid *oidp;
616292920Sdim	struct sysctl_oid_list *lsp = &sysctl__children;
617292920Sdim	char *p;
618292920Sdim
619292920Sdim	if (!*name)
620292920Sdim		return ENOENT;
621314564Sdim
622292920Sdim	p = name + strlen(name) - 1 ;
623292920Sdim	if (*p == '.')
624292920Sdim		*p = '\0';
625292920Sdim
626292920Sdim	*len = 0;
627292920Sdim
628292920Sdim	for (p = name; *p && *p != '.'; p++)
629292920Sdim		;
630292920Sdim	i = *p;
631292920Sdim	if (i == '.')
632292920Sdim		*p = '\0';
633292920Sdim
634292920Sdim	oidp = SLIST_FIRST(lsp);
635292920Sdim
636292920Sdim	while (oidp && *len < CTL_MAXNAME) {
637292920Sdim		if (strcmp(name, oidp->oid_name)) {
638292920Sdim			oidp = SLIST_NEXT(oidp, oid_link);
639292920Sdim			continue;
640292920Sdim		}
641292920Sdim		*oid++ = oidp->oid_number;
642292920Sdim		(*len)++;
643292920Sdim
644292920Sdim		if (!i) {
645292920Sdim			if (oidpp)
646292920Sdim				*oidpp = oidp;
647292920Sdim			return (0);
648292920Sdim		}
649292920Sdim
650292920Sdim		if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
651292920Sdim			break;
652292920Sdim
653292920Sdim		if (oidp->oid_handler)
654292920Sdim			break;
655292920Sdim
656292920Sdim		lsp = (struct sysctl_oid_list *)oidp->oid_arg1;
657292920Sdim		oidp = SLIST_FIRST(lsp);
658292920Sdim		name = p+1;
659292920Sdim		for (p = name; *p && *p != '.'; p++)
660292920Sdim				;
661292920Sdim		i = *p;
662292920Sdim		if (i == '.')
663292920Sdim			*p = '\0';
664292920Sdim	}
665292920Sdim	return ENOENT;
666292920Sdim}
667292920Sdim
668292920Sdimstatic int
669292920Sdimsysctl_sysctl_name2oid(SYSCTL_HANDLER_ARGS)
670321369Sdim{
671321369Sdim	char *p;
672327952Sdim	int error, oid[CTL_MAXNAME], len;
673321369Sdim	struct sysctl_oid *op = 0;
674321369Sdim
675321369Sdim	if (!req->newlen)
676327952Sdim		return ENOENT;
677292920Sdim	if (req->newlen >= MAXPATHLEN)	/* XXX arbitrary, undocumented */
678321369Sdim		return (ENAMETOOLONG);
679292920Sdim
680327952Sdim	p = malloc(req->newlen+1, M_SYSCTL, M_WAITOK);
681321369Sdim
682292920Sdim	error = SYSCTL_IN(req, p, req->newlen);
683292920Sdim	if (error) {
684292920Sdim		free(p, M_SYSCTL);
685292920Sdim		return (error);
686321369Sdim	}
687292920Sdim
688321369Sdim	p [req->newlen] = '\0';
689321369Sdim
690321369Sdim	error = name2oid(p, oid, &len, &op);
691327952Sdim
692321369Sdim	free(p, M_SYSCTL);
693292920Sdim
694292920Sdim	if (error)
695292920Sdim		return (error);
696292920Sdim
697292920Sdim	error = SYSCTL_OUT(req, oid, len * sizeof *oid);
698314564Sdim	return (error);
699314564Sdim}
700292920Sdim
701292920SdimSYSCTL_PROC(_sysctl, 3, name2oid, CTLFLAG_RW|CTLFLAG_ANYBODY, 0, 0,
702292920Sdim	sysctl_sysctl_name2oid, "I", "");
703292920Sdim
704292920Sdimstatic int
705292920Sdimsysctl_sysctl_oidfmt(SYSCTL_HANDLER_ARGS)
706292920Sdim{
707292920Sdim	struct sysctl_oid *oid;
708292920Sdim	int error;
709314564Sdim
710314564Sdim	error = sysctl_find_oid(arg1, arg2, &oid, NULL, req);
711292920Sdim	if (error)
712292920Sdim		return (error);
713292920Sdim
714292920Sdim	if (!oid->oid_fmt)
715292920Sdim		return (ENOENT);
716292920Sdim	error = SYSCTL_OUT(req, &oid->oid_kind, sizeof(oid->oid_kind));
717292920Sdim	if (error)
718292920Sdim		return (error);
719309124Sdim	error = SYSCTL_OUT(req, oid->oid_fmt, strlen(oid->oid_fmt) + 1);
720309124Sdim	return (error);
721314564Sdim}
722292920Sdim
723292920Sdim
724292920SdimSYSCTL_NODE(_sysctl, 4, oidfmt, CTLFLAG_RD, sysctl_sysctl_oidfmt, "");
725292920Sdim
726292920Sdimstatic int
727292920Sdimsysctl_sysctl_oiddescr(SYSCTL_HANDLER_ARGS)
728292920Sdim{
729292920Sdim	struct sysctl_oid *oid;
730292920Sdim	int error;
731292920Sdim
732292920Sdim	error = sysctl_find_oid(arg1, arg2, &oid, NULL, req);
733309124Sdim	if (error)
734309124Sdim		return (error);
735314564Sdim
736292920Sdim	if (!oid->descr)
737292920Sdim		return (ENOENT);
738292920Sdim	error = SYSCTL_OUT(req, oid->descr, strlen(oid->descr) + 1);
739292920Sdim	return (error);
740292920Sdim}
741292920Sdim
742292920SdimSYSCTL_NODE(_sysctl, 5, oiddescr, CTLFLAG_RD, sysctl_sysctl_oiddescr, "");
743292920Sdim
744309124Sdim/*
745314564Sdim * Default "handler" functions.
746292920Sdim */
747292920Sdim
748292920Sdim/*
749292920Sdim * Handle an int, signed or unsigned.
750292920Sdim * Two cases:
751292920Sdim *     a variable:  point arg1 at it.
752292920Sdim *     a constant:  pass it in arg2.
753292920Sdim */
754292920Sdim
755292920Sdimint
756309124Sdimsysctl_handle_int(SYSCTL_HANDLER_ARGS)
757314564Sdim{
758292920Sdim	int tmpout, error = 0;
759292920Sdim
760292920Sdim	/*
761292920Sdim	 * Attempt to get a coherent snapshot by making a copy of the data.
762292920Sdim	 */
763292920Sdim	if (arg1)
764292920Sdim		tmpout = *(int *)arg1;
765292920Sdim	else
766292920Sdim		tmpout = arg2;
767309124Sdim	error = SYSCTL_OUT(req, &tmpout, sizeof(int));
768314564Sdim
769292920Sdim	if (error || !req->newptr)
770292920Sdim		return (error);
771292920Sdim
772292920Sdim	if (!arg1)
773292920Sdim		error = EPERM;
774292920Sdim	else
775292920Sdim		error = SYSCTL_IN(req, arg1, sizeof(int));
776292920Sdim	return (error);
777292920Sdim}
778292920Sdim
779292920Sdim/*
780292920Sdim * Handle a long, signed or unsigned.  arg1 points to it.
781309124Sdim */
782314564Sdim
783292920Sdimint
784292920Sdimsysctl_handle_long(SYSCTL_HANDLER_ARGS)
785292920Sdim{
786292920Sdim	int error = 0;
787292920Sdim	long tmpout;
788292920Sdim
789292920Sdim	/*
790292920Sdim	 * Attempt to get a coherent snapshot by making a copy of the data.
791292920Sdim	 */
792309124Sdim	if (!arg1)
793314564Sdim		return (EINVAL);
794292920Sdim	tmpout = *(long *)arg1;
795292920Sdim	error = SYSCTL_OUT(req, &tmpout, sizeof(long));
796292920Sdim
797292920Sdim	if (error || !req->newptr)
798292920Sdim		return (error);
799292920Sdim
800292920Sdim	error = SYSCTL_IN(req, arg1, sizeof(long));
801292920Sdim	return (error);
802292920Sdim}
803292920Sdim
804292920Sdim/*
805292920Sdim * Handle our generic '\0' terminated 'C' string.
806292920Sdim * Two cases:
807292920Sdim * 	a variable string:  point arg1 at it, arg2 is max length.
808292920Sdim * 	a constant string:  point arg1 at it, arg2 is zero.
809292920Sdim */
810292920Sdim
811292920Sdimint
812309124Sdimsysctl_handle_string(SYSCTL_HANDLER_ARGS)
813314564Sdim{
814292920Sdim	int error=0;
815292920Sdim	char *tmparg;
816292920Sdim	size_t outlen;
817292920Sdim
818292920Sdim	/*
819292920Sdim	 * Attempt to get a coherent snapshot by copying to a
820292920Sdim	 * temporary kernel buffer.
821292920Sdim	 */
822292920Sdimretry:
823292920Sdim	outlen = strlen((char *)arg1)+1;
824309124Sdim	tmparg = malloc(outlen, M_SYSCTLTMP, M_WAITOK);
825314564Sdim	strncpy(tmparg, (char *)arg1, outlen);
826292920Sdim	if (tmparg[outlen-1] != '\0') {
827292920Sdim		free(tmparg, M_SYSCTLTMP);
828292920Sdim		goto retry;
829292920Sdim	}
830292920Sdim	error = SYSCTL_OUT(req, tmparg, outlen);
831292920Sdim	free(tmparg, M_SYSCTLTMP);
832292920Sdim
833292920Sdim	if (error || !req->newptr)
834292920Sdim		return (error);
835292920Sdim
836292920Sdim	if ((req->newlen - req->newidx) >= arg2) {
837292920Sdim		error = EINVAL;
838309124Sdim	} else {
839314564Sdim		arg2 = (req->newlen - req->newidx);
840292920Sdim		error = SYSCTL_IN(req, arg1, arg2);
841292920Sdim		((char *)arg1)[arg2] = '\0';
842292920Sdim	}
843292920Sdim
844292920Sdim	return (error);
845309124Sdim}
846309124Sdim
847309124Sdim/*
848314564Sdim * Handle any kind of opaque data.
849314564Sdim * arg1 points to it, arg2 is the size.
850309124Sdim */
851309124Sdim
852309124Sdimint
853309124Sdimsysctl_handle_opaque(SYSCTL_HANDLER_ARGS)
854309124Sdim{
855309124Sdim	int error;
856309124Sdim	void *tmparg;
857309124Sdim
858309124Sdim	/*
859309124Sdim	 * Attempt to get a coherent snapshot, either by wiring the
860309124Sdim	 * user space buffer or copying to a temporary kernel buffer
861309124Sdim	 * depending on the size of the data.
862314564Sdim	 */
863314564Sdim	if (arg2 > PAGE_SIZE) {
864309124Sdim		sysctl_wire_old_buffer(req, arg2);
865309124Sdim		error = SYSCTL_OUT(req, arg1, arg2);
866309124Sdim	} else {
867309124Sdim		tmparg = malloc(arg2, M_SYSCTLTMP, M_WAITOK);
868309124Sdim		bcopy(arg1, tmparg, arg2);
869309124Sdim		error = SYSCTL_OUT(req, tmparg, arg2);
870309124Sdim		free(tmparg, M_SYSCTLTMP);
871309124Sdim	}
872309124Sdim
873309124Sdim	if (error || !req->newptr)
874314564Sdim		return (error);
875309124Sdim
876309124Sdim	error = SYSCTL_IN(req, arg1, arg2);
877309124Sdim
878309124Sdim	return (error);
879309124Sdim}
880309124Sdim
881309124Sdim/*
882309124Sdim * Transfer functions to/from kernel space.
883309124Sdim * XXX: rather untested at this point
884309124Sdim */
885309124Sdimstatic int
886309124Sdimsysctl_old_kernel(struct sysctl_req *req, const void *p, size_t l)
887309124Sdim{
888309124Sdim	size_t i = 0;
889309124Sdim
890309124Sdim	if (req->oldptr) {
891309124Sdim		i = l;
892309124Sdim		if (req->oldlen <= req->oldidx)
893309124Sdim			i = 0;
894309124Sdim		else
895309124Sdim			if (i > req->oldlen - req->oldidx)
896309124Sdim				i = req->oldlen - req->oldidx;
897309124Sdim		if (i > 0)
898309124Sdim			bcopy(p, (char *)req->oldptr + req->oldidx, i);
899309124Sdim	}
900309124Sdim	req->oldidx += l;
901309124Sdim	if (req->oldptr && i != l)
902360784Sdim		return (ENOMEM);
903360784Sdim	return (0);
904360784Sdim}
905309124Sdim
906309124Sdimstatic int
907309124Sdimsysctl_new_kernel(struct sysctl_req *req, void *p, size_t l)
908309124Sdim{
909309124Sdim	if (!req->newptr)
910309124Sdim		return 0;
911309124Sdim	if (req->newlen - req->newidx < l)
912309124Sdim		return (EINVAL);
913309124Sdim	bcopy((char *)req->newptr + req->newidx, p, l);
914309124Sdim	req->newidx += l;
915314564Sdim	return (0);
916309124Sdim}
917309124Sdim
918309124Sdimint
919309124Sdimkernel_sysctl(struct thread *td, int *name, u_int namelen, void *old,
920309124Sdim    size_t *oldlenp, void *new, size_t newlen, size_t *retval)
921309124Sdim{
922292920Sdim	int error = 0;
923292920Sdim	struct sysctl_req req;
924292920Sdim
925314564Sdim	bzero(&req, sizeof req);
926314564Sdim
927314564Sdim	req.td = td;
928292920Sdim
929292920Sdim	if (oldlenp) {
930292920Sdim		req.oldlen = *oldlenp;
931292920Sdim	}
932292920Sdim
933292920Sdim	if (old) {
934292920Sdim		req.oldptr= old;
935292920Sdim	}
936292920Sdim
937292920Sdim	if (new != NULL) {
938314564Sdim		req.newlen = newlen;
939314564Sdim		req.newptr = new;
940314564Sdim	}
941292920Sdim
942292920Sdim	req.oldfunc = sysctl_old_kernel;
943292920Sdim	req.newfunc = sysctl_new_kernel;
944309124Sdim	req.lock = 1;
945309124Sdim
946327952Sdim	SYSCTL_LOCK();
947314564Sdim
948314564Sdim	error = sysctl_root(0, name, namelen, &req);
949327952Sdim
950309124Sdim	if (req.lock == 2)
951309124Sdim		vsunlock(req.oldptr, req.oldlen);
952309124Sdim
953327952Sdim	SYSCTL_UNLOCK();
954309124Sdim
955309124Sdim	if (error && error != ENOMEM)
956309124Sdim		return (error);
957309124Sdim
958309124Sdim	if (retval) {
959309124Sdim		if (req.oldptr && req.oldidx > req.oldlen)
960314564Sdim			*retval = req.oldlen;
961314564Sdim		else
962327952Sdim			*retval = req.oldidx;
963309124Sdim	}
964309124Sdim	return (error);
965309124Sdim}
966327952Sdim
967327952Sdimint
968327952Sdimkernel_sysctlbyname(struct thread *td, char *name, void *old, size_t *oldlenp,
969314564Sdim    void *new, size_t newlen, size_t *retval)
970314564Sdim{
971327952Sdim        int oid[CTL_MAXNAME];
972309124Sdim        size_t oidlen, plen;
973309124Sdim	int error;
974309124Sdim
975327952Sdim	oid[0] = 0;		/* sysctl internal magic */
976309124Sdim	oid[1] = 3;		/* name2oid */
977309124Sdim	oidlen = sizeof(oid);
978309124Sdim
979309124Sdim	error = kernel_sysctl(td, oid, 2, oid, &oidlen,
980309124Sdim	    (void *)name, strlen(name), &plen);
981309124Sdim	if (error)
982314564Sdim		return (error);
983314564Sdim
984327952Sdim	error = kernel_sysctl(td, oid, plen / sizeof(int), old, oldlenp,
985309124Sdim	    new, newlen, retval);
986309124Sdim	return (error);
987309124Sdim}
988292920Sdim
989292920Sdim/*
990292920Sdim * Transfer function to/from user space.
991292920Sdim */
992292920Sdimstatic int
993309124Sdimsysctl_old_user(struct sysctl_req *req, const void *p, size_t l)
994314564Sdim{
995292920Sdim	int error = 0;
996292920Sdim	size_t i = 0;
997292920Sdim
998292920Sdim	if (req->lock == 1 && req->oldptr)
999292920Sdim		WITNESS_SLEEP(1, NULL);
1000292920Sdim	if (req->oldptr) {
1001292920Sdim		i = l;
1002292920Sdim		if (req->oldlen <= req->oldidx)
1003292920Sdim			i = 0;
1004292920Sdim		else
1005292920Sdim			if (i > req->oldlen - req->oldidx)
1006292920Sdim				i = req->oldlen - req->oldidx;
1007309124Sdim		if (i > 0)
1008314564Sdim			error = copyout(p, (char *)req->oldptr + req->oldidx,
1009292920Sdim					i);
1010292920Sdim	}
1011292920Sdim	req->oldidx += l;
1012292920Sdim	if (error)
1013292920Sdim		return (error);
1014292920Sdim	if (req->oldptr && i < l)
1015292920Sdim		return (ENOMEM);
1016292920Sdim	return (0);
1017292920Sdim}
1018309124Sdim
1019314564Sdimstatic int
1020292920Sdimsysctl_new_user(struct sysctl_req *req, void *p, size_t l)
1021292920Sdim{
1022292920Sdim	int error;
1023292920Sdim
1024292920Sdim	if (!req->newptr)
1025292920Sdim		return 0;
1026292920Sdim	if (req->newlen - req->newidx < l)
1027292920Sdim		return (EINVAL);
1028292920Sdim	error = copyin((char *)req->newptr + req->newidx, p, l);
1029292920Sdim	req->newidx += l;
1030292920Sdim	return (error);
1031292920Sdim}
1032292920Sdim
1033292920Sdim/*
1034292920Sdim * Wire the user space destination buffer.  If set to a value greater than
1035292920Sdim * zero, the len parameter limits the maximum amount of wired memory.
1036292920Sdim *
1037292920Sdim * XXX - The len parameter is currently ignored due to the lack of
1038292920Sdim * a place to save it in the sysctl_req structure so that the matching
1039292920Sdim * amount of memory can be unwired in the sysctl exit code.
1040292920Sdim */
1041309124Sdimvoid
1042292920Sdimsysctl_wire_old_buffer(struct sysctl_req *req, size_t len)
1043292920Sdim{
1044292920Sdim	if (req->lock == 1 && req->oldptr && req->oldfunc == sysctl_old_user) {
1045292920Sdim		vslock(req->oldptr, req->oldlen);
1046292920Sdim		req->lock = 2;
1047360784Sdim	}
1048360784Sdim}
1049360784Sdim
1050309124Sdimint
1051292920Sdimsysctl_find_oid(int *name, u_int namelen, struct sysctl_oid **noid,
1052292920Sdim    int *nindx, struct sysctl_req *req)
1053292920Sdim{
1054292920Sdim	struct sysctl_oid *oid;
1055292920Sdim	int indx;
1056292920Sdim
1057292920Sdim	oid = SLIST_FIRST(&sysctl__children);
1058309124Sdim	indx = 0;
1059314564Sdim	while (oid && indx < CTL_MAXNAME) {
1060292920Sdim		if (oid->oid_number == name[indx]) {
1061292920Sdim			indx++;
1062292920Sdim			if (oid->oid_kind & CTLFLAG_NOLOCK)
1063292920Sdim				req->lock = 0;
1064292920Sdim			if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
1065292920Sdim				if (oid->oid_handler != NULL ||
1066292920Sdim				    indx == namelen) {
1067292920Sdim					*noid = oid;
1068292920Sdim					if (nindx != NULL)
1069292920Sdim						*nindx = indx;
1070314564Sdim					return (0);
1071314564Sdim				}
1072292920Sdim				oid = SLIST_FIRST(
1073292920Sdim				    (struct sysctl_oid_list *)oid->oid_arg1);
1074292920Sdim			} else if (indx == namelen) {
1075292920Sdim				*noid = oid;
1076292920Sdim				if (nindx != NULL)
1077292920Sdim					*nindx = indx;
1078292920Sdim				return (0);
1079292920Sdim			} else {
1080292920Sdim				return (ENOTDIR);
1081292920Sdim			}
1082292920Sdim		} else {
1083292920Sdim			oid = SLIST_NEXT(oid, oid_link);
1084292920Sdim		}
1085292920Sdim	}
1086292920Sdim	return (ENOENT);
1087292920Sdim}
1088292920Sdim
1089292920Sdim/*
1090292920Sdim * Traverse our tree, and find the right node, execute whatever it points
1091292920Sdim * to, and return the resulting error code.
1092292920Sdim */
1093309124Sdim
1094292920Sdimstatic int
1095292920Sdimsysctl_root(SYSCTL_HANDLER_ARGS)
1096292920Sdim{
1097292920Sdim	struct sysctl_oid *oid;
1098292920Sdim	int error, indx;
1099360784Sdim
1100360784Sdim	error = sysctl_find_oid(arg1, arg2, &oid, &indx, req);
1101360784Sdim	if (error)
1102309124Sdim		return (error);
1103292920Sdim
1104292920Sdim	if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
1105292920Sdim		/*
1106292920Sdim		 * You can't call a sysctl when it's a node, but has
1107292920Sdim		 * no handler.  Inform the user that it's a node.
1108292920Sdim		 * The indx may or may not be the same as namelen.
1109314564Sdim		 */
1110314564Sdim		if (oid->oid_handler == NULL)
1111292920Sdim			return (EISDIR);
1112292920Sdim	}
1113292920Sdim
1114292920Sdim	/* Is this sysctl writable? */
1115292920Sdim	if (req->newptr && !(oid->oid_kind & CTLFLAG_WR))
1116292920Sdim		return (EPERM);
1117360784Sdim
1118360784Sdim	KASSERT(req->td != NULL, ("sysctl_root(): req->td == NULL"));
1119360784Sdim
1120360784Sdim	/* Is this sysctl sensitive to securelevels? */
1121360784Sdim	if (req->newptr && (oid->oid_kind & CTLFLAG_SECURE)) {
1122360784Sdim		error = securelevel_gt(req->td->td_ucred, 0);
1123360784Sdim		if (error)
1124360784Sdim			return (error);
1125360784Sdim	}
1126360784Sdim
1127360784Sdim	/* Is this sysctl writable by only privileged users? */
1128360784Sdim	if (req->newptr && !(oid->oid_kind & CTLFLAG_ANYBODY)) {
1129360784Sdim		int flags;
1130360784Sdim
1131360784Sdim		if (oid->oid_kind & CTLFLAG_PRISON)
1132360784Sdim			flags = PRISON_ROOT;
1133360784Sdim		else
1134360784Sdim			flags = 0;
1135360784Sdim		error = suser_cred(req->td->td_ucred, flags);
1136360784Sdim		if (error)
1137360784Sdim			return (error);
1138360784Sdim	}
1139360784Sdim
1140360784Sdim	if (!oid->oid_handler)
1141360784Sdim		return EINVAL;
1142360784Sdim
1143360784Sdim	if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE)
1144360784Sdim		error = oid->oid_handler(oid, (int *)arg1 + indx, arg2 - indx,
1145360784Sdim		    req);
1146360784Sdim	else
1147360784Sdim		error = oid->oid_handler(oid, oid->oid_arg1, oid->oid_arg2,
1148360784Sdim		    req);
1149360784Sdim	return (error);
1150360784Sdim}
1151360784Sdim
1152360784Sdim#ifndef _SYS_SYSPROTO_H_
1153360784Sdimstruct sysctl_args {
1154360784Sdim	int	*name;
1155360784Sdim	u_int	namelen;
1156360784Sdim	void	*old;
1157360784Sdim	size_t	*oldlenp;
1158360784Sdim	void	*new;
1159360784Sdim	size_t	newlen;
1160360784Sdim};
1161360784Sdim#endif
1162360784Sdim
1163360784Sdim/*
1164360784Sdim * MPSAFE
1165360784Sdim */
1166360784Sdimint
1167360784Sdim__sysctl(struct thread *td, struct sysctl_args *uap)
1168360784Sdim{
1169360784Sdim	int error, name[CTL_MAXNAME];
1170360784Sdim	size_t j;
1171360784Sdim
1172360784Sdim	if (uap->namelen > CTL_MAXNAME || uap->namelen < 2)
1173360784Sdim		return (EINVAL);
1174360784Sdim
1175360784Sdim 	error = copyin(uap->name, &name, uap->namelen * sizeof(int));
1176360784Sdim 	if (error)
1177360784Sdim		return (error);
1178360784Sdim
1179360784Sdim	mtx_lock(&Giant);
1180360784Sdim
1181360784Sdim	error = userland_sysctl(td, name, uap->namelen,
1182360784Sdim		uap->old, uap->oldlenp, 0,
1183360784Sdim		uap->new, uap->newlen, &j);
1184360784Sdim	if (error && error != ENOMEM)
1185360784Sdim		goto done2;
1186360784Sdim	if (uap->oldlenp) {
1187360784Sdim		int i = copyout(&j, uap->oldlenp, sizeof(j));
1188360784Sdim		if (i)
1189360784Sdim			error = i;
1190360784Sdim	}
1191360784Sdimdone2:
1192360784Sdim	mtx_unlock(&Giant);
1193360784Sdim	return (error);
1194360784Sdim}
1195360784Sdim
1196360784Sdim/*
1197360784Sdim * This is used from various compatibility syscalls too.  That's why name
1198360784Sdim * must be in kernel space.
1199360784Sdim */
1200360784Sdimint
1201360784Sdimuserland_sysctl(struct thread *td, int *name, u_int namelen, void *old,
1202360784Sdim    size_t *oldlenp, int inkernel, void *new, size_t newlen, size_t *retval)
1203360784Sdim{
1204360784Sdim	int error = 0;
1205360784Sdim	struct sysctl_req req, req2;
1206360784Sdim
1207360784Sdim	bzero(&req, sizeof req);
1208360784Sdim
1209360784Sdim	req.td = td;
1210360784Sdim
1211360784Sdim	if (oldlenp) {
1212360784Sdim		if (inkernel) {
1213360784Sdim			req.oldlen = *oldlenp;
1214360784Sdim		} else {
1215360784Sdim			error = copyin(oldlenp, &req.oldlen, sizeof(*oldlenp));
1216360784Sdim			if (error)
1217360784Sdim				return (error);
1218360784Sdim		}
1219360784Sdim	}
1220360784Sdim
1221360784Sdim	if (old) {
1222360784Sdim		if (!useracc(old, req.oldlen, VM_PROT_WRITE))
1223360784Sdim			return (EFAULT);
1224360784Sdim		req.oldptr= old;
1225360784Sdim	}
1226360784Sdim
1227360784Sdim	if (new != NULL) {
1228360784Sdim		if (!useracc(new, req.newlen, VM_PROT_READ))
1229360784Sdim			return (EFAULT);
1230360784Sdim		req.newlen = newlen;
1231360784Sdim		req.newptr = new;
1232360784Sdim	}
1233360784Sdim
1234360784Sdim	req.oldfunc = sysctl_old_user;
1235360784Sdim	req.newfunc = sysctl_new_user;
1236360784Sdim	req.lock = 1;
1237360784Sdim
1238360784Sdim	SYSCTL_LOCK();
1239360784Sdim
1240360784Sdim	do {
1241360784Sdim	    req2 = req;
1242360784Sdim	    error = sysctl_root(0, name, namelen, &req2);
1243360784Sdim	} while (error == EAGAIN);
1244360784Sdim
1245360784Sdim	req = req2;
1246360784Sdim	if (req.lock == 2)
1247360784Sdim		vsunlock(req.oldptr, req.oldlen);
1248360784Sdim
1249360784Sdim	SYSCTL_UNLOCK();
1250360784Sdim
1251360784Sdim	if (error && error != ENOMEM)
1252360784Sdim		return (error);
1253360784Sdim
1254360784Sdim	if (retval) {
1255360784Sdim		if (req.oldptr && req.oldidx > req.oldlen)
1256360784Sdim			*retval = req.oldlen;
1257360784Sdim		else
1258360784Sdim			*retval = req.oldidx;
1259360784Sdim	}
1260360784Sdim	return (error);
1261360784Sdim}
1262360784Sdim
1263360784Sdim#ifdef COMPAT_43
1264360784Sdim#include <sys/socket.h>
1265360784Sdim#include <vm/vm_param.h>
1266360784Sdim
1267360784Sdim#define	KINFO_PROC		(0<<8)
1268360784Sdim#define	KINFO_RT		(1<<8)
1269360784Sdim#define	KINFO_VNODE		(2<<8)
1270360784Sdim#define	KINFO_FILE		(3<<8)
1271360784Sdim#define	KINFO_METER		(4<<8)
1272360784Sdim#define	KINFO_LOADAVG		(5<<8)
1273360784Sdim#define	KINFO_CLOCKRATE		(6<<8)
1274360784Sdim
1275360784Sdim/* Non-standard BSDI extension - only present on their 4.3 net-2 releases */
1276360784Sdim#define	KINFO_BSDI_SYSINFO	(101<<8)
1277360784Sdim
1278360784Sdim/*
1279360784Sdim * XXX this is bloat, but I hope it's better here than on the potentially
1280360784Sdim * limited kernel stack...  -Peter
1281360784Sdim */
1282360784Sdim
1283360784Sdimstatic struct {
1284360784Sdim	int	bsdi_machine;		/* "i386" on BSD/386 */
1285360784Sdim/*      ^^^ this is an offset to the string, relative to the struct start */
1286360784Sdim	char	*pad0;
1287360784Sdim	long	pad1;
1288360784Sdim	long	pad2;
1289360784Sdim	long	pad3;
1290360784Sdim	u_long	pad4;
1291360784Sdim	u_long	pad5;
1292360784Sdim	u_long	pad6;
1293360784Sdim
1294360784Sdim	int	bsdi_ostype;		/* "BSD/386" on BSD/386 */
1295360784Sdim	int	bsdi_osrelease;		/* "1.1" on BSD/386 */
1296360784Sdim	long	pad7;
1297360784Sdim	long	pad8;
1298360784Sdim	char	*pad9;
1299360784Sdim
1300360784Sdim	long	pad10;
1301360784Sdim	long	pad11;
1302360784Sdim	int	pad12;
1303360784Sdim	long	pad13;
1304360784Sdim	quad_t	pad14;
1305360784Sdim	long	pad15;
1306360784Sdim
1307360784Sdim	struct	timeval pad16;
1308360784Sdim	/* we dont set this, because BSDI's uname used gethostname() instead */
1309360784Sdim	int	bsdi_hostname;		/* hostname on BSD/386 */
1310360784Sdim
1311360784Sdim	/* the actual string data is appended here */
1312360784Sdim
1313360784Sdim} bsdi_si;
1314360784Sdim/*
1315360784Sdim * this data is appended to the end of the bsdi_si structure during copyout.
1316360784Sdim * The "char *" offsets are relative to the base of the bsdi_si struct.
1317360784Sdim * This contains "FreeBSD\02.0-BUILT-nnnnnn\0i386\0", and these strings
1318360784Sdim * should not exceed the length of the buffer here... (or else!! :-)
1319360784Sdim */
1320360784Sdimstatic char bsdi_strings[80];	/* It had better be less than this! */
1321360784Sdim
1322360784Sdim#ifndef _SYS_SYSPROTO_H_
1323360784Sdimstruct getkerninfo_args {
1324360784Sdim	int	op;
1325360784Sdim	char	*where;
1326360784Sdim	size_t	*size;
1327360784Sdim	int	arg;
1328360784Sdim};
1329360784Sdim#endif
1330360784Sdim
1331360784Sdim/*
1332360784Sdim * MPSAFE
1333360784Sdim */
1334360784Sdimint
1335292920Sdimogetkerninfo(struct thread *td, struct getkerninfo_args *uap)
1336292920Sdim{
1337292920Sdim	int error, name[6];
1338292920Sdim	size_t size;
1339314564Sdim	u_int needed = 0;
1340314564Sdim
1341292920Sdim	mtx_lock(&Giant);
1342292920Sdim
1343292920Sdim	switch (uap->op & 0xff00) {
1344292920Sdim
1345292920Sdim	case KINFO_RT:
1346292920Sdim		name[0] = CTL_NET;
1347292920Sdim		name[1] = PF_ROUTE;
1348292920Sdim		name[2] = 0;
1349292920Sdim		name[3] = (uap->op & 0xff0000) >> 16;
1350292920Sdim		name[4] = uap->op & 0xff;
1351292920Sdim		name[5] = uap->arg;
1352292920Sdim		error = userland_sysctl(td, name, 6, uap->where, uap->size,
1353292920Sdim			0, 0, 0, &size);
1354292920Sdim		break;
1355292920Sdim
1356292920Sdim	case KINFO_VNODE:
1357292920Sdim		name[0] = CTL_KERN;
1358292920Sdim		name[1] = KERN_VNODE;
1359292920Sdim		error = userland_sysctl(td, name, 2, uap->where, uap->size,
1360292920Sdim			0, 0, 0, &size);
1361292920Sdim		break;
1362309124Sdim
1363292920Sdim	case KINFO_PROC:
1364292920Sdim		name[0] = CTL_KERN;
1365292920Sdim		name[1] = KERN_PROC;
1366292920Sdim		name[2] = uap->op & 0xff;
1367292920Sdim		name[3] = uap->arg;
1368360784Sdim		error = userland_sysctl(td, name, 4, uap->where, uap->size,
1369360784Sdim			0, 0, 0, &size);
1370360784Sdim		break;
1371309124Sdim
1372292920Sdim	case KINFO_FILE:
1373292920Sdim		name[0] = CTL_KERN;
1374292920Sdim		name[1] = KERN_FILE;
1375292920Sdim		error = userland_sysctl(td, name, 2, uap->where, uap->size,
1376292920Sdim			0, 0, 0, &size);
1377292920Sdim		break;
1378314564Sdim
1379314564Sdim	case KINFO_METER:
1380292920Sdim		name[0] = CTL_VM;
1381292920Sdim		name[1] = VM_METER;
1382292920Sdim		error = userland_sysctl(td, name, 2, uap->where, uap->size,
1383292920Sdim			0, 0, 0, &size);
1384292920Sdim		break;
1385309124Sdim
1386327952Sdim	case KINFO_LOADAVG:
1387327952Sdim		name[0] = CTL_VM;
1388327952Sdim		name[1] = VM_LOADAVG;
1389314564Sdim		error = userland_sysctl(td, name, 2, uap->where, uap->size,
1390314564Sdim			0, 0, 0, &size);
1391327952Sdim		break;
1392327952Sdim
1393309124Sdim	case KINFO_CLOCKRATE:
1394309124Sdim		name[0] = CTL_KERN;
1395309124Sdim		name[1] = KERN_CLOCKRATE;
1396327952Sdim		error = userland_sysctl(td, name, 2, uap->where, uap->size,
1397309124Sdim			0, 0, 0, &size);
1398309124Sdim		break;
1399309124Sdim
1400309124Sdim	case KINFO_BSDI_SYSINFO: {
1401309124Sdim		/*
1402309124Sdim		 * this is pretty crude, but it's just enough for uname()
1403314564Sdim		 * from BSDI's 1.x libc to work.
1404314564Sdim		 *
1405327952Sdim		 * *size gives the size of the buffer before the call, and
1406327952Sdim		 * the amount of data copied after a successful call.
1407309124Sdim		 * If successful, the return value is the amount of data
1408309124Sdim		 * available, which can be larger than *size.
1409309124Sdim		 *
1410309124Sdim		 * BSDI's 2.x product apparently fails with ENOMEM if *size
1411309124Sdim		 * is too small.
1412309124Sdim		 */
1413327952Sdim
1414309124Sdim		u_int left;
1415314564Sdim		char *s;
1416309124Sdim
1417309124Sdim		bzero((char *)&bsdi_si, sizeof(bsdi_si));
1418309124Sdim		bzero(bsdi_strings, sizeof(bsdi_strings));
1419309124Sdim
1420309124Sdim		s = bsdi_strings;
1421309124Sdim
1422309124Sdim		bsdi_si.bsdi_ostype = (s - bsdi_strings) + sizeof(bsdi_si);
1423309124Sdim		strcpy(s, ostype);
1424309124Sdim		s += strlen(s) + 1;
1425309124Sdim
1426309124Sdim		bsdi_si.bsdi_osrelease = (s - bsdi_strings) + sizeof(bsdi_si);
1427309124Sdim		strcpy(s, osrelease);
1428309124Sdim		s += strlen(s) + 1;
1429309124Sdim
1430309124Sdim		bsdi_si.bsdi_machine = (s - bsdi_strings) + sizeof(bsdi_si);
1431309124Sdim		strcpy(s, machine);
1432309124Sdim		s += strlen(s) + 1;
1433309124Sdim
1434309124Sdim		needed = sizeof(bsdi_si) + (s - bsdi_strings);
1435309124Sdim
1436309124Sdim		if ((uap->where == NULL) || (uap->size == NULL)) {
1437309124Sdim			/* process is asking how much buffer to supply.. */
1438309124Sdim			size = needed;
1439309124Sdim			error = 0;
1440309124Sdim			break;
1441309124Sdim		}
1442321369Sdim
1443321369Sdim		if ((error = copyin(uap->size, &size, sizeof(size))) != 0)
1444309124Sdim			break;
1445309124Sdim
1446309124Sdim		/* if too much buffer supplied, trim it down */
1447309124Sdim		if (size > needed)
1448309124Sdim			size = needed;
1449360784Sdim
1450360784Sdim		/* how much of the buffer is remaining */
1451360784Sdim		left = size;
1452309124Sdim
1453321369Sdim		if ((error = copyout((char *)&bsdi_si, uap->where, left)) != 0)
1454321369Sdim			break;
1455321369Sdim
1456321369Sdim		/* is there any point in continuing? */
1457321369Sdim		if (left > sizeof(bsdi_si)) {
1458321369Sdim			left -= sizeof(bsdi_si);
1459321369Sdim			error = copyout(&bsdi_strings,
1460344779Sdim					uap->where + sizeof(bsdi_si), left);
1461344779Sdim		}
1462327952Sdim		break;
1463309124Sdim	}
1464309124Sdim
1465309124Sdim	default:
1466309124Sdim		error = EOPNOTSUPP;
1467309124Sdim		break;
1468309124Sdim	}
1469309124Sdim	if (error == 0) {
1470309124Sdim		td->td_retval[0] = needed ? needed : size;
1471309124Sdim		if (uap->size) {
1472314564Sdim			error = copyout(&size, uap->size, sizeof(size));
1473309124Sdim		}
1474309124Sdim	}
1475309124Sdim	mtx_unlock(&Giant);
1476309124Sdim	return (error);
1477309124Sdim}
1478309124Sdim#endif /* COMPAT_43 */
1479309124Sdim