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

[Xen-changelog] Change watches: operations block until everyone has acked.



# HG changeset patch
# User cl349@xxxxxxxxxxxxxxxxxxxx
# Node ID 4e833037159dd09a62ce10011592f9f67005a7e4
# Parent  997b2b07b96df5ddc1e382460c11ba59e11ee970
Change watches: operations block until everyone has acked.
Watch events are no longer sent to self
Watches no longer take a priority
async and asyncwait commands for xs_test, now we need to continue
despite blocking ops.
Print test name at end of verbose run on failure.
Use --trace-file arg to xenstored when testing
Signed-off-by: Rusty Russel <rusty@xxxxxxxxxxxxxxx>
Signed-off-by: Christian Limpach <Christian.Limpach@xxxxxxxxxxxx>

diff -r 997b2b07b96d -r 4e833037159d tools/xenstore/TODO
--- a/tools/xenstore/TODO       Tue Jul 26 15:13:56 2005
+++ b/tools/xenstore/TODO       Tue Jul 26 15:20:09 2005
@@ -2,8 +2,9 @@
 are omissions of important but necessary things.  It is up to the
 reader to fill in the blanks.
 
-- Remove calls to system() from daemon
 - Timeout failed watch responses
-- Dynamic nodes
+- Dynamic/supply nodes
 - Persistant storage of introductions, watches and transactions, so daemon can 
restart
 - Remove assumption that rename doesn't fail
+- Multi-root transactions, for setting up front and back ends at same time.
+
diff -r 997b2b07b96d -r 4e833037159d tools/xenstore/testsuite/07watch.sh
--- a/tools/xenstore/testsuite/07watch.sh       Tue Jul 26 15:13:56 2005
+++ b/tools/xenstore/testsuite/07watch.sh       Tue Jul 26 15:20:09 2005
@@ -3,45 +3,52 @@
 # 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
