servers/ds/store.c
#include "inc.h"
#include "store.h"

/* Allocate space for the data store. */
static struct data_store ds_store[NR_DS_KEYS];
static struct subscription ds_subs[NR_DS_SUBS];

/*===========================================================================*
 *			      alloc_data_slot				     *
 *===========================================================================*/
static struct data_store *alloc_data_slot(void)
{
/* Allocate a new data slot. */
  int i;

  for (i = 0; i < NR_DS_KEYS; i++) {
	if (!(ds_store[i].flags & DSF_IN_USE))
		return &ds_store[i];
  }

  return NULL;
}

/*===========================================================================*
 *				alloc_sub_slot				     *
 *===========================================================================*/
static struct subscription *alloc_sub_slot(void)
{
/* Allocate a new subscription slot. */
  int i;

  for (i = 0; i < NR_DS_SUBS; i++) {
	if (!(ds_subs[i].flags & DSF_IN_USE))
		return &ds_subs[i];
  }

  return NULL;
}

/*===========================================================================*
 *				lookup_entry				     *
 *===========================================================================*/
static struct data_store *lookup_entry(const char *key_name, int type)
{
/* Lookup an existing entry by key and type. */
  int i;

  for (i = 0; i < NR_DS_KEYS; i++) {
	if ((ds_store[i].flags & DSF_IN_USE) /* used */
		&& (ds_store[i].flags & type) /* same type*/
		&& !strcmp(ds_store[i].key, key_name)) /* same key*/
		return &ds_store[i];
  }

  return NULL;
}

/*===========================================================================*
 *			     lookup_label_entry				     *
 *===========================================================================*/
static struct data_store *lookup_label_entry(unsigned num)
{
/* Lookup an existing label entry by num. */
  int i;

  for (i = 0; i < NR_DS_KEYS; i++) {
	if ((ds_store[i].flags & DSF_IN_USE)
		&& (ds_store[i].flags & DSF_TYPE_LABEL)
		&& (ds_store[i].u.u32 == num))
		return &ds_store[i];
  }

  return NULL;
}

/*===========================================================================*
 *			      lookup_sub				     *
 *===========================================================================*/
static struct subscription *lookup_sub(const char *owner)
{
/* Lookup an existing subscription given its owner. */
  int i;

  for (i = 0; i < NR_DS_SUBS; i++) {
	if ((ds_subs[i].flags & DSF_IN_USE) /* used */
		&& !strcmp(ds_subs[i].owner, owner)) /* same key*/
		return &ds_subs[i];
  }

  return NULL;
}

/*===========================================================================*
 *				ds_getprocname				     *
 *===========================================================================*/
static char *ds_getprocname(endpoint_t e)
{
/* Get a process name given its endpoint. */
	struct data_store *dsp;

	static char *first_proc_name = "ds";
	endpoint_t first_proc_ep = DS_PROC_NR;

	if(e == first_proc_ep)
		return first_proc_name;

	if((dsp = lookup_label_entry(e)) != NULL)
		return dsp->key;

	return NULL;
}

/*===========================================================================*
 *				ds_getprocep				     *
 *===========================================================================*/
static endpoint_t ds_getprocep(const char *s)
{
/* Get a process endpoint given its name. */
	struct data_store *dsp;

	if((dsp = lookup_entry(s, DSF_TYPE_LABEL)) != NULL)
		return dsp->u.u32;
	panic("ds_getprocep: process endpoint not found");
}

/*===========================================================================*
 *				 check_auth				     *
 *===========================================================================*/
static int check_auth(const struct data_store *p, endpoint_t ep, int perm)
{
/* Check authorization for a given type of permission. */
	char *source;

	if(!(p->flags & perm))
		return 1;

	source = ds_getprocname(ep);
	return source && !strcmp(p->owner, source);
}

/*===========================================================================*
 *				get_key_name				     *
 *===========================================================================*/
