aboutsummaryrefslogtreecommitdiffstats
path: root/tools/xenstore/xs_test.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/xenstore/xs_test.c')
-rw-r--r--tools/xenstore/xs_test.c647
1 files changed, 647 insertions, 0 deletions
diff --git a/tools/xenstore/xs_test.c b/tools/xenstore/xs_test.c
new file mode 100644
index 0000000000..f1e66cbe28
--- /dev/null
+++ b/tools/xenstore/xs_test.c
@@ -0,0 +1,647 @@
+/*
+ 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
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.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 "utils.h"
+#include "xs_lib.h"
+
+#define XSTEST
+
+static struct xs_handle *handles[10] = { NULL };
+
+struct ringbuf_head
+{
+ uint32_t write; /* Next place to write to */
+ uint32_t read; /* Next place to read from */
+ uint8_t flags;
+ char buf[0];
+} __attribute__((packed));
+
+static struct ringbuf_head *out, *in;
+static unsigned int ringbuf_datasize;
+static int daemon_pid;
+
+/* FIXME: Mark connection as broken (close it?) when this happens. */
+static bool check_buffer(const struct ringbuf_head *h)
+{
+ return (h->write < ringbuf_datasize && h->read < ringbuf_datasize);
+}
+
+/* We can't fill last byte: would look like empty buffer. */
+static void *get_output_chunk(const struct ringbuf_head *h,
+ void *buf, uint32_t *len)
+{
+ uint32_t read_mark;
+
+ if (h->read == 0)
+ read_mark = ringbuf_datasize - 1;
+ else
+ read_mark = h->read - 1;
+
+ /* Here to the end of buffer, unless they haven't read some out. */
+ *len = ringbuf_datasize - h->write;
+ if (read_mark >= h->write)
+ *len = read_mark - h->write;
+ return buf + h->write;
+}
+
+static const void *get_input_chunk(const struct ringbuf_head *h,
+ const void *buf, uint32_t *len)
+{
+ /* Here to the end of buffer, unless they haven't written some. */
+ *len = ringbuf_datasize - h->read;
+ if (h->write >= h->read)
+ *len = h->write - h->read;
+ return buf + h->read;
+}
+
+static void update_output_chunk(struct ringbuf_head *h, uint32_t len)
+{
+ h->write += len;
+ if (h->write == ringbuf_datasize)
+ h->write = 0;
+}
+
+static void update_input_chunk(struct ringbuf_head *h, uint32_t len)
+{
+ h->read += len;
+ if (h->read == ringbuf_datasize)
+ h->read = 0;
+}
+
+/* FIXME: We spin, and we're sloppy. */
+static bool read_all_shmem(int fd __attribute__((unused)),
+ void *data, unsigned int len)
+{
+ unsigned int avail;
+
+ if (!check_buffer(in))
+ barf("Corrupt buffer");
+
+ while (len) {
+ const void *src = get_input_chunk(in, in->buf, &avail);
+ if (avail > len)
+ avail = len;
+ memcpy(data, src, avail);
+ data += avail;
+ len -= avail;
+ update_input_chunk(in, 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;
+
+ if (!check_buffer(out))
+ barf("Corrupt buffer");
+
+ while (len) {
+ void *dst = get_output_chunk(out, out->buf, &avail);
+ if (avail > len)
+ avail = len;
+ memcpy(dst, data, avail);
+ data += avail;
+ len -= avail;
+ update_output_chunk(out, 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 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] [--notimeout]\n"
+ "Reads commands from stdin, one per line:"
+ " dir <path>\n"
+ " read <path>\n"
+ " write <path> <flags> <value>...\n"
+ " setid <id>\n"
+ " mkdir <path>\n"
+ " rm <path>\n"
+ " getperm <path>\n"
+ " setperm <path> <id> <flags> ...\n"
+ " shutdown\n"
+ " watch <path> <prio>\n"
+ " waitwatch\n"
+ " ackwatch\n"
+ " unwatch <path>\n"
+ " close\n"
+ " start <node>\n"
+ " abort\n"
+ " introduce <domid> <mfn> <eventchn>\n"
+ " commit\n"
+ " sleep <seconds>\n"
+ " dump\n");
+}
+
+static char *arg(char *line, unsigned int num)
+{
+ static char *args[10];
+ unsigned int i, len = 0;
+
+ for (i = 0; i <= num; i++) {
+ line += len;
+ line += strspn(line, " \t\n");
+ len = strcspn(line, " \t\n");
+ if (!len)
+ barf("Can't get arg %u", num);
+ }
+
+ free(args[num]);
+ args[num] = malloc(len + 1);
+ memcpy(args[num], line, len);
+ args[num][len] = '\0';
+ return args[num];
+}
+
+static char *command;
+static void __attribute__((noreturn)) failed(int handle)
+{
+ if (handle)
+ barf_perror("%i: %s", handle, command);
+ barf_perror("%s", command);
+}
+
+static void do_dir(unsigned int handle, char *path)
+{
+ char **entries;
+ unsigned int i, num;
+
+ entries = xs_directory(handles[handle], path, &num);
+ if (!entries)
+ failed(handle);
+
+ for (i = 0; i < num; i++)
+ if (handle)
+ printf("%i:%s\n", handle, entries[i]);
+ else
+ printf("%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], path, &len);
+ if (!value)
+ failed(handle);
+
+ if (handle)
+ printf("%i:%.*s\n", handle, len, value);
+ else
+ printf("%.*s\n", len, value);
+}
+
+static void do_write(unsigned int handle, char *path, char *flags, char *data)
+{
+ int f;
+
+ if (streq(flags, "none"))
+ f = 0;
+ else if (streq(flags, "create"))
+ f = O_CREAT;
+ else if (streq(flags, "excl"))
+ f = O_CREAT | O_EXCL;
+ else if (streq(flags, "crap"))
+ f = 100;
+ else
+ barf("write flags 'none', 'create' or 'excl' only");
+
+ if (!xs_write(handles[handle], path, data, strlen(data)+1, f))
+ 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], path))
+ failed(handle);
+}
+
+static void do_rm(unsigned int handle, char *path)
+{
+ if (!xs_rm(handles[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], path, &num);
+ if (!perms)
+ failed(handle);
+
+ 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)
+ printf("%i:%i %s\n", handle, perms[i].id, permstring);
+ else
+ printf("%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], path, perms, i))
+ failed(handle);
+}
+
+static void do_shutdown(unsigned int handle)
+{
+ if (!xs_shutdown(handles[handle]))
+ failed(handle);
+}
+
+static void do_watch(unsigned int handle, const char *node, const char *pri)
+{
+ if (!xs_watch(handles[handle], node, atoi(pri)))
+ failed(handle);
+}
+
+static void do_waitwatch(unsigned int handle)
+{
+ char *node;
+
+ node = xs_read_watch(handles[handle]);
+ if (!node)
+ failed(handle);
+
+ if (handle)
+ printf("%i:%s\n", handle, node);
+ else
+ printf("%s\n", node);
+ free(node);
+}
+
+static void do_ackwatch(unsigned int handle)
+{
+ if (!xs_acknowledge_watch(handles[handle]))
+ failed(handle);
+}
+
+static void do_unwatch(unsigned int handle, const char *node)
+{
+ if (!xs_unwatch(handles[handle], node))
+ failed(handle);
+}
+
+static void do_start(unsigned int handle, const char *node)
+{
+ if (!xs_transaction_start(handles[handle], node))
+ failed(handle);
+}
+
+static void do_end(unsigned int handle, bool abort)
+{
+ if (!xs_transaction_end(handles[handle], abort))
+ failed(handle);
+}
+
+static void do_introduce(unsigned int handle,
+ const char *domid,
+ const char *mfn,
+ const char *eventchn,
+ const char *path)
+{
+ unsigned int i;
+ int fd;
+
+ /* 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 in and out pointers. */
+ out = mmap(NULL, getpagesize(), PROT_WRITE|PROT_READ, MAP_SHARED,fd,0);
+ if (out == MAP_FAILED)
+ barf_perror("Failed to map /tmp/xcmap page");
+ in = (void *)out + getpagesize() / 2;
+ close(fd);
+
+ /* Tell them the event channel and our PID. */
+ *(int *)((void *)out + 32) = getpid();
+ *(u16 *)((void *)out + 36) = atoi(eventchn);
+
+ /* 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);
+}
+
+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], subnode,&numperms);
+ if (!perms)
+ failed(handle);
+
+ printf("%s%s: ", spacing, dir[i]);
+ for (j = 0; j < numperms; j++) {
+ char buffer[100];
+ if (!perm_to_string(&perms[j], buffer))
+ barf("perm to string");
+ printf("%s ", buffer);
+ }
+ free(perms);
+ printf("\n");
+
+ /* Even directories can have contents. */
+ contents = xs_read(handles[handle], subnode, &len);
+ if (!contents) {
+ if (errno != EISDIR)
+ failed(handle);
+ } else {
+ printf(" %s(%.*s)\n", spacing, len, contents);
+ free(contents);
+ }
+
+ /* Every node is a directory. */
+ subdirs = xs_directory(handles[handle], subnode, &subnum);
+ if (!subdirs)
+ failed(handle);
+ 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], "/", &subnum);
+ if (!subdirs)
+ failed(handle);
+
+ dump_dir(handle, "", subdirs, subnum, 0);
+ free(subdirs);
+}
+
+int main(int argc, char *argv[])
+{
+ char line[1024];
+ bool readonly = false, timeout = true;
+ 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);
+ }
+
+ if (argc > 1 && streq(argv[1], "--readonly")) {
+ readonly = true;
+ argc--;
+ argv++;
+ }
+
+ if (argc > 1 && streq(argv[1], "--notimeout")) {
+ timeout = false;
+ argc--;
+ argv++;
+ }
+
+ if (argc != 1)
+ usage();
+
+ /* The size of the ringbuffer: half a page minus head structure. */
+ ringbuf_datasize = getpagesize() / 2 - sizeof(struct ringbuf_head);
+
+ signal(SIGALRM, alarmed);
+ while (fgets(line, sizeof(line), stdin)) {
+ char *endp;
+
+ if (strspn(line, " \n") == strlen(line))
+ continue;
+ if (strstarts(line, "#"))
+ continue;
+
+ handle = strtoul(line, &endp, 10);
+ if (endp != line)
+ memmove(line, endp+1, strlen(endp));
+ else
+ handle = 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");
+ }
+ command = arg(line, 0);
+
+ if (timeout)
+ alarm(5);
+ 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), arg(line, 3));
+ 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, "shutdown"))
+ do_shutdown(handle);
+ else if (streq(command, "watch"))
+ do_watch(handle, arg(line, 1), arg(line, 2));
+ else if (streq(command, "waitwatch"))
+ do_waitwatch(handle);
+ else if (streq(command, "ackwatch"))
+ do_ackwatch(handle);
+ else if (streq(command, "unwatch"))
+ do_unwatch(handle, arg(line, 1));
+ else if (streq(command, "close")) {
+ xs_daemon_close(handles[handle]);
+ handles[handle] = NULL;
+ } else if (streq(command, "start"))
+ do_start(handle, arg(line, 1));
+ 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"))
+ sleep(atoi(arg(line, 1)));
+ else
+ barf("Unknown command %s", command);
+ fflush(stdout);
+ alarm(0);
+ }
+ return 0;
+}