/*	$NetBSD: param.h,v 1.128 2001/06/03 02:48:45 thorpej Exp $	*/

/*  
 * 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(kdatabase_list, kdatabase) kdatabases =
	LIST_HEAD_INITIALIZER(kdatabases);

struct kdatabase {
	LIST_ENTRY(kdatabase)	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 kdatabase *db);
static struct kdbobj *kdbobj_find(kdatabase_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 %ld bytes for %s %s\n",
		sizeof(struct kdbprop) + dsize + nsize, name, 
		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 kdatabase *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 kdatabase structures we use a simple power-of-2
 * hash.
 */

kdatabase_t 
createpropdb(const char *name)
{
	struct kdatabase *db;
	int i;

	db = (struct kdatabase *)malloc(sizeof(struct kdatabase),
		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(&kdatabases, db, kd_link);
	return (db);
}

void 
delpropdb(kdatabase_t db)
{
	struct kdbobj *obj;
	struct kdbprop *prop;
	int i;

#ifdef DIAGNOSTIC
	struct kdatabase *p;

	/* Make sure we have a handle to a valid database */
	LIST_FOREACH(p, &kdatabases, kd_link) {
		if (p == db) break;
	}
	if (p == NULL) panic("delpropdb: 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(kdatabase_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 
setprop(kdatabase_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, ("setprop: %p, %p, %s, %p, %lx, %x, %d\n", db, object, 
		name ? name : "NULL", val, len, type, wait));

	/* 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 
getprop(kdatabase_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, ("getprop: %p, %p, %s, %p, %lx, %p\n", db, object, 
		name ? name : "NULL", val, len, type));

	/* 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
listobjs(kdatabase_t db, opaque_t *objects, size_t len) 
{
	struct kdbobj *obj;
	int i, j, s, nelem = (len / sizeof(opaque_t));

	DPRINTF(x, ("listobjs: %p, %p, %lx\n", db, objects, len));

	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
listprops(kdatabase_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, ("listprops: %p, %p, %p, %lx\n", 
		db, object, names, len));

	/* 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 
delprop(kdatabase_t db, opaque_t object, const char *name)
{
	struct kdbobj *obj;
	struct kdbprop *prop = NULL;
	int s, i = 0;

	DPRINTF(x, ("delprop: %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 
copyprops(kdatabase_t db, opaque_t source, opaque_t dest, int wait)
{
	struct kdbobj *nobj, *oobj;
	struct kdbprop *prop, *oprop, *srcp;
	int s;

	DPRINTF(x, ("copyprops: %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, ("copyprops: 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, ("copyprops: 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, ("copyprops: 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, ("copyprops: inserting prop %p\n", prop));
			LIST_INSERT_HEAD(&nobj->ko_props, prop, kp_link);
			if (oprop) {
				DPRINTF(x, ("copyprops: removing prop %p\n", 
					oprop));
				LIST_REMOVE(oprop, kp_link);
				free(oprop, M_PROP);
			}
		}
#endif
	}
	DPRINTF(x, ("copyprops: 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_OBJ_ALL.dbid.dbid
 *
 * Return an array of int64_t identifiers for a specific database.
 *
 *	CTL_KERN.KERN_DB.KERN_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 kdatabase	*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_OBJ_ALL:
		if (namelen != 4)
			return (EINVAL);
		break;
	case KERN_PROP_ALL:
		if (namelen != 6)
			return (EINVAL);
		break;
	case KERN_GETPROP:
	case KERN_SETPROP:
	case KERN_DELPROP:
		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, &kdatabases, 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, &kdatabases, kd_link) {
		if (val == (u_int64_t)(u_long)kdb)
			break;
	}
	if (kdb == NULL) {
		error = ENOENT;
		goto cleanup;
	}

	if (op == KERN_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_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 = getprop(kdb, obj->ko_object,
				kipp->kip_name,	NULL, 0, &kipp->kip_type);
			if (op == KERN_GETPROP && 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 (getprop(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_SETPROP) {
			/* XXXX sleeping at splvm() */
			error = setprop(kdb, obj->ko_object, kipp->kip_name,
				(caddr_t)kipp + kip.kip_valoff,
				kip.kip_vallen, kip.kip_type, 1);
		} else if (op == KERN_DELPROP) {
			error = delprop(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);
}


