mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
xenstored has had a testsuite for some time. This splits the test
targets into "make -s check" (the GNU standard) which is designed to run in several seconds or less, with only "<dirname>:" and a seried of "." for output, and "make fullcheck" which is more verbose and complete, and runs in minutes. I plan to submit a patch to plumb these tests in at the top level soon, with the idea of having a "make -s check" which you can reasonable run every checkin (time make check << time make), and a "make fullcheck" which you can run after significant changes. This patch speeds up testing by making tests one single "xs_test" script, enhancing xs_test with a simple "expect" command. It also only runs the "high value" tests under "make check", and leaves the rest for "make fullcheck". Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
1 parent
d2f97d17d4
commit
a3cbc30d9f
17 files changed
+246
-782
No files matched your search
+18
-4
@@ -69,10 +69,21 @@ clean: testsuite-clean
|
||||
rm -f xs_test xenstored_test xs_dom0_test
|
||||
-$(RM) $(PROG_DEP)
|
||||
|
||||
check: testsuite-run randomcheck stresstest
|
||||
print-dir:
|
||||
@echo -n tools/xenstore:
|
||||
|
||||
print-end:
|
||||
@echo
|
||||
|
||||
check: print-dir testsuite-fast randomcheck-fast print-end
|
||||
|
||||
fullcheck: testsuite-run randomcheck stresstest
|
||||
|
||||
testsuite-run: xen xenstored_test xs_test
|
||||
$(TESTENV) testsuite/test.sh
|
||||
$(TESTENV) testsuite/test.sh && echo
|
||||
|
||||
testsuite-fast: xen xenstored_test xs_test
|
||||
@$(TESTENV) testsuite/test.sh --fast
|
||||
|
||||
testsuite-clean:
|
||||
rm -rf $(TESTDIR)
|
||||
@@ -81,10 +92,13 @@ testsuite-clean:
|
||||
# fail.
|
||||
RANDSEED=$(shell date +%s)
|
||||
randomcheck: xs_random xenstored_test
|
||||
$(TESTENV) ./xs_random --simple --fast /tmp/xs_random 200000 $(RANDSEED)
|
||||
$(TESTENV) ./xs_random --fast /tmp/xs_random 100000 $(RANDSEED)
|
||||
$(TESTENV) ./xs_random --simple --fast /tmp/xs_random 200000 $(RANDSEED) && echo
|
||||
$(TESTENV) ./xs_random --fast /tmp/xs_random 100000 $(RANDSEED) && echo
|
||||
$(TESTENV) ./xs_random --fail /tmp/xs_random 10000 $(RANDSEED)
|
||||
|
||||
randomcheck-fast: xs_random xenstored_test
|
||||
@$(TESTENV) ./xs_random --fast /tmp/xs_random 10000 $(RANDSEED)
|
||||
|
||||
stresstest: xs_stress xs_watch_stress xenstored_test
|
||||
rm -rf $(TESTDIR)/store $(TESTDIR)/transactions
|
||||
export $(TESTENV); PID=`./xenstored_test --output-pid --trace-file=/tmp/trace`; ./xs_stress 5000; ret=$$?; kill $$PID; exit $$ret
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Create an entry, read it.
|
||||
[ "`echo -e 'write /test create contents\nread /test' | ./xs_test 2>&1`" = "contents" ]
|
||||
@@ -1,32 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Root directory has only tool dir in it.
|
||||
[ "`echo -e 'dir /' | ./xs_test 2>&1`" = "tool" ]
|
||||
|
||||
# Create a file.
|
||||
[ "`echo -e 'write /test create contents' | ./xs_test 2>&1`" = "" ]
|
||||
|
||||
# Directory shows it.
|
||||
[ "`echo -e 'dir /' | ./xs_test 2>&1 | sort`" = "test
|
||||
tool" ]
|
||||
|
||||
# Make a new directory.
|
||||
[ "`echo -e 'mkdir /dir' | ./xs_test 2>&1`" = "" ]
|
||||
|
||||
# Check it's there.
|
||||
DIR="`echo -e 'dir /' | ./xs_test 2>&1 | sort`"
|
||||
[ "$DIR" = "dir
|
||||
test
|
||||
tool" ]
|
||||
|
||||
# Check it's empty.
|
||||
[ "`echo -e 'dir /dir' | ./xs_test 2>&1`" = "" ]
|
||||
|
||||
# Create a file, check it exists.
|
||||
[ "`echo -e 'write /dir/test2 create contents2' | ./xs_test 2>&1`" = "" ]
|
||||
[ "`echo -e 'dir /dir' | ./xs_test 2>&1`" = "test2" ]
|
||||
[ "`echo -e 'read /dir/test2' | ./xs_test 2>&1`" = "contents2" ]
|
||||
|
||||
# Creating dir over the top should fail.
|
||||
[ "`echo -e 'mkdir /dir' | ./xs_test 2>&1`" = "FATAL: mkdir: File exists" ]
|
||||
[ "`echo -e 'mkdir /dir/test2' | ./xs_test 2>&1`" = "FATAL: mkdir: File exists" ]
|
||||
@@ -1,17 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Write without create fails.
|
||||
[ "`echo -e 'write /test none contents' | ./xs_test 2>&1`" = "FATAL: write: No such file or directory" ]
|
||||
|
||||
# Exclusive write succeeds
|
||||
[ "`echo -e 'write /test excl contents' | ./xs_test 2>&1`" = "" ]
|
||||
[ "`echo -e 'read /test' | ./xs_test 2>&1`" = "contents" ]
|
||||
|
||||
# Exclusive write fails to overwrite.
|
||||
[ "`echo -e 'write /test excl contents' | ./xs_test 2>&1`" = "FATAL: write: File exists" ]
|
||||
|
||||
# Non-exclusive overwrite succeeds.
|
||||
[ "`echo -e 'write /test none contents2' | ./xs_test 2>&1`" = "" ]
|
||||
[ "`echo -e 'read /test' | ./xs_test 2>&1`" = "contents2" ]
|
||||
[ "`echo -e 'write /test create contents3' | ./xs_test 2>&1`" = "" ]
|
||||
[ "`echo -e 'read /test' | ./xs_test 2>&1`" = "contents3" ]
|
||||
@@ -1,18 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Remove non-existant fails.
|
||||
[ "`echo -e 'rm /test' | ./xs_test 2>&1`" = "FATAL: rm: No such file or directory" ]
|
||||
[ "`echo -e 'rm /dir/test' | ./xs_test 2>&1`" = "FATAL: rm: No such file or directory" ]
|
||||
|
||||
# Create file and remove it
|
||||
[ "`echo -e 'write /test excl contents' | ./xs_test 2>&1`" = "" ]
|
||||
[ "`echo -e 'rm /test' | ./xs_test 2>&1`" = "" ]
|
||||
|
||||
# Create directory and remove it.
|
||||
[ "`echo -e 'mkdir /dir' | ./xs_test 2>&1`" = "" ]
|
||||
[ "`echo -e 'rm /dir' | ./xs_test 2>&1`" = "" ]
|
||||
|
||||
# Create directory, create file, remove all.
|
||||
[ "`echo -e 'mkdir /dir' | ./xs_test 2>&1`" = "" ]
|
||||
[ "`echo -e 'write /dir/test excl contents' | ./xs_test 2>&1`" = "" ]
|
||||
[ "`echo -e 'rm /dir' | ./xs_test 2>&1`" = "" ]
|
||||
@@ -1,49 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Fail to get perms on non-existent file.
|
||||
[ "`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: inherits from root (0 READ)
|
||||
[ "`echo -e 'write /test excl contents' | ./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" ]
|
||||
[ "`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`" = "" ]
|
||||
[ "`echo -e 'read /test' | ./xs_test 2>&1`" = "contents2" ]
|
||||
|
||||
# Grant everyone both read and write access.
|
||||
[ "`echo -e 'setperm /test 0 READ/WRITE' | ./xs_test 2>&1`" = "" ]
|
||||
[ "`echo -e 'setid 1\ngetperm /test' | ./xs_test 2>&1`" = "0 READ/WRITE" ]
|
||||
[ "`echo -e 'setid 1\nread /test' | ./xs_test 2>&1`" = "contents2" ]
|
||||
[ "`echo -e 'setid 1\nwrite /test none contents3' | ./xs_test 2>&1`" = "" ]
|
||||
[ "`echo -e 'setid 1\nread /test' | ./xs_test 2>&1`" = "contents3" ]
|
||||
|
||||
# Change so that user 1 owns it, noone else can do anything.
|
||||
[ "`echo -e 'setperm /test 1 NONE' | ./xs_test 2>&1`" = "" ]
|
||||
[ "`echo -e 'setid 1\ngetperm /test' | ./xs_test 2>&1`" = "1 NONE" ]
|
||||
[ "`echo -e 'setid 1\nread /test' | ./xs_test 2>&1`" = "contents3" ]
|
||||
[ "`echo -e 'setid 1\nwrite /test none contents4' | ./xs_test 2>&1`" = "" ]
|
||||
|
||||
# User 2 can do nothing.
|
||||
[ "`echo -e 'setid 2\nsetperm /test 2 NONE' | ./xs_test 2>&1`" = "FATAL: setperm: Permission denied" ]
|
||||
[ "`echo -e 'setid 2\ngetperm /test' | ./xs_test 2>&1`" = "FATAL: getperm: Permission denied" ]
|
||||
[ "`echo -e 'setid 2\nread /test' | ./xs_test 2>&1`" = "FATAL: read: Permission denied" ]
|
||||
[ "`echo -e 'setid 2\nwrite /test none contents4' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
|
||||
|
||||
# Tools can always access things.
|
||||
[ "`echo -e 'getperm /test' | ./xs_test 2>&1`" = "1 NONE" ]
|
||||
[ "`echo -e 'read /test' | ./xs_test 2>&1`" = "contents4" ]
|
||||
[ "`echo -e 'write /test none contents5' | ./xs_test 2>&1`" = "" ]
|
||||
@@ -1,75 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Root directory: owned by tool, everyone has read access.
|
||||
[ "`echo -e 'getperm /' | ./xs_test 2>&1`" = "0 READ" ]
|
||||
|
||||
# Create directory: inherits from root.
|
||||
[ "`echo -e 'mkdir /dir' | ./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.
|
||||
[ "`echo -e 'setperm /dir 0 READ/WRITE' | ./xs_test 2>&1`" = "" ]
|
||||
[ "`echo -e 'setid 1\ngetperm /dir' | ./xs_test 2>&1`" = "0 READ/WRITE" ]
|
||||
[ "`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`" = "" ]
|
||||
[ "`echo -e 'setid 1\ngetperm /dir' | ./xs_test 2>&1`" = "1 NONE" ]
|
||||
[ "`echo -e 'setid 1\ndir /dir' | ./xs_test 2>&1 | sort`" = "test
|
||||
test2" ]
|
||||
[ "`echo -e 'setid 1\nwrite /dir/test3 create contents' | ./xs_test 2>&1`" = "" ]
|
||||
|
||||
# User 2 can do nothing. Can't even tell if file exists.
|
||||
[ "`echo -e 'setid 2\nsetperm /dir 2 NONE' | ./xs_test 2>&1`" = "FATAL: setperm: Permission denied" ]
|
||||
[ "`echo -e 'setid 2\ngetperm /dir' | ./xs_test 2>&1`" = "FATAL: getperm: Permission denied" ]
|
||||
[ "`echo -e 'setid 2\ndir /dir' | ./xs_test 2>&1`" = "FATAL: dir: Permission denied" ]
|
||||
[ "`echo -e 'setid 2\nread /dir/test' | ./xs_test 2>&1`" = "FATAL: read: Permission denied" ]
|
||||
[ "`echo -e 'setid 2\nread /dir/test2' | ./xs_test 2>&1`" = "FATAL: read: Permission denied" ]
|
||||
[ "`echo -e 'setid 2\nread /dir/test3' | ./xs_test 2>&1`" = "FATAL: read: Permission denied" ]
|
||||
[ "`echo -e 'setid 2\nread /dir/test4' | ./xs_test 2>&1`" = "FATAL: read: Permission denied" ]
|
||||
[ "`echo -e 'setid 2\nwrite /dir/test none contents' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
|
||||
[ "`echo -e 'setid 2\nwrite /dir/test create contents' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
|
||||
[ "`echo -e 'setid 2\nwrite /dir/test excl contents' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
|
||||
[ "`echo -e 'setid 2\nwrite /dir/test4 none contents' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
|
||||
[ "`echo -e 'setid 2\nwrite /dir/test4 create contents' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
|
||||
[ "`echo -e 'setid 2\nwrite /dir/test4 excl contents' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
|
||||
|
||||
# Tools can always access things.
|
||||
[ "`echo -e 'getperm /dir' | ./xs_test 2>&1`" = "1 NONE" ]
|
||||
[ "`echo -e 'dir /dir' | ./xs_test 2>&1 | sort`" = "test
|
||||
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" ]
|
||||
@@ -1,181 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Watch something, write to it, check watch has fired.
|
||||
[ "`echo -e 'write /test create contents' | ./xs_test 2>&1`" = "" ]
|
||||
|
||||
[ "`echo -e '1 watch /test token
|
||||
2 async 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 token
|
||||
2 read /test
|
||||
1 waitwatch' | ./xs_test 2>&1`" = "2:contents2
|
||||
1:waitwatch timeout" ]
|
||||
|
||||
# mkdir, setperm and rm should (also tests watching dirs)
|
||||
[ "`echo -e 'mkdir /dir' | ./xs_test 2>&1`" = "" ]
|
||||
[ "`echo -e '1 watch /dir token
|
||||
2 async mkdir /dir/newdir
|
||||
1 waitwatch
|
||||
1 ackwatch token
|
||||
asyncwait
|
||||
2 async setperm /dir/newdir 0 READ
|
||||
1 waitwatch
|
||||
1 ackwatch token
|
||||
asyncwait
|
||||
2 async rm /dir/newdir
|
||||
1 waitwatch
|
||||
1 ackwatch token' | ./xs_test 2>&1`" = "1:/dir/newdir:token
|
||||
1:/dir/newdir:token
|
||||
1:/dir/newdir:token" ]
|
||||
|
||||
# We don't get a watch from our own commands.
|
||||
[ "`echo -e 'watch /dir token
|
||||
mkdir /dir/newdir
|
||||
waitwatch' | ./xs_test 2>&1`" = "waitwatch timeout" ]
|
||||
|
||||
# ignore watches while doing commands, should work.
|
||||
[ "`echo -e 'watch /dir token
|
||||
1 async write /dir/test create contents
|
||||
read /dir/test
|
||||
waitwatch
|
||||
ackwatch token' | ./xs_test 2>&1`" = "contents
|
||||
/dir/test:token" ]
|
||||
|
||||
# watch priority test: all simultaneous
|
||||
[ "`echo -e '1 watch /dir token1
|
||||
3 watch /dir token3
|
||||
2 watch /dir token2
|
||||
async 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 token1
|
||||
2 watch /dir token2
|
||||
async 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 token1
|
||||
2 watch /dir token2
|
||||
async 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
|
||||
1 unwatch /dir token1
|
||||
1 watch /dir token2
|
||||
2 async write /dir/test2 create contents
|
||||
1 waitwatch
|
||||
1 unwatch /dir token2' | ./xs_test 2>&1`" = "1:/dir/test2:token2" ]
|
||||
|
||||
# unwatch while watch pending. Next watcher gets the event.
|
||||
[ "`echo -e '1 watch /dir token1
|
||||
2 watch /dir token2
|
||||
async 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
|
||||
async write /dir/test create contents
|
||||
1 unwatch /dir token1
|
||||
1 watch /dir/test token2
|
||||
asyncwait
|
||||
async 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
|
||||
2 async 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
|
||||
async 2 write /test1 create contents
|
||||
async 2 write /test2 create contents
|
||||
async 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
|
||||
2 async write /test/subnode create contents2
|
||||
2 async 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" ]
|
||||
|
||||
# Watch event must have happened before we registered interest.
|
||||
[ "`echo -e '1 watch / token
|
||||
2 async write /test/subnode create contents2
|
||||
1 watch / token2 0
|
||||
1 waitwatch
|
||||
1 ackwatch token
|
||||
1 waitwatch' | ./xs_test 2>&1`" = "1:/test/subnode:token
|
||||
1:waitwatch timeout" ]
|
||||
|
||||
# Rm fires notification on child.
|
||||
[ "`echo -e '1 watch /test/subnode token
|
||||
2 async 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
|
||||
2 async 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" ]
|
||||
|
||||
# We can watch something which doesn't exist.
|
||||
[ "`echo '1 watch /dir/subdir token
|
||||
2 mkdir /dir/subdir
|
||||
1 waitwatch' | ./xs_test 2>&1`" = "1:/dir/subdir:token" ]
|
||||
|
||||
# If we don't have permission, we won't see event (rm).
|
||||
[ "`echo '1 setid 1
|
||||
1 watch /dir/subdir token
|
||||
setperm /dir 0 NONE
|
||||
rm /dir/subdir
|
||||
1 waitwatch' | ./xs_test 2>&1`" = "1:waitwatch timeout" ]
|
||||
|
||||
# If we don't have permission, we won't see event (create).
|
||||
[ "`echo '1 setid 1
|
||||
1 watch /dir/subdir token
|
||||
mkdir /dir/subdir
|
||||
write /dir/subdir/entry create contents
|
||||
1 waitwatch' | ./xs_test 2>&1`" = "1:waitwatch timeout" ]
|
||||
@@ -1,93 +0,0 @@
|
||||
#! /bin/sh
|
||||
# Test transactions.
|
||||
|
||||
echo mkdir /test | ./xs_test
|
||||
|
||||
# Simple transaction: create a file inside transaction.
|
||||
[ "`echo -e '1 start /test
|
||||
1 write /test/entry1 create contents
|
||||
2 dir /test
|
||||
1 dir /test
|
||||
1 commit
|
||||
2 read /test/entry1' | ./xs_test`" = "1:entry1
|
||||
2:contents" ]
|
||||
echo rm /test/entry1 | ./xs_test
|
||||
|
||||
# Create a file and abort transaction.
|
||||
[ "`echo -e '1 start /test
|
||||
1 write /test/entry1 create contents
|
||||
2 dir /test
|
||||
1 dir /test
|
||||
1 abort
|
||||
2 dir /test' | ./xs_test`" = "1:entry1" ]
|
||||
|
||||
echo write /test/entry1 create contents | ./xs_test
|
||||
# Delete in transaction, commit
|
||||
[ "`echo -e '1 start /test
|
||||
1 rm /test/entry1
|
||||
2 dir /test
|
||||
1 dir /test
|
||||
1 commit
|
||||
2 dir /test' | ./xs_test`" = "2:entry1" ]
|
||||
|
||||
# Delete in transaction, abort.
|
||||
echo write /test/entry1 create contents | ./xs_test
|
||||
[ "`echo -e '1 start /test
|
||||
1 rm /test/entry1
|
||||
2 dir /test
|
||||
1 dir /test
|
||||
1 abort
|
||||
2 dir /test' | ./xs_test`" = "2:entry1
|
||||
2:entry1" ]
|
||||
|
||||
# Transactions can take as long as the want...
|
||||
[ "`echo -e 'start /test
|
||||
sleep 1
|
||||
rm /test/entry1
|
||||
commit
|
||||
dir /test' | ./xs_test --no-timeout`" = "" ]
|
||||
|
||||
# ... as long as noone is waiting.
|
||||
[ "`echo -e '1 start /test
|
||||
2 mkdir /test/dir
|
||||
1 mkdir /test/dir
|
||||
1 dir /test
|
||||
1 commit' | ./xs_test --no-timeout 2>&1`" = "1:dir
|
||||
FATAL: 1: commit: Connection timed out" ]
|
||||
|
||||
# Events inside transactions don't trigger watches until (successful) commit.
|
||||
[ "`echo -e '1 watch /test token
|
||||
2 start /test
|
||||
2 mkdir /test/dir/sub
|
||||
1 waitwatch' | ./xs_test 2>&1`" = "1:waitwatch timeout" ]
|
||||
[ "`echo -e '1 watch /test token
|
||||
2 start /test
|
||||
2 mkdir /test/dir/sub
|
||||
2 abort
|
||||
1 waitwatch' | ./xs_test 2>&1`" = "1:waitwatch timeout" ]
|
||||
[ "`echo -e '1 watch /test token
|
||||
2 start /test
|
||||
2 mkdir /test/dir/sub
|
||||
2 async commit
|
||||
1 waitwatch
|
||||
1 ackwatch token' | ./xs_test 2>&1`" = "1:/test/dir/sub:token" ]
|
||||
|
||||
# Rm inside transaction works like rm outside: children get notified.
|
||||
[ "`echo -e '1 watch /test/dir/sub token
|
||||
2 start /test
|
||||
2 rm /test/dir
|
||||
2 async commit
|
||||
1 waitwatch
|
||||
1 ackwatch token' | ./xs_test 2>&1`" = "1:/test/dir/sub:token" ]
|
||||
|
||||
# Multiple events from single transaction don't trigger assert
|
||||
[ "`echo -e '1 watch /test token
|
||||
2 start /test
|
||||
2 write /test/1 create contents
|
||||
2 write /test/2 create contents
|
||||
2 async commit
|
||||
1 waitwatch
|
||||
1 ackwatch token
|
||||
1 waitwatch
|
||||
1 ackwatch token' | ./xs_test 2>&1`" = "1:/test/1:token
|
||||
1:/test/2:token" ]
|
||||
@@ -1,16 +0,0 @@
|
||||
#! /bin/sh
|
||||
# Test domain communication.
|
||||
|
||||
# Create a domain, write an entry.
|
||||
[ "`echo -e 'introduce 1 100 7 /my/home
|
||||
1 write /entry1 create contents
|
||||
dir /' | ./xs_test 2>&1 | sort`" = "entry1
|
||||
handle is 1
|
||||
tool" ]
|
||||
|
||||
# Release that domain.
|
||||
[ "`echo -e 'release 1' | ./xs_test`" = "" ]
|
||||
|
||||
# Introduce and release by same connection.
|
||||
[ "`echo -e 'introduce 1 100 7 /my/home
|
||||
release 1' | ./xs_test 2>&1`" = "handle is 1" ]
|
||||
@@ -1,20 +0,0 @@
|
||||
#! /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" ]
|
||||
|
||||
# Place a watch using a relative path: expect relative answer.
|
||||
[ "`echo 'introduce 1 100 7 /home
|
||||
1 mkdir foo
|
||||
1 watch foo token
|
||||
async write /home/foo/bar create contents
|
||||
1 waitwatch
|
||||
1 ackwatch token' | ./xs_test 2>&1`" = "handle is 1
|
||||
1:foo/bar:token" ]
|
||||
@@ -1,55 +0,0 @@
|
||||
#! /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
|
||||
async write /test create contents2
|
||||
1 waitwatch
|
||||
1 ackwatch token
|
||||
1 unwatch /test token
|
||||
asyncwait
|
||||
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
|
||||
async write /dir/test create contents
|
||||
1 write /dir/test2 create contents2
|
||||
1 write /dir/test3 create contents3
|
||||
1 write /dir/test4 create contents4
|
||||
1 waitwatch
|
||||
1 ackwatch token
|
||||
asyncwait
|
||||
release 1' | ./xs_test 2>&1`" = "handle is 1
|
||||
1:/dir/test:token" ]
|
||||
|
||||
# unwatch
|
||||
[ "`echo -e 'introduce 1 100 7 /my/home
|
||||
1 watch /dir token1
|
||||
1 unwatch /dir token1
|
||||
1 watch /dir token2
|
||||
async 2 write /dir/test2 create contents
|
||||
1 waitwatch
|
||||
1 unwatch /dir token2
|
||||
asyncwait
|
||||
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
|
||||
2 watch /dir token2
|
||||
3 async 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" ]
|
||||
@@ -1,38 +0,0 @@
|
||||
#! /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 / | ./xs_test --readonly 2>&1 | sort`" = "test
|
||||
tool" ]
|
||||
|
||||
[ "`echo 'read /test
|
||||
getperm /test
|
||||
watch /test token
|
||||
unwatch /test token
|
||||
start /
|
||||
commit
|
||||
start /
|
||||
abort' | ./xs_test --readonly 2>&1`" = "contents
|
||||
0 READ" ]
|
||||
|
||||
# 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
|
||||
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,23 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
# This demonstrates a bug where an xs_acknowledge_watch returns
|
||||
# EINVAL, because the daemon doesn't track what watch event it sent
|
||||
# and relies on it being the "first" watch which has an event.
|
||||
# Watches firing after the first event is sent out will change this.
|
||||
|
||||
# Create three things to watch.
|
||||
echo mkdir /test | ./xs_test
|
||||
echo mkdir /test/1 | ./xs_test
|
||||
echo mkdir /test/2 | ./xs_test
|
||||
echo mkdir /test/3 | ./xs_test
|
||||
|
||||
# Watch all three, fire event on 2, read watch, fire event on 1 and 3, ack 2.
|
||||
[ "`echo '1 watch /test/1 token1 0
|
||||
1 watch /test/2 token2 0
|
||||
1 watch /test/3 token3 0
|
||||
2 async write /test/2 create contents2
|
||||
3 async write /test/1 create contents1
|
||||
4 async write /test/3 create contents3
|
||||
1 waitwatch
|
||||
1 ackwatch token2
|
||||
1 close' | ./xs_test 2>&1`" = "1:/test/2:token2" ]
|
||||
@@ -7,19 +7,16 @@ run_test()
|
||||
{
|
||||
rm -rf $XENSTORED_ROOTDIR
|
||||
mkdir $XENSTORED_ROOTDIR
|
||||
# Weird failures with this.
|
||||
if type valgrind >/dev/null 2>&1; then
|
||||
if [ $VALGRIND -eq 1 ]; then
|
||||
valgrind -q --logfile-fd=3 ./xenstored_test --output-pid --trace-file=testsuite/tmp/trace --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
|
||||
else
|
||||
./xenstored_test --output-pid --trace-file=testsuite/tmp/trace --no-fork > /tmp/pid 2> testsuite/tmp/xenstored_errors &
|
||||
while [ ! -s /tmp/pid ]; do sleep 0; done
|
||||
PID=`cat /tmp/pid`
|
||||
rm /tmp/pid
|
||||
# We don't get error messages from this, though.
|
||||
PID=`./xenstored_test --output-pid --trace-file=testsuite/tmp/trace`
|
||||
fi
|
||||
if sh -e $2 $1; then
|
||||
if ./xs_test $2 $1; then
|
||||
if [ -s testsuite/tmp/vgout ]; then
|
||||
kill $PID
|
||||
echo VALGRIND errors:
|
||||
@@ -36,12 +33,27 @@ run_test()
|
||||
fi
|
||||
}
|
||||
|
||||
if [ x$1 = x--fast ]; then
|
||||
VALGRIND=0
|
||||
SLOWTESTS=""
|
||||
shift
|
||||
else
|
||||
if type valgrind >/dev/null 2>&1; then
|
||||
VALGRIND=1
|
||||
else
|
||||
echo "WARNING: valgrind not available" >&2
|
||||
VALGRIND=0
|
||||
fi
|
||||
SLOWTESTS=testsuite/[0-9]*.slowtest
|
||||
fi
|
||||
|
||||
MATCH=${1:-"*"}
|
||||
for f in testsuite/[0-9]*.sh; do
|
||||
for f in testsuite/[0-9]*.test $SLOWTESTS; do
|
||||
case `basename $f` in $MATCH) RUN=1;; esac
|
||||
[ -n "$RUN" ] || continue
|
||||
if run_test $f; then
|
||||
echo Test $f passed...
|
||||
|
||||
if run_test $f > /dev/null; then
|
||||
echo -n .
|
||||
else
|
||||
echo Test $f failed, running verbosely...
|
||||
run_test $f -x || true
|
||||
|
||||
@@ -1112,9 +1112,6 @@ static unsigned int try_simple(const bool *trymap,
|
||||
data->ops->close(pre);
|
||||
}
|
||||
}
|
||||
if (data->print_progress)
|
||||
printf("\n");
|
||||
|
||||
out:
|
||||
data->ops->close(h);
|
||||
return i;
|
||||
@@ -1192,10 +1189,9 @@ static void simple_test(const char *dir,
|
||||
try = try_simple(NULL, iters, verbose, &data);
|
||||
if (try == iters) {
|
||||
cleanup_xs_ops();
|
||||
printf("Succeeded\n");
|
||||
exit(0);
|
||||
}
|
||||
printf("Failed on iteration %u\n", try + 1);
|
||||
printf("Failed on iteration %u of seed %u\n", try + 1, seed);
|
||||
data.print_progress = false;
|
||||
reduce_problem(try + 1, try_simple, &data);
|
||||
}
|
||||
@@ -1406,8 +1402,6 @@ static unsigned int try_diff(const bool *trymap,
|
||||
talloc_free(fileh_pre);
|
||||
}
|
||||
}
|
||||
if (data->print_progress)
|
||||
printf("\n");
|
||||
|
||||
fail = NULL;
|
||||
if (data->fast)
|
||||
@@ -1435,10 +1429,9 @@ static void diff_test(const char *dir,
|
||||
try = try_diff(NULL, iters, verbose, &data);
|
||||
if (try == iters) {
|
||||
cleanup_xs_ops();
|
||||
printf("Succeeded\n");
|
||||
exit(0);
|
||||
}
|
||||
printf("Failed on iteration %u\n", try + 1);
|
||||
printf("Failed on iteration %u of seed %u\n", try + 1, seed);
|
||||
data.print_progress = false;
|
||||
reduce_problem(try + 1, try_diff, &data);
|
||||
}
|
||||
@@ -1593,8 +1586,6 @@ static unsigned int try_fail(const bool *trymap,
|
||||
xs_close(tmpxsh);
|
||||
file_close(tmpfileh);
|
||||
}
|
||||
|
||||
printf("Total %u of %u not aborted\n", tried - aborted, tried);
|
||||
out:
|
||||
if (xsh)
|
||||
xs_close(xsh);
|
||||
@@ -1615,10 +1606,9 @@ static void fail_test(const char *dir,
|
||||
try = try_fail(NULL, iters, verbose, &data);
|
||||
if (try == iters) {
|
||||
cleanup_xs_ops();
|
||||
printf("Succeeded\n");
|
||||
exit(0);
|
||||
}
|
||||
printf("Failed on iteration %u\n", try + 1);
|
||||
printf("Failed on iteration %u of seed %u\n", try + 1, seed);
|
||||
fflush(stdout);
|
||||
data.print_progress = false;
|
||||
reduce_problem(try + 1, try_fail, &data);
|
||||
|
||||
+203
-134
@@ -17,6 +17,7 @@
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
@@ -27,17 +28,26 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <fnmatch.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/mman.h>
|
||||
#include "utils.h"
|
||||
#include "xs_lib.h"
|
||||
#include "list.h"
|
||||
|
||||
#define XSTEST
|
||||
|
||||
static struct xs_handle *handles[10] = { NULL };
|
||||
static unsigned int children;
|
||||
|
||||
static bool timeout = true;
|
||||
static unsigned int timeout_ms = 50;
|
||||
static bool timeout_suppressed = true;
|
||||
static bool readonly = false;
|
||||
static bool print_input = false;
|
||||
static unsigned int linenum = 0;
|
||||
|
||||
struct ringbuf_head
|
||||
{
|
||||
@@ -178,7 +188,7 @@ static bool write_all_choice(int fd, const void *data, unsigned int len)
|
||||
static void __attribute__((noreturn)) usage(void)
|
||||
{
|
||||
barf("Usage:\n"
|
||||
" xs_test [--readonly] [--notimeout]\n"
|
||||
" xs_test [--readonly] [--no-timeout] [-x]\n"
|
||||
"Reads commands from stdin, one per line:"
|
||||
" dir <path>\n"
|
||||
" read <path>\n"
|
||||
@@ -190,8 +200,6 @@ static void __attribute__((noreturn)) usage(void)
|
||||
" setperm <path> <id> <flags> ...\n"
|
||||
" shutdown\n"
|
||||
" watch <path> <token>\n"
|
||||
" async <command>...\n"
|
||||
" asyncwait\n"
|
||||
" waitwatch\n"
|
||||
" ackwatch <token>\n"
|
||||
" unwatch <path> <token>\n"
|
||||
@@ -200,7 +208,11 @@ static void __attribute__((noreturn)) usage(void)
|
||||
" abort\n"
|
||||
" introduce <domid> <mfn> <eventchn> <path>\n"
|
||||
" commit\n"
|
||||
" sleep <seconds>\n"
|
||||
" sleep <milliseconds>\n"
|
||||
" expect <pattern>\n"
|
||||
" notimeout\n"
|
||||
" readonly\n"
|
||||
" readwrite\n"
|
||||
" dump\n");
|
||||
}
|
||||
|
||||
@@ -218,7 +230,7 @@ static int argpos(const char *line, unsigned int num)
|
||||
return off;
|
||||
}
|
||||
|
||||
static char *arg(char *line, unsigned int num)
|
||||
static char *arg(const char *line, unsigned int num)
|
||||
{
|
||||
static char *args[10];
|
||||
unsigned int off, len;
|
||||
@@ -236,12 +248,64 @@ static char *arg(char *line, unsigned int num)
|
||||
return args[num];
|
||||
}
|
||||
|
||||
struct expect
|
||||
{
|
||||
struct list_head list;
|
||||
char *pattern;
|
||||
};
|
||||
static LIST_HEAD(expects);
|
||||
|
||||
static char *command;
|
||||
static void __attribute__((noreturn)) failed(int handle)
|
||||
|
||||
/* Trim leading and trailing whitespace */
|
||||
static void trim(char *str)
|
||||
{
|
||||
while (isspace(str[0]))
|
||||
memmove(str, str+1, strlen(str));
|
||||
|
||||
while (strlen(str) && isspace(str[strlen(str)-1]))
|
||||
str[strlen(str)-1] = '\0';
|
||||
}
|
||||
|
||||
static void output(const char *fmt, ...)
|
||||
{
|
||||
char *str;
|
||||
struct expect *i;
|
||||
va_list arglist;
|
||||
|
||||
va_start(arglist, fmt);
|
||||
vasprintf(&str, fmt, arglist);
|
||||
va_end(arglist);
|
||||
|
||||
printf("%s", str);
|
||||
fflush(stdout);
|
||||
trim(str);
|
||||
list_for_each_entry(i, &expects, list) {
|
||||
if (fnmatch(i->pattern, str, 0) == 0) {
|
||||
list_del(&i->list);
|
||||
free(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
barf("Unexpected output %s\n", str);
|
||||
}
|
||||
|
||||
static void failed(int handle)
|
||||
{
|
||||
if (handle)
|
||||
barf_perror("%i: %s", handle, command);
|
||||
barf_perror("%s", command);
|
||||
output("%i: %s failed: %s\n",
|
||||
handle, command, strerror(errno));
|
||||
else
|
||||
output("%s failed: %s\n", command, strerror(errno));
|
||||
}
|
||||
|
||||
static void expect(const char *line)
|
||||
{
|
||||
struct expect *e = malloc(sizeof(*e));
|
||||
|
||||
e->pattern = strdup(line + argpos(line, 1));
|
||||
trim(e->pattern);
|
||||
list_add(&e->list, &expects);
|
||||
}
|
||||
|
||||
static void do_dir(unsigned int handle, char *path)
|
||||
@@ -250,14 +314,16 @@ static void do_dir(unsigned int handle, char *path)
|
||||
unsigned int i, num;
|
||||
|
||||
entries = xs_directory(handles[handle], path, &num);
|
||||
if (!entries)
|
||||
if (!entries) {
|
||||
failed(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
if (handle)
|
||||
printf("%i:%s\n", handle, entries[i]);
|
||||
output("%i:%s\n", handle, entries[i]);
|
||||
else
|
||||
printf("%s\n", entries[i]);
|
||||
output("%s\n", entries[i]);
|
||||
free(entries);
|
||||
}
|
||||
|
||||
@@ -267,15 +333,17 @@ static void do_read(unsigned int handle, char *path)
|
||||
unsigned int len;
|
||||
|
||||
value = xs_read(handles[handle], path, &len);
|
||||
if (!value)
|
||||
if (!value) {
|
||||
failed(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
/* It's supposed to nul terminate for us. */
|
||||
assert(value[len] == '\0');
|
||||
if (handle)
|
||||
printf("%i:%.*s\n", handle, len, value);
|
||||
output("%i:%.*s\n", handle, len, value);
|
||||
else
|
||||
printf("%.*s\n", len, value);
|
||||
output("%.*s\n", len, value);
|
||||
}
|
||||
|
||||
static void do_write(unsigned int handle, char *path, char *flags, char *data)
|
||||
@@ -322,8 +390,10 @@ static void do_getperm(unsigned int handle, char *path)
|
||||
struct xs_permissions *perms;
|
||||
|
||||
perms = xs_get_permissions(handles[handle], path, &num);
|
||||
if (!perms)
|
||||
if (!perms) {
|
||||
failed(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
char *permstring;
|
||||
@@ -346,9 +416,9 @@ static void do_getperm(unsigned int handle, char *path)
|
||||
}
|
||||
|
||||
if (handle)
|
||||
printf("%i:%i %s\n", handle, perms[i].id, permstring);
|
||||
output("%i:%i %s\n", handle, perms[i].id, permstring);
|
||||
else
|
||||
printf("%i %s\n", perms[i].id, permstring);
|
||||
output("%i %s\n", perms[i].id, permstring);
|
||||
}
|
||||
free(perms);
|
||||
}
|
||||
@@ -399,15 +469,31 @@ static void do_watch(unsigned int handle, const char *node, const char *token)
|
||||
static void do_waitwatch(unsigned int handle)
|
||||
{
|
||||
char **vec;
|
||||
struct timeval tv = {.tv_sec = timeout_ms/1000,
|
||||
.tv_usec = (timeout_ms*1000)%1000000 };
|
||||
fd_set set;
|
||||
|
||||
if (xs_fileno(handles[handle]) != -2) {
|
||||
FD_ZERO(&set);
|
||||
FD_SET(xs_fileno(handles[handle]), &set);
|
||||
if (select(xs_fileno(handles[handle])+1, &set,
|
||||
NULL, NULL, &tv) == 0) {
|
||||
errno = ETIMEDOUT;
|
||||
failed(handle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
vec = xs_read_watch(handles[handle]);
|
||||
if (!vec)
|
||||
if (!vec) {
|
||||
failed(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
if (handle)
|
||||
printf("%i:%s:%s\n", handle, vec[0], vec[1]);
|
||||
output("%i:%s:%s\n", handle, vec[0], vec[1]);
|
||||
else
|
||||
printf("%s:%s\n", vec[0], vec[1]);
|
||||
output("%s:%s\n", vec[0], vec[1]);
|
||||
free(vec);
|
||||
}
|
||||
|
||||
@@ -417,82 +503,6 @@ static void do_ackwatch(unsigned int handle, const char *token)
|
||||
failed(handle);
|
||||
}
|
||||
|
||||
static bool wait_for_input(unsigned int handle)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; i < ARRAY_SIZE(handles); i++) {
|
||||
int fd;
|
||||
|
||||
if (!handles[i] || i == handle)
|
||||
continue;
|
||||
|
||||
fd = xs_fileno(handles[i]);
|
||||
if (fd == -2) {
|
||||
unsigned int avail;
|
||||
get_input_chunk(in, in->buf, &avail);
|
||||
if (avail != 0)
|
||||
return true;
|
||||
} else {
|
||||
struct timeval tv = {.tv_sec = 0, .tv_usec = 0 };
|
||||
fd_set set;
|
||||
|
||||
FD_ZERO(&set);
|
||||
FD_SET(fd, &set);
|
||||
if (select(fd+1, &set, NULL, NULL,&tv))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* Async wait for watch on handle */
|
||||
static void do_command(unsigned int default_handle, char *line);
|
||||
static void do_async(unsigned int handle, char *line)
|
||||
{
|
||||
int child;
|
||||
unsigned int i;
|
||||
children++;
|
||||
if ((child = fork()) != 0) {
|
||||
/* Wait until *something* happens, which indicates
|
||||
* child has created an event. V. sloppy, but we can't
|
||||
* select on fake domain connections.
|
||||
*/
|
||||
while (!wait_for_input(handle));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Don't keep other handles open in parent. */
|
||||
for (i = 0; i < ARRAY_SIZE(handles); i++) {
|
||||
if (handles[i] && i != handle) {
|
||||
xs_daemon_close(handles[i]);
|
||||
handles[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
do_command(handle, line + argpos(line, 1));
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void do_asyncwait(unsigned int handle)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (handle)
|
||||
barf("handle has no meaning with asyncwait");
|
||||
|
||||
if (children == 0)
|
||||
barf("No children to wait for!");
|
||||
|
||||
if (waitpid(0, &status, 0) > 0) {
|
||||
if (!WIFEXITED(status))
|
||||
barf("async died");
|
||||
if (WEXITSTATUS(status))
|
||||
exit(WEXITSTATUS(status));
|
||||
}
|
||||
children--;
|
||||
}
|
||||
|
||||
static void do_unwatch(unsigned int handle, const char *node, const char *token)
|
||||
{
|
||||
if (!xs_unwatch(handles[handle], node, token))
|
||||
@@ -538,15 +548,18 @@ static void do_introduce(unsigned int handle,
|
||||
*(int *)((void *)out + 32) = getpid();
|
||||
*(u16 *)((void *)out + 36) = atoi(eventchn);
|
||||
|
||||
if (!xs_introduce_domain(handles[handle], atoi(domid),
|
||||
atol(mfn), atoi(eventchn), path)) {
|
||||
failed(handle);
|
||||
munmap(out, getpagesize());
|
||||
return;
|
||||
}
|
||||
output("handle is %i\n", i);
|
||||
|
||||
/* Create new handle. */
|
||||
handles[i] = new(struct xs_handle);
|
||||
handles[i]->fd = -2;
|
||||
|
||||
if (!xs_introduce_domain(handles[handle], atoi(domid),
|
||||
atol(mfn), atoi(eventchn), path))
|
||||
failed(handle);
|
||||
printf("handle is %i\n", i);
|
||||
|
||||
/* Read in daemon pid. */
|
||||
daemon_pid = *(int *)((void *)out + 32);
|
||||
}
|
||||
@@ -593,18 +606,20 @@ static void dump_dir(unsigned int handle,
|
||||
sprintf(subnode, "%s/%s", node, dir[i]);
|
||||
|
||||
perms = xs_get_permissions(handles[handle], subnode,&numperms);
|
||||
if (!perms)
|
||||
if (!perms) {
|
||||
failed(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("%s%s: ", spacing, dir[i]);
|
||||
output("%s%s: ", spacing, dir[i]);
|
||||
for (j = 0; j < numperms; j++) {
|
||||
char buffer[100];
|
||||
if (!xs_perm_to_string(&perms[j], buffer))
|
||||
barf("perm to string");
|
||||
printf("%s ", buffer);
|
||||
output("%s ", buffer);
|
||||
}
|
||||
free(perms);
|
||||
printf("\n");
|
||||
output("\n");
|
||||
|
||||
/* Even directories can have contents. */
|
||||
contents = xs_read(handles[handle], subnode, &len);
|
||||
@@ -612,14 +627,16 @@ static void dump_dir(unsigned int handle,
|
||||
if (errno != EISDIR)
|
||||
failed(handle);
|
||||
} else {
|
||||
printf(" %s(%.*s)\n", spacing, len, contents);
|
||||
output(" %s(%.*s)\n", spacing, len, contents);
|
||||
free(contents);
|
||||
}
|
||||
|
||||
/* Every node is a directory. */
|
||||
subdirs = xs_directory(handles[handle], subnode, &subnum);
|
||||
if (!subdirs)
|
||||
if (!subdirs) {
|
||||
failed(handle);
|
||||
return;
|
||||
}
|
||||
dump_dir(handle, subnode, subdirs, subnum, depth+1);
|
||||
free(subdirs);
|
||||
}
|
||||
@@ -631,8 +648,10 @@ static void dump(int handle)
|
||||
unsigned int subnum;
|
||||
|
||||
subdirs = xs_directory(handles[handle], "/", &subnum);
|
||||
if (!subdirs)
|
||||
if (!subdirs) {
|
||||
failed(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
dump_dir(handle, "", subdirs, subnum, 0);
|
||||
free(subdirs);
|
||||
@@ -652,10 +671,31 @@ static void alarmed(int sig __attribute__((unused)))
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void set_timeout(void)
|
||||
{
|
||||
struct itimerval timeout;
|
||||
|
||||
timeout.it_interval.tv_sec = timeout_ms / 1000;
|
||||
timeout.it_interval.tv_usec = (timeout_ms * 1000) % 1000000;
|
||||
setitimer(ITIMER_REAL, &timeout, NULL);
|
||||
}
|
||||
|
||||
static void disarm_timeout(void)
|
||||
{
|
||||
struct itimerval timeout;
|
||||
|
||||
timeout.it_interval.tv_sec = 0;
|
||||
timeout.it_interval.tv_usec = 0;
|
||||
setitimer(ITIMER_REAL, &timeout, NULL);
|
||||
}
|
||||
|
||||
static void do_command(unsigned int default_handle, char *line)
|
||||
{
|
||||
char *endp;
|
||||
|
||||
if (print_input)
|
||||
printf("%i> %s", ++linenum, line);
|
||||
|
||||
if (strspn(line, " \n") == strlen(line))
|
||||
return;
|
||||
if (strstarts(line, "#"))
|
||||
@@ -667,6 +707,7 @@ static void do_command(unsigned int default_handle, char *line)
|
||||
else
|
||||
handle = default_handle;
|
||||
|
||||
command = arg(line, 0);
|
||||
if (!handles[handle]) {
|
||||
if (readonly)
|
||||
handles[handle] = xs_daemon_open_readonly();
|
||||
@@ -675,10 +716,10 @@ static void do_command(unsigned int default_handle, char *line)
|
||||
if (!handles[handle])
|
||||
barf_perror("Opening connection to daemon");
|
||||
}
|
||||
command = arg(line, 0);
|
||||
|
||||
if (timeout)
|
||||
alarm(1);
|
||||
if (!timeout_suppressed)
|
||||
set_timeout();
|
||||
timeout_suppressed = false;
|
||||
|
||||
if (streq(command, "dir"))
|
||||
do_dir(handle, arg(line, 1));
|
||||
@@ -703,10 +744,6 @@ static void do_command(unsigned int default_handle, char *line)
|
||||
do_watch(handle, arg(line, 1), arg(line, 2));
|
||||
else if (streq(command, "waitwatch"))
|
||||
do_waitwatch(handle);
|
||||
else if (streq(command, "async"))
|
||||
do_async(handle, line);
|
||||
else if (streq(command, "asyncwait"))
|
||||
do_asyncwait(handle);
|
||||
else if (streq(command, "ackwatch"))
|
||||
do_ackwatch(handle, arg(line, 1));
|
||||
else if (streq(command, "unwatch"))
|
||||
@@ -727,32 +764,66 @@ static void do_command(unsigned int default_handle, char *line)
|
||||
do_release(handle, arg(line, 1));
|
||||
else if (streq(command, "dump"))
|
||||
dump(handle);
|
||||
else if (streq(command, "sleep"))
|
||||
sleep(atoi(arg(line, 1)));
|
||||
else
|
||||
else if (streq(command, "sleep")) {
|
||||
disarm_timeout();
|
||||
usleep(atoi(arg(line, 1)) * 1000);
|
||||
} else if (streq(command, "expect"))
|
||||
expect(line);
|
||||
else if (streq(command, "notimeout"))
|
||||
timeout_suppressed = true;
|
||||
else if (streq(command, "readonly")) {
|
||||
readonly = true;
|
||||
xs_daemon_close(handles[handle]);
|
||||
handles[handle] = NULL;
|
||||
} else if (streq(command, "readwrite")) {
|
||||
readonly = false;
|
||||
xs_daemon_close(handles[handle]);
|
||||
handles[handle] = NULL;
|
||||
} else
|
||||
barf("Unknown command %s", command);
|
||||
fflush(stdout);
|
||||
alarm(0);
|
||||
disarm_timeout();
|
||||
|
||||
/* Check expectations. */
|
||||
if (!streq(command, "expect")) {
|
||||
struct expect *i = list_top(&expects, struct expect, list);
|
||||
|
||||
if (i)
|
||||
barf("Expected '%s', didn't happen\n", i->pattern);
|
||||
}
|
||||
}
|
||||
|
||||
static struct option options[] = { { "readonly", 0, NULL, 'r' },
|
||||
{ "no-timeout", 0, NULL, 't' },
|
||||
{ NULL, 0, NULL, 0 } };
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int opt;
|
||||
char line[1024];
|
||||
|
||||
if (argc > 1 && streq(argv[1], "--readonly")) {
|
||||
readonly = true;
|
||||
argc--;
|
||||
argv++;
|
||||
while ((opt = getopt_long(argc, argv, "xrt", options, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case 'r':
|
||||
readonly = true;
|
||||
break;
|
||||
case 't':
|
||||
timeout_ms = 0;
|
||||
break;
|
||||
case 'x':
|
||||
print_input = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > 1 && streq(argv[1], "--no-timeout")) {
|
||||
timeout = false;
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
||||
if (argc != 1)
|
||||
if (optind + 1 == argc) {
|
||||
int fd = open(argv[optind], O_RDONLY);
|
||||
if (!fd)
|
||||
barf_perror("Opening %s", argv[optind]);
|
||||
dup2(fd, STDIN_FILENO);
|
||||
} else if (optind != argc)
|
||||
usage();
|
||||
|
||||
|
||||
/* The size of the ringbuffer: half a page minus head structure. */
|
||||
ringbuf_datasize = getpagesize() / 2 - sizeof(struct ringbuf_head);
|
||||
@@ -761,7 +832,5 @@ int main(int argc, char *argv[])
|
||||
while (fgets(line, sizeof(line), stdin))
|
||||
do_command(0, line);
|
||||
|
||||
while (children)
|
||||
do_asyncwait(0);
|
||||
return 0;
|
||||
}
|
||||