[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 6/11] Xenstore watch rework



# HG changeset patch
# User Rusty Russell <rusty@xxxxxxxxxxxxxxx>
# Node ID b0de1894df67ac7c7d905bf61cdf0210b42752cc
# Parent  ba5d5bd28edf8bce89bdf9fc64047ee4f1dceded
Xenstore watch rework
Change watches to all fire simultaneously, removing priority argument.
Watches no longer fired back to connection/domain which caused event.
Fix up testsuite to match
Use state enum, rather than return value inside daemon to determine blockage

Signed-off-by: Rusty Russell <rusty@xxxxxxxxxxxxxxx>

diff -r ba5d5bd28edf -r b0de1894df67 
linux-2.6.11-xen-sparse/drivers/xen/xenbus/xenbus_probe.c
--- a/linux-2.6.11-xen-sparse/drivers/xen/xenbus/xenbus_probe.c Thu Aug  4 
10:43:03 2005
+++ b/linux-2.6.11-xen-sparse/drivers/xen/xenbus/xenbus_probe.c Thu Aug  4 
11:39:03 2005
@@ -800,7 +800,6 @@
 {
         static int init_done = 0;
        static struct xenbus_watch watch = { .node = "/", 
-                                            .priority = 0, 
                                             .callback = test_callback };
 
         if(init_done) return;
diff -r ba5d5bd28edf -r b0de1894df67 
linux-2.6.11-xen-sparse/drivers/xen/xenbus/xenbus_xs.c
--- a/linux-2.6.11-xen-sparse/drivers/xen/xenbus/xenbus_xs.c    Thu Aug  4 
10:43:03 2005
+++ b/linux-2.6.11-xen-sparse/drivers/xen/xenbus/xenbus_xs.c    Thu Aug  4 
11:39:03 2005
@@ -321,18 +321,14 @@
        return xs_single(XS_GETDOMAINPATH, domid_str, NULL);
 }
 
-static int xs_watch(const char *path, const char *token, unsigned int priority)
-{
-       char prio[32];
-       struct kvec iov[3];
-
-       sprintf(prio, "%u", priority);
+static int xs_watch(const char *path, const char *token)
+{
+       struct kvec iov[2];
+
        iov[0].iov_base = (void *)path;
        iov[0].iov_len = strlen(path) + 1;
        iov[1].iov_base = (void *)token;
        iov[1].iov_len = strlen(token) + 1;
-       iov[2].iov_base = prio;
-       iov[2].iov_len = strlen(prio) + 1;
 
        return xs_error(xs_talkv(XS_WATCH, iov, ARRAY_SIZE(iov), NULL));
 }
@@ -393,7 +389,7 @@
        BUG_ON(find_watch(token));
 
        down(&xs_lock);
-       err = xs_watch(watch->node, token, watch->priority);
+       err = xs_watch(watch->node, token);
        up(&xs_lock);
        if (!err)
                list_add(&watch->list, &watches);
diff -r ba5d5bd28edf -r b0de1894df67 
linux-2.6.11-xen-sparse/include/asm-xen/xenbus.h
--- a/linux-2.6.11-xen-sparse/include/asm-xen/xenbus.h  Thu Aug  4 10:43:03 2005
+++ b/linux-2.6.11-xen-sparse/include/asm-xen/xenbus.h  Thu Aug  4 11:39:03 2005
@@ -117,7 +117,6 @@
 {
        struct list_head list;
        char *node;
-       unsigned int priority;
        void (*callback)(struct xenbus_watch *, const char *node);
 };
 
diff -r ba5d5bd28edf -r b0de1894df67 tools/python/xen/lowlevel/xs/xs.c
--- a/tools/python/xen/lowlevel/xs/xs.c Thu Aug  4 10:43:03 2005
+++ b/tools/python/xen/lowlevel/xs/xs.c Thu Aug  4 11:39:03 2005
@@ -343,7 +343,6 @@
 #define xspy_watch_doc "\n"                                            \
        "Watch a path, get notifications when it changes.\n"            \
        " path     [string] : xenstore path.\n"                         \
-       " priority [int]    : watch priority (default 0).\n"            \
        " token    [string] : returned in watch notification.\n"        \
        "\n"                                                            \
        "Returns: [int] 0 on success.\n"                                \
@@ -352,10 +351,9 @@
 
 static PyObject *xspy_watch(PyObject *self, PyObject *args, PyObject *kwds)
 {
-    static char *kwd_spec[] = { "path", "priority", "token", NULL };
+    static char *kwd_spec[] = { "path", "token", NULL };
     static char *arg_spec = "s|is";
     char *path = NULL;
-    int priority = 0;
     char *token = "";
 
     struct xs_handle *xh = xshandle(self);
@@ -365,9 +363,9 @@
     if (!xh)
        goto exit;
     if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec, 
-                                     &path, &priority, &token))
-        goto exit;
-    xsval = xs_watch(xh, path, token, priority);
+                                     &path, &token))
+        goto exit;
+    xsval = xs_watch(xh, path, token);
     val = pyvalue_int(xsval);
  exit:
     return val;
diff -r ba5d5bd28edf -r b0de1894df67 tools/xenstore/Makefile
--- a/tools/xenstore/Makefile   Thu Aug  4 10:43:03 2005
+++ b/tools/xenstore/Makefile   Thu Aug  4 11:39:03 2005
@@ -42,9 +42,8 @@
 xs_test: xs_test.o xs_lib.o utils.o
 xs_random: xs_random.o xs_test_lib.o xs_lib.o talloc.o utils.o
 xs_stress: xs_stress.o xs_test_lib.o xs_lib.o talloc.o utils.o
-xs_watch_stress: xs_watch_stress.o xs_test_lib.o xs_lib.o talloc.o utils.o
 
-xs_test.o xs_stress.o xs_watch_stress.o xenstored_core_test.o 
xenstored_watch_test.o xenstored_transaction_test.o xenstored_domain_test.o 
xs_random.o xs_test_lib.o talloc_test.o fake_libxc.o: CFLAGS=$(BASECFLAGS) 
$(TESTFLAGS)
+xs_test.o xs_stress.o xenstored_core_test.o xenstored_watch_test.o 
xenstored_transaction_test.o xenstored_domain_test.o xs_random.o xs_test_lib.o 
talloc_test.o fake_libxc.o: CFLAGS=$(BASECFLAGS) $(TESTFLAGS)
 
 xenstored_%_test.o: xenstored_%.c
        $(COMPILE.c) -o $@ $<
@@ -66,7 +65,7 @@
 
 clean: testsuite-clean
        rm -f *.o *.opic *.a
-       rm -f xen xenstored xs_random xs_stress xs_watch_stress
+       rm -f xen xenstored xs_random xs_stress
        rm -f xs_test xenstored_test xs_dom0_test
        -$(RM) $(PROG_DEP)
 
@@ -86,11 +85,9 @@
        $(TESTENV) ./xs_random --fast /tmp/xs_random 100000 $(RANDSEED)
        $(TESTENV) ./xs_random --fail /tmp/xs_random 10000 $(RANDSEED)
 
-stresstest: xs_stress xs_watch_stress xenstored_test
+stresstest: xs_stress xenstored_test
        rm -rf $(TESTDIR)/store $(TESTDIR)/transactions
        export $(TESTENV); PID=`./xenstored_test --output-pid 
--trace-file=/tmp/trace`; ./xs_stress 5000; ret=$$?; kill $$PID; exit $$ret
-       rm -rf $(TESTDIR)/store $(TESTDIR)/transactions
-       export $(TESTENV); PID=`./xenstored_test --output-pid`; 
./xs_watch_stress; ret=$$?; kill $$PID; exit $$ret
 
 xs_dom0_test: xs_dom0_test.o utils.o
        $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -lxc -o $@
diff -r ba5d5bd28edf -r b0de1894df67 tools/xenstore/testsuite/07watch.sh
--- a/tools/xenstore/testsuite/07watch.sh       Thu Aug  4 10:43:03 2005
+++ b/tools/xenstore/testsuite/07watch.sh       Thu Aug  4 11:39:03 2005
@@ -3,20 +3,20 @@
 # Watch something, write to it, check watch has fired.
 [ "`echo -e 'write /test create contents' | ./xs_test 2>&1`" = "" ]
 
