# HG changeset patch # User Rusty Russell <rusty@rustcorp.com.au> # Node ID bdc6aabe3e1a6f83e7119c68212c2c33a4cb851f # Parent 22d7dda0e38dc4f9436682ab6e7879e4f7ae6ace xenstored updates: --trace-file arg to xenstored for better diagnosis Inherit permissions when creating files and directories Rework watch code so it only fires on watches active when event occurs More exhaustive tests for watches, and transactions Fix watches on rm to cover deleted children Watches placed using relative path return relative path when fired Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> diff -r 22d7dda0e38d -r bdc6aabe3e1a tools/xenstore/Makefile --- a/tools/xenstore/Makefile Thu Jun 30 08:10:15 2005 +++ b/tools/xenstore/Makefile Thu Aug 4 08:58:03 2005 @@ -88,7 +88,7 @@ stresstest: xs_stress xs_watch_stress xenstored_test rm -rf $(TESTDIR)/store - export $(TESTENV); PID=`./xenstored_test --output-pid`; ./xs_stress 5000; ret=$$?; kill $$PID; exit $$ret + export $(TESTENV); PID=`./xenstored_test --output-pid --trace-file=/tmp/trace`; ./xs_stress 5000; ret=$$?; kill $$PID; exit $$ret rm -rf $(TESTDIR)/store export $(TESTENV); PID=`./xenstored_test --output-pid`; ./xs_watch_stress; ret=$$?; kill $$PID; exit $$ret diff -r 22d7dda0e38d -r bdc6aabe3e1a tools/xenstore/testsuite/05filepermissions.sh --- a/tools/xenstore/testsuite/05filepermissions.sh Thu Jun 30 08:10:15 2005 +++ b/tools/xenstore/testsuite/05filepermissions.sh Thu Aug 4 08:58:03 2005 @@ -4,17 +4,17 @@ [ "`echo -e ''getperm /test'' | ./xs_test 2>&1`" = "FATAL: getperm: No such file or directory" ] [ "`echo -e ''getperm /dir/test'' | ./xs_test 2>&1`" = "FATAL: getperm: No such file or directory" ] -# Create file: we own it, noone has access. +# Create file: inherits from root (0 READ) [ "`echo -e ''write /test excl contents'' | ./xs_test 2>&1`" = "" ] -[ "`echo -e ''getperm /test'' | ./xs_test 2>&1`" = "0 NONE" ] +[ "`echo -e ''getperm /test'' | ./xs_test 2>&1`" = "0 READ" ] +[ "`echo -e ''setid 1\ngetperm /test'' | ./xs_test 2>&1`" = "0 READ" ] +[ "`echo -e ''setid 1\nread /test'' | ./xs_test 2>&1`" = "contents" ] +[ "`echo -e ''setid 1\nwrite /test none contents2'' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ] + +# Take away read access to file. +[ "`echo -e ''setperm /test 0 NONE'' | ./xs_test 2>&1`" = "" ] [ "`echo -e ''setid 1\ngetperm /test'' | ./xs_test 2>&1`" = "FATAL: getperm: Permission denied" ] [ "`echo -e ''setid 1\nread /test'' | ./xs_test 2>&1`" = "FATAL: read: Permission denied" ] -[ "`echo -e ''setid 1\nwrite /test none contents2'' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ] - -# Grant everyone read access to file. -[ "`echo -e ''setperm /test 0 READ'' | ./xs_test 2>&1`" = "" ] -[ "`echo -e ''setid 1\ngetperm /test'' | ./xs_test 2>&1`" = "0 READ" ] -[ "`echo -e ''setid 1\nread /test'' | ./xs_test 2>&1`" = "contents" ] [ "`echo -e ''setid 1\nwrite /test none contents2'' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ] # Grant everyone write access to file. diff -r 22d7dda0e38d -r bdc6aabe3e1a tools/xenstore/testsuite/06dirpermissions.sh --- a/tools/xenstore/testsuite/06dirpermissions.sh Thu Jun 30 08:10:15 2005 +++ b/tools/xenstore/testsuite/06dirpermissions.sh Thu Aug 4 08:58:03 2005 @@ -3,17 +3,17 @@ # Root directory: owned by tool, everyone has read access. [ "`echo -e ''getperm /'' | ./xs_test 2>&1`" = "0 READ" ] -# Create directory: we own it, noone has access. +# Create directory: inherits from root. [ "`echo -e ''mkdir /dir'' | ./xs_test 2>&1`" = "" ] -[ "`echo -e ''getperm /dir'' | ./xs_test 2>&1`" = "0 NONE" ] +[ "`echo -e ''getperm /dir'' | ./xs_test 2>&1`" = "0 READ" ] +[ "`echo -e ''setid 1\ngetperm /dir'' | ./xs_test 2>&1`" = "0 READ" ] +[ "`echo -e ''setid 1\ndir /dir'' | ./xs_test 2>&1`" = "" ] +[ "`echo -e ''setid 1\nwrite /dir/test create contents2'' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ] + +# Remove everyone''s read access to directoy. +[ "`echo -e ''setperm /dir 0 NONE'' | ./xs_test 2>&1`" = "" ] [ "`echo -e ''setid 1\ndir /dir'' | ./xs_test 2>&1`" = "FATAL: dir: Permission denied" ] [ "`echo -e ''setid 1\nread /dir/test create contents2'' | ./xs_test 2>&1`" = "FATAL: read: Permission denied" ] -[ "`echo -e ''setid 1\nwrite /dir/test create contents2'' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ] - -# Grant everyone read access to directoy. -[ "`echo -e ''setperm /dir 0 READ'' | ./xs_test 2>&1`" = "" ] -[ "`echo -e ''setid 1\ngetperm /dir'' | ./xs_test 2>&1`" = "0 READ" ] -[ "`echo -e ''setid 1\ndir /dir'' | ./xs_test 2>&1`" = "" ] [ "`echo -e ''setid 1\nwrite /dir/test create contents2'' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ] # Grant everyone write access to directory. @@ -21,6 +21,8 @@ [ "`echo -e ''setid 1\ngetperm /dir'' | ./xs_test 2>&1`" = "FATAL: getperm: Permission denied" ] [ "`echo -e ''setid 1\ndir /dir'' | ./xs_test 2>&1`" = "FATAL: dir: Permission denied" ] [ "`echo -e ''setid 1\nwrite /dir/test create contents'' | ./xs_test 2>&1`" = "" ] +[ "`echo -e ''getperm /dir/test'' | ./xs_test 2>&1`" = "1 WRITE" ] +[ "`echo -e ''setperm /dir/test 0 NONE'' | ./xs_test 2>&1`" = "" ] [ "`echo -e ''read /dir/test'' | ./xs_test 2>&1`" = "contents" ] # Grant everyone both read and write access. @@ -29,6 +31,7 @@ [ "`echo -e ''setid 1\ndir /dir'' | ./xs_test 2>&1`" = "test" ] [ "`echo -e ''setid 1\nwrite /dir/test2 create contents'' | ./xs_test 2>&1`" = "" ] [ "`echo -e ''setid 1\nread /dir/test2'' | ./xs_test 2>&1`" = "contents" ] +[ "`echo -e ''setid 1\nsetperm /dir/test2 1 NONE'' | ./xs_test 2>&1`" = "" ] # Change so that user 1 owns it, noone else can do anything. [ "`echo -e ''setperm /dir 1 NONE'' | ./xs_test 2>&1`" = "" ] @@ -59,3 +62,14 @@ test3" ] [ "`echo -e ''write /dir/test4 create contents'' | ./xs_test 2>&1`" = "" ] +# Inherited by child. +[ "`echo -e ''mkdir /dir/subdir'' | ./xs_test 2>&1`" = "" ] +[ "`echo -e ''getperm /dir/subdir'' | ./xs_test 2>&1`" = "1 NONE" ] +[ "`echo -e ''write /dir/subfile excl contents'' | ./xs_test 2>&1`" = "" ] +[ "`echo -e ''getperm /dir/subfile'' | ./xs_test 2>&1`" = "1 NONE" ] + +# But for domains, they own it. +[ "`echo -e ''setperm /dir/subdir 2 READ/WRITE'' | ./xs_test 2>&1`" = "" ] +[ "`echo -e ''getperm /dir/subdir'' | ./xs_test 2>&1`" = "2 READ/WRITE" ] +[ "`echo -e ''setid 3\nwrite /dir/subdir/subfile excl contents'' | ./xs_test 2>&1`" = "" ] +[ "`echo -e ''getperm /dir/subdir/subfile'' | ./xs_test 2>&1`" = "3 READ/WRITE" ] diff -r 22d7dda0e38d -r bdc6aabe3e1a tools/xenstore/testsuite/07watch.sh --- a/tools/xenstore/testsuite/07watch.sh Thu Jun 30 08:10:15 2005 +++ b/tools/xenstore/testsuite/07watch.sh Thu Aug 4 08:58:03 2005 @@ -77,13 +77,22 @@ 1 waitwatch 1 unwatch /dir token2'' | ./xs_test 2>&1`" = "1:/dir/test2:token2" ] -# unwatch while watch pending. +# 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 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 +1 unwatch /dir token1 +1 watch /dir/test token2 0 +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 @@ -118,3 +127,28 @@ 1 waitwatch'' | ./xs_test 2>&1`" = "1:/test/subnode:token 1:/test/subnode/subnode:token 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 +1 waitwatch +1 ackwatch token +2 waitwatch'' | ./xs_test 2>&1`" = "1:/test/subnode:token +2:waitwatch timeout" ] + +# Rm fires notification on child. +[ "`echo -e ''1 watch /test/subnode token 100 +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 +2 write /test2/foo create contents2 +1 waitwatch +1 read /test2/foo +1 ackwatch token +1 waitwatch'' | ./xs_test 2>&1`" = "1:/test2/foo:token +1:contents2 +1:waitwatch timeout" ] diff -r 22d7dda0e38d -r bdc6aabe3e1a tools/xenstore/testsuite/08transaction.sh --- a/tools/xenstore/testsuite/08transaction.sh Thu Jun 30 08:10:15 2005 +++ b/tools/xenstore/testsuite/08transaction.sh Thu Aug 4 08:58:03 2005 @@ -52,3 +52,28 @@ 1 dir / 1 commit'' | ./xs_test 2>&1`" = "1:dir FATAL: 1: commit: Connection timed out" ] + +# Events inside transactions don''t trigger watches until (successful) commit. +[ "`echo -e ''1 watch / token 100 +2 start / +2 mkdir /dir/sub +1 waitwatch'' | ./xs_test 2>&1`" = "1:waitwatch timeout" ] +[ "`echo -e ''1 watch / token 100 +2 start / +2 mkdir /dir/sub +2 abort +1 waitwatch'' | ./xs_test 2>&1`" = "1:waitwatch timeout" ] +[ "`echo -e ''1 watch / token 100 +2 start / +2 mkdir /dir/sub +2 commit +1 waitwatch +1 ackwatch token'' | ./xs_test 2>&1`" = "1:/dir/sub:token" ] + +# Rm inside transaction works like rm outside: children get notified. +[ "`echo -e ''1 watch /dir/sub token 100 +2 start / +2 rm /dir +2 commit +1 waitwatch +1 ackwatch token'' | ./xs_test 2>&1`" = "1:/dir/sub:token" ] diff -r 22d7dda0e38d -r bdc6aabe3e1a tools/xenstore/testsuite/10domain-homedir.sh --- a/tools/xenstore/testsuite/10domain-homedir.sh Thu Jun 30 08:10:15 2005 +++ b/tools/xenstore/testsuite/10domain-homedir.sh Thu Aug 4 08:58:03 2005 @@ -10,3 +10,11 @@ contents entry1" ] +# 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 waitwatch +1 ackwatch token'' | ./xs_test 2>&1`" = "handle is 1 +1:foo/bar:token" ] diff -r 22d7dda0e38d -r bdc6aabe3e1a tools/xenstore/testsuite/12readonly.sh --- a/tools/xenstore/testsuite/12readonly.sh Thu Jun 30 08:10:15 2005 +++ b/tools/xenstore/testsuite/12readonly.sh Thu Aug 4 08:58:03 2005 @@ -14,7 +14,7 @@ start / abort'' | ./xs_test --readonly 2>&1`" = "test contents -0 NONE" ] +0 READ" ] # These don''t work [ "`echo ''write /test2 create contents'' | ./xs_test --readonly 2>&1`" = "FATAL: write: Read-only file system" ] diff -r 22d7dda0e38d -r bdc6aabe3e1a tools/xenstore/xenstored_core.c --- a/tools/xenstore/xenstored_core.c Thu Jun 30 08:10:15 2005 +++ b/tools/xenstore/xenstored_core.c Thu Aug 4 08:58:03 2005 @@ -52,6 +52,7 @@ static bool verbose; static LIST_HEAD(connections); +static int tracefd = -1; #ifdef TESTING static bool failtest = false; @@ -149,6 +150,86 @@ } } +static void trace_io(const struct connection *conn, + const char *prefix, + const struct buffered_data *data) +{ + char string[64]; + unsigned int i; + + if (tracefd < 0) + return; + + write(tracefd, prefix, strlen(prefix)); + sprintf(string, " %p ", conn); + write(tracefd, string, strlen(string)); + write(tracefd, sockmsg_string(data->hdr.msg.type), + strlen(sockmsg_string(data->hdr.msg.type))); + write(tracefd, " (", 2); + for (i = 0; i < data->hdr.msg.len; i++) { + if (data->buffer[i] == ''\0'') + write(tracefd, " ", 1); + else + write(tracefd, data->buffer + i, 1); + } + write(tracefd, ")\n", 2); +} + +void trace_create(const void *data, const char *type) +{ + char string[64]; + if (tracefd < 0) + return; + + write(tracefd, "CREATE ", strlen("CREATE ")); + write(tracefd, type, strlen(type)); + sprintf(string, " %p\n", data); + write(tracefd, string, strlen(string)); +} + +void trace_destroy(const void *data, const char *type) +{ + char string[64]; + if (tracefd < 0) + return; + + write(tracefd, "DESTROY ", strlen("DESTROY ")); + write(tracefd, type, strlen(type)); + sprintf(string, " %p\n", data); + write(tracefd, string, strlen(string)); +} + +void trace_watch_timeout(const struct connection *conn, const char *node, const char *token) +{ + char string[64]; + if (tracefd < 0) + return; + write(tracefd, "WATCH_TIMEOUT ", strlen("WATCH_TIMEOUT ")); + sprintf(string, " %p ", conn); + write(tracefd, string, strlen(string)); + write(tracefd, " (", 2); + write(tracefd, node, strlen(node)); + write(tracefd, " ", 1); + write(tracefd, token, strlen(token)); + write(tracefd, ")\n", 2); +} + +static void trace_blocked(const struct connection *conn, + const struct buffered_data *data) +{ + char string[64]; + + if (tracefd < 0) + return; + + write(tracefd, "BLOCKED", strlen("BLOCKED")); + sprintf(string, " %p (", conn); + write(tracefd, string, strlen(string)); + write(tracefd, sockmsg_string(data->hdr.msg.type), + strlen(sockmsg_string(data->hdr.msg.type))); + write(tracefd, ")\n", 2); +} + static bool write_message(struct connection *conn) { int ret; @@ -186,6 +267,7 @@ if (out->used != out->hdr.msg.len) return true; + trace_io(conn, "OUT", out); conn->out = NULL; talloc_free(out); @@ -213,6 +295,7 @@ close(conn->fd); } list_del(&conn->list); + trace_destroy(conn, "connection"); return 0; } @@ -756,9 +839,9 @@ static bool new_directory(struct connection *conn, const char *node, void *data, unsigned int datalen) { - struct xs_permissions perms; + struct xs_permissions *perms; char *permstr; - unsigned int len; + unsigned int num, len; int *fd; char *dir = node_dir(conn->transaction, node); @@ -768,11 +851,12 @@ /* Set destructor so we clean up if neccesary. */ talloc_set_destructor(dir, destroy_path); - /* Default permisisons: we own it, noone else has permission. */ - perms.id = conn->id; - perms.perms = XS_PERM_NONE; - - permstr = perms_to_strings(dir, &perms, 1, &len); + perms = get_perms(conn->transaction, get_parent(node), &num); + /* Domains own what they create. */ + if (conn->id) + perms->id = conn->id; + + permstr = perms_to_strings(dir, perms, num, &len); fd = talloc_open(node_permfile(conn->transaction, node), O_WRONLY|O_CREAT|O_EXCL, 0640); if (!fd || !xs_write_all(*fd, permstr, len)) @@ -850,9 +934,9 @@ commit_tempfile(tmppath); } - add_change_node(conn->transaction, node); + add_change_node(conn->transaction, node, false); send_ack(conn, XS_WRITE); - fire_watches(conn->transaction, node); + fire_watches(conn->transaction, node, false); return false; } @@ -871,9 +955,9 @@ if (!new_directory(conn, node, NULL, 0)) return send_error(conn, errno); - add_change_node(conn->transaction, node); + add_change_node(conn->transaction, node, false); send_ack(conn, XS_MKDIR); - fire_watches(conn->transaction, node); + fire_watches(conn->transaction, node, false); return false; } @@ -902,10 +986,9 @@ if (rename(path, tmppath) != 0) return send_error(conn, errno); - add_change_node(conn->transaction, node); + add_change_node(conn->transaction, node, true); send_ack(conn, XS_RM); - /* FIXME: traverse and fire watches for ALL of them! */ - fire_watches(conn->transaction, node); + fire_watches(conn->transaction, node, true); return false; } @@ -961,9 +1044,9 @@ if (!set_perms(conn->transaction, node, perms, num)) return send_error(conn, errno); - add_change_node(conn->transaction, node); + add_change_node(conn->transaction, node, false); send_ack(conn, XS_SET_PERMS); - fire_watches(conn->transaction, node); + fire_watches(conn->transaction, node, false); return false; } @@ -1092,6 +1175,7 @@ talloc_free(conn->in); conn->in = in; in = NULL; + trace_blocked(conn, conn->in); } end: @@ -1145,6 +1229,7 @@ if (in->used != in->hdr.msg.len) return; + trace_io(conn, "IN ", in); consider_message(conn); return; @@ -1212,6 +1297,7 @@ list_add_tail(&new->list, &connections); talloc_set_destructor(new, destroy_conn); + trace_create(new, "connection"); return new; } @@ -1299,6 +1385,7 @@ static struct option options[] = { { "no-fork", 0, NULL, ''N'' }, { "verbose", 0, NULL, ''V'' }, { "output-pid", 0, NULL, ''P'' }, + { "trace-file", 1, NULL, ''T'' }, { NULL, 0, NULL, 0 } }; int main(int argc, char *argv[]) @@ -1309,7 +1396,7 @@ bool dofork = true; bool outputpid = false; - while ((opt = getopt_long(argc, argv, "DV", options, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "DVT:", options, NULL)) != -1) { switch (opt) { case ''N'': dofork = false; @@ -1319,6 +1406,13 @@ break; case ''P'': outputpid = true; + break; + case ''T'': + tracefd = open(optarg, O_WRONLY|O_CREAT|O_APPEND, 0600); + if (tracefd < 0) + barf_perror("Could not open tracefile %s", + optarg); + write(tracefd, "\n***\n", strlen("\n***\n")); break; } } diff -r 22d7dda0e38d -r bdc6aabe3e1a tools/xenstore/xenstored_core.h --- a/tools/xenstore/xenstored_core.h Thu Jun 30 08:10:15 2005 +++ b/tools/xenstore/xenstored_core.h Thu Aug 4 08:58:03 2005 @@ -143,4 +143,9 @@ /* Read entire contents of a talloced fd. */ void *read_all(int *fd, unsigned int *size); +/* Tracing infrastructure. */ +void trace_create(const void *data, const char *type); +void trace_destroy(const void *data, const char *type); +void trace_watch_timeout(const struct connection *conn, const char *node, const char *token); + #endif /* _XENSTORED_CORE_H */ diff -r 22d7dda0e38d -r bdc6aabe3e1a tools/xenstore/xenstored_domain.c --- a/tools/xenstore/xenstored_domain.c Thu Jun 30 08:10:15 2005 +++ b/tools/xenstore/xenstored_domain.c Thu Aug 4 08:58:03 2005 @@ -273,7 +273,7 @@ domain = talloc(in, struct domain); domain->domid = atoi(vec[0]); domain->port = atoi(vec[2]); - if (!domain->port || !domain->domid || !is_valid_nodename(vec[3])) + if ((domain->port <= 0) || !is_valid_nodename(vec[3])) return send_error(conn, EINVAL); domain->path = talloc_strdup(domain, vec[3]); domain->page = xc_map_foreign_range(*xc_handle, domain->domid, @@ -349,7 +349,7 @@ return send_error(conn, EINVAL); domid = atoi(domid_str); - if (domid == 0) + if (domid == DOMID_SELF) domain = conn->domain; else domain = find_domain_by_domid(domid); diff -r 22d7dda0e38d -r bdc6aabe3e1a tools/xenstore/xenstored_domain.h --- a/tools/xenstore/xenstored_domain.h Thu Jun 30 08:10:15 2005 +++ b/tools/xenstore/xenstored_domain.h Thu Aug 4 08:58:03 2005 @@ -16,7 +16,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - #ifndef _XENSTORED_DOMAIN_H #define _XENSTORED_DOMAIN_H @@ -36,5 +35,4 @@ /* Returns the implicit path of a connection (only domains have this) */ const char *get_implicit_path(const struct connection *conn); - #endif /* _XENSTORED_DOMAIN_H */ diff -r 22d7dda0e38d -r bdc6aabe3e1a tools/xenstore/xenstored_transaction.c --- a/tools/xenstore/xenstored_transaction.c Thu Jun 30 08:10:15 2005 +++ b/tools/xenstore/xenstored_transaction.c Thu Aug 4 08:58:03 2005 @@ -41,6 +41,9 @@ /* The name of the node. */ char *node; + + /* And the children? (ie. rm) */ + bool recurse; }; struct transaction @@ -119,7 +122,7 @@ /* Callers get a change node (which can fail) and only commit after they''ve * finished. This way they don''t have to unwind eg. a write. */ -void add_change_node(struct transaction *trans, const char *node) +void add_change_node(struct transaction *trans, const char *node, bool recurse) { struct changed_node *i; @@ -132,7 +135,7 @@ i = talloc(trans, struct changed_node); i->node = talloc_strdup(i, node); - INIT_LIST_HEAD(&i->list); + i->recurse = recurse; list_add_tail(&i->list, &trans->changes); } @@ -176,6 +179,7 @@ struct transaction *trans = _transaction; list_del(&trans->list); + trace_destroy(trans, "transaction"); return destroy_path(trans->divert); } @@ -263,13 +267,14 @@ timerclear(&transaction->timeout); transaction->destined_to_fail = false; list_add_tail(&transaction->list, &transactions); - conn->transaction = transaction; talloc_set_destructor(transaction, destroy_transaction); + trace_create(transaction, "transaction"); if (!copy_dir(dir, transaction->divert)) return send_error(conn, errno); talloc_steal(conn, transaction); + conn->transaction = transaction; return send_ack(transaction->conn, XS_TRANSACTION_START); } @@ -292,7 +297,7 @@ /* Fire off the watches for everything that changed. */ list_for_each_entry(i, &trans->changes, list) - fire_watches(NULL, i->node); + fire_watches(NULL, i->node, i->recurse); return true; } diff -r 22d7dda0e38d -r bdc6aabe3e1a tools/xenstore/xenstored_transaction.h --- a/tools/xenstore/xenstored_transaction.h Thu Jun 30 08:10:15 2005 +++ b/tools/xenstore/xenstored_transaction.h Thu Aug 4 08:58:03 2005 @@ -40,7 +40,7 @@ char *node_dir_inside_transaction(struct transaction *t, const char *node); /* This node was changed: can fail and longjmp. */ -void add_change_node(struct transaction *trans, const char *node); +void add_change_node(struct transaction *trans, const char *node, bool recurse); /* Get shortest timeout: leave tv unset if none. */ void shortest_transaction_timeout(struct timeval *tv); diff -r 22d7dda0e38d -r bdc6aabe3e1a tools/xenstore/xenstored_watch.c --- a/tools/xenstore/xenstored_watch.c Thu Jun 30 08:10:15 2005 +++ b/tools/xenstore/xenstored_watch.c Thu Aug 4 08:58:03 2005 @@ -23,12 +23,14 @@ #include <stdlib.h> #include <sys/time.h> #include <time.h> +#include <assert.h> #include "talloc.h" #include "list.h" #include "xenstored_watch.h" #include "xs_lib.h" #include "utils.h" #include "xenstored_test.h" +#include "xenstored_domain.h" /* FIXME: time out unacked watches. */ @@ -40,13 +42,17 @@ /* The watch we are firing for (watch->events) */ struct list_head list; - /* Watch we are currently attached to. */ - struct watch *watch; + /* Watches we need to fire for (watches[0]->events == this). */ + struct watch **watches; + unsigned int num_watches; struct timeval timeout; /* Name of node which changed. */ char *node; + + /* For remove, we trigger on all the children of this node too. */ + bool recurse; }; struct watch @@ -56,6 +62,9 @@ /* Current outstanding events applying to this watch. */ struct list_head events; + + /* Is this relative to connnection''s implicit path? */ + bool relative; char *token; char *node; @@ -84,6 +93,7 @@ void queue_next_event(struct connection *conn) { struct watch_event *event; + const char *node; char *buffer; unsigned int len; @@ -107,53 +117,63 @@ /* If we decide to cancel, we will reset this. */ conn->waiting_for_ack = true; + /* If we deleted /foo and they''re watching /foo/bar, that''s what we + * tell them has changed. */ + if (!is_child(event->node, event->watches[0]->node)) { + assert(event->recurse); + node = event->watches[0]->node; + } else + node = event->node; + + /* If watch placed using relative path, give them relative answer. */ + if (event->watches[0]->relative) { + node += strlen(get_implicit_path(conn)); + if (node[0] == ''/'') /* Could be "". */ + node++; + } + /* Create reply from path and token */ - len = strlen(event->node) + 1 + strlen(event->watch->token) + 1; + len = strlen(node) + 1 + strlen(event->watches[0]->token) + 1; buffer = talloc_array(conn, char, len); - strcpy(buffer, event->node); - strcpy(buffer+strlen(event->node)+1, event->watch->token); + strcpy(buffer, node); + strcpy(buffer+strlen(node)+1, event->watches[0]->token); send_reply(conn, XS_WATCH_EVENT, buffer, len); talloc_free(buffer); } -/* Watch on DIR applies to DIR, DIR/FILE, but not DIRLONG. */ -static bool watch_applies(const struct watch *watch, const char *node) -{ - return is_child(node, watch->node); -} - -static struct watch *find_watch(const char *node) -{ - struct watch *watch; - - list_for_each_entry(watch, &watches, list) { - if (watch_applies(watch, node)) - return watch; - } - return NULL; -} - -static struct watch *find_next_watch(struct watch *watch, const char *node) -{ - list_for_each_entry_continue(watch, &watches, list) { - if (watch_applies(watch, node)) - return watch; - } - return NULL; +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; } /* FIXME: we fail to fire on out of memory. Should drop connections. */ -void fire_watches(struct transaction *trans, const char *node) -{ - struct watch *watch; - struct watch_event *event; +void fire_watches(struct transaction *trans, const char *node, bool recurse) +{ + struct watch **watches; + struct watch_event *event; + unsigned int num_watches; /* During transactions, don''t fire watches. */ if (trans) return; - watch = find_watch(node); - if (!watch) + watches = find_watches(node, recurse, &num_watches); + if (!watches) return; /* Create and fill in info about event. */ @@ -161,16 +181,19 @@ event->node = talloc_strdup(event, node); /* Tie event to this watch. */ - event->watch = watch; - list_add_tail(&event->list, &watch->events); + event->watches = watches; + talloc_steal(event, watches); + event->num_watches = num_watches; + event->recurse = recurse; + list_add_tail(&event->list, &watches[0]->events); /* Warn if not finished after thirty seconds. */ gettimeofday(&event->timeout, NULL); event->timeout.tv_sec += 30; /* If connection not doing anything, queue this. */ - if (!watch->conn->out) - queue_next_event(watch->conn); + if (!watches[0]->conn->out) + queue_next_event(watches[0]->conn); } /* We''re done with this event: see if anyone else wants it. */ @@ -178,18 +201,41 @@ { list_del(&event->list); - /* Remove from this watch, and find next watch to put this on. */ - event->watch = find_next_watch(event->watch, event->node); - if (!event->watch) { + event->num_watches--; + event->watches++; + if (!event->num_watches) { talloc_free(event); return; } - list_add_tail(&event->list, &event->watch->events); + list_add_tail(&event->list, &event->watches[0]->events); /* If connection not doing anything, queue this. */ - if (!event->watch->conn->out) - queue_next_event(event->watch->conn); + if (!event->watches[0]->conn->out) + queue_next_event(event->watches[0]->conn); +} + +static void remove_watch_from_events(struct watch *dying_watch) +{ + struct watch *watch; + struct watch_event *event; + unsigned int i; + + list_for_each_entry(watch, &watches, list) { + list_for_each_entry(event, &watch->events, list) { + for (i = 0; i < event->num_watches; i++) { + if (event->watches[i] != dying_watch) + continue; + + assert(i != 0); + memmove(event->watches+i, + event->watches+i+1, + (event->num_watches - (i+1)) + * sizeof(struct watch *)); + event->num_watches--; + } + } + } } static int destroy_watch(void *_watch) @@ -203,6 +249,11 @@ /* Remove from global list. */ list_del(&watch->list); + + /* Other events which match this watch must be cleared. */ + remove_watch_from_events(watch); + + trace_destroy(watch, "watch"); return 0; } @@ -251,6 +302,7 @@ xprintf("Warning: timeout on watch event %s" " token %s\n", i->node, watch->token); + trace_watch_timeout(watch->conn, i->node, watch->token); timerclear(&i->timeout); } } @@ -261,10 +313,12 @@ { struct watch *watch; char *vec[3]; + bool relative; if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) return send_error(conn, EINVAL); + relative = !strstarts(vec[0], "/"); vec[0] = canonicalize(conn, vec[0]); if (!check_node_perms(conn, vec[0], XS_PERM_READ)) return send_error(conn, errno); @@ -274,22 +328,27 @@ watch->token = talloc_strdup(watch, vec[1]); watch->conn = conn; watch->priority = strtoul(vec[2], NULL, 0); + watch->relative = relative; INIT_LIST_HEAD(&watch->events); insert_watch(watch); talloc_set_destructor(watch, destroy_watch); + trace_create(watch, "watch"); return send_ack(conn, XS_WATCH); } bool do_watch_ack(struct connection *conn, const char *token) { struct watch_event *event; + + if (!token) + return send_error(conn, EINVAL); if (!conn->waiting_for_ack) return send_error(conn, ENOENT); event = get_first_event(conn); - if (!streq(event->watch->token, token)) + if (!streq(event->watches[0]->token, token)) return send_error(conn, EINVAL); move_event_onwards(event); @@ -305,6 +364,9 @@ if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) return send_error(conn, EINVAL); + /* We don''t need to worry if we''re waiting for an ack for the + * watch we''re deleting: conn->waiting_for_ack was reset by + * this command in consider_message anyway. */ node = canonicalize(conn, vec[0]); list_for_each_entry(watch, &watches, list) { if (watch->conn != conn) diff -r 22d7dda0e38d -r bdc6aabe3e1a tools/xenstore/xenstored_watch.h --- a/tools/xenstore/xenstored_watch.h Thu Jun 30 08:10:15 2005 +++ b/tools/xenstore/xenstored_watch.h Thu Aug 4 08:58:03 2005 @@ -32,8 +32,8 @@ /* Look through our watches: if any of them have an event, queue it. */ void queue_next_event(struct connection *conn); -/* Fire all watches. */ -void fire_watches(struct transaction *trans, const char *node); +/* Fire all watches: recurse means all the children are effected (ie. rm) */ +void fire_watches(struct transaction *trans, 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 22d7dda0e38d -r bdc6aabe3e1a tools/xenstore/xs.h --- a/tools/xenstore/xs.h Thu Jun 30 08:10:15 2005 +++ b/tools/xenstore/xs.h Thu Aug 4 08:58:03 2005 @@ -47,7 +47,7 @@ /* Get the value of a single file, nul terminated. * Returns a malloced value: call free() on it after use. - * len indicates length in bytes, not including the nul. + * len indicates length in bytes, not including terminator. */ void *xs_read(struct xs_handle *h, const char *path, unsigned int *len); @@ -103,7 +103,7 @@ */ bool xs_acknowledge_watch(struct xs_handle *h, const char *token); -/* Remove a watch on a node. +/* Remove a watch on a node: implicitly acks any outstanding watch. * Returns false on failure (no watch on that node). */ bool xs_unwatch(struct xs_handle *h, const char *path, const char *token); diff -r 22d7dda0e38d -r bdc6aabe3e1a tools/xenstore/xs_random.c --- a/tools/xenstore/xs_random.c Thu Jun 30 08:10:15 2005 +++ b/tools/xenstore/xs_random.c Thu Aug 4 08:58:03 2005 @@ -201,7 +201,6 @@ unsigned long size; struct stat st; - /* No permfile: we didn''t bother, return defaults. */ if (lstat(filename, &st) != 0) return NULL; @@ -211,18 +210,8 @@ permfile = talloc_asprintf(path, "%s.perms", filename); perms = grab_file(permfile, &size); - if (!perms) { - ret = new(struct xs_permissions); - ret[0].id = 0; - /* Default for root is readable. */ - if (streq(path, "/")) - ret[0].perms = XS_PERM_READ; - else - ret[0].perms = XS_PERM_NONE; - *num = 1; - release_file(perms, size); - return ret; - } + if (!perms) + barf("Grabbing permissions for %s", permfile); *num = xs_count_strings(perms, size); ret = new_array(struct xs_permissions, *num); @@ -231,6 +220,39 @@ release_file(perms, size); return ret; } + +static void do_command(const char *cmd) +{ + int ret; + + ret = system(cmd); + if (ret == -1 || !WIFEXITED(ret) || WEXITSTATUS(ret) != 0) + barf_perror("Failed ''%s'': %i", cmd, ret); +} + +static void init_perms(const char *filename) +{ + struct stat st; + char *permfile, *command; + + if (lstat(filename, &st) != 0) + barf_perror("Failed to stat %s", filename); + + if (S_ISDIR(st.st_mode)) + permfile = talloc_asprintf(filename, "%s/.perms", filename); + else + permfile = talloc_asprintf(filename, "%s.perms", filename); + + /* Leave permfile if it already exists. */ + if (lstat(permfile, &st) == 0) + return; + + /* Copy permissions from parent */ + command = talloc_asprintf(filename, "cp %.*s/.perms %s", + strrchr(filename, ''/'') - filename, + filename, permfile); + do_command(command); +} static bool file_set_perms(struct file_ops_info *info, const char *path, @@ -318,6 +340,7 @@ if (write(fd, data, len) != (int)len) barf_perror("Bad write to %s", filename); + init_perms(filename); close(fd); return true; } @@ -339,16 +362,8 @@ errno = saved_errno; return false; } + init_perms(dirname); return true; -} - -static void do_command(const char *cmd) -{ - int ret; - - ret = system(cmd); - if (ret == -1 || !WIFEXITED(ret) || WEXITSTATUS(ret) != 0) - barf_perror("Failed ''%s'': %i", cmd, ret); } static bool file_rm(struct file_ops_info *info, const char *path) @@ -969,8 +984,11 @@ static void setup_file_ops(const char *dir) { + char *cmd = talloc_asprintf(NULL, "echo -n r0 > %s/.perms", dir); if (mkdir(dir, 0700) != 0) barf_perror("Creating directory %s", dir); + do_command(cmd); + talloc_free(cmd); } static void setup_xs_ops(void) -- A bad analogy is like a leaky screwdriver -- Richard Braakman _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel