bitkeeper revision 1.1713.3.5 (42b2a4e2r6SNlC_nq2hAkXEQjEFAmA)

Many files:
  - watch now takes a token, returned when reading watch
  - More tests
  - Fix domain shared page communication (flush output)
  - Add "home" path for domains
  - More permissions checks in various functions
  - Simplify watch acknowledgement code and fix occasional bug
xs_watch_stress.c, 12readonly.sh, 11domain-watch.sh, 10domain-homedir.sh:
  new file
xs_stress.c, xs_lib.h, xs_lib.c:
  Cleanup whitespace.
ignore:
  Add tools/xenstore/xs_watch_stress
Signed-off-by: Rusty Russell <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-06-17 10:24:34 +00:00
1 parent c364cd9e1a
commit 4ddc54b2db
23 files changed
+717 -216

No files matched your search

+4
View File
@@ -1066,6 +1066,9 @@
42a57d98fdO519YyATk4_Zwr1STNfQ tools/xenstore/testsuite/07watch.sh
42a57d98zZUtvirUMjmHxFphJjmO7Q tools/xenstore/testsuite/08transaction.sh
42a57d98sn9RbpBgHRv1D99Kt7LwYA tools/xenstore/testsuite/09domain.sh
42b2a4bfxAwHlRgd31SJBgFnj8g3MA tools/xenstore/testsuite/10domain-homedir.sh
42b2a4bfHbUp4IB8tfNIa8j37S27fw tools/xenstore/testsuite/11domain-watch.sh
42b2a4bfhrB5v6uYKPj6jSO_Ng0PAA tools/xenstore/testsuite/12readonly.sh
42a57d98tSuoFCHnnM2GgENXJrRQmw tools/xenstore/testsuite/test.sh
42a57d98zxDP2Ti7dTznGROi66rUGw tools/xenstore/utils.c
42a57d98SDvOYCEjmCjwHSk6390GLA tools/xenstore/utils.h
@@ -1087,6 +1090,7 @@
42a57d99Kl6Ba8oCHv2fggl7QN9QZA tools/xenstore/xs_random.c
42a57d99SHYR1lQOD0shuErPDg9NKQ tools/xenstore/xs_stress.c
42a57d996aBawpkQNOWkNWXD6LrhPg tools/xenstore/xs_test.c
42b2a4bfp-lhxBfenUyHlvw7bPcVgA tools/xenstore/xs_watch_stress.c
403a3edbrr8RE34gkbR40zep98SXbg tools/xentrace/Makefile
40a107afN60pFdURgBv9KwEzgRl5mQ tools/xentrace/formats
420d52d2_znVbT4JAPIU36vQOme83g tools/xentrace/xenctx.c
+1
View File
@@ -148,6 +148,7 @@ tools/xenstore/xs_dom0_test
tools/xenstore/xs_random
tools/xenstore/xs_stress
tools/xenstore/xs_test
tools/xenstore/xs_watch_stress
tools/xentrace/xentrace
tools/xfrd/xfrd
xen/BLOG
+22 -13
View File
@@ -277,10 +277,11 @@ static PyObject *xspy_set_permissions(PyObject *self, PyObject *args,
static PyObject *xspy_watch(PyObject *self, PyObject *args, PyObject *kwds)
{
static char *kwd_spec[] = { "path", "priority", NULL };
static char *arg_spec = "s|i";
static char *kwd_spec[] = { "path", "priority", "token", NULL };
static char *arg_spec = "s|is";
char *path = NULL;
int priority = 0;
char *token;
struct xs_handle *xh = xshandle(self);
PyObject *val = NULL;
@@ -289,9 +290,9 @@ static PyObject *xspy_watch(PyObject *self, PyObject *args, PyObject *kwds)
if (!xh)
goto exit;
if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
&path, &priority))
&path, &priority, &token))
goto exit;
xsval = xs_watch(xh, path, priority);
xsval = xs_watch(xh, path, token, priority);
val = pyvalue_int(xsval);
exit:
return val;
@@ -305,14 +306,19 @@ static PyObject *xspy_read_watch(PyObject *self, PyObject *args,
struct xs_handle *xh = xshandle(self);
PyObject *val = NULL;
char *xsval = NULL;
char **xsval = NULL;
if (!xh)
goto exit;
if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec))
goto exit;
xsval = xs_read_watch(xh);
val = pyvalue_str(xsval);
if(!xsval){
val = PyErr_SetFromErrno(PyExc_RuntimeError);
goto exit;
}
/* Create tuple (path, token). */
val = Py_BuildValue("(ss)", xsval[0], xsval[1]);
exit:
if (xsval)
free(xsval);
@@ -323,7 +329,8 @@ static PyObject *xspy_acknowledge_watch(PyObject *self, PyObject *args,
PyObject *kwds)
{
static char *kwd_spec[] = { NULL };
static char *arg_spec = "";
static char *arg_spec = "s";
char *token = "";
struct xs_handle *xh = xshandle(self);
PyObject *val = NULL;
@@ -331,9 +338,9 @@ static PyObject *xspy_acknowledge_watch(PyObject *self, PyObject *args,
if (!xh)
goto exit;
if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec))
if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec, &token))
goto exit;
xsval = xs_acknowledge_watch(xh);
xsval = xs_acknowledge_watch(xh, token);
val = pyvalue_int(xsval);
exit:
return val;
@@ -341,9 +348,10 @@ static PyObject *xspy_acknowledge_watch(PyObject *self, PyObject *args,
static PyObject *xspy_unwatch(PyObject *self, PyObject *args, PyObject *kwds)
{
static char *kwd_spec[] = { "path", NULL };
static char *arg_spec = "s|";
static char *kwd_spec[] = { "path", "token", NULL };
static char *arg_spec = "s|s";
char *path = NULL;
char *token = "";
struct xs_handle *xh = xshandle(self);
PyObject *val = NULL;
@@ -351,9 +359,10 @@ static PyObject *xspy_unwatch(PyObject *self, PyObject *args, PyObject *kwds)
if (!xh)
goto exit;
if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec, &path))
if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec, &path,
&token))
goto exit;
xsval = xs_unwatch(xh, path);
xsval = xs_unwatch(xh, path, token);
val = pyvalue_int(xsval);
exit:
return val;
+9 -5
View File
@@ -41,8 +41,9 @@ xenstored_test: xenstored_core_test.o xenstored_watch_test.o xenstored_domain_te
xs_test: xs_test.o xs_lib.o utils.o
xs_random: xs_random.o xs_test_lib.o xs_lib.o talloc.o utils.o
xs_stress: xs_stress.o xs_test_lib.o xs_lib.o talloc.o utils.o
xs_watch_stress: xs_watch_stress.o xs_test_lib.o xs_lib.o talloc.o utils.o
xs_test.o xs_stress.o xenstored_core_test.o xenstored_watch_test.o xenstored_transaction_test.o xenstored_domain_test.o xs_random.o xs_test_lib.o talloc_test.o fake_libxc.o: CFLAGS=$(BASECFLAGS) $(TESTFLAGS)
xs_test.o xs_stress.o xs_watch_stress.o xenstored_core_test.o xenstored_watch_test.o xenstored_transaction_test.o xenstored_domain_test.o xs_random.o xs_test_lib.o talloc_test.o fake_libxc.o: CFLAGS=$(BASECFLAGS) $(TESTFLAGS)
xenstored_%_test.o: xenstored_%.c
$(COMPILE.c) -o $@ $<
@@ -63,8 +64,9 @@ libxenstore.a: $(LIB_OBJS_A)
libxenstore-pic.a: $(LIB_OBJS_PIC)
clean: testsuite-clean
rm -f *.o *.opic *.a xen
rm -f xs_test xenstored xenstored_test xs_random xs_stress xs_dom0_test
rm -f *.o *.opic *.a
rm -f xen xenstored xs_random xs_stress xs_watch_stress
rm -f xs_test xenstored_test xs_dom0_test
-$(RM) $(PROG_DEP)
check: testsuite-run randomcheck stresstest
@@ -83,9 +85,11 @@ randomcheck: xs_random xenstored_test
$(TESTENV) ./xs_random --fast /tmp/xs_random 100000 $(RANDSEED)
$(TESTENV) ./xs_random --fail /tmp/xs_random 10000 $(RANDSEED)
stresstest: xs_stress xenstored_test
stresstest: xs_stress xs_watch_stress xenstored_test
rm -rf $(TESTDIR)/store
export $(TESTENV); PID=`./xenstored_test --output-pid`; ./xs_stress 10000; ret=$$?; kill $$PID; exit $$ret
export $(TESTENV); PID=`./xenstored_test --output-pid`; ./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
xs_dom0_test: xs_dom0_test.o utils.o
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -lxc -o $@
+102 -14
View File
@@ -3,30 +3,118 @@
# Watch something, write to it, check watch has fired.
[ "`echo -e 'write /test create contents' | ./xs_test 2>&1`" = "" ]
[ "`echo -e '1 watch /test 100\n2 write /test create contents2\n1 waitwatch\n1 ackwatch' | ./xs_test 2>&1`" = "1:/test" ]
[ "`echo -e '1 watch /test token 100
2 write /test create contents2
1 waitwatch
1 ackwatch token' | ./xs_test 2>&1`" = "1:/test:token" ]
# Check that reads don't set it off.
[ "`echo -e '1 watch /test 100\n2 read /test\n1 waitwatch' | ./xs_test 2>&1`" = "2:contents2
[ "`echo -e '1 watch /test token 100
2 read /test
1 waitwatch' | ./xs_test 2>&1`" = "2:contents2
1:waitwatch timeout" ]
# mkdir, setperm and rm should (also /tests watching dirs)
# mkdir, setperm and rm should (also tests watching dirs)
[ "`echo -e 'mkdir /dir' | ./xs_test 2>&1`" = "" ]
[ "`echo -e '1 watch /dir 100\n2 mkdir /dir/newdir\n1 waitwatch\n1 ackwatch\n2 setperm /dir/newdir 0 READ\n1 waitwatch\n1 ackwatch\n2 rm /dir/newdir\n1 waitwatch\n1 ackwatch' | ./xs_test 2>&1`" = "1:/dir/newdir
1:/dir/newdir
1:/dir/newdir" ]
[ "`echo -e '1 watch /dir token 100
2 mkdir /dir/newdir
1 waitwatch
1 ackwatch token
2 setperm /dir/newdir 0 READ
1 waitwatch
1 ackwatch token
2 rm /dir/newdir
1 waitwatch
1 ackwatch token' | ./xs_test 2>&1`" = "1:/dir/newdir:token
1:/dir/newdir:token
1:/dir/newdir:token" ]
# ignore watches while doing commands, should work.
[ "`echo -e 'watch /dir 100\nwrite /dir/test create contents\nread /dir/test\nwaitwatch\nackwatch' | ./xs_test 2>&1`" = "contents
/dir/test" ]
[ "`echo -e 'watch /dir token 100
write /dir/test create contents
read /dir/test
waitwatch
ackwatch token' | ./xs_test 2>&1`" = "contents
/dir/test:token" ]
# watch priority /test.
[ "`echo -e '1 watch /dir 1\n3 watch /dir 3\n2 watch /dir 2\nwrite /dir/test create contents\n3 waitwatch\n3 ackwatch\n2 waitwatch\n2 ackwatch\n1 waitwatch\n1 ackwatch' | ./xs_test 2>&1`" = "3:/dir/test
2:/dir/test
1:/dir/test" ]
[ "`echo -e '1 watch /dir token1 1
3 watch /dir token3 3
2 watch /dir token2 2
write /dir/test create contents
3 waitwatch
3 ackwatch token3
2 waitwatch
2 ackwatch token2
1 waitwatch
1 ackwatch token1' | ./xs_test 2>&1`" = "3:/dir/test:token3
2:/dir/test:token2
1:/dir/test:token1" ]
# If one dies (without acking), the other should still get ack.
[ "`echo -e '1 watch /dir 0\n2 watch /dir 1\nwrite /dir/test create contents\n2 waitwatch\n2 close\n1 waitwatch\n1 ackwatch' | ./xs_test 2>&1`" = "2:/dir/test
1:/dir/test" ]
[ "`echo -e '1 watch /dir token1 0
2 watch /dir token2 1
write /dir/test create contents
2 waitwatch
2 close
1 waitwatch
1 ackwatch token1' | ./xs_test 2>&1`" = "2:/dir/test:token2
1:/dir/test:token1" ]
# If one dies (without reading at all), the other should still get ack.
[ "`echo -e '1 watch /dir 0\n2 watch /dir 1\nwrite /dir/test create contents\n2 close\n1 waitwatch\n1 ackwatch' | ./xs_test 2>&1`" = "1:/dir/test" ]
[ "`echo -e '1 watch /dir token1 0
2 watch /dir token2 1
write /dir/test create contents
2 close
1 waitwatch
1 ackwatch token1' | ./xs_test 2>&1`" = "1:/dir/test:token1" ]
# unwatch
[ "`echo -e '1 watch /dir token1 0
1 unwatch /dir token1
1 watch /dir token2 0
2 write /dir/test2 create contents
1 waitwatch
1 unwatch /dir token2' | ./xs_test 2>&1`" = "1:/dir/test2:token2" ]
# unwatch while watch pending.
[ "`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" ]
# check we only get notified once.
[ "`echo -e '1 watch /test token 100
2 write /test create contents2
1 waitwatch
1 ackwatch token
1 waitwatch' | ./xs_test 2>&1`" = "1:/test:token
1:waitwatch timeout" ]
# watches are queued in order.
[ "`echo -e '1 watch / token 100
2 write /test1 create contents
2 write /test2 create contents
2 write /test3 create contents
1 waitwatch
1 ackwatch token
1 waitwatch
1 ackwatch token
1 waitwatch
1 ackwatch token' | ./xs_test 2>&1`" = "1:/test1:token
1:/test2:token
1:/test3:token" ]
# Creation of subpaths should be covered correctly.
[ "`echo -e '1 watch / token 100
2 write /test/subnode create contents2
2 write /test/subnode/subnode create contents2
1 waitwatch
1 ackwatch token
1 waitwatch
1 ackwatch token
1 waitwatch' | ./xs_test 2>&1`" = "1:/test/subnode:token
1:/test/subnode/subnode:token
1:waitwatch timeout" ]
@@ -0,0 +1,12 @@
#! /bin/sh
# Test domain "implicit" paths.
# Create a domain, write an entry using implicit path, read using implicit
[ "`echo -e 'mkdir /home
introduce 1 100 7 /home
1 write entry1 create contents
read /home/entry1
dir /home' | ./xs_test 2>&1`" = "handle is 1
contents
entry1" ]
@@ -0,0 +1,51 @@
#! /bin/sh
# Test watching from a domain.
# Watch something, write to it, check watch has fired.
[ "`echo -e 'write /test create contents' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'mkdir /dir' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'introduce 1 100 7 /my/home
1 watch /test token 100
write /test create contents2
1 waitwatch
1 ackwatch token
1 unwatch /test token
release 1' | ./xs_test 2>&1`" = "handle is 1
1:/test:token" ]
# ignore watches while doing commands, should work.
[ "`echo -e 'introduce 1 100 7 /my/home
1 watch /dir token 100
1 write /dir/test create contents
1 read /dir/test
1 waitwatch
1 ackwatch token
release 1' | ./xs_test 2>&1`" = "handle is 1
1:contents
1:/dir/test:token" ]
# unwatch
[ "`echo -e 'introduce 1 100 7 /my/home
1 watch /dir token1 0
1 unwatch /dir token1
1 watch /dir token2 0
2 write /dir/test2 create contents
1 waitwatch
1 unwatch /dir token2
release 1' | ./xs_test 2>&1`" = "handle is 1
1:/dir/test2:token2" ]
# unwatch while watch pending.
[ "`echo -e 'introduce 1 100 7 /my/home
introduce 2 101 8 /my/secondhome
1 watch /dir token1 0
2 watch /dir token2 1
write /dir/test create contents
2 unwatch /dir token2
1 waitwatch
1 ackwatch token1
release 1
release 2' | ./xs_test 2>&1`" = "handle is 1
handle is 2
1:/dir/test:token1" ]
+40
View File
@@ -0,0 +1,40 @@
#! /bin/sh
# Test that read only connection can't alter store.
[ "`echo 'write /test create contents' | ./xs_test 2>&1`" = "" ]
# These are all valid.
[ "`echo 'dir /
read /test
getperm /test
watch /test token 0
unwatch /test token
start /
commit
start /
abort' | ./xs_test --readonly 2>&1`" = "test
contents
0 NONE" ]
# These don't work
[ "`echo 'write /test2 create contents' | ./xs_test --readonly 2>&1`" = "FATAL: write: Read-only file system" ]
[ "`echo 'write /test create contents' | ./xs_test --readonly 2>&1`" = "FATAL: write: Read-only file system" ]
[ "`echo 'setperm /test 100 NONE' | ./xs_test --readonly 2>&1`" = "FATAL: setperm: Read-only file system" ]
[ "`echo 'setperm /test 100 NONE' | ./xs_test --readonly 2>&1`" = "FATAL: setperm: Read-only file system" ]
[ "`echo 'shutdown' | ./xs_test --readonly 2>&1`" = "FATAL: shutdown: Read-only file system" ]
[ "`echo 'introduce 1 100 7 /home' | ./xs_test --readonly 2>&1`" = "FATAL: introduce: Read-only file system" ]
# Check that watches work like normal.
set -m
[ "`echo 'watch / token 0
waitwatch
ackwatch token' | ./xs_test --readonly 2>&1`" = "/test:token" ] &
[ "`echo 'write /test create contents' | ./xs_test 2>&1`" = "" ]
if wait; then :; else
echo Readonly wait test failed: $?
exit 1
fi
+1 -1
View File
@@ -9,7 +9,7 @@ run_test()
mkdir $XENSTORED_ROOTDIR
# Weird failures with this.
if type valgrind >/dev/null 2>&1; then
valgrind -q --logfile-fd=3 ./xenstored_test --output-pid --no-fork 3>testsuite/tmp/vgout > /tmp/pid &
valgrind -q --logfile-fd=3 ./xenstored_test --output-pid --no-fork 3>testsuite/tmp/vgout > /tmp/pid 2> testsuite/tmp/xenstored_errors &
while [ ! -s /tmp/pid ]; do sleep 0; done
PID=`cat /tmp/pid`
rm /tmp/pid
+102 -24
View File
@@ -122,6 +122,33 @@ void __attribute__((noreturn)) corrupt(struct connection *conn,
_exit(2);
}
static char *sockmsg_string(enum xsd_sockmsg_type type)
{
switch (type) {
case XS_DEBUG: return "DEBUG";
case XS_SHUTDOWN: return "SHUTDOWN";
case XS_DIRECTORY: return "DIRECTORY";
case XS_READ: return "READ";
case XS_GET_PERMS: return "GET_PERMS";
case XS_WATCH: return "WATCH";
case XS_WATCH_ACK: return "WATCH_ACK";
case XS_UNWATCH: return "UNWATCH";
case XS_TRANSACTION_START: return "TRANSACTION_START";
case XS_TRANSACTION_END: return "TRANSACTION_END";
case XS_INTRODUCE: return "INTRODUCE";
case XS_RELEASE: return "RELEASE";
case XS_GETDOMAINPATH: return "GETDOMAINPATH";
case XS_WRITE: return "WRITE";
case XS_MKDIR: return "MKDIR";
case XS_RM: return "RM";
case XS_SET_PERMS: return "SET_PERMS";
case XS_WATCH_EVENT: return "WATCH_EVENT";
case XS_ERROR: return "ERROR";
default:
return "**UNKNOWN**";
}
}
static bool write_message(struct connection *conn)
{
int ret;
@@ -129,8 +156,9 @@ static bool write_message(struct connection *conn)
if (out->inhdr) {
if (verbose)
xprintf("Writing msg %i out to %p\n",
out->hdr.msg.type, conn);
xprintf("Writing msg %s (%s) out to %p\n",
sockmsg_string(out->hdr.msg.type),
out->buffer, conn);
ret = conn->write(conn, out->hdr.raw + out->used,
sizeof(out->hdr) - out->used);
if (ret < 0)
@@ -148,9 +176,6 @@ static bool write_message(struct connection *conn)
return true;
}
if (verbose)
xprintf("Writing data len %i out to %p\n",
out->hdr.msg.len, conn);
ret = conn->write(conn, out->buffer + out->used,
out->hdr.msg.len - out->used);
@@ -162,10 +187,7 @@ static bool write_message(struct connection *conn)
return true;
conn->out = NULL;
/* If this was an event, we wait for ack, otherwise we're done. */
if (!is_watch_event(conn, out))
talloc_free(out);
talloc_free(out);
queue_next_event(conn);
return true;
@@ -402,7 +424,7 @@ static bool valid_chars(const char *node)
"0123456789-/_@") == strlen(node));
}
static bool is_valid_nodename(const char *node)
bool is_valid_nodename(const char *node)
{
/* Must start in /. */
if (!strstarts(node, "/"))
@@ -601,17 +623,24 @@ static int check_with_parents(struct connection *conn, const char *node,
return errnum;
}
char *canonicalize(struct connection *conn, const char *node)
{
const char *prefix;
if (!node || strstarts(node, "/"))
return (char *)node;
prefix = get_implicit_path(conn);
if (prefix)
return talloc_asprintf(node, "%s/%s", prefix, node);
return (char *)node;
}
bool check_node_perms(struct connection *conn, const char *node,
enum xs_perm_type perm)
{
struct xs_permissions *perms;
unsigned int num;
if (!node) {
errno = EINVAL;
return false;
}
if (!node || !is_valid_nodename(node)) {
errno = EINVAL;
return false;
@@ -651,6 +680,7 @@ static bool send_directory(struct connection *conn, const char *node)
DIR *dir;
struct dirent *dirent;
node = canonicalize(conn, node);
if (!check_node_perms(conn, node, XS_PERM_READ))
return send_error(conn, errno);
@@ -680,6 +710,7 @@ static bool do_read(struct connection *conn, const char *node)
unsigned int size;
int *fd;
node = canonicalize(conn, node);
if (!check_node_perms(conn, node, XS_PERM_READ))
return send_error(conn, errno);
@@ -750,7 +781,7 @@ static bool do_write(struct connection *conn, struct buffered_data *in)
if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec))
return send_error(conn, EINVAL);
node = vec[0];
node = canonicalize(conn, vec[0]);
if (!within_transaction(conn->transaction, node))
return send_error(conn, EROFS);
@@ -804,6 +835,7 @@ static bool do_write(struct connection *conn, struct buffered_data *in)
static bool do_mkdir(struct connection *conn, const char *node)
{
node = canonicalize(conn, node);
if (!check_node_perms(conn, node, XS_PERM_WRITE|XS_PERM_CREATE))
return send_error(conn, errno);
@@ -826,6 +858,7 @@ static bool do_rm(struct connection *conn, const char *node)
{
char *tmppath, *path;
node = canonicalize(conn, node);
if (!check_node_perms(conn, node, XS_PERM_WRITE))
return send_error(conn, errno);
@@ -848,6 +881,7 @@ static bool do_rm(struct connection *conn, const char *node)
add_change_node(conn->transaction, node);
send_ack(conn, XS_RM);
/* FIXME: traverse and fire watches for ALL of them! */
fire_watches(conn->transaction, node);
return false;
}
@@ -858,6 +892,7 @@ static bool do_get_perms(struct connection *conn, const char *node)
char *strings;
unsigned int len, num;
node = canonicalize(conn, node);
if (!check_node_perms(conn, node, XS_PERM_READ))
return send_error(conn, errno);
@@ -883,7 +918,7 @@ static bool do_set_perms(struct connection *conn, struct buffered_data *in)
return send_error(conn, EINVAL);
/* First arg is node name. */
node = in->buffer;
node = canonicalize(conn, in->buffer);
in->buffer += strlen(in->buffer) + 1;
num--;
@@ -968,10 +1003,10 @@ static bool process_message(struct connection *conn, struct buffered_data *in)
return do_watch(conn, in);
case XS_WATCH_ACK:
return do_watch_ack(conn);
return do_watch_ack(conn, onearg(in));
case XS_UNWATCH:
return do_unwatch(conn, onearg(in));
return do_unwatch(conn, in);
case XS_TRANSACTION_START:
return do_transaction_start(conn, onearg(in));
@@ -1015,13 +1050,13 @@ static void consider_message(struct connection *conn)
}
if (verbose)
xprintf("Got message %i len %i from %p\n",
type, conn->in->hdr.msg.len, conn);
xprintf("Got message %s len %i from %p\n",
sockmsg_string(type), conn->in->hdr.msg.len, conn);
/* We might get a command while waiting for an ack: this means
* the other end discarded it: we will re-transmit. */
if (type != XS_WATCH_ACK)
reset_watch_event(conn);
conn->waiting_for_ack = false;
/* Careful: process_message may free connection. We detach
* "in" beforehand and allocate the new buffer to avoid
@@ -1136,7 +1171,6 @@ struct connection *new_connection(connwritefn_t *write, connreadfn_t *read)
new->blocked = false;
new->out = new->waiting_reply = NULL;
new->event = NULL;
new->fd = -1;
new->id = 0;
new->domain = NULL;
@@ -1203,6 +1237,42 @@ static void time_relative_to_now(struct timeval *tv)
}
}
#ifdef TESTING
/* Useful for running under debugger. */
void dump_connection(void)
{
struct connection *i;
list_for_each_entry(i, &connections, list) {
printf("Connection %p:\n", i);
if (i->id)
printf(" id = %i\n", i->id);
if (i->blocked)
printf(" blocked on = %s\n", i->blocked);
if (i->waiting_for_ack)
printf(" waiting_for_ack TRUE\n");
if (!i->in->inhdr || i->in->used)
printf(" got %i bytes of %s\n",
i->in->used, i->in->inhdr ? "header" : "data");
if (i->out)
printf(" sending message %s (%s) out\n",
sockmsg_string(i->out->hdr.msg.type),
i->out->buffer);
if (i->waiting_reply)
printf(" ... and behind is queued %s (%s)\n",
sockmsg_string(i->waiting_reply->hdr.msg.type),
i->waiting_reply->buffer);
#if 0
if (i->transaction)
dump_transaction(i);
if (i->domain)
dump_domain(i);
#endif
dump_watches(i);
}
}
#endif
static struct option options[] = { { "no-fork", 0, NULL, 'N' },
{ "verbose", 0, NULL, 'V' },
{ "output-pid", 0, NULL, 'P' },
@@ -1314,6 +1384,7 @@ int main(int argc, char *argv[])
timerclear(&tv);
shortest_transaction_timeout(&tv);
shortest_watch_ack_timeout(&tv);
if (timerisset(&tv)) {
time_relative_to_now(&tv);
tvp = &tv;
@@ -1351,8 +1422,15 @@ int main(int argc, char *argv[])
}
}
if (tvp)
/* Flush output for domain connections, */
list_for_each_entry(i, &connections, list)
if (i->domain && i->out)
handle_output(i);
if (tvp) {
check_transaction_timeout();
check_watch_ack_timeout();
}
/* If transactions ended, we might be able to do more work. */
unblock_connections();
+14 -5
View File
@@ -16,8 +16,10 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _XENSTORED_INTERNAL_H
#define _XENSTORED_INTERNAL_H
#ifndef _XENSTORED_CORE_H
#define _XENSTORED_CORE_H
#include <stdbool.h>
#include <stdint.h>
#include <errno.h>
@@ -59,8 +61,8 @@ struct connection
/* Is this a read-only connection? */
bool can_write;
/* Our current event. If all used, we're waiting for ack. */
struct watch_event *event;
/* Are we waiting for a watch event ack? */
bool waiting_for_ack;
/* Buffered incoming data. */
struct buffered_data *in;
@@ -105,6 +107,9 @@ bool send_ack(struct connection *conn, enum xsd_sockmsg_type type);
/* Send an error: error is usually "errno". */
bool send_error(struct connection *conn, int error);
/* Canonicalize this path if possible. */
char *canonicalize(struct connection *conn, const char *node);
/* Check permissions on this node. */
bool check_node_perms(struct connection *conn, const char *node,
enum xs_perm_type perm);
@@ -121,6 +126,10 @@ struct connection *new_connection(connwritefn_t *write, connreadfn_t *read);
void handle_input(struct connection *conn);
void handle_output(struct connection *conn);
/* Is this a valid node name? */
bool is_valid_nodename(const char *node);
/* Convenient talloc-style destructor for paths. */
int destroy_path(void *path);
#endif /* _XENSTORED_INTERNAL_H */
#endif /* _XENSTORED_CORE_H */
+19 -9
View File
@@ -65,11 +65,6 @@ struct domain
static LIST_HEAD(domains);
void domain_set_conn(struct domain *domain, struct connection *conn)
{
domain->conn = conn;
}
struct ringbuf_head
{
u32 write; /* Next place to write to */
@@ -268,6 +263,9 @@ bool do_introduce(struct connection *conn, struct buffered_data *in)
if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec))
return send_error(conn, EINVAL);
if (conn->id != 0)
return send_error(conn, EACCES);
if (!conn->can_write)
return send_error(conn, EROFS);
@@ -275,10 +273,9 @@ 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]);
domain->path = talloc_strdup(domain, vec[3]);
talloc_set_destructor(domain, destroy_domain);
if (!domain->port || !domain->domid)
if (!domain->port || !domain->domid || !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,
getpagesize(),
PROT_READ|PROT_WRITE,
@@ -286,6 +283,9 @@ bool do_introduce(struct connection *conn, struct buffered_data *in)
if (!domain->page)
return send_error(conn, errno);
list_add(&domain->list, &domains);
talloc_set_destructor(domain, destroy_domain);
/* One in each half of page. */
domain->input = domain->page;
domain->output = domain->page + getpagesize()/2;
@@ -298,7 +298,6 @@ bool do_introduce(struct connection *conn, struct buffered_data *in)
domain->conn->domain = domain;
talloc_steal(domain->conn, domain);
list_add(&domain->list, &domains);
return send_ack(conn, XS_INTRODUCE);
}
@@ -327,6 +326,9 @@ bool do_release(struct connection *conn, const char *domid_str)
if (!domid)
return send_error(conn, EINVAL);
if (conn->id != 0)
return send_error(conn, EACCES);
domain = find_domain_by_domid(domid);
if (!domain)
return send_error(conn, ENOENT);
@@ -365,6 +367,14 @@ static int close_xc_handle(void *_handle)
return 0;
}
/* Returns the implicit path of a connection (only domains have this) */
const char *get_implicit_path(const struct connection *conn)
{
if (!conn->domain)
return NULL;
return conn->domain->path;
}
/* Returns the event channel handle. */
int domain_init(void)
{
+3 -1
View File
@@ -16,6 +16,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _XENSTORED_DOMAIN_H
#define _XENSTORED_DOMAIN_H
@@ -33,6 +34,7 @@ bool do_get_domain_path(struct connection *conn, const char *domid_str);
/* Returns the event channel handle */
int domain_init(void);
void domain_set_conn(struct domain *domain, struct connection *conn);
/* Returns the implicit path of a connection (only domains have this) */
const char *get_implicit_path(const struct connection *conn);
#endif /* _XENSTORED_DOMAIN_H */
+1
View File
@@ -201,6 +201,7 @@ bool do_transaction_start(struct connection *conn, const char *node)
if (conn->transaction)
return send_error(conn, EBUSY);
node = canonicalize(conn, node);
if (!check_node_perms(conn, node, XS_PERM_READ))
return send_error(conn, errno);
+124 -65
View File
@@ -21,6 +21,8 @@
#include <sys/types.h>
#include <stdarg.h>
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
#include "talloc.h"
#include "list.h"
#include "xenstored_watch.h"
@@ -28,6 +30,8 @@
#include "utils.h"
#include "xenstored_test.h"
/* FIXME: time out unacked watches. */
/* We create this if anyone is interested "node", then we pass it from
* watch to watch as each connection acks it.
*/
@@ -39,7 +43,10 @@ struct watch_event
/* Watch we are currently attached to. */
struct watch *watch;
struct buffered_data *data;
struct timeval timeout;
/* Name of node which changed. */
char *node;
};
struct watch
@@ -50,72 +57,63 @@ struct watch
/* Current outstanding events applying to this watch. */
struct list_head events;
char *token;
char *node;
struct connection *conn;
};
static LIST_HEAD(watches);
static void reset_event(struct watch_event *event)
static struct watch_event *get_first_event(struct connection *conn)
{
event->data->inhdr = true;
event->data->used = 0;
}
struct watch *watch;
struct watch_event *event;
/* We received a non-ACK response: re-queue any watch we just sent. */
void reset_watch_event(struct connection *conn)
{
if (waiting_for_ack(conn))
reset_event(conn->event);
}
/* Find first watch with an event. */
list_for_each_entry(watch, &watches, list) {
if (watch->conn != conn)
continue;
/* We're waiting if we have an event and we sent it all. */
bool waiting_for_ack(struct connection *conn)
{
if (!conn->event)
return false;
if (conn->event->data->inhdr)
return false;
return conn->event->data->used == conn->event->data->hdr.msg.len;
}
bool is_watch_event(struct connection *conn, struct buffered_data *out)
{
return (conn->event && out == conn->event->data);
event = list_top(&watch->events, struct watch_event, list);
if (event)
return event;
}
return NULL;
}
/* Look through our watches: if any of them have an event, queue it. */
void queue_next_event(struct connection *conn)
{
struct watch *watch;
struct watch_event *event;
char *buffer;
unsigned int len;
/* We had a reply queued already? Send it. */
/* We had a reply queued already? Send it: other end will
* discard watch. */
if (conn->waiting_reply) {
conn->out = conn->waiting_reply;
conn->waiting_reply = NULL;
conn->waiting_for_ack = false;
return;
}
/* If we're waiting for ack, don't queue more. */
if (waiting_for_ack(conn))
/* If we're already waiting for ack, don't queue more. */
if (conn->waiting_for_ack)
return;
/* Find a good event to send. */
if (!conn->event) {
list_for_each_entry(watch, &watches, list) {
if (watch->conn != conn)
continue;
event = get_first_event(conn);
if (!event)
return;
conn->event = list_top(&watch->events,
struct watch_event, list);
if (conn->event)
break;
}
if (!conn->event)
return;
}
/* If we decide to cancel, we will reset this. */
conn->waiting_for_ack = true;
conn->out = conn->event->data;
/* Create reply from path and token */
len = strlen(event->node) + 1 + strlen(event->watch->token) + 1;
buffer = talloc_array(conn, char, len);
strcpy(buffer, event->node);
strcpy(buffer+strlen(event->node)+1, event->watch->token);
send_reply(conn, XS_WATCH_EVENT, buffer, len);
talloc_free(buffer);
}
/* Watch on DIR applies to DIR, DIR/FILE, but not DIRLONG. */
@@ -160,14 +158,15 @@ void fire_watches(struct transaction *trans, const char *node)
/* Create and fill in info about event. */
event = talloc(talloc_autofree_context(), struct watch_event);
event->data = new_buffer(event);
event->data->hdr.msg.type = XS_WATCH_EVENT;
event->data->hdr.msg.len = strlen(node) + 1;
event->data->buffer = talloc_strdup(event->data, node);
event->node = talloc_strdup(event, node);
/* Tie event to this watch. */
event->watch = watch;
list_add(&event->list, &watch->events);
list_add_tail(&event->list, &watch->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)
@@ -178,16 +177,15 @@ void fire_watches(struct transaction *trans, const char *node)
static void move_event_onwards(struct watch_event *event)
{
list_del(&event->list);
reset_event(event);
/* Remove from this watch, and find next watch to put this on. */
event->watch = find_next_watch(event->watch, event->data->buffer);
event->watch = find_next_watch(event->watch, event->node);
if (!event->watch) {
talloc_free(event);
return;
}
list_add(&event->list, &event->watch->events);
list_add_tail(&event->list, &event->watch->events);
/* If connection not doing anything, queue this. */
if (!event->watch->conn->out)
@@ -199,10 +197,6 @@ static int destroy_watch(void *_watch)
struct watch *watch = _watch;
struct watch_event *event;
/* Forget about sending out or waiting for acks for this watch. */
if (watch->conn->event && watch->conn->event->watch == watch)
watch->conn->event = NULL;
/* If we have pending events, pass them on to others. */
while ((event = list_top(&watch->events, struct watch_event, list)))
move_event_onwards(event);
@@ -227,21 +221,59 @@ static void insert_watch(struct watch *watch)
list_add_tail(&watch->list, &watches);
}
void shortest_watch_ack_timeout(struct timeval *tv)
{
struct watch *watch;
list_for_each_entry(watch, &watches, list) {
struct watch_event *i;
list_for_each_entry(i, &watch->events, list) {
if (!timerisset(&i->timeout))
continue;
if (!timerisset(tv) || timercmp(&i->timeout, tv, <))
*tv = i->timeout;
}
}
}
void check_watch_ack_timeout(void)
{
struct watch *watch;
struct timeval now;
gettimeofday(&now, NULL);
list_for_each_entry(watch, &watches, list) {
struct watch_event *i, *tmp;
list_for_each_entry_safe(i, tmp, &watch->events, list) {
if (!timerisset(&i->timeout))
continue;
if (timercmp(&i->timeout, &now, <)) {
xprintf("Warning: timeout on watch event %s"
" token %s\n",
i->node, watch->token);
timerclear(&i->timeout);
}
}
}
}
bool do_watch(struct connection *conn, struct buffered_data *in)
{
struct watch *watch;
char *vec[2];
char *vec[3];
if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec))
return send_error(conn, EINVAL);
vec[0] = canonicalize(conn, vec[0]);
if (!check_node_perms(conn, vec[0], XS_PERM_READ))
return send_error(conn, errno);
watch = talloc(conn, struct watch);
watch->node = talloc_strdup(watch, vec[0]);
watch->token = talloc_strdup(watch, vec[1]);
watch->conn = conn;
watch->priority = strtoul(vec[1], NULL, 0);
watch->priority = strtoul(vec[2], NULL, 0);
INIT_LIST_HEAD(&watch->events);
insert_watch(watch);
@@ -249,31 +281,58 @@ bool do_watch(struct connection *conn, struct buffered_data *in)
return send_ack(conn, XS_WATCH);
}
bool do_watch_ack(struct connection *conn)
bool do_watch_ack(struct connection *conn, const char *token)
{
struct watch_event *event;
if (!waiting_for_ack(conn))
if (!conn->waiting_for_ack)
return send_error(conn, ENOENT);
/* Remove this watch event. */
event = conn->event;
conn->event = NULL;
event = get_first_event(conn);
if (!streq(event->watch->token, token))
return send_error(conn, EINVAL);
move_event_onwards(event);
conn->waiting_for_ack = false;
return send_ack(conn, XS_WATCH_ACK);
}
bool do_unwatch(struct connection *conn, const char *node)
bool do_unwatch(struct connection *conn, struct buffered_data *in)
{
struct watch *watch;
char *node, *vec[2];
if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec))
return send_error(conn, EINVAL);
node = canonicalize(conn, vec[0]);
list_for_each_entry(watch, &watches, list) {
if (watch->conn == conn
&& streq(watch->node, node)) {
if (watch->conn != conn)
continue;
if (streq(watch->node, node) && streq(watch->token, vec[1])) {
talloc_free(watch);
return send_ack(conn, XS_UNWATCH);
}
}
return send_error(conn, ENOENT);
}
#ifdef TESTING
void dump_watches(struct connection *conn)
{
struct watch *watch;
struct watch_event *event;
/* Find first watch with an event. */
list_for_each_entry(watch, &watches, list) {
if (watch->conn != conn)
continue;
printf(" watch on %s token %s prio %i\n",
watch->node, watch->token, watch->priority);
list_for_each_entry(event, &watch->events, list)
printf(" event: %s\n", event->node);
}
}
#endif
+12 -8
View File
@@ -16,13 +16,15 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _XENSTORED_WATCH_H
#define _XENSTORED_WATCH_H
#include "xenstored_core.h"
bool do_watch(struct connection *conn, struct buffered_data *in);
bool do_watch_ack(struct connection *conn);
bool do_unwatch(struct connection *conn, const char *node);
bool do_watch_ack(struct connection *conn, const char *token);
bool do_unwatch(struct connection *conn, struct buffered_data *in);
/* Is this a watch event message for this connection? */
bool is_watch_event(struct connection *conn, struct buffered_data *out);
@@ -30,13 +32,15 @@ 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);
/* Is this connection waiting for a watch acknowledgement? */
bool waiting_for_ack(struct connection *conn);
/* Reset event if we were sending one */
void reset_watch_event(struct connection *conn);
/* Fire all watches. */
void fire_watches(struct transaction *trans, const char *node);
/* Find shortest timeout: if any, reduce tv (may already be set). */
void shortest_watch_ack_timeout(struct timeval *tv);
/* Check for watches which may have timed out. */
void check_watch_ack_timeout(void);
void dump_watches(struct connection *conn);
#endif /* _XENSTORED_WATCH_H */
+35 -27
View File
@@ -159,8 +159,7 @@ static void *read_reply(int fd, enum xsd_sockmsg_type *type, unsigned int *len)
/* Send message to xs, get malloc'ed reply. NULL and set errno on error. */
static void *xs_talkv(struct xs_handle *h, enum xsd_sockmsg_type type,
const struct iovec *iovec,
unsigned int num_vecs,
const struct iovec *iovec, unsigned int num_vecs,
unsigned int *len)
{
struct xsd_sockmsg msg;
@@ -330,8 +329,7 @@ bool xs_rm(struct xs_handle *h, const char *path)
* Returns malloced array, or NULL: call free() after use.
*/
struct xs_permissions *xs_get_permissions(struct xs_handle *h,
const char *path,
unsigned int *num)
const char *path, unsigned int *num)
{
char *strings;
unsigned int len;
@@ -400,61 +398,75 @@ unwind:
/* Watch a node for changes (poll on fd to detect, or call read_watch()).
* When the node (or any child) changes, fd will become readable.
* Token is returned when watch is read, to allow matching.
* Priority indicates order if multiple watchers: higher is first.
* Returns false on failure.
*/
bool xs_watch(struct xs_handle *h, const char *path, unsigned int priority)
bool xs_watch(struct xs_handle *h, const char *path, const char *token,
unsigned int priority)
{
char prio[MAX_STRLEN(priority)];
struct iovec iov[2];
struct iovec iov[3];
sprintf(prio, "%u", priority);
iov[0].iov_base = (void *)path;
iov[0].iov_len = strlen(path) + 1;
iov[1].iov_base = prio;
iov[1].iov_len = strlen(prio) + 1;
iov[1].iov_base = (void *)token;
iov[1].iov_len = strlen(token) + 1;
iov[2].iov_base = prio;
iov[2].iov_len = strlen(prio) + 1;
return xs_bool(xs_talkv(h, XS_WATCH, iov, ARRAY_SIZE(iov), NULL));
}
/* Find out what node change was on (will block if nothing pending).
* Returns malloced path, or NULL: call free() after use.
* Returns array of two pointers: path and token, or NULL.
* Call free() after use.
*/
char *xs_read_watch(struct xs_handle *h)
char **xs_read_watch(struct xs_handle *h)
{
struct xsd_sockmsg msg;
char *path;
char **ret;
if (!read_all(h->fd, &msg, sizeof(msg)))
return NULL;
assert(msg.type == XS_WATCH_EVENT);
path = malloc(msg.len);
if (!path)
ret = malloc(sizeof(char *)*2 + msg.len);
if (!ret)
return NULL;
if (!read_all(h->fd, path, msg.len)) {
free_no_errno(path);
ret[0] = (char *)(ret + 2);
if (!read_all(h->fd, ret[0], msg.len)) {
free_no_errno(ret);
return NULL;
}
return path;
ret[1] = ret[0] + strlen(ret[0]) + 1;
return ret;
}
/* Acknowledge watch on node. Watches must be acknowledged before
* any other watches can be read.
* Returns false on failure.
*/
bool xs_acknowledge_watch(struct xs_handle *h)
bool xs_acknowledge_watch(struct xs_handle *h, const char *token)
{
return xs_bool(xs_single(h, XS_WATCH_ACK, "OK", NULL));
return xs_bool(xs_single(h, XS_WATCH_ACK, token, NULL));
}
/* Remove a watch on a node.
* Returns false on failure (no watch on that node).
*/
bool xs_unwatch(struct xs_handle *h, const char *path)
bool xs_unwatch(struct xs_handle *h, const char *path, const char *token)
{
return xs_bool(xs_single(h, XS_UNWATCH, path, NULL));
struct iovec iov[2];
iov[0].iov_base = (char *)path;
iov[0].iov_len = strlen(path) + 1;
iov[1].iov_base = (char *)token;
iov[1].iov_len = strlen(token) + 1;
return xs_bool(xs_talkv(h, XS_UNWATCH, iov, ARRAY_SIZE(iov), NULL));
}
/* Start a transaction: changes by others will not be seen during this
@@ -488,11 +500,8 @@ bool xs_transaction_end(struct xs_handle *h, bool abort)
* This tells the store daemon about a shared memory page and event channel
* associated with a domain: the domain uses these to communicate.
*/
bool xs_introduce_domain(struct xs_handle *h,
domid_t domid,
unsigned long mfn,
unsigned int eventchn,
const char *path)
bool xs_introduce_domain(struct xs_handle *h, domid_t domid, unsigned long mfn,
unsigned int eventchn, const char *path)
{
char domid_str[MAX_STRLEN(domid)];
char mfn_str[MAX_STRLEN(mfn)];
@@ -515,8 +524,7 @@ bool xs_introduce_domain(struct xs_handle *h,
return xs_bool(xs_talkv(h, XS_INTRODUCE, iov, ARRAY_SIZE(iov), NULL));
}
bool xs_release_domain(struct xs_handle *h,
domid_t domid)
bool xs_release_domain(struct xs_handle *h, domid_t domid)
{
char domid_str[MAX_STRLEN(domid)];
+20 -21
View File
@@ -1,5 +1,3 @@
#ifndef _XS_H
#define _XS_H
/*
Xen Store Daemon providing simple tree-like database.
Copyright (C) 2005 Rusty Russell IBM Corporation
@@ -19,11 +17,15 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* On failure, these routines set errno. */
#ifndef _XS_H
#define _XS_H
#include "xs_lib.h"
struct xs_handle;
/* On failure, these routines set errno. */
/* Connect to the xs daemon.
* Returns a handle or NULL.
*/
@@ -52,8 +54,8 @@ void *xs_read(struct xs_handle *h, const char *path, unsigned int *len);
/* Write the value of a single file.
* Returns false on failure. createflags can be 0, O_CREAT, or O_CREAT|O_EXCL.
*/
bool xs_write(struct xs_handle *h, const char *path, const void *data, unsigned int len,
int createflags);
bool xs_write(struct xs_handle *h, const char *path, const void *data,
unsigned int len, int createflags);
/* Create a new directory.
* Returns false on failure.
@@ -69,42 +71,42 @@ bool xs_rm(struct xs_handle *h, const char *path);
* Returns malloced array, or NULL: call free() after use.
*/
struct xs_permissions *xs_get_permissions(struct xs_handle *h,
const char *path,
unsigned int *num);
const char *path, unsigned int *num);
/* Set permissions of node (must be owner).
* Returns false on failure.
*/
bool xs_set_permissions(struct xs_handle *h,
const char *path,
struct xs_permissions *perms,
unsigned int num_perms);
bool xs_set_permissions(struct xs_handle *h, const char *path,
struct xs_permissions *perms, unsigned int num_perms);
/* Watch a node for changes (poll on fd to detect, or call read_watch()).
* When the node (or any child) changes, fd will become readable.
* Token is returned when watch is read, to allow matching.
* Priority indicates order if multiple watchers: higher is first.
* Returns false on failure.
*/
bool xs_watch(struct xs_handle *h, const char *path, unsigned int priority);
bool xs_watch(struct xs_handle *h, const char *path, const char *token,
unsigned int priority);
/* Return the FD to poll on to see if a watch has fired. */
int xs_fileno(struct xs_handle *h);
/* Find out what node change was on (will block if nothing pending).
* Returns malloced path, or NULL: call free() after use.
* Returns array of two pointers: path and token, or NULL.
* Call free() after use.
*/
char *xs_read_watch(struct xs_handle *h);
char **xs_read_watch(struct xs_handle *h);
/* Acknowledge watch on node. Watches must be acknowledged before
* any other watches can be read.
* Returns false on failure.
*/
bool xs_acknowledge_watch(struct xs_handle *h);
bool xs_acknowledge_watch(struct xs_handle *h, const char *token);
/* Remove a watch on a node.
* Returns false on failure (no watch on that node).
*/
bool xs_unwatch(struct xs_handle *h, const char *path);
bool xs_unwatch(struct xs_handle *h, const char *path, const char *token);
/* Start a transaction: changes by others will not be seen during this
* transaction, and changes will not be visible to others until end.
@@ -125,11 +127,8 @@ bool xs_transaction_end(struct xs_handle *h, bool abort);
* This tells the store daemon about a shared memory page, event channel
* and store path associated with a domain: the domain uses these to communicate.
*/
bool xs_introduce_domain(struct xs_handle *h,
domid_t domid,
unsigned long mfn,
unsigned int eventchn,
const char *path);
bool xs_introduce_domain(struct xs_handle *h, domid_t domid, unsigned long mfn,
unsigned int eventchn, const char *path);
/* Release a domain.
* Tells the store domain to release the memory page to the domain.
+1 -2
View File
@@ -67,7 +67,7 @@ bool xs_write_all(int fd, const void *data, unsigned int len)
/* Convert strings to permissions. False if a problem. */
bool xs_strings_to_perms(struct xs_permissions *perms, unsigned int num,
const char *strings)
const char *strings)
{
const char *p;
char *end;
@@ -138,4 +138,3 @@ unsigned int xs_count_strings(const char *strings, unsigned int len)
return num;
}
+5 -3
View File
@@ -1,5 +1,3 @@
#ifndef _XR_LIB_H
#define _XR_LIB_H
/*
Common routines between Xen store user library and daemon.
Copyright (C) 2005 Rusty Russell IBM Corporation
@@ -18,6 +16,10 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _XS_LIB_H
#define _XS_LIB_H
#include <stdbool.h>
#include <limits.h>
#include <xc.h>
@@ -52,7 +54,7 @@ bool xs_write_all(int fd, const void *data, unsigned int len);
/* Convert strings to permissions. False if a problem. */
bool xs_strings_to_perms(struct xs_permissions *perms, unsigned int num,
const char *strings);
const char *strings);
/* Convert permissions to a string (up to len MAX_STRLEN(domid_t)+1). */
bool xs_perm_to_string(const struct xs_permissions *perm, char *buffer);
+1 -1
View File
@@ -50,7 +50,7 @@ static void work(unsigned int cycles, unsigned int childnum)
}
if (streq(lockdir, ""))
strcpy(lockdir, "/");
if (!xs_transaction_start(h, lockdir))
barf_perror("%i: starting transaction %i on %s",
childnum, i, lockdir);
+18 -17
View File
@@ -173,9 +173,9 @@ static void __attribute__((noreturn)) usage(void)
" getperm <path>\n"
" setperm <path> <id> <flags> ...\n"
" shutdown\n"
" watch <path> <prio>\n"
" watch <path> <token> <prio>\n"
" waitwatch\n"
" ackwatch\n"
" ackwatch <token>\n"
" unwatch <path> <token>\n"
" close\n"
" start <node>\n"
@@ -358,36 +358,37 @@ static void do_shutdown(unsigned int handle)
failed(handle);
}
static void do_watch(unsigned int handle, const char *node, const char *pri)
static void do_watch(unsigned int handle, const char *node, const char *token,
const char *pri)
{
if (!xs_watch(handles[handle], node, atoi(pri)))
if (!xs_watch(handles[handle], node, token, atoi(pri)))
failed(handle);
}
static void do_waitwatch(unsigned int handle)
{
char *node;
char **vec;
node = xs_read_watch(handles[handle]);
if (!node)
vec = xs_read_watch(handles[handle]);
if (!vec)
failed(handle);
if (handle)
printf("%i:%s\n", handle, node);
printf("%i:%s:%s\n", handle, vec[0], vec[1]);
else
printf("%s\n", node);
free(node);
printf("%s:%s\n", vec[0], vec[1]);
free(vec);
}
static void do_ackwatch(unsigned int handle)
static void do_ackwatch(unsigned int handle, const char *token)
{
if (!xs_acknowledge_watch(handles[handle]))
if (!xs_acknowledge_watch(handles[handle], token))
failed(handle);
}
static void do_unwatch(unsigned int handle, const char *node)
static void do_unwatch(unsigned int handle, const char *node, const char *token)
{
if (!xs_unwatch(handles[handle], node))
if (!xs_unwatch(handles[handle], node, token))
failed(handle);
}
@@ -613,13 +614,13 @@ int main(int argc, char *argv[])
else if (streq(command, "shutdown"))
do_shutdown(handle);
else if (streq(command, "watch"))
do_watch(handle, arg(line, 1), arg(line, 2));
do_watch(handle, arg(line, 1), arg(line, 2), arg(line, 3));
else if (streq(command, "waitwatch"))
do_waitwatch(handle);
else if (streq(command, "ackwatch"))
do_ackwatch(handle);
do_ackwatch(handle, arg(line, 1));
else if (streq(command, "unwatch"))
do_unwatch(handle, arg(line, 1));
do_unwatch(handle, arg(line, 1), arg(line, 2));
else if (streq(command, "close")) {
xs_daemon_close(handles[handle]);
handles[handle] = NULL;
+120
View File
@@ -0,0 +1,120 @@
/* Stress test for watch code: two processes communicating by watches */
#include "xs.h"
#include "utils.h"
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc __attribute__((unused)), char *argv[])
{
int childpid, status, fds[2];
bool parent;
unsigned int i, acks = 0;
struct xs_handle *h;
char *data;
unsigned int len;
const char *path, *otherpath;
pipe(fds);
childpid = fork();
if (childpid == -1)
barf_perror("Failed fork");
parent = (childpid != 0);
h = xs_daemon_open();
if (!h)
barf_perror("Could not connect to daemon");
if (!xs_watch(h, "/", "token", 0))
barf_perror("Could not set watch");
if (parent) {
char c;
if (read(fds[0], &c, 1) != 1)
barf("Child exited");
path = "/parent";
otherpath = "/child";
/* Create initial node. */
if (!xs_write(h, path, "0", 2, O_CREAT))
barf_perror("Write to %s failed", path);
} else {
path = "/child";
otherpath = "/parent";
if (write(fds[1], "", 1) != 1)
barf_perror("Write to parent failed");
}
for (i = 0; i < (argv[1] ? (unsigned)atoi(argv[1]) : 100);) {
char **vec;
vec = xs_read_watch(h);
if (!vec)
barf_perror("Read watch failed");
if (!streq(vec[1], "token"))
barf("Watch token %s bad", vec[1]);
if (streq(vec[0], otherpath)) {
char number[32];
data = xs_read(h, otherpath, &len);
if (!data)
barf_perror("reading %s", otherpath);
sprintf(number, "%i", atoi(data) + 1);
free(data);
if (!xs_write(h, path, number, strlen(number) + 1,
O_CREAT))
barf_perror("writing %s", path);
i++;
} else if (!streq(vec[0], path))
barf_perror("Watch fired on unknown path %s", vec[0]);
xs_acknowledge_watch(h, vec[1]);
acks++;
free(vec);
}
if (!parent) {
while (acks != 2 * i - 1) {
char **vec;
vec = xs_read_watch(h);
if (!vec)
barf_perror("Watch failed");
if (!streq(vec[0], path))
barf_perror("Watch fired path %s", vec[0]);
if (!streq(vec[1], "token"))
barf("Watch token %s bad", vec[1]);
free(vec);
printf("Expect %i events, only got %i\n",
2 * i - 1, acks);
acks++;
}
exit(0);
}
if (acks != 2 * i)
barf("Parent got %i watch events\n", acks);
printf("Waiting for %i\n", childpid);
if (waitpid(childpid, &status, 0) != childpid)
barf_perror("Child wait failed");
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
barf_perror("Child status %i", status);
data = xs_read(h, path, &len);
if (atoi(data) != 2 * (int)i)
barf("%s count is %s\n", path, data);
free(data);
data = xs_read(h, otherpath, &len);
if (atoi(data) != 2 * (int)i - 1)
barf("%s count is %s\n", otherpath, data);
free(data);
printf("Success!\n");
exit(0);
}
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.