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:f: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, "time") == 0)
731 r = do_time (cmd, argc, argv);
733 r = run_action (cmd, argc, argv);
735 /* Always flush stdout after every command, so that messages, results
736 * etc appear immediately.
742 dup2 (stdout_saved_fd, 1);
743 close (stdout_saved_fd);
744 waitpid (pid, NULL, 0);
751 list_builtin_commands (void)
753 /* help and quit should appear at the top */
754 printf ("%-20s %s\n",
755 "help", _("display a list of commands or help on a command"));
756 printf ("%-20s %s\n",
757 "quit", _("quit guestfish"));
759 printf ("%-20s %s\n",
760 "alloc", _("allocate an image"));
761 printf ("%-20s %s\n",
762 "echo", _("display a line of text"));
763 printf ("%-20s %s\n",
764 "edit", _("edit a file in the image"));
765 printf ("%-20s %s\n",
766 "lcd", _("local change directory"));
767 printf ("%-20s %s\n",
768 "glob", _("expand wildcards in command"));
769 printf ("%-20s %s\n",
770 "time", _("measure time taken to run command"));
772 /* actions are printed after this (see list_commands) */
776 display_builtin_command (const char *cmd)
778 /* help for actions is auto-generated, see display_command */
780 if (strcasecmp (cmd, "alloc") == 0 ||
781 strcasecmp (cmd, "allocate") == 0)
782 printf (_("alloc - allocate an image\n"
783 " alloc <filename> <size>\n"
785 " This creates an empty (zeroed) file of the given size,\n"
786 " and then adds so it can be further examined.\n"
788 " For more advanced image creation, see qemu-img utility.\n"
790 " Size can be specified (where <nn> means a number):\n"
791 " <nn> number of kilobytes\n"
792 " eg: 1440 standard 3.5\" floppy\n"
793 " <nn>K or <nn>KB number of kilobytes\n"
794 " <nn>M or <nn>MB number of megabytes\n"
795 " <nn>G or <nn>GB number of gigabytes\n"
796 " <nn>sects number of 512 byte sectors\n"));
797 else if (strcasecmp (cmd, "echo") == 0)
798 printf (_("echo - display a line of text\n"
799 " echo [<params> ...]\n"
801 " This echos the parameters to the terminal.\n"));
802 else if (strcasecmp (cmd, "edit") == 0 ||
803 strcasecmp (cmd, "vi") == 0 ||
804 strcasecmp (cmd, "emacs") == 0)
805 printf (_("edit - edit a file in the image\n"
808 " This is used to edit a file.\n"
810 " It is the equivalent of (and is implemented by)\n"
811 " running \"cat\", editing locally, and then \"write-file\".\n"
813 " Normally it uses $EDITOR, but if you use the aliases\n"
814 " \"vi\" or \"emacs\" you will get those editors.\n"
816 " NOTE: This will not work reliably for large files\n"
817 " (> 2 MB) or binary files containing \\0 bytes.\n"));
818 else if (strcasecmp (cmd, "lcd") == 0)
819 printf (_("lcd - local change directory\n"
822 " Change guestfish's current directory. This command is\n"
823 " useful if you want to download files to a particular\n"
825 else if (strcasecmp (cmd, "glob") == 0)
826 printf (_("glob - expand wildcards in command\n"
827 " glob <command> [<args> ...]\n"
829 " Glob runs <command> with wildcards expanded in any\n"
830 " command args. Note that the command is run repeatedly\n"
831 " once for each expanded argument.\n"));
832 else if (strcasecmp (cmd, "more") == 0 ||
833 strcasecmp (cmd, "less") == 0)
834 printf (_("more - view a file in the pager\n"
837 " This is used to view a file in the pager.\n"
839 " It is the equivalent of (and is implemented by)\n"
840 " running \"cat\" and using the pager.\n"
842 " Normally it uses $PAGER, but if you use the alias\n"
843 " \"less\" then it always uses \"less\".\n"
845 " NOTE: This will not work reliably for large files\n"
846 " (> 2 MB) or binary files containing \\0 bytes.\n"));
847 else if (strcasecmp (cmd, "help") == 0)
848 printf (_("help - display a list of commands or help on a command\n"
851 else if (strcasecmp (cmd, "quit") == 0 ||
852 strcasecmp (cmd, "exit") == 0 ||
853 strcasecmp (cmd, "q") == 0)
854 printf (_("quit - quit guestfish\n"
856 else if (strcasecmp (cmd, "time") == 0)
857 printf (_("time - measure time taken to run command\n"
858 " time <command> [<args> ...]\n"
860 " This runs <command> as usual, and prints the elapsed\n"
861 " time afterwards.\n"));
863 fprintf (stderr, _("%s: command not known, use -h to list all commands\n"),
868 free_strings (char **argv)
872 for (argc = 0; argv[argc] != NULL; ++argc)
878 count_strings (char * const * const argv)
882 for (c = 0; argv[c]; ++c)
888 print_strings (char * const * const argv)
892 for (argc = 0; argv[argc] != NULL; ++argc)
893 printf ("%s\n", argv[argc]);
897 print_table (char * const * const argv)
901 for (i = 0; argv[i] != NULL; i += 2)
902 printf ("%s: %s\n", argv[i], argv[i+1]);
906 is_true (const char *str)
909 strcasecmp (str, "0") != 0 &&
910 strcasecmp (str, "f") != 0 &&
911 strcasecmp (str, "false") != 0 &&
912 strcasecmp (str, "n") != 0 &&
913 strcasecmp (str, "no") != 0;
916 /* XXX We could improve list parsing. */
918 parse_string_list (const char *str)
921 const char *p, *pend;
925 for (i = 0; str[i]; ++i)
926 if (str[i] == ' ') argc++;
928 argv = malloc (sizeof (char *) * (argc+1));
929 if (argv == NULL) { perror ("malloc"); exit (1); }
934 pend = strchrnul (p, ' ');
935 argv[i] = strndup (p, pend-p);
937 p = *pend == ' ' ? pend+1 : pend;
944 #ifdef HAVE_LIBREADLINE
945 static char histfile[1024];
946 static int nr_history_lines = 0;
950 initialize_readline (void)
952 #ifdef HAVE_LIBREADLINE
955 home = getenv ("HOME");
957 snprintf (histfile, sizeof histfile, "%s/.guestfish", home);
959 (void) read_history (histfile);
962 rl_readline_name = "guestfish";
963 rl_attempted_completion_function = do_completion;
968 cleanup_readline (void)
970 #ifdef HAVE_LIBREADLINE
973 if (histfile[0] != '\0') {
974 fd = open (histfile, O_WRONLY|O_CREAT, 0644);
981 (void) append_history (nr_history_lines, histfile);
987 add_history_line (const char *line)
989 #ifdef HAVE_LIBREADLINE
996 xwrite (int fd, const void *buf, size_t len)
1001 r = write (fd, buf, len);