WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-changelog

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

To: xen-changelog@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-changelog] Change watches: operations block until everyone has acked.
From: Xen patchbot -unstable <patchbot-unstable@xxxxxxxxxxxxxxxxxxx>
Date: Tue, 26 Jul 2005 20:26:12 -0400
Delivery-date: Wed, 27 Jul 2005 00:26:58 +0000
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-changelog-request@lists.xensource.com?subject=help>
List-id: BK change log <xen-changelog.lists.xensource.com>
List-post: <mailto:xen-changelog@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-changelog>, <mailto:xen-changelog-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-changelog>, <mailto:xen-changelog-request@lists.xensource.com?subject=unsubscribe>
Reply-to: xen-devel@xxxxxxxxxxxxxxxxxxx
Sender: xen-changelog-bounces@xxxxxxxxxxxxxxxxxxx
# 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

<Prev in Thread] Current Thread [Next in Thread>
  • [Xen-changelog] Change watches: operations block until everyone has acked., Xen patchbot -unstable <=