ia64/xen-unstable

view tools/xenstore/xs_test.c @ 6946:e703abaf6e3d

Add behaviour to the remove methods to remove the transaction's path itself. This allows us to write Remove(path) to remove the specified path rather than having to slice the path ourselves.
author emellor@ewan
date Sun Sep 18 14:42:13 2005 +0100 (2005-09-18)
parents 3233e7ecfa9f
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 }