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

[Xen-changelog] xenstored updates.



# HG changeset patch
# User cl349@xxxxxxxxxxxxxxxxxxxx
# Node ID 57a5441b323b747b90db089c303e8e79ae7a36b0
# Parent  43f224c33281d981b08f8bd350fdd89d86ae7f38

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@xxxxxxxxxxxxxxx>
Signed-off-by: Christian Limpach <Christian.Limpach@xxxxxxxxxxxx>

diff -r 43f224c33281 -r 57a5441b323b tools/xenstore/xenstored_core.c
--- a/tools/xenstore/xenstored_core.c   Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/xenstored_core.c   Tue Jul 12 10:16:33 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))
@@ -805,7 +889,8 @@
                return send_error(conn, EINVAL);
 
        node = canonicalize(conn, vec[0]);
-       if (!within_transaction(conn->transaction, node))
+       if (/*suppress error on write outside transaction*/ 0 &&
+            !within_transaction(conn->transaction, node))
                return send_error(conn, EROFS);
 
        if (transaction_block(conn, node))
@@ -850,9 +935,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 +956,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 +987,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 +1045,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;
 }
 
@@ -1006,8 +1090,12 @@
                /* 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));
+                       return false;
+               }
 #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));
@@ -1018,9 +1106,8 @@
                        send_ack(conn, XS_DEBUG);
                        failtest = true;
                }
+#endif /* TESTING */
                return false;
-       }
-#endif /* TESTING */
 
        case XS_WATCH:
                return do_watch(conn, in);
@@ -1092,6 +1179,7 @@
                talloc_free(conn->in);
                conn->in = in;
                in = NULL;
+               trace_blocked(conn, conn->in);
        }
 
 end:
@@ -1145,6 +1233,7 @@
        if (in->used != in->hdr.msg.len)
                return;
 
+       trace_io(conn, "IN ", in);
        consider_message(conn);
        return;
 
@@ -1212,6 +1301,7 @@
 
        list_add_tail(&new->list, &connections);
        talloc_set_destructor(new, destroy_conn);
+       trace_create(new, "connection");
        return new;
 }
 
@@ -1299,6 +1389,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 +1400,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 +1410,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 43f224c33281 -r 57a5441b323b tools/xenstore/xenstored_core.h
--- a/tools/xenstore/xenstored_core.h   Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/xenstored_core.h   Tue Jul 12 10:16:33 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 43f224c33281 -r 57a5441b323b 
tools/xenstore/testsuite/10domain-homedir.sh
--- a/tools/xenstore/testsuite/10domain-homedir.sh      Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/testsuite/10domain-homedir.sh      Tue Jul 12 10:16:33 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 43f224c33281 -r 57a5441b323b tools/xenstore/xenstored_watch.h
--- a/tools/xenstore/xenstored_watch.h  Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/xenstored_watch.h  Tue Jul 12 10:16:33 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 43f224c33281 -r 57a5441b323b tools/xenstore/xenstored_transaction.h
--- a/tools/xenstore/xenstored_transaction.h    Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/xenstored_transaction.h    Tue Jul 12 10:16:33 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 43f224c33281 -r 57a5441b323b tools/xenstore/xenstored_transaction.c
--- a/tools/xenstore/xenstored_transaction.c    Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/xenstored_transaction.c    Tue Jul 12 10:16:33 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 43f224c33281 -r 57a5441b323b tools/xenstore/xs.h
--- a/tools/xenstore/xs.h       Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/xs.h       Tue Jul 12 10:16:33 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 43f224c33281 -r 57a5441b323b tools/xenstore/testsuite/07watch.sh
--- a/tools/xenstore/testsuite/07watch.sh       Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/testsuite/07watch.sh       Tue Jul 12 10:16:33 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 43f224c33281 -r 57a5441b323b tools/xenstore/xs_random.c
--- a/tools/xenstore/xs_random.c        Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/xs_random.c        Tue Jul 12 10:16:33 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)
diff -r 43f224c33281 -r 57a5441b323b tools/xenstore/testsuite/08transaction.sh
--- a/tools/xenstore/testsuite/08transaction.sh Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/testsuite/08transaction.sh Tue Jul 12 10:16:33 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 43f224c33281 -r 57a5441b323b tools/xenstore/xenstored_domain.c
--- a/tools/xenstore/xenstored_domain.c Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/xenstored_domain.c Tue Jul 12 10:16:33 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 43f224c33281 -r 57a5441b323b tools/xenstore/Makefile
--- a/tools/xenstore/Makefile   Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/Makefile   Tue Jul 12 10:16:33 2005
@@ -87,7 +87,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 43f224c33281 -r 57a5441b323b 
tools/xenstore/testsuite/06dirpermissions.sh
--- a/tools/xenstore/testsuite/06dirpermissions.sh      Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/testsuite/06dirpermissions.sh      Tue Jul 12 10:16:33 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 43f224c33281 -r 57a5441b323b tools/xenstore/testsuite/12readonly.sh
--- a/tools/xenstore/testsuite/12readonly.sh    Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/testsuite/12readonly.sh    Tue Jul 12 10:16:33 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 43f224c33281 -r 57a5441b323b 
tools/xenstore/testsuite/05filepermissions.sh
--- a/tools/xenstore/testsuite/05filepermissions.sh     Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/testsuite/05filepermissions.sh     Tue Jul 12 10:16:33 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 43f224c33281 -r 57a5441b323b tools/xenstore/utils.c
--- a/tools/xenstore/utils.c    Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/utils.c    Tue Jul 12 10:16:33 2005
@@ -13,17 +13,15 @@
 
 void xprintf(const char *fmt, ...)
 {
-        static FILE *out = NULL;
-        va_list args;
-        if (!out)
-                out = fopen("/dev/console", "w");
+       static FILE *out = NULL;
+       va_list args;
        if (!out)
                out = stderr;
 
-        va_start(args, fmt);
-        vfprintf(out, fmt, args);
-        va_end(args);
-        fflush(out);
+       va_start(args, fmt);
+       vfprintf(out, fmt, args);
+       va_end(args);
+       fflush(out);
 }
 
 void barf(const char *fmt, ...)
@@ -61,14 +59,14 @@
 
 void *_realloc_array(void *ptr, size_t size, size_t num)
 {
-        if (num >= SIZE_MAX/size)
-                return NULL;
-        return realloc_nofail(ptr, size * num);
+       if (num >= SIZE_MAX/size)
+               return NULL;
+       return realloc_nofail(ptr, size * num);
 }
 
 void *realloc_nofail(void *ptr, size_t size)
 {
-        ptr = realloc(ptr, size);
+       ptr = realloc(ptr, size);
        if (ptr)
                return ptr;
        barf("realloc of %zu failed", size);
diff -r 43f224c33281 -r 57a5441b323b tools/xenstore/xenstored_watch.c
--- a/tools/xenstore/xenstored_watch.c  Tue Jul 12 10:09:35 2005
+++ b/tools/xenstore/xenstored_watch.c  Tue Jul 12 10:16:33 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,8 @@
                                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 +314,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 +329,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 +365,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)

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


 


Rackspace

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