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