mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
xenstore: Remove broken and unmaintained test code.
Signed-off-by: Keir Fraser <keir@xensource.com>
This commit is contained in:
1 parent
091eb6c418
commit
fed1946117
29 files changed
+5
-4418
No files matched your search
+2
-92
@@ -11,15 +11,11 @@ BASECFLAGS=-Werror
|
||||
BASECFLAGS += -Wp,-MD,.$(@F).d
|
||||
PROG_DEP = .*.d
|
||||
BASECFLAGS+= $(PROFILE)
|
||||
#BASECFLAGS+= -I$(XEN_ROOT)/tools
|
||||
BASECFLAGS+= -I$(XEN_ROOT)/tools/libxc
|
||||
BASECFLAGS+= -I.
|
||||
|
||||
CFLAGS += $(BASECFLAGS)
|
||||
LDFLAGS += $(PROFILE) -L$(XEN_LIBXC)
|
||||
TESTDIR = testsuite/tmp
|
||||
TESTFLAGS= -DTESTING
|
||||
TESTENV = XENSTORED_ROOTDIR=$(TESTDIR) XENSTORED_RUNDIR=$(TESTDIR)
|
||||
|
||||
CLIENTS := xenstore-exists xenstore-list xenstore-read xenstore-rm xenstore-chmod
|
||||
CLIENTS += xenstore-write
|
||||
@@ -35,12 +31,6 @@ XENSTORED_OBJS += $(XENSTORED_OBJS_y)
|
||||
.PHONY: all
|
||||
all: libxenstore.so libxenstore.a xenstored $(CLIENTS) xs_tdb_dump xenstore-control xenstore-ls
|
||||
|
||||
test_interleaved_transactions: test_interleaved_transactions.o
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -L. -lxenstore -o $@
|
||||
|
||||
.PHONY: testcode
|
||||
testcode: xs_test xenstored_test xs_random
|
||||
|
||||
xenstored: $(XENSTORED_OBJS)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -lxenctrl $(SOCKET_LIBS) -o $@
|
||||
|
||||
@@ -56,35 +46,9 @@ xenstore-control: xenstore_control.o libxenstore.so
|
||||
xenstore-ls: xsls.o libxenstore.so
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $< $(LOADLIBES) $(LDLIBS) -L. -lxenstore $(SOCKET_LIBS) -o $@
|
||||
|
||||
xenstored_test: xenstored_core_test.o xenstored_watch_test.o xenstored_domain_test.o xenstored_transaction_test.o xs_lib.o talloc_test.o fake_libxc.o utils.o tdb.o
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
|
||||
|
||||
xs_tdb_dump: xs_tdb_dump.o utils.o tdb.o talloc.o
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
|
||||
|
||||
xs_test xs_random xs_stress xs_crashme: LDFLAGS+=-lpthread
|
||||
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_crashme: xs_crashme.o xs_lib.o talloc.o utils.o
|
||||
|
||||
speedtest: speedtest.o xs.o xs_lib.o utils.o talloc.o
|
||||
|
||||
.PHONY: check-speed
|
||||
check-speed: speedtest xenstored_test $(TESTDIR)
|
||||
$(TESTENV) time ./speedtest 100
|
||||
|
||||
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 xs_crashme.o: CFLAGS=$(BASECFLAGS) $(TESTFLAGS)
|
||||
|
||||
xenstored_%_test.o: xenstored_%.c
|
||||
$(COMPILE.c) -o $@ $<
|
||||
|
||||
xs_test_lib.o: xs.c
|
||||
$(COMPILE.c) -o $@ $<
|
||||
|
||||
talloc_test.o: talloc.c
|
||||
$(COMPILE.c) -o $@ $<
|
||||
|
||||
libxenstore.so: libxenstore.so.$(MAJOR)
|
||||
ln -sf $< $@
|
||||
libxenstore.so.$(MAJOR): libxenstore.so.$(MAJOR).$(MINOR)
|
||||
@@ -97,67 +61,13 @@ libxenstore.a: xs.o xs_lib.o
|
||||
$(AR) rcs libxenstore.a $^
|
||||
|
||||
.PHONY: clean
|
||||
clean: testsuite-clean
|
||||
clean:
|
||||
rm -f *.a *.o *.opic *.so*
|
||||
rm -f xenstored xs_random xs_stress xs_crashme
|
||||
rm -f xs_test xenstored_test xs_tdb_dump xenstore-control xenstore-ls
|
||||
rm -f xs_tdb_dump xenstore-control xenstore-ls
|
||||
rm -f $(CLIENTS)
|
||||
$(RM) $(PROG_DEP)
|
||||
|
||||
.PHONY: print-dir
|
||||
print-dir:
|
||||
@echo -n tools/xenstore:
|
||||
|
||||
.PHONY: print-end
|
||||
print-end:
|
||||
@echo
|
||||
|
||||
.PHONY: check
|
||||
check: print-dir testsuite-fast randomcheck-fast print-end
|
||||
|
||||
.PHONY: fullcheck
|
||||
fullcheck: testsuite-run randomcheck stresstest
|
||||
|
||||
$(TESTDIR):
|
||||
mkdir $@
|
||||
|
||||
.PHONY: testsuite-run
|
||||
testsuite-run: xenstored_test xs_test $(TESTDIR)
|
||||
$(TESTENV) testsuite/test.sh && echo
|
||||
|
||||
.PHONY: testsuite-fast
|
||||
testsuite-fast: xenstored_test xs_test $(TESTDIR)
|
||||
@$(TESTENV) testsuite/test.sh --fast
|
||||
|
||||
.PHONY: testsuite-clean
|
||||
testsuite-clean:
|
||||
rm -rf $(TESTDIR)
|
||||
|
||||
# Make this visible so they can see repeat tests without --fast if they
|
||||
# fail.
|
||||
RANDSEED=$(shell date +%s)
|
||||
.PHONY: randomcheck
|
||||
randomcheck: xs_random xenstored_test $(TESTDIR)
|
||||
$(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)
|
||||
|
||||
.PHONY: crashme
|
||||
crashme: xs_crashme xenstored_test $(TESTDIR)
|
||||
rm -rf $(TESTDIR)/store $(TESTDIR)/transactions /tmp/xs_crashme.vglog* /tmp/trace
|
||||
export $(TESTENV); ./xs_crashme 5000 $(RANDSEED) 2>/dev/null
|
||||
if [ -n "`cat /tmp/xs_crashme.vglog*`" ]; then echo Valgrind complained; cat /tmp/xs_crashme.vglog*; exit 1; fi
|
||||
rm -rf $(TESTDIR)/store $(TESTDIR)/transactions /tmp/xs_crashme.vglog* /tmp/trace
|
||||
|
||||
.PHONY: randomcheck-fast
|
||||
randomcheck-fast: xs_random xenstored_test $(TESTDIR)
|
||||
@$(TESTENV) ./xs_random --fast /tmp/xs_random 2000 $(RANDSEED)
|
||||
|
||||
.PHONY: stresstest
|
||||
stresstest: xs_stress xenstored_test $(TESTDIR)
|
||||
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
|
||||
|
||||
.PHONY: TAGS
|
||||
TAGS:
|
||||
etags `find . -name '*.[ch]'`
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
Fake libxc which doesn't require hypervisor but talks to xs_test.
|
||||
Copyright (C) 2005 Rusty Russell IBM Corporation
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include "utils.h"
|
||||
#include "xenstored_core.h"
|
||||
#include "xenstored_domain.h"
|
||||
#include "xenstored_test.h"
|
||||
#include <xenctrl.h>
|
||||
|
||||
static int sigfd;
|
||||
static int xs_test_pid;
|
||||
static evtchn_port_t port;
|
||||
|
||||
/* The event channel maps to a signal, shared page to an mmapped file. */
|
||||
void xc_evtchn_notify(int xce_handle, int local_port)
|
||||
{
|
||||
assert(local_port == port);
|
||||
if (kill(xs_test_pid, SIGUSR2) != 0)
|
||||
barf_perror("fake event channel failed");
|
||||
}
|
||||
|
||||
void *xc_map_foreign_range(int xc_handle, uint32_t dom __attribute__((unused)),
|
||||
int size, int prot,
|
||||
unsigned long mfn __attribute__((unused)))
|
||||
{
|
||||
void *ret;
|
||||
|
||||
ret = mmap(NULL, size, prot, MAP_SHARED, xc_handle, 0);
|
||||
if (ret == MAP_FAILED)
|
||||
return NULL;
|
||||
|
||||
/* xs_test tells us pid and port by putting it in buffer, we reply. */
|
||||
xs_test_pid = *(int *)(ret + 32);
|
||||
port = *(int *)(ret + 36);
|
||||
*(int *)(ret + 32) = getpid();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xc_interface_open(void)
|
||||
{
|
||||
int fd;
|
||||
char page[getpagesize()];
|
||||
|
||||
fd = open("/tmp/xcmap", O_RDWR|O_CREAT|O_TRUNC, 0600);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
memset(page, 0, sizeof(page));
|
||||
if (!xs_write_all(fd, page, sizeof(page)))
|
||||
barf_perror("Failed to write /tmp/xcmap page");
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int xc_interface_close(int xc_handle)
|
||||
{
|
||||
close(xc_handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xc_domain_getinfo(int xc_handle __attribute__((unused)),
|
||||
uint32_t first_domid, unsigned int max_doms,
|
||||
xc_dominfo_t *info)
|
||||
{
|
||||
assert(max_doms == 1);
|
||||
info->domid = first_domid;
|
||||
|
||||
info->dying = 0;
|
||||
info->shutdown = 0;
|
||||
info->paused = 0;
|
||||
info->blocked = 0;
|
||||
info->running = 1;
|
||||
|
||||
info->shutdown_reason = 0;
|
||||
|
||||
if ( info->shutdown && (info->shutdown_reason == SHUTDOWN_crash) )
|
||||
{
|
||||
info->shutdown = 0;
|
||||
info->crashed = 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void send_to_fd(int signo __attribute__((unused)))
|
||||
{
|
||||
int saved_errno = errno;
|
||||
write(sigfd, &port, sizeof(port));
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
||||
void fake_block_events(void)
|
||||
{
|
||||
signal(SIGUSR2, SIG_IGN);
|
||||
}
|
||||
|
||||
void fake_ack_event(void)
|
||||
{
|
||||
signal(SIGUSR2, send_to_fd);
|
||||
}
|
||||
|
||||
int xc_evtchn_open(void)
|
||||
{
|
||||
int fds[2];
|
||||
|
||||
if (pipe(fds) != 0)
|
||||
return -1;
|
||||
|
||||
if (signal(SIGUSR2, send_to_fd) == SIG_ERR) {
|
||||
int saved_errno = errno;
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
sigfd = fds[1];
|
||||
return fds[0];
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
Xen Store Daemon Speed test
|
||||
Copyright (C) 2005 Rusty Russell IBM Corporation
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include "utils.h"
|
||||
#include "xs.h"
|
||||
#include "list.h"
|
||||
#include "talloc.h"
|
||||
|
||||
static void do_command(const char *cmd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = system(cmd);
|
||||
if (ret == -1 || !WIFEXITED(ret) || WEXITSTATUS(ret) != 0)
|
||||
barf_perror("Failed '%s': %i", cmd, ret);
|
||||
}
|
||||
|
||||
static int start_daemon(void)
|
||||
{
|
||||
int fds[2], pid;
|
||||
|
||||
do_command(talloc_asprintf(NULL, "rm -rf testsuite/tmp/*"));
|
||||
|
||||
/* Start daemon. */
|
||||
pipe(fds);
|
||||
if ((pid = fork())) {
|
||||
/* Child writes PID when its ready: we wait for that. */
|
||||
char buffer[20];
|
||||
close(fds[1]);
|
||||
if (read(fds[0], buffer, sizeof(buffer)) < 0)
|
||||
barf("Failed to summon daemon");
|
||||
close(fds[0]);
|
||||
} else {
|
||||
dup2(fds[1], STDOUT_FILENO);
|
||||
close(fds[0]);
|
||||
#if 0
|
||||
execlp("valgrind", "valgrind", "-q", "--suppressions=testsuite/vg-suppressions", "xenstored_test", "--output-pid",
|
||||
"--no-fork", "--trace-file=/tmp/trace", NULL);
|
||||
#else
|
||||
execlp("./xenstored_test", "xenstored_test", "--output-pid", "--no-fork", NULL);
|
||||
// execlp("strace", "strace", "-o", "/tmp/out", "./xenstored_test", "--output-pid", "--no-fork", NULL);
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
static void kill_daemon(int pid)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
kill(pid, SIGTERM);
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
||||
#define NUM_ENTRIES 50
|
||||
|
||||
/* We create the given number of trees, each with NUM_ENTRIES, using
|
||||
* transactions. */
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i, j, pid, print;
|
||||
struct xs_handle *h;
|
||||
|
||||
if (argc != 2)
|
||||
barf("Usage: speedtest <numdomains>");
|
||||
|
||||
pid = start_daemon();
|
||||
h = xs_daemon_open();
|
||||
print = atoi(argv[1]) / 76;
|
||||
if (!print)
|
||||
print = 1;
|
||||
for (i = 0; i < atoi(argv[1]); i ++) {
|
||||
char name[64];
|
||||
|
||||
if (i % print == 0)
|
||||
write(1, ".", 1);
|
||||
if (!xs_transaction_start(h)) {
|
||||
kill_daemon(pid);
|
||||
barf_perror("Starting transaction");
|
||||
}
|
||||
sprintf(name, "/%i", i);
|
||||
if (!xs_mkdir(h, name)) {
|
||||
kill_daemon(pid);
|
||||
barf_perror("Making directory %s", name);
|
||||
}
|
||||
|
||||
for (j = 0; j < NUM_ENTRIES; j++) {
|
||||
sprintf(name, "/%i/%i", i, j);
|
||||
if (!xs_write(h, name, name, strlen(name))) {
|
||||
kill_daemon(pid);
|
||||
barf_perror("Making directory %s", name);
|
||||
}
|
||||
}
|
||||
if (!xs_transaction_end(h, false)) {
|
||||
kill_daemon(pid);
|
||||
barf_perror("Ending transaction");
|
||||
}
|
||||
}
|
||||
write(1, "\n", 1);
|
||||
|
||||
kill_daemon(pid);
|
||||
wait(NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# Create an entry, read it.
|
||||
write /test contents
|
||||
expect contents
|
||||
read /test
|
||||
@@ -1,45 +0,0 @@
|
||||
# Root directory has only tool dir in it.
|
||||
expect tool
|
||||
dir /
|
||||
|
||||
# Create a file.
|
||||
write /test contents
|
||||
|
||||
# Directory shows it.
|
||||
expect test
|
||||
expect tool
|
||||
dir /
|
||||
|
||||
# Make a new directory, check it's there
|
||||
mkdir /dir
|
||||
expect dir
|
||||
expect test
|
||||
expect tool
|
||||
dir /
|
||||
|
||||
# Check it's empty.
|
||||
dir /dir
|
||||
|
||||
# Create a file, check it exists.
|
||||
write /dir/test2 contents2
|
||||
expect test2
|
||||
dir /dir
|
||||
expect contents2
|
||||
read /dir/test2
|
||||
|
||||
# Creating dir over the top should succeed.
|
||||
mkdir /dir
|
||||
mkdir /dir/test2
|
||||
|
||||
# Mkdir implicitly creates directories.
|
||||
mkdir /dir/1/2/3/4
|
||||
expect test2
|
||||
expect 1
|
||||
dir /dir
|
||||
expect 2
|
||||
dir /dir/1
|
||||
expect 3
|
||||
dir /dir/1/2
|
||||
expect 4
|
||||
dir /dir/1/2/3
|
||||
dir /dir/1/2/3/4
|
||||
@@ -1,28 +0,0 @@
|
||||
# Write succeeds
|
||||
write /test contents
|
||||
expect contents
|
||||
read /test
|
||||
|
||||
# Overwrite succeeds.
|
||||
write /test contents2
|
||||
expect contents2
|
||||
read /test
|
||||
|
||||
# Write should implicitly create directories
|
||||
write /dir/test contents
|
||||
expect test
|
||||
dir /dir
|
||||
expect contents
|
||||
read /dir/test
|
||||
write /dir/1/2/3/4 contents4
|
||||
expect test
|
||||
expect 1
|
||||
dir /dir
|
||||
expect 2
|
||||
dir /dir/1
|
||||
expect 3
|
||||
dir /dir/1/2
|
||||
expect 4
|
||||
dir /dir/1/2/3
|
||||
expect contents4
|
||||
read /dir/1/2/3/4
|
||||
@@ -1,20 +0,0 @@
|
||||
# Remove non-existant is OK, as long as parent exists
|
||||
rm /test
|
||||
expect rm failed: No such file or directory
|
||||
rm /dir/test
|
||||
|
||||
# Create file and remove it
|
||||
write /test contents
|
||||
rm /test
|
||||
expect tool
|
||||
dir /
|
||||
|
||||
# Create directory and remove it.
|
||||
mkdir /dir
|
||||
rm /dir
|
||||
|
||||
# Create directory, create file, remove all.
|
||||
mkdir /dir
|
||||
write /dir/test contents
|
||||
rm /dir
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
# Fail to get perms on non-existent file.
|
||||
expect getperm failed: No such file or directory
|
||||
getperm /test
|
||||
expect getperm failed: No such file or directory
|
||||
getperm /dir/test
|
||||
|
||||
# Create file: inherits from root (0 READ)
|
||||
write /test contents
|
||||
expect 0 READ
|
||||
getperm /test
|
||||
setid 1
|
||||
expect 0 READ
|
||||
getperm /test
|
||||
expect contents
|
||||
read /test
|
||||
expect write failed: Permission denied
|
||||
write /test contents
|
||||
|
||||
# Take away read access to file.
|
||||
setid 0
|
||||
setperm /test 0 NONE
|
||||
setid 1
|
||||
expect getperm failed: Permission denied
|
||||
getperm /test
|
||||
expect read failed: Permission denied
|
||||
read /test
|
||||
expect write failed: Permission denied
|
||||
write /test contents
|
||||
|
||||
# Grant everyone write access to file.
|
||||
setid 0
|
||||
setperm /test 0 WRITE
|
||||
setid 1
|
||||
expect getperm failed: Permission denied
|
||||
getperm /test
|
||||
expect read failed: Permission denied
|
||||
read /test
|
||||
write /test contents2
|
||||
setid 0
|
||||
expect contents2
|
||||
read /test
|
||||
|
||||
# Grant everyone both read and write access.
|
||||
setperm /test 0 READ/WRITE
|
||||
setid 1
|
||||
expect 0 READ/WRITE
|
||||
getperm /test
|
||||
expect contents2
|
||||
read /test
|
||||
write /test contents3
|
||||
expect contents3
|
||||
read /test
|
||||
|
||||
# Change so that user 1 owns it, noone else can do anything.
|
||||
setid 0
|
||||
setperm /test 1 NONE
|
||||
setid 1
|
||||
expect 1 NONE
|
||||
getperm /test
|
||||
expect contents3
|
||||
read /test
|
||||
write /test contents4
|
||||
|
||||
# User 2 can do nothing.
|
||||
setid 2
|
||||
expect setperm failed: Permission denied
|
||||
setperm /test 2 NONE
|
||||
expect getperm failed: Permission denied
|
||||
getperm /test
|
||||
expect read failed: Permission denied
|
||||
read /test
|
||||
expect write failed: Permission denied
|
||||
write /test contents4
|
||||
|
||||
# Tools can always access things.
|
||||
setid 0
|
||||
expect 1 NONE
|
||||
getperm /test
|
||||
expect contents4
|
||||
read /test
|
||||
write /test contents5
|
||||
@@ -1,119 +0,0 @@
|
||||
# Root directory: owned by tool, everyone has read access.
|
||||
expect 0 READ
|
||||
getperm /
|
||||
|
||||
# Create directory: inherits from root.
|
||||
mkdir /dir
|
||||
expect 0 READ
|
||||
getperm /dir
|
||||
setid 1
|
||||
expect 0 READ
|
||||
getperm /dir
|
||||
dir /dir
|
||||
expect write failed: Permission denied
|
||||
write /dir/test contents2
|
||||
|
||||
# Remove everyone's read access to directoy.
|
||||
setid 0
|
||||
setperm /dir 0 NONE
|
||||
setid 1
|
||||
expect dir failed: Permission denied
|
||||
dir /dir
|
||||
expect read failed: Permission denied
|
||||
read /dir/test create contents2
|
||||
expect write failed: Permission denied
|
||||
write /dir/test contents2
|
||||
|
||||
# Grant everyone write access to directory.
|
||||
setid 0
|
||||
setperm /dir 0 WRITE
|
||||
setid 1
|
||||
expect getperm failed: Permission denied
|
||||
getperm /dir
|
||||
expect dir failed: Permission denied
|
||||
dir /dir
|
||||
write /dir/test contents
|
||||
setid 0
|
||||
expect 1 WRITE
|
||||
getperm /dir/test
|
||||
setperm /dir/test 0 NONE
|
||||
expect contents
|
||||
read /dir/test
|
||||
|
||||
# Grant everyone both read and write access.
|
||||
setperm /dir 0 READ/WRITE
|
||||
setid 1
|
||||
expect 0 READ/WRITE
|
||||
getperm /dir
|
||||
expect test
|
||||
dir /dir
|
||||
write /dir/test2 contents
|
||||
expect contents
|
||||
read /dir/test2
|
||||
setperm /dir/test2 1 NONE
|
||||
|
||||
# Change so that user 1 owns it, noone else can do anything.
|
||||
setid 0
|
||||
setperm /dir 1 NONE
|
||||
expect 1 NONE
|
||||
getperm /dir
|
||||
expect test
|
||||
expect test2
|
||||
dir /dir
|
||||
write /dir/test3 contents
|
||||
|
||||
# User 2 can do nothing. Can't even tell if file exists.
|
||||
setid 2
|
||||
expect setperm failed: Permission denied
|
||||
setperm /dir 2 NONE
|
||||
expect getperm failed: Permission denied
|
||||
getperm /dir
|
||||
expect dir failed: Permission denied
|
||||
dir /dir
|
||||
expect read failed: Permission denied
|
||||
read /dir/test
|
||||
expect read failed: Permission denied
|
||||
read /dir/test2
|
||||
expect read failed: Permission denied
|
||||
read /dir/test3
|
||||
expect read failed: Permission denied
|
||||
read /dir/test4
|
||||
expect write failed: Permission denied
|
||||
write /dir/test contents
|
||||
expect write failed: Permission denied
|
||||
write /dir/test4 contents
|
||||
|
||||
# Tools can always access things.
|
||||
setid 0
|
||||
expect 1 NONE
|
||||
getperm /dir
|
||||
expect test
|
||||
expect test2
|
||||
expect test3
|
||||
dir /dir
|
||||
write /dir/test4 contents
|
||||
|
||||
# Inherited by child.
|
||||
mkdir /dir/subdir
|
||||
expect 1 NONE
|
||||
getperm /dir/subdir
|
||||
write /dir/subfile contents
|
||||
expect 1 NONE
|
||||
getperm /dir/subfile
|
||||
|
||||
# But for domains, they own it.
|
||||
setperm /dir/subdir 2 READ/WRITE
|
||||
expect 2 READ/WRITE
|
||||
getperm /dir/subdir
|
||||
setid 3
|
||||
write /dir/subdir/subfile contents
|
||||
expect 3 READ/WRITE
|
||||
getperm /dir/subdir/subfile
|
||||
|
||||
# Inheritence works through multiple directories, too.
|
||||
write /dir/subdir/1/2/3/4 contents
|
||||
expect 3 READ/WRITE
|
||||
getperm /dir/subdir/1/2/3/4
|
||||
mkdir /dir/subdir/a/b/c/d
|
||||
expect 3 READ/WRITE
|
||||
getperm /dir/subdir/a/b/c/d
|
||||
@@ -1,176 +0,0 @@
|
||||
# Watch something, write to it, check watch has fired.
|
||||
write /test contents
|
||||
|
||||
1 watch /test token
|
||||
2 write /test contents2
|
||||
expect 1:/test:token
|
||||
1 waitwatch
|
||||
1 close
|
||||
|
||||
# Check that reads don't set it off.
|
||||
1 watch /test token
|
||||
expect 2:contents2
|
||||
2 read /test
|
||||
expect 1: waitwatch failed: Connection timed out
|
||||
1 waitwatch
|
||||
1 close
|
||||
|
||||
# mkdir, setperm and rm should (also tests watching dirs)
|
||||
mkdir /dir
|
||||
1 watch /dir token
|
||||
2 mkdir /dir/newdir
|
||||
expect 1:/dir/newdir:token
|
||||
1 waitwatch
|
||||
2 setperm /dir/newdir 0 READ
|
||||
expect 1:/dir/newdir:token
|
||||
1 waitwatch
|
||||
2 rm /dir/newdir
|
||||
expect 1:/dir/newdir:token
|
||||
1 waitwatch
|
||||
1 close
|
||||
2 close
|
||||
|
||||
# Changed in b594bb976a743d509f1ffabb5bc698874ab90d8f
|
||||
## We don't get a watch from our own commands.
|
||||
#watch /dir token
|
||||
#mkdir /dir/newdir
|
||||
#expect waitwatch failed: Connection timed out
|
||||
#waitwatch
|
||||
#close
|
||||
|
||||
# ignore watches while doing commands, should work.
|
||||
watch /dir token
|
||||
1 write /dir/test contents
|
||||
expect contents
|
||||
read /dir/test
|
||||
expect /dir/test:token
|
||||
waitwatch
|
||||
close
|
||||
|
||||
# watch priority test: all simultaneous
|
||||
1 watch /dir token1
|
||||
3 watch /dir token3
|
||||
2 watch /dir token2
|
||||
write /dir/test contents
|
||||
expect 3:/dir/test:token3
|
||||
3 waitwatch
|
||||
expect 2:/dir/test:token2
|
||||
2 waitwatch
|
||||
expect 1:/dir/test:token1
|
||||
1 waitwatch
|
||||
1 close
|
||||
2 close
|
||||
3 close
|
||||
|
||||
# If one dies (without acking), the other should still get ack.
|
||||
1 watch /dir token1
|
||||
2 watch /dir token2
|
||||
write /dir/test contents
|
||||
expect 2:/dir/test:token2
|
||||
2 waitwatch
|
||||
2 close
|
||||
expect 1:/dir/test:token1
|
||||
1 waitwatch
|
||||
1 close
|
||||
|
||||
# If one dies (without reading at all), the other should still get ack.
|
||||
1 watch /dir token1
|
||||
2 watch /dir token2
|
||||
write /dir/test contents
|
||||
2 close
|
||||
expect 1:/dir/test:token1
|
||||
1 waitwatch
|
||||
1 close
|
||||
2 close
|
||||
|
||||
# unwatch
|
||||
1 watch /dir token1
|
||||
1 unwatch /dir token1
|
||||
1 watch /dir token2
|
||||
2 write /dir/test2 contents
|
||||
expect 1:/dir/test2:token2
|
||||
1 waitwatch
|
||||
1 unwatch /dir token2
|
||||
1 close
|
||||
2 close
|
||||
|
||||
# unwatch while watch pending. Other watcher still gets the event.
|
||||
1 watch /dir token1
|
||||
2 watch /dir token2
|
||||
write /dir/test contents
|
||||
2 unwatch /dir token2
|
||||
expect 1:/dir/test:token1
|
||||
1 waitwatch
|
||||
1 close
|
||||
2 close
|
||||
|
||||
# unwatch while watch pending. Should clear this so we get next event.
|
||||
1 watch /dir token1
|
||||
write /dir/test contents
|
||||
1 unwatch /dir token1
|
||||
1 watch /dir/test token2
|
||||
write /dir/test contents2
|
||||
expect 1:/dir/test:token2
|
||||
1 waitwatch
|
||||
|
||||
# check we only get notified once.
|
||||
1 watch /test token
|
||||
2 write /test contents2
|
||||
expect 1:/test:token
|
||||
1 waitwatch
|
||||
expect 1: waitwatch failed: Connection timed out
|
||||
1 waitwatch
|
||||
1 close
|
||||
|
||||
# watches are queued in order.
|
||||
1 watch / token
|
||||
2 write /test1 contents
|
||||
2 write /test2 contents
|
||||
2 write /test3 contents
|
||||
expect 1:/test1:token
|
||||
1 waitwatch
|
||||
expect 1:/test2:token
|
||||
1 waitwatch
|
||||
expect 1:/test3:token
|
||||
1 waitwatch
|
||||
1 close
|
||||
|
||||
# Creation of subpaths should be covered correctly.
|
||||
1 watch / token
|
||||
2 write /test/subnode contents2
|
||||
2 write /test/subnode/subnode contents2
|
||||
expect 1:/test/subnode:token
|
||||
1 waitwatch
|
||||
expect 1:/test/subnode/subnode:token
|
||||
1 waitwatch
|
||||
expect 1: waitwatch failed: Connection timed out
|
||||
1 waitwatch
|
||||
1 close
|
||||
|
||||
# Watch event must have happened before we registered interest.
|
||||
1 watch / token
|
||||
2 write /test/subnode contents2
|
||||
1 watchnoack / token2 0
|
||||
expect 1:/test/subnode:token
|
||||
1 waitwatch
|
||||
expect 1:/:token2
|
||||
1 waitwatch
|
||||
expect 1: waitwatch failed: Connection timed out
|
||||
1 waitwatch
|
||||
1 close
|
||||
|
||||
# Rm fires notification on child.
|
||||
1 watch /test/subnode token
|
||||
2 rm /test
|
||||
expect 1:/test/subnode:token
|
||||
1 waitwatch
|
||||
|
||||
# Watch should not double-send after we ack, even if we did something in between.
|
||||
1 watch /test2 token
|
||||
2 write /test2/foo contents2
|
||||
expect 1:/test2/foo:token
|
||||
1 waitwatch
|
||||
expect 1:contents2
|
||||
1 read /test2/foo
|
||||
expect 1: waitwatch failed: Connection timed out
|
||||
1 waitwatch
|
||||
@@ -1,43 +0,0 @@
|
||||
# Test transaction clashes.
|
||||
|
||||
mkdir /test
|
||||
write /test/entry1 contents
|
||||
|
||||
# Start transaction, do read-only op, transaction succeeds
|
||||
1 start
|
||||
1 write /test/entry1 contents2
|
||||
expect contents
|
||||
read /test/entry1
|
||||
1 commit
|
||||
expect contents2
|
||||
read /test/entry1
|
||||
|
||||
# Start transaction, abort other transaction, transaction succeeds.
|
||||
1 start
|
||||
1 write /test/entry1 contents3
|
||||
start
|
||||
write /test/entry1 contents
|
||||
abort
|
||||
1 commit
|
||||
expect contents3
|
||||
read /test/entry1
|
||||
|
||||
# Start transaction, do write op, transaction fails
|
||||
1 start
|
||||
1 write /test/entry1 contents4
|
||||
write /test/entry1 contents
|
||||
expect 1: commit failed: Resource temporarily unavailable
|
||||
1 commit
|
||||
expect contents
|
||||
read /test/entry1
|
||||
|
||||
# Start transaction, do other transaction, transaction fails
|
||||
1 start
|
||||
1 write /test/entry1 contents4
|
||||
start
|
||||
write /test/entry1 contents5
|
||||
commit
|
||||
expect 1: commit failed: Resource temporarily unavailable
|
||||
1 commit
|
||||
expect contents5
|
||||
read /test/entry1
|
||||
@@ -1,92 +0,0 @@
|
||||
# Test transactions.
|
||||
|
||||
mkdir /test
|
||||
|
||||
# Simple transaction: create a file inside transaction.
|
||||
1 start
|
||||
1 write /test/entry1 contents
|
||||
2 dir /test
|
||||
expect 1:entry1
|
||||
1 dir /test
|
||||
1 commit
|
||||
expect 2:contents
|
||||
2 read /test/entry1
|
||||
|
||||
rm /test/entry1
|
||||
|
||||
# Create a file and abort transaction.
|
||||
1 start
|
||||
1 write /test/entry1 contents
|
||||
2 dir /test
|
||||
expect 1:entry1
|
||||
1 dir /test
|
||||
1 abort
|
||||
2 dir /test
|
||||
|
||||
write /test/entry1 contents
|
||||
# Delete in transaction, commit
|
||||
1 start
|
||||
1 rm /test/entry1
|
||||
expect 2:entry1
|
||||
2 dir /test
|
||||
1 dir /test
|
||||
1 commit
|
||||
2 dir /test
|
||||
|
||||
# Delete in transaction, abort.
|
||||
write /test/entry1 contents
|
||||
1 start
|
||||
1 rm /test/entry1
|
||||
expect 2:entry1
|
||||
2 dir /test
|
||||
1 dir /test
|
||||
1 abort
|
||||
expect 2:entry1
|
||||
2 dir /test
|
||||
|
||||
# Events inside transactions don't trigger watches until (successful) commit.
|
||||
mkdir /test/dir
|
||||
1 watch /test token
|
||||
2 start
|
||||
2 mkdir /test/dir/sub
|
||||
expect 1: waitwatch failed: Connection timed out
|
||||
1 waitwatch
|
||||
2 close
|
||||
1 close
|
||||
|
||||
1 watch /test token
|
||||
2 start
|
||||
2 mkdir /test/dir/sub
|
||||
2 abort
|
||||
expect 1: waitwatch failed: Connection timed out
|
||||
1 waitwatch
|
||||
1 close
|
||||
|
||||
1 watch /test token
|
||||
2 start
|
||||
2 mkdir /test/dir/sub
|
||||
2 commit
|
||||
expect 1:/test/dir/sub:token
|
||||
1 waitwatch
|
||||
1 close
|
||||
|
||||
# Rm inside transaction works like rm outside: children get notified.
|
||||
1 watch /test/dir/sub token
|
||||
2 start
|
||||
2 rm /test/dir
|
||||
2 commit
|
||||
expect 1:/test/dir/sub:token
|
||||
1 waitwatch
|
||||
1 close
|
||||
|
||||
# Multiple events from single transaction don't trigger assert
|
||||
1 watch /test token
|
||||
2 start
|
||||
2 write /test/1 contents
|
||||
2 write /test/2 contents
|
||||
2 commit
|
||||
expect 1:/test/1:token
|
||||
1 waitwatch
|
||||
expect 1:/test/2:token
|
||||
1 waitwatch
|
||||
1 close
|
||||
@@ -1,19 +0,0 @@
|
||||
# Test domain communication.
|
||||
|
||||
# Create a domain, write an entry.
|
||||
expect handle is 1
|
||||
introduce 1 100 7 /my/home
|
||||
1 write /entry1 contents
|
||||
expect entry1
|
||||
expect tool
|
||||
dir /
|
||||
close
|
||||
|
||||
# Release that domain.
|
||||
release 1
|
||||
close
|
||||
|
||||
# Introduce and release by same connection.
|
||||
expect handle is 2
|
||||
introduce 1 100 7 /my/home
|
||||
release 1
|
||||
@@ -1,18 +0,0 @@
|
||||
# Test domain "implicit" paths.
|
||||
|
||||
# Create a domain, write an entry using implicit path, read using implicit
|
||||
mkdir /home
|
||||
expect handle is 1
|
||||
introduce 1 100 7 /home
|
||||
1 write entry1 contents
|
||||
expect contents
|
||||
read /home/entry1
|
||||
expect entry1
|
||||
dir /home
|
||||
|
||||
# Place a watch using a relative path: expect relative answer.
|
||||
1 mkdir foo
|
||||
1 watch foo token
|
||||
write /home/foo/bar contents
|
||||
expect 1:foo/bar:token
|
||||
1 waitwatch
|
||||
@@ -1,50 +0,0 @@
|
||||
# Test watching from a domain.
|
||||
|
||||
# Watch something, write to it, check watch has fired.
|
||||
write /test contents
|
||||
mkdir /dir
|
||||
|
||||
expect handle is 1
|
||||
introduce 1 100 7 /my/home
|
||||
1 watch /test token
|
||||
write /test contents2
|
||||
expect 1:/test:token
|
||||
1 waitwatch
|
||||
1 unwatch /test token
|
||||
release 1
|
||||
1 close
|
||||
|
||||
# ignore watches while doing commands, should work.
|
||||
expect handle is 1
|
||||
introduce 1 100 7 /my/home
|
||||
1 watch /dir token
|
||||
write /dir/test contents
|
||||
1 write /dir/test2 contents2
|
||||
1 write /dir/test3 contents3
|
||||
1 write /dir/test4 contents4
|
||||
expect 1:/dir/test:token
|
||||
1 waitwatch
|
||||
release 1
|
||||
1 close
|
||||
|
||||
# unwatch
|
||||
expect handle is 1
|
||||
introduce 1 100 7 /my/home
|
||||
1 watch /dir token1
|
||||
1 unwatch /dir token1
|
||||
1 watch /dir token2
|
||||
write /dir/test2 contents
|
||||
expect 1:/dir/test2:token2
|
||||
1 waitwatch
|
||||
1 unwatch /dir token2
|
||||
release 1
|
||||
1 close
|
||||
|
||||
# unwatch while watch pending.
|
||||
expect handle is 1
|
||||
introduce 1 100 7 /my/home
|
||||
1 watch /dir token1
|
||||
write /dir/test2 contents
|
||||
1 unwatch /dir token1
|
||||
release 1
|
||||
1 close
|
||||
@@ -1,38 +0,0 @@
|
||||
# Test that read only connection can't alter store.
|
||||
|
||||
write /test contents
|
||||
|
||||
readonly
|
||||
expect test
|
||||
expect tool
|
||||
dir /
|
||||
|
||||
expect contents
|
||||
read /test
|
||||
expect 0 READ
|
||||
getperm /test
|
||||
watch /test token
|
||||
unwatch /test token
|
||||
start
|
||||
commit
|
||||
start
|
||||
abort
|
||||
|
||||
# These don't work
|
||||
expect write failed: Permission denied
|
||||
write /test2 contents
|
||||
expect write failed: Permission denied
|
||||
write /test contents
|
||||
expect setperm failed: Permission denied
|
||||
setperm /test 100 NONE
|
||||
expect setperm failed: Permission denied
|
||||
setperm /test 100 NONE
|
||||
expect introduce failed: Permission denied
|
||||
introduce 1 100 7 /home
|
||||
|
||||
# Check that watches work like normal.
|
||||
watch / token
|
||||
1 readwrite
|
||||
1 write /test contents
|
||||
expect /test:token
|
||||
waitwatch
|
||||
@@ -1,21 +0,0 @@
|
||||
# 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.
|
||||
mkdir /test
|
||||
mkdir /test/1
|
||||
mkdir /test/2
|
||||
mkdir /test/3
|
||||
|
||||
# Watch all three, fire event on 2, read watch, fire event on 1 and 3, ack 2.
|
||||
1 watch /test/1 token1
|
||||
1 watch /test/2 token2
|
||||
1 watch /test/3 token3
|
||||
2 write /test/2 contents2
|
||||
expect 1:/test/2:token2
|
||||
1 waitwatch
|
||||
3 write /test/1 contents1
|
||||
4 write /test/3 contents3
|
||||
1 close
|
||||
@@ -1,68 +0,0 @@
|
||||
# We should not be able to tell the difference between a node which
|
||||
# doesn't exist, and a node we don't have permission on, if we don't
|
||||
# have permission on it directory.
|
||||
|
||||
mkdir /dir
|
||||
setperm /dir 0 NONE
|
||||
|
||||
# First when it doesn't exist
|
||||
setid 1
|
||||
expect *Permission denied
|
||||
dir /dir/file
|
||||
expect *Permission denied
|
||||
read /dir/file
|
||||
expect *Permission denied
|
||||
write /dir/file value
|
||||
expect *Permission denied
|
||||
mkdir /dir/file
|
||||
expect *Permission denied
|
||||
rm /dir/file
|
||||
expect *Permission denied
|
||||
rm /dir
|
||||
expect *Permission denied
|
||||
getperm /dir/file
|
||||
expect *Permission denied
|
||||
setperm /dir/file 0 NONE
|
||||
# We get no watch event when there's no permission. It's a corner case.
|
||||
watchnoack /dir/file token
|
||||
1 write /dir/file contents
|
||||
1 rm /dir/file
|
||||
expect waitwatch failed: Connection timed out
|
||||
waitwatch
|
||||
unwatch /dir/file token
|
||||
expect *No such file or directory
|
||||
unwatch /dir/file token
|
||||
expect *Permission denied
|
||||
introduce 2 100 7 /dir/file
|
||||
|
||||
# Now it exists
|
||||
setid 0
|
||||
write /dir/file contents
|
||||
|
||||
setid 1
|
||||
expect *Permission denied
|
||||
dir /dir/file
|
||||
expect *Permission denied
|
||||
read /dir/file
|
||||
expect *Permission denied
|
||||
write /dir/file value
|
||||
expect *Permission denied
|
||||
mkdir /dir/file
|
||||
expect *Permission denied
|
||||
rm /dir/file
|
||||
expect *Permission denied
|
||||
rm /dir
|
||||
expect *Permission denied
|
||||
getperm /dir/file
|
||||
expect *Permission denied
|
||||
setperm /dir/file 0 NONE
|
||||
watchnoack /dir/file token
|
||||
1 write /dir/file contents
|
||||
1 rm /dir/file
|
||||
expect waitwatch failed: Connection timed out
|
||||
waitwatch
|
||||
unwatch /dir/file token
|
||||
expect *No such file or directory
|
||||
unwatch /dir/file token
|
||||
expect *Permission denied
|
||||
introduce 2 100 7 /dir/file
|
||||
@@ -1,64 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
set -e
|
||||
set -m
|
||||
|
||||
run_test()
|
||||
{
|
||||
rm -rf $XENSTORED_ROOTDIR
|
||||
mkdir $XENSTORED_ROOTDIR
|
||||
if [ $VALGRIND -eq 1 ]; then
|
||||
valgrind --suppressions=testsuite/vg-suppressions -q ./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
|
||||
else
|
||||
# We don't get error messages from this, though.
|
||||
PID=`./xenstored_test --output-pid --trace-file=testsuite/tmp/trace`
|
||||
fi
|
||||
if ./xs_test $2 $1; then
|
||||
if [ -s testsuite/tmp/xenstored_errors ]; then
|
||||
kill $PID
|
||||
echo Errors:
|
||||
cat testsuite/tmp/xenstored_errors
|
||||
return 1
|
||||
fi
|
||||
kill $PID
|
||||
sleep 1
|
||||
return 0
|
||||
else
|
||||
# In case daemon is wedged.
|
||||
kill $PID
|
||||
sleep 1
|
||||
return 1
|
||||
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]*.test $SLOWTESTS; do
|
||||
case `basename $f` in $MATCH) RUN=1;; esac
|
||||
[ -n "$RUN" ] || continue
|
||||
|
||||
if run_test $f -x >/tmp/out; then
|
||||
echo -n .
|
||||
else
|
||||
cat /tmp/out
|
||||
# That will have filled the screen, repeat message.
|
||||
echo Test $f failed
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
Glibc goes boom from _start (Debian glibc 2.3.5-3)
|
||||
Memcheck:Cond
|
||||
obj:/lib/ld-2.3.5.so
|
||||
obj:/lib/ld-2.3.5.so
|
||||
obj:/lib/ld-2.3.5.so
|
||||
obj:/lib/ld-2.3.5.so
|
||||
obj:/lib/ld-2.3.5.so
|
||||
}
|
||||
@@ -39,7 +39,6 @@
|
||||
#include <assert.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
//#define DEBUG
|
||||
#include "utils.h"
|
||||
#include "list.h"
|
||||
#include "talloc.h"
|
||||
@@ -53,7 +52,6 @@
|
||||
|
||||
#include "hashtable.h"
|
||||
|
||||
|
||||
extern int xce_handle; /* in xenstored_domain.c */
|
||||
|
||||
static bool verbose = false;
|
||||
@@ -82,50 +80,6 @@ int quota_nb_watch_per_domain = 128;
|
||||
int quota_max_entry_size = 2048; /* 2K */
|
||||
int quota_max_transaction = 10;
|
||||
|
||||
#ifdef TESTING
|
||||
static bool failtest = false;
|
||||
|
||||
/* We override talloc's malloc. */
|
||||
void *test_malloc(size_t size)
|
||||
{
|
||||
/* 1 in 20 means only about 50% of connections establish. */
|
||||
if (failtest && (random() % 32) == 0)
|
||||
return NULL;
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void stop_failtest(int signum __attribute__((unused)))
|
||||
{
|
||||
failtest = false;
|
||||
}
|
||||
|
||||
/* Need these before we #define away write_all/mkdir in testing.h */
|
||||
bool test_write_all(int fd, void *contents, unsigned int len);
|
||||
bool test_write_all(int fd, void *contents, unsigned int len)
|
||||
{
|
||||
if (failtest && (random() % 8) == 0) {
|
||||
if (len)
|
||||
len = random() % len;
|
||||
write(fd, contents, len);
|
||||
errno = ENOSPC;
|
||||
return false;
|
||||
}
|
||||
return xs_write_all(fd, contents, len);
|
||||
}
|
||||
|
||||
int test_mkdir(const char *dir, int perms);
|
||||
int test_mkdir(const char *dir, int perms)
|
||||
{
|
||||
if (failtest && (random() % 8) == 0) {
|
||||
errno = ENOSPC;
|
||||
return -1;
|
||||
}
|
||||
return mkdir(dir, perms);
|
||||
}
|
||||
#endif /* TESTING */
|
||||
|
||||
#include "xenstored_test.h"
|
||||
|
||||
TDB_CONTEXT *tdb_context(struct connection *conn)
|
||||
{
|
||||
/* conn = NULL used in manual_node at setup. */
|
||||
@@ -1163,12 +1117,10 @@ static void do_debug(struct connection *conn, struct buffered_data *in)
|
||||
{
|
||||
int num;
|
||||
|
||||
#ifndef TESTING
|
||||
if (conn->id != 0) {
|
||||
send_error(conn, EACCES);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
num = xs_count_strings(in->buffer, in->used);
|
||||
|
||||
@@ -1179,18 +1131,10 @@ static void do_debug(struct connection *conn, struct buffered_data *in)
|
||||
}
|
||||
xprintf("debug: %s", in->buffer + get_string(in, 0));
|
||||
}
|
||||
|
||||
if (streq(in->buffer, "check"))
|
||||
check_store();
|
||||
#ifdef TESTING
|
||||
/* For testing, we allow them to set id. */
|
||||
if (streq(in->buffer, "setid")) {
|
||||
conn->id = atoi(in->buffer + get_string(in, 0));
|
||||
} else if (streq(in->buffer, "failtest")) {
|
||||
if (get_string(in, 0) < in->used)
|
||||
srandom(atoi(in->buffer + get_string(in, 0)));
|
||||
failtest = true;
|
||||
}
|
||||
#endif /* TESTING */
|
||||
|
||||
send_ack(conn, XS_DEBUG);
|
||||
}
|
||||
|
||||
@@ -1319,10 +1263,8 @@ static void handle_input(struct connection *conn)
|
||||
return;
|
||||
|
||||
if (in->hdr.msg.len > PATH_MAX) {
|
||||
#ifndef TESTING
|
||||
syslog(LOG_ERR, "Client tried to feed us %i",
|
||||
in->hdr.msg.len);
|
||||
#endif
|
||||
goto bad_client;
|
||||
}
|
||||
|
||||
@@ -1414,39 +1356,7 @@ static void accept_connection(int sock, bool canwrite)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
#ifdef TESTING
|
||||
/* Valgrind can check our writes better if we don't use mmap */
|
||||
#define TDB_FLAGS TDB_NOMMAP
|
||||
/* Useful for running under debugger. */
|
||||
void dump_connection(void)
|
||||
{
|
||||
struct connection *i;
|
||||
|
||||
list_for_each_entry(i, &connections, list) {
|
||||
printf("Connection %p:\n", i);
|
||||
printf(" state = %s\n",
|
||||
list_empty(&i->out_list) ? "OK" : "BUSY");
|
||||
if (i->id)
|
||||
printf(" id = %i\n", i->id);
|
||||
if (!i->in->inhdr || i->in->used)
|
||||
printf(" got %i bytes of %s\n",
|
||||
i->in->used, i->in->inhdr ? "header" : "data");
|
||||
#if 0
|
||||
if (i->out)
|
||||
printf(" sending message %s (%s) out\n",
|
||||
sockmsg_string(i->out->hdr.msg.type),
|
||||
i->out->buffer);
|
||||
if (i->transaction)
|
||||
dump_transaction(i);
|
||||
if (i->domain)
|
||||
dump_domain(i);
|
||||
#endif
|
||||
dump_watches(i);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define TDB_FLAGS 0
|
||||
#endif
|
||||
|
||||
/* We create initial nodes manually. */
|
||||
static void manual_node(const char *name, const char *child)
|
||||
@@ -1693,10 +1603,6 @@ static void corrupt(struct connection *conn, const char *fmt, ...)
|
||||
log("corruption detected by connection %i: err %s: %s",
|
||||
conn ? (int)conn->id : -1, strerror(saved_errno), str);
|
||||
|
||||
#ifdef TESTING
|
||||
/* Allow them to attach debugger. */
|
||||
sleep(30);
|
||||
#endif
|
||||
check_store();
|
||||
}
|
||||
|
||||
@@ -1740,11 +1646,10 @@ static void daemonize(void)
|
||||
if (pid != 0)
|
||||
exit(0);
|
||||
|
||||
#ifndef TESTING /* Relative paths for socket names */
|
||||
/* Move off any mount points we might be in. */
|
||||
if (chdir("/") == -1)
|
||||
barf_perror("Failed to chdir");
|
||||
#endif
|
||||
|
||||
/* Discard our parent's old-fashioned umask prejudices. */
|
||||
umask(0);
|
||||
}
|
||||
@@ -1941,10 +1846,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
signal(SIGHUP, trigger_reopen_log);
|
||||
|
||||
#ifdef TESTING
|
||||
signal(SIGUSR1, stop_failtest);
|
||||
#endif
|
||||
|
||||
if (xce_handle != -1)
|
||||
evtchn_fd = xc_evtchn_fd(xce_handle);
|
||||
|
||||
|
||||
@@ -23,14 +23,12 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
//#define DEBUG
|
||||
#include "utils.h"
|
||||
#include "talloc.h"
|
||||
#include "xenstored_core.h"
|
||||
#include "xenstored_domain.h"
|
||||
#include "xenstored_transaction.h"
|
||||
#include "xenstored_watch.h"
|
||||
#include "xenstored_test.h"
|
||||
|
||||
#include <xenctrl.h>
|
||||
|
||||
@@ -217,10 +215,8 @@ void handle_event(void)
|
||||
if (port == virq_port)
|
||||
domain_cleanup();
|
||||
|
||||
#ifndef TESTING
|
||||
if (xc_evtchn_unmask(xce_handle, port) == -1)
|
||||
barf_perror("Failed to write to event fd");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool domain_can_read(struct connection *conn)
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
Testing replcements for Xen Store Daemon.
|
||||
Copyright (C) 2005 Rusty Russell IBM Corporation
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
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_TEST_H
|
||||
#define _XENSTORED_TEST_H
|
||||
|
||||
#ifdef TESTING
|
||||
bool test_write_all(int fd, void *contents, unsigned int len);
|
||||
#define xs_write_all test_write_all
|
||||
|
||||
int test_mkdir(const char *dir, int perms);
|
||||
#define mkdir test_mkdir
|
||||
|
||||
int fake_open_eventchn(void);
|
||||
void fake_block_events(void);
|
||||
void fake_ack_event(void);
|
||||
|
||||
#define ioctl(a,b,c) 0
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _XENSTORED_INTERNAL_H */
|
||||
@@ -35,7 +35,6 @@
|
||||
#include "xenstored_domain.h"
|
||||
#include "xs_lib.h"
|
||||
#include "utils.h"
|
||||
#include "xenstored_test.h"
|
||||
|
||||
struct changed_node
|
||||
{
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include "xenstored_watch.h"
|
||||
#include "xs_lib.h"
|
||||
#include "utils.h"
|
||||
#include "xenstored_test.h"
|
||||
#include "xenstored_domain.h"
|
||||
|
||||
extern int quota_nb_watch_per_domain;
|
||||
@@ -196,17 +195,6 @@ void conn_delete_all_watches(struct connection *conn)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TESTING
|
||||
void dump_watches(struct connection *conn)
|
||||
{
|
||||
struct watch *watch;
|
||||
|
||||
list_for_each_entry(watch, &conn->watches, list)
|
||||
printf(" watch on %s token %s\n",
|
||||
watch->node, watch->token);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
|
||||
@@ -1,393 +0,0 @@
|
||||
/* Code which randomly corrupts bits going to the daemon.
|
||||
Copyright (C) 2005 Rusty Russell IBM Corporation
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include "xs.h"
|
||||
#include "talloc.h"
|
||||
#include <errno.h>
|
||||
|
||||
#define XSTEST
|
||||
#define RAND_FREQ 128 /* One char in 32 is corrupted. */
|
||||
|
||||
/* jhash.h: Jenkins hash support.
|
||||
*
|
||||
* Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net)
|
||||
*
|
||||
* http://burtleburtle.net/bob/hash/
|
||||
*
|
||||
* These are the credits from Bob's sources:
|
||||
*
|
||||
* lookup2.c, by Bob Jenkins, December 1996, Public Domain.
|
||||
* hash(), hash2(), hash3, and mix() are externally useful functions.
|
||||
* Routines to test the hash are included if SELF_TEST is defined.
|
||||
* You can use this free for any purpose. It has no warranty.
|
||||
*
|
||||
* Copyright (C) 2003 David S. Miller (davem@redhat.com)
|
||||
*
|
||||
* I've modified Bob's hash to be useful in the Linux kernel, and
|
||||
* any bugs present are surely my fault. -DaveM
|
||||
*/
|
||||
|
||||
/* NOTE: Arguments are modified. */
|
||||
#define __jhash_mix(a, b, c) \
|
||||
{ \
|
||||
a -= b; a -= c; a ^= (c>>13); \
|
||||
b -= c; b -= a; b ^= (a<<8); \
|
||||
c -= a; c -= b; c ^= (b>>13); \
|
||||
a -= b; a -= c; a ^= (c>>12); \
|
||||
b -= c; b -= a; b ^= (a<<16); \
|
||||
c -= a; c -= b; c ^= (b>>5); \
|
||||
a -= b; a -= c; a ^= (c>>3); \
|
||||
b -= c; b -= a; b ^= (a<<10); \
|
||||
c -= a; c -= b; c ^= (b>>15); \
|
||||
}
|
||||
|
||||
/* The golden ration: an arbitrary value */
|
||||
#define JHASH_GOLDEN_RATIO 0x9e3779b9
|
||||
|
||||
/* The most generic version, hashes an arbitrary sequence
|
||||
* of bytes. No alignment or length assumptions are made about
|
||||
* the input key.
|
||||
*/
|
||||
static inline uint32_t jhash(const void *key, uint32_t length, uint32_t initval)
|
||||
{
|
||||
uint32_t a, b, c, len;
|
||||
const uint8_t *k = key;
|
||||
|
||||
len = length;
|
||||
a = b = JHASH_GOLDEN_RATIO;
|
||||
c = initval;
|
||||
|
||||
while (len >= 12) {
|
||||
a += (k[0] +((uint32_t)k[1]<<8) +((uint32_t)k[2]<<16) +((uint32_t)k[3]<<24));
|
||||
b += (k[4] +((uint32_t)k[5]<<8) +((uint32_t)k[6]<<16) +((uint32_t)k[7]<<24));
|
||||
c += (k[8] +((uint32_t)k[9]<<8) +((uint32_t)k[10]<<16)+((uint32_t)k[11]<<24));
|
||||
|
||||
__jhash_mix(a,b,c);
|
||||
|
||||
k += 12;
|
||||
len -= 12;
|
||||
}
|
||||
|
||||
c += length;
|
||||
switch (len) {
|
||||
case 11: c += ((uint32_t)k[10]<<24);
|
||||
case 10: c += ((uint32_t)k[9]<<16);
|
||||
case 9 : c += ((uint32_t)k[8]<<8);
|
||||
case 8 : b += ((uint32_t)k[7]<<24);
|
||||
case 7 : b += ((uint32_t)k[6]<<16);
|
||||
case 6 : b += ((uint32_t)k[5]<<8);
|
||||
case 5 : b += k[4];
|
||||
case 4 : a += ((uint32_t)k[3]<<24);
|
||||
case 3 : a += ((uint32_t)k[2]<<16);
|
||||
case 2 : a += ((uint32_t)k[1]<<8);
|
||||
case 1 : a += k[0];
|
||||
};
|
||||
|
||||
__jhash_mix(a,b,c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/* A special optimized version that handles 1 or more of uint32_ts.
|
||||
* The length parameter here is the number of uint32_ts in the key.
|
||||
*/
|
||||
static inline uint32_t jhash2(uint32_t *k, uint32_t length, uint32_t initval)
|
||||
{
|
||||
uint32_t a, b, c, len;
|
||||
|
||||
a = b = JHASH_GOLDEN_RATIO;
|
||||
c = initval;
|
||||
len = length;
|
||||
|
||||
while (len >= 3) {
|
||||
a += k[0];
|
||||
b += k[1];
|
||||
c += k[2];
|
||||
__jhash_mix(a, b, c);
|
||||
k += 3; len -= 3;
|
||||
}
|
||||
|
||||
c += length * 4;
|
||||
|
||||
switch (len) {
|
||||
case 2 : b += k[1];
|
||||
case 1 : a += k[0];
|
||||
};
|
||||
|
||||
__jhash_mix(a,b,c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/* A special ultra-optimized versions that knows they are hashing exactly
|
||||
* 3, 2 or 1 word(s).
|
||||
*
|
||||
* NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally
|
||||
* done at the end is not done here.
|
||||
*/
|
||||
static inline uint32_t jhash_3words(uint32_t a, uint32_t b, uint32_t c, uint32_t initval)
|
||||
{
|
||||
a += JHASH_GOLDEN_RATIO;
|
||||
b += JHASH_GOLDEN_RATIO;
|
||||
c += initval;
|
||||
|
||||
__jhash_mix(a, b, c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline uint32_t jhash_2words(uint32_t a, uint32_t b, uint32_t initval)
|
||||
{
|
||||
return jhash_3words(a, b, 0, initval);
|
||||
}
|
||||
|
||||
static inline uint32_t jhash_1word(uint32_t a, uint32_t initval)
|
||||
{
|
||||
return jhash_3words(a, 0, 0, initval);
|
||||
}
|
||||
|
||||
static unsigned int get_randomness(int *state)
|
||||
{
|
||||
return jhash_1word((*state)++, *state * 1103515243);
|
||||
}
|
||||
|
||||
static int state;
|
||||
|
||||
/* Lengthening headers is pointless: other end will just wait for more
|
||||
* data and timeout. We merely shorten the length. */
|
||||
static void corrupt_header(char *output, const struct xsd_sockmsg *msg,
|
||||
unsigned int *next_bit)
|
||||
{
|
||||
struct xsd_sockmsg newmsg = *msg;
|
||||
|
||||
while (*next_bit < sizeof(*msg)) {
|
||||
if (newmsg.len)
|
||||
newmsg.len = get_randomness(&state) % newmsg.len;
|
||||
*next_bit += get_randomness(&state) % RAND_FREQ;
|
||||
}
|
||||
memcpy(output, &newmsg, sizeof(newmsg));
|
||||
}
|
||||
|
||||
#define read_all_choice read_all
|
||||
static bool write_all_choice(int fd, const void *data, unsigned int len)
|
||||
{
|
||||
char corrupt_data[len];
|
||||
bool ret;
|
||||
static unsigned int next_bit;
|
||||
|
||||
if (len == sizeof(struct xsd_sockmsg)
|
||||
&& ((unsigned long)data % __alignof__(struct xsd_sockmsg)) == 0)
|
||||
corrupt_header(corrupt_data, data, &next_bit);
|
||||
else {
|
||||
memcpy(corrupt_data, data, len);
|
||||
while (next_bit < len * CHAR_BIT) {
|
||||
corrupt_data[next_bit/CHAR_BIT]
|
||||
^= (1 << (next_bit%CHAR_BIT));
|
||||
next_bit += get_randomness(&state) % RAND_FREQ;
|
||||
}
|
||||
}
|
||||
|
||||
ret = xs_write_all(fd, corrupt_data, len);
|
||||
next_bit -= len * CHAR_BIT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#include "xs.c"
|
||||
|
||||
static char *random_path(void)
|
||||
{
|
||||
unsigned int i;
|
||||
char *ret = NULL;
|
||||
|
||||
if (get_randomness(&state) % 20 == 0)
|
||||
return talloc_strdup(NULL, "/");
|
||||
|
||||
for (i = 0; i < 1 || (get_randomness(&state) % 2); i++) {
|
||||
ret = talloc_asprintf_append(ret, "/%i",
|
||||
get_randomness(&state) % 15);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Do the next operation, return the results. */
|
||||
static void do_next_op(struct xs_handle *h, bool verbose)
|
||||
{
|
||||
char *name;
|
||||
unsigned int num;
|
||||
|
||||
if (verbose)
|
||||
printf("State %i: ", state);
|
||||
|
||||
name = random_path();
|
||||
switch (get_randomness(&state) % 9) {
|
||||
case 0:
|
||||
if (verbose)
|
||||
printf("DIR %s\n", name);
|
||||
free(xs_directory(h, name, &num));
|
||||
break;
|
||||
case 1:
|
||||
if (verbose)
|
||||
printf("READ %s\n", name);
|
||||
free(xs_read(h, name, &num));
|
||||
break;
|
||||
case 2: {
|
||||
char *contents = talloc_asprintf(NULL, "%i",
|
||||
get_randomness(&state));
|
||||
unsigned int len = get_randomness(&state)%(strlen(contents)+1);
|
||||
if (verbose)
|
||||
printf("WRITE %s %.*s\n", name, len, contents);
|
||||
xs_write(h, name, contents, len);
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
if (verbose)
|
||||
printf("MKDIR %s\n", name);
|
||||
xs_mkdir(h, name);
|
||||
break;
|
||||
case 4:
|
||||
if (verbose)
|
||||
printf("RM %s\n", name);
|
||||
xs_rm(h, name);
|
||||
break;
|
||||
case 5:
|
||||
if (verbose)
|
||||
printf("GETPERMS %s\n", name);
|
||||
free(xs_get_permissions(h, name, &num));
|
||||
break;
|
||||
case 6: {
|
||||
unsigned int i, num = get_randomness(&state)%8;
|
||||
struct xs_permissions perms[num];
|
||||
|
||||
if (verbose)
|
||||
printf("SETPERMS %s: ", name);
|
||||
for (i = 0; i < num; i++) {
|
||||
perms[i].id = get_randomness(&state)%8;
|
||||
perms[i].perms = get_randomness(&state)%4;
|
||||
if (verbose)
|
||||
printf("%i%c ", perms[i].id,
|
||||
perms[i].perms == XS_PERM_WRITE ? 'W'
|
||||
: perms[i].perms == XS_PERM_READ ? 'R'
|
||||
: perms[i].perms ==
|
||||
(XS_PERM_READ|XS_PERM_WRITE) ? 'B'
|
||||
: 'N');
|
||||
}
|
||||
if (verbose)
|
||||
printf("\n");
|
||||
xs_set_permissions(h, name, perms, num);
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
if (verbose)
|
||||
printf("START %s\n", name);
|
||||
xs_transaction_start(h);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
bool abort = (get_randomness(&state) % 2);
|
||||
|
||||
if (verbose)
|
||||
printf("STOP %s\n", abort ? "ABORT" : "COMMIT");
|
||||
xs_transaction_end(h, abort);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
barf("Impossible randomness");
|
||||
}
|
||||
}
|
||||
|
||||
static struct xs_handle *h;
|
||||
static void alarmed(int sig __attribute__((unused)))
|
||||
{
|
||||
/* We force close on timeout. */
|
||||
close(h->fd);
|
||||
}
|
||||
|
||||
static int start_daemon(void)
|
||||
{
|
||||
int fds[2];
|
||||
int daemon_pid;
|
||||
|
||||
/* Start daemon. */
|
||||
pipe(fds);
|
||||
if ((daemon_pid = fork())) {
|
||||
/* Child writes PID when its ready: we wait for that. */
|
||||
char buffer[20];
|
||||
close(fds[1]);
|
||||
if (read(fds[0], buffer, sizeof(buffer)) < 0)
|
||||
barf("Failed to summon daemon");
|
||||
close(fds[0]);
|
||||
return daemon_pid;
|
||||
} else {
|
||||
dup2(fds[1], STDOUT_FILENO);
|
||||
close(fds[0]);
|
||||
#if 1
|
||||
execlp("valgrind", "valgrind", "--log-file=/tmp/xs_crashme.vglog", "-q", "./xenstored_test", "--output-pid",
|
||||
"--no-fork", "--trace-file=/tmp/trace", NULL);
|
||||
#else
|
||||
execlp("./xenstored_test", "xenstored_test", "--output-pid",
|
||||
"--no-fork", NULL);
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
unsigned int i;
|
||||
int pid;
|
||||
|
||||
if (argc != 3 && argc != 4)
|
||||
barf("Usage: xs_crashme <iterations> <seed> [pid]");
|
||||
|
||||
if (argc == 3)
|
||||
pid = start_daemon();
|
||||
else
|
||||
pid = atoi(argv[3]);
|
||||
|
||||
state = atoi(argv[2]);
|
||||
h = xs_daemon_open();
|
||||
if (!h)
|
||||
barf_perror("Opening connection to daemon");
|
||||
signal(SIGALRM, alarmed);
|
||||
for (i = 0; i < (unsigned)atoi(argv[1]); i++) {
|
||||
alarm(1);
|
||||
do_next_op(h, false);
|
||||
if (i % (atoi(argv[1]) / 72 ?: 1) == 0) {
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
}
|
||||
if (kill(pid, 0) != 0)
|
||||
barf_perror("Pinging daemon on iteration %i", i);
|
||||
if (h->fd < 0) {
|
||||
xs_daemon_close(h);
|
||||
h = xs_daemon_open();
|
||||
if (!h)
|
||||
barf_perror("Connecting on iteration %i", i);
|
||||
}
|
||||
}
|
||||
kill(pid, SIGTERM);
|
||||
return 0;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,207 +0,0 @@
|
||||
/* Stress test for Xen Store: multiple people hammering transactions */
|
||||
#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>
|
||||
#include <errno.h>
|
||||
|
||||
#define NUM_HANDLES 2
|
||||
#define DIR_FANOUT 3
|
||||
#define DIR_DEPTH 3
|
||||
|
||||
/* How often to print progress */
|
||||
static int print;
|
||||
|
||||
/* Layout looks like /<num>/<num>/count. */
|
||||
static void work(unsigned int cycles, unsigned int childnum)
|
||||
{
|
||||
unsigned int i;
|
||||
struct xs_handle *handles[NUM_HANDLES];
|
||||
char id;
|
||||
|
||||
if (childnum < 10)
|
||||
id = '0' + childnum;
|
||||
else
|
||||
id = 'A' + childnum - 10;
|
||||
|
||||
for (i = 0; i < NUM_HANDLES; i++) {
|
||||
handles[i] = xs_daemon_open();
|
||||
if (!handles[i])
|
||||
barf_perror("Opening handle %i", i);
|
||||
}
|
||||
|
||||
srandom(childnum);
|
||||
for (i = 0; i < cycles; i++) {
|
||||
unsigned int j, len;
|
||||
char file[100] = "";
|
||||
char *contents, tmp[100];
|
||||
struct xs_handle *h = handles[random() % NUM_HANDLES];
|
||||
|
||||
for (j = 0; j < DIR_DEPTH; j++)
|
||||
sprintf(file + strlen(file), "/%li",
|
||||
random()%DIR_FANOUT);
|
||||
|
||||
if (!xs_transaction_start(h))
|
||||
barf_perror("%i: starting transaction %i",
|
||||
childnum, i);
|
||||
|
||||
sprintf(file + strlen(file), "/count");
|
||||
contents = xs_read(h, file, &len);
|
||||
if (!contents)
|
||||
barf_perror("%i: can't read %s iter %i",
|
||||
childnum, file, i);
|
||||
sprintf(tmp, "%i", atoi(contents) + 1);
|
||||
if (!xs_write(h, file, tmp, strlen(tmp)+1))
|
||||
barf_perror("%i: can't write %s iter %i",
|
||||
childnum, file, i);
|
||||
|
||||
/* Abandon 1 in 10 */
|
||||
if (random() % 10 == 0) {
|
||||
if (!xs_transaction_end(h, true))
|
||||
barf_perror("%i: can't abort transact",
|
||||
childnum);
|
||||
i--;
|
||||
} else {
|
||||
if (!xs_transaction_end(h, false)) {
|
||||
if (errno == EAGAIN) {
|
||||
write(STDOUT_FILENO, "!", 1);
|
||||
i--;
|
||||
} else
|
||||
barf_perror("%i: can't commit trans",
|
||||
childnum);
|
||||
} else {
|
||||
/* Offset when we print . so kids don't all
|
||||
* print at once. */
|
||||
if ((i + print/(childnum+1)) % print == 0)
|
||||
write(STDOUT_FILENO, &id, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void create_dirs(struct xs_handle *h, const char *base, int togo)
|
||||
{
|
||||
unsigned int i;
|
||||
char filename[100];
|
||||
|
||||
if (togo == 0) {
|
||||
sprintf(filename, "%s/count", base);
|
||||
if (!xs_write(h, filename, "0", 1))
|
||||
barf_perror("Writing to %s", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < DIR_FANOUT; i++) {
|
||||
sprintf(filename, "%s/%i", base, i);
|
||||
if (!xs_mkdir(h, filename))
|
||||
barf_perror("xs_mkdir %s", filename);
|
||||
create_dirs(h, filename, togo-1);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int add_count(struct xs_handle *h, const char *base, int togo)
|
||||
{
|
||||
unsigned int i, count;
|
||||
char filename[100];
|
||||
|
||||
if (togo == 0) {
|
||||
char *answer;
|
||||
unsigned int len;
|
||||
|
||||
sprintf(filename, "%s/count", base);
|
||||
answer = xs_read(h, filename, &len);
|
||||
if (!answer)
|
||||
barf_perror("Reading %s", filename);
|
||||
count = atoi(answer);
|
||||
free(answer);
|
||||
return count;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
for (i = 0; i < DIR_FANOUT; i++) {
|
||||
sprintf(filename, "%s/%i", base, i);
|
||||
count += add_count(h, filename, togo-1);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void setup(void)
|
||||
{
|
||||
struct xs_handle *h;
|
||||
|
||||
/* Do setup. */
|
||||
h = xs_daemon_open();
|
||||
if (!h)
|
||||
barf_perror("Contacting daemon");
|
||||
create_dirs(h, "", DIR_DEPTH);
|
||||
xs_daemon_close(h);
|
||||
}
|
||||
|
||||
static unsigned int tally_counts(void)
|
||||
{
|
||||
struct xs_handle *h;
|
||||
unsigned int ret;
|
||||
|
||||
h = xs_daemon_open();
|
||||
if (!h)
|
||||
barf_perror("Contacting daemon");
|
||||
|
||||
ret = add_count(h, "", DIR_DEPTH);
|
||||
xs_daemon_close(h);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
bool failed = false;
|
||||
int kids[10];
|
||||
|
||||
if (argc != 2)
|
||||
barf("Usage: xs_stress <iterations>");
|
||||
|
||||
printf("Setting up directories...\n");
|
||||
setup();
|
||||
|
||||
print = atoi(argv[1]) / 76;
|
||||
if (!print)
|
||||
print = 1;
|
||||
|
||||
printf("Running %i children...\n", ARRAY_SIZE(kids));
|
||||
for (i = 0; i < ARRAY_SIZE(kids); i++) {
|
||||
kids[i] = fork();
|
||||
if (kids[i] == -1)
|
||||
barf_perror("fork");
|
||||
if (kids[i] == 0) {
|
||||
work(atoi(argv[1]) / ARRAY_SIZE(kids), i);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(kids); i++) {
|
||||
int status;
|
||||
if (waitpid(kids[i], &status, 0) == -1)
|
||||
barf_perror("waitpid");
|
||||
if (!WIFEXITED(status))
|
||||
barf("Kid %i died via signal %i\n",
|
||||
i, WTERMSIG(status));
|
||||
if (WEXITSTATUS(status) != 0) {
|
||||
printf("Child %i exited %i\n", i, WEXITSTATUS(status));
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
if (failed)
|
||||
exit(1);
|
||||
|
||||
printf("\nCounting results...\n");
|
||||
i = tally_counts();
|
||||
if (i != (unsigned)atoi(argv[1]))
|
||||
barf("Total counts %i not %s", i, argv[1]);
|
||||
printf("Success!\n");
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,812 +0,0 @@
|
||||
/*
|
||||
Xen Store Daemon Test tool
|
||||
Copyright (C) 2005 Rusty Russell IBM Corporation
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
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>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fnmatch.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/time.h>
|
||||
#include "utils.h"
|
||||
#include "xs_lib.h"
|
||||
#include "xs.h"
|
||||
#include "list.h"
|
||||
|
||||
#define XSTEST
|
||||
|
||||
static struct xs_handle *handles[10] = { NULL };
|
||||
static xs_transaction_t txh[10] = { XBT_NULL };
|
||||
|
||||
static unsigned int timeout_ms = 500;
|
||||
static bool timeout_suppressed = true;
|
||||
static bool readonly = false;
|
||||
static bool print_input = false;
|
||||
static unsigned int linenum = 0;
|
||||
|
||||
static int daemon_pid;
|
||||
static struct xenstore_domain_interface *interface;
|
||||
|
||||
/* FIXME: Mark connection as broken (close it?) when this happens. */
|
||||
static bool check_indexes(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod)
|
||||
{
|
||||
return ((prod - cons) <= XENSTORE_RING_SIZE);
|
||||
}
|
||||
|
||||
static void *get_output_chunk(XENSTORE_RING_IDX cons,
|
||||
XENSTORE_RING_IDX prod,
|
||||
char *buf, uint32_t *len)
|
||||
{
|
||||
*len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod);
|
||||
if ((XENSTORE_RING_SIZE - (prod - cons)) < *len)
|
||||
*len = XENSTORE_RING_SIZE - (prod - cons);
|
||||
return buf + MASK_XENSTORE_IDX(prod);
|
||||
}
|
||||
|
||||
static const void *get_input_chunk(XENSTORE_RING_IDX cons,
|
||||
XENSTORE_RING_IDX prod,
|
||||
const char *buf, uint32_t *len)
|
||||
{
|
||||
*len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(cons);
|
||||
if ((prod - cons) < *len)
|
||||
*len = prod - cons;
|
||||
return buf + MASK_XENSTORE_IDX(cons);
|
||||
}
|
||||
|
||||
/* FIXME: We spin, and we're sloppy. */
|
||||
static bool read_all_shmem(int fd __attribute__((unused)),
|
||||
void *data, unsigned int len)
|
||||
{
|
||||
unsigned int avail;
|
||||
struct xenstore_domain_interface *intf = interface;
|
||||
XENSTORE_RING_IDX cons, prod;
|
||||
const void *src;
|
||||
|
||||
while (len) {
|
||||
cons = intf->rsp_cons;
|
||||
prod = intf->rsp_prod;
|
||||
if (!check_indexes(cons, prod))
|
||||
barf("Corrupt buffer");
|
||||
|
||||
src = get_input_chunk(cons, prod, intf->rsp, &avail);
|
||||
if (avail > len)
|
||||
avail = len;
|
||||
memcpy(data, src, avail);
|
||||
data += avail;
|
||||
len -= avail;
|
||||
intf->rsp_cons += avail;
|
||||
}
|
||||
|
||||
/* Tell other end we read something. */
|
||||
kill(daemon_pid, SIGUSR2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool write_all_shmem(int fd __attribute__((unused)),
|
||||
const void *data, unsigned int len)
|
||||
{
|
||||
uint32_t avail;
|
||||
struct xenstore_domain_interface *intf = interface;
|
||||
XENSTORE_RING_IDX cons, prod;
|
||||
void *dst;
|
||||
|
||||
while (len) {
|
||||
cons = intf->req_cons;
|
||||
prod = intf->req_prod;
|
||||
if (!check_indexes(cons, prod))
|
||||
barf("Corrupt buffer");
|
||||
|
||||
dst = get_output_chunk(cons, prod, intf->req, &avail);
|
||||
if (avail > len)
|
||||
avail = len;
|
||||
memcpy(dst, data, avail);
|
||||
data += avail;
|
||||
len -= avail;
|
||||
intf->req_prod += avail;
|
||||
}
|
||||
|
||||
/* Tell other end we wrote something. */
|
||||
kill(daemon_pid, SIGUSR2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_all(int fd, void *data, unsigned int len);
|
||||
static bool read_all_choice(int fd, void *data, unsigned int len)
|
||||
{
|
||||
if (fd == -2)
|
||||
return read_all_shmem(fd, data, len);
|
||||
return read_all(fd, data, len);
|
||||
}
|
||||
|
||||
static bool write_all_choice(int fd, const void *data, unsigned int len)
|
||||
{
|
||||
if (fd == -2)
|
||||
return write_all_shmem(fd, data, len);
|
||||
return xs_write_all(fd, data, len);
|
||||
}
|
||||
|
||||
/* We want access to internal functions. */
|
||||
#include "xs.c"
|
||||
|
||||
static void __attribute__((noreturn)) usage(void)
|
||||
{
|
||||
barf("Usage:\n"
|
||||
" xs_test [--readonly] [--no-timeout] [-x]\n"
|
||||
"Reads commands from stdin, one per line:"
|
||||
" dir <path>\n"
|
||||
" read <path>\n"
|
||||
" write <path> <value>...\n"
|
||||
" setid <id>\n"
|
||||
" mkdir <path>\n"
|
||||
" rm <path>\n"
|
||||
" getperm <path>\n"
|
||||
" setperm <path> <id> <flags> ...\n"
|
||||
" watch <path> <token>\n"
|
||||
" watchnoack <path> <token>\n"
|
||||
" waitwatch\n"
|
||||
" unwatch <path> <token>\n"
|
||||
" close\n"
|
||||
" start <node>\n"
|
||||
" abort\n"
|
||||
" introduce <domid> <mfn> <eventchn> <path>\n"
|
||||
" commit\n"
|
||||
" sleep <milliseconds>\n"
|
||||
" expect <pattern>\n"
|
||||
" notimeout\n"
|
||||
" readonly\n"
|
||||
" readwrite\n"
|
||||
" dump\n");
|
||||
}
|
||||
|
||||
static int argpos(const char *line, unsigned int num)
|
||||
{
|
||||
unsigned int i, len = 0, off = 0;
|
||||
|
||||
for (i = 0; i <= num; i++) {
|
||||
off += len;
|
||||
off += strspn(line + off, " \t\n");
|
||||
len = strcspn(line + off, " \t\n");
|
||||
if (!len)
|
||||
return off;
|
||||
}
|
||||
return off;
|
||||
}
|
||||
|
||||
static char *arg(const char *line, unsigned int num)
|
||||
{
|
||||
static char *args[10];
|
||||
unsigned int off, len;
|
||||
|
||||
off = argpos(line, num);
|
||||
len = strcspn(line + off, " \t\n");
|
||||
|
||||
if (!len)
|
||||
barf("Can't get arg %u", num);
|
||||
|
||||
free(args[num]);
|
||||
args[num] = malloc(len + 1);
|
||||
memcpy(args[num], line+off, len);
|
||||
args[num][len] = '\0';
|
||||
return args[num];
|
||||
}
|
||||
|
||||
struct expect
|
||||
{
|
||||
struct list_head list;
|
||||
char *pattern;
|
||||
};
|
||||
static LIST_HEAD(expects);
|
||||
|
||||
static char *command;
|
||||
|
||||
/* 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)
|
||||
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)
|
||||
{
|
||||
char **entries;
|
||||
unsigned int i, num;
|
||||
|
||||
entries = xs_directory(handles[handle], txh[handle], path, &num);
|
||||
if (!entries) {
|
||||
failed(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
if (handle)
|
||||
output("%i:%s\n", handle, entries[i]);
|
||||
else
|
||||
output("%s\n", entries[i]);
|
||||
free(entries);
|
||||
}
|
||||
|
||||
static void do_read(unsigned int handle, char *path)
|
||||
{
|
||||
char *value;
|
||||
unsigned int len;
|
||||
|
||||
value = xs_read(handles[handle], txh[handle], path, &len);
|
||||
if (!value) {
|
||||
failed(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
/* It's supposed to nul terminate for us. */
|
||||
assert(value[len] == '\0');
|
||||
if (handle)
|
||||
output("%i:%.*s\n", handle, len, value);
|
||||
else
|
||||
output("%.*s\n", len, value);
|
||||
}
|
||||
|
||||
static void do_write(unsigned int handle, char *path, char *data)
|
||||
{
|
||||
if (!xs_write(handles[handle], txh[handle], path, data, strlen(data)))
|
||||
failed(handle);
|
||||
}
|
||||
|
||||
static void do_setid(unsigned int handle, char *id)
|
||||
{
|
||||
if (!xs_bool(xs_debug_command(handles[handle], "setid", id,
|
||||
strlen(id)+1)))
|
||||
failed(handle);
|
||||
}
|
||||
|
||||
static void do_mkdir(unsigned int handle, char *path)
|
||||
{
|
||||
if (!xs_mkdir(handles[handle], txh[handle], path))
|
||||
failed(handle);
|
||||
}
|
||||
|
||||
static void do_rm(unsigned int handle, char *path)
|
||||
{
|
||||
if (!xs_rm(handles[handle], txh[handle], path))
|
||||
failed(handle);
|
||||
}
|
||||
|
||||
static void do_getperm(unsigned int handle, char *path)
|
||||
{
|
||||
unsigned int i, num;
|
||||
struct xs_permissions *perms;
|
||||
|
||||
perms = xs_get_permissions(handles[handle], txh[handle], path, &num);
|
||||
if (!perms) {
|
||||
failed(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
char *permstring;
|
||||
|
||||
switch (perms[i].perms) {
|
||||
case XS_PERM_NONE:
|
||||
permstring = "NONE";
|
||||
break;
|
||||
case XS_PERM_WRITE:
|
||||
permstring = "WRITE";
|
||||
break;
|
||||
case XS_PERM_READ:
|
||||
permstring = "READ";
|
||||
break;
|
||||
case XS_PERM_READ|XS_PERM_WRITE:
|
||||
permstring = "READ/WRITE";
|
||||
break;
|
||||
default:
|
||||
barf("bad perm value %i", perms[i].perms);
|
||||
}
|
||||
|
||||
if (handle)
|
||||
output("%i:%i %s\n", handle, perms[i].id, permstring);
|
||||
else
|
||||
output("%i %s\n", perms[i].id, permstring);
|
||||
}
|
||||
free(perms);
|
||||
}
|
||||
|
||||
static void do_setperm(unsigned int handle, char *path, char *line)
|
||||
{
|
||||
unsigned int i;
|
||||
struct xs_permissions perms[100];
|
||||
|
||||
strtok(line, " \t\n");
|
||||
strtok(NULL, " \t\n");
|
||||
for (i = 0; ; i++) {
|
||||
char *arg = strtok(NULL, " \t\n");
|
||||
if (!arg)
|
||||
break;
|
||||
perms[i].id = atoi(arg);
|
||||
arg = strtok(NULL, " \t\n");
|
||||
if (!arg)
|
||||
break;
|
||||
if (streq(arg, "WRITE"))
|
||||
perms[i].perms = XS_PERM_WRITE;
|
||||
else if (streq(arg, "READ"))
|
||||
perms[i].perms = XS_PERM_READ;
|
||||
else if (streq(arg, "READ/WRITE"))
|
||||
perms[i].perms = XS_PERM_READ|XS_PERM_WRITE;
|
||||
else if (streq(arg, "NONE"))
|
||||
perms[i].perms = XS_PERM_NONE;
|
||||
else
|
||||
barf("bad flags %s\n", arg);
|
||||
}
|
||||
|
||||
if (!xs_set_permissions(handles[handle], txh[handle], path, perms, i))
|
||||
failed(handle);
|
||||
}
|
||||
|
||||
static void do_watch(unsigned int handle, const char *node, const char *token,
|
||||
bool swallow_event)
|
||||
{
|
||||
if (!xs_watch(handles[handle], node, token))
|
||||
failed(handle);
|
||||
|
||||
/* Convenient for testing... */
|
||||
if (swallow_event) {
|
||||
unsigned int num;
|
||||
char **vec = xs_read_watch(handles[handle], &num);
|
||||
if (!vec ||
|
||||
!streq(vec[XS_WATCH_PATH], node) ||
|
||||
!streq(vec[XS_WATCH_TOKEN], token))
|
||||
failed(handle);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_timeout(void)
|
||||
{
|
||||
struct itimerval timeout;
|
||||
|
||||
timeout.it_value.tv_sec = timeout_ms / 1000;
|
||||
timeout.it_value.tv_usec = (timeout_ms * 1000) % 1000000;
|
||||
timeout.it_interval.tv_sec = timeout.it_interval.tv_usec = 0;
|
||||
setitimer(ITIMER_REAL, &timeout, NULL);
|
||||
}
|
||||
|
||||
static void disarm_timeout(void)
|
||||
{
|
||||
struct itimerval timeout;
|
||||
|
||||
timeout.it_value.tv_sec = 0;
|
||||
timeout.it_value.tv_usec = 0;
|
||||
setitimer(ITIMER_REAL, &timeout, NULL);
|
||||
}
|
||||
|
||||
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;
|
||||
unsigned int num;
|
||||
|
||||
if (xs_fileno(handles[handle]) != -2) {
|
||||
/* Manually select here so we can time out gracefully. */
|
||||
FD_ZERO(&set);
|
||||
FD_SET(xs_fileno(handles[handle]), &set);
|
||||
disarm_timeout();
|
||||
if (select(xs_fileno(handles[handle])+1, &set,
|
||||
NULL, NULL, &tv) == 0) {
|
||||
errno = ETIMEDOUT;
|
||||
failed(handle);
|
||||
return;
|
||||
}
|
||||
set_timeout();
|
||||
}
|
||||
|
||||
vec = xs_read_watch(handles[handle], &num);
|
||||
if (!vec) {
|
||||
failed(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
if (handle)
|
||||
output("%i:%s:%s\n", handle,
|
||||
vec[XS_WATCH_PATH], vec[XS_WATCH_TOKEN]);
|
||||
else
|
||||
output("%s:%s\n", vec[XS_WATCH_PATH], vec[XS_WATCH_TOKEN]);
|
||||
free(vec);
|
||||
}
|
||||
|
||||
static void do_unwatch(unsigned int handle, const char *node, const char *token)
|
||||
{
|
||||
if (!xs_unwatch(handles[handle], node, token))
|
||||
failed(handle);
|
||||
}
|
||||
|
||||
static void do_start(unsigned int handle)
|
||||
{
|
||||
txh[handle] = xs_transaction_start(handles[handle]);
|
||||
if (txh[handle] == XBT_NULL)
|
||||
failed(handle);
|
||||
}
|
||||
|
||||
static void do_end(unsigned int handle, bool abort)
|
||||
{
|
||||
if (!xs_transaction_end(handles[handle], txh[handle], abort))
|
||||
failed(handle);
|
||||
txh[handle] = XBT_NULL;
|
||||
}
|
||||
|
||||
static void do_introduce(unsigned int handle,
|
||||
const char *domid,
|
||||
const char *mfn,
|
||||
const char *eventchn,
|
||||
const char *path)
|
||||
{
|
||||
unsigned int i;
|
||||
int fd;
|
||||
|
||||
/* This mechanism is v. slow w. valgrind running. */
|
||||
timeout_ms = 5000;
|
||||
|
||||
/* We poll, so ignore signal */
|
||||
signal(SIGUSR2, SIG_IGN);
|
||||
for (i = 0; i < ARRAY_SIZE(handles); i++)
|
||||
if (!handles[i])
|
||||
break;
|
||||
|
||||
fd = open("/tmp/xcmap", O_RDWR);
|
||||
/* Set shared comms page. */
|
||||
interface = mmap(NULL, getpagesize(), PROT_WRITE|PROT_READ,
|
||||
MAP_SHARED,fd,0);
|
||||
if (interface == MAP_FAILED)
|
||||
barf_perror("Failed to map /tmp/xcmap page");
|
||||
close(fd);
|
||||
|
||||
/* Tell them the event channel and our PID. */
|
||||
*(int *)((void *)interface + 32) = getpid();
|
||||
*(uint16_t *)((void *)interface + 36) = atoi(eventchn);
|
||||
|
||||
if (!xs_introduce_domain(handles[handle], atoi(domid),
|
||||
atol(mfn), atoi(eventchn))) {
|
||||
failed(handle);
|
||||
munmap(interface, getpagesize());
|
||||
return;
|
||||
}
|
||||
output("handle is %i\n", i);
|
||||
|
||||
/* Create new handle. */
|
||||
handles[i] = new(struct xs_handle);
|
||||
handles[i]->fd = -2;
|
||||
|
||||
/* Read in daemon pid. */
|
||||
daemon_pid = *(int *)((void *)interface + 32);
|
||||
}
|
||||
|
||||
static void do_release(unsigned int handle, const char *domid)
|
||||
{
|
||||
if (!xs_release_domain(handles[handle], atoi(domid)))
|
||||
failed(handle);
|
||||
}
|
||||
|
||||
static int strptrcmp(const void *a, const void *b)
|
||||
{
|
||||
return strcmp(*(char **)a, *(char **)b);
|
||||
}
|
||||
|
||||
static void sort_dir(char **dir, unsigned int num)
|
||||
{
|
||||
qsort(dir, num, sizeof(char *), strptrcmp);
|
||||
}
|
||||
|
||||
static void dump_dir(unsigned int handle,
|
||||
const char *node,
|
||||
char **dir,
|
||||
unsigned int numdirs,
|
||||
unsigned int depth)
|
||||
{
|
||||
unsigned int i;
|
||||
char spacing[depth+1];
|
||||
|
||||
memset(spacing, ' ', depth);
|
||||
spacing[depth] = '\0';
|
||||
|
||||
sort_dir(dir, numdirs);
|
||||
|
||||
for (i = 0; i < numdirs; i++) {
|
||||
struct xs_permissions *perms;
|
||||
unsigned int j, numperms;
|
||||
unsigned int len;
|
||||
char *contents;
|
||||
unsigned int subnum;
|
||||
char **subdirs;
|
||||
char subnode[strlen(node) + 1 + strlen(dir[i]) + 1];
|
||||
|
||||
sprintf(subnode, "%s/%s", node, dir[i]);
|
||||
|
||||
perms = xs_get_permissions(handles[handle], txh[handle],
|
||||
subnode,&numperms);
|
||||
if (!perms) {
|
||||
failed(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
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");
|
||||
output("%s ", buffer);
|
||||
}
|
||||
free(perms);
|
||||
output("\n");
|
||||
|
||||
/* Even directories can have contents. */
|
||||
contents = xs_read(handles[handle], txh[handle],
|
||||
subnode, &len);
|
||||
if (!contents) {
|
||||
if (errno != EISDIR)
|
||||
failed(handle);
|
||||
} else {
|
||||
output(" %s(%.*s)\n", spacing, len, contents);
|
||||
free(contents);
|
||||
}
|
||||
|
||||
/* Every node is a directory. */
|
||||
subdirs = xs_directory(handles[handle], txh[handle],
|
||||
subnode, &subnum);
|
||||
if (!subdirs) {
|
||||
failed(handle);
|
||||
return;
|
||||
}
|
||||
dump_dir(handle, subnode, subdirs, subnum, depth+1);
|
||||
free(subdirs);
|
||||
}
|
||||
}
|
||||
|
||||
static void dump(int handle)
|
||||
{
|
||||
char **subdirs;
|
||||
unsigned int subnum;
|
||||
|
||||
subdirs = xs_directory(handles[handle], txh[handle], "/", &subnum);
|
||||
if (!subdirs) {
|
||||
failed(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
dump_dir(handle, "", subdirs, subnum, 0);
|
||||
free(subdirs);
|
||||
}
|
||||
|
||||
static int handle;
|
||||
|
||||
static void alarmed(int sig __attribute__((unused)))
|
||||
{
|
||||
if (handle) {
|
||||
char handlename[10];
|
||||
sprintf(handlename, "%u:", handle);
|
||||
write(STDOUT_FILENO, handlename, strlen(handlename));
|
||||
}
|
||||
write(STDOUT_FILENO, command, strlen(command));
|
||||
write(STDOUT_FILENO, " timeout\n", strlen(" timeout\n"));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
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, "#"))
|
||||
return;
|
||||
|
||||
handle = strtoul(line, &endp, 10);
|
||||
if (endp != line)
|
||||
memmove(line, endp+1, strlen(endp));
|
||||
else
|
||||
handle = default_handle;
|
||||
|
||||
command = arg(line, 0);
|
||||
if (!handles[handle]) {
|
||||
if (readonly)
|
||||
handles[handle] = xs_daemon_open_readonly();
|
||||
else
|
||||
handles[handle] = xs_daemon_open();
|
||||
if (!handles[handle])
|
||||
barf_perror("Opening connection to daemon");
|
||||
}
|
||||
|
||||
if (!timeout_suppressed)
|
||||
set_timeout();
|
||||
timeout_suppressed = false;
|
||||
|
||||
if (streq(command, "dir"))
|
||||
do_dir(handle, arg(line, 1));
|
||||
else if (streq(command, "read"))
|
||||
do_read(handle, arg(line, 1));
|
||||
else if (streq(command, "write"))
|
||||
do_write(handle, arg(line, 1), arg(line, 2));
|
||||
else if (streq(command, "setid"))
|
||||
do_setid(handle, arg(line, 1));
|
||||
else if (streq(command, "mkdir"))
|
||||
do_mkdir(handle, arg(line, 1));
|
||||
else if (streq(command, "rm"))
|
||||
do_rm(handle, arg(line, 1));
|
||||
else if (streq(command, "getperm"))
|
||||
do_getperm(handle, arg(line, 1));
|
||||
else if (streq(command, "setperm"))
|
||||
do_setperm(handle, arg(line, 1), line);
|
||||
else if (streq(command, "watch"))
|
||||
do_watch(handle, arg(line, 1), arg(line, 2), true);
|
||||
else if (streq(command, "watchnoack"))
|
||||
do_watch(handle, arg(line, 1), arg(line, 2), false);
|
||||
else if (streq(command, "waitwatch"))
|
||||
do_waitwatch(handle);
|
||||
else if (streq(command, "unwatch"))
|
||||
do_unwatch(handle, arg(line, 1), arg(line, 2));
|
||||
else if (streq(command, "close")) {
|
||||
xs_daemon_close(handles[handle]);
|
||||
handles[handle] = NULL;
|
||||
txh[handle] = XBT_NULL;
|
||||
} else if (streq(command, "start"))
|
||||
do_start(handle);
|
||||
else if (streq(command, "commit"))
|
||||
do_end(handle, false);
|
||||
else if (streq(command, "abort"))
|
||||
do_end(handle, true);
|
||||
else if (streq(command, "introduce"))
|
||||
do_introduce(handle, arg(line, 1), arg(line, 2),
|
||||
arg(line, 3), arg(line, 4));
|
||||
else if (streq(command, "release"))
|
||||
do_release(handle, arg(line, 1));
|
||||
else if (streq(command, "dump"))
|
||||
dump(handle);
|
||||
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);
|
||||
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];
|
||||
|
||||
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 (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();
|
||||
|
||||
|
||||
signal(SIGALRM, alarmed);
|
||||
while (fgets(line, sizeof(line), stdin))
|
||||
do_command(0, line);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* indent-tabs-mode: t
|
||||
* c-indent-level: 8
|
||||
* c-basic-offset: 8
|
||||
* tab-width: 8
|
||||
* End:
|
||||
*/
|
||||