static int get_key_name(const message *m_ptr, char *key_name)
{
/* Get key name given an input message. */
  int r;

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

  /* Copy name from caller. */
  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);
  if(r != OK) {
	printf("DS: publish: copy failed from %d: %d\n", m_ptr->m_source, r);
	return r;
  }

  key_name[DS_MAX_KEYLEN-1] = '\0';

  return OK;
}

/*===========================================================================*
 *				check_sub_match				     *
 *===========================================================================*/
static int check_sub_match(const struct subscription *subp,
		struct data_store *dsp, endpoint_t ep)
{
/* Check if an entry matches a subscription. Return 1 in case of match. */
  return (check_auth(dsp, ep, DSF_PRIV_SUBSCRIBE)
	  && regexec(&subp->regex, dsp->key, 0, NULL, 0) == 0)
	  ? 1 : 0;
}

/*===========================================================================*
 *			     update_subscribers				     *
 *===========================================================================*/
static void update_subscribers(struct data_store *dsp, int set)
{
/* If set = 1, set bit in the sub bitmap of any subscription matching the given
 * entry, otherwise clear it. In both cases, notify the subscriber.
 */
	int i;
	int nr = dsp - ds_store;
	endpoint_t ep;

	for(i = 0; i < NR_DS_SUBS; i++) {
		if(!(ds_subs[i].flags & DSF_IN_USE))
			continue;
		if(!(ds_subs[i].flags & dsp->flags & DSF_MASK_TYPE))
			continue;

		ep = ds_getprocep(ds_subs[i].owner);
		if(!check_sub_match(&ds_subs[i], dsp, ep))
			continue;

		if(set == 1) {
			SET_BIT(ds_subs[i].old_subs, nr);
		} else {
			UNSET_BIT(ds_subs[i].old_subs, nr);
		}
		notify(ep);
	}
}

/*===========================================================================*
 *		               map_service                                   *
 *===========================================================================*/
static int map_service(const struct rprocpub *rpub)
{
/* Map a new service by registering its label. */
  struct data_store *dsp;

  /* Allocate a new data slot. */
  if((dsp = alloc_data_slot()) == NULL) {
	return ENOMEM;
  }

  /* Set attributes. */
  strcpy(dsp->key, rpub->label);
  dsp->u.u32 = (u32_t) rpub->endpoint;
  strcpy(dsp->owner, "rs");
  dsp->flags = DSF_IN_USE | DSF_TYPE_LABEL;

  /* Update subscribers having a matching subscription. */
  update_subscribers(dsp, 1);

  return(OK);
}

/*===========================================================================*
 *		            sef_cb_init_fresh                                *
 *===========================================================================*/
int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *info)
{
/* Initialize the data store server. */
	int i, r;
	struct rprocpub rprocpub[NR_BOOT_PROCS];

	/* Reset data store: data and subscriptions. */
	for(i = 0; i < NR_DS_KEYS; i++) {
		ds_store[i].flags = 0;
	}
	for(i = 0; i < NR_DS_SUBS; i++) {
		ds_subs[i].flags = 0;
	}

	/* Map all the services in the boot image. */
	if((r = sys_safecopyfrom(RS_PROC_NR, info->rproctab_gid, 0,
		(vir_bytes) rprocpub, sizeof(rprocpub))) != OK) {
		panic("sys_safecopyfrom failed: %d", r);
	}
	for(i=0;i < NR_BOOT_PROCS;i++) {
		if(rprocpub[i].in_use) {
			if((r = map_service(&rprocpub[i])) != OK) {
				panic("unable to map service: %d", r);
			}
		}
	}

	return(OK);
}

/*===========================================================================*
 *				do_publish				     *
 *===========================================================================*/
