1 /* guestfish - the filesystem interactive shell
2 * Copyright (C) 2009 Red Hat Inc.
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.
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.
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.
21 #define _GNU_SOURCE // for strchrnul
32 #include <sys/types.h>
35 #ifdef HAVE_LIBREADLINE
36 #include <readline/readline.h>
37 #include <readline/history.h>
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 *);
65 /* Currently open libguestfs handle. */
73 launch (guestfs_h *_g)
77 if (guestfs_is_config (g)) {
78 if (guestfs_launch (g) == -1)
80 if (guestfs_wait_ready (g) == -1)
90 _("guestfish: guest filesystem shell\n"
91 "guestfish lets you edit virtual machine filesystems\n"
92 "Copyright (C) 2009 Red Hat Inc.\n"
94 " guestfish [--options] cmd [: cmd : cmd ...]\n"
95 " guestfish -i libvirt-domain\n"
96 " guestfish -i disk-image(s)\n"
97 "or for interactive use:\n"
99 "or from a shell script:\n"
105 " -h|--cmd-help List available commands\n"
106 " -h|--cmd-help cmd Display detailed help on 'cmd'\n"
107 " -a|--add image Add image\n"
108 " -D|--no-dest-paths Don't tab-complete paths from guest fs\n"
109 " -f|--file file Read commands from file\n"
110 " -i|--inspector Run virt-inspector to get disk mountpoints\n"
111 " -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n"
112 " -n|--no-sync Don't autosync\n"
113 " -r|--ro Mount read-only\n"
114 " -v|--verbose Verbose messages\n"
115 " -V|--version Display version and exit\n"
116 "For more information, see the manpage guestfish(1).\n"));
120 main (int argc, char *argv[])
122 static const char *options = "a:Df:h::im:nrv?V";
123 static struct option long_options[] = {
124 { "add", 1, 0, 'a' },
125 { "cmd-help", 2, 0, 'h' },
126 { "file", 1, 0, 'f' },
127 { "help", 0, 0, '?' },
128 { "inspector", 0, 0, 'i' },
129 { "mount", 1, 0, 'm' },
130 { "no-dest-paths", 0, 0, 'D' },
131 { "no-sync", 0, 0, 'n' },
133 { "verbose", 0, 0, 'v' },
134 { "version", 0, 0, 'V' },
137 struct drv *drvs = NULL;
139 struct mp *mps = NULL;
141 char *p, *file = NULL;
142 int c, inspector = 0;
145 initialize_readline ();
147 memset (&sa, 0, sizeof sa);
148 sa.sa_handler = SIG_IGN;
149 sa.sa_flags = SA_RESTART;
150 sigaction (SIGPIPE, &sa, NULL);
152 /* guestfs_create is meant to be a lightweight operation, so
153 * it's OK to do it early here.
155 g = guestfs_create ();
157 fprintf (stderr, _("guestfs_create: failed to create handle\n"));
161 guestfs_set_autosync (g, 1);
163 /* If developing, add ./appliance to the path. Note that libtools
164 * interferes with this because uninstalled guestfish is a shell
165 * script that runs the real program with an absolute path. Detect
168 * BUT if LIBGUESTFS_PATH environment variable is already set by
169 * the user, then don't override it.
171 if (getenv ("LIBGUESTFS_PATH") == NULL &&
173 (argv[0][0] != '/' || strstr (argv[0], "/.libs/lt-") != NULL))
174 guestfs_set_path (g, "appliance:" GUESTFS_DEFAULT_PATH);
177 c = getopt_long (argc, argv, options, long_options, NULL);
182 if (access (optarg, R_OK) != 0) {
186 drv = malloc (sizeof (struct drv));
191 drv->filename = optarg;
197 complete_dest_paths = 0;
202 fprintf (stderr, _("guestfish: only one -f parameter can be given\n"));
210 display_command (optarg);
211 else if (argv[optind] && argv[optind][0] != '-')
212 display_command (argv[optind++]);
222 mp = malloc (sizeof (struct mp));
227 p = strchr (optarg, ':');
230 mp->mountpoint = p+1;
232 mp->mountpoint = "/";
239 guestfs_set_autosync (g, 0);
248 guestfs_set_verbose (g, verbose);
252 printf ("guestfish %s\n", PACKAGE_VERSION);
260 fprintf (stderr, _("guestfish: unexpected command line option 0x%x\n"),
266 /* Inspector mode invalidates most of the other arguments. */
272 fprintf (stderr, _("guestfish: cannot use -i option with -a or -m\n"));
275 if (optind >= argc) {
276 fprintf (stderr, _("guestfish -i requires a libvirt domain or path(s) to disk image(s)\n"));
280 strcpy (cmd, "a=`virt-inspector");
281 while (optind < argc) {
282 if (strlen (cmd) + strlen (argv[optind]) + strlen (argv[0]) + 60
284 fprintf (stderr, _("guestfish: virt-inspector command too long for fixed-size buffer\n"));
288 strcat (cmd, argv[optind]);
294 strcat (cmd, " --ro-fish");
296 strcat (cmd, " --fish");
298 sprintf (&cmd[strlen(cmd)], "` && %s $a", argv[0]);
300 if (guestfs_get_verbose (g))
302 if (!guestfs_get_autosync (g))
305 /*printf ("%s\n", cmd);*/
312 exit (WEXITSTATUS (r));
315 /* If we've got drives to add, add them now. */
318 /* If we've got mountpoints, we must launch the guest and mount them. */
320 if (launch (g) == -1) exit (1);
324 /* -f (file) parameter? */
327 if (open (file, O_RDONLY) == -1) {
333 /* Interactive, shell script, or command(s) on the command line? */
334 if (optind >= argc) {
341 cmdline (argv, optind, argc);
349 pod2text (const char *heading, const char *str)
353 fp = popen ("pod2text", "w");
355 /* pod2text failed, maybe not found, so let's just print the
356 * source instead, since that's better than doing nothing.
358 printf ("%s\n\n%s\n", heading, str);
361 fputs ("=head1 ", fp);
368 /* List is built in reverse order, so mount them in reverse order. */
370 mount_mps (struct mp *mp)
375 mount_mps (mp->next);
377 r = guestfs_mount (g, mp->device, mp->mountpoint);
379 r = guestfs_mount_ro (g, mp->device, mp->mountpoint);
386 add_drives (struct drv *drv)
391 add_drives (drv->next);
393 r = guestfs_add_drive (g, drv->filename);
395 r = guestfs_add_drive_ro (g, drv->filename);
413 #define FISH "><fs> "
415 static char *line_read = NULL;
420 #ifdef HAVE_LIBREADLINE
428 line_read = readline (prompt ? FISH : "");
430 if (line_read && *line_read)
431 add_history_line (line_read);
436 #endif /* HAVE_LIBREADLINE */
438 static char buf[8192];
441 if (prompt) printf (FISH);
442 line_read = fgets (buf, sizeof buf, stdin);
445 len = strlen (line_read);
446 if (len > 0 && buf[len-1] == '\n') buf[len-1] = '\0';
460 int global_exit_on_error = !prompt;
465 "Welcome to guestfish, the libguestfs filesystem interactive shell for\n"
466 "editing virtual machine filesystems.\n"
468 "Type: 'help' for help with commands\n"
469 " 'quit' to quit the shell\n"
475 exit_on_error = global_exit_on_error;
477 buf = rl_gets (prompt);
483 /* Skip any initial whitespace before the command. */
485 while (*buf && isspace (*buf))
490 /* If the next character is '#' then this is a comment. */
491 if (*buf == '#') continue;
493 /* If the next character is '!' then pass the whole lot to system(3). */
501 (WTERMSIG (r) == SIGINT || WTERMSIG (r) == SIGQUIT)) ||
502 WEXITSTATUS (r) != 0)
508 /* If the next character is '-' allow the command to fail without
509 * exiting on error (just for this one command though).
517 /* Get the command (cannot be quoted). */
518 len = strcspn (buf, " \t");
520 if (len == 0) continue;
524 if (buf[len] == '\0') {
531 p += strspn (p, " \t");
533 /* Get the parameters. */
534 while (*p && i < sizeof argv / sizeof argv[0]) {
535 /* Parameters which start with quotes or pipes are treated
536 * specially. Bare parameters are delimited by whitespace.
540 len = strcspn (p, "\"");
541 if (p[len] == '\0') {
542 fprintf (stderr, _("guestfish: unterminated double quote\n"));
543 if (exit_on_error) exit (1);
546 if (p[len+1] && (p[len+1] != ' ' && p[len+1] != '\t')) {
547 fprintf (stderr, _("guestfish: command arguments not separated by whitespace\n"));
548 if (exit_on_error) exit (1);
552 pend = p[len+1] ? &p[len+2] : &p[len+1];
553 } else if (*p == '\'') {
555 len = strcspn (p, "'");
556 if (p[len] == '\0') {
557 fprintf (stderr, _("guestfish: unterminated single quote\n"));
558 if (exit_on_error) exit (1);
561 if (p[len+1] && (p[len+1] != ' ' && p[len+1] != '\t')) {
562 fprintf (stderr, _("guestfish: command arguments not separated by whitespace\n"));
563 if (exit_on_error) exit (1);
567 pend = p[len+1] ? &p[len+2] : &p[len+1];
568 } else if (*p == '|') {
573 } else if (*p == '[') {
577 while (*pend && c != 0) {
578 if (*pend == '[') c++;
579 else if (*pend == ']') c--;
583 fprintf (stderr, _("guestfish: unterminated \"[...]\" sequence\n"));
584 if (exit_on_error) exit (1);
587 if (*pend && (*pend != ' ' && *pend != '\t')) {
588 fprintf (stderr, _("guestfish: command arguments not separated by whitespace\n"));
589 if (exit_on_error) exit (1);
594 } else if (*p != ' ' && *p != '\t') {
595 len = strcspn (p, " \t");
602 fprintf (stderr, _("guestfish: internal error parsing string at '%s'\n"),
611 p += strspn (p, " \t");
614 if (i == sizeof argv / sizeof argv[0]) {
615 fprintf (stderr, _("guestfish: too many arguments\n"));
616 if (exit_on_error) exit (1);
623 if (issue_command (cmd, argv, pipe) == -1) {
624 if (exit_on_error) exit (1);
629 if (prompt) printf ("\n");
633 cmdline (char *argv[], int optind, int argc)
638 if (optind >= argc) return;
640 cmd = argv[optind++];
641 if (strcmp (cmd, ":") == 0) {
642 fprintf (stderr, _("guestfish: empty command on command line\n"));
645 params = &argv[optind];
647 /* Search for end of command list or ":" ... */
648 while (optind < argc && strcmp (argv[optind], ":") != 0)
651 if (optind == argc) {
652 if (issue_command (cmd, params, NULL) == -1) exit (1);
655 if (issue_command (cmd, params, NULL) == -1) exit (1);
656 cmdline (argv, optind+1, argc);
661 issue_command (const char *cmd, char *argv[], const char *pipecmd)
664 int stdout_saved_fd = -1;
668 /* For | ... commands. Annoyingly we can't use popen(3) here. */
680 if (pid == 0) { /* Child process. */
684 r = system (pipecmd);
689 _exit (WEXITSTATUS (r));
692 stdout_saved_fd = dup (1);
698 for (argc = 0; argv[argc] != NULL; ++argc)
701 if (strcasecmp (cmd, "help") == 0) {
705 display_command (argv[0]);
708 else if (strcasecmp (cmd, "quit") == 0 ||
709 strcasecmp (cmd, "exit") == 0 ||
710 strcasecmp (cmd, "q") == 0) {
714 else if (strcasecmp (cmd, "alloc") == 0 ||
715 strcasecmp (cmd, "allocate") == 0)
716 r = do_alloc (cmd, argc, argv);
717 else if (strcasecmp (cmd, "echo") == 0)
718 r = do_echo (cmd, argc, argv);
719 else if (strcasecmp (cmd, "edit") == 0 ||
720 strcasecmp (cmd, "vi") == 0 ||
721 strcasecmp (cmd, "emacs") == 0)
722 r = do_edit (cmd, argc, argv);
723 else if (strcasecmp (cmd, "lcd") == 0)
724 r = do_lcd (cmd, argc, argv);
725 else if (strcasecmp (cmd, "glob") == 0)
726 r = do_glob (cmd, argc, argv);
727 else if (strcasecmp (cmd, "more") == 0 ||
728 strcasecmp (cmd, "less") == 0)
729 r = do_more (cmd, argc, argv);
730 else if (strcasecmp (cmd, "reopen") == 0)
731 r = do_reopen (cmd, argc, argv);
732 else if (strcasecmp (cmd, "time") == 0)
733 r = do_time (cmd, argc, argv);
735 r = run_action (cmd, argc, argv);
737 /* Always flush stdout after every command, so that messages, results
738 * etc appear immediately.
744 dup2 (stdout_saved_fd, 1);
745 close (stdout_saved_fd);
746 waitpid (pid, NULL, 0);
753 list_builtin_commands (void)
755 /* help and quit should appear at the top */
756 printf ("%-20s %s\n",
757 "help", _("display a list of commands or help on a command"));
758 printf ("%-20s %s\n",
759 "quit", _("quit guestfish"));
761 printf ("%-20s %s\n",
762 "alloc", _("allocate an image"));
763 printf ("%-20s %s\n",
764 "echo", _("display a line of text"));
765 printf ("%-20s %s\n",
766 "edit", _("edit a file in the image"));
767 printf ("%-20s %s\n",
768 "lcd", _("local change directory"));
769 printf ("%-20s %s\n",
770 "glob", _("expand wildcards in command"));
771 printf ("%-20s %s\n",
772 "more", _("view a file in the pager"));
773 printf ("%-20s %s\n",
774 "reopen", _("close and reopen libguestfs handle"));
775 printf ("%-20s %s\n",
776 "time", _("measure time taken to run command"));
778 /* actions are printed after this (see list_commands) */
782 display_builtin_command (const char *cmd)
784 /* help for actions is auto-generated, see display_command */
786 if (strcasecmp (cmd, "alloc") == 0 ||
787 strcasecmp (cmd, "allocate") == 0)
788 printf (_("alloc - allocate an image\n"
789 " alloc <filename> <size>\n"
791 " This creates an empty (zeroed) file of the given size,\n"
792 " and then adds so it can be further examined.\n"
794 " For more advanced image creation, see qemu-img utility.\n"
796 " Size can be specified (where <nn> means a number):\n"
797 " <nn> number of kilobytes\n"
798 " eg: 1440 standard 3.5\" floppy\n"
799 " <nn>K or <nn>KB number of kilobytes\n"
800 " <nn>M or <nn>MB number of megabytes\n"
801 " <nn>G or <nn>GB number of gigabytes\n"
802 " <nn>sects number of 512 byte sectors\n"));
803 else if (strcasecmp (cmd, "echo") == 0)
804 printf (_("echo - display a line of text\n"
805 " echo [<params> ...]\n"
807 " This echos the parameters to the terminal.\n"));
808 else if (strcasecmp (cmd, "edit") == 0 ||
809 strcasecmp (cmd, "vi") == 0 ||
810 strcasecmp (cmd, "emacs") == 0)
811 printf (_("edit - edit a file in the image\n"
814 " This is used to edit a file.\n"
816 " It is the equivalent of (and is implemented by)\n"
817 " running \"cat\", editing locally, and then \"write-file\".\n"
819 " Normally it uses $EDITOR, but if you use the aliases\n"
820 " \"vi\" or \"emacs\" you will get those editors.\n"
822 " NOTE: This will not work reliably for large files\n"
823 " (> 2 MB) or binary files containing \\0 bytes.\n"));
824 else if (strcasecmp (cmd, "lcd") == 0)
825 printf (_("lcd - local change directory\n"
828 " Change guestfish's current directory. This command is\n"
829 " useful if you want to download files to a particular\n"
831 else if (strcasecmp (cmd, "glob") == 0)
832 printf (_("glob - expand wildcards in command\n"
833 " glob <command> [<args> ...]\n"
835 " Glob runs <command> with wildcards expanded in any\n"
836 " command args. Note that the command is run repeatedly\n"
837 " once for each expanded argument.\n"));
838 else if (strcasecmp (cmd, "help") == 0)
839 printf (_("help - display a list of commands or help on a command\n"
842 else if (strcasecmp (cmd, "more") == 0 ||
843 strcasecmp (cmd, "less") == 0)
844 printf (_("more - view a file in the pager\n"
847 " This is used to view a file in the pager.\n"
849 " It is the equivalent of (and is implemented by)\n"
850 " running \"cat\" and using the pager.\n"
852 " Normally it uses $PAGER, but if you use the alias\n"
853 " \"less\" then it always uses \"less\".\n"
855 " NOTE: This will not work reliably for large files\n"
856 " (> 2 MB) or binary files containing \\0 bytes.\n"));
857 else if (strcasecmp (cmd, "quit") == 0 ||
858 strcasecmp (cmd, "exit") == 0 ||
859 strcasecmp (cmd, "q") == 0)
860 printf (_("quit - quit guestfish\n"
862 else if (strcasecmp (cmd, "reopen") == 0)
863 printf (_("reopen - close and reopen the libguestfs handle\n"
866 "Close and reopen the libguestfs handle. It is not necessary to use\n"
867 "this normally, because the handle is closed properly when guestfish\n"
868 "exits. However this is occasionally useful for testing.\n"));
869 else if (strcasecmp (cmd, "time") == 0)
870 printf (_("time - measure time taken to run command\n"
871 " time <command> [<args> ...]\n"
873 " This runs <command> as usual, and prints the elapsed\n"
874 " time afterwards.\n"));
876 fprintf (stderr, _("%s: command not known, use -h to list all commands\n"),
881 free_strings (char **argv)
885 for (argc = 0; argv[argc] != NULL; ++argc)
891 count_strings (char * const * const argv)
895 for (c = 0; argv[c]; ++c)
901 print_strings (char * const * const argv)
905 for (argc = 0; argv[argc] != NULL; ++argc)
906 printf ("%s\n", argv[argc]);
910 print_table (char * const * const argv)
914 for (i = 0; argv[i] != NULL; i += 2)
915 printf ("%s: %s\n", argv[i], argv[i+1]);
919 is_true (const char *str)
922 strcasecmp (str, "0") != 0 &&
923 strcasecmp (str, "f") != 0 &&
924 strcasecmp (str, "false") != 0 &&
925 strcasecmp (str, "n") != 0 &&
926 strcasecmp (str, "no") != 0;
929 /* XXX We could improve list parsing. */
931 parse_string_list (const char *str)
934 const char *p, *pend;
938 for (i = 0; str[i]; ++i)
939 if (str[i] == ' ') argc++;
941 argv = malloc (sizeof (char *) * (argc+1));
942 if (argv == NULL) { perror ("malloc"); exit (1); }
947 pend = strchrnul (p, ' ');
948 argv[i] = strndup (p, pend-p);
950 p = *pend == ' ' ? pend+1 : pend;
957 #ifdef HAVE_LIBREADLINE
958 static char histfile[1024];
959 static int nr_history_lines = 0;
963 initialize_readline (void)
965 #ifdef HAVE_LIBREADLINE
968 home = getenv ("HOME");
970 snprintf (histfile, sizeof histfile, "%s/.guestfish", home);
972 (void) read_history (histfile);
975 rl_readline_name = "guestfish";
976 rl_attempted_completion_function = do_completion;
981 cleanup_readline (void)
983 #ifdef HAVE_LIBREADLINE
986 if (histfile[0] != '\0') {
987 fd = open (histfile, O_WRONLY|O_CREAT, 0644);
994 (void) append_history (nr_history_lines, histfile);
1000 add_history_line (const char *line)
1002 #ifdef HAVE_LIBREADLINE
1009 xwrite (int fd, const void *buf, size_t len)
1014 r = write (fd, buf, len);