ia64/xen-unstable

view tools/xenstore/xs_test.c @ 6886:3233e7ecfa9f

merge?
author kaf24@firebug.cl.cam.ac.uk
date Thu Sep 15 07:38:53 2005 +0000 (2005-09-15)
parents 9af349b055e5 c63529f3367d
children f7a7f8f2e6e4 872cf6ee0594
line source
1 /*
2 Xen Store Daemon Test tool
3 Copyright (C) 2005 Rusty Russell IBM Corporation
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.
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.
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 */
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 "list.h"
42 #define XSTEST
44 static struct xs_handle *handles[10] = { NULL };
46 static unsigned int timeout_ms = 500;
47 static bool timeout_suppressed = true;
48 static bool readonly = false;
49 static bool print_input = false;
50 static unsigned int linenum = 0;
52 struct ringbuf_head
53 {
54 uint32_t write; /* Next place to write to */
55 uint32_t read; /* Next place to read from */
56 uint8_t flags;
57 char buf[0];
58 } __attribute__((packed));
60 static struct ringbuf_head *out, *in;
61 static unsigned int ringbuf_datasize;
62 static int daemon_pid;
64 /* FIXME: Mark connection as broken (close it?) when this happens. */
65 static bool check_buffer(const struct ringbuf_head *h)
66 {
67 return (h->write < ringbuf_datasize && h->read < ringbuf_datasize);
68 }
70 /* We can't fill last byte: would look like empty buffer. */
71 static void *get_output_chunk(const struct ringbuf_head *h,
72 void *buf, uint32_t *len)
73 {
74 uint32_t read_mark;
76 if (h->read == 0)
77 read_mark = ringbuf_datasize - 1;
78 else
79 read_mark = h->read - 1;
81 /* Here to the end of buffer, unless they haven't read some out. */
82 *len = ringbuf_datasize - h->write;
83 if (read_mark >= h->write)
84 *len = read_mark - h->write;
85 return buf + h->write;
86 }
88 static const void *get_input_chunk(const struct ringbuf_head *h,
89 const void *buf, uint32_t *len)
90 {
91 /* Here to the end of buffer, unless they haven't written some. */
92 *len = ringbuf_datasize - h->read;
93 if (h->write >= h->read)
94 *len = h->write - h->read;
95 return buf + h->read;
96 }
98 static int output_avail(struct ringbuf_head *out)
99 {
100 unsigned int avail;
102 get_output_chunk(out, out->buf, &avail);
103 return avail != 0;
104 }
106 static void update_output_chunk(struct ringbuf_head *h, uint32_t len)
107 {
108 h->write += len;
109 if (h->write == ringbuf_datasize)
110 h->write = 0;
111 }
113 static void update_input_chunk(struct ringbuf_head *h, uint32_t len)
114 {
115 h->read += len;
116 if (h->read == ringbuf_datasize)
117 h->read = 0;
118 }
120 /* FIXME: We spin, and we're sloppy. */
121 static bool read_all_shmem(int fd __attribute__((unused)),
122 void *data, unsigned int len)
123 {
124 unsigned int avail;
125 int was_full;
127 if (!check_buffer(in))
128 barf("Corrupt buffer");
130 was_full = !output_avail(in);
131 while (len) {
132 const void *src = get_input_chunk(in, in->buf, &avail);
133 if (avail > len)
134 avail = len;
135 memcpy(data, src, avail);
136 data += avail;
137 len -= avail;
138 update_input_chunk(in, avail);
139 }
141 /* Tell other end we read something. */
142 if (was_full)
143 kill(daemon_pid, SIGUSR2);
144 return true;
145 }
147 static bool write_all_shmem(int fd __attribute__((unused)),
148 const void *data, unsigned int len)
149 {
150 uint32_t avail;
152 if (!check_buffer(out))
153 barf("Corrupt buffer");
155 while (len) {
156 void *dst = get_output_chunk(out, out->buf, &avail);
157 if (avail > len)
158 avail = len;
159 memcpy(dst, data, avail);
160 data += avail;
161 len -= avail;
162 update_output_chunk(out, avail);
163 }
165 /* Tell other end we wrote something. */
166 kill(daemon_pid, SIGUSR2);
167 return true;
168 }
170 static bool read_all(int fd, void *data, unsigned int len);
171 static bool read_all_choice(int fd, void *data, unsigned int len)
172 {
173 if (fd == -2)
174 return read_all_shmem(fd, data, len);
175 return read_all(fd, data, len);
176 }
178 static bool write_all_choice(int fd, const void *data, unsigned int len)
179 {
180 if (fd == -2)
181 return write_all_shmem(fd, data, len);
182 return xs_write_all(fd, data, len);
183 }
185 /* We want access to internal functions. */
186 #include "xs.c"
188 static void __attribute__((noreturn)) usage(void)
189 {
190 barf("Usage:\n"
191 " xs_test [--readonly] [--no-timeout] [-x]\n"
192 "Reads commands from stdin, one per line:"
193 " dir <path>\n"
194 " read <path>\n"
195 " write <path> <flags> <value>...\n"
196 " setid <id>\n"
197 " mkdir <path>\n"
198 " rm <path>\n"
199 " getperm <path>\n"
200 " setperm <path> <id> <flags> ...\n"
201 " shutdown\n"
202 " watch <path> <token>\n"
203 " waitwatch\n"
204 " ackwatch <token>\n"
205 " unwatch <path> <token>\n"
206 " close\n"
207 " start <node>\n"
208 " abort\n"
209 " introduce <domid> <mfn> <eventchn> <path>\n"
210 " commit\n"
211 " sleep <milliseconds>\n"
212 " expect <pattern>\n"
213 " notimeout\n"
214 " readonly\n"
215 " readwrite\n"
216 " noackwrite <path> <flags> <value>...\n"
217 " readack\n"
218 " dump\n");
219 }
221 static int argpos(const char *line, unsigned int num)
222 {
223 unsigned int i, len = 0, off = 0;
225 for (i = 0; i <= num; i++) {
226 off += len;
227 off += strspn(line + off, " \t\n");
228 len = strcspn(line + off, " \t\n");
229 if (!len)
230 return off;
231 }
232 return off;
233 }
235 static char *arg(const char *line, unsigned int num)
236 {
237 static char *args[10];
238 unsigned int off, len;
240 off = argpos(line, num);
241 len = strcspn(line + off, " \t\n");
243 if (!len)
244 barf("Can't get arg %u", num);
246 free(args[num]);
247 args[num] = malloc(len + 1);
248 memcpy(args[num], line+off, len);
249 args[num][len] = '\0';
250 return args[num];
251 }
253 struct expect
254 {
255 struct list_head list;
256 char *pattern;
257 };
258 static LIST_HEAD(expects);
260 static char *command;
262 /* Trim leading and trailing whitespace */
263 static void trim(char *str)
264 {
265 while (isspace(str[0]))
266 memmove(str, str+1, strlen(str));
268 while (strlen(str) && isspace(str[strlen(str)-1]))
269 str[strlen(str)-1] = '\0';
270 }
272 static void output(const char *fmt, ...)
273 {
274 char *str;
275 struct expect *i;
276 va_list arglist;
278 va_start(arglist, fmt);
279 vasprintf(&str, fmt, arglist);
280 va_end(arglist);
282 printf("%s", str);
283 fflush(stdout);
284 trim(str);
285 list_for_each_entry(i, &expects, list) {
286 if (fnmatch(i->pattern, str, 0) == 0) {
287 list_del(&i->list);
288 free(i);
289 return;
290 }
291 }
292 barf("Unexpected output %s\n", str);
293 }
295 static void failed(int handle)
296 {
297 if (handle)
298 output("%i: %s failed: %s\n",
299 handle, command, strerror(errno));
300 else
301 output("%s failed: %s\n", command, strerror(errno));
302 }
304 static void expect(const char *line)
305 {
306 struct expect *e = malloc(sizeof(*e));
308 e->pattern = strdup(line + argpos(line, 1));
309 trim(e->pattern);
310 list_add(&e->list, &expects);
311 }
313 static void do_dir(unsigned int handle, char *path)
314 {
315 char **entries;
316 unsigned int i, num;
318 entries = xs_directory(handles[handle], path, &num);
319 if (!entries) {
320 failed(handle);
321 return;
322 }
324 for (i = 0; i < num; i++)
325 if (handle)
326 output("%i:%s\n", handle, entries[i]);
327 else
328 output("%s\n", entries[i]);
329 free(entries);
330 }
332 static void do_read(unsigned int handle, char *path)
333 {
334 char *value;
335 unsigned int len;
337 value = xs_read(handles[handle], path, &len);
338 if (!value) {
339 failed(handle);
340 return;
341 }
343 /* It's supposed to nul terminate for us. */
344 assert(value[len] == '\0');
345 if (handle)
346 output("%i:%.*s\n", handle, len, value);
347 else
348 output("%.*s\n", len, value);
349 }
351 static void do_write(unsigned int handle, char *path, char *flags, char *data)
352 {
353 int f;
355 if (streq(flags, "none"))
356 f = 0;
357 else if (streq(flags, "create"))
358 f = O_CREAT;
359 else if (streq(flags, "excl"))
360 f = O_CREAT | O_EXCL;
361 else if (streq(flags, "crap"))
362 f = 100;
363 else
364 barf("write flags 'none', 'create' or 'excl' only");
366 if (!xs_write(handles[handle], path, data, strlen(data), f))
367 failed(handle);
368 }
370 static void do_noackwrite(unsigned int handle,
371 char *path, const char *flags, char *data)
372 {
373 struct xsd_sockmsg msg;
375 /* Format: Flags (as string), path, data. */
376 if (streq(flags, "none"))
377 flags = XS_WRITE_NONE;
378 else if (streq(flags, "create"))
379 flags = XS_WRITE_CREATE;
380 else if (streq(flags, "excl"))
381 flags = XS_WRITE_CREATE_EXCL;
382 else
383 barf("noackwrite flags 'none', 'create' or 'excl' only");
385 msg.len = strlen(path) + 1 + strlen(flags) + 1 + strlen(data);
386 msg.type = XS_WRITE;
387 if (!write_all_choice(handles[handle]->fd, &msg, sizeof(msg)))
388 failed(handle);
389 if (!write_all_choice(handles[handle]->fd, path, strlen(path) + 1))
390 failed(handle);
391 if (!write_all_choice(handles[handle]->fd, flags, strlen(flags) + 1))
392 failed(handle);
393 if (!write_all_choice(handles[handle]->fd, data, strlen(data)))
394 failed(handle);
395 /* Do not wait for ack. */
396 }
398 static void do_readack(unsigned int handle)
399 {
400 enum xsd_sockmsg_type type;
401 char *ret = NULL;
403 /* Watches can have fired before reply comes: daemon detects
404 * and re-transmits, so we can ignore this. */
405 do {
406 free(ret);
407 ret = read_reply(handles[handle]->fd, &type, NULL);
408 if (!ret)
409 failed(handle);
410 } while (type == XS_WATCH_EVENT);
411 }
413 static void do_setid(unsigned int handle, char *id)
414 {
415 if (!xs_bool(xs_debug_command(handles[handle], "setid", id,
416 strlen(id)+1)))
417 failed(handle);
418 }
420 static void do_mkdir(unsigned int handle, char *path)
421 {
422 if (!xs_mkdir(handles[handle], path))
423 failed(handle);
424 }
426 static void do_rm(unsigned int handle, char *path)
427 {
428 if (!xs_rm(handles[handle], path))
429 failed(handle);
430 }
432 static void do_getperm(unsigned int handle, char *path)
433 {
434 unsigned int i, num;
435 struct xs_permissions *perms;
437 perms = xs_get_permissions(handles[handle], path, &num);
438 if (!perms) {
439 failed(handle);
440 return;
441 }
443 for (i = 0; i < num; i++) {
444 char *permstring;
446 switch (perms[i].perms) {
447 case XS_PERM_NONE:
448 permstring = "NONE";
449 break;
450 case XS_PERM_WRITE:
451 permstring = "WRITE";
452 break;
453 case XS_PERM_READ:
454 permstring = "READ";
455 break;
456 case XS_PERM_READ|XS_PERM_WRITE:
457 permstring = "READ/WRITE";
458 break;
459 default:
460 barf("bad perm value %i", perms[i].perms);
461 }
463 if (handle)
464 output("%i:%i %s\n", handle, perms[i].id, permstring);
465 else
466 output("%i %s\n", perms[i].id, permstring);
467 }
468 free(perms);
469 }
471 static void do_setperm(unsigned int handle, char *path, char *line)
472 {
473 unsigned int i;
474 struct xs_permissions perms[100];
476 strtok(line, " \t\n");
477 strtok(NULL, " \t\n");
478 for (i = 0; ; i++) {
479 char *arg = strtok(NULL, " \t\n");
480 if (!arg)
481 break;
482 perms[i].id = atoi(arg);
483 arg = strtok(NULL, " \t\n");
484 if (!arg)
485 break;
486 if (streq(arg, "WRITE"))
487 perms[i].perms = XS_PERM_WRITE;
488 else if (streq(arg, "READ"))
489 perms[i].perms = XS_PERM_READ;
490 else if (streq(arg, "READ/WRITE"))
491 perms[i].perms = XS_PERM_READ|XS_PERM_WRITE;
492 else if (streq(arg, "NONE"))
493 perms[i].perms = XS_PERM_NONE;
494 else
495 barf("bad flags %s\n", arg);
496 }
498 if (!xs_set_permissions(handles[handle], path, perms, i))
499 failed(handle);
500 }
502 static void do_shutdown(unsigned int handle)
503 {
504 if (!xs_shutdown(handles[handle]))
505 failed(handle);
506 }
508 static void do_watch(unsigned int handle, const char *node, const char *token)
509 {
510 if (!xs_watch(handles[handle], node, token))
511 failed(handle);
512 }
514 static void set_timeout(void)
515 {
516 struct itimerval timeout;
518 timeout.it_value.tv_sec = timeout_ms / 1000;
519 timeout.it_value.tv_usec = (timeout_ms * 1000) % 1000000;
520 timeout.it_interval.tv_sec = timeout.it_interval.tv_usec = 0;
521 setitimer(ITIMER_REAL, &timeout, NULL);
522 }
524 static void disarm_timeout(void)
525 {
526 struct itimerval timeout;
528 timeout.it_value.tv_sec = 0;
529 timeout.it_value.tv_usec = 0;
530 setitimer(ITIMER_REAL, &timeout, NULL);
531 }
533 static void do_waitwatch(unsigned int handle)
534 {
535 char **vec;
536 struct timeval tv = {.tv_sec = timeout_ms/1000,
537 .tv_usec = (timeout_ms*1000)%1000000 };
538 fd_set set;
540 if (xs_fileno(handles[handle]) != -2) {
541 /* Manually select here so we can time out gracefully. */
542 FD_ZERO(&set);
543 FD_SET(xs_fileno(handles[handle]), &set);
544 disarm_timeout();
545 if (select(xs_fileno(handles[handle])+1, &set,
546 NULL, NULL, &tv) == 0) {
547 errno = ETIMEDOUT;
548 failed(handle);
549 return;
550 }
551 set_timeout();
552 }
554 vec = xs_read_watch(handles[handle]);
555 if (!vec) {
556 failed(handle);
557 return;
558 }
560 if (handle)
561 output("%i:%s:%s\n", handle, vec[0], vec[1]);
562 else
563 output("%s:%s\n", vec[0], vec[1]);
564 free(vec);
565 }
567 static void do_ackwatch(unsigned int handle, const char *token)
568 {
569 if (!xs_acknowledge_watch(handles[handle], token))
570 failed(handle);
571 }
573 static void do_unwatch(unsigned int handle, const char *node, const char *token)
574 {
575 if (!xs_unwatch(handles[handle], node, token))
576 failed(handle);
577 }
579 static void do_start(unsigned int handle, const char *node)
580 {
581 if (!xs_transaction_start(handles[handle], node))
582 failed(handle);
583 }
585 static void do_end(unsigned int handle, bool abort)
586 {
587 if (!xs_transaction_end(handles[handle], abort))
588 failed(handle);
589 }
591 static void do_introduce(unsigned int handle,
592 const char *domid,
593 const char *mfn,
594 const char *eventchn,
595 const char *path)
596 {
597 unsigned int i;
598 int fd;
600 /* This mechanism is v. slow w. valgrind running. */
601 timeout_ms = 5000;
603 /* We poll, so ignore signal */
604 signal(SIGUSR2, SIG_IGN);
605 for (i = 0; i < ARRAY_SIZE(handles); i++)
606 if (!handles[i])
607 break;
609 fd = open("/tmp/xcmap", O_RDWR);
610 /* Set in and out pointers. */
611 out = mmap(NULL, getpagesize(), PROT_WRITE|PROT_READ, MAP_SHARED,fd,0);
612 if (out == MAP_FAILED)
613 barf_perror("Failed to map /tmp/xcmap page");
614 in = (void *)out + getpagesize() / 2;
615 close(fd);
617 /* Tell them the event channel and our PID. */
618 *(int *)((void *)out + 32) = getpid();
619 *(u16 *)((void *)out + 36) = atoi(eventchn);
621 if (!xs_introduce_domain(handles[handle], atoi(domid),
622 atol(mfn), atoi(eventchn), path)) {
623 failed(handle);
624 munmap(out, getpagesize());
625 return;
626 }
627 output("handle is %i\n", i);
629 /* Create new handle. */
630 handles[i] = new(struct xs_handle);
631 handles[i]->fd = -2;
633 /* Read in daemon pid. */
634 daemon_pid = *(int *)((void *)out + 32);
635 }
637 static void do_release(unsigned int handle, const char *domid)
638 {
639 if (!xs_release_domain(handles[handle], atoi(domid)))
640 failed(handle);
641 }
643 static int strptrcmp(const void *a, const void *b)
644 {
645 return strcmp(*(char **)a, *(char **)b);
646 }
648 static void sort_dir(char **dir, unsigned int num)
649 {
650 qsort(dir, num, sizeof(char *), strptrcmp);
651 }
653 static void dump_dir(unsigned int handle,
654 const char *node,
655 char **dir,
656 unsigned int numdirs,
657 unsigned int depth)
658 {
659 unsigned int i;
660 char spacing[depth+1];
662 memset(spacing, ' ', depth);
663 spacing[depth] = '\0';
665 sort_dir(dir, numdirs);
667 for (i = 0; i < numdirs; i++) {
668 struct xs_permissions *perms;
669 unsigned int j, numperms;
670 unsigned int len;
671 char *contents;
672 unsigned int subnum;
673 char **subdirs;
674 char subnode[strlen(node) + 1 + strlen(dir[i]) + 1];
676 sprintf(subnode, "%s/%s", node, dir[i]);
678 perms = xs_get_permissions(handles[handle], subnode,&numperms);
679 if (!perms) {
680 failed(handle);
681 return;
682 }
684 output("%s%s: ", spacing, dir[i]);
685 for (j = 0; j < numperms; j++) {
686 char buffer[100];
687 if (!xs_perm_to_string(&perms[j], buffer))
688 barf("perm to string");
689 output("%s ", buffer);
690 }
691 free(perms);
692 output("\n");
694 /* Even directories can have contents. */
695 contents = xs_read(handles[handle], subnode, &len);
696 if (!contents) {
697 if (errno != EISDIR)
698 failed(handle);
699 } else {
700 output(" %s(%.*s)\n", spacing, len, contents);
701 free(contents);
702 }
704 /* Every node is a directory. */
705 subdirs = xs_directory(handles[handle], subnode, &subnum);
706 if (!subdirs) {
707 failed(handle);
708 return;
709 }
710 dump_dir(handle, subnode, subdirs, subnum, depth+1);
711 free(subdirs);
712 }
713 }
715 static void dump(int handle)
716 {
717 char **subdirs;
718 unsigned int subnum;
720 subdirs = xs_directory(handles[handle], "/", &subnum);
721 if (!subdirs) {
722 failed(handle);
723 return;
724 }
726 dump_dir(handle, "", subdirs, subnum, 0);
727 free(subdirs);
728 }
730 static int handle;
732 static void alarmed(int sig __attribute__((unused)))
733 {
734 if (handle) {
735 char handlename[10];
736 sprintf(handlename, "%u:", handle);
737 write(STDOUT_FILENO, handlename, strlen(handlename));
738 }
739 write(STDOUT_FILENO, command, strlen(command));
740 write(STDOUT_FILENO, " timeout\n", strlen(" timeout\n"));
741 exit(1);
742 }
744 static void do_command(unsigned int default_handle, char *line)
745 {
746 char *endp;
748 if (print_input)
749 printf("%i> %s", ++linenum, line);
751 if (strspn(line, " \n") == strlen(line))
752 return;
753 if (strstarts(line, "#"))
754 return;
756 handle = strtoul(line, &endp, 10);
757 if (endp != line)
758 memmove(line, endp+1, strlen(endp));
759 else
760 handle = default_handle;
762 command = arg(line, 0);
763 if (!handles[handle]) {
764 if (readonly)
765 handles[handle] = xs_daemon_open_readonly();
766 else
767 handles[handle] = xs_daemon_open();
768 if (!handles[handle])
769 barf_perror("Opening connection to daemon");
770 }
772 if (!timeout_suppressed)
773 set_timeout();
774 timeout_suppressed = false;
776 if (streq(command, "dir"))
777 do_dir(handle, arg(line, 1));
778 else if (streq(command, "read"))
779 do_read(handle, arg(line, 1));
780 else if (streq(command, "write"))
781 do_write(handle,
782 arg(line, 1), arg(line, 2), arg(line, 3));
783 else if (streq(command, "setid"))
784 do_setid(handle, arg(line, 1));
785 else if (streq(command, "mkdir"))
786 do_mkdir(handle, arg(line, 1));
787 else if (streq(command, "rm"))
788 do_rm(handle, arg(line, 1));
789 else if (streq(command, "getperm"))
790 do_getperm(handle, arg(line, 1));
791 else if (streq(command, "setperm"))
792 do_setperm(handle, arg(line, 1), line);
793 else if (streq(command, "shutdown"))
794 do_shutdown(handle);
795 else if (streq(command, "watch"))
796 do_watch(handle, arg(line, 1), arg(line, 2));
797 else if (streq(command, "waitwatch"))
798 do_waitwatch(handle);
799 else if (streq(command, "ackwatch"))
800 do_ackwatch(handle, arg(line, 1));
801 else if (streq(command, "unwatch"))
802 do_unwatch(handle, arg(line, 1), arg(line, 2));
803 else if (streq(command, "close")) {
804 xs_daemon_close(handles[handle]);
805 handles[handle] = NULL;
806 } else if (streq(command, "start"))
807 do_start(handle, arg(line, 1));
808 else if (streq(command, "commit"))
809 do_end(handle, false);
810 else if (streq(command, "abort"))
811 do_end(handle, true);
812 else if (streq(command, "introduce"))
813 do_introduce(handle, arg(line, 1), arg(line, 2),
814 arg(line, 3), arg(line, 4));
815 else if (streq(command, "release"))
816 do_release(handle, arg(line, 1));
817 else if (streq(command, "dump"))
818 dump(handle);
819 else if (streq(command, "sleep")) {
820 disarm_timeout();
821 usleep(atoi(arg(line, 1)) * 1000);
822 } else if (streq(command, "expect"))
823 expect(line);
824 else if (streq(command, "notimeout"))
825 timeout_suppressed = true;
826 else if (streq(command, "readonly")) {
827 readonly = true;
828 xs_daemon_close(handles[handle]);
829 handles[handle] = NULL;
830 } else if (streq(command, "readwrite")) {
831 readonly = false;
832 xs_daemon_close(handles[handle]);
833 handles[handle] = NULL;
834 } else if (streq(command, "noackwrite"))
835 do_noackwrite(handle, arg(line,1), arg(line,2), arg(line,3));
836 else if (streq(command, "readack"))
837 do_readack(handle);
838 else
839 barf("Unknown command %s", command);
840 fflush(stdout);
841 disarm_timeout();
843 /* Check expectations. */
844 if (!streq(command, "expect")) {
845 struct expect *i = list_top(&expects, struct expect, list);
847 if (i)
848 barf("Expected '%s', didn't happen\n", i->pattern);
849 }
850 }
852 static struct option options[] = { { "readonly", 0, NULL, 'r' },
853 { "no-timeout", 0, NULL, 't' },
854 { NULL, 0, NULL, 0 } };
856 int main(int argc, char *argv[])
857 {
858 int opt;
859 char line[1024];
861 while ((opt = getopt_long(argc, argv, "xrt", options, NULL)) != -1) {
862 switch (opt) {
863 case 'r':
864 readonly = true;
865 break;
866 case 't':
867 timeout_ms = 0;
868 break;
869 case 'x':
870 print_input = true;
871 break;
872 }
873 }
875 if (optind + 1 == argc) {
876 int fd = open(argv[optind], O_RDONLY);
877 if (!fd)
878 barf_perror("Opening %s", argv[optind]);
879 dup2(fd, STDIN_FILENO);
880 } else if (optind != argc)
881 usage();
884 /* The size of the ringbuffer: half a page minus head structure. */
885 ringbuf_datasize = getpagesize() / 2 - sizeof(struct ringbuf_head);
887 signal(SIGALRM, alarmed);
888 while (fgets(line, sizeof(line), stdin))
889 do_command(0, line);
891 return 0;
892 }