minix/servers/ds/store.c
Ben Gras 3512a86b44 . DS understands publishing and subscribing where keys are in string
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
2006-07-13 14:50:23 +00:00

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);
}
}
}
}