form. Subscriptions are regular expressions. . different types are stored per key; currently u32 and/or string. the same key can be referenced (publish, subscribe, check) as any type. . notify()s are sent when subscriptions are triggered (publishing or updating of matching keys); optionally, a subscribe flag sends updates for all matching keys at subscription time, instead of only after updates after subscribing . all interfacing to ds is in /usr/src/lib/syslib/ds.c. . subscribe is ds_subscribe publish functions are ds_publish_<type> retrieve functions are ds_retrieve_<type> (one-time retrieval of a value) check functions are ds_check_<type> (check for updated key caller subscribes to not yet checked for, or ESRCH for none) . ramdisk driver updated with new ds interface
419 lines
11 KiB
C
419 lines
11 KiB
C
/* Implementation of the Data Store. */
|
|
|
|
#include "inc.h"
|
|
|
|
/* Allocate space for the data store. */
|
|
PRIVATE struct data_store ds_store[NR_DS_KEYS];
|
|
PRIVATE struct subscription ds_subs[NR_DS_SUBS];
|
|
PRIVATE int nr_in_use;
|
|
|
|
PRIVATE _PROTOTYPE(int find_key, (char *key, struct data_store **dsp, int t));
|
|
PRIVATE _PROTOTYPE(int set_owner, (struct data_store *dsp, int auth));
|
|
PRIVATE _PROTOTYPE(int is_authorized, (struct data_store *dsp, int auth));
|
|
PRIVATE _PROTOTYPE(void check_subscribers, (struct data_store *dsp));
|
|
|
|
|
|
/*===========================================================================*
|
|
* ds_init *
|
|
*===========================================================================*/
|
|
PUBLIC void ds_init(void)
|
|
{
|
|
int i;
|
|
|
|
/* Reset data store: data and subscriptions. */
|
|
|
|
for(i = 0; i < NR_DS_KEYS; i++) {
|
|
int b;
|
|
ds_store[i].ds_flags = 0;
|
|
for(b = 0; b < BITMAP_CHUNKS(NR_DS_SUBS); b++) {
|
|
ds_store[i].ds_old_subs[b] = 0;
|
|
}
|
|
}
|
|
for(i = 0; i < NR_DS_SUBS; i++)
|
|
ds_subs[i].sub_flags = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
PRIVATE int set_owner(dsp, auth)
|
|
struct data_store *dsp; /* data store structure */
|
|
int auth;
|
|
{
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
PRIVATE int is_authorized(dsp, ap)
|
|
struct data_store *dsp; /* data store structure */
|
|
int ap; /* authorization value */
|
|
{
|
|
/* Authorize the caller. */
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
PRIVATE int find_key(key_name, dsp, type)
|
|
char *key_name; /* key to look up */
|
|
struct data_store **dsp; /* store pointer here */
|
|
int type; /* type info */
|
|
{
|
|
register int i;
|
|
|
|
*dsp = NULL;
|
|
for (i=0; i<NR_DS_KEYS; i++) {
|
|
if ((ds_store[i].ds_flags & DS_IN_USE) /* valid slot? */
|
|
&& ((ds_store[i].ds_flags & type) == type) /* right type? */
|
|
&& !strcmp(ds_store[i].ds_key, key_name)) { /* matching name? */
|
|
*dsp = &ds_store[i];
|
|
return(TRUE); /* report success */
|
|
}
|
|
}
|
|
return(FALSE); /* report not found */
|
|
}
|
|
|
|
|
|
PUBLIC int do_publish(m_ptr)
|
|
message *m_ptr; /* request message */
|
|
{
|
|
struct data_store *dsp;
|
|
char key_name[DS_MAX_KEYLEN];
|
|
int r, type;
|
|
|
|
/* Store (key,value)-pair. First see if key already exists. If so,
|
|
* check if the caller is allowed to overwrite the value. Otherwise
|
|
* find a new slot and store the new value.
|
|
*/
|
|
if (m_ptr->DS_KEY_LEN > DS_MAX_KEYLEN || m_ptr->DS_KEY_LEN < 2) {
|
|
printf("DS: bogus key length (%d) from %d\n", m_ptr->DS_KEY_LEN,
|
|
m_ptr->m_source);
|
|
return EINVAL;
|
|
}
|
|
|
|
/* Check type info. */
|
|
type = m_ptr->DS_FLAGS & DS_TYPE_MASK;
|
|
if(type != DS_TYPE_U32 && type != DS_TYPE_STR) {
|
|
printf("DS: bogus type code %lx from %d\n", type, m_ptr->m_source);
|
|
return EINVAL;
|
|
}
|
|
|
|
/* Copy name from caller. */
|
|
if ((r=sys_safecopyfrom(m_ptr->m_source,
|
|
(cp_grant_id_t) m_ptr->DS_KEY_GRANT, 0,
|
|
(vir_bytes) key_name, m_ptr->DS_KEY_LEN, D)) != OK) {
|
|
printf("DS: publish: copy failed from %d: %d\n", m_ptr->m_source, r);
|
|
return r;
|
|
}
|
|
|
|
/* Make sure name is 0-terminated. */
|
|
key_name[DS_MAX_KEYLEN-1] = '\0';
|
|
|
|
/* See if it already exists. */
|
|
if (!find_key(key_name, &dsp, type)) { /* look up key */
|
|
if (nr_in_use >= NR_DS_KEYS) {
|
|
return(EAGAIN); /* store is full */
|
|
} else {
|
|
dsp = &ds_store[nr_in_use]; /* new slot found */
|
|
strcpy(dsp->ds_key, key_name);
|
|
dsp->ds_flags = DS_IN_USE | m_ptr->DS_FLAGS; /* initialize slot */
|
|
nr_in_use ++;
|
|
}
|
|
}
|
|
|
|
/* At this point we have a data store pointer and know the caller is
|
|
* authorize to write to it. Set all fields as requested.
|
|
*/
|
|
switch(type) {
|
|
case DS_TYPE_U32:
|
|
dsp->ds_val.ds_val_u32 = (u32_t) m_ptr->DS_VAL; /* store data */
|
|
break;
|
|
case DS_TYPE_STR:
|
|
/* store string data: check size, then do copy */
|
|
if(m_ptr->DS_VAL_LEN < 1 || m_ptr->DS_VAL_LEN > DS_MAX_VALLEN) {
|
|
printf("DS: publish: bogus len from %d: %d\n",
|
|
m_ptr->m_source, m_ptr->DS_VAL_LEN);
|
|
return EINVAL;
|
|
}
|
|
|
|
if((r=sys_safecopyfrom(m_ptr->m_source, m_ptr->DS_VAL, 0,
|
|
(vir_bytes) dsp->ds_val.ds_val_str,
|
|
m_ptr->DS_VAL_LEN, D)) != OK) {
|
|
printf("DS: publish: str copy failed from %d: %d\n",
|
|
m_ptr->m_source, r);
|
|
return r;
|
|
}
|
|
break;
|
|
default:
|
|
panic(__FILE__, "Impossible type.", type);
|
|
break;
|
|
}
|
|
|
|
/* If anyone has a matching subscription, update them. */
|
|
check_subscribers(dsp);
|
|
|
|
return(OK);
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* do_retrieve *
|
|
*===========================================================================*/
|
|
PUBLIC int do_retrieve(m_ptr)
|
|
message *m_ptr; /* request message */
|
|
{
|
|
struct data_store *dsp;
|
|
char key_name[DS_MAX_KEYLEN];
|
|
int r, type;
|
|
size_t len;
|
|
|
|
if (m_ptr->DS_KEY_LEN > DS_MAX_KEYLEN || m_ptr->DS_KEY_LEN < 1) {
|
|
printf("DS: bogus key length (%d) from %d\n", m_ptr->DS_KEY_LEN,
|
|
m_ptr->m_source);
|
|
return EINVAL;
|
|
}
|
|
|
|
/* Copy name from caller. */
|
|
if ((r=sys_safecopyfrom(m_ptr->m_source,
|
|
(cp_grant_id_t) m_ptr->DS_KEY_GRANT, 0,
|
|
(vir_bytes) key_name, m_ptr->DS_KEY_LEN, D)) != OK) {
|
|
printf("DS: retrieve: copy failed from %d: %d\n", m_ptr->m_source, r);
|
|
return r;
|
|
}
|
|
|
|
/* Make sure name is 0-terminated. */
|
|
key_name[DS_MAX_KEYLEN-1] = '\0';
|
|
|
|
|
|
/* Retrieve data. Look up the key in the data store. Return an error if it
|
|
* is not found. If this data is private, only the owner may retrieve it.
|
|
*/
|
|
type = m_ptr->DS_FLAGS & DS_TYPE_MASK;
|
|
if (find_key(key_name, &dsp, type)) { /* look up key */
|
|
/* Data is public or the caller is authorized to retrieve it. */
|
|
switch(type) {
|
|
case DS_TYPE_U32:
|
|
m_ptr->DS_VAL = dsp->ds_val.ds_val_u32; /* return value */
|
|
break;
|
|
case DS_TYPE_STR:
|
|
len = strlen(dsp->ds_val.ds_val_str) + 1;
|
|
if(len > m_ptr->DS_VAL_LEN)
|
|
len = m_ptr->DS_VAL_LEN;
|
|
if ((r=sys_safecopyto(m_ptr->m_source, m_ptr->DS_VAL,
|
|
0, (vir_bytes) dsp->ds_val.ds_val_str,len, D)) != OK) {
|
|
printf("DS: retrieve: copy failed to %d: %d\n",
|
|
m_ptr->m_source, r);
|
|
return r;
|
|
}
|
|
break;
|
|
default:
|
|
panic(__FILE__, "retrieve: impossible type.", type);
|
|
/* not reached. */
|
|
break;
|
|
|
|
}
|
|
return(OK); /* report success */
|
|
}
|
|
return(ESRCH); /* key not found */
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* do_check *
|
|
*===========================================================================*/
|
|
PUBLIC int do_check(m_ptr)
|
|
message *m_ptr; /* request message */
|
|
{
|
|
/* This routine goes through all subscriptions for a client,
|
|
* and checks all data items if it has been flagged (i.e.,
|
|
* created or updated) matching that subscription. Return
|
|
* a message and copy the key and value for every one.
|
|
*/
|
|
struct data_store *dsp;
|
|
int r, s, d, type = m_ptr->DS_FLAGS & DS_TYPE_MASK;
|
|
if(!type) return EINVAL;
|
|
for(s = 0; s < NR_DS_SUBS; s++) {
|
|
int len;
|
|
if(!(ds_subs[s].sub_flags & DS_IN_USE))
|
|
continue;
|
|
if(m_ptr->m_source != ds_subs[s].sub_owner)
|
|
continue;
|
|
for(d = 0; d < NR_DS_KEYS; d++) {
|
|
|
|
/* No match if this is no value, it's
|
|
* not flagged, or the type is wrong.
|
|
*/
|
|
|
|
if(!(ds_store[d].ds_flags & DS_IN_USE))
|
|
continue;
|
|
if(!GET_BIT(ds_store[d].ds_old_subs, s))
|
|
continue;
|
|
if(type != (ds_store[d].ds_flags & DS_TYPE_MASK))
|
|
continue;
|
|
|
|
/* We have a match. Unflag it for this
|
|
* subscription.
|
|
*/
|
|
UNSET_BIT(ds_store[d].ds_old_subs, s);
|
|
len = strlen(ds_store[d].ds_key)+1;
|
|
if(len > m_ptr->DS_KEY_LEN)
|
|
len = m_ptr->DS_KEY_LEN;
|
|
|
|
/* Copy the key into client. */
|
|
if ((r=sys_safecopyto(m_ptr->m_source,
|
|
(cp_grant_id_t) m_ptr->DS_KEY_GRANT, 0,
|
|
(vir_bytes) ds_store[d].ds_key,
|
|
len, D)) != OK)
|
|
return r;
|
|
|
|
/* Now copy the value. */
|
|
switch(type) {
|
|
case DS_TYPE_STR:
|
|
len = strlen(ds_store[d].
|
|
ds_val.ds_val_str)+1;
|
|
if(len > m_ptr->DS_VAL_LEN)
|
|
len = m_ptr->DS_VAL_LEN;
|
|
if ((r=sys_safecopyto(m_ptr->m_source,
|
|
m_ptr->DS_VAL, 0,
|
|
(vir_bytes) ds_store[d].
|
|
ds_val.ds_val_str,
|
|
len, D)) != OK)
|
|
return r;
|
|
break;
|
|
case DS_TYPE_U32:
|
|
m_ptr->DS_VAL =
|
|
ds_store[d].ds_val.ds_val_u32;
|
|
break;
|
|
default:
|
|
panic(__FILE__,
|
|
"Check impossible type.",
|
|
type);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
}
|
|
|
|
return(ESRCH); /* key not found */
|
|
}
|
|
|
|
PUBLIC int do_subscribe(m_ptr)
|
|
message *m_ptr; /* request message */
|
|
{
|
|
char regex[DS_MAX_KEYLEN+3];
|
|
int s, type, e, d, n = 0;
|
|
char errbuf[80];
|
|
|
|
/* Subscribe to a key of interest.
|
|
* All updates to the key will cause a notification message
|
|
* to be sent to the subscribed. On success, directly return a copy of the
|
|
* data for the given key.
|
|
*/
|
|
if(m_ptr->DS_KEY_LEN < 2 || m_ptr->DS_KEY_LEN > DS_MAX_KEYLEN)
|
|
return EINVAL;
|
|
|
|
/* Copy name from caller. Anchor the subscription with "^regexp$" so
|
|
* substrings don't match. The caller probably will not expect this,
|
|
* and the usual case is for a complete match.
|
|
*/
|
|
regex[0] = '^';
|
|
if ((s=sys_safecopyfrom(m_ptr->m_source,
|
|
(cp_grant_id_t) m_ptr->DS_KEY_GRANT, 0,
|
|
(vir_bytes) regex + 1, m_ptr->DS_KEY_LEN, D)) != OK) {
|
|
printf("DS: retrieve: copy failed from %d: %d\n", m_ptr->m_source, s);
|
|
return s;
|
|
}
|
|
|
|
regex[DS_MAX_KEYLEN-1] = '\0';
|
|
strcat(regex, "$");
|
|
|
|
/* Find subscription slot. */
|
|
for(s = 0; s < NR_DS_SUBS; s++)
|
|
if(!(ds_subs[s].sub_flags & DS_IN_USE))
|
|
break;
|
|
|
|
if(s >= NR_DS_SUBS) {
|
|
printf("DS: no space for subscription by %d.\n", m_ptr->m_source);
|
|
return ENOSPC;
|
|
}
|
|
|
|
/* Compile regular expression. */
|
|
if((e=regcomp(&ds_subs[s].sub_regex, regex, REG_EXTENDED)) != 0) {
|
|
regerror(e, &ds_subs[s].sub_regex, errbuf, sizeof(errbuf));
|
|
printf("DS: subscribe: regerror: %s\n", errbuf);
|
|
return EINVAL;
|
|
}
|
|
type = (m_ptr->DS_FLAGS & DS_TYPE_MASK);
|
|
ds_subs[s].sub_flags = DS_IN_USE | type;
|
|
ds_subs[s].sub_owner = m_ptr->m_source;
|
|
|
|
/* Caller requested an instant initial list? */
|
|
if(m_ptr->DS_FLAGS & DS_INITIAL) {
|
|
for(d = 0; d < NR_DS_KEYS; d++) {
|
|
if(!(ds_store[d].ds_flags & DS_IN_USE))
|
|
continue;
|
|
if(regexec(&ds_subs[s].sub_regex, ds_store[d].ds_key,
|
|
0, NULL, 0) == 0) {
|
|
SET_BIT(ds_store[d].ds_old_subs, s);
|
|
n = 1;
|
|
}
|
|
}
|
|
|
|
/* Any matches? */
|
|
if(n) notify(ds_subs[s].sub_owner);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* do_getsysinfo *
|
|
*===========================================================================*/
|
|
PUBLIC int do_getsysinfo(m_ptr)
|
|
message *m_ptr;
|
|
{
|
|
vir_bytes src_addr, dst_addr;
|
|
int dst_proc;
|
|
size_t len;
|
|
int s;
|
|
|
|
switch(m_ptr->m1_i1) {
|
|
case SI_DATA_STORE:
|
|
src_addr = (vir_bytes) ds_store;
|
|
len = sizeof(struct data_store) * NR_DS_KEYS;
|
|
break;
|
|
default:
|
|
return(EINVAL);
|
|
}
|
|
|
|
dst_proc = m_ptr->m_source;
|
|
dst_addr = (vir_bytes) m_ptr->m1_p1;
|
|
if (OK != (s=sys_datacopy(SELF, src_addr, dst_proc, dst_addr, len))) {
|
|
printf("DS: copy failed: %d\n", s);
|
|
return(s);
|
|
}
|
|
return(OK);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* check_subscribers *
|
|
*===========================================================================*/
|
|
PRIVATE void
|
|
check_subscribers(struct data_store *dsp)
|
|
{
|
|
/* Send subscribers whose subscriptions match this (new
|
|
* or updated) data item a notify(), and flag the subscriptions
|
|
* as updated.
|
|
*/
|
|
int i;
|
|
for(i = 0; i < NR_DS_SUBS; i++) {
|
|
if(ds_subs[i].sub_flags & DS_IN_USE) {
|
|
if(regexec(&ds_subs[i].sub_regex, dsp->ds_key,
|
|
0, NULL, 0) == 0) {
|
|
SET_BIT(dsp->ds_old_subs, i);
|
|
notify(ds_subs[i].sub_owner);
|
|
} else {
|
|
UNSET_BIT(dsp->ds_old_subs, i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|