-2 write /test create contents2
+[ "`echo -e '1 watch /test token
+2 async 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
-2 mkdir /dir/newdir
+[ "`echo -e '1 watch /dir token
+2 async mkdir /dir/newdir
 1 waitwatch
 1 ackwatch token
-2 setperm /dir/newdir 0 READ
+asyncwait
+2 async setperm /dir/newdir 0 READ
 1 waitwatch
 1 ackwatch token
-2 rm /dir/newdir
+asyncwait
+2 async rm /dir/newdir
 1 waitwatch
 1 ackwatch token' | ./xs_test 2>&1`" = "1:/dir/newdir:token
 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 async 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
-write /dir/test create contents
+# watch priority test: all simultaneous
+[ "`echo -e '1 watch /dir token1
+3 watch /dir token3
+2 watch /dir token2
+async write /dir/test create contents
 3 waitwatch
 3 ackwatch token3
 2 waitwatch
@@ -52,9 +59,9 @@
 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
-write /dir/test create contents
+[ "`echo -e '1 watch /dir token1
+2 watch /dir token2
+async write /dir/test create contents
 2 waitwatch
 2 close
 1 waitwatch
@@ -62,51 +69,52 @@
 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
-write /dir/test create contents
+[ "`echo -e '1 watch /dir token1
+2 watch /dir token2
+async 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
-2 write /dir/test2 create contents
+1 watch /dir token2
+2 async 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
-write /dir/test create contents
+[ "`echo -e '1 watch /dir token1
+2 watch /dir token2
+async 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
-write /dir/test create contents
+[ "`echo -e '1 watch /dir token1
+async write /dir/test create contents
 1 unwatch /dir token1
-1 watch /dir/test token2 0
-write /dir/test none contents2
+1 watch /dir/test token2
+asyncwait
+async 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
-2 write /test create contents2
+[ "`echo -e '1 watch /test token
+2 async write /test create contents2
 1 waitwatch
 1 ackwatch token
 1 waitwatch' | ./xs_test 2>&1`" = "1:/test:token
 1:waitwatch timeout" ]
 
 # watches are queued in order.
-[ "`echo -e '1 watch / token 100
-2 write /test1 create contents
-2 write /test2 create contents
-2 write /test3 create contents
+[ "`echo -e '1 watch / token
+async 2 write /test1 create contents
+async 2 write /test2 create contents
+async 2 write /test3 create contents
 1 waitwatch
 1 ackwatch token
 1 waitwatch
@@ -117,9 +125,9 @@
 1:/test3:token" ]
 
 # Creation of subpaths should be covered correctly.
-[ "`echo -e '1 watch / token 100
-2 write /test/subnode create contents2
-2 write /test/subnode/subnode create contents2
+[ "`echo -e '1 watch / token
+2 async write /test/subnode create contents2
+2 async write /test/subnode/subnode create contents2
 1 waitwatch
 1 ackwatch token
 1 waitwatch
@@ -129,23 +137,23 @@
 1:waitwatch timeout" ]
 
 # Watch event must have happened before we registered interest.
-[ "`echo -e '1 watch / token 100
-2 write /test/subnode create contents2
-2 watch / token2 0
+[ "`echo -e '1 watch / token
+2 async write /test/subnode create contents2
+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
-2 rm /test
+[ "`echo -e '1 watch /test/subnode token
+2 async 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
-2 write /test2/foo create contents2
+[ "`echo -e '1 watch /test2 token
+2 async write /test2/foo create contents2
 1 waitwatch
 1 read /test2/foo
 1 ackwatch token
diff -r 997b2b07b96d -r 4e833037159d tools/xenstore/testsuite/08transaction.sh
--- a/tools/xenstore/testsuite/08transaction.sh Tue Jul 26 15:13:56 2005
+++ b/tools/xenstore/testsuite/08transaction.sh Tue Jul 26 15:20:09 2005
@@ -45,37 +45,37 @@
 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
+2 async commit
 1 waitwatch
 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
+2 async commit
 1 waitwatch
 1 ackwatch token' | ./xs_test 2>&1`" = "1:/test/dir/sub:token" ]
diff -r 997b2b07b96d -r 4e833037159d 
tools/xenstore/testsuite/10domain-homedir.sh
--- a/tools/xenstore/testsuite/10domain-homedir.sh      Tue Jul 26 15:13:56 2005
+++ b/tools/xenstore/testsuite/10domain-homedir.sh      Tue Jul 26 15:20:09 2005
@@ -13,8 +13,8 @@
 # Place a watch using a relative path: expect relative answer.
 [ "`echo 'introduce 1 100 7 /home
 1 mkdir foo
-1 watch foo token 0
-write /home/foo/bar create contents
+1 watch foo token
+async write /home/foo/bar create contents
 1 waitwatch
 1 ackwatch token' | ./xs_test 2>&1`" = "handle is 1
 1:foo/bar:token" ]
diff -r 997b2b07b96d -r 4e833037159d tools/xenstore/testsuite/11domain-watch.sh
--- a/tools/xenstore/testsuite/11domain-watch.sh        Tue Jul 26 15:13:56 2005
+++ b/tools/xenstore/testsuite/11domain-watch.sh        Tue Jul 26 15:20:09 2005
@@ -6,42 +6,46 @@
 [ "`echo -e 'mkdir /dir' | ./xs_test 2>&1`" = "" ]
 
 [ "`echo -e 'introduce 1 100 7 /my/home
-1 watch /test token 100
-write /test create contents2
+1 watch /test token
+async write /test create contents2
 1 waitwatch
 1 ackwatch token
 1 unwatch /test token
+asyncwait
 release 1' | ./xs_test 2>&1`" = "handle is 1
 1:/test:token" ]
 
 # 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 read /dir/test
+1 watch /dir token
+async write /dir/test create contents
+1 write /dir/test2 create contents2
+1 write /dir/test3 create contents3
+1 write /dir/test4 create contents4
 1 waitwatch
 1 ackwatch token
+asyncwait
 release 1' | ./xs_test 2>&1`" = "handle is 1
-1:contents
 1:/dir/test:token" ]
 
 # 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
-2 write /dir/test2 create contents
+1 watch /dir token2
+async 2 write /dir/test2 create contents
 1 waitwatch
 1 unwatch /dir token2
+asyncwait
 release 1' | ./xs_test 2>&1`" = "handle is 1
 1:/dir/test2:token2" ]
 
 # 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
-write /dir/test create contents
+1 watch /dir token1
+2 watch /dir token2
+3 async write /dir/test create contents
 2 unwatch /dir token2
 1 waitwatch
 1 ackwatch token1
diff -r 997b2b07b96d -r 4e833037159d tools/xenstore/testsuite/12readonly.sh
--- a/tools/xenstore/testsuite/12readonly.sh    Tue Jul 26 15:13:56 2005
+++ b/tools/xenstore/testsuite/12readonly.sh    Tue Jul 26 15:20:09 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" ] &
 
@@ -36,6 +36,3 @@
     echo Readonly wait test failed: $?
     exit 1
 fi
-    
-    
-
diff -r 997b2b07b96d -r 4e833037159d tools/xenstore/testsuite/13watch-ack.sh
--- a/tools/xenstore/testsuite/13watch-ack.sh   Tue Jul 26 15:13:56 2005
+++ b/tools/xenstore/testsuite/13watch-ack.sh   Tue Jul 26 15:20:09 2005
@@ -15,8 +15,9 @@
 [ "`echo '1 watch /test/1 token1 0
 1 watch /test/2 token2 0
 1 watch /test/3 token3 0
-2 write /test/2 create contents2
+2 async write /test/2 create contents2
 1 waitwatch
-2 write /test/1 create contents1
-2 write /test/3 create contents3
-1 ackwatch token2' | ./xs_test 2>&1`" = "1:/test/2:token2" ]
+3 async write /test/1 create contents1
+4 async write /test/3 create contents3
+1 ackwatch token2
+1 close' | ./xs_test 2>&1`" = "1:/test/2:token2" ]
diff -r 997b2b07b96d -r 4e833037159d tools/xenstore/testsuite/test.sh
--- a/tools/xenstore/testsuite/test.sh  Tue Jul 26 15:13:56 2005
+++ b/tools/xenstore/testsuite/test.sh  Tue Jul 26 15:20:09 2005
@@ -9,7 +9,7 @@
     mkdir $XENSTORED_ROOTDIR
 # Weird failures with this.
     if type valgrind >/dev/null 2>&1; then
-       valgrind -q --logfile-fd=3 ./xenstored_test --output-pid --no-fork 
3>testsuite/tmp/vgout > /tmp/pid 2> testsuite/tmp/xenstored_errors &
+       valgrind -q --logfile-fd=3 ./xenstored_test --output-pid 
--trace-file=testsuite/tmp/trace --no-fork 3>testsuite/tmp/vgout > /tmp/pid 2> 
testsuite/tmp/xenstored_errors &
        while [ ! -s /tmp/pid ]; do sleep 0; done
        PID=`cat /tmp/pid`
        rm /tmp/pid
@@ -38,7 +38,9 @@
        echo Test $f passed...
     else
        echo Test $f failed, running verbosely...
-       run_test $f -x
+       run_test $f -x || true
+       # That will have filled the screen, repeat message.
+       echo Test $f failed
        exit 1
     fi
 done
diff -r 997b2b07b96d -r 4e833037159d tools/xenstore/xenstored_core.c
--- a/tools/xenstore/xenstored_core.c   Tue Jul 26 15:13:56 2005
+++ b/tools/xenstore/xenstored_core.c   Tue Jul 26 15:20:09 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
@@ -959,8 +959,11 @@
        }
 
        add_change_node(conn->transaction, node, false);
+       if (fire_watches(conn, node, false)) {
+               conn->watch_ack = XS_WRITE;
+               return;
+       }
        send_ack(conn, XS_WRITE);
-       fire_watches(conn->transaction, node, false);
 }
 
 static void do_mkdir(struct connection *conn, const char *node)
@@ -985,8 +988,11 @@
        }
 
        add_change_node(conn->transaction, node, false);
+       if (fire_watches(conn, node, false)) {
+               conn->watch_ack = XS_MKDIR;
+               return;
+       }
        send_ack(conn, XS_MKDIR);
-       fire_watches(conn->transaction, node, false);
 }
 
 static void do_rm(struct connection *conn, const char *node)
@@ -1023,8 +1029,11 @@
        }
 
        add_change_node(conn->transaction, node, true);
+       if (fire_watches(conn, node, true)) {
+               conn->watch_ack = XS_RM;
+               return;
+       }
        send_ack(conn, XS_RM);
-       fire_watches(conn->transaction, node, true);
 }
 
 static void do_get_perms(struct connection *conn, const char *node)
@@ -1095,8 +1104,11 @@
        }
 
        add_change_node(conn->transaction, node, false);
+       if (fire_watches(conn, node, false)) {
+               conn->watch_ack = XS_SET_PERMS;
+               return;
+       }
        send_ack(conn, XS_SET_PERMS);
-       fire_watches(conn->transaction, node, false);
 }
 
 /* Process "in" for conn: "in" will vanish after this conversation, so
@@ -1321,14 +1333,23 @@
        struct connection *i, *tmp;
 
        list_for_each_entry_safe(i, tmp, &connections, list) {
-               if (i->state == OK)
-                       continue;
-
-               if (!transaction_covering_node(i->blocked_by)) {
-                       talloc_free(i->blocked_by);
-                       i->blocked_by = NULL;
-                       i->state = OK;
-                       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 WATCHED:
+                       if (i->watches_unacked == 0) {
+                               i->state = OK;
+                               send_ack(i, i->watch_ack);
+                       }
+                       break;
+               case OK:
+                       break;
                }
        }
 
@@ -1351,6 +1372,8 @@
 
        new->state = OK;
        new->blocked_by = NULL;
+       new->watch_ack = XS_ERROR;
+       new->watches_unacked = 0;
        new->out = new->waiting_reply = NULL;
        new->fd = -1;
        new->id = 0;
@@ -1359,6 +1382,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)) {
@@ -1430,13 +1454,12 @@
                printf("    state = %s\n",
                       i->state == OK ? "OK"
                       : i->state == BLOCKED ? "BLOCKED"
+                      : i->state == WATCHED ? "WATCHED"
                       : "INVALID");
                if (i->id)
                        printf("    id = %i\n", i->id);
                if (i->blocked_by)
                        printf("    blocked on = %s\n", i->blocked_by);
-               if (i->waiting_for_ack)
-                       printf("    waiting_for_ack TRUE\n");
                if (!i->in->inhdr || i->in->used)
                        printf("    got %i bytes of %s\n",
                               i->in->used, i->in->inhdr ? "header" : "data");
diff -r 997b2b07b96d -r 4e833037159d tools/xenstore/xenstored_core.h
--- a/tools/xenstore/xenstored_core.h   Tue Jul 26 15:13:56 2005
+++ b/tools/xenstore/xenstored_core.h   Tue Jul 26 15:20:09 2005
@@ -51,6 +51,8 @@
 {
        /* Blocked by transaction. */
        BLOCKED,
+       /* Waiting for watchers to ack event we caused */
+       WATCHED,
        /* Completed */
        OK,
 };
@@ -70,6 +72,12 @@
 
        /* Node we are waiting for (if state == BLOCKED) */
        char *blocked_by;
+
+       /* Are we waiting for watches to be acked from an event we caused? */
+       unsigned int watches_unacked;
+
+       /* Type of ack to send once watches fired. */
+       enum xsd_sockmsg_type watch_ack;
 
        /* Is this a read-only connection? */
        bool can_write;
@@ -92,10 +100,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,
diff -r 997b2b07b96d -r 4e833037159d tools/xenstore/xenstored_transaction.c
--- a/tools/xenstore/xenstored_transaction.c    Tue Jul 26 15:13:56 2005
+++ b/tools/xenstore/xenstored_transaction.c    Tue Jul 26 15:20:09 2005
@@ -288,7 +288,6 @@
 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);
@@ -301,15 +300,15 @@
                        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;
 }
 
 void do_transaction_end(struct connection *conn, const char *arg)
 {
+       struct changed_node *i;
+       struct transaction *trans;
+       bool fired = false;
+
        if (!arg || (!streq(arg, "T") && !streq(arg, "F"))) {
                send_error(conn, EINVAL);
                return;
@@ -320,24 +319,30 @@
                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;
-       send_ack(conn, XS_TRANSACTION_END);
-       return;
-
-failed:
-       talloc_free(conn->transaction);
-       conn->transaction = NULL;
-}
-
+
+               /* Fire off the watches for everything that changed. */
+               list_for_each_entry(i, &trans->changes, list)
+                       fired |= fire_watches(conn, i->node, i->recurse);
+       }
+
+       if (fired)
+               conn->watch_ack = XS_TRANSACTION_END;
+       else
+               send_ack(conn, XS_TRANSACTION_END);
+}
+
diff -r 997b2b07b96d -r 4e833037159d tools/xenstore/xenstored_watch.c
--- a/tools/xenstore/xenstored_watch.c  Tue Jul 26 15:13:56 2005
+++ b/tools/xenstore/xenstored_watch.c  Tue Jul 26 15:20:09 2005
@@ -33,69 +33,39 @@
 #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;
+
+       /* Connection which caused watch event (which we are blocking) */
+       struct connection *cause;
 };
 
 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 +80,93 @@
        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");
+       assert(event->cause->watches_unacked != 0);
+       /* If it hits zero, will unblock in unblock_connections. */
+       event->cause->watches_unacked--;
+       return 0;
+}
+
+static void add_event(struct connection *cause, 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);
+       event->cause = cause;
+       cause->watches_unacked++;
+       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;
+bool 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 false;
+
+       assert(conn->state == OK);
+
+       /* 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(conn, watch, node);
+                       else if (recurse && is_child(watch->node, node))
+                               add_event(conn, watch, watch->node);
+                       else
+                               continue;
+                       conn->state = WATCHED;
+                       /* If connection not doing anything, queue this. */
+                       if (!i->out)
+                               queue_next_event(i);
+               }
+       }
+       return conn->state == WATCHED;
 }
 
 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 +178,12 @@
                                *tv = i->timeout;
                }
        }
