Add generic properties.

This commit is contained in:
eeh 2001-10-04 18:56:06 +00:00
parent f39983133f
commit e1bd5ac110
3 changed files with 1380 additions and 0 deletions

296
share/man/man9/properties.9 Normal file
View File

@ -0,0 +1,296 @@
.\" $NetBSD: properties.9,v 1.1 2001/10/04 18:56:43 eeh 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 for the
.\" NetBSD Project. See http://www.netbsd.org/ for
.\" information about NetBSD.
.\" 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.
.\"
.\"
.Dd October 3, 2001
.Dt PROPERTIES 9
.Os
.Sh NAME
.Nm propdb_create ,
.Nm propdb_destroy ,
.Nm prop_set ,
.Nm prop_get ,
.Nm prop_delete ,
.Nm prop_copy ,
.Nm prop_objs ,
.Nm prop_list
.Nd generic kernel properties
.Sh SYNOPSIS
.Fd #include <sys/properties.h>
.Pp
.Va typedef void *propdb_t
.Pp
.Va typedef int64_t opaque_t
.Ft propdb_t
.Fn propdb_create "const char *name"
.Ft void
.Fn propdb_destroy "propdb_t db"
.Ft int
.Fn prop_set "propdb_t db" "opaque_t object" "const char *name" "void *val" \
"size_t len" "int type" "int wait"
.Ft size_t
.Fn prop_get "propdb_t db" "opaque_t object" "const char *name" "void *val" \
"size_t len" "int *type"
.Ft int
.Fn prop_delete "propdb_t db" "opaque_t object" "const char *name"
.Ft int
.Fn prop_copy "propdb_t db" "opaque_t source" "opaque_t dest" "int wait"
.Ft size_t
.Fn prop_objs "propdb_t db" "opaque_t *objects" "size_t len"
.Ft size_t
.Fn prop_list "propdb_t db" "opaque_t object" "char *names" "size_t len"
.Sh DESCRIPTION
The
.Nx
property management routines allow kernel subsystems to associate
.Po name, value
.Pc pairs with arbitrary keys in a generalized manner.
.Pp
A database is a container for a set of properties. It is created with
.Fn propdb_create
and discarded with
.Fn propdb_destroy .
Kernel subsystems should create their own databases to prevent possible
namespace conflicts.
.Pp
A property is a tuple that consists of an opaque identifier
.Po often a pointer to a kernel data structure
.Pc , string, and an arbitrary amount of data. This
association is established by
.Fn prop_set ,
retrieved by
.Fn prop_get ,
and destroyed by
.Fn prop_delete .
.Pp
A system call interface makes use of the existing
.Ic sysctl
interface, and is provided
primarily for diagnostic purposes.
.Pp
.Sh TYPES
Several types are defined in
.Pa Aq sys/properties.h .
.Pp
.Bl -ohang -compact
.It Fa propdb_t
.Pp
The
.Fa probdb_t
type is used to contain a handle for a property database.
.Pp
.It Fa opaque_t
.Pp
The
.Fa opaque_t
type is a 64-bit scalar type used to store arbitrary object identifiers.
.Pp
The
.Nm
makes no type distinctions, but it does associate a type datum with each
property. Users of the interface can use that field to help determine what
information is stored in the value field of the property. There are three
base types:
.El
.Pp
.Bl -tag -width "PROP_ELSZ(type)" -compact -offset indent
.It PROP_INT
Property is an integer type.
.It PROP_STRING
Property is a string.
.It PROP_AGGREGATE
Property is an aggregation of different types.
.El
.Pp
Which can be further modified:
.Pp
.Bl -tag -width "PROP_ELSZ(type)" -compact -offset indent
.It PROP_ARRAY
Property is an array of values.
.It PROP_CONST
Property value has static storage and is maintained outside the database.
.It PROP_ELSZ
.Pq Fa size
Encode element size into the type field. This is primarily used to describe
the size of individual elements of an array.
.El
.Pp
.Sh FUNCTIONS
.Pp
.Bl -tag -width indent
.It Fn "propdb_t propdb_create" "const char *name"
.Pp
Allocate and initialize a kernel database object, and associate
.Fa name
with the database.
.Fa name
may later be used to access this database from userland throught the
userland database query interface. This operation may block.
Returns
.Li NULL
on failure.
.Pp
.It Fn "void propdb_destroy" "propdb_t db"
Destroy and deallocate a kernel database and all data within. This
routine deallocates all properties contained within the database.
.Pp
.It Fn "int prop_set" "propdb_t db" "opaque_t object" "const char *name" \
"void *val" "size_t len" "int type" "int wait"
Create a property
.Fa name
associated with
.Fa object
inside database
.Fa db ,
with a
.Fa len
byte value copied from location
.Fa val .
The database must already have been initialized with
.Fn propdb_create .
.Fa object
is treated as an opaque value. If
.Fa len
is
.Li 0
then no data is copied. This can be used to create properties which
convey information by their presence or absence. The
.Fa type
field is used to identify what the format of the object is. This value
is usually only used to help programs dump property values into human
readable formats. However, if
.Li PROP_CONST
is specified in the
.Fa type
field, no storage is allocated for the value, and when the property is
queried it will copy
.Fa len
bytes directly from the location specified by
.Fa val ,
so that data cannot be freed or the kernel may panic. If
.Fa wait
is zero then
.Fn prop_set
will not sleep for resource shortage. Returns
.Li 0
on success, or an error value.
.Pp
.It Fn "size_t prop_get" "propdb_t db" "opaque_t object" "const char *name" \
"void *val" "size_t len" "int *type"
Retrieve a property called
.Fa name
associated with
.Fa object .
.Fa name
is a pointer to a string. The property that matches both
.Fa object
and
.Fa name
will be selected, and the data and type information associated with that
property will be returned in the buffers pointed to by
.Fa val
and
.Fa type
as appropriate.
.Pp
Returns
.Li -1
if the property cannot be found, otherwise it returns the length of the
value data, and copies up to
.Fa len
bytes of the property data to the location pointed to by
.Fa val .
If
.Fa type
is not
.Li NULL ,
the type information associated with that property is stored in the location
it points to.
.Pp
.It Fn "int prop_delete" "propdb_t db" "opaque_t object" "const char *name"
Remove a property from a database. If a
.Li NULL
is supplied for
.Fa name ,
.Fn prop_delete
will remove all properties associated with
.Fa object .
It returns the number of properties deleted.
.Pp
.It Fn "int prop_copy" "propdb_t db" "opaque_t source" "opaque_t dest" \
"int wait"
Copy all properties associated with
.Fa source
to
.Fa dest
structure. If
.Fa wait
is zero then
.Fn prop_copy
will not sleep for resource shortage. Returns
.Li 0
on success or an error value. The state of properties is undefined if the
operation fails.
.It Fn "size_t prop_objs" "propdb_t db" "opaque_t *objects" "size_t len"
Get a list of objects identifiers from a database. An array of object
idientifiers will be copied into the buffer pointed to by
.Fa objects
up to
.Fa len
bytes. It returns the amount of memory needed to store the entire list.
.Pp
.It Fn "size_t prop_list" "propdb_t db" "opaque_t object" "char *names" \
"size_t len"
Get a list of an object's properties from the database. It queries the
database to locate all properties associated with
.Pa object
objedt identifier, and copies up to
.Pa len
bytes of
.Li NUL
terminated property names into the buffer pointed to by
.Pa names .
Partial strings are not copied, and another NUL character to indicate the
termination of the list. It returns the size needed to hold the
entire list.
.El
.Pp
.Sh AUTHORS
The
.Nx
property management system was developed by Eduardo Horvath <eeh@netbsd.org>
.Sh SEE ALSO
.Sh HISTORY
The
.Nx
property management system first appeared in
.Nx 1.6 .

