Guestfish feature: remote control of guestfish over a pipe.
[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     /*printf ("%s\n", cmd);*/
344
345     r = system (cmd);
346     if (r == -1) {
347       perror ("system");
348       exit (1);
349     }
350     exit (WEXITSTATUS (r));
351   }
352
353   /* If we've got drives to add, add them now. */
354   add_drives (drvs);
355
356   /* If we've got mountpoints, we must launch the guest and mount them. */
357   if (mps != NULL) {
358     if (launch (g) == -1) exit (1);
359     mount_mps (mps);
360   }
361
362   /* Remote control? */
363   if (remote_control_listen && remote_control) {
364     fprintf (stderr, _("guestfish: cannot use --listen and --remote options at the same time\n"));
365     exit (1);
366   }
367
368   if (remote_control_listen) {
369     if (optind < argc) {
370       fprintf (stderr, _("guestfish: extra parameters on the command line with --listen flag\n"));
371       exit (1);
372     }
373     if (file) {
374       fprintf (stderr, _("guestfish: cannot use --listen and --file options at the same time\n"));
375       exit (1);
376     }
377     rc_listen ();
378   }
379
380   /* -f (file) parameter? */
381   if (file) {
382     close (0);
383     if (open (file, O_RDONLY) == -1) {
384       perror (file);
385       exit (1);
386     }
387   }
388
389   /* Interactive, shell script, or command(s) on the command line? */
390   if (optind >= argc) {
391     if (isatty (0))
392       interactive ();
393     else
394       shell_script ();
395   }
396   else
397     cmdline (argv, optind, argc);
398
399   cleanup_readline ();
400
401   exit (0);
402 }
403
404 void
405 pod2text (const char *heading, const char *str)
406 {
407   FILE *fp;
408
409   fp = popen ("pod2text", "w");
410   if (fp == NULL) {
411     /* pod2text failed, maybe not found, so let's just print the
412      * source instead, since that's better than doing nothing.
413      */
414     printf ("%s\n\n%s\n", heading, str);
415     return;
416   }
417   fputs ("=head1 ", fp);
418   fputs (heading, fp);
419   fputs ("\n\n", fp);
420   fputs (str, fp);
421   pclose (fp);
422 }
423
424 /* List is built in reverse order, so mount them in reverse order. */
425 static void
426 mount_mps (struct mp *mp)
427 {
428   int r;
429
430   if (mp) {
431     mount_mps (mp->next);
432     if (!read_only)
433       r = guestfs_mount (g, mp->device, mp->mountpoint);
434     else
435       r = guestfs_mount_ro (g, mp->device, mp->mountpoint);
436     if (r == -1)
437       exit (1);
438   }
439 }
440
441 static void
442 add_drives (struct drv *drv)
443 {
444   int r;
445
446   if (drv) {
447     add_drives (drv->next);
448     if (!read_only)
449       r = guestfs_add_drive (g, drv->filename);
450     else
451       r = guestfs_add_drive_ro (g, drv->filename);
452     if (r == -1)
453       exit (1);
454   }
455 }
456
457 static void
458 interactive (void)
459 {
460   script (1);
461 }
462
463 static void
464 shell_script (void)
465 {
466   script (0);
467 }
468
469 #define FISH "><fs> "
470
471 static char *line_read = NULL;
472
473 static char *
474 rl_gets (int prompt)
475 {
476 #ifdef HAVE_LIBREADLINE
477
478   if (prompt) {
479     if (line_read) {
480       free (line_read);
481       line_read = NULL;
482     }
483
484     line_read = readline (prompt ? FISH : "");
485
486     if (line_read && *line_read)
487       add_history_line (line_read);
488
489     return line_read;
490   }
491
492 #endif /* HAVE_LIBREADLINE */
493
494   static char buf[8192];
495   int len;
496
497   if (prompt) printf (FISH);
498   line_read = fgets (buf, sizeof buf, stdin);
499
500   if (line_read) {
501     len = strlen (line_read);
502     if (len > 0 && buf[len-1] == '\n') buf[len-1] = '\0';
503   }
504
505   return line_read;
506 }
507
508 static void
509 script (int prompt)
510 {
511   char *buf;
512   char *cmd;
513   char *p, *pend;
514   char *argv[64];
515   int i, len;
516   int global_exit_on_error = !prompt;
517
518   if (prompt)
519     printf (_("\n"
520               "Welcome to guestfish, the libguestfs filesystem interactive shell for\n"
521               "editing virtual machine filesystems.\n"
522               "\n"
523               "Type: 'help' for help with commands\n"
524               "      'quit' to quit the shell\n"
525               "\n"));
526
527   while (!quit) {
528     char *pipe = NULL;
529
530     exit_on_error = global_exit_on_error;
531
532     buf = rl_gets (prompt);
533     if (!buf) {
534       quit = 1;
535       break;
536     }
537
538     /* Skip any initial whitespace before the command. */
539   again:
540     while (*buf && isspace (*buf))
541       buf++;
542
543     if (!*buf) continue;
544
545     /* If the next character is '#' then this is a comment. */
546     if (*buf == '#') continue;
547
548     /* If the next character is '!' then pass the whole lot to system(3). */
549     if (*buf == '!') {
550       int r;
551
552       r = system (buf+1);
553       if (exit_on_error) {
554         if (r == -1 ||
555             (WIFSIGNALED (r) &&
556              (WTERMSIG (r) == SIGINT || WTERMSIG (r) == SIGQUIT)) ||
557             WEXITSTATUS (r) != 0)
558           exit (1);
559       }
560       continue;
561     }
562
563     /* If the next character is '-' allow the command to fail without
564      * exiting on error (just for this one command though).
565      */
566     if (*buf == '-') {
567       exit_on_error = 0;
568       buf++;
569       goto again;
570     }
571
572     /* Get the command (cannot be quoted). */
573     len = strcspn (buf, " \t");
574
575     if (len == 0) continue;
576
577     cmd = buf;
578     i = 0;
579     if (buf[len] == '\0') {
580       argv[0] = NULL;
581       goto got_command;
582     }
583
584     buf[len] = '\0';
585     p = &buf[len+1];
586     p += strspn (p, " \t");
587
588     /* Get the parameters. */
589     while (*p && i < sizeof argv / sizeof argv[0]) {
590       /* Parameters which start with quotes or pipes are treated
591        * specially.  Bare parameters are delimited by whitespace.
592        */
593       if (*p == '"') {
594         p++;
595         len = strcspn (p, "\"");
596         if (p[len] == '\0') {
597           fprintf (stderr, _("guestfish: unterminated double quote\n"));
598           if (exit_on_error) exit (1);
599           goto next_command;
600         }
601         if (p[len+1] && (p[len+1] != ' ' && p[len+1] != '\t')) {
602           fprintf (stderr, _("guestfish: command arguments not separated by whitespace\n"));
603           if (exit_on_error) exit (1);
604           goto next_command;
605         }
606         p[len] = '\0';
607         pend = p[len+1] ? &p[len+2] : &p[len+1];
608       } else if (*p == '\'') {
609         p++;
610         len = strcspn (p, "'");
611         if (p[len] == '\0') {
612           fprintf (stderr, _("guestfish: unterminated single quote\n"));
613           if (exit_on_error) exit (1);
614           goto next_command;
615         }
616         if (p[len+1] && (p[len+1] != ' ' && p[len+1] != '\t')) {
617           fprintf (stderr, _("guestfish: command arguments not separated by whitespace\n"));
618           if (exit_on_error) exit (1);
619           goto next_command;
620         }
621         p[len] = '\0';
622         pend = p[len+1] ? &p[len+2] : &p[len+1];
623       } else if (*p == '|') {
624         *p = '\0';
625         pipe = p+1;
626         continue;
627         /*
628       } else if (*p == '[') {
629         int c = 1;
630         p++;
631         pend = p;
632         while (*pend && c != 0) {
633           if (*pend == '[') c++;
634           else if (*pend == ']') c--;
635           pend++;
636         }
637         if (c != 0) {
638           fprintf (stderr, _("guestfish: unterminated \"[...]\" sequence\n"));
639           if (exit_on_error) exit (1);
640           goto next_command;
641         }
642         if (*pend && (*pend != ' ' && *pend != '\t')) {
643           fprintf (stderr, _("guestfish: command arguments not separated by whitespace\n"));
644           if (exit_on_error) exit (1);
645           goto next_command;
646         }
647         *(pend-1) = '\0';
648         */
649       } else if (*p != ' ' && *p != '\t') {
650         len = strcspn (p, " \t");
651         if (p[len]) {
652           p[len] = '\0';
653           pend = &p[len+1];
654         } else
655           pend = &p[len];
656       } else {
657         fprintf (stderr, _("guestfish: internal error parsing string at '%s'\n"),
658                  p);
659         abort ();
660       }
661
662       argv[i++] = p;
663       p = pend;
664
665       if (*p)
666         p += strspn (p, " \t");
667     }
668
669     if (i == sizeof argv / sizeof argv[0]) {
670       fprintf (stderr, _("guestfish: too many arguments\n"));
671       if (exit_on_error) exit (1);
672       goto next_command;
673     }
674
675     argv[i] = NULL;
676
677   got_command:
678     if (issue_command (cmd, argv, pipe) == -1) {
679       if (exit_on_error) exit (1);
680     }
681
682   next_command:;
683   }
684   if (prompt) printf ("\n");
685 }
686
687 static void
688 cmdline (char *argv[], int optind, int argc)
689 {
690   const char *cmd;
691   char **params;
692
693   exit_on_error = 1;
694
695   if (optind >= argc) return;
696
697   cmd = argv[optind++];
698   if (strcmp (cmd, ":") == 0) {
699     fprintf (stderr, _("guestfish: empty command on command line\n"));
700     exit (1);
701   }
702   params = &argv[optind];
703
704   /* Search for end of command list or ":" ... */
705   while (optind < argc && strcmp (argv[optind], ":") != 0)
706     optind++;
707
708   if (optind == argc) {
709     if (issue_command (cmd, params, NULL) == -1) exit (1);
710   } else {
711     argv[optind] = NULL;
712     if (issue_command (cmd, params, NULL) == -1) exit (1);
713     cmdline (argv, optind+1, argc);
714   }
715 }
716
717 int
718 issue_command (const char *cmd, char *argv[], const char *pipecmd)
719 {
720   int argc;
721   int stdout_saved_fd = -1;
722   int pid = 0;
723   int i, r;
724
725   if (echo_commands) {
726     printf ("%s", cmd);
727     for (i = 0; argv[i] != NULL; ++i)
728       printf (" %s", argv[i]);
729     printf ("\n");
730   }
731
732   /* For | ... commands.  Annoyingly we can't use popen(3) here. */
733   if (pipecmd) {
734     int fd[2];
735
736     fflush (stdout);
737     pipe (fd);
738     pid = fork ();
739     if (pid == -1) {
740       perror ("fork");
741       return -1;
742     }
743
744     if (pid == 0) {             /* Child process. */
745       close (fd[1]);
746       dup2 (fd[0], 0);
747
748       r = system (pipecmd);
749       if (r == -1) {
750         perror (pipecmd);
751         _exit (1);
752       }
753       _exit (WEXITSTATUS (r));
754     }
755
756     stdout_saved_fd = dup (1);
757     close (fd[0]);
758     dup2 (fd[1], 1);
759     close (fd[1]);
760   }
761
762   for (argc = 0; argv[argc] != NULL; ++argc)
763     ;
764
765   /* If --remote was set, then send this command to a remote process. */
766   if (remote_control)
767     r = rc_remote (remote_control, cmd, argc, argv, exit_on_error);
768
769   /* Otherwise execute it locally. */
770   else if (strcasecmp (cmd, "help") == 0) {
771     if (argc == 0)
772       list_commands ();
773     else
774       display_command (argv[0]);
775     r = 0;
776   }
777   else if (strcasecmp (cmd, "quit") == 0 ||
778            strcasecmp (cmd, "exit") == 0 ||
779            strcasecmp (cmd, "q") == 0) {
780     quit = 1;
781     r = 0;
782   }
783   else if (strcasecmp (cmd, "alloc") == 0 ||
784            strcasecmp (cmd, "allocate") == 0)
785     r = do_alloc (cmd, argc, argv);
786   else if (strcasecmp (cmd, "echo") == 0)
787     r = do_echo (cmd, argc, argv);
788   else if (strcasecmp (cmd, "edit") == 0 ||
789            strcasecmp (cmd, "vi") == 0 ||
790            strcasecmp (cmd, "emacs") == 0)
791     r = do_edit (cmd, argc, argv);
792   else if (strcasecmp (cmd, "lcd") == 0)
793     r = do_lcd (cmd, argc, argv);
794   else if (strcasecmp (cmd, "glob") == 0)
795     r = do_glob (cmd, argc, argv);
796   else if (strcasecmp (cmd, "more") == 0 ||
797            strcasecmp (cmd, "less") == 0)
798     r = do_more (cmd, argc, argv);
799   else if (strcasecmp (cmd, "reopen") == 0)
800     r = do_reopen (cmd, argc, argv);
801   else if (strcasecmp (cmd, "time") == 0)
802     r = do_time (cmd, argc, argv);
803   else
804     r = run_action (cmd, argc, argv);
805
806   /* Always flush stdout after every command, so that messages, results
807    * etc appear immediately.
808    */
809   fflush (stdout);
810
811   if (pipecmd) {
812     close (1);
813     dup2 (stdout_saved_fd, 1);
814     close (stdout_saved_fd);
815     waitpid (pid, NULL, 0);
816   }
817
818   return r;
819 }
820
821 void
822 list_builtin_commands (void)
823 {
824   /* help and quit should appear at the top */
825   printf ("%-20s %s\n",
826           "help", _("display a list of commands or help on a command"));
827   printf ("%-20s %s\n",
828           "quit", _("quit guestfish"));
829
830   printf ("%-20s %s\n",
831           "alloc", _("allocate an image"));
832   printf ("%-20s %s\n",
833           "echo", _("display a line of text"));
834   printf ("%-20s %s\n",
835           "edit", _("edit a file in the image"));
836   printf ("%-20s %s\n",
837           "lcd", _("local change directory"));
838   printf ("%-20s %s\n",
839           "glob", _("expand wildcards in command"));
840   printf ("%-20s %s\n",
841           "more", _("view a file in the pager"));
842   printf ("%-20s %s\n",
843           "reopen", _("close and reopen libguestfs handle"));
844   printf ("%-20s %s\n",
845           "time", _("measure time taken to run command"));
846
847   /* actions are printed after this (see list_commands) */
848 }
849
850 void
851 display_builtin_command (const char *cmd)
852 {
853   /* help for actions is auto-generated, see display_command */
854
855   if (strcasecmp (cmd, "alloc") == 0 ||
856       strcasecmp (cmd, "allocate") == 0)
857     printf (_("alloc - allocate an image\n"
858               "     alloc <filename> <size>\n"
859               "\n"
860               "    This creates an empty (zeroed) file of the given size,\n"
861               "    and then adds so it can be further examined.\n"
862               "\n"
863               "    For more advanced image creation, see qemu-img utility.\n"
864               "\n"
865               "    Size can be specified (where <nn> means a number):\n"
866               "    <nn>             number of kilobytes\n"
867               "      eg: 1440       standard 3.5\" floppy\n"
868               "    <nn>K or <nn>KB  number of kilobytes\n"
869               "    <nn>M or <nn>MB  number of megabytes\n"
870               "    <nn>G or <nn>GB  number of gigabytes\n"
871               "    <nn>sects        number of 512 byte sectors\n"));
872   else if (strcasecmp (cmd, "echo") == 0)
873     printf (_("echo - display a line of text\n"
874               "     echo [<params> ...]\n"
875               "\n"
876               "    This echos the parameters to the terminal.\n"));
877   else if (strcasecmp (cmd, "edit") == 0 ||
878            strcasecmp (cmd, "vi") == 0 ||
879            strcasecmp (cmd, "emacs") == 0)
880     printf (_("edit - edit a file in the image\n"
881               "     edit <filename>\n"
882               "\n"
883               "    This is used to edit a file.\n"
884               "\n"
885               "    It is the equivalent of (and is implemented by)\n"
886               "    running \"cat\", editing locally, and then \"write-file\".\n"
887               "\n"
888               "    Normally it uses $EDITOR, but if you use the aliases\n"
889               "    \"vi\" or \"emacs\" you will get those editors.\n"
890               "\n"
891               "    NOTE: This will not work reliably for large files\n"
892               "    (> 2 MB) or binary files containing \\0 bytes.\n"));
893   else if (strcasecmp (cmd, "lcd") == 0)
894     printf (_("lcd - local change directory\n"
895               "    lcd <directory>\n"
896               "\n"
897               "    Change guestfish's current directory. This command is\n"
898               "    useful if you want to download files to a particular\n"
899               "    place.\n"));
900   else if (strcasecmp (cmd, "glob") == 0)
901     printf (_("glob - expand wildcards in command\n"
902               "    glob <command> [<args> ...]\n"
903               "\n"
904               "    Glob runs <command> with wildcards expanded in any\n"
905               "    command args.  Note that the command is run repeatedly\n"
906               "    once for each expanded argument.\n"));
907   else if (strcasecmp (cmd, "help") == 0)
908     printf (_("help - display a list of commands or help on a command\n"
909               "     help cmd\n"
910               "     help\n"));
911   else if (strcasecmp (cmd, "more") == 0 ||
912            strcasecmp (cmd, "less") == 0)
913     printf (_("more - view a file in the pager\n"
914               "     more <filename>\n"
915               "\n"
916               "    This is used to view a file in the pager.\n"
917               "\n"
918               "    It is the equivalent of (and is implemented by)\n"
919               "    running \"cat\" and using the pager.\n"
920               "\n"
921               "    Normally it uses $PAGER, but if you use the alias\n"
922               "    \"less\" then it always uses \"less\".\n"
923               "\n"
924               "    NOTE: This will not work reliably for large files\n"
925               "    (> 2 MB) or binary files containing \\0 bytes.\n"));
926   else if (strcasecmp (cmd, "quit") == 0 ||
927            strcasecmp (cmd, "exit") == 0 ||
928            strcasecmp (cmd, "q") == 0)
929     printf (_("quit - quit guestfish\n"
930               "     quit\n"));
931   else if (strcasecmp (cmd, "reopen") == 0)
932     printf (_("reopen - close and reopen the libguestfs handle\n"
933               "     reopen\n"
934               "\n"
935               "Close and reopen the libguestfs handle.  It is not necessary to use\n"
936               "this normally, because the handle is closed properly when guestfish\n"
937               "exits.  However this is occasionally useful for testing.\n"));
938   else if (strcasecmp (cmd, "time") == 0)
939     printf (_("time - measure time taken to run command\n"
940               "    time <command> [<args> ...]\n"
941               "\n"
942               "    This runs <command> as usual, and prints the elapsed\n"
943               "    time afterwards.\n"));
944   else
945     fprintf (stderr, _("%s: command not known, use -h to list all commands\n"),
946              cmd);
947 }
948
949 void
950 free_strings (char **argv)
951 {
952   int argc;
953
954   for (argc = 0; argv[argc] != NULL; ++argc)
955     free (argv[argc]);
956   free (argv);
957 }
958
959 int
960 count_strings (char * const * const argv)
961 {
962   int c;
963
964   for (c = 0; argv[c]; ++c)
965     ;
966   return c;
967 }
968
969 void
970 print_strings (char * const * const argv)
971 {
972   int argc;
973
974   for (argc = 0; argv[argc] != NULL; ++argc)
975     printf ("%s\n", argv[argc]);
976 }
977
978 void
979 print_table (char * const * const argv)
980 {
981   int i;
982
983   for (i = 0; argv[i] != NULL; i += 2)
984     printf ("%s: %s\n", argv[i], argv[i+1]);
985 }
986
987 int
988 is_true (const char *str)
989 {
990   return
991     strcasecmp (str, "0") != 0 &&
992     strcasecmp (str, "f") != 0 &&
993     strcasecmp (str, "false") != 0 &&
994     strcasecmp (str, "n") != 0 &&
995     strcasecmp (str, "no") != 0;
996 }
997
998 /* XXX We could improve list parsing. */
999 char **
1000 parse_string_list (const char *str)
1001 {
1002   char **argv;
1003   const char *p, *pend;
1004   int argc, i;
1005
1006   argc = 1;
1007   for (i = 0; str[i]; ++i)
1008     if (str[i] == ' ') argc++;
1009
1010   argv = malloc (sizeof (char *) * (argc+1));
1011   if (argv == NULL) { perror ("malloc"); exit (1); }
1012
1013   p = str;
1014   i = 0;
1015   while (*p) {
1016     pend = strchrnul (p, ' ');
1017     argv[i] = strndup (p, pend-p);
1018     i++;
1019     p = *pend == ' ' ? pend+1 : pend;
1020   }
1021   argv[i] = NULL;
1022
1023   return argv;
1024 }
1025
1026 #ifdef HAVE_LIBREADLINE
1027 static char histfile[1024];
1028 static int nr_history_lines = 0;
1029 #endif
1030
1031 static void
1032 initialize_readline (void)
1033 {
1034 #ifdef HAVE_LIBREADLINE
1035   const char *home;
1036
1037   home = getenv ("HOME");
1038   if (home) {
1039     snprintf (histfile, sizeof histfile, "%s/.guestfish", home);
1040     using_history ();
1041     (void) read_history (histfile);
1042   }
1043
1044   rl_readline_name = "guestfish";
1045   rl_attempted_completion_function = do_completion;
1046 #endif
1047 }
1048
1049 static void
1050 cleanup_readline (void)
1051 {
1052 #ifdef HAVE_LIBREADLINE
1053   int fd;
1054
1055   if (histfile[0] != '\0') {
1056     fd = open (histfile, O_WRONLY|O_CREAT, 0644);
1057     if (fd == -1) {
1058       perror (histfile);
1059       return;
1060     }
1061     close (fd);
1062
1063     (void) append_history (nr_history_lines, histfile);
1064   }
1065 #endif
1066 }
1067
1068 static void
1069 add_history_line (const char *line)
1070 {
1071 #ifdef HAVE_LIBREADLINE
1072   add_history (line);
1073   nr_history_lines++;
1074 #endif
1075 }
1076
1077 int
1078 xwrite (int fd, const void *buf, size_t len)
1079 {
1080   int r;
1081
1082   while (len > 0) {
1083     r = write (fd, buf, len);
1084     if (r == -1) {
1085       perror ("write");
1086       return -1;
1087     }
1088     buf += r;
1089     len -= r;
1090   }
1091
1092   return 0;
1093 }