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