aboutsummaryrefslogtreecommitdiffstats
path: root/tools/xenstore/xenstored_watch.c
diff options
context:
space:
mode:
authorcl349@firebug.cl.cam.ac.uk <cl349@firebug.cl.cam.ac.uk>2005-07-12 10:16:33 +0000
committercl349@firebug.cl.cam.ac.uk <cl349@firebug.cl.cam.ac.uk>2005-07-12 10:16:33 +0000
commit0f36f0e0957a552d7d577c849182b783543998f2 (patch)
tree04d58fc9f98685a0a4ee402c154ec4da505bedee /tools/xenstore/xenstored_watch.c
parent247bc37e8389c6b32e80c8bba67b70f2e851950b (diff)
downloadxen-0f36f0e0957a552d7d577c849182b783543998f2.tar.gz
xen-0f36f0e0957a552d7d577c849182b783543998f2.tar.bz2
xen-0f36f0e0957a552d7d577c849182b783543998f2.zip
xenstored updates.
- add trace file support. - update permissions code. - trigger watches on children of nodes being removed. - update test cases. Signed-off-by: Rusty Russel <rusty@rustcorp.com.au> Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
Diffstat (limited to 'tools/xenstore/xenstored_watch.c')
-rw-r--r--tools/xenstore/xenstored_watch.c143
1 files changed, 103 insertions, 40 deletions
diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c
index d0e00f53c2..6945dd91e0 100644
--- a/tools/xenstore/xenstored_watch.c
+++ b/tools/xenstore/xenstored_watch.c
@@ -23,12 +23,14 @@
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
+#include <assert.h>
#include "talloc.h"
#include "list.h"
#include "xenstored_watch.h"
#include "xs_lib.h"
#include "utils.h"
#include "xenstored_test.h"
+#include "xenstored_domain.h"
/* FIXME: time out unacked watches. */
@@ -40,13 +42,17 @@ struct watch_event
/* The watch we are firing for (watch->events) */
struct list_head list;
- /* Watch we are currently attached to. */
- struct watch *watch;
+ /* Watches we need to fire for (watches[0]->events == this). */
+ struct watch **watches;
+ unsigned int num_watches;
struct timeval timeout;
/* Name of node which changed. */
char *node;
+
+ /* For remove, we trigger on all the children of this node too. */
+ bool recurse;
};
struct watch
@@ -57,6 +63,9 @@ struct watch
/* Current outstanding events applying to this watch. */
struct list_head events;
+ /* Is this relative to connnection's implicit path? */
+ bool relative;
+
char *token;
char *node;
struct connection *conn;
@@ -84,6 +93,7 @@ static struct watch_event *get_first_event(struct connection *conn)
void queue_next_event(struct connection *conn)
{
struct watch_event *event;
+ const char *node;
char *buffer;
unsigned int len;
@@ -107,53 +117,63 @@ void queue_next_event(struct connection *conn)
/* If we decide to cancel, we will reset this. */
conn->waiting_for_ack = true;
+ /* If we deleted /foo and they're watching /foo/bar, that's what we
+ * tell them has changed. */
+ if (!is_child(event->node, event->watches[0]->node)) {
+ assert(event->recurse);
+ node = event->watches[0]->node;
+ } else
+ node = event->node;
+
+ /* If watch placed using relative path, give them relative answer. */
+ if (event->watches[0]->relative) {
+ node += strlen(get_implicit_path(conn));
+ if (node[0] == '/') /* Could be "". */
+ node++;
+ }
+
/* Create reply from path and token */
- len = strlen(event->node) + 1 + strlen(event->watch->token) + 1;
+ len = strlen(node) + 1 + strlen(event->watches[0]->token) + 1;
buffer = talloc_array(conn, char, len);
- strcpy(buffer, event->node);
- strcpy(buffer+strlen(event->node)+1, event->watch->token);
+ strcpy(buffer, node);
+ strcpy(buffer+strlen(node)+1, event->watches[0]->token);
send_reply(conn, XS_WATCH_EVENT, buffer, len);
talloc_free(buffer);
}
-/* Watch on DIR applies to DIR, DIR/FILE, but not DIRLONG. */
-static bool watch_applies(const struct watch *watch, const char *node)
+static struct watch **find_watches(const char *node, bool recurse,
+ unsigned int *num)
{
- return is_child(node, watch->node);
-}
+ struct watch *i;
+ struct watch **ret = NULL;
-static struct watch *find_watch(const char *node)
-{
- struct watch *watch;
+ *num = 0;
- list_for_each_entry(watch, &watches, list) {
- if (watch_applies(watch, node))
- return watch;
+ /* We include children too if this is an rm. */
+ list_for_each_entry(i, &watches, list) {
+ if (is_child(node, i->node) ||
+ (recurse && is_child(i->node, node))) {
+ (*num)++;
+ ret = talloc_realloc(node, ret, struct watch *, *num);
+ ret[*num - 1] = i;
+ }
}
- return NULL;
-}
-
-static struct watch *find_next_watch(struct watch *watch, const char *node)
-{
- list_for_each_entry_continue(watch, &watches, list) {
- if (watch_applies(watch, node))
- return watch;
- }
- return NULL;
+ return ret;
}
/* FIXME: we fail to fire on out of memory. Should drop connections. */
-void fire_watches(struct transaction *trans, const char *node)
+void fire_watches(struct transaction *trans, const char *node, bool recurse)
{
- struct watch *watch;
+ struct watch **watches;
struct watch_event *event;
+ unsigned int num_watches;
/* During transactions, don't fire watches. */
if (trans)
return;
- watch = find_watch(node);
- if (!watch)
+ watches = find_watches(node, recurse, &num_watches);
+ if (!watches)
return;
/* Create and fill in info about event. */
@@ -161,16 +181,19 @@ void fire_watches(struct transaction *trans, const char *node)
event->node = talloc_strdup(event, node);
/* Tie event to this watch. */
- event->watch = watch;
- list_add_tail(&event->list, &watch->events);
+ event->watches = watches;
+ talloc_steal(event, watches);
+ event->num_watches = num_watches;
+ event->recurse = recurse;
+ list_add_tail(&event->list, &watches[0]->events);
/* Warn if not finished after thirty seconds. */
gettimeofday(&event->timeout, NULL);
event->timeout.tv_sec += 30;
/* If connection not doing anything, queue this. */
- if (!watch->conn->out)
- queue_next_event(watch->conn);
+ if (!watches[0]->conn->out)
+ queue_next_event(watches[0]->conn);
}
/* We're done with this event: see if anyone else wants it. */
@@ -178,18 +201,41 @@ static void move_event_onwards(struct watch_event *event)
{
list_del(&event->list);
- /* Remove from this watch, and find next watch to put this on. */
- event->watch = find_next_watch(event->watch, event->node);
- if (!event->watch) {
+ event->num_watches--;
+ event->watches++;
+ if (!event->num_watches) {
talloc_free(event);
return;
}
- list_add_tail(&event->list, &event->watch->events);
+ list_add_tail(&event->list, &event->watches[0]->events);
/* If connection not doing anything, queue this. */
- if (!event->watch->conn->out)
- queue_next_event(event->watch->conn);
+ if (!event->watches[0]->conn->out)
+ queue_next_event(event->watches[0]->conn);
+}
+
+static void remove_watch_from_events(struct watch *dying_watch)
+{
+ struct watch *watch;
+ struct watch_event *event;
+ unsigned int i;
+
+ list_for_each_entry(watch, &watches, list) {
+ list_for_each_entry(event, &watch->events, list) {
+ for (i = 0; i < event->num_watches; i++) {
+ if (event->watches[i] != dying_watch)
+ continue;
+
+ assert(i != 0);
+ memmove(event->watches+i,
+ event->watches+i+1,
+ (event->num_watches - (i+1))
+ * sizeof(struct watch *));
+ event->num_watches--;
+ }
+ }
+ }
}
static int destroy_watch(void *_watch)
@@ -203,6 +249,11 @@ static int destroy_watch(void *_watch)
/* Remove from global list. */
list_del(&watch->list);
+
+ /* Other events which match this watch must be cleared. */
+ remove_watch_from_events(watch);
+
+ trace_destroy(watch, "watch");
return 0;
}
@@ -251,6 +302,8 @@ void check_watch_ack_timeout(void)
xprintf("Warning: timeout on watch event %s"
" token %s\n",
i->node, watch->token);
+ trace_watch_timeout(watch->conn, i->node,
+ watch->token);
timerclear(&i->timeout);
}
}
@@ -261,10 +314,12 @@ bool do_watch(struct connection *conn, struct buffered_data *in)
{
struct watch *watch;
char *vec[3];
+ bool relative;
if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec))
return send_error(conn, EINVAL);
+ relative = !strstarts(vec[0], "/");
vec[0] = canonicalize(conn, vec[0]);
if (!check_node_perms(conn, vec[0], XS_PERM_READ))
return send_error(conn, errno);
@@ -274,10 +329,12 @@ bool do_watch(struct connection *conn, struct buffered_data *in)
watch->token = talloc_strdup(watch, vec[1]);
watch->conn = conn;
watch->priority = strtoul(vec[2], NULL, 0);
+ watch->relative = relative;
INIT_LIST_HEAD(&watch->events);
insert_watch(watch);
talloc_set_destructor(watch, destroy_watch);
+ trace_create(watch, "watch");
return send_ack(conn, XS_WATCH);
}
@@ -285,11 +342,14 @@ bool do_watch_ack(struct connection *conn, const char *token)
{
struct watch_event *event;
+ if (!token)
+ return send_error(conn, EINVAL);
+
if (!conn->waiting_for_ack)
return send_error(conn, ENOENT);
event = get_first_event(conn);
- if (!streq(event->watch->token, token))
+ if (!streq(event->watches[0]->token, token))
return send_error(conn, EINVAL);
move_event_onwards(event);
@@ -305,6 +365,9 @@ bool do_unwatch(struct connection *conn, struct buffered_data *in)
if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec))
return send_error(conn, EINVAL);
+ /* We don't need to worry if we're waiting for an ack for the
+ * watch we're deleting: conn->waiting_for_ack was reset by
+ * this command in consider_message anyway. */
node = canonicalize(conn, vec[0]);
list_for_each_entry(watch, &watches, list) {
if (watch->conn != conn)