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