source: trunk/packages/xen-3.1/xen-3.1/tools/xenstore/xs_test.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 18.3 KB
Line 
1/*
2    Xen Store Daemon Test tool
3    Copyright (C) 2005 Rusty Russell IBM Corporation
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18*/
19
20#define _GNU_SOURCE
21#include <stdio.h>
22#include <stdlib.h>
23#include <sys/types.h>
24#include <sys/wait.h>
25#include <sys/stat.h>
26#include <fcntl.h>
27#include <signal.h>
28#include <stdint.h>
29#include <stdbool.h>
30#include <stdlib.h>
31#include <sys/mman.h>
32#include <fnmatch.h>
33#include <stdarg.h>
34#include <string.h>
35#include <getopt.h>
36#include <ctype.h>
37#include <sys/time.h>
38#include "utils.h"
39#include "xs_lib.h"
40#include "xs.h"
41#include "list.h"
42
43#define XSTEST
44
45static struct xs_handle *handles[10] = { NULL };
46static xs_transaction_t txh[10] = { XBT_NULL };
47
48static unsigned int timeout_ms = 500;
49static bool timeout_suppressed = true;
50static bool readonly = false;
51static bool print_input = false;
52static unsigned int linenum = 0;
53
54static int daemon_pid;
55static struct xenstore_domain_interface *interface;
56
57/* FIXME: Mark connection as broken (close it?) when this happens. */
58static bool check_indexes(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod)
59{
60        return ((prod - cons) <= XENSTORE_RING_SIZE);
61}
62
63static void *get_output_chunk(XENSTORE_RING_IDX cons,
64                              XENSTORE_RING_IDX prod,
65                              char *buf, uint32_t *len)
66{
67        *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod);
68        if ((XENSTORE_RING_SIZE - (prod - cons)) < *len)
69                *len = XENSTORE_RING_SIZE - (prod - cons);
70        return buf + MASK_XENSTORE_IDX(prod);
71}
72
73static const void *get_input_chunk(XENSTORE_RING_IDX cons,
74                                   XENSTORE_RING_IDX prod,
75                                   const char *buf, uint32_t *len)
76{
77        *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(cons);
78        if ((prod - cons) < *len)
79                *len = prod - cons;
80        return buf + MASK_XENSTORE_IDX(cons);
81}
82
83/* FIXME: We spin, and we're sloppy. */
84static bool read_all_shmem(int fd __attribute__((unused)),
85                           void *data, unsigned int len)
86{
87        unsigned int avail;
88        struct xenstore_domain_interface *intf = interface;
89        XENSTORE_RING_IDX cons, prod;
90        const void *src;
91
92        while (len) {
93                cons = intf->rsp_cons;
94                prod = intf->rsp_prod;
95                if (!check_indexes(cons, prod))
96                        barf("Corrupt buffer");
97
98                src = get_input_chunk(cons, prod, intf->rsp, &avail);
99                if (avail > len)
100                        avail = len;
101                memcpy(data, src, avail);
102                data += avail;
103                len -= avail;
104                intf->rsp_cons += avail;
105        }
106
107        /* Tell other end we read something. */
108        kill(daemon_pid, SIGUSR2);
109
110        return true;
111}
112
113static bool write_all_shmem(int fd __attribute__((unused)),
114                            const void *data, unsigned int len)
115{
116        uint32_t avail;
117        struct xenstore_domain_interface *intf = interface;
118        XENSTORE_RING_IDX cons, prod;
119        void *dst;
120
121        while (len) {
122                cons = intf->req_cons;
123                prod = intf->req_prod;
124                if (!check_indexes(cons, prod))
125                        barf("Corrupt buffer");
126
127                dst = get_output_chunk(cons, prod, intf->req, &avail);
128                if (avail > len)
129                        avail = len;
130                memcpy(dst, data, avail);
131                data += avail;
132                len -= avail;
133                intf->req_prod += avail;
134        }
135
136        /* Tell other end we wrote something. */
137        kill(daemon_pid, SIGUSR2);
138
139        return true;
140}
141
142static bool read_all(int fd, void *data, unsigned int len);
143static bool read_all_choice(int fd, void *data, unsigned int len)
144{
145        if (fd == -2)
146                return read_all_shmem(fd, data, len);
147        return read_all(fd, data, len);
148}
149
150static bool write_all_choice(int fd, const void *data, unsigned int len)
151{
152        if (fd == -2)
153                return write_all_shmem(fd, data, len);
154        return xs_write_all(fd, data, len);
155}
156
157/* We want access to internal functions. */
158#include "xs.c"
159
160static void __attribute__((noreturn)) usage(void)
161{
162        barf("Usage:\n"
163             "       xs_test [--readonly] [--no-timeout] [-x]\n"
164             "Reads commands from stdin, one per line:"
165             "  dir <path>\n"
166             "  read <path>\n"
167             "  write <path> <value>...\n"
168             "  setid <id>\n"
169             "  mkdir <path>\n"
170             "  rm <path>\n"
171             "  getperm <path>\n"
172             "  setperm <path> <id> <flags> ...\n"
173             "  watch <path> <token>\n"
174             "  watchnoack <path> <token>\n"
175             "  waitwatch\n"
176             "  unwatch <path> <token>\n"
177             "  close\n"
178             "  start <node>\n"
179             "  abort\n"
180             "  introduce <domid> <mfn> <eventchn> <path>\n"
181             "  commit\n"
182             "  sleep <milliseconds>\n"
183             "  expect <pattern>\n"
184             "  notimeout\n"
185             "  readonly\n"
186             "  readwrite\n"
187             "  dump\n");
188}
189
190static int argpos(const char *line, unsigned int num)
191{
192        unsigned int i, len = 0, off = 0;
193
194        for (i = 0; i <= num; i++) {
195                off += len;
196                off += strspn(line + off, " \t\n");
197                len = strcspn(line + off, " \t\n");
198                if (!len)
199                        return off;
200        }
201        return off;
202}
203
204static char *arg(const char *line, unsigned int num)
205{
206        static char *args[10];
207        unsigned int off, len;
208
209        off = argpos(line, num);
210        len = strcspn(line + off, " \t\n");
211
212        if (!len)
213                barf("Can't get arg %u", num);
214
215        free(args[num]);
216        args[num] = malloc(len + 1);
217        memcpy(args[num], line+off, len);
218        args[num][len] = '\0';
219        return args[num];
220}
221
222struct expect
223{
224        struct list_head list;
225        char *pattern;
226};
227static LIST_HEAD(expects);
228
229static char *command;
230
231/* Trim leading and trailing whitespace */
232static void trim(char *str)
233{
234        while (isspace(str[0]))
235                memmove(str, str+1, strlen(str));
236
237        while (strlen(str) && isspace(str[strlen(str)-1]))
238                str[strlen(str)-1] = '\0';
239}
240
241static void output(const char *fmt, ...)
242{
243        char *str;
244        struct expect *i;
245        va_list arglist;
246
247        va_start(arglist, fmt);
248        vasprintf(&str, fmt, arglist);
249        va_end(arglist);
250
251        printf("%s", str);
252        fflush(stdout);
253        trim(str);
254        list_for_each_entry(i, &expects, list) {
255                if (fnmatch(i->pattern, str, 0) == 0) {
256                        list_del(&i->list);
257                        free(i);
258                        return;
259                }
260        }
261        barf("Unexpected output %s\n", str);
262}
263
264static void failed(int handle)
265{
266        if (handle)
267                output("%i: %s failed: %s\n",
268                       handle, command, strerror(errno));
269        else
270                output("%s failed: %s\n", command, strerror(errno));
271}
272
273static void expect(const char *line)
274{
275        struct expect *e = malloc(sizeof(*e));
276
277        e->pattern = strdup(line + argpos(line, 1));
278        trim(e->pattern);
279        list_add(&e->list, &expects);
280}
281
282static void do_dir(unsigned int handle, char *path)
283{
284        char **entries;
285        unsigned int i, num;
286
287        entries = xs_directory(handles[handle], txh[handle], path, &num);
288        if (!entries) {
289                failed(handle);
290                return;
291        }
292
293        for (i = 0; i < num; i++)
294                if (handle)
295                        output("%i:%s\n", handle, entries[i]);
296                else
297                        output("%s\n", entries[i]);
298        free(entries);
299}
300
301static void do_read(unsigned int handle, char *path)
302{
303        char *value;
304        unsigned int len;
305
306        value = xs_read(handles[handle], txh[handle], path, &len);
307        if (!value) {
308                failed(handle);
309                return;
310        }
311
312        /* It's supposed to nul terminate for us. */
313        assert(value[len] == '\0');
314        if (handle)
315                output("%i:%.*s\n", handle, len, value);
316        else
317                output("%.*s\n", len, value);
318}
319
320static void do_write(unsigned int handle, char *path, char *data)
321{
322        if (!xs_write(handles[handle], txh[handle], path, data, strlen(data)))
323                failed(handle);
324}
325
326static void do_setid(unsigned int handle, char *id)
327{
328        if (!xs_bool(xs_debug_command(handles[handle], "setid", id,
329                                      strlen(id)+1)))
330                failed(handle);
331}
332
333static void do_mkdir(unsigned int handle, char *path)
334{
335        if (!xs_mkdir(handles[handle], txh[handle], path))
336                failed(handle);
337}
338
339static void do_rm(unsigned int handle, char *path)
340{
341        if (!xs_rm(handles[handle], txh[handle], path))
342                failed(handle);
343}
344
345static void do_getperm(unsigned int handle, char *path)
346{
347        unsigned int i, num;
348        struct xs_permissions *perms;
349
350        perms = xs_get_permissions(handles[handle], txh[handle], path, &num);
351        if (!perms) {
352                failed(handle);
353                return;
354        }
355
356        for (i = 0; i < num; i++) {
357                char *permstring;
358
359                switch (perms[i].perms) {
360                case XS_PERM_NONE:
361                        permstring = "NONE";
362                        break;
363                case XS_PERM_WRITE:
364                        permstring = "WRITE";
365                        break;
366                case XS_PERM_READ:
367                        permstring = "READ";
368                        break;
369                case XS_PERM_READ|XS_PERM_WRITE:
370                        permstring = "READ/WRITE";
371                        break;
372                default:
373                        barf("bad perm value %i", perms[i].perms);
374                }
375
376                if (handle)
377                        output("%i:%i %s\n", handle, perms[i].id, permstring);
378                else
379                        output("%i %s\n", perms[i].id, permstring);
380        }
381        free(perms);
382}
383
384static void do_setperm(unsigned int handle, char *path, char *line)
385{
386        unsigned int i;
387        struct xs_permissions perms[100];
388
389        strtok(line, " \t\n");
390        strtok(NULL, " \t\n");
391        for (i = 0; ; i++) {
392                char *arg = strtok(NULL, " \t\n");
393                if (!arg)
394                        break;
395                perms[i].id = atoi(arg);
396                arg = strtok(NULL, " \t\n");
397                if (!arg)
398                        break;
399                if (streq(arg, "WRITE"))
400                        perms[i].perms = XS_PERM_WRITE;
401                else if (streq(arg, "READ"))
402                        perms[i].perms = XS_PERM_READ;
403                else if (streq(arg, "READ/WRITE"))
404                        perms[i].perms = XS_PERM_READ|XS_PERM_WRITE;
405                else if (streq(arg, "NONE"))
406                        perms[i].perms = XS_PERM_NONE;
407                else
408                        barf("bad flags %s\n", arg);
409        }
410
411        if (!xs_set_permissions(handles[handle], txh[handle], path, perms, i))
412                failed(handle);
413}
414
415static void do_watch(unsigned int handle, const char *node, const char *token,
416                     bool swallow_event)
417{
418        if (!xs_watch(handles[handle], node, token))
419                failed(handle);
420
421        /* Convenient for testing... */
422        if (swallow_event) {
423                unsigned int num;
424                char **vec = xs_read_watch(handles[handle], &num);
425                if (!vec ||
426                    !streq(vec[XS_WATCH_PATH], node) ||
427                    !streq(vec[XS_WATCH_TOKEN], token))
428                        failed(handle);
429        }
430}
431
432static void set_timeout(void)
433{
434        struct itimerval timeout;
435
436        timeout.it_value.tv_sec = timeout_ms / 1000;
437        timeout.it_value.tv_usec = (timeout_ms * 1000) % 1000000;
438        timeout.it_interval.tv_sec = timeout.it_interval.tv_usec = 0;
439        setitimer(ITIMER_REAL, &timeout, NULL);
440}
441
442static void disarm_timeout(void)
443{
444        struct itimerval timeout;
445
446        timeout.it_value.tv_sec = 0;
447        timeout.it_value.tv_usec = 0;
448        setitimer(ITIMER_REAL, &timeout, NULL);
449}
450
451static void do_waitwatch(unsigned int handle)
452{
453        char **vec;
454        struct timeval tv = {.tv_sec = timeout_ms/1000,
455                             .tv_usec = (timeout_ms*1000)%1000000 };
456        fd_set set;
457        unsigned int num;
458
459        if (xs_fileno(handles[handle]) != -2) {
460                /* Manually select here so we can time out gracefully. */
461                FD_ZERO(&set);
462                FD_SET(xs_fileno(handles[handle]), &set);
463                disarm_timeout();
464                if (select(xs_fileno(handles[handle])+1, &set,
465                           NULL, NULL, &tv) == 0) {
466                        errno = ETIMEDOUT;
467                        failed(handle);
468                        return;
469                }
470                set_timeout();
471        }
472
473        vec = xs_read_watch(handles[handle], &num);
474        if (!vec) {
475                failed(handle);
476                return;
477        }
478
479        if (handle)
480                output("%i:%s:%s\n", handle,
481                       vec[XS_WATCH_PATH], vec[XS_WATCH_TOKEN]);
482        else
483                output("%s:%s\n", vec[XS_WATCH_PATH], vec[XS_WATCH_TOKEN]);
484        free(vec);
485}
486
487static void do_unwatch(unsigned int handle, const char *node, const char *token)
488{
489        if (!xs_unwatch(handles[handle], node, token))
490                failed(handle);
491}
492
493static void do_start(unsigned int handle)
494{
495        txh[handle] = xs_transaction_start(handles[handle]);
496        if (txh[handle] == XBT_NULL)
497                failed(handle);
498}
499
500static void do_end(unsigned int handle, bool abort)
501{
502        if (!xs_transaction_end(handles[handle], txh[handle], abort))
503                failed(handle);
504        txh[handle] = XBT_NULL;
505}
506
507static void do_introduce(unsigned int handle,
508                         const char *domid,
509                         const char *mfn,
510                         const char *eventchn,
511                         const char *path)
512{
513        unsigned int i;
514        int fd;
515
516        /* This mechanism is v. slow w. valgrind running. */
517        timeout_ms = 5000;
518
519        /* We poll, so ignore signal */
520        signal(SIGUSR2, SIG_IGN);
521        for (i = 0; i < ARRAY_SIZE(handles); i++)
522                if (!handles[i])
523                        break;
524
525        fd = open("/tmp/xcmap", O_RDWR);
526        /* Set shared comms page. */
527        interface = mmap(NULL, getpagesize(), PROT_WRITE|PROT_READ,
528                         MAP_SHARED,fd,0);
529        if (interface == MAP_FAILED)
530                barf_perror("Failed to map /tmp/xcmap page");
531        close(fd);
532
533        /* Tell them the event channel and our PID. */
534        *(int *)((void *)interface + 32) = getpid();
535        *(uint16_t *)((void *)interface + 36) = atoi(eventchn);
536
537        if (!xs_introduce_domain(handles[handle], atoi(domid),
538                                 atol(mfn), atoi(eventchn))) {
539                failed(handle);
540                munmap(interface, getpagesize());
541                return;
542        }
543        output("handle is %i\n", i);
544
545        /* Create new handle. */
546        handles[i] = new(struct xs_handle);
547        handles[i]->fd = -2;
548
549        /* Read in daemon pid. */
550        daemon_pid = *(int *)((void *)interface + 32);
551}
552
553static void do_release(unsigned int handle, const char *domid)
554{
555        if (!xs_release_domain(handles[handle], atoi(domid)))
556                failed(handle);
557}
558
559static int strptrcmp(const void *a, const void *b)
560{
561        return strcmp(*(char **)a, *(char **)b);
562}
563
564static void sort_dir(char **dir, unsigned int num)
565{
566        qsort(dir, num, sizeof(char *), strptrcmp);
567}
568
569static void dump_dir(unsigned int handle,
570                     const char *node,
571                     char **dir,
572                     unsigned int numdirs,
573                     unsigned int depth)
574{
575        unsigned int i;
576        char spacing[depth+1];
577
578        memset(spacing, ' ', depth);
579        spacing[depth] = '\0';
580
581        sort_dir(dir, numdirs);
582
583        for (i = 0; i < numdirs; i++) {
584                struct xs_permissions *perms;
585                unsigned int j, numperms;
586                unsigned int len;
587                char *contents;
588                unsigned int subnum;
589                char **subdirs;
590                char subnode[strlen(node) + 1 + strlen(dir[i]) + 1];
591
592                sprintf(subnode, "%s/%s", node, dir[i]);
593
594                perms = xs_get_permissions(handles[handle], txh[handle],
595                                           subnode,&numperms);
596                if (!perms) {
597                        failed(handle);
598                        return;
599                }
600
601                output("%s%s: ", spacing, dir[i]);
602                for (j = 0; j < numperms; j++) {
603                        char buffer[100];
604                        if (!xs_perm_to_string(&perms[j], buffer))
605                                barf("perm to string");
606                        output("%s ", buffer);
607                }
608                free(perms);
609                output("\n");
610
611                /* Even directories can have contents. */
612                contents = xs_read(handles[handle], txh[handle], 
613                                   subnode, &len);
614                if (!contents) {
615                        if (errno != EISDIR)
616                                failed(handle);
617                } else {
618                        output(" %s(%.*s)\n", spacing, len, contents);
619                        free(contents);
620                }                       
621
622                /* Every node is a directory. */
623                subdirs = xs_directory(handles[handle], txh[handle], 
624                                       subnode, &subnum);
625                if (!subdirs) {
626                        failed(handle);
627                        return;
628                }
629                dump_dir(handle, subnode, subdirs, subnum, depth+1);
630                free(subdirs);
631        }
632}
633
634static void dump(int handle)
635{
636        char **subdirs;
637        unsigned int subnum;
638
639        subdirs = xs_directory(handles[handle], txh[handle], "/", &subnum);
640        if (!subdirs) {
641                failed(handle);
642                return;
643        }
644
645        dump_dir(handle, "", subdirs, subnum, 0);
646        free(subdirs);
647}
648
649static int handle;
650
651static void alarmed(int sig __attribute__((unused)))
652{
653        if (handle) {
654                char handlename[10];
655                sprintf(handlename, "%u:", handle);
656                write(STDOUT_FILENO, handlename, strlen(handlename));
657        }
658        write(STDOUT_FILENO, command, strlen(command));
659        write(STDOUT_FILENO, " timeout\n", strlen(" timeout\n"));
660        exit(1);
661}
662
663static void do_command(unsigned int default_handle, char *line)
664{
665        char *endp;
666
667        if (print_input)
668                printf("%i> %s", ++linenum, line);
669
670        if (strspn(line, " \n") == strlen(line))
671                return;
672        if (strstarts(line, "#"))
673                return;
674
675        handle = strtoul(line, &endp, 10);
676        if (endp != line)
677                memmove(line, endp+1, strlen(endp));
678        else
679                handle = default_handle;
680
681        command = arg(line, 0);
682        if (!handles[handle]) {
683                if (readonly)
684                        handles[handle] = xs_daemon_open_readonly();
685                else
686                        handles[handle] = xs_daemon_open();
687                if (!handles[handle])
688                        barf_perror("Opening connection to daemon");
689        }
690
691        if (!timeout_suppressed)
692                set_timeout();
693        timeout_suppressed = false;
694
695        if (streq(command, "dir"))
696                do_dir(handle, arg(line, 1));
697        else if (streq(command, "read"))
698                do_read(handle, arg(line, 1));
699        else if (streq(command, "write"))
700                do_write(handle, arg(line, 1), arg(line, 2));
701        else if (streq(command, "setid"))
702                do_setid(handle, arg(line, 1));
703        else if (streq(command, "mkdir"))
704                do_mkdir(handle, arg(line, 1));
705        else if (streq(command, "rm"))
706                do_rm(handle, arg(line, 1));
707        else if (streq(command, "getperm"))
708                do_getperm(handle, arg(line, 1));
709        else if (streq(command, "setperm"))
710                do_setperm(handle, arg(line, 1), line);
711        else if (streq(command, "watch"))
712                do_watch(handle, arg(line, 1), arg(line, 2), true);
713        else if (streq(command, "watchnoack"))
714                do_watch(handle, arg(line, 1), arg(line, 2), false);
715        else if (streq(command, "waitwatch"))
716                do_waitwatch(handle);
717        else if (streq(command, "unwatch"))
718                do_unwatch(handle, arg(line, 1), arg(line, 2));
719        else if (streq(command, "close")) {
720                xs_daemon_close(handles[handle]);
721                handles[handle] = NULL;
722                txh[handle] = XBT_NULL;
723        } else if (streq(command, "start"))
724                do_start(handle);
725        else if (streq(command, "commit"))
726                do_end(handle, false);
727        else if (streq(command, "abort"))
728                do_end(handle, true);
729        else if (streq(command, "introduce"))
730                do_introduce(handle, arg(line, 1), arg(line, 2),
731                             arg(line, 3), arg(line, 4));
732        else if (streq(command, "release"))
733                do_release(handle, arg(line, 1));
734        else if (streq(command, "dump"))
735                dump(handle);
736        else if (streq(command, "sleep")) {
737                disarm_timeout();
738                usleep(atoi(arg(line, 1)) * 1000);
739        } else if (streq(command, "expect"))
740                expect(line);
741        else if (streq(command, "notimeout"))
742                timeout_suppressed = true;
743        else if (streq(command, "readonly")) {
744                readonly = true;
745                xs_daemon_close(handles[handle]);
746                handles[handle] = NULL;
747        } else if (streq(command, "readwrite")) {
748                readonly = false;
749                xs_daemon_close(handles[handle]);
750                handles[handle] = NULL;
751        } else
752                barf("Unknown command %s", command);
753        fflush(stdout);
754        disarm_timeout();
755
756        /* Check expectations. */
757        if (!streq(command, "expect")) {
758                struct expect *i = list_top(&expects, struct expect, list);
759
760                if (i)
761                        barf("Expected '%s', didn't happen\n", i->pattern);
762        }
763}
764
765static struct option options[] = { { "readonly", 0, NULL, 'r' },
766                                   { "no-timeout", 0, NULL, 't' },
767                                   { NULL, 0, NULL, 0 } };
768
769int main(int argc, char *argv[])
770{
771        int opt;
772        char line[1024];
773
774        while ((opt = getopt_long(argc, argv, "xrt", options, NULL)) != -1) {
775                switch (opt) {
776                case 'r':
777                        readonly = true;
778                        break;
779                case 't':
780                        timeout_ms = 0;
781                        break;
782                case 'x':
783                        print_input = true;
784                        break;
785                }
786        }
787
788        if (optind + 1 == argc) {
789                int fd = open(argv[optind], O_RDONLY);
790                if (!fd)
791                        barf_perror("Opening %s", argv[optind]);
792                dup2(fd, STDIN_FILENO);
793        } else if (optind != argc)
794                usage();
795       
796
797        signal(SIGALRM, alarmed);
798        while (fgets(line, sizeof(line), stdin))
799                do_command(0, line);
800
801        return 0;
802}
803
804/*
805 * Local variables:
806 *  c-file-style: "linux"
807 *  indent-tabs-mode: t
808 *  c-indent-level: 8
809 *  c-basic-offset: 8
810 *  tab-width: 8
811 * End:
812 */
Note: See TracBrowser for help on using the repository browser.