#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 void free_drives (struct drv *drv);
-static void free_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;
" -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 = 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 */
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) {
- /* Display possible mountpoints before exiting. */
- char **fses = guestfs_list_filesystems (g);
- if (fses == NULL || fses[0] == NULL)
- goto out;
- fprintf (stderr,
- _("guestfish: '%s' could not be mounted. Did you mean one of these?\n"),
- mp->device);
- size_t i;
- for (i = 0; fses[i] != NULL; i += 2)
- fprintf (stderr, "\t%s (%s)\n", fses[i], fses[i+1]);
-
- out:
- 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)
{
}
}
-static void
-free_drives (struct drv *drv)
-{
- if (!drv) return;
- free_drives (drv->next);
-
- switch (drv->type) {
- case drv_a: /* a.filename is optarg, don't free it */ break;
- case drv_d: /* d.filename is optarg, don't free it */ break;
- case drv_N:
- free (drv->N.filename);
- free (drv->N.device);
- free_prep_data (drv->N.data);
- break;
- default: ; /* keep GCC happy */
- }
- free (drv);
-}
-
-static void
-free_mps (struct mp *mp)
-{
- if (!mp) return;
- free_mps (mp->next);
-
- /* The drive and mountpoint fields are not allocated
- * from the heap, so we should not free them here.
- */
-
- free (mp);
-}
-
static int
launch (void)
{
"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]);
}
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 (!echo_keys) {
- 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;
-}