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>
56 static void add_drives (struct drv *drv);
57 static void mount_mps (struct mp *mp);
58 static void interactive (void);
59 static void shell_script (void);
60 static void script (int prompt);
61 static void cmdline (char *argv[], int optind, int argc);
62 static void initialize_readline (void);
63 static void cleanup_readline (void);
64 static void add_history_line (const char *);
66 /* Currently open libguestfs handle. */
72 int echo_commands = 0;
73 int remote_control_listen = 0;
74 int remote_control = 0;
75 int exit_on_error = 1;
78 launch (guestfs_h *_g)
82 if (guestfs_is_config (g)) {
83 if (guestfs_launch (g) == -1)
85 if (guestfs_wait_ready (g) == -1)
91 static void __attribute__((noreturn))
94 if (status != EXIT_SUCCESS)
95 fprintf (stderr, _("Try `%s --help' for more information.\n"),
99 _("%s: guest filesystem shell\n"
100 "%s lets you edit virtual machine filesystems\n"
101 "Copyright (C) 2009 Red Hat Inc.\n"
103 " %s [--options] cmd [: cmd : cmd ...]\n"
104 " %s -i libvirt-domain\n"
105 " %s -i disk-image(s)\n"
106 "or for interactive use:\n"
108 "or from a shell script:\n"
114 " -h|--cmd-help List available commands\n"
115 " -h|--cmd-help cmd Display detailed help on 'cmd'\n"
116 " -a|--add image Add image\n"
117 " -D|--no-dest-paths Don't tab-complete paths from guest fs\n"
118 " -f|--file file Read commands from file\n"
119 " -i|--inspector Run virt-inspector to get disk mountpoints\n"
120 " --listen Listen for remote commands\n"
121 " -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n"
122 " -n|--no-sync Don't autosync\n"
123 " --remote[=pid] Send commands to remote %s\n"
124 " -r|--ro Mount read-only\n"
125 " --selinux Enable SELinux support\n"
126 " -v|--verbose Verbose messages\n"
127 " -x Echo each command before executing it\n"
128 " -V|--version Display version and exit\n"
129 "For more information, see the manpage %s(1).\n"),
130 program_name, program_name, program_name,
131 program_name, program_name, program_name,
132 program_name, program_name, program_name);
138 main (int argc, char *argv[])
140 /* Set global program name that is not polluted with libtool artifacts. */
141 set_program_name (argv[0]);
143 enum { HELP_OPTION = CHAR_MAX + 1 };
145 static const char *options = "a:Df:h::im:nrv?Vx";
146 static const struct option long_options[] = {
147 { "add", 1, 0, 'a' },
148 { "cmd-help", 2, 0, 'h' },
149 { "file", 1, 0, 'f' },
150 { "help", 0, 0, HELP_OPTION },
151 { "inspector", 0, 0, 'i' },
152 { "listen", 0, 0, 0 },
153 { "mount", 1, 0, 'm' },
154 { "no-dest-paths", 0, 0, 'D' },
155 { "no-sync", 0, 0, 'n' },
156 { "remote", 2, 0, 0 },
158 { "selinux", 0, 0, 0 },
159 { "verbose", 0, 0, 'v' },
160 { "version", 0, 0, 'V' },
163 struct drv *drvs = NULL;
165 struct mp *mps = NULL;
167 char *p, *file = NULL;
173 initialize_readline ();
175 memset (&sa, 0, sizeof sa);
176 sa.sa_handler = SIG_IGN;
177 sa.sa_flags = SA_RESTART;
178 sigaction (SIGPIPE, &sa, NULL);
180 /* guestfs_create is meant to be a lightweight operation, so
181 * it's OK to do it early here.
183 g = guestfs_create ();
185 fprintf (stderr, _("guestfs_create: failed to create handle\n"));
189 guestfs_set_autosync (g, 1);
191 /* If developing, add ./appliance to the path. Note that libtools
192 * interferes with this because uninstalled guestfish is a shell
193 * script that runs the real program with an absolute path. Detect
196 * BUT if LIBGUESTFS_PATH environment variable is already set by
197 * the user, then don't override it.
199 if (getenv ("LIBGUESTFS_PATH") == NULL &&
201 (argv[0][0] != '/' || strstr (argv[0], "/.libs/lt-") != NULL))
202 guestfs_set_path (g, "appliance:" GUESTFS_DEFAULT_PATH);
204 /* CAUTION: we are careful to modify argv[0] here, only after
205 * using it just above.
207 * getopt_long uses argv[0], so give it the sanitized name. Save a copy
208 * of the original, in case it's needed in virt-inspector mode, below.
210 char *real_argv0 = argv[0];
211 argv[0] = bad_cast (program_name);
214 c = getopt_long (argc, argv, options, long_options, &option_index);
218 case 0: /* options which are long only */
219 if (strcmp (long_options[option_index].name, "listen") == 0)
220 remote_control_listen = 1;
221 else if (strcmp (long_options[option_index].name, "remote") == 0) {
223 if (sscanf (optarg, "%d", &remote_control) != 1) {
224 fprintf (stderr, _("%s: --listen=PID: PID was not a number: %s\n"),
225 program_name, optarg);
229 p = getenv ("GUESTFISH_PID");
230 if (!p || sscanf (p, "%d", &remote_control) != 1) {
231 fprintf (stderr, _("%s: remote: $GUESTFISH_PID must be set"
232 " to the PID of the remote process\n"),
237 } else if (strcmp (long_options[option_index].name, "selinux") == 0) {
238 guestfs_set_selinux (g, 1);
240 fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
241 program_name, long_options[option_index].name, option_index);
247 if (access (optarg, R_OK) != 0) {
251 drv = malloc (sizeof (struct drv));
256 drv->filename = optarg;
262 complete_dest_paths = 0;
267 fprintf (stderr, _("%s: only one -f parameter can be given\n"),
276 display_command (optarg);
277 else if (argv[optind] && argv[optind][0] != '-')
278 display_command (argv[optind++]);
288 mp = malloc (sizeof (struct mp));
293 p = strchr (optarg, ':');
296 mp->mountpoint = p+1;
298 mp->mountpoint = bad_cast ("/");
305 guestfs_set_autosync (g, 0);
314 guestfs_set_verbose (g, verbose);
318 printf ("%s %s\n", program_name, PACKAGE_VERSION);
329 fprintf (stderr, _("%s: unexpected command line option 0x%x\n"),
335 /* Inspector mode invalidates most of the other arguments. */
340 if (drvs || mps || remote_control_listen || remote_control ||
341 guestfs_get_selinux (g)) {
342 fprintf (stderr, _("%s: cannot use -i option with -a, -m,"
343 " --listen, --remote or --selinux\n"),
347 if (optind >= argc) {
349 _("%s: -i requires a libvirt domain or path(s) to disk image(s)\n"),
354 strcpy (cmd, "a=`virt-inspector");
355 while (optind < argc) {
356 if (strlen (cmd) + strlen (argv[optind]) + strlen (real_argv0) + 60
359 _("%s: virt-inspector command too long for fixed-size buffer\n"),
364 strcat (cmd, argv[optind]);
370 strcat (cmd, " --ro-fish");
372 strcat (cmd, " --fish");
374 sprintf (&cmd[strlen(cmd)], "` && %s $a", real_argv0);
376 if (guestfs_get_verbose (g))
378 if (!guestfs_get_autosync (g))
383 "%s -i: running virt-inspector command:\n%s\n", program_name, cmd);
390 exit (WEXITSTATUS (r));
393 /* If we've got drives to add, add them now. */
396 /* If we've got mountpoints, we must launch the guest and mount them. */
398 if (launch (g) == -1) exit (1);
402 /* Remote control? */
403 if (remote_control_listen && remote_control) {
405 _("%s: cannot use --listen and --remote options at the same time\n"),
410 if (remote_control_listen) {
413 _("%s: extra parameters on the command line with --listen flag\n"),
419 _("%s: cannot use --listen and --file options at the same time\n"),
426 /* -f (file) parameter? */
429 if (open (file, O_RDONLY) == -1) {
435 /* Interactive, shell script, or command(s) on the command line? */
436 if (optind >= argc) {
443 cmdline (argv, optind, argc);
451 pod2text (const char *name, const char *shortdesc, const char *str)
455 fp = popen ("pod2text", "w");
457 /* pod2text failed, maybe not found, so let's just print the
458 * source instead, since that's better than doing nothing.
460 printf ("%s - %s\n\n%s\n", name, shortdesc, str);
463 fprintf (fp, "=head1 %s - %s\n\n", name, shortdesc);
468 /* List is built in reverse order, so mount them in reverse order. */
470 mount_mps (struct mp *mp)
475 mount_mps (mp->next);
477 r = guestfs_mount (g, mp->device, mp->mountpoint);
479 r = guestfs_mount_ro (g, mp->device, mp->mountpoint);
486 add_drives (struct drv *drv)
491 add_drives (drv->next);
493 r = guestfs_add_drive (g, drv->filename);
495 r = guestfs_add_drive_ro (g, drv->filename);
513 #define FISH "><fs> "
515 static char *line_read = NULL;
520 #ifdef HAVE_LIBREADLINE
528 line_read = readline (prompt ? FISH : "");
530 if (line_read && *line_read)
531 add_history_line (line_read);
536 #endif /* HAVE_LIBREADLINE */
538 static char buf[8192];
541 if (prompt) printf (FISH);
542 line_read = fgets (buf, sizeof buf, stdin);
545 len = strlen (line_read);
546 if (len > 0 && buf[len-1] == '\n') buf[len-1] = '\0';
560 int global_exit_on_error = !prompt;
565 "Welcome to guestfish, the libguestfs filesystem interactive shell for\n"
566 "editing virtual machine filesystems.\n"
568 "Type: 'help' for help with commands\n"
569 " 'quit' to quit the shell\n"
575 exit_on_error = global_exit_on_error;
577 buf = rl_gets (prompt);
583 /* Skip any initial whitespace before the command. */
585 while (*buf && isspace (*buf))
590 /* If the next character is '#' then this is a comment. */
591 if (*buf == '#') continue;
593 /* If the next character is '!' then pass the whole lot to system(3). */
601 (WTERMSIG (r) == SIGINT || WTERMSIG (r) == SIGQUIT)) ||
602 WEXITSTATUS (r) != 0)
608 /* If the next character is '-' allow the command to fail without
609 * exiting on error (just for this one command though).
617 /* Get the command (cannot be quoted). */
618 len = strcspn (buf, " \t");
620 if (len == 0) continue;
624 if (buf[len] == '\0') {
631 p += strspn (p, " \t");
633 /* Get the parameters. */
634 while (*p && i < sizeof argv / sizeof argv[0]) {
637 /* Parameters which start with quotes or pipes are treated
638 * specially. Bare parameters are delimited by whitespace.
642 len = strcspn (p, "\"");
643 if (p[len] == '\0') {
644 fprintf (stderr, _("%s: unterminated double quote\n"), program_name);
645 if (exit_on_error) exit (1);
648 if (p[len+1] && (p[len+1] != ' ' && p[len+1] != '\t')) {
650 _("%s: command arguments not separated by whitespace\n"),
652 if (exit_on_error) exit (1);
656 pend = p[len+1] ? &p[len+2] : &p[len+1];
657 } else if (*p == '\'') {
659 len = strcspn (p, "'");
660 if (p[len] == '\0') {
661 fprintf (stderr, _("%s: unterminated single quote\n"), program_name);
662 if (exit_on_error) exit (1);
665 if (p[len+1] && (p[len+1] != ' ' && p[len+1] != '\t')) {
667 _("%s: command arguments not separated by whitespace\n"),
669 if (exit_on_error) exit (1);
673 pend = p[len+1] ? &p[len+2] : &p[len+1];
674 } else if (*p == '|') {
679 } else if (*p == '[') {
683 while (*pend && c != 0) {
684 if (*pend == '[') c++;
685 else if (*pend == ']') c--;
690 _("%s: unterminated \"[...]\" sequence\n"), program_name);
691 if (exit_on_error) exit (1);
694 if (*pend && (*pend != ' ' && *pend != '\t')) {
696 _("%s: command arguments not separated by whitespace\n"),
698 if (exit_on_error) exit (1);
703 } else if (*p != ' ' && *p != '\t') {
704 /* If the first character is a ~ then note that this parameter
705 * is a candidate for ~username expansion. NB this does not
706 * apply to quoted parameters.
708 tilde_candidate = *p == '~';
709 len = strcspn (p, " \t");
716 fprintf (stderr, _("%s: internal error parsing string at '%s'\n"),
721 if (!tilde_candidate)
724 argv[i] = try_tilde_expansion (p);
729 p += strspn (p, " \t");
732 if (i == sizeof argv / sizeof argv[0]) {
733 fprintf (stderr, _("%s: too many arguments\n"), program_name);
734 if (exit_on_error) exit (1);
741 if (issue_command (cmd, argv, pipe) == -1) {
742 if (exit_on_error) exit (1);
747 if (prompt) printf ("\n");
751 cmdline (char *argv[], int optind, int argc)
758 if (optind >= argc) return;
760 cmd = argv[optind++];
761 if (strcmp (cmd, ":") == 0) {
762 fprintf (stderr, _("%s: empty command on command line\n"), program_name);
765 params = &argv[optind];
767 /* Search for end of command list or ":" ... */
768 while (optind < argc && strcmp (argv[optind], ":") != 0)
771 if (optind == argc) {
772 if (issue_command (cmd, params, NULL) == -1) exit (1);
775 if (issue_command (cmd, params, NULL) == -1) exit (1);
776 cmdline (argv, optind+1, argc);
781 issue_command (const char *cmd, char *argv[], const char *pipecmd)
784 int stdout_saved_fd = -1;
790 for (i = 0; argv[i] != NULL; ++i)
791 printf (" %s", argv[i]);
795 /* For | ... commands. Annoyingly we can't use popen(3) here. */
799 if (fflush (stdout) == EOF) {
800 perror ("failed to flush standard output");
804 perror ("pipe failed");
813 if (pid == 0) { /* Child process. */
815 if (dup2 (fd[0], 0) < 0) {
816 perror ("dup2 of stdin failed");
820 r = system (pipecmd);
825 _exit (WEXITSTATUS (r));
828 if ((stdout_saved_fd = dup (1)) < 0) {
829 perror ("failed to dup stdout");
833 if (dup2 (fd[1], 1) < 0) {
834 perror ("failed to dup stdout");
835 close (stdout_saved_fd);
841 for (argc = 0; argv[argc] != NULL; ++argc)
844 /* If --remote was set, then send this command to a remote process. */
846 r = rc_remote (remote_control, cmd, argc, argv, exit_on_error);
848 /* Otherwise execute it locally. */
849 else if (strcasecmp (cmd, "help") == 0) {
853 display_command (argv[0]);
856 else if (strcasecmp (cmd, "quit") == 0 ||
857 strcasecmp (cmd, "exit") == 0 ||
858 strcasecmp (cmd, "q") == 0) {
862 else if (strcasecmp (cmd, "alloc") == 0 ||
863 strcasecmp (cmd, "allocate") == 0)
864 r = do_alloc (cmd, argc, argv);
865 else if (strcasecmp (cmd, "echo") == 0)
866 r = do_echo (cmd, argc, argv);
867 else if (strcasecmp (cmd, "edit") == 0 ||
868 strcasecmp (cmd, "vi") == 0 ||
869 strcasecmp (cmd, "emacs") == 0)
870 r = do_edit (cmd, argc, argv);
871 else if (strcasecmp (cmd, "lcd") == 0)
872 r = do_lcd (cmd, argc, argv);
873 else if (strcasecmp (cmd, "glob") == 0)
874 r = do_glob (cmd, argc, argv);
875 else if (strcasecmp (cmd, "more") == 0 ||
876 strcasecmp (cmd, "less") == 0)
877 r = do_more (cmd, argc, argv);
878 else if (strcasecmp (cmd, "reopen") == 0)
879 r = do_reopen (cmd, argc, argv);
880 else if (strcasecmp (cmd, "time") == 0)
881 r = do_time (cmd, argc, argv);
883 r = run_action (cmd, argc, argv);
885 /* Always flush stdout after every command, so that messages, results
886 * etc appear immediately.
888 if (fflush (stdout) == EOF) {
889 perror ("failed to flush standard output");
895 if (dup2 (stdout_saved_fd, 1) < 0) {
896 perror ("failed to dup2 standard output");
899 close (stdout_saved_fd);
900 if (waitpid (pid, NULL, 0) < 0) {
901 perror ("waiting for command to complete");
910 list_builtin_commands (void)
912 /* help and quit should appear at the top */
913 printf ("%-20s %s\n",
914 "help", _("display a list of commands or help on a command"));
915 printf ("%-20s %s\n",
916 "quit", _("quit guestfish"));
918 printf ("%-20s %s\n",
919 "alloc", _("allocate an image"));
920 printf ("%-20s %s\n",
921 "echo", _("display a line of text"));
922 printf ("%-20s %s\n",
923 "edit", _("edit a file in the image"));
924 printf ("%-20s %s\n",
925 "lcd", _("local change directory"));
926 printf ("%-20s %s\n",
927 "glob", _("expand wildcards in command"));
928 printf ("%-20s %s\n",
929 "more", _("view a file in the pager"));
930 printf ("%-20s %s\n",
931 "reopen", _("close and reopen libguestfs handle"));
932 printf ("%-20s %s\n",
933 "time", _("measure time taken to run command"));
935 /* actions are printed after this (see list_commands) */
939 display_builtin_command (const char *cmd)
941 /* help for actions is auto-generated, see display_command */
943 if (strcasecmp (cmd, "alloc") == 0 ||
944 strcasecmp (cmd, "allocate") == 0)
945 printf (_("alloc - allocate an image\n"
946 " alloc <filename> <size>\n"
948 " This creates an empty (zeroed) file of the given size,\n"
949 " and then adds so it can be further examined.\n"
951 " For more advanced image creation, see qemu-img utility.\n"
953 " Size can be specified (where <nn> means a number):\n"
954 " <nn> number of kilobytes\n"
955 " eg: 1440 standard 3.5\" floppy\n"
956 " <nn>K or <nn>KB number of kilobytes\n"
957 " <nn>M or <nn>MB number of megabytes\n"
958 " <nn>G or <nn>GB number of gigabytes\n"
959 " <nn>sects number of 512 byte sectors\n"));
960 else if (strcasecmp (cmd, "echo") == 0)
961 printf (_("echo - display a line of text\n"
962 " echo [<params> ...]\n"
964 " This echos the parameters to the terminal.\n"));
965 else if (strcasecmp (cmd, "edit") == 0 ||
966 strcasecmp (cmd, "vi") == 0 ||
967 strcasecmp (cmd, "emacs") == 0)
968 printf (_("edit - edit a file in the image\n"
971 " This is used to edit a file.\n"
973 " It is the equivalent of (and is implemented by)\n"
974 " running \"cat\", editing locally, and then \"write-file\".\n"
976 " Normally it uses $EDITOR, but if you use the aliases\n"
977 " \"vi\" or \"emacs\" you will get those editors.\n"
979 " NOTE: This will not work reliably for large files\n"
980 " (> 2 MB) or binary files containing \\0 bytes.\n"));
981 else if (strcasecmp (cmd, "lcd") == 0)
982 printf (_("lcd - local change directory\n"
985 " Change guestfish's current directory. This command is\n"
986 " useful if you want to download files to a particular\n"
988 else if (strcasecmp (cmd, "glob") == 0)
989 printf (_("glob - expand wildcards in command\n"
990 " glob <command> [<args> ...]\n"
992 " Glob runs <command> with wildcards expanded in any\n"
993 " command args. Note that the command is run repeatedly\n"
994 " once for each expanded argument.\n"));
995 else if (strcasecmp (cmd, "help") == 0)
996 printf (_("help - display a list of commands or help on a command\n"
999 else if (strcasecmp (cmd, "more") == 0 ||
1000 strcasecmp (cmd, "less") == 0)
1001 printf (_("more - view a file in the pager\n"
1002 " more <filename>\n"
1004 " This is used to view a file in the pager.\n"
1006 " It is the equivalent of (and is implemented by)\n"
1007 " running \"cat\" and using the pager.\n"
1009 " Normally it uses $PAGER, but if you use the alias\n"
1010 " \"less\" then it always uses \"less\".\n"
1012 " NOTE: This will not work reliably for large files\n"
1013 " (> 2 MB) or binary files containing \\0 bytes.\n"));
1014 else if (strcasecmp (cmd, "quit") == 0 ||
1015 strcasecmp (cmd, "exit") == 0 ||
1016 strcasecmp (cmd, "q") == 0)
1017 printf (_("quit - quit guestfish\n"
1019 else if (strcasecmp (cmd, "reopen") == 0)
1020 printf (_("reopen - close and reopen the libguestfs handle\n"
1023 "Close and reopen the libguestfs handle. It is not necessary to use\n"
1024 "this normally, because the handle is closed properly when guestfish\n"
1025 "exits. However this is occasionally useful for testing.\n"));
1026 else if (strcasecmp (cmd, "time") == 0)
1027 printf (_("time - measure time taken to run command\n"
1028 " time <command> [<args> ...]\n"
1030 " This runs <command> as usual, and prints the elapsed\n"
1031 " time afterwards.\n"));
1033 fprintf (stderr, _("%s: command not known, use -h to list all commands\n"),
1038 free_strings (char **argv)
1042 for (argc = 0; argv[argc] != NULL; ++argc)
1048 count_strings (char *const *argv)
1052 for (c = 0; argv[c]; ++c)
1058 print_strings (char *const *argv)
1062 for (argc = 0; argv[argc] != NULL; ++argc)
1063 printf ("%s\n", argv[argc]);
1067 print_table (char *const *argv)
1071 for (i = 0; argv[i] != NULL; i += 2)
1072 printf ("%s: %s\n", argv[i], argv[i+1]);
1076 is_true (const char *str)
1079 strcasecmp (str, "0") != 0 &&
1080 strcasecmp (str, "f") != 0 &&
1081 strcasecmp (str, "false") != 0 &&
1082 strcasecmp (str, "n") != 0 &&
1083 strcasecmp (str, "no") != 0;
1086 /* XXX We could improve list parsing. */
1088 parse_string_list (const char *str)
1091 const char *p, *pend;
1095 for (i = 0; str[i]; ++i)
1096 if (str[i] == ' ') argc++;
1098 argv = malloc (sizeof (char *) * (argc+1));
1099 if (argv == NULL) { perror ("malloc"); exit (1); }
1104 pend = strchrnul (p, ' ');
1105 argv[i] = strndup (p, pend-p);
1107 p = *pend == ' ' ? pend+1 : pend;
1114 #ifdef HAVE_LIBREADLINE
1115 static char histfile[1024];
1116 static int nr_history_lines = 0;
1120 initialize_readline (void)
1122 #ifdef HAVE_LIBREADLINE
1125 home = getenv ("HOME");
1127 snprintf (histfile, sizeof histfile, "%s/.guestfish", home);
1129 (void) read_history (histfile);
1132 rl_readline_name = "guestfish";
1133 rl_attempted_completion_function = do_completion;
1138 cleanup_readline (void)
1140 #ifdef HAVE_LIBREADLINE
1143 if (histfile[0] != '\0') {
1144 fd = open (histfile, O_WRONLY|O_CREAT, 0644);
1151 (void) append_history (nr_history_lines, histfile);
1157 add_history_line (const char *line)
1159 #ifdef HAVE_LIBREADLINE
1166 xwrite (int fd, const void *v_buf, size_t len)
1169 const char *buf = v_buf;
1172 r = write (fd, buf, len);