+#endif
 }      
 
 void check_watch_ack_timeout(void)
 {
+#if 0
        struct watch *watch;
        struct timeval now;
 
@@ -308,12 +203,13 @@
                        }
                }
        }
+#endif
 }
 
 void do_watch(struct connection *conn, struct buffered_data *in)
 {
        struct watch *watch;
-       char *vec[3];
+       char *vec[2];
        bool relative;
 
        if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) {
@@ -331,14 +227,16 @@
        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");
        send_ack(conn, XS_WATCH);
 }
 
@@ -356,9 +254,6 @@
                return;
        }
 
-       event = list_top(&conn->waiting_for_ack->events,
-                        struct watch_event, list);
-       assert(event->watches[0] == conn->waiting_for_ack);
        if (!streq(conn->waiting_for_ack->token, token)) {
                /* They're confused: this will cause us to send event again */
                conn->waiting_for_ack = NULL;
@@ -366,7 +261,12 @@
                return;
        }
 
-       move_event_onwards(event);
+       /* 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;
        send_ack(conn, XS_WATCH_ACK);
 }
@@ -385,11 +285,9 @@
         * 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);
                        send_ack(conn, XS_UNWATCH);
                        return;
@@ -404,15 +302,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 997b2b07b96d -r 4e833037159d tools/xenstore/xenstored_watch.h
--- a/tools/xenstore/xenstored_watch.h  Tue Jul 26 15:13:56 2005
+++ b/tools/xenstore/xenstored_watch.h  Tue Jul 26 15:20:09 2005
@@ -32,8 +32,10 @@
 /* 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).
+ * Returns true if there were any, meaning connection has to wait.
+ */
+bool 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 997b2b07b96d -r 4e833037159d tools/xenstore/xs.c
--- a/tools/xenstore/xs.c       Tue Jul 26 15:13:56 2005
+++ b/tools/xenstore/xs.c       Tue Jul 26 15:20:09 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 997b2b07b96d -r 4e833037159d tools/xenstore/xs.h
--- a/tools/xenstore/xs.h       Tue Jul 26 15:13:56 2005
+++ b/tools/xenstore/xs.h       Tue Jul 26 15:20:09 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 997b2b07b96d -r 4e833037159d tools/xenstore/xs_test.c
--- a/tools/xenstore/xs_test.c  Tue Jul 26 15:13:56 2005
+++ b/tools/xenstore/xs_test.c  Tue Jul 26 15:20:09 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,10 @@
 #define XSTEST
 
 static struct xs_handle *handles[10] = { NULL };
