guestfish: detect more failed syscalls
[libguestfs.git] / fish / fish.c
1 /* guestfish - the filesystem interactive shell
2  * Copyright (C) 2009 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <config.h>
20
21 #define _GNU_SOURCE // for strchrnul
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <getopt.h>
29 #include <signal.h>
30 #include <assert.h>
31 #include <ctype.h>
32 #include <sys/types.h>
33 #include <sys/wait.h>
34
35 #ifdef HAVE_LIBREADLINE
36 #include <readline/readline.h>
37 #include <readline/history.h>
38 #endif
39
40 #include <guestfs.h>
41
42 #include "fish.h"
43
44 struct mp {
45   struct mp *next;
46   char *device;
47   char *mountpoint;
48 };
49
50 struct drv {
51   struct drv *next;
52   char *filename;
53 };
54
55 static void add_drives (struct drv *drv);
56 static void mount_mps (struct mp *mp);
57 static void interactive (void);
58 static void shell_script (void);
59 static void script (int prompt);
60 static void cmdline (char *argv[], int optind, int argc);
61 static void initialize_readline (void);
62 static void cleanup_readline (void);
63 static void add_history_line (const char *);
64
65 /* Currently open libguestfs handle. */
66 guestfs_h *g;
67
68 int read_only = 0;
69 int quit = 0;
70 int verbose = 0;
71 int echo_commands = 0;
72 int remote_control_listen = 0;
73 int remote_control = 0;
74 int exit_on_error = 1;
75
76 int
77 launch (guestfs_h *_g)
78 {
79   assert (_g == g);
80
81   if (guestfs_is_config (g)) {
82     if (guestfs_launch (g) == -1)
83       return -1;
84     if (guestfs_wait_ready (g) == -1)
85       return -1;
86   }
87   return 0;
88 }
89
90 static void
91 usage (void)
92 {
93   fprintf (stderr,
94            _("guestfish: guest filesystem shell\n"
95              "guestfish lets you edit virtual machine filesystems\n"
96              "Copyright (C) 2009 Red Hat Inc.\n"
97              "Usage:\n"
98              "  guestfish [--options] cmd [: cmd : cmd ...]\n"
99              "  guestfish -i libvirt-domain\n"
100              "  guestfish -i disk-image(s)\n"
101              "or for interactive use:\n"
102              "  guestfish\n"
103              "or from a shell script:\n"
104              "  guestfish <<EOF\n"
105              "  cmd\n"
106              "  ...\n"
107              "  EOF\n"
108              "Options:\n"
109              "  -h|--cmd-help        List available commands\n"
110              "  -h|--cmd-help cmd    Display detailed help on 'cmd'\n"
111              "  -a|--add image       Add image\n"
112              "  -D|--no-dest-paths   Don't tab-complete paths from guest fs\n"
113              "  -f|--file file       Read commands from file\n"
114              "  -i|--inspector       Run virt-inspector to get disk mountpoints\n"
115              "  --listen             Listen for remote commands\n"
116              "  -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n"
117              "  -n|--no-sync         Don't autosync\n"
118              "  --remote[=pid]       Send commands to remote guestfish\n"
119              "  -r|--ro              Mount read-only\n"
120              "  --selinux            Enable SELinux support\n"
121              "  -v|--verbose         Verbose messages\n"
122              "  -x                   Echo each command before executing it\n"
123              "  -V|--version         Display version and exit\n"
124              "For more information,  see the manpage guestfish(1).\n"));
125 }
126
127 int
128 main (int argc, char *argv[])
129 {
130   static const char *options = "a:Df:h::im:nrv?Vx";
131   static const struct option long_options[] = {
132     { "add", 1, 0, 'a' },
133     { "cmd-help", 2, 0, 'h' },
134     { "file", 1, 0, 'f' },
135     { "help", 0, 0, '?' },
136     { "inspector", 0, 0, 'i' },
137     { "listen", 0, 0, 0 },
138     { "mount", 1, 0, 'm' },
139     { "no-dest-paths", 0, 0, 'D' },
140     { "no-sync", 0, 0, 'n' },
141     { "remote", 2, 0, 0 },
142     { "ro", 0, 0, 'r' },
143     { "selinux", 0, 0, 0 },
144     { "verbose", 0, 0, 'v' },
145     { "version", 0, 0, 'V' },
146     { 0, 0, 0, 0 }
147   };
148   struct drv *drvs = NULL;
149   struct drv *drv;
150   struct mp *mps = NULL;
151   struct mp *mp;
152   char *p, *file = NULL;
153   int c;
154   int inspector = 0;
155   int option_index;
156   struct sigaction sa;
157
158   initialize_readline ();
159
160   memset (&sa, 0, sizeof sa);
161   sa.sa_handler = SIG_IGN;
162   sa.sa_flags = SA_RESTART;
163   sigaction (SIGPIPE, &sa, NULL);
164
165   /* guestfs_create is meant to be a lightweight operation, so
166    * it's OK to do it early here.
167    */
168   g = guestfs_create ();
169   if (g == NULL) {
170     fprintf (stderr, _("guestfs_create: failed to create handle\n"));
171     exit (1);
172   }
173
174   guestfs_set_autosync (g, 1);
175
176   /* If developing, add ./appliance to the path.  Note that libtools
177    * interferes with this because uninstalled guestfish is a shell
178    * script that runs the real program with an absolute path.  Detect
179    * that too.
180    *
181    * BUT if LIBGUESTFS_PATH environment variable is already set by
182    * the user, then don't override it.
183    */
184   if (getenv ("LIBGUESTFS_PATH") == NULL &&
185       argv[0] &&
186       (argv[0][0] != '/' || strstr (argv[0], "/.libs/lt-") != NULL))
187     guestfs_set_path (g, "appliance:" GUESTFS_DEFAULT_PATH);
188
189   for (;;) {
190     c = getopt_long (argc, argv, options, long_options, &option_index);
191     if (c == -1) break;
192
193     switch (c) {
194     case 0:                     /* options which are long only */
195       if (strcmp (long_options[option_index].name, "listen") == 0)
196         remote_control_listen = 1;
197       else if (strcmp (long_options[option_index].name, "remote") == 0) {
198         if (optarg) {
199           if (sscanf (optarg, "%d", &remote_control) != 1) {
200             fprintf (stderr, _("guestfish: --listen=PID: PID was not a number: %s\n"), optarg);
201             exit (1);
202           }
203         } else {
204           p = getenv ("GUESTFISH_PID");
205           if (!p || sscanf (p, "%d", &remote_control) != 1) {
206             fprintf (stderr, _("guestfish: remote: $GUESTFISH_PID must be set to the PID of the remote process\n"));
207             exit (1);
208           }
209         }
210       } else if (strcmp (long_options[option_index].name, "selinux") == 0) {
211         guestfs_set_selinux (g, 1);
212       } else {
213         fprintf (stderr, _("guestfish: unknown long option: %s (%d)\n"),
214                  long_options[option_index].name, option_index);
215         exit (1);
216       }
217       break;
218
219     case 'a':
220       if (access (optarg, R_OK) != 0) {
221         perror (optarg);
222         exit (1);
223       }
224       drv = malloc (sizeof (struct drv));
225       if (!drv) {
226         perror ("malloc");
227         exit (1);
228       }
229       drv->filename = optarg;
230       drv->next = drvs;
231       drvs = drv;
232       break;
233
234     case 'D':
235       complete_dest_paths = 0;
236       break;
237
238     case 'f':
239       if (file) {
240         fprintf (stderr, _("guestfish: only one -f parameter can be given\n"));
241         exit (1);
242       }
243       file = optarg;
244       break;
245
246     case 'h':
247       if (optarg)
248         display_command (optarg);
249       else if (argv[optind] && argv[optind][0] != '-')
250         display_command (argv[optind++]);
251       else
252         list_commands ();
253       exit (0);
254
255     case 'i':
256       inspector = 1;
257       break;
258
259     case 'm':
260       mp = malloc (sizeof (struct mp));
261       if (!mp) {
262         perror ("malloc");
263         exit (1);
264       }
265       p = strchr (optarg, ':');
266       if (p) {
267         *p = '\0';
268         mp->mountpoint = p+1;
269       } else
270         mp->mountpoint = "/";
271       mp->device = optarg;
272       mp->next = mps;
273       mps = mp;
274       break;
275
276     case 'n':
277       guestfs_set_autosync (g, 0);
278       break;
279
280     case 'r':
281       read_only = 1;
282       break;
283
284     case 'v':
285       verbose++;
286       guestfs_set_verbose (g, verbose);
287       break;
288
289     case 'V':
290       printf ("guestfish %s\n", PACKAGE_VERSION);
291       exit (0);
292
293     case 'x':
294       echo_commands = 1;
295       break;
296
297     case '?':
298       usage ();
299       exit (0);
300
301     default:
302       fprintf (stderr, _("guestfish: unexpected command line option 0x%x\n"),
303                c);
304       exit (1);
305     }
306   }
307
308   /* Inspector mode invalidates most of the other arguments. */
309   if (inspector) {
310     char cmd[1024];
311     int r;
312
313     if (drvs || mps || remote_control_listen || remote_control ||
314         guestfs_get_selinux (g)) {
315       fprintf (stderr, _("guestfish: cannot use -i option with -a, -m, --listen, --remote or --selinux\n"));
316       exit (1);
317     }
318     if (optind >= argc) {
319       fprintf (stderr, _("guestfish -i requires a libvirt domain or path(s) to disk image(s)\n"));
320       exit (1);
321     }
322
323     strcpy (cmd, "a=`virt-inspector");
324     while (optind < argc) {
325       if (strlen (cmd) + strlen (argv[optind]) + strlen (argv[0]) + 60
326           >= sizeof cmd) {
327         fprintf (stderr, _("guestfish: virt-inspector command too long for fixed-size buffer\n"));
328         exit (1);
329       }
330       strcat (cmd, " '");
331       strcat (cmd, argv[optind]);
332       strcat (cmd, "'");
333       optind++;
334     }
335
336     if (read_only)
337       strcat (cmd, " --ro-fish");
338     else
339       strcat (cmd, " --fish");
340
341     sprintf (&cmd[strlen(cmd)], "` && %s $a", argv[0]);
342
343     if (guestfs_get_verbose (g))
344       strcat (cmd, " -v");
345     if (!guestfs_get_autosync (g))
346       strcat (cmd, " -n");
347
348     if (verbose)
349       fprintf (stderr,
350                "guestfish -i: running virt-inspector command:\n%s\n", cmd);
351
352     r = system (cmd);
353     if (r == -1) {
354       perror ("system");
355       exit (1);
356     }
357     exit (WEXITSTATUS (r));
358   }
359
360   /* If we've got drives to add, add them now. */
361   add_drives (drvs);
362
363   /* If we've got mountpoints, we must launch the guest and mount them. */
364   if (mps != NULL) {
365     if (launch (g) == -1) exit (1);
366     mount_mps (mps);
367   }
368
369   /* Remote control? */
370   if (remote_control_listen && remote_control) {
371     fprintf (stderr, _("guestfish: cannot use --listen and --remote options at the same time\n"));
372     exit (1);
373   }
374
375   if (remote_control_listen) {
376     if (optind < argc) {
377       fprintf (stderr, _("guestfish: extra parameters on the command line with --listen flag\n"));
378       exit (1);
379     }
380     if (file) {
381       fprintf (stderr, _("guestfish: cannot use --listen and --file options at the same time\n"));
382       exit (1);
383     }
384     rc_listen ();
385   }
386
387   /* -f (file) parameter? */
388   if (file) {
389     close (0);
390     if (open (file, O_RDONLY) == -1) {
391       perror (file);
392       exit (1);
393     }
394   }
395
396   /* Interactive, shell script, or command(s) on the command line? */
397   if (optind >= argc) {
398     if (isatty (0))
399       interactive ();
400     else
401       shell_script ();
402   }
403   else
404     cmdline (argv, optind, argc);
405
406   cleanup_readline ();
407
408   exit (0);
409 }
410
411 void
412 pod2text (const char *name, const char *shortdesc, const char *str)
413 {
414   FILE *fp;
415
416   fp = popen ("pod2text", "w");
417   if (fp == NULL) {
418     /* pod2text failed, maybe not found, so let's just print the
419      * source instead, since that's better than doing nothing.
420      */
421     printf ("%s - %s\n\n%s\n", name, shortdesc, str);
422     return;
423   }
424   fprintf (fp, "=head1 %s - %s\n\n", name, shortdesc);
425   fputs (str, fp);
426   pclose (fp);
427 }
428
429 /* List is built in reverse order, so mount them in reverse order. */
430 static void
431 mount_mps (struct mp *mp)
432 {
433   int r;
434
435   if (mp) {
436     mount_mps (mp->next);
437     if (!read_only)
438       r = guestfs_mount (g, mp->device, mp->mountpoint);
439     else
440       r = guestfs_mount_ro (g, mp->device, mp->mountpoint);
441     if (r == -1)
442       exit (1);
443   }
444 }
445
446 static void
447 add_drives (struct drv *drv)
448 {
449   int r;
450
451   if (drv) {
452     add_drives (drv->next);
453     if (!read_only)
454       r = guestfs_add_drive (g, drv->filename);
455     else
456       r = guestfs_add_drive_ro (g, drv->filename);
457     if (r == -1)
458       exit (1);
459   }
460 }
461
462 static void
463 interactive (void)
464 {
465   script (1);
466 }
467
468 static void
469 shell_script (void)
470 {
471   script (0);
472 }
473
474 #define FISH "><fs> "
475
476 static char *line_read = NULL;
477
478 static char *
479 rl_gets (int prompt)
480 {
481 #ifdef HAVE_LIBREADLINE
482
483   if (prompt) {
484     if (line_read) {
485       free (line_read);
486       line_read = NULL;
487     }
488
489     line_read = readline (prompt ? FISH : "");
490
491     if (line_read && *line_read)
492       add_history_line (line_read);
493
494     return line_read;
495   }
496
497 #endif /* HAVE_LIBREADLINE */
498
499   static char buf[8192];
500   int len;
501
502   if (prompt) printf (FISH);
503   line_read = fgets (buf, sizeof buf, stdin);
504
505   if (line_read) {
506     len = strlen (line_read);
507     if (len > 0 && buf[len-1] == '\n') buf[len-1] = '\0';
508   }
509
510   return line_read;
511 }
512
513 static void
514 script (int prompt)
515 {
516   char *buf;
517   char *cmd;
518   char *p, *pend;
519   char *argv[64];
520   int i, len;
521   int global_exit_on_error = !prompt;
522   int tilde_candidate;
523
524   if (prompt)
525     printf (_("\n"
526               "Welcome to guestfish, the libguestfs filesystem interactive shell for\n"
527               "editing virtual machine filesystems.\n"
528               "\n"
529               "Type: 'help' for help with commands\n"
530               "      'quit' to quit the shell\n"
531               "\n"));
532
533   while (!quit) {
534     char *pipe = NULL;
535
536     exit_on_error = global_exit_on_error;
537
538     buf = rl_gets (prompt);
539     if (!buf) {
540       quit = 1;
541       break;
542     }
543
544     /* Skip any initial whitespace before the command. */
545   again:
546     while (*buf && isspace (*buf))
547       buf++;
548
549     if (!*buf) continue;
550
551     /* If the next character is '#' then this is a comment. */
552     if (*buf == '#') continue;
553
554     /* If the next character is '!' then pass the whole lot to system(3). */
555     if (*buf == '!') {
556       int r;
557
558       r = system (buf+1);
559       if (exit_on_error) {
560         if (r == -1 ||
561             (WIFSIGNALED (r) &&
562              (WTERMSIG (r) == SIGINT || WTERMSIG (r) == SIGQUIT)) ||
563             WEXITSTATUS (r) != 0)
564           exit (1);
565       }
566       continue;
567     }
568
569     /* If the next character is '-' allow the command to fail without
570      * exiting on error (just for this one command though).
571      */
572     if (*buf == '-') {
573       exit_on_error = 0;
574       buf++;
575       goto again;
576     }
577
578     /* Get the command (cannot be quoted). */
579     len = strcspn (buf, " \t");
580
581     if (len == 0) continue;
582
583     cmd = buf;
584     i = 0;
585     if (buf[len] == '\0') {
586       argv[0] = NULL;
587       goto got_command;
588     }
589
590     buf[len] = '\0';
591     p = &buf[len+1];
592     p += strspn (p, " \t");
593
594     /* Get the parameters. */
595     while (*p && i < sizeof argv / sizeof argv[0]) {
596       tilde_candidate = 0;
597
598       /* Parameters which start with quotes or pipes are treated
599        * specially.  Bare parameters are delimited by whitespace.
600        */
601       if (*p == '"') {
602         p++;
603         len = strcspn (p, "\"");
604         if (p[len] == '\0') {
605           fprintf (stderr, _("guestfish: unterminated double quote\n"));
606           if (exit_on_error) exit (1);
607           goto next_command;
608         }
609         if (p[len+1] && (p[len+1] != ' ' && p[len+1] != '\t')) {
610           fprintf (stderr, _("guestfish: command arguments not separated by whitespace\n"));
611           if (exit_on_error) exit (1);
612           goto next_command;
613         }
614         p[len] = '\0';
615         pend = p[len+1] ? &p[len+2] : &p[len+1];
616       } else if (*p == '\'') {
617         p++;
618         len = strcspn (p, "'");
619         if (p[len] == '\0') {
620           fprintf (stderr, _("guestfish: unterminated single quote\n"));
621           if (exit_on_error) exit (1);
622           goto next_command;
623         }
624         if (p[len+1] && (p[len+1] != ' ' && p[len+1] != '\t')) {
625           fprintf (stderr, _("guestfish: command arguments not separated by whitespace\n"));
626           if (exit_on_error) exit (1);
627           goto next_command;
628         }
629         p[len] = '\0';
630         pend = p[len+1] ? &p[len+2] : &p[len+1];
631       } else if (*p == '|') {
632         *p = '\0';
633         pipe = p+1;
634         continue;
635         /*
636       } else if (*p == '[') {
637         int c = 1;
638         p++;
639         pend = p;
640         while (*pend && c != 0) {
641           if (*pend == '[') c++;
642           else if (*pend == ']') c--;
643           pend++;
644         }
645         if (c != 0) {
646           fprintf (stderr, _("guestfish: unterminated \"[...]\" sequence\n"));
647           if (exit_on_error) exit (1);
648           goto next_command;
649         }
650         if (*pend && (*pend != ' ' && *pend != '\t')) {
651           fprintf (stderr, _("guestfish: command arguments not separated by whitespace\n"));
652           if (exit_on_error) exit (1);
653           goto next_command;
654         }
655         *(pend-1) = '\0';
656         */
657       } else if (*p != ' ' && *p != '\t') {
658         /* If the first character is a ~ then note that this parameter
659          * is a candidate for ~username expansion.  NB this does not
660          * apply to quoted parameters.
661          */
662         tilde_candidate = *p == '~';
663         len = strcspn (p, " \t");
664         if (p[len]) {
665           p[len] = '\0';
666           pend = &p[len+1];
667         } else
668           pend = &p[len];
669       } else {
670         fprintf (stderr, _("guestfish: internal error parsing string at '%s'\n"),
671                  p);
672         abort ();
673       }
674
675       if (!tilde_candidate)
676         argv[i] = p;
677       else
678         argv[i] = try_tilde_expansion (p);
679       i++;
680       p = pend;
681
682       if (*p)
683         p += strspn (p, " \t");
684     }
685
686     if (i == sizeof argv / sizeof argv[0]) {
687       fprintf (stderr, _("guestfish: too many arguments\n"));
688       if (exit_on_error) exit (1);
689       goto next_command;
690     }
691
692     argv[i] = NULL;
693
694   got_command:
695     if (issue_command (cmd, argv, pipe) == -1) {
696       if (exit_on_error) exit (1);
697     }
698
699   next_command:;
700   }
701   if (prompt) printf ("\n");
702 }
703
704 static void
705 cmdline (char *argv[], int optind, int argc)
706 {
707   const char *cmd;
708   char **params;
709
710   exit_on_error = 1;
711
712   if (optind >= argc) return;
713
714   cmd = argv[optind++];
715   if (strcmp (cmd, ":") == 0) {
716     fprintf (stderr, _("guestfish: empty command on command line\n"));
717     exit (1);
718   }
719   params = &argv[optind];
720
721   /* Search for end of command list or ":" ... */
722   while (optind < argc && strcmp (argv[optind], ":") != 0)
723     optind++;
724
725   if (optind == argc) {
726     if (issue_command (cmd, params, NULL) == -1) exit (1);
727   } else {
728     argv[optind] = NULL;
729     if (issue_command (cmd, params, NULL) == -1) exit (1);
730     cmdline (argv, optind+1, argc);
731   }
732 }
733
734 int
735 issue_command (const char *cmd, char *argv[], const char *pipecmd)
736 {
737   int argc;
738   int stdout_saved_fd = -1;
739   int pid = 0;
740   int i, r;
741
742   if (echo_commands) {
743     printf ("%s", cmd);
744     for (i = 0; argv[i] != NULL; ++i)
745       printf (" %s", argv[i]);
746     printf ("\n");
747   }
748
749   /* For | ... commands.  Annoyingly we can't use popen(3) here. */
750   if (pipecmd) {
751     int fd[2];
752
753     if (fflush (stdout) == EOF) {
754       perror ("failed to flush standard output");
755       return -1;
756     }
757     if (pipe (fd) < 0) {
758       perror ("pipe failed");
759       return -1;
760     }
761     pid = fork ();
762     if (pid == -1) {
763       perror ("fork");
764       return -1;
765     }
766
767     if (pid == 0) {             /* Child process. */
768       close (fd[1]);
769       if (dup2 (fd[0], 0) < 0) {
770         perror ("dup2 of stdin failed");
771         _exit (1);
772       }
773
774       r = system (pipecmd);
775       if (r == -1) {
776         perror (pipecmd);
777         _exit (1);
778       }
779       _exit (WEXITSTATUS (r));
780     }
781
782     if ((stdout_saved_fd = dup (1)) < 0) {
783       perror ("failed to dup stdout");
784       return -1;
785     }
786     close (fd[0]);
787     if (dup2 (fd[1], 1) < 0) {
788       perror ("failed to dup stdout");
789       close (stdout_saved_fd);
790       return -1;
791     }
792     close (fd[1]);
793   }
794
795   for (argc = 0; argv[argc] != NULL; ++argc)
796     ;
797
798   /* If --remote was set, then send this command to a remote process. */
799   if (remote_control)
800     r = rc_remote (remote_control, cmd, argc, argv, exit_on_error);
801
802   /* Otherwise execute it locally. */
803   else if (strcasecmp (cmd, "help") == 0) {
804     if (argc == 0)
805       list_commands ();
806     else
807       display_command (argv[0]);
808     r = 0;
809   }
810   else if (strcasecmp (cmd, "quit") == 0 ||
811            strcasecmp (cmd, "exit") == 0 ||
812            strcasecmp (cmd, "q") == 0) {
813     quit = 1;
814     r = 0;
815   }
816   else if (strcasecmp (cmd, "alloc") == 0 ||
817            strcasecmp (cmd, "allocate") == 0)
818     r = do_alloc (cmd, argc, argv);
819   else if (strcasecmp (cmd, "echo") == 0)
820     r = do_echo (cmd, argc, argv);
821   else if (strcasecmp (cmd, "edit") == 0 ||
822            strcasecmp (cmd, "vi") == 0 ||
823            strcasecmp (cmd, "emacs") == 0)
824     r = do_edit (cmd, argc, argv);
825   else if (strcasecmp (cmd, "lcd") == 0)
826     r = do_lcd (cmd, argc, argv);
827   else if (strcasecmp (cmd, "glob") == 0)
828     r = do_glob (cmd, argc, argv);
829   else if (strcasecmp (cmd, "more") == 0 ||
830            strcasecmp (cmd, "less") == 0)
831     r = do_more (cmd, argc, argv);
832   else if (strcasecmp (cmd, "reopen") == 0)
833     r = do_reopen (cmd, argc, argv);
834   else if (strcasecmp (cmd, "time") == 0)
835     r = do_time (cmd, argc, argv);
836   else
837     r = run_action (cmd, argc, argv);
838
839   /* Always flush stdout after every command, so that messages, results
840    * etc appear immediately.
841    */
842   if (fflush (stdout) == EOF) {
843     perror ("failed to flush standard output");
844     return -1;
845   }
846
847   if (pipecmd) {
848     close (1);
849     if (dup2 (stdout_saved_fd, 1) < 0) {
850       perror ("failed to dup2 standard output");
851       r = -1;
852     }
853     close (stdout_saved_fd);
854     if (waitpid (pid, NULL, 0) < 0) {
855       perror ("waiting for command to complete");
856       r = -1;
857     }
858   }
859
860   return r;
861 }
862
863 void
864 list_builtin_commands (void)
865 {
866   /* help and quit should appear at the top */
867   printf ("%-20s %s\n",
868           "help", _("display a list of commands or help on a command"));
869   printf ("%-20s %s\n",
870           "quit", _("quit guestfish"));
871
872   printf ("%-20s %s\n",
873           "alloc", _("allocate an image"));
874   printf ("%-20s %s\n",
875           "echo", _("display a line of text"));
876   printf ("%-20s %s\n",
877           "edit", _("edit a file in the image"));
878   printf ("%-20s %s\n",
879           "lcd", _("local change directory"));
880   printf ("%-20s %s\n",
881           "glob", _("expand wildcards in command"));
882   printf ("%-20s %s\n",
883           "more", _("view a file in the pager"));
884   printf ("%-20s %s\n",
885           "reopen", _("close and reopen libguestfs handle"));
886   printf ("%-20s %s\n",
887           "time", _("measure time taken to run command"));
888
889   /* actions are printed after this (see list_commands) */
890 }
891
892 void
893 display_builtin_command (const char *cmd)
894 {
895   /* help for actions is auto-generated, see display_command */
896
897   if (strcasecmp (cmd, "alloc") == 0 ||
898       strcasecmp (cmd, "allocate") == 0)
899     printf (_("alloc - allocate an image\n"
900               "     alloc <filename> <size>\n"
901               "\n"
902               "    This creates an empty (zeroed) file of the given size,\n"
903               "    and then adds so it can be further examined.\n"
904               "\n"
905               "    For more advanced image creation, see qemu-img utility.\n"
906               "\n"
907               "    Size can be specified (where <nn> means a number):\n"
908               "    <nn>             number of kilobytes\n"
909               "      eg: 1440       standard 3.5\" floppy\n"
910               "    <nn>K or <nn>KB  number of kilobytes\n"
911               "    <nn>M or <nn>MB  number of megabytes\n"
912               "    <nn>G or <nn>GB  number of gigabytes\n"
913               "    <nn>sects        number of 512 byte sectors\n"));
914   else if (strcasecmp (cmd, "echo") == 0)
915     printf (_("echo - display a line of text\n"
916               "     echo [<params> ...]\n"
917               "\n"
918               "    This echos the parameters to the terminal.\n"));
919   else if (strcasecmp (cmd, "edit") == 0 ||
920            strcasecmp (cmd, "vi") == 0 ||
921            strcasecmp (cmd, "emacs") == 0)
922     printf (_("edit - edit a file in the image\n"
923               "     edit <filename>\n"
924               "\n"
925               "    This is used to edit a file.\n"
926               "\n"
927               "    It is the equivalent of (and is implemented by)\n"
928               "    running \"cat\", editing locally, and then \"write-file\".\n"
929               "\n"
930               "    Normally it uses $EDITOR, but if you use the aliases\n"
931               "    \"vi\" or \"emacs\" you will get those editors.\n"
932               "\n"
933               "    NOTE: This will not work reliably for large files\n"
934               "    (> 2 MB) or binary files containing \\0 bytes.\n"));
935   else if (strcasecmp (cmd, "lcd") == 0)
936     printf (_("lcd - local change directory\n"
937               "    lcd <directory>\n"
938               "\n"
939               "    Change guestfish's current directory. This command is\n"
940               "    useful if you want to download files to a particular\n"
941               "    place.\n"));
942   else if (strcasecmp (cmd, "glob") == 0)
943     printf (_("glob - expand wildcards in command\n"
944               "    glob <command> [<args> ...]\n"
945               "\n"
946               "    Glob runs <command> with wildcards expanded in any\n"
947               "    command args.  Note that the command is run repeatedly\n"
948               "    once for each expanded argument.\n"));
949   else if (strcasecmp (cmd, "help") == 0)
950     printf (_("help - display a list of commands or help on a command\n"
951               "     help cmd\n"
952               "     help\n"));
953   else if (strcasecmp (cmd, "more") == 0 ||
954            strcasecmp (cmd, "less") == 0)
955     printf (_("more - view a file in the pager\n"
956               "     more <filename>\n"
957               "\n"
958               "    This is used to view a file in the pager.\n"
959               "\n"
960               "    It is the equivalent of (and is implemented by)\n"
961               "    running \"cat\" and using the pager.\n"
962               "\n"
963               "    Normally it uses $PAGER, but if you use the alias\n"
964               "    \"less\" then it always uses \"less\".\n"
965               "\n"
966               "    NOTE: This will not work reliably for large files\n"
967               "    (> 2 MB) or binary files containing \\0 bytes.\n"));
968   else if (strcasecmp (cmd, "quit") == 0 ||
969            strcasecmp (cmd, "exit") == 0 ||
970            strcasecmp (cmd, "q") == 0)
971     printf (_("quit - quit guestfish\n"
972               "     quit\n"));
973   else if (strcasecmp (cmd, "reopen") == 0)
974     printf (_("reopen - close and reopen the libguestfs handle\n"
975               "     reopen\n"
976               "\n"
977               "Close and reopen the libguestfs handle.  It is not necessary to use\n"
978               "this normally, because the handle is closed properly when guestfish\n"
979               "exits.  However this is occasionally useful for testing.\n"));
980   else if (strcasecmp (cmd, "time") == 0)
981     printf (_("time - measure time taken to run command\n"
982               "    time <command> [<args> ...]\n"
983               "\n"
984               "    This runs <command> as usual, and prints the elapsed\n"
985               "    time afterwards.\n"));
986   else
987     fprintf (stderr, _("%s: command not known, use -h to list all commands\n"),
988              cmd);
989 }
990
991 void
992 free_strings (char **argv)
993 {
994   int argc;
995
996   for (argc = 0; argv[argc] != NULL; ++argc)
997     free (argv[argc]);
998   free (argv);
999 }
1000
1001 int
1002 count_strings (char *const *argv)
1003 {
1004   int c;
1005
1006   for (c = 0; argv[c]; ++c)
1007     ;
1008   return c;
1009 }
1010
1011 void
1012 print_strings (char *const *argv)
1013 {
1014   int argc;
1015
1016   for (argc = 0; argv[argc] != NULL; ++argc)
1017     printf ("%s\n", argv[argc]);
1018 }
1019
1020 void
1021 print_table (char *const *argv)
1022 {
1023   int i;
1024
1025   for (i = 0; argv[i] != NULL; i += 2)
1026     printf ("%s: %s\n", argv[i], argv[i+1]);
1027 }
1028
1029 int
1030 is_true (const char *str)
1031 {
1032   return
1033     strcasecmp (str, "0") != 0 &&
1034     strcasecmp (str, "f") != 0 &&
1035     strcasecmp (str, "false") != 0 &&
1036     strcasecmp (str, "n") != 0 &&
1037     strcasecmp (str, "no") != 0;
1038 }
1039
1040 /* XXX We could improve list parsing. */
1041 char **
1042 parse_string_list (const char *str)
1043 {
1044   char **argv;
1045   const char *p, *pend;
1046   int argc, i;
1047
1048   argc = 1;
1049   for (i = 0; str[i]; ++i)
1050     if (str[i] == ' ') argc++;
1051
1052   argv = malloc (sizeof (char *) * (argc+1));
1053   if (argv == NULL) { perror ("malloc"); exit (1); }
1054
1055   p = str;
1056   i = 0;
1057   while (*p) {
1058     pend = strchrnul (p, ' ');
1059     argv[i] = strndup (p, pend-p);
1060     i++;
1061     p = *pend == ' ' ? pend+1 : pend;
1062   }
1063   argv[i] = NULL;
1064
1065   return argv;
1066 }
1067
1068 #ifdef HAVE_LIBREADLINE
1069 static char histfile[1024];
1070 static int nr_history_lines = 0;
1071 #endif
1072
1073 static void
1074 initialize_readline (void)
1075 {
1076 #ifdef HAVE_LIBREADLINE
1077   const char *home;
1078
1079   home = getenv ("HOME");
1080   if (home) {
1081     snprintf (histfile, sizeof histfile, "%s/.guestfish", home);
1082     using_history ();
1083     (void) read_history (histfile);
1084   }
1085
1086   rl_readline_name = "guestfish";
1087   rl_attempted_completion_function = do_completion;
1088 #endif
1089 }
1090
1091 static void
1092 cleanup_readline (void)
1093 {
1094 #ifdef HAVE_LIBREADLINE
1095   int fd;
1096
1097   if (histfile[0] != '\0') {
1098     fd = open (histfile, O_WRONLY|O_CREAT, 0644);
1099     if (fd == -1) {
1100       perror (histfile);
1101       return;
1102     }
1103     close (fd);
1104
1105     (void) append_history (nr_history_lines, histfile);
1106   }
1107 #endif
1108 }
1109
1110 static void
1111 add_history_line (const char *line)
1112 {
1113 #ifdef HAVE_LIBREADLINE
1114   add_history (line);
1115   nr_history_lines++;
1116 #endif
1117 }
1118
1119 int
1120 xwrite (int fd, const void *buf, size_t len)
1121 {
1122   int r;
1123
1124   while (len > 0) {
1125     r = write (fd, buf, len);
1126     if (r == -1) {
1127       perror ("write");
1128       return -1;
1129     }
1130     buf += r;
1131     len -= r;
1132   }
1133
1134   return 0;
1135 }