ia64/xen-unstable

view tools/xenstore/xs_test.c @ 7238:971e7c7411b3

Raise an exception if an error appears on the pipes to our children, and make
sure that the child's pipes are closed even under that exception. Move the
handling of POLLHUP to the end of the loop, so that we guarantee to read any
remaining data from the child if POLLHUP and POLLIN appear at the same time.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@ewan
date Thu Oct 06 10:13:11 2005 +0100 (2005-10-06)
parents ef9591d03fdd
children 93e27f7ca8a8 61b3b357d827 402b5eb85905
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> <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 " watchnoack <path> <token>\n"
204 " waitwatch\n"
205 " ackwatch <token>\n"
206 " unwatch <path> <token>\n"
207 " close\n"
208 " start <node>\n"
209 " abort\n"
210 " introduce <domid> <mfn> <eventchn> <path>\n"
211 " commit\n"
212 " sleep <milliseconds>\n"
213 " expect <pattern>\n"
214 " notimeout\n"
215 " readonly\n"
216 " readwrite\n"
217 " noackwrite <path> <value>...\n"
218 " readack\n"
219 " dump\n");
220 }
222 static int argpos(const char *line, unsigned int num)
223 {
224 unsigned int i, len = 0, off = 0;
226 for (i = 0; i <= num; i++) {
227 off += len;
228 off += strspn(line + off, " \t\n");
229 len = strcspn(line + off, " \t\n");
230 if (!len)
231 return off;
232 }
233 return off;
234 }
236 static char *arg(const char *line, unsigned int num)
237 {
238 static char *args[10];
239 unsigned int off, len;
241 off = argpos(line, num);
242 len = strcspn(line + off, " \t\n");
244 if (!len)
245 barf("Can't get arg %u", num);
247 free(args[num]);
248 args[num] = malloc(len + 1);
249 memcpy(args[num], line+off, len);
250 args[num][len] = '\0';
251 return args[num];
252 }
254 struct expect
255 {
256 struct list_head list;
257 char *pattern;
258 };
259 static LIST_HEAD(expects);
261 static char *command;
263 /* Trim leading and trailing whitespace */
264 static void trim(char *str)
265 {
266 while (isspace(str[0]))
267 memmove(str, str+1, strlen(str));
269 while (strlen(str) && isspace(str[strlen(str)-1]))
270 str[strlen(str)-1] = '\0';
271 }
273 static void output(const char *fmt, ...)
274 {
275 char *str;
276 struct expect *i;
277 va_list arglist;
279 va_start(arglist, fmt);
280 vasprintf(&str, fmt, arglist);
281 va_end(arglist);
283 printf("%s", str);
284 fflush(stdout);
285 trim(str);
286 list_for_each_entry(i, &expects, list) {
287 if (fnmatch(i->pattern, str, 0) == 0) {
288 list_del(&i->list);
289 free(i);
290 return;
291 }
292 }
293 barf("Unexpected output %s\n", str);
294 }
296 static void failed(int handle)
297 {
298 if (handle)
299 output("%i: %s failed: %s\n",
300 handle, command, strerror(errno));
301 else
302 output("%s failed: %s\n", command, strerror(errno));
303 }
305 static void expect(const char *line)
306 {
307 struct expect *e = malloc(sizeof(*e));
309 e->pattern = strdup(line + argpos(line, 1));
310 trim(e->pattern);
311 list_add(&e->list, &expects);
312 }
314 static void do_dir(unsigned int handle, char *path)
315 {
316 char **entries;
317 unsigned int i, num;
319 entries = xs_directory(handles[handle], path, &num);
320 if (!entries) {
321 failed(handle);
322 return;
323 }
325 for (i = 0; i < num; i++)
326 if (handle)
327 output("%i:%s\n", handle, entries[i]);
328 else
329 output("%s\n", entries[i]);
330 free(entries);
331 }
333 static void do_read(unsigned int handle, char *path)
334 {
335 char *value;
336 unsigned int len;
338 value = xs_read(handles[handle], path, &len);
339 if (!value) {
340 failed(handle);
341 return;
342 }
344 /* It's supposed to nul terminate for us. */
345 assert(value[len] == '\0');
346 if (handle)
347 output("%i:%.*s\n", handle, len, value);
348 else
349 output("%.*s\n", len, value);
350 }
352 static void do_write(unsigned int handle, char *path, char *data)
353 {
354 if (!xs_write(handles[handle], path, data, strlen(data)))
355 failed(handle);
356 }
358 static void do_noackwrite(unsigned int handle,
359 char *path, char *data)
360 {
361 struct xsd_sockmsg msg;
363 msg.len = strlen(path) + 1 + strlen(data);
364 msg.type = XS_WRITE;
365 if (!write_all_choice(handles[handle]->fd, &msg, sizeof(msg)))
366 failed(handle);
367 if (!write_all_choice(handles[handle]->fd, path, strlen(path) + 1))
368 failed(handle);
369 if (!write_all_choice(handles[handle]->fd, data, strlen(data)))
370 failed(handle);
371 /* Do not wait for ack. */
372 }
374 static void do_readack(unsigned int handle)
375 {
376 enum xsd_sockmsg_type type;
377 char *ret = NULL;
379 /* Watches can have fired before reply comes: daemon detects
380 * and re-transmits, so we can ignore this. */
381 do {
382 free(ret);
383 ret = read_reply(handles[handle]->fd, &type, NULL);
384 if (!ret)
385 failed(handle);
386 } while (type == XS_WATCH_EVENT);
387 }
389 static void do_setid(unsigned int handle, char *id)
390 {
391 if (!xs_bool(xs_debug_command(handles[handle], "setid", id,
392 strlen(id)+1)))
393 failed(handle);
394 }
396 static void do_mkdir(unsigned int handle, char *path)
397 {
398 if (!xs_mkdir(handles[handle], path))
399 failed(handle);
400 }
402 static void do_rm(unsigned int handle, char *path)
403 {
404 if (!xs_rm(handles[handle], path))
405 failed(handle);
406 }
408 static void do_getperm(unsigned int handle, char *path)
409 {
410 unsigned int i, num;
411 struct xs_permissions *perms;
413 perms = xs_get_permissions(handles[handle], path, &num);
414 if (!perms) {
415 failed(handle);
416 return;
417 }
419 for (i = 0; i < num; i++) {
420 char *permstring;
422 switch (perms[i].perms) {
423 case XS_PERM_NONE:
424 permstring = "NONE";
425 break;
426 case XS_PERM_WRITE:
427 permstring = "WRITE";
428 break;
429 case XS_PERM_READ:
430 permstring = "READ";
431 break;
432 case XS_PERM_READ|XS_PERM_WRITE:
433 permstring = "READ/WRITE";
434 break;
435 default:
436 barf("bad perm value %i", perms[i].perms);
437 }
439 if (handle)
440 output("%i:%i %s\n", handle, perms[i].id, permstring);
441 else
442 output("%i %s\n", perms[i].id, permstring);
443 }
444 free(perms);
445 }
447 static void do_setperm(unsigned int handle, char *path, char *line)
448 {
449 unsigned int i;
450 struct xs_permissions perms[100];
452 strtok(line, " \t\n");
453 strtok(NULL, " \t\n");
454 for (i = 0; ; i++) {
455 char *arg = strtok(NULL, " \t\n");
456 if (!arg)
457 break;
458 perms[i].id = atoi(arg);
459 arg = strtok(NULL, " \t\n");
460 if (!arg)
461 break;
462 if (streq(arg, "WRITE"))
463 perms[i].perms = XS_PERM_WRITE;
464 else if (streq(arg, "READ"))
465 perms[i].perms = XS_PERM_READ;
466 else if (streq(arg, "READ/WRITE"))
467 perms[i].perms = XS_PERM_READ|XS_PERM_WRITE;
468 else if (streq(arg, "NONE"))
469 perms[i].perms = XS_PERM_NONE;
470 else
471 barf("bad flags %s\n", arg);
472 }
474 if (!xs_set_permissions(handles[handle], path, perms, i))
475 failed(handle);
476 }
478 static void do_shutdown(unsigned int handle)
479 {
480 if (!xs_shutdown(handles[handle]))
481 failed(handle);
482 }
484 static void do_watch(unsigned int handle, const char *node, const char *token,
485 bool swallow_event)
486 {
487 if (!xs_watch(handles[handle], node, token))
488 failed(handle);
490 /* Convenient for testing... */
491 if (swallow_event) {
492 char **vec = xs_read_watch(handles[handle]);
493 if (!vec || !streq(vec[0], node) || !streq(vec[1], token))
494 failed(handle);
495 if (!xs_acknowledge_watch(handles[handle], token))
496 failed(handle);
497 }
498 }
500 static void set_timeout(void)
501 {
502 struct itimerval timeout;
504 timeout.it_value.tv_sec = timeout_ms / 1000;
505 timeout.it_value.tv_usec = (timeout_ms * 1000) % 1000000;
506 timeout.it_interval.tv_sec = timeout.it_interval.tv_usec = 0;
507 setitimer(ITIMER_REAL, &timeout, NULL);
508 }
510 static void disarm_timeout(void)
511 {
512 struct itimerval timeout;
514 timeout.it_value.tv_sec = 0;
515 timeout.it_value.tv_usec = 0;
516 setitimer(ITIMER_REAL, &timeout, NULL);
517 }
519 static void do_waitwatch(unsigned int handle)
520 {
521 char **vec;
522 struct timeval tv = {.tv_sec = timeout_ms/1000,
523 .tv_usec = (timeout_ms*1000)%1000000 };
524 fd_set set;
526 if (xs_fileno(handles[handle]) != -2) {
527 /* Manually select here so we can time out gracefully. */
528 FD_ZERO(&set);
529 FD_SET(xs_fileno(handles[handle]), &set);
530 disarm_timeout();
531 if (select(xs_fileno(handles[handle])+1, &set,
532 NULL, NULL, &tv) == 0) {
533 errno = ETIMEDOUT;
534 failed(handle);
535 return;
536 }
537 set_timeout();
538 }
540 vec = xs_read_watch(handles[handle]);
541 if (!vec) {
542 failed(handle);
543 return;
544 }
546 if (handle)
547 output("%i:%s:%s\n", handle, vec[0], vec[1]);
548 else
549 output("%s:%s\n", vec[0], vec[1]);
550 free(vec);
551 }
553 static void do_ackwatch(unsigned int handle, const char *token)
554 {
555 if (!xs_acknowledge_watch(handles[handle], token))
556 failed(handle);
557 }
559 static void do_unwatch(unsigned int handle, const char *node, const char *token)
560 {
561 if (!xs_unwatch(handles[handle], node, token))
562 failed(handle);
563 }
565 static void do_start(unsigned int handle)
566 {
567 if (!xs_transaction_start(handles[handle]))
568 failed(handle);
569 }
571 static void do_end(unsigned int handle, bool abort)
572 {
573 if (!xs_transaction_end(handles[handle], abort))
574 failed(handle);
575 }
577 static void do_introduce(unsigned int handle,
578 const char *domid,
579 const char *mfn,
580 const char *eventchn,
581 const char *path)
582 {
583 unsigned int i;
584 int fd;
586 /* This mechanism is v. slow w. valgrind running. */
587 timeout_ms = 5000;
589 /* We poll, so ignore signal */
590 signal(SIGUSR2, SIG_IGN);
591 for (i = 0; i < ARRAY_SIZE(handles); i++)
592 if (!handles[i])
593 break;
595 fd = open("/tmp/xcmap", O_RDWR);
596 /* Set in and out pointers. */
597 out = mmap(NULL, getpagesize(), PROT_WRITE|PROT_READ, MAP_SHARED,fd,0);
598 if (out == MAP_FAILED)
599 barf_perror("Failed to map /tmp/xcmap page");
600 in = (void *)out + getpagesize() / 2;
601 close(fd);
603 /* Tell them the event channel and our PID. */
604 *(int *)((void *)out + 32) = getpid();
605 *(u16 *)((void *)out + 36) = atoi(eventchn);
607 if (!xs_introduce_domain(handles[handle], atoi(domid),
608 atol(mfn), atoi(eventchn), path)) {
609 failed(handle);
610 munmap(out, getpagesize());
611 return;
612 }
613 output("handle is %i\n", i);
615 /* Create new handle. */
616 handles[i] = new(struct xs_handle);
617 handles[i]->fd = -2;
619 /* Read in daemon pid. */
620 daemon_pid = *(int *)((void *)out + 32);
621 }
623 static void do_release(unsigned int handle, const char *domid)
624 {
625 if (!xs_release_domain(handles[handle], atoi(domid)))
626 failed(handle);
627 }
629 static int strptrcmp(const void *a, const void *b)
630 {
631 return strcmp(*(char **)a, *(char **)b);
632 }
634 static void sort_dir(char **dir, unsigned int num)
635 {
636 qsort(dir, num, sizeof(char *), strptrcmp);
637 }
639 static void dump_dir(unsigned int handle,
640 const char *node,
641 char **dir,
642 unsigned int numdirs,
643 unsigned int depth)
644 {
645 unsigned int i;
646 char spacing[depth+1];
648 memset(spacing, ' ', depth);
649 spacing[depth] = '\0';
651 sort_dir(dir, numdirs);
653 for (i = 0; i < numdirs; i++) {
654 struct xs_permissions *perms;
655 unsigned int j, numperms;
656 unsigned int len;
657 char *contents;
658 unsigned int subnum;
659 char **subdirs;
660 char subnode[strlen(node) + 1 + strlen(dir[i]) + 1];
662 sprintf(subnode, "%s/%s", node, dir[i]);
664 perms = xs_get_permissions(handles[handle], subnode,&numperms);
665 if (!perms) {
666 failed(handle);
667 return;
668 }
670 output("%s%s: ", spacing, dir[i]);
671 for (j = 0; j < numperms; j++) {
672 char buffer[100];
673 if (!xs_perm_to_string(&perms[j], buffer))
674 barf("perm to string");
675 output("%s ", buffer);
676 }
677 free(perms);
678 output("\n");
680 /* Even directories can have contents. */
681 contents = xs_read(handles[handle], subnode, &len);
682 if (!contents) {
683 if (errno != EISDIR)
684 failed(handle);
685 } else {
686 output(" %s(%.*s)\n", spacing, len, contents);
687 free(contents);
688 }
690 /* Every node is a directory. */
691 subdirs = xs_directory(handles[handle], subnode, &subnum);
692 if (!subdirs) {
693 failed(handle);
694 return;
695 }
696 dump_dir(handle, subnode, subdirs, subnum, depth+1);
697 free(subdirs);
698 }
699 }
701 static void dump(int handle)
702 {
703 char **subdirs;
704 unsigned int subnum;
706 subdirs = xs_directory(handles[handle], "/", &subnum);
707 if (!subdirs) {
708 failed(handle);
709 return;
710 }
712 dump_dir(handle, "", subdirs, subnum, 0);
713 free(subdirs);
714 }
716 static int handle;
718 static void alarmed(int sig __attribute__((unused)))
719 {
720 if (handle) {
721 char handlename[10];
722 sprintf(handlename, "%u:", handle);
723 write(STDOUT_FILENO, handlename, strlen(handlename));
724 }
725 write(STDOUT_FILENO, command, strlen(command));
726 write(STDOUT_FILENO, " timeout\n", strlen(" timeout\n"));
727 exit(1);
728 }
730 static void do_command(unsigned int default_handle, char *line)
731 {
732 char *endp;
734 if (print_input)
735 printf("%i> %s", ++linenum, line);
737 if (strspn(line, " \n") == strlen(line))
738 return;
739 if (strstarts(line, "#"))
740 return;
742 handle = strtoul(line, &endp, 10);
743 if (endp != line)
744 memmove(line, endp+1, strlen(endp));
745 else
746 handle = default_handle;
748 command = arg(line, 0);
749 if (!handles[handle]) {
750 if (readonly)
751 handles[handle] = xs_daemon_open_readonly();
752 else
753 handles[handle] = xs_daemon_open();
754 if (!handles[handle])
755 barf_perror("Opening connection to daemon");
756 }
758 if (!timeout_suppressed)
759 set_timeout();
760 timeout_suppressed = false;
762 if (streq(command, "dir"))
763 do_dir(handle, arg(line, 1));
764 else if (streq(command, "read"))
765 do_read(handle, arg(line, 1));
766 else if (streq(command, "write"))
767 do_write(handle, arg(line, 1), arg(line, 2));
768 else if (streq(command, "setid"))
769 do_setid(handle, arg(line, 1));
770 else if (streq(command, "mkdir"))
771 do_mkdir(handle, arg(line, 1));
772 else if (streq(command, "rm"))
773 do_rm(handle, arg(line, 1));
774 else if (streq(command, "getperm"))
775 do_getperm(handle, arg(line, 1));
776 else if (streq(command, "setperm"))
777 do_setperm(handle, arg(line, 1), line);
778 else if (streq(command, "shutdown"))
779 do_shutdown(handle);
780 else if (streq(command, "watch"))
781 do_watch(handle, arg(line, 1), arg(line, 2), true);
782 else if (streq(command, "watchnoack"))
783 do_watch(handle, arg(line, 1), arg(line, 2), false);
784 else if (streq(command, "waitwatch"))
785 do_waitwatch(handle);
786 else if (streq(command, "ackwatch"))
787 do_ackwatch(handle, arg(line, 1));
788 else if (streq(command, "unwatch"))
789 do_unwatch(handle, arg(line, 1), arg(line, 2));
790 else if (streq(command, "close")) {
791 xs_daemon_close(handles[handle]);
792 handles[handle] = NULL;
793 } else if (streq(command, "start"))
794 do_start(handle);
795 else if (streq(command, "commit"))
796 do_end(handle, false);
797 else if (streq(command, "abort"))
798 do_end(handle, true);
799 else if (streq(command, "introduce"))
800 do_introduce(handle, arg(line, 1), arg(line, 2),
801 arg(line, 3), arg(line, 4));
802 else if (streq(command, "release"))
803 do_release(handle, arg(line, 1));
804 else if (streq(command, "dump"))
805 dump(handle);
806 else if (streq(command, "sleep")) {
807 disarm_timeout();
808 usleep(atoi(arg(line, 1)) * 1000);
809 } else if (streq(command, "expect"))
810 expect(line);
811 else if (streq(command, "notimeout"))
812 timeout_suppressed = true;
813 else if (streq(command, "readonly")) {
814 readonly = true;
815 xs_daemon_close(handles[handle]);
816 handles[handle] = NULL;
817 } else if (streq(command, "readwrite")) {
818 readonly = false;
819 xs_daemon_close(handles[handle]);
820 handles[handle] = NULL;
821 } else if (streq(command, "noackwrite"))
822 do_noackwrite(handle, arg(line,1), arg(line,2));
823 else if (streq(command, "readack"))
824 do_readack(handle);
825 else
826 barf("Unknown command %s", command);
827 fflush(stdout);
828 disarm_timeout();
830 /* Check expectations. */
831 if (!streq(command, "expect")) {
832 struct expect *i = list_top(&expects, struct expect, list);
834 if (i)
835 barf("Expected '%s', didn't happen\n", i->pattern);
836 }
837 }
839 static struct option options[] = { { "readonly", 0, NULL, 'r' },
840 { "no-timeout", 0, NULL, 't' },
841 { NULL, 0, NULL, 0 } };
843 int main(int argc, char *argv[])
844 {
845 int opt;
846 char line[1024];
848 while ((opt = getopt_long(argc, argv, "xrt", options, NULL)) != -1) {
849 switch (opt) {
850 case 'r':
851 readonly = true;
852 break;
853 case 't':
854 timeout_ms = 0;
855 break;
856 case 'x':
857 print_input = true;
858 break;
859 }
860 }
862 if (optind + 1 == argc) {
863 int fd = open(argv[optind], O_RDONLY);
864 if (!fd)
865 barf_perror("Opening %s", argv[optind]);
866 dup2(fd, STDIN_FILENO);
867 } else if (optind != argc)
868 usage();
871 /* The size of the ringbuffer: half a page minus head structure. */
872 ringbuf_datasize = getpagesize() / 2 - sizeof(struct ringbuf_head);
874 signal(SIGALRM, alarmed);
875 while (fgets(line, sizeof(line), stdin))
876 do_command(0, line);
878 return 0;
879 }