966
sys/kern/subr_prop.c Normal file
View File

@ -0,0 +1,966 @@
/* $NetBSD: subr_prop.c,v 1.1 2001/10/04 18:56:06 eeh 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(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 %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 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, 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
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, 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
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, 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
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, 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
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);
}

118
sys/sys/properties.h Normal file
View File

@ -0,0 +1,118 @@
/* $NetBSD: properties.h,v 1.1 2001/10/04 18:56:06 eeh 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.
*/
#ifndef _SYS_PROPERTIES_H_
#define _SYS_PROPERTIES_H_
typedef void *opaque_t; /* Value large enough to hold a pointer */
struct propdb;
typedef struct propdb *propdb_t;
#define MAX_KDBNAME 32
#define PROP_INT 0x10000000
#define PROP_STRING 0x20000000
#define PROP_AGGREGATE 0x30000000
#define PROP_TYPE(x) ((x)&0x30000000)
#define PROP_ARRAY 0x40000000
#define PROP_CONST 0x80000000
#define PROP_ELSZ(x) 0x0fffffff
propdb_t propdb_create(const char *name);
void propdb_destroy(propdb_t db);
int prop_set(propdb_t db, opaque_t object, const char *name,
void *val, size_t len, int type, int wait);
size_t prop_objs(propdb_t db, opaque_t *objects, size_t len);
size_t prop_list(propdb_t db, opaque_t object, char *names,
size_t len);
size_t prop_get(propdb_t db, opaque_t object, const char *name,
void *val, size_t len, int *type);
int prop_delete(propdb_t db, opaque_t object, const char *name);
int prop_copy(propdb_t db, opaque_t source, opaque_t dest,
int wait);
int sysctl_propdb(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen);
/*
* KERN_DB subtypes
*/
#define KERN_DB_ALL 0 /* all databases */
#define KERN_DB_OBJ_ALL 1 /* all objects in a database */
#define KERN_DB_PROP_ALL 2 /* all properties in an object */
#define KERN_DB_PROP_GET 3 /* get a single property */
#define KERN_DB_PROP_SET 4 /* set a single property */
#define KERN_DB_PROP_DELETE 5 /* delete a single property */
#define CTL_DB_NAMES { \
{ 0, 0 }, \
{ "all", CTLTYPE_STRUCT }, \
{ "allobj", CTLTYPE_QUAD }, \
{ "allprops", CTLTYPE_INT }, \
{ "propget", CTLTYPE_STRUCT }, \
{ "propset", CTLTYPE_STRUCT }, \
{ "propdelete", CTLTYPE_STRUCT }, \
}
/* Info available for each database. */
struct kinfo_kdb {
char ki_name[MAX_KDBNAME];
uint64_t ki_id;
};
/* A single property */
struct kinfo_prop {
uint32_t kip_len; /* total len of this prop */
uint32_t kip_type; /* type of this prop */
uint32_t kip_valoff; /* offset of start of value */
uint32_t kip_vallen; /* length of value */
char kip_name[1]; /* name of this property */
};
/* Use these macros to encode database and object information in the MIB. */
#define KERN_DB_ENCODE_DB(p, m) do { \
(m)[2] = (int)(((uint64_t)(long)(p))>>32); \
(m)[3] = (int)(p); \
} while (0)
#define KERN_DB_ENCODE_OBJ(p, o, m) do { \
(m)[2] = (int)(((uint64_t)(long)(p))>>32); \
(m)[3] = (int)(p); \
(m)[4] = (int)(((uint64_t)(long)(o))>>32); \
(m)[5] = (int)(o); \
} while (0)
#endif