-[ "`echo -e '1 watch /test token 100
+[ "`echo -e '1 watch /test token
 2 write /test create contents2
 1 waitwatch
 1 ackwatch token' | ./xs_test 2>&1`" = "1:/test:token" ]
 
 # Check that reads don't set it off.
-[ "`echo -e '1 watch /test token 100
+[ "`echo -e '1 watch /test token
 2 read /test
 1 waitwatch' | ./xs_test 2>&1`" = "2:contents2
 1:waitwatch timeout" ]
 
 # mkdir, setperm and rm should (also tests watching dirs)
 [ "`echo -e 'mkdir /dir' | ./xs_test 2>&1`" = "" ]
-[ "`echo -e '1 watch /dir token 100
+[ "`echo -e '1 watch /dir token
 2 mkdir /dir/newdir
 1 waitwatch
 1 ackwatch token
@@ -29,18 +29,23 @@
 1:/dir/newdir:token
 1:/dir/newdir:token" ]
 
+# We don't get a watch from our own commands.
+[ "`echo -e 'watch /dir token
+mkdir /dir/newdir
+waitwatch' | ./xs_test 2>&1`" = "waitwatch timeout" ]
+
 # ignore watches while doing commands, should work.
-[ "`echo -e 'watch /dir token 100
-write /dir/test create contents
+[ "`echo -e 'watch /dir token
+1 write /dir/test create contents
 read /dir/test
 waitwatch
 ackwatch token' | ./xs_test 2>&1`" = "contents
 /dir/test:token" ]
 
-# watch priority /test.
-[ "`echo -e '1 watch /dir token1 1
-3 watch /dir token3 3
-2 watch /dir token2 2
+# watch priority test: all simultaneous
+[ "`echo -e '1 watch /dir token1
+3 watch /dir token3
+2 watch /dir token2
 write /dir/test create contents
 3 waitwatch
 3 ackwatch token3
@@ -52,8 +57,8 @@
 1:/dir/test:token1" ]
 
 # If one dies (without acking), the other should still get ack.
-[ "`echo -e '1 watch /dir token1 0
-2 watch /dir token2 1
+[ "`echo -e '1 watch /dir token1
+2 watch /dir token2
 write /dir/test create contents
 2 waitwatch
 2 close
@@ -62,40 +67,40 @@
 1:/dir/test:token1" ]
 
 # If one dies (without reading at all), the other should still get ack.
-[ "`echo -e '1 watch /dir token1 0
-2 watch /dir token2 1
+[ "`echo -e '1 watch /dir token1
+2 watch /dir token2
 write /dir/test create contents
 2 close
 1 waitwatch
 1 ackwatch token1' | ./xs_test 2>&1`" = "1:/dir/test:token1" ]
 
 # unwatch
-[ "`echo -e '1 watch /dir token1 0
+[ "`echo -e '1 watch /dir token1
 1 unwatch /dir token1
-1 watch /dir token2 0
+1 watch /dir token2
 2 write /dir/test2 create contents
 1 waitwatch
 1 unwatch /dir token2' | ./xs_test 2>&1`" = "1:/dir/test2:token2" ]
 
 # unwatch while watch pending.  Next watcher gets the event.
-[ "`echo -e '1 watch /dir token1 0
-2 watch /dir token2 1
+[ "`echo -e '1 watch /dir token1
+2 watch /dir token2
 write /dir/test create contents
 2 unwatch /dir token2
 1 waitwatch
 1 ackwatch token1' | ./xs_test 2>&1`" = "1:/dir/test:token1" ]
 
 # unwatch while watch pending.  Should clear this so we get next event.
-[ "`echo -e '1 watch /dir token1 0
+[ "`echo -e '1 watch /dir token1
 write /dir/test create contents
 1 unwatch /dir token1
-1 watch /dir/test token2 0
+1 watch /dir/test token2
 write /dir/test none contents2
 1 waitwatch
 1 ackwatch token2' | ./xs_test 2>&1`" = "1:/dir/test:token2" ]
 
 # check we only get notified once.
-[ "`echo -e '1 watch /test token 100
+[ "`echo -e '1 watch /test token
 2 write /test create contents2
 1 waitwatch
 1 ackwatch token
@@ -103,7 +108,7 @@
 1:waitwatch timeout" ]
 
 # watches are queued in order.
-[ "`echo -e '1 watch / token 100
+[ "`echo -e '1 watch / token
 2 write /test1 create contents
 2 write /test2 create contents
 2 write /test3 create contents
@@ -117,7 +122,7 @@
 1:/test3:token" ]
 
 # Creation of subpaths should be covered correctly.
-[ "`echo -e '1 watch / token 100
+[ "`echo -e '1 watch / token
 2 write /test/subnode create contents2
 2 write /test/subnode/subnode create contents2
 1 waitwatch
@@ -129,22 +134,22 @@
 1:waitwatch timeout" ]
 
 # Watch event must have happened before we registered interest.
-[ "`echo -e '1 watch / token 100
+[ "`echo -e '1 watch / token
 2 write /test/subnode create contents2
-2 watch / token2 0
+1 watch / token2 0
 1 waitwatch
 1 ackwatch token
-2 waitwatch' | ./xs_test 2>&1`" = "1:/test/subnode:token
-2:waitwatch timeout" ]
+1 waitwatch' | ./xs_test 2>&1`" = "1:/test/subnode:token
+1:waitwatch timeout" ]
 
 # Rm fires notification on child.
-[ "`echo -e '1 watch /test/subnode token 100
+[ "`echo -e '1 watch /test/subnode token
 2 rm /test
 1 waitwatch
 1 ackwatch token' | ./xs_test 2>&1`" = "1:/test/subnode:token" ]
 
 # Watch should not double-send after we ack, even if we did something in 
between.
-[ "`echo -e '1 watch /test2 token 100
+[ "`echo -e '1 watch /test2 token
 2 write /test2/foo create contents2
 1 waitwatch
 1 read /test2/foo
diff -r ba5d5bd28edf -r b0de1894df67 tools/xenstore/testsuite/08transaction.sh
--- a/tools/xenstore/testsuite/08transaction.sh Thu Aug  4 10:43:03 2005
+++ b/tools/xenstore/testsuite/08transaction.sh Thu Aug  4 11:39:03 2005
@@ -45,27 +45,27 @@
 sleep 1
 rm /test/entry1
 commit
-dir /test' | ./xs_test`" = "" ]
+dir /test' | ./xs_test --no-timeout`" = "" ]
 
 # ... as long as noone is waiting.
 [ "`echo -e '1 start /test
 2 mkdir /test/dir
 1 mkdir /test/dir
 1 dir /test
-1 commit' | ./xs_test 2>&1`" = "1:dir
+1 commit' | ./xs_test --no-timeout 2>&1`" = "1:dir
 FATAL: 1: commit: Connection timed out" ]
 
 # Events inside transactions don't trigger watches until (successful) commit.
-[ "`echo -e '1 watch /test token 100
+[ "`echo -e '1 watch /test token
 2 start /test
 2 mkdir /test/dir/sub
 1 waitwatch' | ./xs_test 2>&1`" = "1:waitwatch timeout" ]
-[ "`echo -e '1 watch /test token 100
+[ "`echo -e '1 watch /test token
 2 start /test
 2 mkdir /test/dir/sub
 2 abort
 1 waitwatch' | ./xs_test 2>&1`" = "1:waitwatch timeout" ]
-[ "`echo -e '1 watch /test token 100
+[ "`echo -e '1 watch /test token
 2 start /test
 2 mkdir /test/dir/sub
 2 commit
@@ -73,7 +73,7 @@
 1 ackwatch token' | ./xs_test 2>&1`" = "1:/test/dir/sub:token" ]
 
 # Rm inside transaction works like rm outside: children get notified.
-[ "`echo -e '1 watch /test/dir/sub token 100
+[ "`echo -e '1 watch /test/dir/sub token
 2 start /test
 2 rm /test/dir
 2 commit
diff -r ba5d5bd28edf -r b0de1894df67 
tools/xenstore/testsuite/10domain-homedir.sh
--- a/tools/xenstore/testsuite/10domain-homedir.sh      Thu Aug  4 10:43:03 2005
+++ b/tools/xenstore/testsuite/10domain-homedir.sh      Thu Aug  4 11:39:03 2005
@@ -13,7 +13,7 @@
 # Place a watch using a relative path: expect relative answer.
 [ "`echo 'introduce 1 100 7 /home
 1 mkdir foo
-1 watch foo token 0
+1 watch foo token
 write /home/foo/bar create contents
 1 waitwatch
 1 ackwatch token' | ./xs_test 2>&1`" = "handle is 1
diff -r ba5d5bd28edf -r b0de1894df67 tools/xenstore/testsuite/11domain-watch.sh
--- a/tools/xenstore/testsuite/11domain-watch.sh        Thu Aug  4 10:43:03 2005
+++ b/tools/xenstore/testsuite/11domain-watch.sh        Thu Aug  4 11:39:03 2005
@@ -6,7 +6,7 @@
 [ "`echo -e 'mkdir /dir' | ./xs_test 2>&1`" = "" ]
 
 [ "`echo -e 'introduce 1 100 7 /my/home
-1 watch /test token 100
+1 watch /test token
 write /test create contents2
 1 waitwatch
 1 ackwatch token
@@ -16,8 +16,8 @@
 
 # ignore watches while doing commands, should work.
 [ "`echo -e 'introduce 1 100 7 /my/home
-1 watch /dir token 100
-1 write /dir/test create contents
+1 watch /dir token
+write /dir/test create contents
 1 read /dir/test
 1 waitwatch
 1 ackwatch token
@@ -27,9 +27,9 @@
 
 # unwatch
 [ "`echo -e 'introduce 1 100 7 /my/home
-1 watch /dir token1 0
+1 watch /dir token1
 1 unwatch /dir token1
-1 watch /dir token2 0
+1 watch /dir token2
 2 write /dir/test2 create contents
 1 waitwatch
 1 unwatch /dir token2
@@ -39,8 +39,8 @@
 # unwatch while watch pending.
 [ "`echo -e 'introduce 1 100 7 /my/home
 introduce 2 101 8 /my/secondhome
-1 watch /dir token1 0
-2 watch /dir token2 1
+1 watch /dir token1
+2 watch /dir token2
 write /dir/test create contents
 2 unwatch /dir token2
 1 waitwatch
diff -r ba5d5bd28edf -r b0de1894df67 tools/xenstore/testsuite/12readonly.sh
--- a/tools/xenstore/testsuite/12readonly.sh    Thu Aug  4 10:43:03 2005
+++ b/tools/xenstore/testsuite/12readonly.sh    Thu Aug  4 11:39:03 2005
@@ -9,7 +9,7 @@
 
 [ "`echo 'read /test
 getperm /test
-watch /test token 0
+watch /test token
 unwatch /test token 
 start /
 commit
@@ -27,7 +27,7 @@
 
 # Check that watches work like normal.
 set -m
-[ "`echo 'watch / token 0
+[ "`echo 'watch / token
 waitwatch
 ackwatch token' | ./xs_test --readonly 2>&1`" = "/test:token" ] &
 
diff -r ba5d5bd28edf -r b0de1894df67 tools/xenstore/xenstored_core.c
--- a/tools/xenstore/xenstored_core.c   Thu Aug  4 10:43:03 2005
+++ b/tools/xenstore/xenstored_core.c   Thu Aug  4 11:39:03 2005
@@ -51,7 +51,7 @@
 #include "xenstored_domain.h"
 
 static bool verbose;
-static LIST_HEAD(connections);
+LIST_HEAD(connections);
 static int tracefd = -1;
 
 #ifdef TESTING
@@ -335,7 +335,7 @@
        list_for_each_entry(i, &connections, list) {
                if (i->domain)
                        continue;
-               if (!i->blocked)
+               if (i->state == OK)
                        FD_SET(i->fd, inset);
                if (i->out)
                        FD_SET(i->fd, outset);
@@ -471,8 +471,7 @@
        return i;
 }
 
-/* Returns "false", meaning "connection is not blocked". */
-bool send_reply(struct connection *conn, enum xsd_sockmsg_type type,
+void send_reply(struct connection *conn, enum xsd_sockmsg_type type,
                const void *data, unsigned int len)
 {
        struct buffered_data *bdata;
@@ -493,16 +492,15 @@
                conn->waiting_reply = bdata;
        } else
                conn->out = bdata;
-       return false;
 }
 
 /* Some routines (write, mkdir, etc) just need a non-error return */
-bool send_ack(struct connection *conn, enum xsd_sockmsg_type type)
-{
-       return send_reply(conn, type, "OK", sizeof("OK"));
-}
-
-bool send_error(struct connection *conn, int error)
+void send_ack(struct connection *conn, enum xsd_sockmsg_type type)
+{
+       send_reply(conn, type, "OK", sizeof("OK"));
+}
+
+void send_error(struct connection *conn, int error)
 {
        unsigned int i;
 
@@ -511,7 +509,7 @@
                        corrupt(conn, "Unknown error %i (%s)", error,
                                strerror(error));
 
-       return send_reply(conn, XS_ERROR, xsd_errors[i].errstring,
+       send_reply(conn, XS_ERROR, xsd_errors[i].errstring,
                          strlen(xsd_errors[i].errstring) + 1);
 }
 
@@ -797,7 +795,7 @@
        return false;
 }
 
-static bool send_directory(struct connection *conn, const char *node)
+static void send_directory(struct connection *conn, const char *node)
 {
        char *path, *reply = talloc_strdup(node, "");
        unsigned int reply_len = 0;
@@ -805,13 +803,17 @@
        struct dirent *dirent;
 
        node = canonicalize(conn, node);
-       if (!check_node_perms(conn, node, XS_PERM_READ))
-               return send_error(conn, errno);
+       if (!check_node_perms(conn, node, XS_PERM_READ)) {
+               send_error(conn, errno);
+               return;
+       }
 
        path = node_dir(conn->transaction, node);
        dir = talloc_opendir(path);
-       if (!dir)
-               return send_error(conn, errno);
+       if (!dir) {
+               send_error(conn, errno);
+               return;
+       }
 
        while ((dirent = readdir(*dir)) != NULL) {
                int len = strlen(dirent->d_name) + 1;
@@ -824,32 +826,35 @@
                reply_len += len;
        }
 
-       return send_reply(conn, XS_DIRECTORY, reply, reply_len);
-}
-
-static bool do_read(struct connection *conn, const char *node)
+       send_reply(conn, XS_DIRECTORY, reply, reply_len);
+}
+
+static void do_read(struct connection *conn, const char *node)
 {
        char *value;
        unsigned int size;
        int *fd;
 
        node = canonicalize(conn, node);
-       if (!check_node_perms(conn, node, XS_PERM_READ))
-               return send_error(conn, errno);
+       if (!check_node_perms(conn, node, XS_PERM_READ)) {
+               send_error(conn, errno);
+               return;
+       }
 
        fd = talloc_open(node_datafile(conn->transaction, node), O_RDONLY, 0);
        if (!fd) {
                /* Data file doesn't exist?  We call that a directory */
                if (errno == ENOENT)
                        errno = EISDIR;
-               return send_error(conn, errno);
+               send_error(conn, errno);
+               return;
        }
 
        value = read_all(fd, &size);
        if (!value)
-               return send_error(conn, errno);
-
-       return send_reply(conn, XS_READ, value, size);
+               send_error(conn, errno);
+       else
+               send_reply(conn, XS_READ, value, size);
 }
 
 /* Create a new directory.  Optionally put data in it (if data != NULL) */
@@ -893,7 +898,7 @@
 }
 
 /* path, flags, data... */
-static bool do_write(struct connection *conn, struct buffered_data *in)
+static void do_write(struct connection *conn, struct buffered_data *in)
 {
        unsigned int offset, datalen;
        char *vec[2];
@@ -902,15 +907,19 @@
        struct stat st;
 
        /* Extra "strings" can be created by binary data. */
-       if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec))
-               return send_error(conn, EINVAL);
+       if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) {
+               send_error(conn, EINVAL);
+               return;
+       }
 
        node = canonicalize(conn, vec[0]);
-       if (!within_transaction(conn->transaction, node))
-               return send_error(conn, EROFS);
+       if (!within_transaction(conn->transaction, node)) {
+               send_error(conn, EROFS);
+               return;
+       }
 
        if (transaction_block(conn, node))
-               return true;
+               return;
 
        offset = strlen(vec[0]) + strlen(vec[1]) + 2;
        datalen = in->used - offset;
@@ -921,193 +930,244 @@
                mode = XS_PERM_WRITE|XS_PERM_CREATE;
        else if (streq(vec[1], XS_WRITE_CREATE_EXCL))
                mode = XS_PERM_WRITE|XS_PERM_CREATE;
-       else
-               return send_error(conn, EINVAL);
-
-       if (!check_node_perms(conn, node, mode))
-               return send_error(conn, errno);
+       else {
+               send_error(conn, EINVAL);
+               return;
+       }
+
+       if (!check_node_perms(conn, node, mode)) {
+               send_error(conn, errno);
+               return;
+       }
 
        if (lstat(node_dir(conn->transaction, node), &st) != 0) {
                /* Does not exist... */
-               if (errno != ENOENT)
-                       return send_error(conn, errno);
+               if (errno != ENOENT) {
+                       send_error(conn, errno);
+                       return;
+               }
 
                /* Not going to create it? */
-               if (!(mode & XS_PERM_CREATE))
-                       return send_error(conn, ENOENT);
-
-               if (!new_directory(conn, node, in->buffer + offset, datalen))
-                       return send_error(conn, errno);
+               if (!(mode & XS_PERM_CREATE)) {
+                       send_error(conn, ENOENT);
+                       return;
+               }
+
+               if (!new_directory(conn, node, in->buffer + offset, datalen)) {
+                       send_error(conn, errno);
+                       return;
+               }
        } else {
                /* Exists... */
-               if (streq(vec[1], XS_WRITE_CREATE_EXCL))
-                       return send_error(conn, EEXIST);
+               if (streq(vec[1], XS_WRITE_CREATE_EXCL)) {
+                       send_error(conn, EEXIST);
+                       return;
+               }
 
                tmppath = tempfile(node_datafile(conn->transaction, node),
                                   in->buffer + offset, datalen);
-               if (!tmppath)
-                       return send_error(conn, errno);
+               if (!tmppath) {
+                       send_error(conn, errno);
+                       return;
+               }
 
                commit_tempfile(tmppath);
        }
 
        add_change_node(conn->transaction, node, false);
+       fire_watches(conn, node, false);
        send_ack(conn, XS_WRITE);
-       fire_watches(conn->transaction, node, false);
-       return false;
-}
-
-static bool do_mkdir(struct connection *conn, const char *node)
+}
+
+static void do_mkdir(struct connection *conn, const char *node)
 {
        node = canonicalize(conn, node);
-       if (!check_node_perms(conn, node, XS_PERM_WRITE|XS_PERM_CREATE))
-               return send_error(conn, errno);
-
-       if (!within_transaction(conn->transaction, node))
-               return send_error(conn, EROFS);
+       if (!check_node_perms(conn, node, XS_PERM_WRITE|XS_PERM_CREATE)) {
+               send_error(conn, errno);
+               return;
+       }
+
+       if (!within_transaction(conn->transaction, node)) {
+               send_error(conn, EROFS);
+               return;
+       }
 
        if (transaction_block(conn, node))
-               return true;
-
-       if (!new_directory(conn, node, NULL, 0))
-               return send_error(conn, errno);
+               return;
+
+       if (!new_directory(conn, node, NULL, 0)) {
+               send_error(conn, errno);
+               return;
+       }
 
        add_change_node(conn->transaction, node, false);
+       fire_watches(conn, node, false);
        send_ack(conn, XS_MKDIR);
-       fire_watches(conn->transaction, node, false);
-       return false;
-}
-
-static bool do_rm(struct connection *conn, const char *node)
+}
+
+static void do_rm(struct connection *conn, const char *node)
 {
        char *tmppath, *path;
 
        node = canonicalize(conn, node);
-       if (!check_node_perms(conn, node, XS_PERM_WRITE))
-               return send_error(conn, errno);
-
-       if (!within_transaction(conn->transaction, node))
-               return send_error(conn, EROFS);
+       if (!check_node_perms(conn, node, XS_PERM_WRITE)) {
+               send_error(conn, errno);
+               return;
+       }
+
+       if (!within_transaction(conn->transaction, node)) {
+               send_error(conn, EROFS);
+               return;
+       }
 
        if (transaction_block(conn, node))
-               return true;
-
-       if (streq(node, "/"))
-               return send_error(conn, EINVAL);
+               return;
+
+       if (streq(node, "/")) {
+               send_error(conn, EINVAL);
+               return;
+       }
 
        /* We move the directory to temporary name, destructor cleans up. */
        path = node_dir(conn->transaction, node);
        tmppath = talloc_asprintf(node, "%s.tmp", path);
        talloc_set_destructor(tmppath, destroy_path);
 
-       if (rename(path, tmppath) != 0)
-               return send_error(conn, errno);
+       if (rename(path, tmppath) != 0) {
+               send_error(conn, errno);
+               return;
+       }
 
        add_change_node(conn->transaction, node, true);
+       fire_watches(conn, node, true);
        send_ack(conn, XS_RM);
-       fire_watches(conn->transaction, node, true);
-       return false;
-}
-
-static bool do_get_perms(struct connection *conn, const char *node)
+}
+
+static void do_get_perms(struct connection *conn, const char *node)
 {
        struct xs_permissions *perms;
        char *strings;
        unsigned int len, num;
 
        node = canonicalize(conn, node);
-       if (!check_node_perms(conn, node, XS_PERM_READ))
-               return send_error(conn, errno);
+       if (!check_node_perms(conn, node, XS_PERM_READ)) {
+               send_error(conn, errno);
+               return;
+       }
 
        perms = get_perms(conn->transaction, node, &num);
-       if (!perms)
-               return send_error(conn, errno);
+       if (!perms) {
+               send_error(conn, errno);
+               return;
+       }
 
        strings = perms_to_strings(node, perms, num, &len);
        if (!strings)
-               return send_error(conn, errno);
-
-       return send_reply(conn, XS_GET_PERMS, strings, len);
-}
-
-static bool do_set_perms(struct connection *conn, struct buffered_data *in)
+               send_error(conn, errno);
+       else
+               send_reply(conn, XS_GET_PERMS, strings, len);
+}
+
+static void do_set_perms(struct connection *conn, struct buffered_data *in)
 {
        unsigned int num;
        char *node;
        struct xs_permissions *perms;
 
        num = xs_count_strings(in->buffer, in->used);
-       if (num < 2)
-               return send_error(conn, EINVAL);
+       if (num < 2) {
+               send_error(conn, EINVAL);
+               return;
+       }
 
        /* First arg is node name. */
        node = canonicalize(conn, in->buffer);
        in->buffer += strlen(in->buffer) + 1;
        num--;
 
-       if (!within_transaction(conn->transaction, node))
-               return send_error(conn, EROFS);
+       if (!within_transaction(conn->transaction, node)) {
+               send_error(conn, EROFS);
+               return;
+       }
 
        if (transaction_block(conn, node))
-               return true;
+               return;
 
        /* We must own node to do this (tools can do this too). */
-       if (!check_node_perms(conn, node, XS_PERM_WRITE|XS_PERM_OWNER))
-               return send_error(conn, errno);
+       if (!check_node_perms(conn, node, XS_PERM_WRITE|XS_PERM_OWNER)) {
+               send_error(conn, errno);
+               return;
+       }
 
        perms = talloc_array(node, struct xs_permissions, num);
-       if (!xs_strings_to_perms(perms, num, in->buffer))
-               return send_error(conn, errno);
-
-       if (!set_perms(conn->transaction, node, perms, num))
-               return send_error(conn, errno);
+       if (!xs_strings_to_perms(perms, num, in->buffer)) {
+               send_error(conn, errno);
+               return;
+       }
+
+       if (!set_perms(conn->transaction, node, perms, num)) {
+               send_error(conn, errno);
+               return;
+       }
+
        add_change_node(conn->transaction, node, false);
+       fire_watches(conn, node, false);
        send_ack(conn, XS_SET_PERMS);
-       fire_watches(conn->transaction, node, false);
-       return false;
 }
 
 /* Process "in" for conn: "in" will vanish after this conversation, so
  * we can talloc off it for temporary variables.  May free "conn".
- * Returns true if can't complete due to block.
  */
-static bool process_message(struct connection *conn, struct buffered_data *in)
+static void process_message(struct connection *conn, struct buffered_data *in)
 {
        switch (in->hdr.msg.type) {
        case XS_DIRECTORY:
-               return send_directory(conn, onearg(in));
+               send_directory(conn, onearg(in));
+               break;
 
        case XS_READ:
-               return do_read(conn, onearg(in));
+               do_read(conn, onearg(in));
+               break;
 
        case XS_WRITE:
-               return do_write(conn, in);
+               do_write(conn, in);
+               break;
 
        case XS_MKDIR:
-               return do_mkdir(conn, onearg(in));
+               do_mkdir(conn, onearg(in));
+               break;
 
        case XS_RM:
-               return do_rm(conn, onearg(in));
+               do_rm(conn, onearg(in));
+               break;
 
        case XS_GET_PERMS:
-               return do_get_perms(conn, onearg(in));
+               do_get_perms(conn, onearg(in));
+               break;
 
        case XS_SET_PERMS:
-               return do_set_perms(conn, in);
+               do_set_perms(conn, in);
+               break;
 
        case XS_SHUTDOWN:
                /* FIXME: Implement gentle shutdown too. */
                /* Only tools can do this. */
-               if (conn->id != 0)
-                       return send_error(conn, EACCES);
-               if (!conn->can_write)
-                       return send_error(conn, EROFS);
+               if (conn->id != 0) {
+                       send_error(conn, EACCES);
+                       break;
+               }
+               if (!conn->can_write) {
+                       send_error(conn, EROFS);
+                       break;
+               }
                send_ack(conn, XS_SHUTDOWN);
                /* Everything hangs off auto-free context, freed at exit. */
                exit(0);
 
+       case XS_DEBUG:
+               if (streq(in->buffer, "print"))
+                       xprintf("debug: %s", in->buffer + get_string(in, 0));
 #ifdef TESTING
-       case XS_DEBUG: {
                /* For testing, we allow them to set id. */
                if (streq(in->buffer, "setid")) {
                        conn->id = atoi(in->buffer + get_string(in, 0));
@@ -1118,39 +1178,45 @@
                        send_ack(conn, XS_DEBUG);
                        failtest = true;
                }
-               return false;
-       }
 #endif /* TESTING */
+               break;
 
        case XS_WATCH:
-               return do_watch(conn, in);
+               do_watch(conn, in);
+               break;
 
        case XS_WATCH_ACK:
-               return do_watch_ack(conn, onearg(in));
+               do_watch_ack(conn, onearg(in));
+               break;
 
        case XS_UNWATCH:
-               return do_unwatch(conn, in);
+               do_unwatch(conn, in);
+               break;
 
        case XS_TRANSACTION_START:
-               return do_transaction_start(conn, onearg(in));
+               do_transaction_start(conn, onearg(in));
+               break;
 
        case XS_TRANSACTION_END:
-               return do_transaction_end(conn, onearg(in));
+               do_transaction_end(conn, onearg(in));
+               break;
 
        case XS_INTRODUCE:
-               return do_introduce(conn, in);
+               do_introduce(conn, in);
+               break;
 
        case XS_RELEASE:
-               return do_release(conn, onearg(in));
+               do_release(conn, onearg(in));
+               break;
 
        case XS_GETDOMAINPATH:
-               return do_get_domain_path(conn, onearg(in));
+               do_get_domain_path(conn, onearg(in));
+               break;
 
        case XS_WATCH_EVENT:
        default:
                eprintf("Client unknown operation %i", in->hdr.msg.type);
                send_error(conn, ENOSYS);
-               return false;
        }
 }
 
@@ -1164,6 +1230,8 @@
        struct buffered_data *in = NULL;
        enum xsd_sockmsg_type type = conn->in->hdr.msg.type;
        jmp_buf talloc_fail;
+
+       assert(conn->state == OK);
 
        /* For simplicity, we kill the connection on OOM. */
        talloc_set_fail_handler(out_of_mem, &talloc_fail);
@@ -1187,7 +1255,9 @@
         */
        in = talloc_steal(talloc_autofree_context(), conn->in);
        conn->in = new_buffer(conn);
-       if (process_message(conn, in)) {
+       process_message(conn, in);
+
+       if (conn->state == BLOCKED) {
                /* Blocked by transaction: queue for re-xmit. */
                talloc_free(conn->in);
                conn->in = in;
@@ -1210,7 +1280,7 @@
        int bytes;
        struct buffered_data *in;
 
-       assert(!conn->blocked);
+       assert(conn->state == OK);
        in = conn->in;
 
        /* Not finished header yet? */
@@ -1267,13 +1337,17 @@
        struct connection *i, *tmp;
 
        list_for_each_entry_safe(i, tmp, &connections, list) {
-               if (!i->blocked)
-                       continue;
-
-               if (!transaction_covering_node(i->blocked)) {
-                       talloc_free(i->blocked);
-                       i->blocked = NULL;
-                       consider_message(i);
+               switch (i->state) {
+               case BLOCKED:
+                       if (!transaction_covering_node(i->blocked_by)) {
+                               talloc_free(i->blocked_by);
+                               i->blocked_by = NULL;
+                               i->state = OK;
+                               consider_message(i);
+                       }
+                       break;
+               case OK:
+                       break;
                }
        }
 
@@ -1294,7 +1368,8 @@
        if (!new)
                return NULL;
 
-       new->blocked = false;
+       new->state = OK;
+       new->blocked_by = NULL;
        new->out = new->waiting_reply = NULL;
        new->fd = -1;
        new->id = 0;
@@ -1303,6 +1378,7 @@
        new->write = write;
        new->read = read;
        new->can_write = true;
+       INIT_LIST_HEAD(&new->watches);
 
        talloc_set_fail_handler(out_of_mem, &talloc_fail);
        if (setjmp(talloc_fail)) {
@@ -1371,12 +1447,14 @@
 
        list_for_each_entry(i, &connections, list) {
                printf("Connection %p:\n", i);
+               printf("    state = %s\n",
+                      i->state == OK ? "OK"
+                      : i->state == BLOCKED ? "BLOCKED"
+                      : "INVALID");
                if (i->id)
                        printf("    id = %i\n", i->id);
-               if (i->blocked)
-                       printf("    blocked on = %s\n", i->blocked);
-               if (i->waiting_for_ack)
-                       printf("    waiting_for_ack TRUE\n");
+               if (i->blocked_by)
+                       printf("    blocked on = %s\n", i->blocked_by);
                if (!i->in->inhdr || i->in->used)
                        printf("    got %i bytes of %s\n",
                               i->in->used, i->in->inhdr ? "header" : "data");
@@ -1431,7 +1509,6 @@
        permfile = talloc_strdup(root, "/tool/xenstored");
        if (!set_perms(NULL, permfile, &perms, 1))
                barf_perror("Could not create permissions on %s", permfile);
-
        talloc_free(root);
        if (mkdir(xs_daemon_transactions(), 0750) != 0)
                barf_perror("Could not create transaction dir %s",
diff -r ba5d5bd28edf -r b0de1894df67 tools/xenstore/xenstored_core.h
--- a/tools/xenstore/xenstored_core.h   Thu Aug  4 10:43:03 2005
+++ b/tools/xenstore/xenstored_core.h   Thu Aug  4 11:39:03 2005
@@ -47,6 +47,14 @@
 typedef int connwritefn_t(struct connection *, const void *, unsigned int);
 typedef int connreadfn_t(struct connection *, void *, unsigned int);
 
+enum state
+{
+       /* Blocked by transaction. */
+       BLOCKED,
+       /* Completed */
+       OK,
+};
+
 struct connection
 {
        struct list_head list;
@@ -57,8 +65,11 @@
        /* Who am I?  0 for socket connections. */
        domid_t id;
 
-       /* Are we blocked waiting for a transaction to end?  Contains node. */
-       char *blocked;
+       /* Blocked on transaction? */
+       enum state state;
+
+       /* Node we are waiting for (if state == BLOCKED) */
+       char *blocked_by;
 
        /* Is this a read-only connection? */
        bool can_write;
@@ -81,10 +92,14 @@
        /* The domain I'm associated with, if any. */
        struct domain *domain;
 
+       /* My watches. */
+       struct list_head watches;
+
        /* Methods for communicating over this connection: write can be NULL */
        connwritefn_t *write;
        connreadfn_t *read;
 };
+extern struct list_head connections;
 
 /* Return length of string (including nul) at this offset. */
 unsigned int get_string(const struct buffered_data *data,
@@ -100,14 +115,14 @@
 /* Create a new buffer with lifetime of context. */
 struct buffered_data *new_buffer(void *ctx);
 
-bool send_reply(struct connection *conn, enum xsd_sockmsg_type type,
-                const void *data, unsigned int len);
+void send_reply(struct connection *conn, enum xsd_sockmsg_type type,
+               const void *data, unsigned int len);
 
 /* Some routines (write, mkdir, etc) just need a non-error return */
-bool send_ack(struct connection *conn, enum xsd_sockmsg_type type);
+void send_ack(struct connection *conn, enum xsd_sockmsg_type type);
 
 /* Send an error: error is usually "errno". */
-bool send_error(struct connection *conn, int error);
+void send_error(struct connection *conn, int error);
 
 /* Canonicalize this path if possible. */
 char *canonicalize(struct connection *conn, const char *node);
diff -r ba5d5bd28edf -r b0de1894df67 tools/xenstore/xenstored_domain.c
--- a/tools/xenstore/xenstored_domain.c Thu Aug  4 10:43:03 2005
+++ b/tools/xenstore/xenstored_domain.c Thu Aug  4 11:39:03 2005
@@ -239,7 +239,8 @@
         * careful that handle_input/handle_output can destroy conn.
         */
        while ((domain = find_domain(port)) != NULL) {
-               if (!domain->conn->blocked && buffer_has_input(domain->input))
+               if (domain->conn->state == OK
+                   && buffer_has_input(domain->input))
                        handle_input(domain->conn);
                else if (domain->conn->out
                         && buffer_has_output_room(domain->output))
@@ -287,33 +288,42 @@
 }
 
 /* domid, mfn, evtchn, path */
-bool do_introduce(struct connection *conn, struct buffered_data *in)
+void do_introduce(struct connection *conn, struct buffered_data *in)
 {
        struct domain *domain;
        char *vec[4];
 
-       if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec))
-               return send_error(conn, EINVAL);
-
-       if (conn->id != 0)
-               return send_error(conn, EACCES);
-
-       if (!conn->can_write)
-               return send_error(conn, EROFS);
+       if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) {
+               send_error(conn, EINVAL);
+               return;
+       }
+
+       if (conn->id != 0) {
+               send_error(conn, EACCES);
+               return;
+       }
+
+       if (!conn->can_write) {
+               send_error(conn, EROFS);
+               return;
+       }
 
        /* Sanity check args. */
-       if ((atoi(vec[2]) <= 0) || !is_valid_nodename(vec[3]))
-               return send_error(conn, EINVAL);
+       if ((atoi(vec[2]) <= 0) || !is_valid_nodename(vec[3])) {
+               send_error(conn, EINVAL);
+               return;
+       }
        /* Hang domain off "in" until we're finished. */
        domain = new_domain(in, atoi(vec[0]), atol(vec[1]), atol(vec[2]),
                            vec[3]);
-       if (!domain)
-               return send_error(conn, errno);
+       if (!domain) {
+               send_error(conn, errno);
+               return;
+       }
 
        /* Now domain belongs to its connection. */
        talloc_steal(domain->conn, domain);
-
-       return send_ack(conn, XS_INTRODUCE);
+       send_ack(conn, XS_INTRODUCE);
 }
 
 static struct domain *find_domain_by_domid(domid_t domid)
@@ -328,39 +338,51 @@
 }
 
 /* domid */
-bool do_release(struct connection *conn, const char *domid_str)
+void do_release(struct connection *conn, const char *domid_str)
 {
        struct domain *domain;
        domid_t domid;
 
-       if (!domid_str)
-               return send_error(conn, EINVAL);
+       if (!domid_str) {
+               send_error(conn, EINVAL);
+               return;
+       }
 
        domid = atoi(domid_str);
-       if (!domid)
-               return send_error(conn, EINVAL);
-
-       if (conn->id != 0)
-               return send_error(conn, EACCES);
+       if (!domid) {
+               send_error(conn, EINVAL);
+               return;
+       }
+
+       if (conn->id != 0) {
+               send_error(conn, EACCES);
+               return;
+       }
 
        domain = find_domain_by_domid(domid);
-       if (!domain)
-               return send_error(conn, ENOENT);
-
-       if (!domain->conn)
-               return send_error(conn, EINVAL);
-
-       talloc_free(domain->conn);
-       return send_ack(conn, XS_RELEASE);
-}
-
-bool do_get_domain_path(struct connection *conn, const char *domid_str)
+       if (!domain) {
+               send_error(conn, ENOENT);
+               return;
+       }
+
+       if (!domain->conn) {
+               send_error(conn, EINVAL);
+               return;
+       }
+
+        talloc_free(domain->conn);
+       send_ack(conn, XS_RELEASE);
+}
+
+void do_get_domain_path(struct connection *conn, const char *domid_str)
 {
        struct domain *domain;
        domid_t domid;
 
-       if (!domid_str)
-               return send_error(conn, EINVAL);
+       if (!domid_str) {
+               send_error(conn, EINVAL);
+               return;
+       }
 
        domid = atoi(domid_str);
        if (domid == DOMID_SELF)
@@ -368,11 +390,11 @@
        else
                domain = find_domain_by_domid(domid);
 
-       if (!domain)
-               return send_error(conn, ENOENT);
-
-       return send_reply(conn, XS_GETDOMAINPATH, domain->path,
-                         strlen(domain->path) + 1);
+       if (!domain) 
+               send_error(conn, ENOENT);
+       else
+               send_reply(conn, XS_GETDOMAINPATH, domain->path,
+                          strlen(domain->path) + 1);
 }
 
 static int close_xc_handle(void *_handle)
diff -r ba5d5bd28edf -r b0de1894df67 tools/xenstore/xenstored_domain.h
--- a/tools/xenstore/xenstored_domain.h Thu Aug  4 10:43:03 2005
+++ b/tools/xenstore/xenstored_domain.h Thu Aug  4 11:39:03 2005
@@ -22,13 +22,13 @@
 void handle_event(int event_fd);
 
 /* domid, mfn, eventchn, path */
-bool do_introduce(struct connection *conn, struct buffered_data *in);
+void do_introduce(struct connection *conn, struct buffered_data *in);
 
 /* domid */
-bool do_release(struct connection *conn, const char *domid_str);
+void do_release(struct connection *conn, const char *domid_str);
 
 /* domid */
-bool do_get_domain_path(struct connection *conn, const char *domid_str);
+void do_get_domain_path(struct connection *conn, const char *domid_str);
 
 /* Returns the event channel handle */
 int domain_init(void);
diff -r ba5d5bd28edf -r b0de1894df67 tools/xenstore/xenstored_transaction.c
--- a/tools/xenstore/xenstored_transaction.c    Thu Aug  4 10:43:03 2005
+++ b/tools/xenstore/xenstored_transaction.c    Thu Aug  4 11:39:03 2005
@@ -114,7 +114,8 @@
        trans = transaction_covering_node(node);
        if (trans) {
                start_transaction_timeout(trans);
-               conn->blocked = talloc_strdup(conn, node);
+               conn->state = BLOCKED;
+               conn->blocked_by = talloc_strdup(conn, node);
                return true;
        }
        return false;
@@ -239,20 +240,24 @@
        return true;
 }
 
-bool do_transaction_start(struct connection *conn, const char *node)
+void do_transaction_start(struct connection *conn, const char *node)
 {
        struct transaction *transaction;
        char *dir;
 
-       if (conn->transaction)
-               return send_error(conn, EBUSY);
+       if (conn->transaction) {
+               send_error(conn, EBUSY);
+               return;
+       }
 
        node = canonicalize(conn, node);
-       if (!check_node_perms(conn, node, XS_PERM_READ))
-               return send_error(conn, errno);
+       if (!check_node_perms(conn, node, XS_PERM_READ)) {
+               send_error(conn, errno);
+               return;
+       }
 
        if (transaction_block(conn, node))
-               return true;
+               return;
 
        dir = node_dir_outside_transaction(node);
 
@@ -270,18 +275,19 @@
        talloc_set_destructor(transaction, destroy_transaction);
        trace_create(transaction, "transaction");
 
-       if (!copy_dir(dir, transaction->divert))
-               return send_error(conn, errno);
+       if (!copy_dir(dir, transaction->divert)) {
+               send_error(conn, errno);
+               return;
+       }
 
        talloc_steal(conn, transaction);
        conn->transaction = transaction;
-       return send_ack(transaction->conn, XS_TRANSACTION_START);
+       send_ack(transaction->conn, XS_TRANSACTION_START);
 }
 
 static bool commit_transaction(struct transaction *trans)
 {
        char *tmp, *dir;
-       struct changed_node *i;
 
        /* Move: orig -> .old, repl -> orig.  Cleanup deletes .old. */
        dir = node_dir_outside_transaction(trans->node);
@@ -294,39 +300,44 @@
                        trans->divert, dir);
 
        trans->divert = tmp;
-
-       /* Fire off the watches for everything that changed. */
-       list_for_each_entry(i, &trans->changes, list)
-               fire_watches(NULL, i->node, i->recurse);
        return true;
 }
 
-bool do_transaction_end(struct connection *conn, const char *arg)
-{
-       if (!arg || (!streq(arg, "T") && !streq(arg, "F")))
-               return send_error(conn, EINVAL);
-
-       if (!conn->transaction)
-               return send_error(conn, ENOENT);
+void do_transaction_end(struct connection *conn, const char *arg)
+{
+       struct changed_node *i;
+       struct transaction *trans;
+
+       if (!arg || (!streq(arg, "T") && !streq(arg, "F"))) {
+               send_error(conn, EINVAL);
+               return;
+       }
+
+       if (!conn->transaction) {
+               send_error(conn, ENOENT);
+               return;
+       }
+
+       /* Set to NULL so fire_watches sends events. */
+       trans = conn->transaction;
+       conn->transaction = NULL;
+       /* Attach transaction to arg for auto-cleanup */
+       talloc_steal(arg, trans);
 
        if (streq(arg, "T")) {
-               if (conn->transaction->destined_to_fail) {
+               if (trans->destined_to_fail) {
                        send_error(conn, ETIMEDOUT);
-                       goto failed;
+                       return;
                }
-               if (!commit_transaction(conn->transaction)) {
+               if (!commit_transaction(trans)) {
                        send_error(conn, errno);
-                       goto failed;
+                       return;
                }
-       }
-
-       talloc_free(conn->transaction);
-       conn->transaction = NULL;
-       return send_ack(conn, XS_TRANSACTION_END);
-
-failed:
-       talloc_free(conn->transaction);
-       conn->transaction = NULL;
-       return false;
-}
-
+
+               /* Fire off the watches for everything that changed. */
+               list_for_each_entry(i, &trans->changes, list)
+                       fire_watches(conn, i->node, i->recurse);
+       }
+       send_ack(conn, XS_TRANSACTION_END);
+}
+
diff -r ba5d5bd28edf -r b0de1894df67 tools/xenstore/xenstored_transaction.h
--- a/tools/xenstore/xenstored_transaction.h    Thu Aug  4 10:43:03 2005
+++ b/tools/xenstore/xenstored_transaction.h    Thu Aug  4 11:39:03 2005
@@ -22,8 +22,8 @@
 
 struct transaction;
 
-bool do_transaction_start(struct connection *conn, const char *node);
-bool do_transaction_end(struct connection *conn, const char *arg);
+void do_transaction_start(struct connection *conn, const char *node);
+void do_transaction_end(struct connection *conn, const char *arg);
 
 /* Is node covered by this transaction? */
 bool within_transaction(struct transaction *trans, const char *node);
diff -r ba5d5bd28edf -r b0de1894df67 tools/xenstore/xenstored_watch.c
--- a/tools/xenstore/xenstored_watch.c  Thu Aug  4 10:43:03 2005
+++ b/tools/xenstore/xenstored_watch.c  Thu Aug  4 11:39:03 2005
@@ -33,69 +33,36 @@
 #include "xenstored_domain.h"
 
 /* FIXME: time out unacked watches. */
-
-/* We create this if anyone is interested "node", then we pass it from
- * watch to watch as each connection acks it.
- */
 struct watch_event
 {
-       /* The watch we are firing for (watch->events) */
+       /* The events on this watch. */
        struct list_head list;
 
-       /* 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;
+       /* Data to send (node\0token\0). */
+       unsigned int len;
+       char *data;
 };
 
 struct watch
 {
+       /* Watches on this connection */
        struct list_head list;
-       unsigned int priority;
 
        /* Current outstanding events applying to this watch. */
        struct list_head events;
 
        /* Is this relative to connnection's implicit path? */
-       bool relative;
+       const char *relative_path;
 
        char *token;
        char *node;
-       struct connection *conn;
 };
-static LIST_HEAD(watches);
-
-static struct watch_event *get_first_event(struct connection *conn)
-{
-       struct watch *watch;
-       struct watch_event *event;
-
-       /* Find first watch with an event. */
-       list_for_each_entry(watch, &watches, list) {
-               if (watch->conn != conn)
-                       continue;
-
-               event = list_top(&watch->events, struct watch_event, list);
-               if (event)
-                       return event;
-       }
-       return NULL;
-}
 
 /* Look through our watches: if any of them have an event, queue it. */
 void queue_next_event(struct connection *conn)
 {
        struct watch_event *event;
-       const char *node;
-       char *buffer;
-       unsigned int len;
+       struct watch *watch;
 
        /* We had a reply queued already?  Send it: other end will
         * discard watch. */
@@ -110,170 +77,83 @@
        if (conn->waiting_for_ack)
                return;
 
-       event = get_first_event(conn);
-       if (!event)
-               return;
-
-       /* If we decide to cancel, we will reset this. */
-       conn->waiting_for_ack = event->watches[0];
-
-       /* 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 "". */
+       list_for_each_entry(watch, &conn->watches, list) {
+               event = list_top(&watch->events, struct watch_event, list);
+               if (event) {
+                       conn->waiting_for_ack = watch;
+                       send_reply(conn,XS_WATCH_EVENT,event->data,event->len);
+                       break;
+               }
+       }
+}
+
+static int destroy_watch_event(void *_event)
+{
+       struct watch_event *event = _event;
+
+       trace_destroy(event, "watch_event");
+       return 0;
+}
+
+static void add_event(struct watch *watch, const char *node)
+{
+       struct watch_event *event;
+
+       if (watch->relative_path) {
+               node += strlen(watch->relative_path);
+               if (*node == '/') /* Could be "" */
                        node++;
        }
 
-       /* Create reply from path and token */
-       len = strlen(node) + 1 + strlen(event->watches[0]->token) + 1;
-       buffer = talloc_array(conn, char, len);
-       strcpy(buffer, node);
-       strcpy(buffer+strlen(node)+1, event->watches[0]->token);
-       send_reply(conn, XS_WATCH_EVENT, buffer, len);
-       talloc_free(buffer);
-}
-
-static struct watch **find_watches(const char *node, bool recurse,
-                                  unsigned int *num)
-{
-       struct watch *i;
-       struct watch **ret = NULL;
-
-       *num = 0;
-
-       /* 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 ret;
+       event = talloc(watch, struct watch_event);
+       event->len = strlen(node) + 1 + strlen(watch->token) + 1;
+       event->data = talloc_array(event, char, event->len);
+       strcpy(event->data, node);
+       strcpy(event->data + strlen(node) + 1, watch->token);
+       talloc_set_destructor(event, destroy_watch_event);
+       list_add_tail(&event->list, &watch->events);
+       trace_create(event, "watch_event");
 }
 
 /* FIXME: we fail to fire on out of memory.  Should drop connections. */
-void fire_watches(struct transaction *trans, const char *node, bool recurse)
-{
-       struct watch **watches;
-       struct watch_event *event;
-       unsigned int num_watches;
+void fire_watches(struct connection *conn, const char *node, bool recurse)
+{
+       struct connection *i;
+       struct watch *watch;
 
        /* During transactions, don't fire watches. */
-       if (trans)
-               return;
-
-       watches = find_watches(node, recurse, &num_watches);
-       if (!watches)
-               return;
-
-       /* Create and fill in info about event. */
-       event = talloc(talloc_autofree_context(), struct watch_event);
-       event->node = talloc_strdup(event, node);
-
-       /* Tie event to this watch. */
-       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 (!watches[0]->conn->out)
-               queue_next_event(watches[0]->conn);
-}
-
-/* We're done with this event: see if anyone else wants it. */
-static void move_event_onwards(struct watch_event *event)
-{
-       list_del(&event->list);
-
-       event->num_watches--;
-       event->watches++;
-       if (!event->num_watches) {
-               talloc_free(event);
-               return;
-       }
-
-       list_add_tail(&event->list, &event->watches[0]->events);
-
-       /* If connection not doing anything, queue this. */
-       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--;
-                       }
+       if (conn->transaction)
+               return;
+
+       /* Create an event for each watch.  Don't send to self. */
+       list_for_each_entry(i, &connections, list) {
+               if (i == conn)
+                       continue;
+
+               list_for_each_entry(watch, &i->watches, list) {
+                       if (is_child(node, watch->node))
+                               add_event(watch, node);
+                       else if (recurse && is_child(watch->node, node))
+                               add_event(watch, watch->node);
+                       else
+                               continue;
+                       /* If connection not doing anything, queue this. */
+                       if (!i->out)
+                               queue_next_event(i);
                }
        }
 }
 
 static int destroy_watch(void *_watch)
 {
-       struct watch *watch = _watch;
-       struct watch_event *event;
-
-       /* If we have pending events, pass them on to others. */
-       while ((event = list_top(&watch->events, struct watch_event, list)))
-               move_event_onwards(event);
-
-       /* 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");
+       trace_destroy(_watch, "watch");
        return 0;
 }
 
-/* We keep watches in priority order. */
-static void insert_watch(struct watch *watch)
-{
-       struct watch *i;
-
-       list_for_each_entry(i, &watches, list) {
-               if (i->priority <= watch->priority) {
-                       list_add_tail(&watch->list, &i->list);
-                       return;
-               }
-       }
-
-       list_add_tail(&watch->list, &watches);
-}
-
 void shortest_watch_ack_timeout(struct timeval *tv)
 {
+       (void)tv;
+#if 0 /* FIXME */
        struct watch *watch;
 
        list_for_each_entry(watch, &watches, list) {
@@ -285,10 +165,12 @@
                                *tv = i->timeout;
                }
        }
+#endif
 }      
 
 void check_watch_ack_timeout(void)
 {
+#if 0
        struct watch *watch;
        struct timeval now;
 
@@ -307,82 +189,97 @@
                        }
                }
        }
-}
-
-bool do_watch(struct connection *conn, struct buffered_data *in)
-{
-       struct watch *watch;
-       char *vec[3];
+#endif
+}
+
+void do_watch(struct connection *conn, struct buffered_data *in)
+{
+       struct watch *watch;
+       char *vec[2];
        bool relative;
 
-       if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec))
-               return send_error(conn, EINVAL);
+       if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) {
+               send_error(conn, EINVAL);
+               return;
+       }
 
        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);
+       if (!check_node_perms(conn, vec[0], XS_PERM_READ)) {
+               send_error(conn, errno);
+               return;
+       }
 
        watch = talloc(conn, struct watch);
        watch->node = talloc_strdup(watch, vec[0]);
        watch->token = talloc_strdup(watch, vec[1]);
-       watch->conn = conn;
-       watch->priority = strtoul(vec[2], NULL, 0);
-       watch->relative = relative;
+       if (relative)
+               watch->relative_path = get_implicit_path(conn);
+       else
+               watch->relative_path = NULL;
+
        INIT_LIST_HEAD(&watch->events);
 
-       insert_watch(watch);
+       list_add_tail(&watch->list, &conn->watches);
+       trace_create(watch, "watch");
        talloc_set_destructor(watch, destroy_watch);
-       trace_create(watch, "watch");
-       return send_ack(conn, XS_WATCH);
-}
-
-bool do_watch_ack(struct connection *conn, const char *token)
+       send_ack(conn, XS_WATCH);
+}
+
+void 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 = list_top(&conn->waiting_for_ack->events,
-                        struct watch_event, list);
-       assert(event->watches[0] == conn->waiting_for_ack);
+       if (!token) {
+               send_error(conn, EINVAL);
+               return;
+       }
+
+       if (!conn->waiting_for_ack) {
+               send_error(conn, ENOENT);
+               return;
+       }
+
        if (!streq(conn->waiting_for_ack->token, token)) {
                /* They're confused: this will cause us to send event again */
                conn->waiting_for_ack = NULL;
-               return send_error(conn, EINVAL);
-       }
-
-       move_event_onwards(event);
+               send_error(conn, EINVAL);
+               return;
+       }
+
+       /* Remove event: after ack sent, core will call queue_next_event */
+       event = list_top(&conn->waiting_for_ack->events, struct watch_event,
+                        list);
+       list_del(&event->list);
+       talloc_free(event);
+
        conn->waiting_for_ack = NULL;
-       return send_ack(conn, XS_WATCH_ACK);
-}
-
-bool do_unwatch(struct connection *conn, struct buffered_data *in)
+       send_ack(conn, XS_WATCH_ACK);
+}
+
+void do_unwatch(struct connection *conn, struct buffered_data *in)
 {
        struct watch *watch;
        char *node, *vec[2];
 
-       if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec))
-               return send_error(conn, EINVAL);
+       if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) {
+               send_error(conn, EINVAL);
+               return;
+       }
 
        /* 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)
-                       continue;
-
+       list_for_each_entry(watch, &conn->watches, list) {
                if (streq(watch->node, node) && streq(watch->token, vec[1])) {
+                       list_del(&watch->list);
                        talloc_free(watch);
-                       return send_ack(conn, XS_UNWATCH);
-               }
-       }
-       return send_error(conn, ENOENT);
+                       send_ack(conn, XS_UNWATCH);
+                       return;
+               }
+       }
+       send_error(conn, ENOENT);
 }
 
 #ifdef TESTING
@@ -391,15 +288,16 @@
        struct watch *watch;
        struct watch_event *event;
 
-       /* Find first watch with an event. */
-       list_for_each_entry(watch, &watches, list) {
-               if (watch->conn != conn)
-                       continue;
-
-               printf("    watch on %s token %s prio %i\n",
-                      watch->node, watch->token, watch->priority);
+       if (conn->waiting_for_ack)
+               printf("    waiting_for_ack for watch on %s token %s\n",
+                      conn->waiting_for_ack->node,
+                      conn->waiting_for_ack->token);
+
+       list_for_each_entry(watch, &conn->watches, list) {
+               printf("    watch on %s token %s\n",
+                      watch->node, watch->token);
                list_for_each_entry(event, &watch->events, list)
-                       printf("        event: %s\n", event->node);
+                       printf("        event: %s\n", event->data);
        }
 }
 #endif
diff -r ba5d5bd28edf -r b0de1894df67 tools/xenstore/xenstored_watch.h
--- a/tools/xenstore/xenstored_watch.h  Thu Aug  4 10:43:03 2005
+++ b/tools/xenstore/xenstored_watch.h  Thu Aug  4 11:39:03 2005
@@ -22,9 +22,9 @@
 
 #include "xenstored_core.h"
 
-bool do_watch(struct connection *conn, struct buffered_data *in);
-bool do_watch_ack(struct connection *conn, const char *token);
-bool do_unwatch(struct connection *conn, struct buffered_data *in);
+void do_watch(struct connection *conn, struct buffered_data *in);
+void do_watch_ack(struct connection *conn, const char *token);
+void do_unwatch(struct connection *conn, struct buffered_data *in);
 
 /* Is this a watch event message for this connection? */
 bool is_watch_event(struct connection *conn, struct buffered_data *out);
@@ -32,8 +32,9 @@
 /* Look through our watches: if any of them have an event, queue it. */
 void queue_next_event(struct connection *conn);
 
-/* Fire all watches: recurse means all the children are effected (ie. rm) */
-void fire_watches(struct transaction *trans, const char *node, bool recurse);
+/* Fire all watches: recurse means all the children are effected (ie. rm).
+ */
+void fire_watches(struct connection *conn, const char *node, bool recurse);
 
 /* Find shortest timeout: if any, reduce tv (may already be set). */
 void shortest_watch_ack_timeout(struct timeval *tv);
diff -r ba5d5bd28edf -r b0de1894df67 tools/xenstore/xs.c
--- a/tools/xenstore/xs.c       Thu Aug  4 10:43:03 2005
+++ b/tools/xenstore/xs.c       Thu Aug  4 11:39:03 2005
@@ -401,22 +401,16 @@
 /* Watch a node for changes (poll on fd to detect, or call read_watch()).
  * When the node (or any child) changes, fd will become readable.
  * Token is returned when watch is read, to allow matching.
- * Priority indicates order if multiple watchers: higher is first.
  * Returns false on failure.
  */
-bool xs_watch(struct xs_handle *h, const char *path, const char *token,
-             unsigned int priority)
-{
-       char prio[MAX_STRLEN(priority)];
-       struct iovec iov[3];
-
-       sprintf(prio, "%u", priority);
+bool xs_watch(struct xs_handle *h, const char *path, const char *token)
+{
+       struct iovec iov[2];
+
        iov[0].iov_base = (void *)path;
        iov[0].iov_len = strlen(path) + 1;
        iov[1].iov_base = (void *)token;
        iov[1].iov_len = strlen(token) + 1;
-       iov[2].iov_base = prio;
-       iov[2].iov_len = strlen(prio) + 1;
 
        return xs_bool(xs_talkv(h, XS_WATCH, iov, ARRAY_SIZE(iov), NULL));
 }
diff -r ba5d5bd28edf -r b0de1894df67 tools/xenstore/xs.h
--- a/tools/xenstore/xs.h       Thu Aug  4 10:43:03 2005
+++ b/tools/xenstore/xs.h       Thu Aug  4 11:39:03 2005
@@ -82,11 +82,9 @@
 /* Watch a node for changes (poll on fd to detect, or call read_watch()).
  * When the node (or any child) changes, fd will become readable.
  * Token is returned when watch is read, to allow matching.
- * Priority indicates order if multiple watchers: higher is first.
  * Returns false on failure.
  */
-bool xs_watch(struct xs_handle *h, const char *path, const char *token,
-             unsigned int priority);
+bool xs_watch(struct xs_handle *h, const char *path, const char *token);
 
 /* Return the FD to poll on to see if a watch has fired. */
 int xs_fileno(struct xs_handle *h);
diff -r ba5d5bd28edf -r b0de1894df67 tools/xenstore/xs_test.c
--- a/tools/xenstore/xs_test.c  Thu Aug  4 10:43:03 2005
+++ b/tools/xenstore/xs_test.c  Thu Aug  4 11:39:03 2005
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/types.h>
+#include <sys/wait.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <signal.h>
@@ -33,6 +34,9 @@
 #define XSTEST
 
 static struct xs_handle *handles[10] = { NULL };
+
+static bool timeout = true;
+static bool readonly = false;
 
 struct ringbuf_head
 {
@@ -184,7 +188,7 @@
             "  getperm <path>\n"
             "  setperm <path> <id> <flags> ...\n"
             "  shutdown\n"
-            "  watch <path> <token> <prio>\n"
+            "  watch <path> <token>\n"
             "  waitwatch\n"
             "  ackwatch <token>\n"
             "  unwatch <path> <token>\n"
@@ -197,22 +201,34 @@
             "  dump\n");
 }
 
+static int argpos(const char *line, unsigned int num)
+{
+       unsigned int i, len = 0, off = 0;
+
+       for (i = 0; i <= num; i++) {
+               off += len;
+               off += strspn(line + off, " \t\n");
+               len = strcspn(line + off, " \t\n");
+               if (!len)
+                       return off;
+       }
+       return off;
+}
+
 static char *arg(char *line, unsigned int num)
 {
        static char *args[10];
-       unsigned int i, len = 0;
-
-       for (i = 0; i <= num; i++) {
-               line += len;
-               line += strspn(line, " \t\n");
-               len = strcspn(line, " \t\n");
-               if (!len)
-                       barf("Can't get arg %u", num);
-       }
+       unsigned int off, len;
+
+       off = argpos(line, num);
+       len = strcspn(line + off, " \t\n");
+
+       if (!len)
+               barf("Can't get arg %u", num);
 
        free(args[num]);
        args[num] = malloc(len + 1);
-       memcpy(args[num], line, len);
+       memcpy(args[num], line+off, len);
        args[num][len] = '\0';
        return args[num];
 }
@@ -371,10 +387,9 @@
                failed(handle);
 }
 
-static void do_watch(unsigned int handle, const char *node, const char *token,
-                    const char *pri)
-{
-       if (!xs_watch(handles[handle], node, token, atoi(pri)))
+static void do_watch(unsigned int handle, const char *node, const char *token)
+{
+       if (!xs_watch(handles[handle], node, token))
                failed(handle);
 }
 
@@ -544,23 +559,102 @@
        free(subdirs);
 }
 
+static int handle;
+
+static void alarmed(int sig __attribute__((unused)))
+{
+       if (handle) {
+               char handlename[10];
+               sprintf(handlename, "%u:", handle);
+               write(STDOUT_FILENO, handlename, strlen(handlename));
+       }
+       write(STDOUT_FILENO, command, strlen(command));
+       write(STDOUT_FILENO, " timeout\n", strlen(" timeout\n"));
+       exit(1);
+}
+
+static void do_command(unsigned int default_handle, char *line)
+{
+       char *endp;
+
+       if (strspn(line, " \n") == strlen(line))
+               return;
+       if (strstarts(line, "#"))
+               return;
+
+       handle = strtoul(line, &endp, 10);
+       if (endp != line)
+               memmove(line, endp+1, strlen(endp));
+       else
+               handle = default_handle;
+
+       if (!handles[handle]) {
+               if (readonly)
+                       handles[handle] = xs_daemon_open_readonly();
+               else
+                       handles[handle] = xs_daemon_open();
+               if (!handles[handle])
+                       barf_perror("Opening connection to daemon");
+       }
+       command = arg(line, 0);
+
+       if (timeout)
+               alarm(1);
+
+       if (streq(command, "dir"))
+               do_dir(handle, arg(line, 1));
+       else if (streq(command, "read"))
+               do_read(handle, arg(line, 1));
+       else if (streq(command, "write"))
+               do_write(handle,
+                        arg(line, 1), arg(line, 2), arg(line, 3));
+       else if (streq(command, "setid"))
+               do_setid(handle, arg(line, 1));
+       else if (streq(command, "mkdir"))
+               do_mkdir(handle, arg(line, 1));
+       else if (streq(command, "rm"))
+               do_rm(handle, arg(line, 1));
+       else if (streq(command, "getperm"))
+               do_getperm(handle, arg(line, 1));
+       else if (streq(command, "setperm"))
+               do_setperm(handle, arg(line, 1), line);
+       else if (streq(command, "shutdown"))
+               do_shutdown(handle);
+       else if (streq(command, "watch"))
+               do_watch(handle, arg(line, 1), arg(line, 2));
+       else if (streq(command, "waitwatch"))
+               do_waitwatch(handle);
+       else if (streq(command, "ackwatch"))
+               do_ackwatch(handle, arg(line, 1));
+       else if (streq(command, "unwatch"))
+               do_unwatch(handle, arg(line, 1), arg(line, 2));
+       else if (streq(command, "close")) {
+               xs_daemon_close(handles[handle]);
+               handles[handle] = NULL;
+       } else if (streq(command, "start"))
+               do_start(handle, arg(line, 1));
+       else if (streq(command, "commit"))
+               do_end(handle, false);
+       else if (streq(command, "abort"))
+               do_end(handle, true);
+       else if (streq(command, "introduce"))
+               do_introduce(handle, arg(line, 1), arg(line, 2),
+                            arg(line, 3), arg(line, 4));
+       else if (streq(command, "release"))
+               do_release(handle, arg(line, 1));
+       else if (streq(command, "dump"))
+               dump(handle);
+       else if (streq(command, "sleep"))
+               sleep(atoi(arg(line, 1)));
+       else
+               barf("Unknown command %s", command);
+       fflush(stdout);
+       alarm(0);
+}
+
 int main(int argc, char *argv[])
 {
        char line[1024];
-       bool readonly = false, timeout = true;
-       int handle;
-
-       static void alarmed(int sig __attribute__((unused)))
-       {
-               if (handle) {
-                       char handlename[10];
-                       sprintf(handlename, "%u:", handle);
-                       write(STDOUT_FILENO, handlename, strlen(handlename));
-               }
-               write(STDOUT_FILENO, command, strlen(command));
-               write(STDOUT_FILENO, " timeout\n", strlen(" timeout\n"));
-               exit(1);
-       }
 
        if (argc > 1 && streq(argv[1], "--readonly")) {
                readonly = true;
@@ -568,7 +662,7 @@
                argv++;
        }
 
-       if (argc > 1 && streq(argv[1], "--notimeout")) {
+       if (argc > 1 && streq(argv[1], "--no-timeout")) {
                timeout = false;
                argc--;
                argv++;
@@ -581,81 +675,8 @@
        ringbuf_datasize = getpagesize() / 2 - sizeof(struct ringbuf_head);
 
        signal(SIGALRM, alarmed);
-       while (fgets(line, sizeof(line), stdin)) {
-               char *endp;
-
-               if (strspn(line, " \n") == strlen(line))
-                       continue;
-               if (strstarts(line, "#"))
-                       continue;
-
-               handle = strtoul(line, &endp, 10);
-               if (endp != line)
-                       memmove(line, endp+1, strlen(endp));
-               else
-                       handle = 0;
-
-               if (!handles[handle]) {
-                       if (readonly)
-                               handles[handle] = xs_daemon_open_readonly();
-                       else
-                               handles[handle] = xs_daemon_open();
-                       if (!handles[handle])
-                               barf_perror("Opening connection to daemon");
-               }
-               command = arg(line, 0);
-
-               if (timeout)
-                       alarm(5);
-               if (streq(command, "dir"))
-                       do_dir(handle, arg(line, 1));
-               else if (streq(command, "read"))
-                       do_read(handle, arg(line, 1));
-               else if (streq(command, "write"))
-                       do_write(handle,
-                                arg(line, 1), arg(line, 2), arg(line, 3));
-               else if (streq(command, "setid"))
-                       do_setid(handle, arg(line, 1));
-               else if (streq(command, "mkdir"))
-                       do_mkdir(handle, arg(line, 1));
-               else if (streq(command, "rm"))
-                       do_rm(handle, arg(line, 1));
-               else if (streq(command, "getperm"))
-                       do_getperm(handle, arg(line, 1));
-               else if (streq(command, "setperm"))
-                       do_setperm(handle, arg(line, 1), line);
-               else if (streq(command, "shutdown"))
-                       do_shutdown(handle);
-               else if (streq(command, "watch"))
-                       do_watch(handle, arg(line, 1), arg(line, 2), arg(line, 
3));
-               else if (streq(command, "waitwatch"))
-                       do_waitwatch(handle);
-               else if (streq(command, "ackwatch"))
-                       do_ackwatch(handle, arg(line, 1));
-               else if (streq(command, "unwatch"))
-                       do_unwatch(handle, arg(line, 1), arg(line, 2));
-               else if (streq(command, "close")) {
-                       xs_daemon_close(handles[handle]);
-                       handles[handle] = NULL;
-               } else if (streq(command, "start"))
-                       do_start(handle, arg(line, 1));
-               else if (streq(command, "commit"))
-                       do_end(handle, false);
-               else if (streq(command, "abort"))
-                       do_end(handle, true);
-               else if (streq(command, "introduce"))
-                       do_introduce(handle, arg(line, 1), arg(line, 2),
-                                    arg(line, 3), arg(line, 4));
-               else if (streq(command, "release"))
-                       do_release(handle, arg(line, 1));
-               else if (streq(command, "dump"))
-                       dump(handle);
-               else if (streq(command, "sleep"))
-                       sleep(atoi(arg(line, 1)));
-               else
-                       barf("Unknown command %s", command);
-               fflush(stdout);
-               alarm(0);
-       }
+       while (fgets(line, sizeof(line), stdin))
+               do_command(0, line);
+
        return 0;
 }
diff -r ba5d5bd28edf -r b0de1894df67 tools/xenstore/xs_watch_stress.c
--- a/tools/xenstore/xs_watch_stress.c  Thu Aug  4 10:43:03 2005
+++ /dev/null   Thu Aug  4 11:39:03 2005
@@ -1,120 +0,0 @@
-/* Stress test for watch code: two processes communicating by watches */
-#include "xs.h"
-#include "utils.h"
-#include <stdlib.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-int main(int argc __attribute__((unused)), char *argv[])
-{
-       int childpid, status, fds[2];
-       bool parent;
-       unsigned int i, acks = 0;
-       struct xs_handle *h;
-       char *data;
-       unsigned int len;
-       const char *path, *otherpath;
-
-       pipe(fds);
-       childpid = fork();
-       if (childpid == -1)
-               barf_perror("Failed fork");
-       parent = (childpid != 0);
-
-       h = xs_daemon_open();
-       if (!h)
-               barf_perror("Could not connect to daemon");
-
-       if (!xs_watch(h, "/", "token", 0))
-               barf_perror("Could not set watch");
-
-       if (parent) {
-               char c;
-
-               if (read(fds[0], &c, 1) != 1)
-                       barf("Child exited");
-
-               path = "/parent";
-               otherpath = "/child";
-               /* Create initial node. */
-               if (!xs_write(h, path, "0", 2, O_CREAT))
-                       barf_perror("Write to %s failed", path);
-       } else {
-               path = "/child";
-               otherpath = "/parent";
-
-               if (write(fds[1], "", 1) != 1)
-                       barf_perror("Write to parent failed");
-       }
-
-       for (i = 0; i < (argv[1] ? (unsigned)atoi(argv[1]) : 100);) {
-               char **vec;
-
-               vec = xs_read_watch(h);
-               if (!vec)
-                       barf_perror("Read watch failed");
-
-               if (!streq(vec[1], "token"))
-                       barf("Watch token %s bad", vec[1]);
-               if (streq(vec[0], otherpath)) {
-                       char number[32];
-
-                       data = xs_read(h, otherpath, &len);
-                       if (!data)
-                               barf_perror("reading %s", otherpath);
-                       sprintf(number, "%i", atoi(data) + 1);
-                       free(data);
-                       if (!xs_write(h, path, number, strlen(number) + 1,
-                                     O_CREAT))
-                               barf_perror("writing %s", path);
-                       i++;
-               } else if (!streq(vec[0], path))
-                       barf_perror("Watch fired on unknown path %s", vec[0]);
-               xs_acknowledge_watch(h, vec[1]);
-               acks++;
-               free(vec);
-       }
-
-       if (!parent) {
-               while (acks != 2 * i - 1) {
-                       char **vec;
-                       vec = xs_read_watch(h);
-                       if (!vec)
-                               barf_perror("Watch failed");
-                       if (!streq(vec[0], path))
-                               barf_perror("Watch fired path %s", vec[0]);
-                       if (!streq(vec[1], "token"))
-                               barf("Watch token %s bad", vec[1]);
-                       free(vec);
-
-                       printf("Expect %i events, only got %i\n",
-                              2 * i - 1, acks);
-                       acks++;
-               }
-               exit(0);
-       }
-
-       if (acks != 2 * i)
-               barf("Parent got %i watch events\n", acks);
-
-       printf("Waiting for %i\n", childpid);
-       if (waitpid(childpid, &status, 0) != childpid)
-               barf_perror("Child wait failed");
-       if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
-               barf_perror("Child status %i", status);
-
-       data = xs_read(h, path, &len);
-       if (atoi(data) != 2 * (int)i)
-               barf("%s count is %s\n", path, data);
-       free(data);
-       data = xs_read(h, otherpath, &len);
-       if (atoi(data) != 2 * (int)i - 1)
-               barf("%s count is %s\n", otherpath, data);
-       free(data);
-       printf("Success!\n");
-       exit(0);
-}


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.