ia64/xen-unstable

changeset 5751:57a5441b323b

xenstored updates.
- add trace file support.
- update permissions code.
- trigger watches on children of nodes being removed.
- update test cases.
Signed-off-by: Rusty Russel <rusty@rustcorp.com.au>
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Tue Jul 12 10:16:33 2005 +0000 (2005-07-12)
parents 43f224c33281
children c5db6fd54e36
files tools/xenstore/Makefile tools/xenstore/testsuite/05filepermissions.sh tools/xenstore/testsuite/06dirpermissions.sh tools/xenstore/testsuite/07watch.sh tools/xenstore/testsuite/08transaction.sh tools/xenstore/testsuite/10domain-homedir.sh tools/xenstore/testsuite/12readonly.sh tools/xenstore/utils.c tools/xenstore/xenstored_core.c tools/xenstore/xenstored_core.h tools/xenstore/xenstored_domain.c tools/xenstore/xenstored_transaction.c tools/xenstore/xenstored_transaction.h tools/xenstore/xenstored_watch.c tools/xenstore/xenstored_watch.h tools/xenstore/xs.h tools/xenstore/xs_random.c
line diff
     1.1 --- a/tools/xenstore/Makefile	Tue Jul 12 10:09:35 2005 +0000
     1.2 +++ b/tools/xenstore/Makefile	Tue Jul 12 10:16:33 2005 +0000
     1.3 @@ -87,7 +87,7 @@ randomcheck: xs_random xenstored_test
     1.4  
     1.5  stresstest: xs_stress xs_watch_stress xenstored_test
     1.6  	rm -rf $(TESTDIR)/store
     1.7 -	export $(TESTENV); PID=`./xenstored_test --output-pid`; ./xs_stress 5000; ret=$$?; kill $$PID; exit $$ret
     1.8 +	export $(TESTENV); PID=`./xenstored_test --output-pid --trace-file=/tmp/trace`; ./xs_stress 5000; ret=$$?; kill $$PID; exit $$ret
     1.9  	rm -rf $(TESTDIR)/store
    1.10  	export $(TESTENV); PID=`./xenstored_test --output-pid`; ./xs_watch_stress; ret=$$?; kill $$PID; exit $$ret
    1.11  
     2.1 --- a/tools/xenstore/testsuite/05filepermissions.sh	Tue Jul 12 10:09:35 2005 +0000
     2.2 +++ b/tools/xenstore/testsuite/05filepermissions.sh	Tue Jul 12 10:16:33 2005 +0000
     2.3 @@ -4,19 +4,19 @@
     2.4  [ "`echo -e 'getperm /test' | ./xs_test 2>&1`" = "FATAL: getperm: No such file or directory" ]
     2.5  [ "`echo -e 'getperm /dir/test' | ./xs_test 2>&1`" = "FATAL: getperm: No such file or directory" ]
     2.6  
     2.7 -# Create file: we own it, noone has access.
     2.8 +# Create file: inherits from root (0 READ)
     2.9  [ "`echo -e 'write /test excl contents' | ./xs_test 2>&1`" = "" ]
    2.10 -[ "`echo -e 'getperm /test' | ./xs_test 2>&1`" = "0 NONE" ]
    2.11 +[ "`echo -e 'getperm /test' | ./xs_test 2>&1`" = "0 READ" ]
    2.12 +[ "`echo -e 'setid 1\ngetperm /test' | ./xs_test 2>&1`" = "0 READ" ]
    2.13 +[ "`echo -e 'setid 1\nread /test' | ./xs_test 2>&1`" = "contents" ]
    2.14 +[ "`echo -e 'setid 1\nwrite /test none contents2' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
    2.15 +
    2.16 +# Take away read access to file.
    2.17 +[ "`echo -e 'setperm /test 0 NONE' | ./xs_test 2>&1`" = "" ]
    2.18  [ "`echo -e 'setid 1\ngetperm /test' | ./xs_test 2>&1`" = "FATAL: getperm: Permission denied" ]
    2.19  [ "`echo -e 'setid 1\nread /test' | ./xs_test 2>&1`" = "FATAL: read: Permission denied" ]
    2.20  [ "`echo -e 'setid 1\nwrite /test none contents2' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
    2.21  
    2.22 -# Grant everyone read access to file.
    2.23 -[ "`echo -e 'setperm /test 0 READ' | ./xs_test 2>&1`" = "" ]
    2.24 -[ "`echo -e 'setid 1\ngetperm /test' | ./xs_test 2>&1`" = "0 READ" ]
    2.25 -[ "`echo -e 'setid 1\nread /test' | ./xs_test 2>&1`" = "contents" ]
    2.26 -[ "`echo -e 'setid 1\nwrite /test none contents2' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
    2.27 -
    2.28  # Grant everyone write access to file.
    2.29  [ "`echo -e 'setperm /test 0 WRITE' | ./xs_test 2>&1`" = "" ]
    2.30  [ "`echo -e 'setid 1\ngetperm /test' | ./xs_test 2>&1`" = "FATAL: getperm: Permission denied" ]
     3.1 --- a/tools/xenstore/testsuite/06dirpermissions.sh	Tue Jul 12 10:09:35 2005 +0000
     3.2 +++ b/tools/xenstore/testsuite/06dirpermissions.sh	Tue Jul 12 10:16:33 2005 +0000
     3.3 @@ -3,24 +3,26 @@
     3.4  # Root directory: owned by tool, everyone has read access.
     3.5  [ "`echo -e 'getperm /' | ./xs_test 2>&1`" = "0 READ" ]
     3.6  
     3.7 -# Create directory: we own it, noone has access.
     3.8 +# Create directory: inherits from root.
     3.9  [ "`echo -e 'mkdir /dir' | ./xs_test 2>&1`" = "" ]
    3.10 -[ "`echo -e 'getperm /dir' | ./xs_test 2>&1`" = "0 NONE" ]
    3.11 +[ "`echo -e 'getperm /dir' | ./xs_test 2>&1`" = "0 READ" ]
    3.12 +[ "`echo -e 'setid 1\ngetperm /dir' | ./xs_test 2>&1`" = "0 READ" ]
    3.13 +[ "`echo -e 'setid 1\ndir /dir' | ./xs_test 2>&1`" = "" ]
    3.14 +[ "`echo -e 'setid 1\nwrite /dir/test create contents2' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
    3.15 +
    3.16 +# Remove everyone's read access to directoy.
    3.17 +[ "`echo -e 'setperm /dir 0 NONE' | ./xs_test 2>&1`" = "" ]
    3.18  [ "`echo -e 'setid 1\ndir /dir' | ./xs_test 2>&1`" = "FATAL: dir: Permission denied" ]
    3.19  [ "`echo -e 'setid 1\nread /dir/test create contents2' | ./xs_test 2>&1`" = "FATAL: read: Permission denied" ]
    3.20  [ "`echo -e 'setid 1\nwrite /dir/test create contents2' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
    3.21  
    3.22 -# Grant everyone read access to directoy.
    3.23 -[ "`echo -e 'setperm /dir 0 READ' | ./xs_test 2>&1`" = "" ]
    3.24 -[ "`echo -e 'setid 1\ngetperm /dir' | ./xs_test 2>&1`" = "0 READ" ]
    3.25 -[ "`echo -e 'setid 1\ndir /dir' | ./xs_test 2>&1`" = "" ]
    3.26 -[ "`echo -e 'setid 1\nwrite /dir/test create contents2' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
    3.27 -
    3.28  # Grant everyone write access to directory.
    3.29  [ "`echo -e 'setperm /dir 0 WRITE' | ./xs_test 2>&1`" = "" ]
    3.30  [ "`echo -e 'setid 1\ngetperm /dir' | ./xs_test 2>&1`" = "FATAL: getperm: Permission denied" ]
    3.31  [ "`echo -e 'setid 1\ndir /dir' | ./xs_test 2>&1`" = "FATAL: dir: Permission denied" ]
    3.32  [ "`echo -e 'setid 1\nwrite /dir/test create contents' | ./xs_test 2>&1`" = "" ]
    3.33 +[ "`echo -e 'getperm /dir/test' | ./xs_test 2>&1`" = "1 WRITE" ]
    3.34 +[ "`echo -e 'setperm /dir/test 0 NONE' | ./xs_test 2>&1`" = "" ]
    3.35  [ "`echo -e 'read /dir/test' | ./xs_test 2>&1`" = "contents" ]
    3.36  
    3.37  # Grant everyone both read and write access.
    3.38 @@ -29,6 +31,7 @@
    3.39  [ "`echo -e 'setid 1\ndir /dir' | ./xs_test 2>&1`" = "test" ]
    3.40  [ "`echo -e 'setid 1\nwrite /dir/test2 create contents' | ./xs_test 2>&1`" = "" ]
    3.41  [ "`echo -e 'setid 1\nread /dir/test2' | ./xs_test 2>&1`" = "contents" ]
    3.42 +[ "`echo -e 'setid 1\nsetperm /dir/test2 1 NONE' | ./xs_test 2>&1`" = "" ]
    3.43  
    3.44  # Change so that user 1 owns it, noone else can do anything.
    3.45  [ "`echo -e 'setperm /dir 1 NONE' | ./xs_test 2>&1`" = "" ]
    3.46 @@ -59,3 +62,14 @@ test2
    3.47  test3" ]
    3.48  [ "`echo -e 'write /dir/test4 create contents' | ./xs_test 2>&1`" = "" ]
    3.49  
    3.50 +# Inherited by child.
    3.51 +[ "`echo -e 'mkdir /dir/subdir' | ./xs_test 2>&1`" = "" ]
    3.52 +[ "`echo -e 'getperm /dir/subdir' | ./xs_test 2>&1`" = "1 NONE" ]
    3.53 +[ "`echo -e 'write /dir/subfile excl contents' | ./xs_test 2>&1`" = "" ]
    3.54 +[ "`echo -e 'getperm /dir/subfile' | ./xs_test 2>&1`" = "1 NONE" ]
    3.55 +
    3.56 +# But for domains, they own it.
    3.57 +[ "`echo -e 'setperm /dir/subdir 2 READ/WRITE' | ./xs_test 2>&1`" = "" ]
    3.58 +[ "`echo -e 'getperm /dir/subdir' | ./xs_test 2>&1`" = "2 READ/WRITE" ]
    3.59 +[ "`echo -e 'setid 3\nwrite /dir/subdir/subfile excl contents' | ./xs_test 2>&1`" = "" ]
    3.60 +[ "`echo -e 'getperm /dir/subdir/subfile' | ./xs_test 2>&1`" = "3 READ/WRITE" ]
     4.1 --- a/tools/xenstore/testsuite/07watch.sh	Tue Jul 12 10:09:35 2005 +0000
     4.2 +++ b/tools/xenstore/testsuite/07watch.sh	Tue Jul 12 10:16:33 2005 +0000
     4.3 @@ -77,7 +77,7 @@ 2 write /dir/test2 create contents
     4.4  1 waitwatch
     4.5  1 unwatch /dir token2' | ./xs_test 2>&1`" = "1:/dir/test2:token2" ]
     4.6  
     4.7 -# unwatch while watch pending.
     4.8 +# unwatch while watch pending.  Next watcher gets the event.
     4.9  [ "`echo -e '1 watch /dir token1 0
    4.10  2 watch /dir token2 1
    4.11  write /dir/test create contents
    4.12 @@ -85,6 +85,15 @@ 2 unwatch /dir token2
    4.13  1 waitwatch
    4.14  1 ackwatch token1' | ./xs_test 2>&1`" = "1:/dir/test:token1" ]
    4.15  
    4.16 +# unwatch while watch pending.  Should clear this so we get next event.
    4.17 +[ "`echo -e '1 watch /dir token1 0
    4.18 +write /dir/test create contents
    4.19 +1 unwatch /dir token1
    4.20 +1 watch /dir/test token2 0
    4.21 +write /dir/test none contents2
    4.22 +1 waitwatch
    4.23 +1 ackwatch token2' | ./xs_test 2>&1`" = "1:/dir/test:token2" ]
    4.24 +
    4.25  # check we only get notified once.
    4.26  [ "`echo -e '1 watch /test token 100
    4.27  2 write /test create contents2
    4.28 @@ -118,3 +127,28 @@ 1 ackwatch token
    4.29  1 waitwatch' | ./xs_test 2>&1`" = "1:/test/subnode:token
    4.30  1:/test/subnode/subnode:token
    4.31  1:waitwatch timeout" ]
    4.32 +
    4.33 +# Watch event must have happened before we registered interest.
    4.34 +[ "`echo -e '1 watch / token 100
    4.35 +2 write /test/subnode create contents2
    4.36 +2 watch / token2 0
    4.37 +1 waitwatch
    4.38 +1 ackwatch token
    4.39 +2 waitwatch' | ./xs_test 2>&1`" = "1:/test/subnode:token
    4.40 +2:waitwatch timeout" ]
    4.41 +
    4.42 +# Rm fires notification on child.
    4.43 +[ "`echo -e '1 watch /test/subnode token 100
    4.44 +2 rm /test
    4.45 +1 waitwatch
    4.46 +1 ackwatch token' | ./xs_test 2>&1`" = "1:/test/subnode:token" ]
    4.47 +
    4.48 +# Watch should not double-send after we ack, even if we did something in between.
    4.49 +[ "`echo -e '1 watch /test2 token 100
    4.50 +2 write /test2/foo create contents2
    4.51 +1 waitwatch
    4.52 +1 read /test2/foo
    4.53 +1 ackwatch token
    4.54 +1 waitwatch' | ./xs_test 2>&1`" = "1:/test2/foo:token
    4.55 +1:contents2
    4.56 +1:waitwatch timeout" ]
     5.1 --- a/tools/xenstore/testsuite/08transaction.sh	Tue Jul 12 10:09:35 2005 +0000
     5.2 +++ b/tools/xenstore/testsuite/08transaction.sh	Tue Jul 12 10:16:33 2005 +0000
     5.3 @@ -52,3 +52,28 @@ 1 mkdir /dir
     5.4  1 dir /
     5.5  1 commit' | ./xs_test 2>&1`" = "1:dir
     5.6  FATAL: 1: commit: Connection timed out" ]
     5.7 +
     5.8 +# Events inside transactions don't trigger watches until (successful) commit.
     5.9 +[ "`echo -e '1 watch / token 100
    5.10 +2 start /
    5.11 +2 mkdir /dir/sub
    5.12 +1 waitwatch' | ./xs_test 2>&1`" = "1:waitwatch timeout" ]
    5.13 +[ "`echo -e '1 watch / token 100
    5.14 +2 start /
    5.15 +2 mkdir /dir/sub
    5.16 +2 abort
    5.17 +1 waitwatch' | ./xs_test 2>&1`" = "1:waitwatch timeout" ]
    5.18 +[ "`echo -e '1 watch / token 100
    5.19 +2 start /
    5.20 +2 mkdir /dir/sub
    5.21 +2 commit
    5.22 +1 waitwatch
    5.23 +1 ackwatch token' | ./xs_test 2>&1`" = "1:/dir/sub:token" ]
    5.24 +
    5.25 +# Rm inside transaction works like rm outside: children get notified.
    5.26 +[ "`echo -e '1 watch /dir/sub token 100
    5.27 +2 start /
    5.28 +2 rm /dir
    5.29 +2 commit
    5.30 +1 waitwatch
    5.31 +1 ackwatch token' | ./xs_test 2>&1`" = "1:/dir/sub:token" ]
     6.1 --- a/tools/xenstore/testsuite/10domain-homedir.sh	Tue Jul 12 10:09:35 2005 +0000
     6.2 +++ b/tools/xenstore/testsuite/10domain-homedir.sh	Tue Jul 12 10:16:33 2005 +0000
     6.3 @@ -10,3 +10,11 @@ dir /home' | ./xs_test 2>&1`" = "handle 
     6.4  contents
     6.5  entry1" ]
     6.6  
     6.7 +# Place a watch using a relative path: expect relative answer.
     6.8 +[ "`echo 'introduce 1 100 7 /home
     6.9 +1 mkdir foo
    6.10 +1 watch foo token 0
    6.11 +write /home/foo/bar create contents
    6.12 +1 waitwatch
    6.13 +1 ackwatch token' | ./xs_test 2>&1`" = "handle is 1
    6.14 +1:foo/bar:token" ]
     7.1 --- a/tools/xenstore/testsuite/12readonly.sh	Tue Jul 12 10:09:35 2005 +0000
     7.2 +++ b/tools/xenstore/testsuite/12readonly.sh	Tue Jul 12 10:16:33 2005 +0000
     7.3 @@ -14,7 +14,7 @@ commit
     7.4  start /
     7.5  abort' | ./xs_test --readonly 2>&1`" = "test
     7.6  contents
     7.7 -0 NONE" ]
     7.8 +0 READ" ]
     7.9  
    7.10  # These don't work
    7.11  [ "`echo 'write /test2 create contents' | ./xs_test --readonly 2>&1`" = "FATAL: write: Read-only file system" ]
     8.1 --- a/tools/xenstore/utils.c	Tue Jul 12 10:09:35 2005 +0000
     8.2 +++ b/tools/xenstore/utils.c	Tue Jul 12 10:16:33 2005 +0000
     8.3 @@ -13,17 +13,15 @@
     8.4  
     8.5  void xprintf(const char *fmt, ...)
     8.6  {
     8.7 -        static FILE *out = NULL;
     8.8 -        va_list args;
     8.9 -        if (!out)
    8.10 -                out = fopen("/dev/console", "w");
    8.11 +	static FILE *out = NULL;
    8.12 +	va_list args;
    8.13  	if (!out)
    8.14  		out = stderr;
    8.15  
    8.16 -        va_start(args, fmt);
    8.17 -        vfprintf(out, fmt, args);
    8.18 -        va_end(args);
    8.19 -        fflush(out);
    8.20 +	va_start(args, fmt);
    8.21 +	vfprintf(out, fmt, args);
    8.22 +	va_end(args);
    8.23 +	fflush(out);
    8.24  }
    8.25  
    8.26  void barf(const char *fmt, ...)
    8.27 @@ -61,14 +59,14 @@ void barf_perror(const char *fmt, ...)
    8.28  
    8.29  void *_realloc_array(void *ptr, size_t size, size_t num)
    8.30  {
    8.31 -        if (num >= SIZE_MAX/size)
    8.32 -                return NULL;
    8.33 -        return realloc_nofail(ptr, size * num);
    8.34 +	if (num >= SIZE_MAX/size)
    8.35 +		return NULL;
    8.36 +	return realloc_nofail(ptr, size * num);
    8.37  }
    8.38  
    8.39  void *realloc_nofail(void *ptr, size_t size)
    8.40  {
    8.41 -        ptr = realloc(ptr, size);
    8.42 +	ptr = realloc(ptr, size);
    8.43  	if (ptr)
    8.44  		return ptr;
    8.45  	barf("realloc of %zu failed", size);
     9.1 --- a/tools/xenstore/xenstored_core.c	Tue Jul 12 10:09:35 2005 +0000
     9.2 +++ b/tools/xenstore/xenstored_core.c	Tue Jul 12 10:16:33 2005 +0000
     9.3 @@ -52,6 +52,7 @@
     9.4  
     9.5  static bool verbose;
     9.6  static LIST_HEAD(connections);
     9.7 +static int tracefd = -1;
     9.8  
     9.9  #ifdef TESTING
    9.10  static bool failtest = false;
    9.11 @@ -149,6 +150,86 @@ static char *sockmsg_string(enum xsd_soc
    9.12  	}
    9.13  }
    9.14  
    9.15 +static void trace_io(const struct connection *conn,
    9.16 +		     const char *prefix,
    9.17 +		     const struct buffered_data *data)
    9.18 +{
    9.19 +	char string[64];
    9.20 +	unsigned int i;
    9.21 +
    9.22 +	if (tracefd < 0)
    9.23 +		return;
    9.24 +
    9.25 +	write(tracefd, prefix, strlen(prefix));
    9.26 +	sprintf(string, " %p ", conn);
    9.27 +	write(tracefd, string, strlen(string));
    9.28 +	write(tracefd, sockmsg_string(data->hdr.msg.type),
    9.29 +	      strlen(sockmsg_string(data->hdr.msg.type)));
    9.30 +	write(tracefd, " (", 2);
    9.31 +	for (i = 0; i < data->hdr.msg.len; i++) {
    9.32 +		if (data->buffer[i] == '\0')
    9.33 +			write(tracefd, " ", 1);
    9.34 +		else
    9.35 +			write(tracefd, data->buffer + i, 1);
    9.36 +	}
    9.37 +	write(tracefd, ")\n", 2);
    9.38 +}
    9.39 +
    9.40 +void trace_create(const void *data, const char *type)
    9.41 +{
    9.42 +	char string[64];
    9.43 +	if (tracefd < 0)
    9.44 +		return;
    9.45 +
    9.46 +	write(tracefd, "CREATE ", strlen("CREATE "));
    9.47 +	write(tracefd, type, strlen(type));
    9.48 +	sprintf(string, " %p\n", data);
    9.49 +	write(tracefd, string, strlen(string));
    9.50 +}
    9.51 +
    9.52 +void trace_destroy(const void *data, const char *type)
    9.53 +{
    9.54 +	char string[64];
    9.55 +	if (tracefd < 0)
    9.56 +		return;
    9.57 +
    9.58 +	write(tracefd, "DESTROY ", strlen("DESTROY "));
    9.59 +	write(tracefd, type, strlen(type));
    9.60 +	sprintf(string, " %p\n", data);
    9.61 +	write(tracefd, string, strlen(string));
    9.62 +}
    9.63 +
    9.64 +void trace_watch_timeout(const struct connection *conn, const char *node, const char *token)
    9.65 +{
    9.66 +	char string[64];
    9.67 +	if (tracefd < 0)
    9.68 +		return;
    9.69 +	write(tracefd, "WATCH_TIMEOUT ", strlen("WATCH_TIMEOUT "));
    9.70 +	sprintf(string, " %p ", conn);
    9.71 +	write(tracefd, string, strlen(string));
    9.72 +	write(tracefd, " (", 2);
    9.73 +	write(tracefd, node, strlen(node));
    9.74 +	write(tracefd, " ", 1);
    9.75 +	write(tracefd, token, strlen(token));
    9.76 +	write(tracefd, ")\n", 2);
    9.77 +}
    9.78 +
    9.79 +static void trace_blocked(const struct connection *conn,
    9.80 +			  const struct buffered_data *data)
    9.81 +{
    9.82 +	char string[64];
    9.83 +
    9.84 +	if (tracefd < 0)
    9.85 +		return;
    9.86 +
    9.87 +	write(tracefd, "BLOCKED", strlen("BLOCKED"));
    9.88 +	sprintf(string, " %p (", conn);
    9.89 +	write(tracefd, string, strlen(string));
    9.90 +	write(tracefd, sockmsg_string(data->hdr.msg.type),
    9.91 +	      strlen(sockmsg_string(data->hdr.msg.type)));
    9.92 +	write(tracefd, ")\n", 2);
    9.93 +}
    9.94 +
    9.95  static bool write_message(struct connection *conn)
    9.96  {
    9.97  	int ret;
    9.98 @@ -186,6 +267,7 @@ static bool write_message(struct connect
    9.99  	if (out->used != out->hdr.msg.len)
   9.100  		return true;
   9.101  
   9.102 +	trace_io(conn, "OUT", out);
   9.103  	conn->out = NULL;
   9.104  	talloc_free(out);
   9.105  
   9.106 @@ -213,6 +295,7 @@ static int destroy_conn(void *_conn)
   9.107  		close(conn->fd);
   9.108  	}
   9.109  	list_del(&conn->list);
   9.110 +	trace_destroy(conn, "connection");
   9.111  	return 0;
   9.112  }
   9.113  
   9.114 @@ -756,9 +839,9 @@ static bool do_read(struct connection *c
   9.115  static bool new_directory(struct connection *conn,
   9.116  			  const char *node, void *data, unsigned int datalen)
   9.117  {
   9.118 -	struct xs_permissions perms;
   9.119 +	struct xs_permissions *perms;
   9.120  	char *permstr;
   9.121 -	unsigned int len;
   9.122 +	unsigned int num, len;
   9.123  	int *fd;
   9.124  	char *dir = node_dir(conn->transaction, node);
   9.125  
   9.126 @@ -768,11 +851,12 @@ static bool new_directory(struct connect
   9.127  	/* Set destructor so we clean up if neccesary. */
   9.128  	talloc_set_destructor(dir, destroy_path);
   9.129  
   9.130 -	/* Default permisisons: we own it, noone else has permission. */
   9.131 -	perms.id = conn->id;
   9.132 -	perms.perms = XS_PERM_NONE;
   9.133 +	perms = get_perms(conn->transaction, get_parent(node), &num);
   9.134 +	/* Domains own what they create. */
   9.135 +	if (conn->id)
   9.136 +		perms->id = conn->id;
   9.137  
   9.138 -	permstr = perms_to_strings(dir, &perms, 1, &len);
   9.139 +	permstr = perms_to_strings(dir, perms, num, &len);
   9.140  	fd = talloc_open(node_permfile(conn->transaction, node),
   9.141  			 O_WRONLY|O_CREAT|O_EXCL, 0640);
   9.142  	if (!fd || !xs_write_all(*fd, permstr, len))
   9.143 @@ -805,7 +889,8 @@ static bool do_write(struct connection *
   9.144  		return send_error(conn, EINVAL);
   9.145  
   9.146  	node = canonicalize(conn, vec[0]);
   9.147 -	if (!within_transaction(conn->transaction, node))
   9.148 +	if (/*suppress error on write outside transaction*/ 0 &&
   9.149 +            !within_transaction(conn->transaction, node))
   9.150  		return send_error(conn, EROFS);
   9.151  
   9.152  	if (transaction_block(conn, node))
   9.153 @@ -850,9 +935,9 @@ static bool do_write(struct connection *
   9.154  		commit_tempfile(tmppath);
   9.155  	}
   9.156  
   9.157 -	add_change_node(conn->transaction, node);
   9.158 +	add_change_node(conn->transaction, node, false);
   9.159  	send_ack(conn, XS_WRITE);
   9.160 -	fire_watches(conn->transaction, node);
   9.161 +	fire_watches(conn->transaction, node, false);
   9.162  	return false;
   9.163  }
   9.164  
   9.165 @@ -871,9 +956,9 @@ static bool do_mkdir(struct connection *
   9.166  	if (!new_directory(conn, node, NULL, 0))
   9.167  		return send_error(conn, errno);
   9.168  
   9.169 -	add_change_node(conn->transaction, node);
   9.170 +	add_change_node(conn->transaction, node, false);
   9.171  	send_ack(conn, XS_MKDIR);
   9.172 -	fire_watches(conn->transaction, node);
   9.173 +	fire_watches(conn->transaction, node, false);
   9.174  	return false;
   9.175  }
   9.176  
   9.177 @@ -902,10 +987,9 @@ static bool do_rm(struct connection *con
   9.178  	if (rename(path, tmppath) != 0)
   9.179  		return send_error(conn, errno);
   9.180  
   9.181 -	add_change_node(conn->transaction, node);
   9.182 +	add_change_node(conn->transaction, node, true);
   9.183  	send_ack(conn, XS_RM);
   9.184 -	/* FIXME: traverse and fire watches for ALL of them! */
   9.185 -	fire_watches(conn->transaction, node);
   9.186 +	fire_watches(conn->transaction, node, true);
   9.187  	return false;
   9.188  }
   9.189  
   9.190 @@ -961,9 +1045,9 @@ static bool do_set_perms(struct connecti
   9.191  
   9.192  	if (!set_perms(conn->transaction, node, perms, num))
   9.193  		return send_error(conn, errno);
   9.194 -	add_change_node(conn->transaction, node);
   9.195 +	add_change_node(conn->transaction, node, false);
   9.196  	send_ack(conn, XS_SET_PERMS);
   9.197 -	fire_watches(conn->transaction, node);
   9.198 +	fire_watches(conn->transaction, node, false);
   9.199  	return false;
   9.200  }
   9.201  
   9.202 @@ -1006,8 +1090,12 @@ static bool process_message(struct conne
   9.203  		/* Everything hangs off auto-free context, freed at exit. */
   9.204  		exit(0);
   9.205  
   9.206 +	case XS_DEBUG:
   9.207 +		if (streq(in->buffer, "print")) {
   9.208 +			xprintf("debug: %s", in->buffer + get_string(in, 0));
   9.209 +			return false;
   9.210 +		}
   9.211  #ifdef TESTING
   9.212 -	case XS_DEBUG: {
   9.213  		/* For testing, we allow them to set id. */
   9.214  		if (streq(in->buffer, "setid")) {
   9.215  			conn->id = atoi(in->buffer + get_string(in, 0));
   9.216 @@ -1018,9 +1106,8 @@ static bool process_message(struct conne
   9.217  			send_ack(conn, XS_DEBUG);
   9.218  			failtest = true;
   9.219  		}
   9.220 +#endif /* TESTING */
   9.221  		return false;
   9.222 -	}
   9.223 -#endif /* TESTING */
   9.224  
   9.225  	case XS_WATCH:
   9.226  		return do_watch(conn, in);
   9.227 @@ -1092,6 +1179,7 @@ static void consider_message(struct conn
   9.228  		talloc_free(conn->in);
   9.229  		conn->in = in;
   9.230  		in = NULL;
   9.231 +		trace_blocked(conn, conn->in);
   9.232  	}
   9.233  
   9.234  end:
   9.235 @@ -1145,6 +1233,7 @@ void handle_input(struct connection *con
   9.236  	if (in->used != in->hdr.msg.len)
   9.237  		return;
   9.238  
   9.239 +	trace_io(conn, "IN ", in);
   9.240  	consider_message(conn);
   9.241  	return;
   9.242  
   9.243 @@ -1212,6 +1301,7 @@ struct connection *new_connection(connwr
   9.244  
   9.245  	list_add_tail(&new->list, &connections);
   9.246  	talloc_set_destructor(new, destroy_conn);
   9.247 +	trace_create(new, "connection");
   9.248  	return new;
   9.249  }
   9.250  
   9.251 @@ -1299,6 +1389,7 @@ void dump_connection(void)
   9.252  static struct option options[] = { { "no-fork", 0, NULL, 'N' },
   9.253  				   { "verbose", 0, NULL, 'V' },
   9.254  				   { "output-pid", 0, NULL, 'P' },
   9.255 +				   { "trace-file", 1, NULL, 'T' },
   9.256  				   { NULL, 0, NULL, 0 } };
   9.257  
   9.258  int main(int argc, char *argv[])
   9.259 @@ -1309,7 +1400,7 @@ int main(int argc, char *argv[])
   9.260  	bool dofork = true;
   9.261  	bool outputpid = false;
   9.262  
   9.263 -	while ((opt = getopt_long(argc, argv, "DV", options, NULL)) != -1) {
   9.264 +	while ((opt = getopt_long(argc, argv, "DVT:", options, NULL)) != -1) {
   9.265  		switch (opt) {
   9.266  		case 'N':
   9.267  			dofork = false;
   9.268 @@ -1320,6 +1411,13 @@ int main(int argc, char *argv[])
   9.269  		case 'P':
   9.270  			outputpid = true;
   9.271  			break;
   9.272 +		case 'T':
   9.273 +			tracefd = open(optarg, O_WRONLY|O_CREAT|O_APPEND, 0600);
   9.274 +			if (tracefd < 0)
   9.275 +				barf_perror("Could not open tracefile %s",
   9.276 +					    optarg);
   9.277 +                        write(tracefd, "\n***\n", strlen("\n***\n"));
   9.278 +			break;
   9.279  		}
   9.280  	}
   9.281  	if (optind != argc)
    10.1 --- a/tools/xenstore/xenstored_core.h	Tue Jul 12 10:09:35 2005 +0000
    10.2 +++ b/tools/xenstore/xenstored_core.h	Tue Jul 12 10:16:33 2005 +0000
    10.3 @@ -143,4 +143,9 @@ int destroy_path(void *path);
    10.4  /* Read entire contents of a talloced fd. */
    10.5  void *read_all(int *fd, unsigned int *size);
    10.6  
    10.7 +/* Tracing infrastructure. */
    10.8 +void trace_create(const void *data, const char *type);
    10.9 +void trace_destroy(const void *data, const char *type);
   10.10 +void trace_watch_timeout(const struct connection *conn, const char *node, const char *token);
   10.11 +
   10.12  #endif /* _XENSTORED_CORE_H */
    11.1 --- a/tools/xenstore/xenstored_domain.c	Tue Jul 12 10:09:35 2005 +0000
    11.2 +++ b/tools/xenstore/xenstored_domain.c	Tue Jul 12 10:16:33 2005 +0000
    11.3 @@ -273,7 +273,7 @@ bool do_introduce(struct connection *con
    11.4  	domain = talloc(in, struct domain);
    11.5  	domain->domid = atoi(vec[0]);
    11.6  	domain->port = atoi(vec[2]);
    11.7 -	if (!domain->port || !domain->domid || !is_valid_nodename(vec[3]))
    11.8 +	if ((domain->port <= 0) || !is_valid_nodename(vec[3]))
    11.9  		return send_error(conn, EINVAL);
   11.10  	domain->path = talloc_strdup(domain, vec[3]);
   11.11  	domain->page = xc_map_foreign_range(*xc_handle, domain->domid,
   11.12 @@ -349,7 +349,7 @@ bool do_get_domain_path(struct connectio
   11.13  		return send_error(conn, EINVAL);
   11.14  
   11.15  	domid = atoi(domid_str);
   11.16 -	if (domid == 0)
   11.17 +	if (domid == DOMID_SELF)
   11.18  		domain = conn->domain;
   11.19  	else
   11.20  		domain = find_domain_by_domid(domid);
    12.1 --- a/tools/xenstore/xenstored_transaction.c	Tue Jul 12 10:09:35 2005 +0000
    12.2 +++ b/tools/xenstore/xenstored_transaction.c	Tue Jul 12 10:16:33 2005 +0000
    12.3 @@ -41,6 +41,9 @@ struct changed_node
    12.4  
    12.5  	/* The name of the node. */
    12.6  	char *node;
    12.7 +
    12.8 +	/* And the children? (ie. rm) */
    12.9 +	bool recurse;
   12.10  };
   12.11  
   12.12  struct transaction
   12.13 @@ -119,7 +122,7 @@ bool transaction_block(struct connection
   12.14  
   12.15  /* Callers get a change node (which can fail) and only commit after they've
   12.16   * finished.  This way they don't have to unwind eg. a write. */
   12.17 -void add_change_node(struct transaction *trans, const char *node)
   12.18 +void add_change_node(struct transaction *trans, const char *node, bool recurse)
   12.19  {
   12.20  	struct changed_node *i;
   12.21  
   12.22 @@ -132,7 +135,7 @@ void add_change_node(struct transaction 
   12.23  
   12.24  	i = talloc(trans, struct changed_node);
   12.25  	i->node = talloc_strdup(i, node);
   12.26 -	INIT_LIST_HEAD(&i->list);
   12.27 +	i->recurse = recurse;
   12.28  	list_add_tail(&i->list, &trans->changes);
   12.29  }
   12.30  
   12.31 @@ -176,6 +179,7 @@ static int destroy_transaction(void *_tr
   12.32  	struct transaction *trans = _transaction;
   12.33  
   12.34  	list_del(&trans->list);
   12.35 +	trace_destroy(trans, "transaction");
   12.36  	return destroy_path(trans->divert);
   12.37  }
   12.38  
   12.39 @@ -263,13 +267,14 @@ bool do_transaction_start(struct connect
   12.40  	timerclear(&transaction->timeout);
   12.41  	transaction->destined_to_fail = false;
   12.42  	list_add_tail(&transaction->list, &transactions);
   12.43 -	conn->transaction = transaction;
   12.44  	talloc_set_destructor(transaction, destroy_transaction);
   12.45 +	trace_create(transaction, "transaction");
   12.46  
   12.47  	if (!copy_dir(dir, transaction->divert))
   12.48  		return send_error(conn, errno);
   12.49  
   12.50  	talloc_steal(conn, transaction);
   12.51 +	conn->transaction = transaction;
   12.52  	return send_ack(transaction->conn, XS_TRANSACTION_START);
   12.53  }
   12.54  
   12.55 @@ -292,7 +297,7 @@ static bool commit_transaction(struct tr
   12.56  
   12.57  	/* Fire off the watches for everything that changed. */
   12.58  	list_for_each_entry(i, &trans->changes, list)
   12.59 -		fire_watches(NULL, i->node);
   12.60 +		fire_watches(NULL, i->node, i->recurse);
   12.61  	return true;
   12.62  }
   12.63  
    13.1 --- a/tools/xenstore/xenstored_transaction.h	Tue Jul 12 10:09:35 2005 +0000
    13.2 +++ b/tools/xenstore/xenstored_transaction.h	Tue Jul 12 10:16:33 2005 +0000
    13.3 @@ -40,7 +40,7 @@ struct transaction *transaction_covering
    13.4  char *node_dir_inside_transaction(struct transaction *t, const char *node);
    13.5  
    13.6  /* This node was changed: can fail and longjmp. */
    13.7 -void add_change_node(struct transaction *trans, const char *node);
    13.8 +void add_change_node(struct transaction *trans, const char *node, bool recurse);
    13.9  
   13.10  /* Get shortest timeout: leave tv unset if none. */
   13.11  void shortest_transaction_timeout(struct timeval *tv);
    14.1 --- a/tools/xenstore/xenstored_watch.c	Tue Jul 12 10:09:35 2005 +0000
    14.2 +++ b/tools/xenstore/xenstored_watch.c	Tue Jul 12 10:16:33 2005 +0000
    14.3 @@ -23,12 +23,14 @@
    14.4  #include <stdlib.h>
    14.5  #include <sys/time.h>
    14.6  #include <time.h>
    14.7 +#include <assert.h>
    14.8  #include "talloc.h"
    14.9  #include "list.h"
   14.10  #include "xenstored_watch.h"
   14.11  #include "xs_lib.h"
   14.12  #include "utils.h"
   14.13  #include "xenstored_test.h"
   14.14 +#include "xenstored_domain.h"
   14.15  
   14.16  /* FIXME: time out unacked watches. */
   14.17  
   14.18 @@ -40,13 +42,17 @@ struct watch_event
   14.19  	/* The watch we are firing for (watch->events) */
   14.20  	struct list_head list;
   14.21  
   14.22 -	/* Watch we are currently attached to. */
   14.23 -	struct watch *watch;
   14.24 +	/* Watches we need to fire for (watches[0]->events == this). */
   14.25 +	struct watch **watches;
   14.26 +	unsigned int num_watches;
   14.27  
   14.28  	struct timeval timeout;
   14.29  
   14.30  	/* Name of node which changed. */
   14.31  	char *node;
   14.32 +
   14.33 +	/* For remove, we trigger on all the children of this node too. */
   14.34 +	bool recurse;
   14.35  };
   14.36  
   14.37  struct watch
   14.38 @@ -57,6 +63,9 @@ struct watch
   14.39  	/* Current outstanding events applying to this watch. */
   14.40  	struct list_head events;
   14.41  
   14.42 +	/* Is this relative to connnection's implicit path? */
   14.43 +	bool relative;
   14.44 +
   14.45  	char *token;
   14.46  	char *node;
   14.47  	struct connection *conn;
   14.48 @@ -84,6 +93,7 @@ static struct watch_event *get_first_eve
   14.49  void queue_next_event(struct connection *conn)
   14.50  {
   14.51  	struct watch_event *event;
   14.52 +	const char *node;
   14.53  	char *buffer;
   14.54  	unsigned int len;
   14.55  
   14.56 @@ -107,53 +117,63 @@ void queue_next_event(struct connection 
   14.57  	/* If we decide to cancel, we will reset this. */
   14.58  	conn->waiting_for_ack = true;
   14.59  
   14.60 +	/* If we deleted /foo and they're watching /foo/bar, that's what we
   14.61 +	 * tell them has changed. */
   14.62 +	if (!is_child(event->node, event->watches[0]->node)) {
   14.63 +		assert(event->recurse);
   14.64 +		node = event->watches[0]->node;
   14.65 +	} else
   14.66 +		node = event->node;
   14.67 +
   14.68 +	/* If watch placed using relative path, give them relative answer. */
   14.69 +	if (event->watches[0]->relative) {
   14.70 +		node += strlen(get_implicit_path(conn));
   14.71 +		if (node[0] == '/') /* Could be "". */
   14.72 +			node++;
   14.73 +	}
   14.74 +
   14.75  	/* Create reply from path and token */
   14.76 -	len = strlen(event->node) + 1 + strlen(event->watch->token) + 1;
   14.77 +	len = strlen(node) + 1 + strlen(event->watches[0]->token) + 1;
   14.78  	buffer = talloc_array(conn, char, len);
   14.79 -	strcpy(buffer, event->node);
   14.80 -	strcpy(buffer+strlen(event->node)+1, event->watch->token);
   14.81 +	strcpy(buffer, node);
   14.82 +	strcpy(buffer+strlen(node)+1, event->watches[0]->token);
   14.83  	send_reply(conn, XS_WATCH_EVENT, buffer, len);
   14.84  	talloc_free(buffer);
   14.85  }
   14.86  
   14.87 -/* Watch on DIR applies to DIR, DIR/FILE, but not DIRLONG. */
   14.88 -static bool watch_applies(const struct watch *watch, const char *node)
   14.89 +static struct watch **find_watches(const char *node, bool recurse,
   14.90 +				   unsigned int *num)
   14.91  {
   14.92 -	return is_child(node, watch->node);
   14.93 -}
   14.94 +	struct watch *i;
   14.95 +	struct watch **ret = NULL;
   14.96  
   14.97 -static struct watch *find_watch(const char *node)
   14.98 -{
   14.99 -	struct watch *watch;
  14.100 +	*num = 0;
  14.101  
  14.102 -	list_for_each_entry(watch, &watches, list) {
  14.103 -		if (watch_applies(watch, node))
  14.104 -			return watch;
  14.105 +	/* We include children too if this is an rm. */
  14.106 +	list_for_each_entry(i, &watches, list) {
  14.107 +		if (is_child(node, i->node) ||
  14.108 +		    (recurse && is_child(i->node, node))) {
  14.109 +			(*num)++;
  14.110 +			ret = talloc_realloc(node, ret, struct watch *, *num);
  14.111 +			ret[*num - 1] = i;
  14.112 +		}
  14.113  	}
  14.114 -	return NULL;
  14.115 -}
  14.116 -
  14.117 -static struct watch *find_next_watch(struct watch *watch, const char *node)
  14.118 -{
  14.119 -	list_for_each_entry_continue(watch, &watches, list) {
  14.120 -		if (watch_applies(watch, node))
  14.121 -			return watch;
  14.122 -	}
  14.123 -	return NULL;
  14.124 +	return ret;
  14.125  }
  14.126  
  14.127  /* FIXME: we fail to fire on out of memory.  Should drop connections. */
  14.128 -void fire_watches(struct transaction *trans, const char *node)
  14.129 +void fire_watches(struct transaction *trans, const char *node, bool recurse)
  14.130  {
  14.131 -	struct watch *watch;
  14.132 +	struct watch **watches;
  14.133  	struct watch_event *event;
  14.134 +	unsigned int num_watches;
  14.135  
  14.136  	/* During transactions, don't fire watches. */
  14.137  	if (trans)
  14.138  		return;
  14.139  
  14.140 -	watch = find_watch(node);
  14.141 -	if (!watch)
  14.142 +	watches = find_watches(node, recurse, &num_watches);
  14.143 +	if (!watches)
  14.144  		return;
  14.145  
  14.146  	/* Create and fill in info about event. */
  14.147 @@ -161,16 +181,19 @@ void fire_watches(struct transaction *tr
  14.148  	event->node = talloc_strdup(event, node);
  14.149  
  14.150  	/* Tie event to this watch. */
  14.151 -	event->watch = watch;
  14.152 -	list_add_tail(&event->list, &watch->events);
  14.153 +	event->watches = watches;
  14.154 +	talloc_steal(event, watches);
  14.155 +	event->num_watches = num_watches;
  14.156 +	event->recurse = recurse;
  14.157 +	list_add_tail(&event->list, &watches[0]->events);
  14.158  
  14.159  	/* Warn if not finished after thirty seconds. */
  14.160  	gettimeofday(&event->timeout, NULL);
  14.161  	event->timeout.tv_sec += 30;
  14.162  
  14.163  	/* If connection not doing anything, queue this. */
  14.164 -	if (!watch->conn->out)
  14.165 -		queue_next_event(watch->conn);
  14.166 +	if (!watches[0]->conn->out)
  14.167 +		queue_next_event(watches[0]->conn);
  14.168  }
  14.169  
  14.170  /* We're done with this event: see if anyone else wants it. */
  14.171 @@ -178,18 +201,41 @@ static void move_event_onwards(struct wa
  14.172  {
  14.173  	list_del(&event->list);
  14.174  
  14.175 -	/* Remove from this watch, and find next watch to put this on. */
  14.176 -	event->watch = find_next_watch(event->watch, event->node);
  14.177 -	if (!event->watch) {
  14.178 +	event->num_watches--;
  14.179 +	event->watches++;
  14.180 +	if (!event->num_watches) {
  14.181  		talloc_free(event);
  14.182  		return;
  14.183  	}
  14.184  
  14.185 -	list_add_tail(&event->list, &event->watch->events);
  14.186 +	list_add_tail(&event->list, &event->watches[0]->events);
  14.187  
  14.188  	/* If connection not doing anything, queue this. */
  14.189 -	if (!event->watch->conn->out)
  14.190 -		queue_next_event(event->watch->conn);
  14.191 +	if (!event->watches[0]->conn->out)
  14.192 +		queue_next_event(event->watches[0]->conn);
  14.193 +}
  14.194 +
  14.195 +static void remove_watch_from_events(struct watch *dying_watch)
  14.196 +{
  14.197 +	struct watch *watch;
  14.198 +	struct watch_event *event;
  14.199 +	unsigned int i;
  14.200 +
  14.201 +	list_for_each_entry(watch, &watches, list) {
  14.202 +		list_for_each_entry(event, &watch->events, list) {
  14.203 +			for (i = 0; i < event->num_watches; i++) {
  14.204 +				if (event->watches[i] != dying_watch)
  14.205 +					continue;
  14.206 +
  14.207 +				assert(i != 0);
  14.208 +				memmove(event->watches+i,
  14.209 +					event->watches+i+1,
  14.210 +					(event->num_watches - (i+1))
  14.211 +					* sizeof(struct watch *));
  14.212 +				event->num_watches--;
  14.213 +			}
  14.214 +		}
  14.215 +	}
  14.216  }
  14.217  
  14.218  static int destroy_watch(void *_watch)
  14.219 @@ -203,6 +249,11 @@ static int destroy_watch(void *_watch)
  14.220  
  14.221  	/* Remove from global list. */
  14.222  	list_del(&watch->list);
  14.223 +
  14.224 +	/* Other events which match this watch must be cleared. */
  14.225 +	remove_watch_from_events(watch);
  14.226 +
  14.227 +	trace_destroy(watch, "watch");
  14.228  	return 0;
  14.229  }
  14.230  
  14.231 @@ -251,6 +302,8 @@ void check_watch_ack_timeout(void)
  14.232  				xprintf("Warning: timeout on watch event %s"
  14.233  					" token %s\n",
  14.234  					i->node, watch->token);
  14.235 +				trace_watch_timeout(watch->conn, i->node,
  14.236 +						    watch->token);
  14.237  				timerclear(&i->timeout);
  14.238  			}
  14.239  		}
  14.240 @@ -261,10 +314,12 @@ bool do_watch(struct connection *conn, s
  14.241  {
  14.242  	struct watch *watch;
  14.243  	char *vec[3];
  14.244 +	bool relative;
  14.245  
  14.246  	if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec))
  14.247  		return send_error(conn, EINVAL);
  14.248  
  14.249 +	relative = !strstarts(vec[0], "/");
  14.250  	vec[0] = canonicalize(conn, vec[0]);
  14.251  	if (!check_node_perms(conn, vec[0], XS_PERM_READ))
  14.252  		return send_error(conn, errno);
  14.253 @@ -274,10 +329,12 @@ bool do_watch(struct connection *conn, s
  14.254  	watch->token = talloc_strdup(watch, vec[1]);
  14.255  	watch->conn = conn;
  14.256  	watch->priority = strtoul(vec[2], NULL, 0);
  14.257 +	watch->relative = relative;
  14.258  	INIT_LIST_HEAD(&watch->events);
  14.259  
  14.260  	insert_watch(watch);
  14.261  	talloc_set_destructor(watch, destroy_watch);
  14.262 +	trace_create(watch, "watch");
  14.263  	return send_ack(conn, XS_WATCH);
  14.264  }
  14.265  
  14.266 @@ -285,11 +342,14 @@ bool do_watch_ack(struct connection *con
  14.267  {
  14.268  	struct watch_event *event;
  14.269  
  14.270 +	if (!token)
  14.271 +		return send_error(conn, EINVAL);
  14.272 +
  14.273  	if (!conn->waiting_for_ack)
  14.274  		return send_error(conn, ENOENT);
  14.275  
  14.276  	event = get_first_event(conn);
  14.277 -	if (!streq(event->watch->token, token))
  14.278 +	if (!streq(event->watches[0]->token, token))
  14.279  		return send_error(conn, EINVAL);
  14.280  
  14.281  	move_event_onwards(event);
  14.282 @@ -305,6 +365,9 @@ bool do_unwatch(struct connection *conn,
  14.283  	if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec))
  14.284  		return send_error(conn, EINVAL);
  14.285  
  14.286 +	/* We don't need to worry if we're waiting for an ack for the
  14.287 +	 * watch we're deleting: conn->waiting_for_ack was reset by
  14.288 +	 * this command in consider_message anyway. */
  14.289  	node = canonicalize(conn, vec[0]);
  14.290  	list_for_each_entry(watch, &watches, list) {
  14.291  		if (watch->conn != conn)
    15.1 --- a/tools/xenstore/xenstored_watch.h	Tue Jul 12 10:09:35 2005 +0000
    15.2 +++ b/tools/xenstore/xenstored_watch.h	Tue Jul 12 10:16:33 2005 +0000
    15.3 @@ -32,8 +32,8 @@ bool is_watch_event(struct connection *c
    15.4  /* Look through our watches: if any of them have an event, queue it. */
    15.5  void queue_next_event(struct connection *conn);
    15.6  
    15.7 -/* Fire all watches. */
    15.8 -void fire_watches(struct transaction *trans, const char *node);
    15.9 +/* Fire all watches: recurse means all the children are effected (ie. rm) */
   15.10 +void fire_watches(struct transaction *trans, const char *node, bool recurse);
   15.11  
   15.12  /* Find shortest timeout: if any, reduce tv (may already be set). */
   15.13  void shortest_watch_ack_timeout(struct timeval *tv);
    16.1 --- a/tools/xenstore/xs.h	Tue Jul 12 10:09:35 2005 +0000
    16.2 +++ b/tools/xenstore/xs.h	Tue Jul 12 10:16:33 2005 +0000
    16.3 @@ -47,7 +47,7 @@ char **xs_directory(struct xs_handle *h,
    16.4  
    16.5  /* Get the value of a single file, nul terminated.
    16.6   * Returns a malloced value: call free() on it after use.
    16.7 - * len indicates length in bytes, not including the nul.
    16.8 + * len indicates length in bytes, not including terminator.
    16.9   */
   16.10  void *xs_read(struct xs_handle *h, const char *path, unsigned int *len);
   16.11  
   16.12 @@ -103,7 +103,7 @@ char **xs_read_watch(struct xs_handle *h
   16.13   */
   16.14  bool xs_acknowledge_watch(struct xs_handle *h, const char *token);
   16.15  
   16.16 -/* Remove a watch on a node.
   16.17 +/* Remove a watch on a node: implicitly acks any outstanding watch.
   16.18   * Returns false on failure (no watch on that node).
   16.19   */
   16.20  bool xs_unwatch(struct xs_handle *h, const char *path, const char *token);
    17.1 --- a/tools/xenstore/xs_random.c	Tue Jul 12 10:09:35 2005 +0000
    17.2 +++ b/tools/xenstore/xs_random.c	Tue Jul 12 10:16:33 2005 +0000
    17.3 @@ -201,7 +201,6 @@ static struct xs_permissions *file_get_p
    17.4  	unsigned long size;
    17.5  	struct stat st;
    17.6  
    17.7 -	/* No permfile: we didn't bother, return defaults. */
    17.8  	if (lstat(filename, &st) != 0)
    17.9  		return NULL;
   17.10  
   17.11 @@ -211,18 +210,8 @@ static struct xs_permissions *file_get_p
   17.12  		permfile = talloc_asprintf(path, "%s.perms", filename);
   17.13  
   17.14  	perms = grab_file(permfile, &size);
   17.15 -	if (!perms) {
   17.16 -		ret = new(struct xs_permissions);
   17.17 -		ret[0].id = 0;
   17.18 -		/* Default for root is readable. */
   17.19 -		if (streq(path, "/"))
   17.20 -			ret[0].perms = XS_PERM_READ;
   17.21 -		else
   17.22 -			ret[0].perms = XS_PERM_NONE;
   17.23 -		*num = 1;
   17.24 -		release_file(perms, size);
   17.25 -		return ret;
   17.26 -	}
   17.27 +	if (!perms)
   17.28 +		barf("Grabbing permissions for %s", permfile);
   17.29  	*num = xs_count_strings(perms, size);
   17.30  
   17.31  	ret = new_array(struct xs_permissions, *num);
   17.32 @@ -232,6 +221,39 @@ static struct xs_permissions *file_get_p
   17.33  	return ret;
   17.34  }
   17.35  
   17.36 +static void do_command(const char *cmd)
   17.37 +{
   17.38 +	int ret;
   17.39 +
   17.40 +	ret = system(cmd);
   17.41 +	if (ret == -1 || !WIFEXITED(ret) || WEXITSTATUS(ret) != 0)
   17.42 +		barf_perror("Failed '%s': %i", cmd, ret);
   17.43 +}
   17.44 +
   17.45 +static void init_perms(const char *filename)
   17.46 +{
   17.47 +	struct stat st;
   17.48 +	char *permfile, *command;
   17.49 +
   17.50 +	if (lstat(filename, &st) != 0)
   17.51 +		barf_perror("Failed to stat %s", filename);
   17.52 +
   17.53 +	if (S_ISDIR(st.st_mode)) 
   17.54 +		permfile = talloc_asprintf(filename, "%s/.perms", filename);
   17.55 +	else
   17.56 +		permfile = talloc_asprintf(filename, "%s.perms", filename);
   17.57 +
   17.58 +	/* Leave permfile if it already exists. */
   17.59 +	if (lstat(permfile, &st) == 0)
   17.60 +		return;
   17.61 +
   17.62 +	/* Copy permissions from parent */
   17.63 +	command = talloc_asprintf(filename, "cp %.*s/.perms %s",
   17.64 +				  strrchr(filename, '/') - filename,
   17.65 +				  filename, permfile);
   17.66 +	do_command(command);
   17.67 +}	
   17.68 +
   17.69  static bool file_set_perms(struct file_ops_info *info,
   17.70  			   const char *path,
   17.71  			   struct xs_permissions *perms,
   17.72 @@ -318,6 +340,7 @@ static bool file_write(struct file_ops_i
   17.73  	if (write(fd, data, len) != (int)len)
   17.74  		barf_perror("Bad write to %s", filename);
   17.75  
   17.76 +	init_perms(filename);
   17.77  	close(fd);
   17.78  	return true;
   17.79  }
   17.80 @@ -339,18 +362,10 @@ static bool file_mkdir(struct file_ops_i
   17.81  		errno = saved_errno;
   17.82  		return false;
   17.83  	}
   17.84 +	init_perms(dirname);
   17.85  	return true;
   17.86  }
   17.87  
   17.88 -static void do_command(const char *cmd)
   17.89 -{
   17.90 -	int ret;
   17.91 -
   17.92 -	ret = system(cmd);
   17.93 -	if (ret == -1 || !WIFEXITED(ret) || WEXITSTATUS(ret) != 0)
   17.94 -		barf_perror("Failed '%s': %i", cmd, ret);
   17.95 -}
   17.96 -
   17.97  static bool file_rm(struct file_ops_info *info, const char *path)
   17.98  {
   17.99  	char *filename = path_to_name(info, path);
  17.100 @@ -969,8 +984,11 @@ static void cleanup(const char *dir)
  17.101  
  17.102  static void setup_file_ops(const char *dir)
  17.103  {
  17.104 +	char *cmd = talloc_asprintf(NULL, "echo -n r0 > %s/.perms", dir);
  17.105  	if (mkdir(dir, 0700) != 0)
  17.106  		barf_perror("Creating directory %s", dir);
  17.107 +	do_command(cmd);
  17.108 +	talloc_free(cmd);
  17.109  }
  17.110  
  17.111  static void setup_xs_ops(void)