int do_publish(message *m_ptr)
{
  struct data_store *dsp;
  char key_name[DS_MAX_KEYLEN];
  char *source;
  int flags = m_ptr->DS_FLAGS;
  size_t length;
  int r;

  /* Lookup the source. */
  source = ds_getprocname(m_ptr->m_source);
  if(source == NULL)
	  return EPERM;

  /* Only RS can publish labels. */
  if((flags & DSF_TYPE_LABEL) && m_ptr->m_source != RS_PROC_NR)
	  return EPERM;

  /* Get key name. */
  if((r = get_key_name(m_ptr, key_name)) != OK)
	return r;

  /* Lookup the entry. */
  dsp = lookup_entry(key_name, flags & DSF_MASK_TYPE);
  /* If type is LABEL, also try to lookup the entry by num. */
  if((flags & DSF_TYPE_LABEL) && (dsp == NULL))
	dsp = lookup_label_entry(m_ptr->DS_VAL);

  if(dsp == NULL) {
	/* The entry doesn't exist, allocate a new data slot. */
	if((dsp = alloc_data_slot()) == NULL)
		return ENOMEM;
  } else if (flags & DSF_OVERWRITE) {
	/* Overwrite. */
	if(!check_auth(dsp, m_ptr->m_source, DSF_PRIV_OVERWRITE))
		return EPERM;
  } else {
	/* Don't overwrite and return error. */
	return EEXIST;
  }

  /* Store! */
  switch(flags & DSF_MASK_TYPE) {
  case DSF_TYPE_U32:
  case DSF_TYPE_LABEL:
	dsp->u.u32 = m_ptr->DS_VAL;
	break;
  case DSF_TYPE_STR:
  case DSF_TYPE_MEM:
	length = m_ptr->DS_VAL_LEN;
	/* Allocate a new data buffer if necessary. */
	if(!(dsp->flags & DSF_IN_USE)) {
		if((dsp->u.mem.data = malloc(length)) == NULL)
			return ENOMEM;
		dsp->u.mem.reallen = length;
	} else if(length > dsp->u.mem.reallen) {
		free(dsp->u.mem.data);
		if((dsp->u.mem.data = malloc(length)) == NULL)
			return ENOMEM;
		dsp->u.mem.reallen = length;
	}

	/* Copy the memory range. */
	r = sys_safecopyfrom(m_ptr->m_source, (cp_grant_id_t) m_ptr->DS_VAL,
	        0, (vir_bytes) dsp->u.mem.data, length);
	if(r != OK) {
		printf("DS: publish: memory map/copy failed from %d: %d\n",
			m_ptr->m_source, r);
		free(dsp->u.mem.data);
		return r;
	}
	dsp->u.mem.length = length;
	if(flags & DSF_TYPE_STR) {
		((char*)dsp->u.mem.data)[length-1] = '\0';
	}
	break;
  default:
	return EINVAL;
  }

  /* Set attributes. */
  strcpy(dsp->key, key_name);
  strcpy(dsp->owner, source);
  dsp->flags = DSF_IN_USE | (flags & DSF_MASK_INTERNAL);

  /* Update subscribers having a matching subscription. */
  update_subscribers(dsp, 1);

  return(OK);
}

/*===========================================================================*
 *				do_retrieve				     *
 *===========================================================================*/
int do_retrieve(message *m_ptr)
{
  struct data_store *dsp;
  char key_name[DS_MAX_KEYLEN];
  int flags = m_ptr->DS_FLAGS;
  int type = flags & DSF_MASK_TYPE;
  size_t length;
  int r;

  /* Get key name. */
  if((r = get_key_name(m_ptr, key_name)) != OK)
	return r;

  /* Lookup the entry. */
  if((dsp = lookup_entry(key_name, type)) == NULL)
	return ESRCH;
  if(!check_auth(dsp, m_ptr->m_source, DSF_PRIV_RETRIEVE))
	return EPERM;

  /* Copy the requested data. */
  switch(type) {
  case DSF_TYPE_U32:
  case DSF_TYPE_LABEL:
	m_ptr->DS_VAL = dsp->u.u32;
	break;
  case DSF_TYPE_STR:
  case DSF_TYPE_MEM:
	length = MIN(m_ptr->DS_VAL_LEN, dsp->u.mem.length);
	r = sys_safecopyto(m_ptr->m_source, (cp_grant_id_t) m_ptr->DS_VAL, 0,
		(vir_bytes) dsp->u.mem.data, length);
	if(r != OK) {
		printf("DS: retrieve: copy failed to %d: %d\n",	
			m_ptr->m_source, r);
		return r;
	}
	m_ptr->DS_VAL_LEN = length;
	break;
  default:
	return EINVAL;
  }

  return OK;
}

