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>
This commit is contained in:
cl349@firebug.cl.cam.ac.uk committed 2005-07-12 10:16:33 +00:00
1 parent 247bc37e83
commit 0f36f0e095
17 files changed
+392 -124

No files matched your search

+1 -1
View File
@@ -87,7 +87,7 @@ randomcheck: xs_random xenstored_test
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
@@ -4,19 +4,19 @@
[ "`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 '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 '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 write access to file.
[ "`echo -e 'setperm /test 0 WRITE' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'setid 1\ngetperm /test' | ./xs_test 2>&1`" = "FATAL: getperm: Permission denied" ]
+22 -8
View File
@@ -3,24 +3,26 @@
# 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 '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 '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 write access to directory.
[ "`echo -e 'setperm /dir 0 WRITE' | ./xs_test 2>&1`" = "" ]
[ "`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 @@ test2
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" ]
+35 -1
View File
@@ -77,7 +77,7 @@ write /dir/test create contents
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
@@ -85,6 +85,15 @@ write /dir/test create contents
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
2 write /test create contents2
@@ -118,3 +127,28 @@ write /dir/test create contents
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" ]
+25
View File
@@ -52,3 +52,28 @@ dir /' | ./xs_test`" = "" ]
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" ]
@@ -10,3 +10,11 @@ dir /home' | ./xs_test 2>&1`" = "handle is 1
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" ]
+1 -1
View File
@@ -14,7 +14,7 @@ commit
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" ]
+10 -12
View File
@@ -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 barf_perror(const char *fmt, ...)
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);
+118 -20
View File
@@ -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 char *sockmsg_string(enum xsd_sockmsg_type type)
}
}
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 @@ static bool write_message(struct connection *conn)
if (out->used != out->hdr.msg.len)
return true;
trace_io(conn, "OUT", out);
conn->out = NULL;
talloc_free(out);
@@ -213,6 +295,7 @@ static int destroy_conn(void *_conn)
close(conn->fd);
}
list_del(&conn->list);
trace_destroy(conn, "connection");
return 0;
}
@@ -756,9 +839,9 @@ static bool do_read(struct connection *conn, const char *node)
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 @@ static bool new_directory(struct connection *conn,
/* 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;
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, 1, &len);
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 @@ static bool do_write(struct connection *conn, struct buffered_data *in)
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 @@ static bool do_write(struct connection *conn, struct buffered_data *in)
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 @@ static bool do_mkdir(struct connection *conn, const char *node)
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 @@ static bool do_rm(struct connection *conn, const char *node)
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 @@ static bool do_set_perms(struct connection *conn, struct buffered_data *in)
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 @@ static bool process_message(struct connection *conn, struct buffered_data *in)
/* 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 @@ static bool process_message(struct connection *conn, struct buffered_data *in)
send_ack(conn, XS_DEBUG);
failtest = true;
}
return false;
}
#endif /* TESTING */
return false;
case XS_WATCH:
return do_watch(conn, in);
@@ -1092,6 +1179,7 @@ static void consider_message(struct connection *conn)
talloc_free(conn->in);
conn->in = in;
in = NULL;
trace_blocked(conn, conn->in);
}
end:
@@ -1145,6 +1233,7 @@ void handle_input(struct connection *conn)
if (in->used != in->hdr.msg.len)
return;
trace_io(conn, "IN ", in);
consider_message(conn);
return;
@@ -1212,6 +1301,7 @@ struct connection *new_connection(connwritefn_t *write, connreadfn_t *read)
list_add_tail(&new->list, &connections);
talloc_set_destructor(new, destroy_conn);
trace_create(new, "connection");
return new;
}
@@ -1299,6 +1389,7 @@ void dump_connection(void)
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 @@ int main(int argc, char *argv[])
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;
@@ -1320,6 +1411,13 @@ int main(int argc, char *argv[])
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;
}
}
if (optind != argc)
+5
View File
@@ -143,4 +143,9 @@ int destroy_path(void *path);
/* 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 */
+2 -2
View File
@@ -273,7 +273,7 @@ bool do_introduce(struct connection *conn, struct buffered_data *in)
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 @@ bool do_get_domain_path(struct connection *conn, const char *domid_str)
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);
+9 -4
View File
@@ -41,6 +41,9 @@ struct changed_node
/* The name of the node. */
char *node;
/* And the children? (ie. rm) */
bool recurse;
};
struct transaction
@@ -119,7 +122,7 @@ bool transaction_block(struct connection *conn, const char *node)
/* 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 @@ void add_change_node(struct transaction *trans, const char *node)
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 @@ static int destroy_transaction(void *_transaction)
struct transaction *trans = _transaction;
list_del(&trans->list);
trace_destroy(trans, "transaction");
return destroy_path(trans->divert);
}
@@ -263,13 +267,14 @@ bool do_transaction_start(struct connection *conn, const char *node)
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 @@ static bool commit_transaction(struct transaction *trans)
/* 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;
}
+1 -1
View File
@@ -40,7 +40,7 @@ struct transaction *transaction_covering_node(const char *node);
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);
+103 -40
View File
@@ -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 @@ struct watch_event
/* 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
@@ -57,6 +63,9 @@ struct watch
/* 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;
struct connection *conn;
@@ -84,6 +93,7 @@ static struct watch_event *get_first_event(struct connection *conn)
void queue_next_event(struct connection *conn)
{
struct watch_event *event;
const char *node;
char *buffer;
unsigned int len;
@@ -107,53 +117,63 @@ void queue_next_event(struct connection *conn)
/* 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)
static struct watch **find_watches(const char *node, bool recurse,
unsigned int *num)
{
return is_child(node, watch->node);
}
struct watch *i;
struct watch **ret = NULL;
static struct watch *find_watch(const char *node)
{
struct watch *watch;
*num = 0;
list_for_each_entry(watch, &watches, list) {
if (watch_applies(watch, node))
return watch;
/* 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 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;
return ret;
}
/* FIXME: we fail to fire on out of memory. Should drop connections. */
void fire_watches(struct transaction *trans, const char *node)
void fire_watches(struct transaction *trans, const char *node, bool recurse)
{
struct watch *watch;
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 @@ void fire_watches(struct transaction *trans, const char *node)
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 @@ static void move_event_onwards(struct watch_event *event)
{
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 @@ static int destroy_watch(void *_watch)
/* 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 @@ void check_watch_ack_timeout(void)
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 @@ bool do_watch(struct connection *conn, struct buffered_data *in)
{
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,10 +329,12 @@ bool do_watch(struct connection *conn, struct buffered_data *in)
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);
}
@@ -285,11 +342,14 @@ 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 @@ bool do_unwatch(struct connection *conn, struct buffered_data *in)
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)
+2 -2
View File
@@ -32,8 +32,8 @@ bool is_watch_event(struct connection *conn, struct buffered_data *out);
/* 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);
+2 -2
View File
@@ -47,7 +47,7 @@ char **xs_directory(struct xs_handle *h, const char *path, unsigned int *num);
/* 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 @@ char **xs_read_watch(struct xs_handle *h);
*/
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);
+40 -22
View File
@@ -201,7 +201,6 @@ static struct xs_permissions *file_get_perms(struct file_ops_info *info,
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 @@ static struct xs_permissions *file_get_perms(struct file_ops_info *info,
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);
@@ -232,6 +221,39 @@ static struct xs_permissions *file_get_perms(struct file_ops_info *info,
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,
struct xs_permissions *perms,
@@ -318,6 +340,7 @@ static bool file_write(struct file_ops_info *info,
if (write(fd, data, len) != (int)len)
barf_perror("Bad write to %s", filename);
init_perms(filename);
close(fd);
return true;
}
@@ -339,18 +362,10 @@ static bool file_mkdir(struct file_ops_info *info, const char *path)
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)
{
char *filename = path_to_name(info, path);
@@ -969,8 +984,11 @@ static void cleanup(const char *dir)
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)
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.