ia64/xen-unstable

view tools/xenstore/xs_test.c @ 6808:72e4e2aab342

merge?
author cl349@firebug.cl.cam.ac.uk
date Tue Sep 13 15:48:19 2005 +0000 (2005-09-13)
parents 8ca0f98ba8e2 e7c7196fa329
children c63529f3367d
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;
403 ret = read_reply(handles[handle]->fd, &type, NULL);
404 if (!ret)
405 failed(handle);
406 free(ret);
407 }
409 static void do_setid(unsigned int handle, char *id)
410 {
411 if (!xs_bool(xs_debug_command(handles[handle], "setid", id,
412 strlen(id)+1)))
413 failed(handle);
414 }
416 static void do_mkdir(unsigned int handle, char *path)
417 {
418 if (!xs_mkdir(handles[handle], path))
419 failed(handle);
420 }
422 static void do_rm(unsigned int handle, char *path)
423 {
424 if (!xs_rm(handles[handle], path))
425 failed(handle);
426 }
428 static void do_getperm(unsigned int handle, char *path)
429 {
430 unsigned int i, num;
431 struct xs_permissions *perms;
433 perms = xs_get_permissions(handles[handle], path, &num);
434 if (!perms) {
435 failed(handle);
436 return;
437 }
439 for (i = 0; i < num; i++) {
440 char *permstring;
442 switch (perms[i].perms) {
443 case XS_PERM_NONE:
444 permstring = "NONE";
445 break;
446 case XS_PERM_WRITE:
447 permstring = "WRITE";
448 break;
449 case XS_PERM_READ:
450 permstring = "READ";
451 break;
452 case XS_PERM_READ|XS_PERM_WRITE:
453 permstring = "READ/WRITE";
454 break;
455 default:
456 barf("bad perm value %i", perms[i].perms);
457 }
459 if (handle)
460 output("%i:%i %s\n", handle, perms[i].id, permstring);
461 else
462 output("%i %s\n", perms[i].id, permstring);
463 }
464 free(perms);
465 }
467 static void do_setperm(unsigned int handle, char *path, char *line)
468 {
469 unsigned int i;
470 struct xs_permissions perms[100];
472 strtok(line, " \t\n");
473 strtok(NULL, " \t\n");
474 for (i = 0; ; i++) {
475 char *arg = strtok(NULL, " \t\n");
476 if (!arg)
477 break;
478 perms[i].id = atoi(arg);
479 arg = strtok(NULL, " \t\n");
480 if (!arg)
481 break;
482 if (streq(arg, "WRITE"))
483 perms[i].perms = XS_PERM_WRITE;
484 else if (streq(arg, "READ"))
485 perms[i].perms = XS_PERM_READ;
486 else if (streq(arg, "READ/WRITE"))
487 perms[i].perms = XS_PERM_READ|XS_PERM_WRITE;
488 else if (streq(arg, "NONE"))
489 perms[i].perms = XS_PERM_NONE;
490 else
491 barf("bad flags %s\n", arg);
492 }
494 if (!xs_set_permissions(handles[handle], path, perms, i))
495 failed(handle);
496 }
498 static void do_shutdown(unsigned int handle)
499 {
500 if (!xs_shutdown(handles[handle]))
501 failed(handle);
502 }
504 static void do_watch(unsigned int handle, const char *node, const char *token)
505 {
506 if (!xs_watch(handles[handle], node, token))
507 failed(handle);
508 }
510 static void set_timeout(void)
511 {
512 struct itimerval timeout;
514 timeout.it_value.tv_sec = timeout_ms / 1000;
515 timeout.it_value.tv_usec = (timeout_ms * 1000) % 1000000;
516 timeout.it_interval.tv_sec = timeout.it_interval.tv_usec = 0;
517 setitimer(ITIMER_REAL, &timeout, NULL);
518 }
520 static void disarm_timeout(void)
521 {
522 struct itimerval timeout;
524 timeout.it_value.tv_sec = 0;
525 timeout.it_value.tv_usec = 0;
526 setitimer(ITIMER_REAL, &timeout, NULL);
527 }
529 static void do_waitwatch(unsigned int handle)
530 {
531 char **vec;
532 struct timeval tv = {.tv_sec = timeout_ms/1000,
533 .tv_usec = (timeout_ms*1000)%1000000 };
534 fd_set set;
536 if (xs_fileno(handles[handle]) != -2) {
537 /* Manually select here so we can time out gracefully. */
538 FD_ZERO(&set);
539 FD_SET(xs_fileno(handles[handle]), &set);
540 disarm_timeout();
541 if (select(xs_fileno(handles[handle])+1, &set,
542 NULL, NULL, &tv) == 0) {
543 errno = ETIMEDOUT;
544 failed(handle);
545 return;
546 }
547 set_timeout();
548 }
550 vec = xs_read_watch(handles[handle]);
551 if (!vec) {
552 failed(handle);
553 return;
554 }
556 if (handle)
557 output("%i:%s:%s\n", handle, vec[0], vec[1]);
558 else
559 output("%s:%s\n", vec[0], vec[1]);
560 free(vec);
561 }
563 static void do_ackwatch(unsigned int handle, const char *token)
564 {
565 if (!xs_acknowledge_watch(handles[handle], token))
566 failed(handle);
567 }
569 static void do_unwatch(unsigned int handle, const char *node, const char *token)
570 {
571 if (!xs_unwatch(handles[handle], node, token))
572 failed(handle);
573 }
575 static void do_start(unsigned int handle, const char *node)
576 {
577 if (!xs_transaction_start(handles[handle], node))
578 failed(handle);
579 }
581 static void do_end(unsigned int handle, bool abort)
582 {
583 if (!xs_transaction_end(handles[handle], abort))
584 failed(handle);
585 }
587 static void do_introduce(unsigned int handle,
588 const char *domid,
589 const char *mfn,
590 const char *eventchn,
591 const char *path)
592 {
593 unsigned int i;
594 int fd;
596 /* This mechanism is v. slow w. valgrind running. */
597 timeout_ms = 5000;
599 /* We poll, so ignore signal */
600 signal(SIGUSR2, SIG_IGN);
601 for (i = 0; i < ARRAY_SIZE(handles); i++)
602 if (!handles[i])
603 break;
605 fd = open("/tmp/xcmap", O_RDWR);
606 /* Set in and out pointers. */
607 out = mmap(NULL, getpagesize(), PROT_WRITE|PROT_READ, MAP_SHARED,fd,0);
608 if (out == MAP_FAILED)
609 barf_perror("Failed to map /tmp/xcmap page");
610 in = (void *)out + getpagesize() / 2;
611 close(fd);
613 /* Tell them the event channel and our PID. */
614 *(int *)((void *)out + 32) = getpid();
615 *(u16 *)((void *)out + 36) = atoi(eventchn);
617 if (!xs_introduce_domain(handles[handle], atoi(domid),
618 atol(mfn), atoi(eventchn), path)) {
619 failed(handle);
620 munmap(out, getpagesize());
621 return;
622 }
623 output("handle is %i\n", i);
625 /* Create new handle. */
626 handles[i] = new(struct xs_handle);
627 handles[i]->fd = -2;
629 /* Read in daemon pid. */
630 daemon_pid = *(int *)((void *)out + 32);
631 }
633 static void do_release(unsigned int handle, const char *domid)
634 {
635 if (!xs_release_domain(handles[handle], atoi(domid)))
636 failed(handle);
637 }
639 static int strptrcmp(const void *a, const void *b)
640 {
641 return strcmp(*(char **)a, *(char **)b);
642 }
644 static void sort_dir(char **dir, unsigned int num)
645 {
646 qsort(dir, num, sizeof(char *), strptrcmp);
647 }
649 static void dump_dir(unsigned int handle,
650 const char *node,
651 char **dir,
652 unsigned int numdirs,
653 unsigned int depth)
654 {
655 unsigned int i;
656 char spacing[depth+1];
658 memset(spacing, ' ', depth);
659 spacing[depth] = '\0';
661 sort_dir(dir, numdirs);
663 for (i = 0; i < numdirs; i++) {
664 struct xs_permissions *perms;
665 unsigned int j, numperms;
666 unsigned int len;
667 char *contents;
668 unsigned int subnum;
669 char **subdirs;
670 char subnode[strlen(node) + 1 + strlen(dir[i]) + 1];
672 sprintf(subnode, "%s/%s", node, dir[i]);
674 perms = xs_get_permissions(handles[handle], subnode,&numperms);
675 if (!perms) {
676 failed(handle);
677 return;
678 }
680 output("%s%s: ", spacing, dir[i]);
681 for (j = 0; j < numperms; j++) {
682 char buffer[100];
683 if (!xs_perm_to_string(&perms[j], buffer))
684 barf("perm to string");
685 output("%s ", buffer);
686 }
687 free(perms);
688 output("\n");
690 /* Even directories can have contents. */
691 contents = xs_read(handles[handle], subnode, &len);
692 if (!contents) {
693 if (errno != EISDIR)
694 failed(handle);
695 } else {
696 output(" %s(%.*s)\n", spacing, len, contents);
697 free(contents);
698 }
700 /* Every node is a directory. */
701 subdirs = xs_directory(handles[handle], subnode, &subnum);
702 if (!subdirs) {
703 failed(handle);
704 return;
705 }
706 dump_dir(handle, subnode, subdirs, subnum, depth+1);
707 free(subdirs);
708 }
709 }
711 static void dump(int handle)
712 {
713 char **subdirs;
714 unsigned int subnum;
716 subdirs = xs_directory(handles[handle], "/", &subnum);
717 if (!subdirs) {
718 failed(handle);
719 return;
720 }
722 dump_dir(handle, "", subdirs, subnum, 0);
723 free(subdirs);
724 }
726 static int handle;
728 static void alarmed(int sig __attribute__((unused)))
729 {
730 if (handle) {
731 char handlename[10];
732 sprintf(handlename, "%u:", handle);
733 write(STDOUT_FILENO, handlename, strlen(handlename));
734 }
735 write(STDOUT_FILENO, command, strlen(command));
736 write(STDOUT_FILENO, " timeout\n", strlen(" timeout\n"));
737 exit(1);
738 }
740 static void do_command(unsigned int default_handle, char *line)
741 {
742 char *endp;
744 if (print_input)
745 printf("%i> %s", ++linenum, line);
747 if (strspn(line, " \n") == strlen(line))
748 return;
749 if (strstarts(line, "#"))
750 return;
752 handle = strtoul(line, &endp, 10);
753 if (endp != line)
754 memmove(line, endp+1, strlen(endp));
755 else
756 handle = default_handle;
758 command = arg(line, 0);
759 if (!handles[handle]) {
760 if (readonly)
761 handles[handle] = xs_daemon_open_readonly();
762 else
763 handles[handle] = xs_daemon_open();
764 if (!handles[handle])
765 barf_perror("Opening connection to daemon");
766 }
768 if (!timeout_suppressed)
769 set_timeout();
770 timeout_suppressed = false;
772 if (streq(command, "dir"))
773 do_dir(handle, arg(line, 1));
774 else if (streq(command, "read"))
775 do_read(handle, arg(line, 1));
776 else if (streq(command, "write"))
777 do_write(handle,
778 arg(line, 1), arg(line, 2), arg(line, 3));
779 else if (streq(command, "setid"))
780 do_setid(handle, arg(line, 1));
781 else if (streq(command, "mkdir"))
782 do_mkdir(handle, arg(line, 1));
783 else if (streq(command, "rm"))
784 do_rm(handle, arg(line, 1));
785 else if (streq(command, "getperm"))
786 do_getperm(handle, arg(line, 1));
787 else if (streq(command, "setperm"))
788 do_setperm(handle, arg(line, 1), line);
789 else if (streq(command, "shutdown"))
790 do_shutdown(handle);
791 else if (streq(command, "watch"))
792 do_watch(handle, arg(line, 1), arg(line, 2));
793 else if (streq(command, "waitwatch"))
794 do_waitwatch(handle);
795 else if (streq(command, "ackwatch"))
796 do_ackwatch(handle, arg(line, 1));
797 else if (streq(command, "unwatch"))
798 do_unwatch(handle, arg(line, 1), arg(line, 2));
799 else if (streq(command, "close")) {
800 xs_daemon_close(handles[handle]);
801 handles[handle] = NULL;
802 } else if (streq(command, "start"))
803 do_start(handle, arg(line, 1));
804 else if (streq(command, "commit"))
805 do_end(handle, false);
806 else if (streq(command, "abort"))
807 do_end(handle, true);
808 else if (streq(command, "introduce"))
809 do_introduce(handle, arg(line, 1), arg(line, 2),
810 arg(line, 3), arg(line, 4));
811 else if (streq(command, "release"))
812 do_release(handle, arg(line, 1));
813 else if (streq(command, "dump"))
814 dump(handle);
815 else if (streq(command, "sleep")) {
816 disarm_timeout();
817 usleep(atoi(arg(line, 1)) * 1000);
818 } else if (streq(command, "expect"))
819 expect(line);
820 else if (streq(command, "notimeout"))
821 timeout_suppressed = true;
822 else if (streq(command, "readonly")) {
823 readonly = true;
824 xs_daemon_close(handles[handle]);
825 handles[handle] = NULL;
826 } else if (streq(command, "readwrite")) {
827 readonly = false;
828 xs_daemon_close(handles[handle]);
829 handles[handle] = NULL;
830 } else if (streq(command, "noackwrite"))
831 do_noackwrite(handle, arg(line,1), arg(line,2), arg(line,3));
832 else if (streq(command, "readack"))
833 do_readack(handle);
834 else
835 barf("Unknown command %s", command);
836 fflush(stdout);
837 disarm_timeout();
839 /* Check expectations. */
840 if (!streq(command, "expect")) {
841 struct expect *i = list_top(&expects, struct expect, list);
843 if (i)
844 barf("Expected '%s', didn't happen\n", i->pattern);
845 }
846 }
848 static struct option options[] = { { "readonly", 0, NULL, 'r' },
849 { "no-timeout", 0, NULL, 't' },
850 { NULL, 0, NULL, 0 } };
852 int main(int argc, char *argv[])
853 {
854 int opt;
855 char line[1024];
857 while ((opt = getopt_long(argc, argv, "xrt", options, NULL)) != -1) {
858 switch (opt) {
859 case 'r':
860 readonly = true;
861 break;
862 case 't':
863 timeout_ms = 0;
864 break;
865 case 'x':
866 print_input = true;
867 break;
868 }
869 }
871 if (optind + 1 == argc) {
872 int fd = open(argv[optind], O_RDONLY);
873 if (!fd)
874 barf_perror("Opening %s", argv[optind]);
875 dup2(fd, STDIN_FILENO);
876 } else if (optind != argc)
877 usage();
880 /* The size of the ringbuffer: half a page minus head structure. */
881 ringbuf_datasize = getpagesize() / 2 - sizeof(struct ringbuf_head);
883 signal(SIGALRM, alarmed);
884 while (fgets(line, sizeof(line), stdin))
885 do_command(0, line);
887 return 0;
888 }