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