X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/de355530ae67247cbd0da700edb3a2a1dae884c2..ab86ba338a07a58a89f50cf7066a0f0e487ac0cc:/bsd/kern/kern_newsysctl.c diff --git a/bsd/kern/kern_newsysctl.c b/bsd/kern/kern_newsysctl.c index 399bbdb92..002a77df4 100644 --- a/bsd/kern/kern_newsysctl.c +++ b/bsd/kern/kern_newsysctl.c @@ -3,19 +3,22 @@ * * @APPLE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ @@ -77,6 +80,7 @@ struct sysctl_oid_list sysctl__sysctl_children; */ extern struct sysctl_oid *newsysctl_list[]; +extern struct sysctl_oid *machdep_sysctl_list[]; static void @@ -93,6 +97,33 @@ static struct sysctl_lock { int sl_locked; } memlock; +/* + * XXX this does not belong here + */ +static funnel_t * +spl_kernel_funnel(void) +{ + funnel_t *cfunnel; + + cfunnel = thread_funnel_get(); + if (cfunnel != kernel_flock) { + if (cfunnel != NULL) + thread_funnel_set(cfunnel, FALSE); + thread_funnel_set(kernel_flock, TRUE); + } + return(cfunnel); +} + +static void +splx_kernel_funnel(funnel_t *saved) +{ + if (saved != kernel_flock) { + thread_funnel_set(kernel_flock, FALSE); + if (saved != NULL) + thread_funnel_set(saved, TRUE); + } +} + static int sysctl_root SYSCTL_HANDLER_ARGS; struct sysctl_oid_list sysctl__children; /* root list */ @@ -109,6 +140,9 @@ void sysctl_register_oid(struct sysctl_oid *oidp) struct sysctl_oid *p; struct sysctl_oid *q; int n; + funnel_t *fnl; + + fnl = spl_kernel_funnel(); /* * If this oid has a number OID_AUTO, give it a number which @@ -139,11 +173,17 @@ void sysctl_register_oid(struct sysctl_oid *oidp) SLIST_INSERT_AFTER(q, oidp, oid_link); else SLIST_INSERT_HEAD(parent, oidp, oid_link); + + splx_kernel_funnel(fnl); } void sysctl_unregister_oid(struct sysctl_oid *oidp) { + funnel_t *fnl; + + fnl = spl_kernel_funnel(); SLIST_REMOVE(oidp->oid_parent, oidp, sysctl_oid, oid_link); + splx_kernel_funnel(fnl); } /* @@ -172,12 +212,13 @@ void sysctl_unregister_set(struct linker_set *lsp) void sysctl_register_fixed() { - int i = 0; + int i; - - while (newsysctl_list[i]) { -/* printf("Registering %d\n", i); */ - sysctl_register_oid(newsysctl_list[i++]); + for (i=0; newsysctl_list[i]; i++) { + sysctl_register_oid(newsysctl_list[i]); + } + for (i=0; machdep_sysctl_list[i]; i++) { + sysctl_register_oid(machdep_sysctl_list[i]); } } @@ -581,6 +622,64 @@ sysctl_handle_long SYSCTL_HANDLER_ARGS return (error); } +/* + * Handle a quad, signed or unsigned. arg1 points to it. + */ + +int +sysctl_handle_quad SYSCTL_HANDLER_ARGS +{ + int error = 0; + + if (!arg1) + return (EINVAL); + error = SYSCTL_OUT(req, arg1, sizeof(long long)); + + if (error || !req->newptr) + return (error); + + error = SYSCTL_IN(req, arg1, sizeof(long long)); + return (error); +} + +/* + * Expose an int value as a quad. + * + * This interface allows us to support interfaces defined + * as using quad values while the implementation is still + * using ints. + */ +int +sysctl_handle_int2quad SYSCTL_HANDLER_ARGS +{ + int error = 0; + long long val; + int newval; + + if (!arg1) + return (EINVAL); + val = (long long)*(int *)arg1; + error = SYSCTL_OUT(req, &val, sizeof(long long)); + + if (error || !req->newptr) + return (error); + + error = SYSCTL_IN(req, &val, sizeof(long long)); + if (!error) { + /* + * Value must be representable; check by + * casting and then casting back. + */ + newval = (int)val; + if ((long long)newval != val) { + error = ERANGE; + } else { + *(int *)arg1 = newval; + } + } + return (error); +} + /* * Handle our generic '\0' terminated 'C' string. * Two cases: @@ -631,7 +730,6 @@ sysctl_handle_opaque SYSCTL_HANDLER_ARGS /* * Transfer functions to/from kernel space. - * XXX: rather untested at this point */ static int sysctl_old_kernel(struct sysctl_req *req, const void *p, size_t l) @@ -643,11 +741,8 @@ sysctl_old_kernel(struct sysctl_req *req, const void *p, size_t l) i = l; if (i > req->oldlen - req->oldidx) i = req->oldlen - req->oldidx; - if (i > 0) { - error = copyout((void*)p, (char *)req->oldptr + req->oldidx, i); - if (error) - return error; - } + if (i > 0) + bcopy((void*)p, (char *)req->oldptr + req->oldidx, i); } req->oldidx += l; if (req->oldptr && i != l) @@ -662,38 +757,40 @@ sysctl_new_kernel(struct sysctl_req *req, void *p, size_t l) return 0; if (req->newlen - req->newidx < l) return (EINVAL); - copyin((char *)req->newptr + req->newidx, p, l); + bcopy((char *)req->newptr + req->newidx, p, l); req->newidx += l; return (0); } int -kernel_sysctl(struct proc *p, int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen, size_t *retval) +kernel_sysctl(struct proc *p, int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen) { int error = 0; struct sysctl_req req; + funnel_t *fnl; + /* + * Construct request. + */ bzero(&req, sizeof req); - req.p = p; - - if (oldlenp) { + if (oldlenp) req.oldlen = *oldlenp; - } - - if (old) { + if (old) req.oldptr= old; - } - if (newlen) { req.newlen = newlen; req.newptr = new; } - req.oldfunc = sysctl_old_kernel; req.newfunc = sysctl_new_kernel; req.lock = 1; + /* + * Locking. Tree traversal always begins with the kernel funnel held. + */ + fnl = spl_kernel_funnel(); + /* XXX this should probably be done in a general way */ while (memlock.sl_lock) { memlock.sl_want = 1; @@ -702,8 +799,10 @@ kernel_sysctl(struct proc *p, int *name, u_int namelen, void *old, size_t *oldle } memlock.sl_lock = 1; + /* make the request */ error = sysctl_root(0, name, namelen, &req); + /* unlock memory if required */ if (req.lock == 2) vsunlock(req.oldptr, req.oldlen, B_WRITE); @@ -714,15 +813,17 @@ kernel_sysctl(struct proc *p, int *name, u_int namelen, void *old, size_t *oldle wakeup((caddr_t)&memlock); } + /* + * Undo locking. + */ + splx_kernel_funnel(fnl); + if (error && error != ENOMEM) return (error); - if (retval) { - if (req.oldptr && req.oldidx > req.oldlen) - *retval = req.oldlen; - else - *retval = req.oldidx; - } + if (oldlenp) + *oldlenp = req.oldidx; + return (error); } @@ -814,6 +915,13 @@ found: return (EPERM); } + /* + * If we're inside the kernel, the OID must be marked as kernel-valid. + * XXX This mechanism for testing is bad. + */ + if ((req->oldfunc == sysctl_old_kernel) && !(oid->oid_kind & CTLFLAG_KERN)) + return(EPERM); + /* Most likely only root can write */ if (!(oid->oid_kind & CTLFLAG_ANYBODY) && req->newptr && req->p && @@ -946,214 +1054,58 @@ userland_sysctl(struct proc *p, int *name, u_int namelen, void *old, size_t *old } return (error); } -#if 0 - -#if COMPAT_43 -#include -#include - -#define KINFO_PROC (0<<8) -#define KINFO_RT (1<<8) -#define KINFO_VNODE (2<<8) -#define KINFO_FILE (3<<8) -#define KINFO_METER (4<<8) -#define KINFO_LOADAVG (5<<8) -#define KINFO_CLOCKRATE (6<<8) /* Non-standard BSDI extension - only present on their 4.3 net-2 releases */ #define KINFO_BSDI_SYSINFO (101<<8) /* - * XXX this is bloat, but I hope it's better here than on the potentially - * limited kernel stack... -Peter - */ - -static struct { - int bsdi_machine; /* "i386" on BSD/386 */ -/* ^^^ this is an offset to the string, relative to the struct start */ - char *pad0; - long pad1; - long pad2; - long pad3; - u_long pad4; - u_long pad5; - u_long pad6; - - int bsdi_ostype; /* "BSD/386" on BSD/386 */ - int bsdi_osrelease; /* "1.1" on BSD/386 */ - long pad7; - long pad8; - char *pad9; - - long pad10; - long pad11; - int pad12; - long pad13; - quad_t pad14; - long pad15; - - struct timeval pad16; - /* we dont set this, because BSDI's uname used gethostname() instead */ - int bsdi_hostname; /* hostname on BSD/386 */ - - /* the actual string data is appended here */ - -} bsdi_si; -/* - * this data is appended to the end of the bsdi_si structure during copyout. - * The "char *" offsets are relative to the base of the bsdi_si struct. - * This contains "FreeBSD\02.0-BUILT-nnnnnn\0i386\0", and these strings - * should not exceed the length of the buffer here... (or else!! :-) + * Kernel versions of the userland sysctl helper functions. + * + * These allow sysctl to be used in the same fashion in both + * userland and the kernel. + * + * Note that some sysctl handlers use copyin/copyout, which + * may not work correctly. */ -static char bsdi_strings[80]; /* It had better be less than this! */ - -#ifndef _SYS_SYSPROTO_H_ -struct getkerninfo_args { - int op; - char *where; - size_t *size; - int arg; -}; -#endif -int -ogetkerninfo(struct proc *p, struct getkerninfo_args *uap) +static int +sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int error, name[6]; - size_t size; - - switch (uap->op & 0xff00) { - - case KINFO_RT: - name[0] = CTL_NET; - name[1] = PF_ROUTE; - name[2] = 0; - name[3] = (uap->op & 0xff0000) >> 16; - name[4] = uap->op & 0xff; - name[5] = uap->arg; - error = userland_sysctl(p, name, 6, uap->where, uap->size, - 0, 0, 0, &size); - break; - - case KINFO_VNODE: - name[0] = CTL_KERN; - name[1] = KERN_VNODE; - error = userland_sysctl(p, name, 2, uap->where, uap->size, - 0, 0, 0, &size); - break; - - case KINFO_PROC: - name[0] = CTL_KERN; - name[1] = KERN_PROC; - name[2] = uap->op & 0xff; - name[3] = uap->arg; - error = userland_sysctl(p, name, 4, uap->where, uap->size, - 0, 0, 0, &size); - break; - - case KINFO_FILE: - name[0] = CTL_KERN; - name[1] = KERN_FILE; - error = userland_sysctl(p, name, 2, uap->where, uap->size, - 0, 0, 0, &size); - break; - - case KINFO_METER: - name[0] = CTL_VM; - name[1] = VM_METER; - error = userland_sysctl(p, name, 2, uap->where, uap->size, - 0, 0, 0, &size); - break; - - case KINFO_LOADAVG: - name[0] = CTL_VM; - name[1] = VM_LOADAVG; - error = userland_sysctl(p, name, 2, uap->where, uap->size, - 0, 0, 0, &size); - break; - - case KINFO_CLOCKRATE: - name[0] = CTL_KERN; - name[1] = KERN_CLOCKRATE; - error = userland_sysctl(p, name, 2, uap->where, uap->size, - 0, 0, 0, &size); - break; - - case KINFO_BSDI_SYSINFO: { - /* - * this is pretty crude, but it's just enough for uname() - * from BSDI's 1.x libc to work. - * - * In particular, it doesn't return the same results when - * the supplied buffer is too small. BSDI's version apparently - * will return the amount copied, and set the *size to how - * much was needed. The emulation framework here isn't capable - * of that, so we just set both to the amount copied. - * BSDI's 2.x product apparently fails with ENOMEM in this - * scenario. - */ - - u_int needed; - u_int left; - char *s; - - bzero((char *)&bsdi_si, sizeof(bsdi_si)); - bzero(bsdi_strings, sizeof(bsdi_strings)); - s = bsdi_strings; - - bsdi_si.bsdi_ostype = (s - bsdi_strings) + sizeof(bsdi_si); - strcpy(s, ostype); - s += strlen(s) + 1; - - bsdi_si.bsdi_osrelease = (s - bsdi_strings) + sizeof(bsdi_si); - strcpy(s, osrelease); - s += strlen(s) + 1; - - bsdi_si.bsdi_machine = (s - bsdi_strings) + sizeof(bsdi_si); - strcpy(s, machine); - s += strlen(s) + 1; - - needed = sizeof(bsdi_si) + (s - bsdi_strings); - - if (uap->where == NULL) { - /* process is asking how much buffer to supply.. */ - size = needed; - error = 0; - break; - } + return(kernel_sysctl(current_proc(), name, namelen, oldp, oldlenp, newp, newlen)); +} +static int +sysctlnametomib(const char *name, int *mibp, size_t *sizep) +{ + int oid[2]; + int error; - /* if too much buffer supplied, trim it down */ - if (size > needed) - size = needed; + /* magic service node */ + oid[0] = 0; + oid[1] = 3; - /* how much of the buffer is remaining */ - left = size; + /* look up OID for name */ + *sizep *= sizeof(int); + error = sysctl(oid, 2, mibp, sizep, (void *)name, strlen(name)); + *sizep /= sizeof(int); + return(error); +} - if ((error = copyout((char *)&bsdi_si, uap->where, left)) != 0) - break; +int +sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen) +{ + int oid[CTL_MAXNAME + 2]; + int error; + size_t oidlen; - /* is there any point in continuing? */ - if (left > sizeof(bsdi_si)) { - left -= sizeof(bsdi_si); - error = copyout(&bsdi_strings, - uap->where + sizeof(bsdi_si), left); - } - break; - } + /* look up the OID */ + oidlen = CTL_MAXNAME; + error = sysctlnametomib(name, oid, &oidlen); - default: - return (EOPNOTSUPP); - } - if (error) - return (error); - p->p_retval[0] = size; - if (uap->size) - error = copyout((caddr_t)&size, (caddr_t)uap->size, - sizeof(size)); - return (error); + /* now use the OID */ + if (error == 0) + error = sysctl(oid, oidlen, oldp, oldlenp, newp, newlen); + return(error); } -#endif /* COMPAT_43 */ -#endif