/*===========================================================================*
 *				do_retrieve_label			     *
 *===========================================================================*/
int do_retrieve_label(const message *m_ptr)
{
  struct data_store *dsp;
  int r;

  /* Lookup the label entry. */
  if((dsp = lookup_label_entry(m_ptr->DS_VAL)) == NULL)
	return ESRCH;

  /* Copy the key name. */
  r = sys_safecopyto(m_ptr->m_source,
	(cp_grant_id_t) m_ptr->DS_KEY_GRANT, (vir_bytes) 0,
	(vir_bytes) dsp->key, strlen(dsp->key) + 1);
  if(r != OK) {
	printf("DS: copy failed from %d: %d\n", m_ptr->m_source, r);
	return r;
  }

  return OK;
}

/*===========================================================================*
 *				do_subscribe				     *
 *===========================================================================*/
int do_subscribe(message *m_ptr)
{
  char regex[DS_MAX_KEYLEN+2];
  struct subscription *subp;
  char errbuf[80];
  char *owner;
  int type_set;
  int r, e, b;

  /* Find the owner. */
  owner = ds_getprocname(m_ptr->m_source);
  if(owner == NULL)
	  return ESRCH;

  /* See if the owner already has an existing subscription. */
  if((subp = lookup_sub(owner)) == NULL) {
	/* The subscription doesn't exist, allocate a new one. */
	if((subp = alloc_sub_slot()) == NULL)
		return EAGAIN;
  } else if(!(m_ptr->DS_FLAGS & DSF_OVERWRITE)) {
	/* The subscription exists but we can't overwrite, return error. */
	return EEXIST;
  }

  /* Copy key name from the caller. Anchor the subscription with "^regexp$" so
   * substrings don't match. The caller will probably not expect this,
   * and the usual case is for a complete match.
   */
  regex[0] = '^';
  if((r = get_key_name(m_ptr, regex+1)) != OK)
	return r;
  strcat(regex, "$");

  /* Compile regular expression. */
  if((e=regcomp(&subp->regex, regex, REG_EXTENDED)) != 0) {
	regerror(e, &subp->regex, errbuf, sizeof(errbuf));
	printf("DS: subscribe: regerror: %s\n", errbuf);
	return EINVAL;
  }

  /* If type_set = 0, then subscribe all types. */
  type_set = m_ptr->DS_FLAGS & DSF_MASK_TYPE;
  if(type_set == 0)
	  type_set = DSF_MASK_TYPE;

  subp->flags = DSF_IN_USE | type_set;
  strcpy(subp->owner, owner);
  for(b = 0; b < BITMAP_CHUNKS(NR_DS_KEYS); b++)
	subp->old_subs[b] = 0;

  /* See if caller requested an instant initial list. */
  if(m_ptr->DS_FLAGS & DSF_INITIAL) {
	int i, match_found = FALSE;
	for(i = 0; i < NR_DS_KEYS; i++) {
		if(!(ds_store[i].flags & DSF_IN_USE))
			continue;
		if(!(ds_store[i].flags & type_set))
			continue;
		if(!check_sub_match(subp, &ds_store[i], m_ptr->m_source))
			continue;

		SET_BIT(subp->old_subs, i);
		match_found = TRUE;
	}

	/* Notify in case of match. */
	if(match_found)
		notify(m_ptr->m_source);
  }

  return OK;
}

/*===========================================================================*
 *				do_check				     *
 *===========================================================================*/
