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