+static unsigned int children;
+
+static bool timeout = true;
+static bool readonly = false;
 
 struct ringbuf_head
 {
@@ -173,7 +178,9 @@
             "  getperm <path>\n"
             "  setperm <path> <id> <flags> ...\n"
             "  shutdown\n"
-            "  watch <path> <token> <prio>\n"
+            "  watch <path> <token>\n"
+            "  async <command>...\n"
+            "  asyncwait\n"
             "  waitwatch\n"
             "  ackwatch <token>\n"
             "  unwatch <path> <token>\n"
@@ -186,22 +193,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];
 }
@@ -360,10 +379,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);
 }
 
@@ -386,6 +404,47 @@
 {
        if (!xs_acknowledge_watch(handles[handle], token))
                failed(handle);
+}
+
+/* Async wait for watch on handle */
+static void do_command(unsigned int default_handle, char *line);
+static void do_async(unsigned int handle, char *line)
+{
+       int child;
+       unsigned int i;
+       children++;
+       if ((child = fork()) != 0)
+               return;
+
+       /* Don't keep other handles open in parent. */
+       for (i = 0; i < ARRAY_SIZE(handles); i++) {
+               if (handles[i] && i != handle) {
+                       xs_daemon_close(handles[i]);
+                       handles[i] = NULL;
+               }
+       }
+
+       do_command(handle, line + argpos(line, 1));
+       exit(0);
+}
+
+static void do_asyncwait(unsigned int handle)
+{
+       int status;
+
+       if (handle)
+               barf("handle has no meaning with asyncwait");
+
+       if (children == 0)
+               barf("No children to wait for!");
+
+       if (waitpid(0, &status, 0) > 0) {
+               if (!WIFEXITED(status))
+                       barf("async died");
+               if (WEXITSTATUS(status))
+                       exit(WEXITSTATUS(status));
+       }
+       children--;
 }
 
 static void do_unwatch(unsigned int handle, const char *node, const char 
*token)
@@ -533,23 +592,106 @@
        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(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));
+       else if (streq(command, "waitwatch"))
+               do_waitwatch(handle);
+       else if (streq(command, "async"))
+               do_async(handle, line);
+       else if (streq(command, "asyncwait"))
+               do_asyncwait(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;
@@ -557,7 +699,7 @@
                argv++;
        }
 
-       if (argc > 1 && streq(argv[1], "--notimeout")) {
+       if (argc > 1 && streq(argv[1], "--no-timeout")) {
                timeout = false;
                argc--;
                argv++;
@@ -570,81 +712,10 @@
        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);
+
+       while (children)
+               do_asyncwait(0);
        return 0;
 }

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


 


Rackspace

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