int do_check(message *m_ptr)
{
  struct subscription *subp;
  char *owner;
  endpoint_t entry_owner_e;
  int r, i;

  /* Find the subscription owner. */
  owner = ds_getprocname(m_ptr->m_source);
  if(owner == NULL)
	  return ESRCH;

  /* Lookup the owner's subscription. */
  if((subp = lookup_sub(owner)) == NULL)
	return ESRCH;

  /* Look for an updated entry the subscriber is interested in. */
  for(i = 0; i < NR_DS_KEYS; i++) {
	if(GET_BIT(subp->old_subs, i))
		break;
  }
  if(i == NR_DS_KEYS)
	return ENOENT;

  /* Copy the key name. */
  r = sys_safecopyto(m_ptr->m_source,
	(cp_grant_id_t) m_ptr->DS_KEY_GRANT, (vir_bytes) 0, 
	(vir_bytes) ds_store[i].key, strlen(ds_store[i].key) + 1);
  if(r != OK) {
	printf("DS: check: copy failed from %d: %d\n", m_ptr->m_source, r);
	return r;
  }

  /* Copy the type and the owner of the original entry. */
  entry_owner_e = ds_getprocep(ds_store[i].owner);
  m_ptr->DS_FLAGS = ds_store[i].flags & DSF_MASK_TYPE;
  m_ptr->DS_OWNER = entry_owner_e;

  /* Mark the entry as no longer updated for the subscriber. */
  UNSET_BIT(subp->old_subs, i);

  return OK;
}

/*===========================================================================*
 *				do_delete				     *
 *===========================================================================*/
int do_delete(message *m_ptr)
{
  struct data_store *dsp;
  char key_name[DS_MAX_KEYLEN];
  char *source;
  char *label;
  int type = m_ptr->DS_FLAGS & DSF_MASK_TYPE;
  int i, r;

  /* Lookup the source. */
  source = ds_getprocname(m_ptr->m_source);
  if(source == NULL)
	  return EPERM;

  /* Get key name. */
  if((r = get_key_name(m_ptr, key_name)) != OK)
	return r;

  /* Lookup the entry. */
  if((dsp = lookup_entry(key_name, type)) == NULL)
	return ESRCH;

  /* Only the owner can delete. */
  if(strcmp(dsp->owner, source))
	return EPERM;

  switch(type) {
  case DSF_TYPE_U32:
	break;
  case DSF_TYPE_LABEL:
	label = dsp->key;

	/* Clean up subscriptions. */
	for (i = 0; i < NR_DS_SUBS; i++) {
		if ((ds_subs[i].flags & DSF_IN_USE)
			&& !strcmp(ds_subs[i].owner, label)) {
			ds_subs[i].flags = 0;
		}
	}

	/* Clean up data entries. */
	for (i = 0; i < NR_DS_KEYS; i++) {
		if ((ds_store[i].flags & DSF_IN_USE)
			&& !strcmp(ds_store[i].owner, label)) {
			update_subscribers(&ds_store[i], 0);

			ds_store[i].flags = 0;
		}
	}
	break;
  case DSF_TYPE_STR:
  case DSF_TYPE_MEM:
	free(dsp->u.mem.data);
	break;
  default:
	return EINVAL;
  }

  /* Update subscribers having a matching subscription. */
  update_subscribers(dsp, 0);

  /* Clear the entry. */
  dsp->flags = 0;

  return OK;
}

/*===========================================================================*
 *				do_getsysinfo				     *
 *===========================================================================*/
int do_getsysinfo(const message *m_ptr)
{
  vir_bytes src_addr;
  size_t length;
  int s;

  switch(m_ptr->SI_WHAT) {
  case SI_DATA_STORE:
	src_addr = (vir_bytes)ds_store;
	length = sizeof(struct data_store) * NR_DS_KEYS;
	break;
  default:
  	return EINVAL;
  }

  if (length != m_ptr->SI_SIZE)
	return EINVAL;

  if (OK != (s=sys_datacopy(SELF, src_addr,
		m_ptr->m_source, (vir_bytes)m_ptr->SI_WHERE, length))) {
	printf("DS: copy failed: %d\n", s);
	return s;
  }

  return OK;
}

