#include <sys/wait.h>
#include <locale.h>
#include <langinfo.h>
-#include <termios.h>
#ifdef HAVE_LIBREADLINE
#include <readline/readline.h>
#include <guestfs.h>
#include "fish.h"
+#include "options.h"
+
#include "c-ctype.h"
#include "closeout.h"
#include "progname.h"
-/* List of drives added via -a, -d or -N options. */
-struct drv {
- struct drv *next;
- enum { drv_a, drv_d, drv_N } type;
- union {
- struct {
- char *filename; /* disk filename */
- } a;
- struct {
- char *guest; /* guest name */
- } d;
- struct {
- char *filename; /* disk filename (testX.img) */
- prep_data *data; /* prepared type */
- char *device; /* device inside the appliance */
- } N;
- };
-};
-
-struct mp {
- struct mp *next;
- char *device;
- char *mountpoint;
-};
-
static void set_up_terminal (void);
-static char add_drives (struct drv *drv, char next_drive);
static void prepare_drives (struct drv *drv);
-static void mount_mps (struct mp *mp);
static int launch (void);
static void interactive (void);
static void shell_script (void);
int quit = 0;
int verbose = 0;
int remote_control_listen = 0;
+int remote_control_csh = 0;
int remote_control = 0;
int exit_on_error = 1;
int command_num = 0;
int keys_from_stdin = 0;
+int echo_keys = 0;
const char *libvirt_uri = NULL;
int inspector = 0;
int utf8_mode = 0;
" -h|--cmd-help cmd Display detailed help on 'cmd'\n"
" -a|--add image Add image\n"
" -c|--connect uri Specify libvirt URI for -d option\n"
+ " --csh Make --listen csh-compatible\n"
" -d|--domain guest Add disks from libvirt guest\n"
" -D|--no-dest-paths Don't tab-complete paths from guest fs\n"
+ " --echo-keys Don't turn off echo for passphrases\n"
" -f|--file file Read commands from file\n"
+ " --format[=raw|..] Force disk format for -a option\n"
" -i|--inspector Automatically mount filesystems\n"
" --keys-from-stdin Read passphrases from stdin\n"
" --listen Listen for remote commands\n"
" -r|--ro Mount read-only\n"
" --selinux Enable SELinux support\n"
" -v|--verbose Verbose messages\n"
- " -x Echo each command before executing it\n"
" -V|--version Display version and exit\n"
+ " -x Echo each command before executing it\n"
"For more information, see the manpage %s(1).\n"),
program_name, program_name, program_name,
program_name, program_name, program_name,
enum { HELP_OPTION = CHAR_MAX + 1 };
- static const char *options = "a:c:d:Df:h::im:nN:rv?Vx";
+ static const char *options = "a:c:d:Df:h::im:nN:rv?Vwx";
static const struct option long_options[] = {
{ "add", 1, 0, 'a' },
{ "cmd-help", 2, 0, 'h' },
{ "connect", 1, 0, 'c' },
+ { "csh", 0, 0, 0 },
{ "domain", 1, 0, 'd' },
+ { "echo-keys", 0, 0, 0 },
{ "file", 1, 0, 'f' },
+ { "format", 2, 0, 0 },
{ "help", 0, 0, HELP_OPTION },
{ "inspector", 0, 0, 'i' },
{ "keys-from-stdin", 0, 0, 0 },
{ "no-progress-bars", 0, 0, 0 },
{ "remote", 2, 0, 0 },
{ "ro", 0, 0, 'r' },
+ { "rw", 0, 0, 'w' },
{ "selinux", 0, 0, 0 },
{ "verbose", 0, 0, 'v' },
{ "version", 0, 0, 'V' },
struct mp *mps = NULL;
struct mp *mp;
char *p, *file = NULL;
+ const char *format = NULL;
int c;
int option_index;
struct sigaction sa;
exit (EXIT_FAILURE);
}
- guestfs_set_autosync (g, 1);
-
/* If developing, add ./appliance to the path. Note that libtools
* interferes with this because uninstalled guestfish is a shell
* script that runs the real program with an absolute path. Detect
override_progress_bars = 1;
} else if (STREQ (long_options[option_index].name, "no-progress-bars")) {
override_progress_bars = 0;
+ } else if (STREQ (long_options[option_index].name, "echo-keys")) {
+ echo_keys = 1;
+ } else if (STREQ (long_options[option_index].name, "format")) {
+ if (!optarg || STREQ (optarg, ""))
+ format = NULL;
+ else
+ format = optarg;
+ } else if (STREQ (long_options[option_index].name, "csh")) {
+ remote_control_csh = 1;
} else {
fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
program_name, long_options[option_index].name, option_index);
break;
case 'a':
- if (access (optarg, R_OK) != 0) {
- perror (optarg);
- exit (EXIT_FAILURE);
- }
- drv = malloc (sizeof (struct drv));
- if (!drv) {
- perror ("malloc");
- exit (EXIT_FAILURE);
- }
- drv->type = drv_a;
- drv->a.filename = optarg;
- drv->next = drvs;
- drvs = drv;
+ OPTION_a;
break;
case 'c':
- libvirt_uri = optarg;
+ OPTION_c;
break;
case 'd':
- drv = malloc (sizeof (struct drv));
- if (!drv) {
- perror ("malloc");
- exit (EXIT_FAILURE);
- }
- drv->type = drv_d;
- drv->d.guest = optarg;
- drv->next = drvs;
- drvs = drv;
- break;
-
- case 'N':
- if (STRCASEEQ (optarg, "list") ||
- STRCASEEQ (optarg, "help") ||
- STRCASEEQ (optarg, "h") ||
- STRCASEEQ (optarg, "?")) {
- list_prepared_drives ();
- exit (EXIT_SUCCESS);
- }
- drv = malloc (sizeof (struct drv));
- if (!drv) {
- perror ("malloc");
- exit (EXIT_FAILURE);
- }
- drv->type = drv_N;
- if (asprintf (&drv->N.filename, "test%d.img",
- next_prepared_drive++) == -1) {
- perror ("asprintf");
- exit (EXIT_FAILURE);
- }
- drv->N.data = create_prepared_file (optarg, drv->N.filename);
- drv->N.device = NULL; /* filled in by add_drives */
- drv->next = drvs;
- drvs = drv;
+ OPTION_d;
break;
case 'D':
}
case 'i':
- inspector = 1;
+ OPTION_i;
break;
case 'm':
- mp = malloc (sizeof (struct mp));
- if (!mp) {
- perror ("malloc");
- exit (EXIT_FAILURE);
- }
- p = strchr (optarg, ':');
- if (p) {
- *p = '\0';
- mp->mountpoint = p+1;
- } else
- mp->mountpoint = bad_cast ("/");
- mp->device = optarg;
- mp->next = mps;
- mps = mp;
+ OPTION_m;
break;
case 'n':
- guestfs_set_autosync (g, 0);
+ OPTION_n;
+ break;
+
+ case 'N':
+ if (STRCASEEQ (optarg, "list") ||
+ STRCASEEQ (optarg, "help") ||
+ STRCASEEQ (optarg, "h") ||
+ STRCASEEQ (optarg, "?")) {
+ list_prepared_drives ();
+ exit (EXIT_SUCCESS);
+ }
+ drv = malloc (sizeof (struct drv));
+ if (!drv) {
+ perror ("malloc");
+ exit (EXIT_FAILURE);
+ }
+ drv->type = drv_N;
+ if (asprintf (&drv->N.filename, "test%d.img",
+ next_prepared_drive++) == -1) {
+ perror ("asprintf");
+ exit (EXIT_FAILURE);
+ }
+ drv->N.data = create_prepared_file (optarg, drv->N.filename);
+ drv->N.data_free = free_prep_data;
+ drv->N.device = NULL; /* filled in by add_drives */
+ drv->next = drvs;
+ drvs = drv;
break;
case 'r':
- read_only = 1;
+ OPTION_r;
break;
case 'v':
- verbose++;
- guestfs_set_verbose (g, verbose);
+ OPTION_v;
break;
- case 'V': {
- struct guestfs_version *v = guestfs_version (g);
- printf ("%s %"PRIi64".%"PRIi64".%"PRIi64"%s\n", program_name,
- v->major, v->minor, v->release, v->extra);
- exit (EXIT_SUCCESS);
- }
+ case 'V':
+ OPTION_V;
+ break;
+
+ case 'w':
+ OPTION_w;
+ break;
case 'x':
- guestfs_set_trace (g, 1);
+ OPTION_x;
break;
case HELP_OPTION:
}
drv->type = drv_a;
drv->a.filename = argv[optind];
+ drv->a.format = NULL;
drv->next = drvs;
drvs = drv;
} else { /* simulate -d option */
mount_mps (mps);
}
+ /* Free up data structures, no longer needed after this point. */
+ free_drives (drvs);
+ free_mps (mps);
+
/* Remote control? */
if (remote_control_listen && remote_control) {
fprintf (stderr,
pclose (fp);
}
-/* List is built in reverse order, so mount them in reverse order. */
-static void
-mount_mps (struct mp *mp)
-{
- int r;
-
- if (mp) {
- mount_mps (mp->next);
-
- /* Don't use guestfs_mount here because that will default to mount
- * options -o sync,noatime. For more information, see guestfs(3)
- * section "LIBGUESTFS GOTCHAS".
- */
- const char *options = read_only ? "ro" : "";
- r = guestfs_mount_options (g, options, mp->device, mp->mountpoint);
- if (r == -1)
- exit (EXIT_FAILURE);
- }
-}
-
-static char
-add_drives (struct drv *drv, char next_drive)
-{
- int r;
-
- if (next_drive > 'z') {
- fprintf (stderr,
- _("guestfish: too many drives added on the command line\n"));
- exit (EXIT_FAILURE);
- }
-
- if (drv) {
- next_drive = add_drives (drv->next, next_drive);
-
- switch (drv->type) {
- case drv_a:
- if (!read_only)
- r = guestfs_add_drive (g, drv->a.filename);
- else
- r = guestfs_add_drive_ro (g, drv->a.filename);
- if (r == -1)
- exit (EXIT_FAILURE);
-
- next_drive++;
- break;
-
- case drv_d:
- r = add_libvirt_drives (drv->d.guest);
- if (r == -1)
- exit (EXIT_FAILURE);
-
- next_drive += r;
- break;
-
- case drv_N:
- /* -N option is not affected by --ro */
- r = guestfs_add_drive (g, drv->N.filename);
- if (r == -1)
- exit (EXIT_FAILURE);
-
- if (asprintf (&drv->N.device, "/dev/sd%c", next_drive) == -1) {
- perror ("asprintf");
- exit (EXIT_FAILURE);
- }
-
- next_drive++;
- break;
-
- default: /* keep GCC happy */
- abort ();
- }
- }
-
- return next_drive;
-}
-
static void
prepare_drives (struct drv *drv)
{
"Welcome to guestfish, the libguestfs filesystem interactive shell for\n"
"editing virtual machine filesystems.\n"
"\n"
- "Type: 'help' for a list of commands\n"
+ "Type: 'help' for help on commands\n"
" 'man' to read the manual\n"
" 'quit' to quit the shell\n"
"\n"));
/* Otherwise execute it locally. */
else if (STRCASEEQ (cmd, "help")) {
if (argc == 0) {
- list_commands ();
+ display_help ();
r = 0;
} else
r = display_command (argv[0]);
quit = 1;
r = 0;
}
- else if (STRCASEEQ (cmd, "alloc") ||
- STRCASEEQ (cmd, "allocate"))
- r = do_alloc (cmd, argc, argv);
- else if (STRCASEEQ (cmd, "copy-in") ||
- STRCASEEQ (cmd, "copy_in"))
- r = do_copy_in (cmd, argc, argv);
- else if (STRCASEEQ (cmd, "copy-out") ||
- STRCASEEQ (cmd, "copy_out"))
- r = do_copy_out (cmd, argc, argv);
- else if (STRCASEEQ (cmd, "echo"))
- r = do_echo (cmd, argc, argv);
- else if (STRCASEEQ (cmd, "edit") ||
- STRCASEEQ (cmd, "vi") ||
- STRCASEEQ (cmd, "emacs"))
- r = do_edit (cmd, argc, argv);
- else if (STRCASEEQ (cmd, "lcd"))
- r = do_lcd (cmd, argc, argv);
- else if (STRCASEEQ (cmd, "glob"))
- r = do_glob (cmd, argc, argv);
- else if (STRCASEEQ (cmd, "man") ||
- STRCASEEQ (cmd, "manual"))
- r = do_man (cmd, argc, argv);
- else if (STRCASEEQ (cmd, "more") ||
- STRCASEEQ (cmd, "less"))
- r = do_more (cmd, argc, argv);
- else if (STRCASEEQ (cmd, "reopen"))
- r = do_reopen (cmd, argc, argv);
- else if (STRCASEEQ (cmd, "sparse"))
- r = do_sparse (cmd, argc, argv);
- else if (STRCASEEQ (cmd, "supported"))
- r = do_supported (cmd, argc, argv);
- else if (STRCASEEQ (cmd, "time"))
- r = do_time (cmd, argc, argv);
else
r = run_action (cmd, argc, argv);
void
list_builtin_commands (void)
{
- /* help, man and quit should appear at the top */
+ /* help and quit should appear at the top */
printf ("%-20s %s\n",
"help", _("display a list of commands or help on a command"));
printf ("%-20s %s\n",
- "man", _("read the manual"));
- printf ("%-20s %s\n",
"quit", _("quit guestfish"));
- printf ("%-20s %s\n",
- "alloc", _("allocate an image"));
- printf ("%-20s %s\n",
- "copy-in", _("copy files into an image"));
- printf ("%-20s %s\n",
- "copy-out", _("copy files out of an image"));
- printf ("%-20s %s\n",
- "echo", _("display a line of text"));
- printf ("%-20s %s\n",
- "edit", _("edit a file in the image"));
- printf ("%-20s %s\n",
- "lcd", _("local change directory"));
- printf ("%-20s %s\n",
- "glob", _("expand wildcards in command"));
- printf ("%-20s %s\n",
- "more", _("view a file in the pager"));
- printf ("%-20s %s\n",
- "reopen", _("close and reopen libguestfs handle"));
- printf ("%-20s %s\n",
- "sparse", _("allocate a sparse image file"));
- printf ("%-20s %s\n",
- "supported", _("list supported groups of commands"));
- printf ("%-20s %s\n",
- "time", _("measure time taken to run command"));
-
/* actions are printed after this (see list_commands) */
}
{
/* help for actions is auto-generated, see display_command */
- if (STRCASEEQ (cmd, "alloc") ||
- STRCASEEQ (cmd, "allocate")) {
- printf (_("alloc - allocate an image\n"
- " alloc <filename> <size>\n"
- "\n"
- " This creates an empty (zeroed) file of the given size,\n"
- " and then adds so it can be further examined.\n"
- "\n"
- " For more advanced image creation, see qemu-img utility.\n"
- "\n"
- " Size can be specified using standard suffixes, eg. '1M'.\n"
- ));
- return 0;
- }
- else if (STRCASEEQ (cmd, "copy-in") ||
- STRCASEEQ (cmd, "copy_in")) {
- printf (_("copy-in - copy files into an image\n"
- " copy-in <local> [<local> ...] <remotedir>\n"
- "\n"
- " Copy local files or directories recursively into the\n"
- " image, placing them on a remote directory.\n"
- ));
- return 0;
- }
- else if (STRCASEEQ (cmd, "copy-out") ||
- STRCASEEQ (cmd, "copy_out")) {
- printf (_("copy-out - copy files out of an image\n"
- " copy-out <remote> [<remote> ...] <localdir>\n"
- "\n"
- " Copy remote files or directories recursively out of the\n"
- " image, placing them in a local directory.\n"
- ));
- return 0;
- }
- else if (STRCASEEQ (cmd, "echo")) {
- printf (_("echo - display a line of text\n"
- " echo [<params> ...]\n"
- "\n"
- " This echos the parameters to the terminal.\n"));
- return 0;
- }
- else if (STRCASEEQ (cmd, "edit") ||
- STRCASEEQ (cmd, "vi") ||
- STRCASEEQ (cmd, "emacs")) {
- printf (_("edit - edit a file in the image\n"
- " edit <filename>\n"
- "\n"
- " This is used to edit a file.\n"
- "\n"
- " It is the equivalent of (and is implemented by)\n"
- " running \"cat\", editing locally, and then \"write\".\n"
- "\n"
- " Normally it uses $EDITOR, but if you use the aliases\n"
- " \"vi\" or \"emacs\" you will get those editors.\n"
- "\n"
- " NOTE: This will not work reliably for large files\n"
- " (> 2 MB) or binary files containing \\0 bytes.\n"));
- return 0;
- }
- else if (STRCASEEQ (cmd, "lcd")) {
- printf (_("lcd - local change directory\n"
- " lcd <directory>\n"
- "\n"
- " Change guestfish's current directory. This command is\n"
- " useful if you want to download files to a particular\n"
- " place.\n"));
- return 0;
- }
- else if (STRCASEEQ (cmd, "glob")) {
- printf (_("glob - expand wildcards in command\n"
- " glob <command> [<args> ...]\n"
- "\n"
- " Glob runs <command> with wildcards expanded in any\n"
- " command args. Note that the command is run repeatedly\n"
- " once for each expanded argument.\n"));
- return 0;
- }
- else if (STRCASEEQ (cmd, "man") ||
- STRCASEEQ (cmd, "manual")) {
- printf (_("man - read the manual\n"
- " man\n"
- "\n"
- " Opens the manual page for guestfish.\n"));
- return 0;
- }
- else if (STRCASEEQ (cmd, "help")) {
+ if (STRCASEEQ (cmd, "help")) {
printf (_("help - display a list of commands or help on a command\n"
" help cmd\n"
" help\n"));
return 0;
}
- else if (STRCASEEQ (cmd, "more") ||
- STRCASEEQ (cmd, "less")) {
- printf (_("more - view a file in the pager\n"
- " more <filename>\n"
- "\n"
- " This is used to view a file in the pager.\n"
- "\n"
- " It is the equivalent of (and is implemented by)\n"
- " running \"cat\" and using the pager.\n"
- "\n"
- " Normally it uses $PAGER, but if you use the alias\n"
- " \"less\" then it always uses \"less\".\n"
- "\n"
- " NOTE: This will not work reliably for large files\n"
- " (> 2 MB) or binary files containing \\0 bytes.\n"));
- return 0;
- }
else if (STRCASEEQ (cmd, "quit") ||
STRCASEEQ (cmd, "exit") ||
STRCASEEQ (cmd, "q")) {
" quit\n"));
return 0;
}
- else if (STRCASEEQ (cmd, "reopen")) {
- printf (_("reopen - close and reopen the libguestfs handle\n"
- " reopen\n"
- "\n"
- "Close and reopen the libguestfs handle. It is not necessary to use\n"
- "this normally, because the handle is closed properly when guestfish\n"
- "exits. However this is occasionally useful for testing.\n"));
- return 0;
- }
- else if (STRCASEEQ (cmd, "sparse")) {
- printf (_("sparse - allocate a sparse image file\n"
- " sparse <filename> <size>\n"
- "\n"
- " This creates an empty sparse file of the given size,\n"
- " and then adds so it can be further examined.\n"
- "\n"
- " In all respects it works the same as the 'alloc'\n"
- " command, except that the image file is allocated\n"
- " sparsely, which means that disk blocks are not assigned\n"
- " to the file until they are needed. Sparse disk files\n"
- " only use space when written to, but they are slower\n"
- " and there is a danger you could run out of real disk\n"
- " space during a write operation.\n"
- "\n"
- " For more advanced image creation, see qemu-img utility.\n"
- "\n"
- " Size can be specified using standard suffixes, eg. '1M'.\n"
- ));
- return 0;
- }
- else if (STRCASEEQ (cmd, "supported")) {
- printf (_("supported - list supported groups of commands\n"
- " supported\n"
- "\n"
- " This command returns a list of the optional groups\n"
- " known to the daemon, and indicates which ones are\n"
- " supported by this build of the libguestfs appliance.\n"
- "\n"
- " See also guestfs(3) section AVAILABILITY.\n"
- ));
- return 0;
- }
- else if (STRCASEEQ (cmd, "time")) {
- printf (_("time - measure time taken to run command\n"
- " time <command> [<args> ...]\n"
- "\n"
- " This runs <command> as usual, and prints the elapsed\n"
- " time afterwards.\n"));
- return 0;
- }
else {
fprintf (stderr, _("%s: command not known, use -h to list all commands\n"),
cmd);
#else
(void) write_history (histfile);
#endif
+ clear_history ();
}
#endif
}
static char *
file_in_heredoc (const char *endmarker)
{
- static const char template[] = "/tmp/heredocXXXXXX";
+ TMP_TEMPLATE_ON_STACK (template);
file_in_tmpfile = strdup (template);
if (file_in_tmpfile == NULL) {
perror ("strdup");
}
return ret;
}
-
-/* Read a passphrase ('Key') from /dev/tty with echo off.
- * The caller (cmds.c) will call free on the string afterwards.
- * Based on the code in cryptsetup file lib/utils.c.
- */
-char *
-read_key (const char *param)
-{
- FILE *infp, *outfp;
- struct termios orig, temp;
- char *ret = NULL;
-
- /* Read and write to /dev/tty if available. */
- if (keys_from_stdin ||
- (infp = outfp = fopen ("/dev/tty", "w+")) == NULL) {
- infp = stdin;
- outfp = stdout;
- }
-
- /* Print the prompt and set no echo. */
- int tty = isatty (fileno (infp));
- int tcset = 0;
- if (tty) {
- fprintf (outfp, _("Enter key or passphrase (\"%s\"): "), param);
-
- if (tcgetattr (fileno (infp), &orig) == -1) {
- perror ("tcgetattr");
- goto error;
- }
- memcpy (&temp, &orig, sizeof temp);
- temp.c_lflag &= ~ECHO;
-
- tcsetattr (fileno (infp), TCSAFLUSH, &temp);
- tcset = 1;
- }
-
- size_t n = 0;
- ssize_t len;
- len = getline (&ret, &n, infp);
- if (len == -1) {
- perror ("getline");
- ret = NULL;
- goto error;
- }
-
- /* Remove the terminating \n if there is one. */
- if (len > 0 && ret[len-1] == '\n')
- ret[len-1] = '\0';
-
- error:
- /* Restore echo, close file descriptor. */
- if (tty && tcset) {
- printf ("\n");
- tcsetattr (fileno (infp), TCSAFLUSH, &orig);
- }
-
- if (infp != stdin)
- fclose (infp); /* outfp == infp, so this is closed also */
-
- return ret;
-}