NetBSD/sys/kern/subr_prop.c

968 lines
22 KiB
C
Raw Normal View History

/* $NetBSD: subr_prop.c,v 1.2 2001/10/05 12:55:24 pooka Exp $ */
2001-10-04 22:56:06 +04:00
/*
* Copyright (c) 2001 Eduardo Horvath.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Eduardo Horvath.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/sysctl.h>
#include <sys/properties.h>
#ifdef DEBUG
int propdebug = 0;
#define DPRINTF(v, t) if (propdebug) printf t
#else
#define DPRINTF(v, t)
#endif
/*
* Kernel properties database implementation.
*
* While this could theoretically be flat, lookups
* are always done in the following order:
*
* database, object, name
*
* So we'll lay out the structures to make this efficient.
*
*/
#define KDB_SIZE 32 /* Initial hash table size */
#define KDB_MAXLEN 6 /* Max acceptable bucket length */
#define KDB_STEP 2 /* Increment size for hash table */
#define KDB_HASH(v, s) ((((v)>>16)^((v)>>8))&((s)-1))
typedef LIST_HEAD(kobj_head, kdbobj) kobj_bucket_t;
static LIST_HEAD(propdb_list, propdb) propdbs =
LIST_HEAD_INITIALIZER(propdbs);
struct propdb {
LIST_ENTRY(propdb) kd_link;
char kd_name[MAX_KDBNAME];
size_t kd_size;
/* Hash table of kdbobj structs */
kobj_bucket_t *kd_obj;
int kd_longest; /* Keep track of collisions */
};
struct kdbobj {
LIST_ENTRY(kdbobj) ko_link;
opaque_t ko_object;
/*
* There should only be a dozen props for each object,
* so we can keep them in a list.
*/
LIST_HEAD(kprops, kdbprop) ko_props;
};
struct kdbprop {
LIST_ENTRY(kdbprop) kp_link;
const char *kp_name;
const char *kp_val;
int kp_len;
int kp_type;
};
static struct kdbprop *allocprop(const char *name, size_t len, int wait);
static void kdb_rehash(struct propdb *db);
static struct kdbobj *kdbobj_find(propdb_t db, opaque_t object,
int create, int wait);
static int prop_insert(struct kdbobj *obj, const char *name, void *val,
size_t len, int type, int wait);
/*
* Allocate a prop structure large enough to hold
* `name' and `len' bytes of data. For PROP_CONST
* pass in a `len' of 0.
*/
static struct kdbprop *
allocprop(const char *name, size_t len, int wait)
{
struct kdbprop *kp;
char *np, *vp;
size_t dsize, nsize;
dsize = ALIGN(len);
nsize = ALIGN(strlen(name));
DPRINTF(x, ("allocprop: allocating %lu bytes for %s %s\n",
(unsigned long)(sizeof(struct kdbprop) + dsize + nsize), name,
2001-10-04 22:56:06 +04:00
wait ? "can wait" : "can't wait"));
kp = (struct kdbprop *)malloc(sizeof(struct kdbprop) + dsize + nsize,
M_PROP, wait ? M_WAITOK : M_NOWAIT);
DPRINTF(x, ("allocprop: got %p for prop\n", kp));
if (kp) {
/* Install name and init pointers */
vp = (char *)&kp[1];
kp->kp_val = (const char *)vp;
np = vp + dsize;
strcpy(np, name);
kp->kp_name = (const char *)np;
kp->kp_len = len;
}
return (kp);
}
/*
* If the database hash chains grow too long try to resize
* the hash table. Failure is not catastrophic.
*/
static void
kdb_rehash(struct propdb *db)
{
struct kdbobj *obj;
kobj_bucket_t *new, *old = db->kd_obj;
long hash;
size_t newsize = (db->kd_size << KDB_STEP);
int i, s;
new = (kobj_bucket_t *)malloc(sizeof(kobj_bucket_t) * newsize,
M_PROP, M_NOWAIT);
if (!new) return;
s = splvm();
for (i=0; i<newsize; i++)
LIST_INIT(&new[i]);
/* Now pop an object from the old table and insert it in the new one. */
for (i=0; i<db->kd_size; i++) {
while ((obj = LIST_FIRST(&old[i]))) {
LIST_REMOVE(obj, ko_link);
hash = (long)obj->ko_object;
hash = KDB_HASH(hash, db->kd_size);
LIST_INSERT_HEAD(&new[hash], obj, ko_link);
}
}
db->kd_size = newsize;
db->kd_obj = new;
splx(s);
free(old, M_PROP);
}
/*
* For propdb structures we use a simple power-of-2
* hash.
*/
propdb_t
propdb_create(const char *name)
{
struct propdb *db;
int i;
db = (struct propdb *)malloc(sizeof(struct propdb),
M_PROP, M_WAITOK);
strncpy(db->kd_name, name, 32);
/* Initialize the hash table. */
db->kd_size = KDB_SIZE;
db->kd_longest = 0;
db->kd_obj = (kobj_bucket_t *)malloc(sizeof(kobj_bucket_t) *
db->kd_size, M_PROP, M_WAITOK);
for (i = 0; i < db->kd_size; i++)
LIST_INIT(&db->kd_obj[i]);
LIST_INSERT_HEAD(&propdbs, db, kd_link);
return (db);
}
void
propdb_destroy(propdb_t db)
{
struct kdbobj *obj;
struct kdbprop *prop;
int i;
#ifdef DIAGNOSTIC
struct propdb *p;
/* Make sure we have a handle to a valid database */
LIST_FOREACH(p, &propdbs, kd_link) {
if (p == db) break;
}
if (p == NULL) panic("propdb_destroy: invalid database\n");
#endif
LIST_REMOVE(db, kd_link);
/* Empty out each hash bucket */
for (i = 0; i < db->kd_size; i++) {
while ((obj = LIST_FIRST(&db->kd_obj[i]))) {
LIST_REMOVE(obj, ko_link);
while ((prop = LIST_FIRST(&obj->ko_props))) {
LIST_REMOVE(prop, kp_link);
free(prop, M_PROP);
}
free(obj, M_PROP);
}
}
free(db->kd_obj, M_PROP);
free(db, M_PROP);
}
/*
* Find an object in the database and possibly create it too.
*/
static struct kdbobj *
kdbobj_find(propdb_t db, opaque_t object, int create, int wait)
{
struct kdbobj *obj;
long hash = (long)object;
int i;
/* Find our object */
hash = KDB_HASH(hash, db->kd_size);
i=0;
LIST_FOREACH(obj, &db->kd_obj[hash], ko_link) {
i++; /* Measure chain depth */
if (obj->ko_object == object)
break;
}
if (create && (obj == NULL)) {
/* Need a new object. */
obj = (struct kdbobj *)malloc(sizeof(struct kdbobj),
M_PROP, wait ? M_WAITOK : M_NOWAIT);
if (!obj) {
return (obj);
}
/* Handle hash table growth */
if (++i > db->kd_longest)
db->kd_longest = i;
if (db->kd_longest > KDB_MAXLEN) {
/* Increase the size of our hash table */
kdb_rehash(db);
}
/* Initialize object */
obj->ko_object = object;
LIST_INIT(&obj->ko_props);
LIST_INSERT_HEAD(&db->kd_obj[hash], obj, ko_link);
}
return (obj);
}
/*
* Internal property insertion routine.
*/
static int
prop_insert(struct kdbobj *obj, const char *name, void *val, size_t len,
int type, int wait)
{
struct kdbprop *prop = NULL, *oprop;
/* Does the prop exist already? */
LIST_FOREACH(oprop, &obj->ko_props, kp_link) {
if (strcmp(oprop->kp_name, name) == 0)
break;
}
if (oprop) {
/* Can is it big enough? */
if ((type & PROP_CONST) ||
((ALIGN(len) < ALIGN(oprop->kp_len)) &&
(oprop->kp_type & PROP_CONST) == 0)) {
/* We can reuse it */
prop = oprop;
}
}
if (!prop) {
/* Allocate a new prop */
if (type & PROP_CONST)
prop = allocprop(name, 0, wait);
else
prop = allocprop(name, len, wait);
if (!prop) return (wait ? ENOMEM : EAGAIN);
}
/* Set the values */
if (type & PROP_CONST) {
prop->kp_val = val;
} else {
char *dest = (char *)prop->kp_val;
memcpy(dest, val, len);
}
prop->kp_len = len;
prop->kp_type = type;
/* Now clean up if necessary */
if (prop != oprop) {
LIST_INSERT_HEAD(&obj->ko_props, prop, kp_link);
if (oprop) {
LIST_REMOVE(oprop, kp_link);
free(oprop, M_PROP);
}
}
return (0);
}
int
prop_set(propdb_t db, opaque_t object, const char *name,
void *val, size_t len, int type, int wait)
{
struct kdbobj *obj;
struct kdbprop *prop = NULL, *oprop;
int s;
DPRINTF(x, ("prop_set: %p, %p, %s, %p, %lx, %x, %d\n", db, object,
name ? name : "NULL", val, (unsigned long)len, type, wait));
2001-10-04 22:56:06 +04:00
/* Find our object */
s = splvm();
obj = kdbobj_find(db, object, 1, wait);
if (!obj) {
splx(s);
return (wait ? ENOMEM : EAGAIN);
}
#if 1
{
int rv;
oprop = prop; /* XXXX -- use vars to make gcc happy. */
rv = prop_insert(obj, name, val, len, type, wait);
splx(s);
return (rv);
}
#else
/* Does the prop exist already? */
LIST_FOREACH(oprop, &obj->ko_props, kp_link) {
if (strcmp(oprop->kp_name, name) == 0)
break;
}
if (oprop) {
/* Can is it big enough? */
if ((type & PROP_CONST) ||
((ALIGN(len) < ALIGN(oprop->kp_len)) &&
(oprop->kp_type & PROP_CONST) == 0)) {
/* We can reuse it */
prop = oprop;
}
}
if (!prop) {
/* Allocate a new prop */
if (type & PROP_CONST)
prop = allocprop(name, 0, wait);
else
prop = allocprop(name, len, wait);
if (!prop) return (wait ? ENOMEM : EAGAIN);
}
/* Set the values */
if (type & PROP_CONST) {
prop->kp_val = val;
} else {
char *dest = (char *)prop->kp_val;
memcpy(dest, val, len);
}
prop->kp_len = len;
prop->kp_type = type;
/* Now clean up if necessary */
if (prop != oprop) {
LIST_INSERT_HEAD(&obj->ko_props, prop, kp_link);
if (oprop) {
LIST_REMOVE(oprop, kp_link);
free(oprop, M_PROP);
}
}
splx(s);
return (0);
#endif
}
size_t
prop_get(propdb_t db, opaque_t object, const char *name, void *val,
size_t len, int *type)
{
struct kdbobj *obj;
struct kdbprop *prop = NULL;
int s;
DPRINTF(x, ("prop_get: %p, %p, %s, %p, %lx, %p\n", db, object,
name ? name : "NULL", val, (unsigned long)len, type));
2001-10-04 22:56:06 +04:00
/* Find our object */
s = splvm();
obj = kdbobj_find(db, object, 0, 0);
if (!obj) {
splx(s);
return (-1);
}
/* find our prop */
LIST_FOREACH(prop, &obj->ko_props, kp_link) {
if (strcmp(prop->kp_name, name) == 0)
break;
}
if (!prop) {
splx(s);
return (-1);
}
/* Copy out our prop */
len = min(len, prop->kp_len);
if (val && len) {
memcpy(val, prop->kp_val, len);
}
if (type)
*type = prop->kp_type;
splx(s);
return (prop->kp_len);
}
/*
* Return the total number of objects in the database and as
* many as fit in the buffer.
*/
size_t
prop_objs(propdb_t db, opaque_t *objects, size_t len)
{
struct kdbobj *obj;
int i, j, s, nelem = (len / sizeof(opaque_t));
DPRINTF(x, ("prop_objs: %p, %p, %lx\n", db, objects,
(unsigned long)len));
2001-10-04 22:56:06 +04:00
s = splvm();
for (i=0, j=0; i < db->kd_size; i++) {
LIST_FOREACH(obj, &db->kd_obj[i], ko_link) {
if (objects && j<nelem)
objects[j] = obj->ko_object;
j++;
}
}
splx(s);
return (j * sizeof(opaque_t));
}
/*
* Return the total number of property names associated with an object
* and as many as fit in the buffer.
*/
size_t
prop_list(propdb_t db, opaque_t object, char *names, size_t len)
{
struct kdbobj *obj;
struct kdbprop *prop = NULL;
size_t total_len = 0;
int s, i = 0;
DPRINTF(x, ("prop_list: %p, %p, %p, %lx\n",
db, object, names, (unsigned long)len));
2001-10-04 22:56:06 +04:00
/* Find our source object */
s = splvm();
obj = kdbobj_find(db, object, 0, 0);
if (obj == NULL) {
splx(s);
return (0);
}
LIST_FOREACH(prop, &obj->ko_props, kp_link) {
i = strlen(prop->kp_name) + 1;
total_len += i;
if (total_len < len) {
strcpy(names, prop->kp_name);
names += i;
/* Add an extra NUL */
names[i+1] = 0;
}
}
splx(s);
return (total_len);
}
int
prop_delete(propdb_t db, opaque_t object, const char *name)
{
struct kdbobj *obj;
struct kdbprop *prop = NULL;
int s, i = 0;
DPRINTF(x, ("prop_delete: %p, %p, %s\n", db, object,
name ? name : "NULL"));
/* Find our object */
s = splvm();
obj = kdbobj_find(db, object, 0, 0);
if (obj == NULL) {
splx(s);
return (0);
}
if (name) {
/* Find our prop */
LIST_FOREACH(prop, &obj->ko_props, kp_link) {
if (strcmp(prop->kp_name, name) == 0)
break;
}
if (!prop) {
splx(s);
return (0);
}
LIST_REMOVE(prop, kp_link);
free(prop, M_PROP);
i++;
} else {
while ((prop = LIST_FIRST(&obj->ko_props))) {
LIST_REMOVE(prop, kp_link);
free(prop, M_PROP);
i++;
}
}
if (LIST_EMPTY(&obj->ko_props)) {
/* Free up the empty container. */
LIST_REMOVE(obj, ko_link);
free(obj, M_PROP);
}
splx(s);
return (i);
}
int
prop_copy(propdb_t db, opaque_t source, opaque_t dest, int wait)
{
struct kdbobj *nobj, *oobj;
struct kdbprop *prop, *oprop, *srcp;
int s;
DPRINTF(x, ("prop_copy: %p, %p, %p, %d\n", db, source, dest, wait));
/* Find our source object */
s = splvm();
oobj = kdbobj_find(db, source, 0, wait);
if (oobj == NULL) {
splx(s);
return (EINVAL);
}
/* Find our dest object */
nobj = kdbobj_find(db, dest, 1, wait);
if (!nobj) {
splx(s);
return (wait ? ENOMEM : EAGAIN);
}
/* Copy these properties over now */
LIST_FOREACH(srcp, &oobj->ko_props, kp_link) {
DPRINTF(x, ("prop_copy: copying prop %s\n",
srcp->kp_name));
#if 1
{
int rv;
oprop = prop; /* XXXX -- use vars to make gcc happy. */
rv = prop_insert(nobj, srcp->kp_name,
(void *)srcp->kp_val, srcp->kp_len,
srcp->kp_type, wait);
if (rv) {
/* Error of some sort */
splx(s);
return (rv);
}
}
#else
/* Does the prop exist already? */
prop = NULL;
LIST_FOREACH(oprop, &nobj->ko_props, kp_link) {
if (strcmp(oprop->kp_name, srcp->kp_name) == 0)
break;
}
if (oprop) {
DPRINTF(x, ("prop_copy: found old prop %p\n",
oprop));
/* Can is it big enough? */
if ((srcp->kp_type & PROP_CONST) ||
((ALIGN(srcp->kp_len) < ALIGN(oprop->kp_len)) &&
(oprop->kp_type & PROP_CONST) == 0)) {
/* We can reuse it */
prop = oprop;
DPRINTF(x, ("prop_copy: using old prop\n"));
}
}
if (!prop) {
/* Allocate a new prop */
if (srcp->kp_type & PROP_CONST)
prop = allocprop(srcp->kp_name, 0, wait);
else
prop = allocprop(srcp->kp_name,
srcp->kp_len, wait);
if (!prop) {
splx(s);
return (wait ? ENOMEM : EAGAIN);
}
}
/* Set the values */
if (srcp->kp_type & PROP_CONST) {
prop->kp_val = srcp->kp_val;
} else {
char *dest = (char *)prop->kp_val;
memcpy(dest, srcp->kp_val, srcp->kp_len);
}
prop->kp_len = srcp->kp_len;
prop->kp_type = srcp->kp_type;
/* Now clean up if necessary */
if (prop != oprop) {
DPRINTF(x, ("prop_copy: inserting prop %p\n", prop));
LIST_INSERT_HEAD(&nobj->ko_props, prop, kp_link);
if (oprop) {
DPRINTF(x, ("prop_copy: removing prop %p\n",
oprop));
LIST_REMOVE(oprop, kp_link);
free(oprop, M_PROP);
}
}
#endif
}
DPRINTF(x, ("prop_copy: done\n"));
splx(s);
return (0);
}
/*
* sysctl() accessors.
*
* Operations:
*
* CTL_KERN.KERN_DB.KERN_DB_ALL
*
* Return an array of kinfo_kdb structures for all databases.
*
* CTL_KERN.KERN_DB.KERN_DB_OBJ_ALL.dbid.dbid
*
* Return an array of int64_t identifiers for a specific database.
*
* CTL_KERN.KERN_DB.KERN_DB_PROP_ALL.dbid.dbid.objid.objid
*
* Return ????
*
*
*/
int
sysctl_propdb(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
struct propdb *kdb;
struct kdbobj *obj;
struct kdbprop *prop;
struct kinfo_kdb kdbi;
struct kinfo_prop kip, *kipp;
u_int64_t val;
caddr_t dp = (caddr_t)oldp;
size_t dlen;
int i, s, op;
int error = 0, needed = 0;
#ifdef DIAGNOSTIC
/* How can this happen if we've gotten this far? */
if (namelen <= 1)
return (EINVAL);
#endif
op = name[1];
switch(op) {
case KERN_DB_ALL:
if (namelen != 2)
return (EINVAL);
break;
case KERN_DB_OBJ_ALL:
if (namelen != 4)
return (EINVAL);
break;
case KERN_DB_PROP_ALL:
if (namelen != 6)
return (EINVAL);
break;
case KERN_DB_PROP_GET:
case KERN_DB_PROP_SET:
case KERN_DB_PROP_DELETE:
if (namelen != 6)
return (EINVAL);
if (newlen < sizeof(kip) || newp == NULL)
return (EINVAL);
break;
default:
return (EINVAL);
}
/* Figure out how much space we were given */
if (oldp && oldlenp)
dlen = *oldlenp;
else
dlen = 0;
if (name[0] != KERN_DB) {
/* How did we get here? */
DPRINTF(x, ("sysctl_propdb: %d != KERN_DB (%d)\n",
name[0], KERN_DB));
return (EINVAL);
}
s = splvm();
if (op == KERN_DB_ALL) {
LIST_FOREACH(kdb, &propdbs, kd_link) {
/* Fill the kdbi */
strncpy(&kdbi.ki_name[0], kdb->kd_name, MAX_KDBNAME);
kdbi.ki_id = (u_int64_t)(u_long)kdb;
/* And copy it out if we can */
if (dlen >= sizeof(kdbi)) {
error = copyout((caddr_t)&kdbi, dp,
sizeof(kdbi));
dp += sizeof(kdbi);
dlen -= sizeof(kdbi);
}
if (error)
goto cleanup;
needed += sizeof(kdbi);
}
goto cleanup;
}
/* Get database ID. I wish sysctl used 64-bit ints. */
val = (((u_int64_t)name[2])<<32)|name[3];
/* Find the correct database */
LIST_FOREACH(kdb, &propdbs, kd_link) {
if (val == (u_int64_t)(u_long)kdb)
break;
}
if (kdb == NULL) {
error = ENOENT;
goto cleanup;
}
if (op == KERN_DB_OBJ_ALL) {
/* Now write out each object id XXX we should batch this... */
for (i=0; i < kdb->kd_size; i++) {
LIST_FOREACH(obj, &kdb->kd_obj[i], ko_link) {
size_t siz = sizeof(u_int64_t);
if (dlen >= siz) {
error = copyout((caddr_t)
&obj->ko_object, dp, siz);
dp += siz;
dlen -= siz;
}
if (error)
goto cleanup;
needed += siz;
}
}
goto cleanup;
}
/* Get object ID. */
val = (((u_int64_t)name[4]<<32))|name[5];
/* Find object */
obj = kdbobj_find(kdb, (opaque_t)(long)val, 0, 0);
if (obj == NULL) {
error = ENOENT;
goto cleanup;
}
if (op == KERN_DB_PROP_ALL) {
LIST_FOREACH(prop, &obj->ko_props, kp_link) {
size_t stlen;
int vlen;
kipp = (struct kinfo_prop *)dp;
vlen = prop->kp_len;
/* XXXX we don't know how long this will get... */
if (dlen >= sizeof(kip)) {
error = copyoutstr(prop->kp_name,
&kipp->kip_name[0],
dlen - sizeof(kip), &stlen);
if (error)
goto cleanup;
/* Fill out some of the kinfo_prop */
kip.kip_len = stlen;
kip.kip_vallen = vlen;
kip.kip_type = prop->kp_type;
/* Round to a pointer boundary */
stlen = ALIGN(stlen);
dp += sizeof(kip) + stlen;
dlen -= sizeof(kip) - stlen;
/* Finish filling out kinfo_prop */
kip.kip_valoff =
(uintptr_t)dp - (uintptr_t)kipp;
kip.kip_len = kip.kip_valoff + kip.kip_vallen;
error = copyout((caddr_t)&kip, dp,
sizeof(kip) - 1);
} else
stlen = ALIGN(strlen(prop->kp_name) + 1);
if (error)
goto cleanup;
/* Finally we can try to copy out the value */
if (dlen >= prop->kp_len) {
error = copyout(prop->kp_val, dp,
vlen);
dp += vlen;
dlen -= vlen;
}
if (error)
goto cleanup;
needed += stlen + dlen;
}
} else {
caddr_t inbuf, outbuf = NULL;
/* Change a property */
error = copyin(newp, (caddr_t)&kip, sizeof(kip) - 1);
if (error)
goto cleanup;
if (kip.kip_len > newlen) {
/* The property is bigger than the buffer? */
error = EINVAL;
goto cleanup;
}
/* Allocate storage for our args and copy them in */
inbuf = malloc(kip.kip_len, M_TEMP, M_WAITOK);
if (!inbuf) {
error = ENOMEM;
goto cleanup;
}
error = copyin(newp, inbuf, kip.kip_len);
if (error) {
free(inbuf, M_TEMP);
goto cleanup;
}
kipp = (struct kinfo_prop *)inbuf;
/* Make *sure* we're NULL terminated. */
if (kip.kip_valoff > sizeof(kip) ||
kip.kip_valoff > kip.kip_len)
inbuf[kip.kip_len] = 0;
else
inbuf[kip.kip_valoff - 1] = 0;
/*
* We need to copy out the old prop before we zap it.
*/
if (dp && dlen) {
/* Find out how big the existing prop is (if any) */
kipp->kip_vallen = prop_get(kdb, obj->ko_object,
kipp->kip_name, NULL, 0, &kipp->kip_type);
if (op == KERN_DB_PROP_GET && kipp->kip_len < 0) {
free(inbuf, M_TEMP);
error = ENOENT;
goto cleanup;
}
/* Calculate where we'll store the data. */
kipp->kip_valoff = ALIGN(strlen(kipp->kip_name) + 1);
needed = kipp->kip_vallen + kipp->kip_valoff;
kipp->kip_len = needed;
if (needed > dlen) {
free(inbuf, M_TEMP);
goto cleanup;
}
if (kipp->kip_vallen > 0) {
outbuf = malloc(kipp->kip_vallen,
M_TEMP, M_WAITOK);
if (!outbuf) {
free(inbuf, M_TEMP);
error = ENOMEM;
goto cleanup;
}
if (prop_get(kdb, obj->ko_object,
kipp->kip_name, outbuf,
kipp->kip_vallen, NULL) !=
kipp->kip_vallen) {
/* It existed a second ago */
panic("sysctl_propdb: "
"inconsistent property %s",
kipp->kip_name);
}
/* copyout the data */
error = copyout(outbuf, dp + kipp->kip_valoff,
kipp->kip_vallen);
free(outbuf, M_TEMP);
if (error) {
free(inbuf, M_TEMP);
goto cleanup;
}
}
/* Now copyout the header and string */
error = copyout((caddr_t)kipp, dp,
kipp->kip_valoff);
if (error) {
free(inbuf, M_TEMP);
goto cleanup;
}
}
/*
* We can finally replace/delete the property.
*/
if (op == KERN_DB_PROP_SET) {
/* XXXX sleeping at splvm() */
error = prop_set(kdb, obj->ko_object, kipp->kip_name,
(caddr_t)kipp + kip.kip_valoff,
kip.kip_vallen, kip.kip_type, 1);
} else if (op == KERN_DB_PROP_DELETE) {
error = prop_delete(kdb, obj->ko_object,
kipp->kip_name);
}
free(inbuf, M_TEMP);
}
cleanup:
splx(s);
dlen = dp - (caddr_t)oldp;
if (needed > dlen)
dlen = needed;
if (oldp && oldlenp)
*oldlenp = dlen;
return (error);
}