X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=fish%2Ffish.c;h=461f55b8ba6ffd519cf0e2ba760f3d018a25a6f8;hp=bd13a827dbc12c341a6b3a2fc02dfc8a17086a9b;hb=bece54704bb081339a87233bbc5401f2892467be;hpb=4c50f4c38d9a50fbf983c79dd208d1b1598fef8a;ds=sidebyside diff --git a/fish/fish.c b/fish/fish.c index bd13a82..461f55b 100644 --- a/fish/fish.c +++ b/fish/fish.c @@ -42,19 +42,23 @@ #include "closeout.h" #include "progname.h" +struct drv { + struct drv *next; + char *filename; /* disk filename (for -a or -N options) */ + prep_data *data; /* prepared type (for -N option only) */ + char *device; /* device inside the appliance */ +}; + struct mp { struct mp *next; char *device; char *mountpoint; }; -struct drv { - struct drv *next; - char *filename; -}; - static void add_drives (struct drv *drv); +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); static void script (int prompt); @@ -69,24 +73,11 @@ guestfs_h *g; int read_only = 0; int quit = 0; int verbose = 0; -int echo_commands = 0; int remote_control_listen = 0; int remote_control = 0; int exit_on_error = 1; int command_num = 0; -int -launch (guestfs_h *_g) -{ - assert (_g == g); - - if (guestfs_is_config (g)) { - if (guestfs_launch (g) == -1) - return -1; - } - return 0; -} - static void __attribute__((noreturn)) usage (int status) { @@ -119,6 +110,7 @@ usage (int status) " --listen Listen for remote commands\n" " -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n" " -n|--no-sync Don't autosync\n" + " -N|--new type Create prepared disk (test1.img, ...)\n" " --remote[=pid] Send commands to remote %s\n" " -r|--ro Mount read-only\n" " --selinux Enable SELinux support\n" @@ -147,7 +139,7 @@ main (int argc, char *argv[]) enum { HELP_OPTION = CHAR_MAX + 1 }; - static const char *options = "a:Df:h::im:nrv?Vx"; + static const char *options = "a:Df:h::im:nN:rv?Vx"; static const struct option long_options[] = { { "add", 1, 0, 'a' }, { "cmd-help", 2, 0, 'h' }, @@ -156,6 +148,7 @@ main (int argc, char *argv[]) { "inspector", 0, 0, 'i' }, { "listen", 0, 0, 0 }, { "mount", 1, 0, 'm' }, + { "new", 1, 0, 'N' }, { "no-dest-paths", 0, 0, 'D' }, { "no-sync", 0, 0, 'n' }, { "remote", 2, 0, 0 }, @@ -174,6 +167,8 @@ main (int argc, char *argv[]) int inspector = 0; int option_index; struct sigaction sa; + char next_drive = 'a'; + int next_prepared_drive = 1; initialize_readline (); @@ -259,6 +254,36 @@ main (int argc, char *argv[]) exit (EXIT_FAILURE); } drv->filename = optarg; + drv->data = NULL; + /* We could fill the device field in, but in fact we + * only use it for the -N option at present. + */ + drv->device = NULL; + drv->next = drvs; + drvs = drv; + next_drive++; + break; + + case 'N': + if (STRCASEEQ (optarg, "list")) { + list_prepared_drives (); + exit (EXIT_SUCCESS); + } + drv = malloc (sizeof (struct drv)); + if (!drv) { + perror ("malloc"); + exit (EXIT_FAILURE); + } + if (asprintf (&drv->filename, "test%d.img", + next_prepared_drive++) == -1) { + perror ("asprintf"); + exit (EXIT_FAILURE); + } + drv->data = create_prepared_file (optarg, drv->filename); + if (asprintf (&drv->device, "/dev/sd%c", next_drive++) == -1) { + perror ("asprintf"); + exit (EXIT_FAILURE); + } drv->next = drvs; drvs = drv; break; @@ -324,7 +349,7 @@ main (int argc, char *argv[]) exit (EXIT_SUCCESS); case 'x': - echo_commands = 1; + guestfs_set_trace (g, 1); break; case HELP_OPTION: @@ -342,8 +367,8 @@ main (int argc, char *argv[]) if (drvs || mps || remote_control_listen || remote_control || guestfs_get_selinux (g)) { - fprintf (stderr, _("%s: cannot use -i option with -a, -m," - " --listen, --remote or --selinux\n"), + fprintf (stderr, _("%s: cannot use -i option with -a, -m, -N, " + "--listen, --remote or --selinux\n"), program_name); exit (EXIT_FAILURE); } @@ -396,9 +421,12 @@ main (int argc, char *argv[]) /* If we've got drives to add, add them now. */ add_drives (drvs); - /* If we've got mountpoints, we must launch the guest and mount them. */ - if (mps != NULL) { - if (launch (g) == -1) exit (EXIT_FAILURE); + /* If we've got mountpoints or prepared drives, we must launch the + * guest and mount them. + */ + if (next_prepared_drive > 1 || mps != NULL) { + if (launch () == -1) exit (EXIT_FAILURE); + prepare_drives (drvs); mount_mps (mps); } @@ -495,7 +523,8 @@ add_drives (struct drv *drv) if (drv) { add_drives (drv->next); - if (!read_only) + + if (drv->data /* -N option is not affected by --ro */ || !read_only) r = guestfs_add_drive (g, drv->filename); else r = guestfs_add_drive_ro (g, drv->filename); @@ -505,6 +534,26 @@ add_drives (struct drv *drv) } static void +prepare_drives (struct drv *drv) +{ + if (drv) { + prepare_drives (drv->next); + if (drv->data) + prepare_drive (drv->filename, drv->data, drv->device); + } +} + +static int +launch (void) +{ + if (guestfs_is_config (g)) { + if (guestfs_launch (g) == -1) + return -1; + } + return 0; +} + +static void interactive (void) { script (1); @@ -571,7 +620,8 @@ script (int prompt) "Welcome to guestfish, the libguestfs filesystem interactive shell for\n" "editing virtual machine filesystems.\n" "\n" - "Type: 'help' for help with commands\n" + "Type: 'help' for a list of commands\n" + " 'man' to read the manual\n" " 'quit' to quit the shell\n" "\n")); @@ -768,6 +818,15 @@ cmdline (char *argv[], int optind, int argc) fprintf (stderr, _("%s: empty command on command line\n"), program_name); exit (EXIT_FAILURE); } + + /* Allow -cmd on the command line to mean (temporarily) override + * the normal exit on error (RHBZ#578407). + */ + if (cmd[0] == '-') { + exit_on_error = 0; + cmd++; + } + params = &argv[optind]; /* Search for end of command list or ":" ... */ @@ -775,10 +834,12 @@ cmdline (char *argv[], int optind, int argc) optind++; if (optind == argc) { - if (issue_command (cmd, params, NULL) == -1) exit (EXIT_FAILURE); + if (issue_command (cmd, params, NULL) == -1 && exit_on_error) + exit (EXIT_FAILURE); } else { argv[optind] = NULL; - if (issue_command (cmd, params, NULL) == -1) exit (EXIT_FAILURE); + if (issue_command (cmd, params, NULL) == -1 && exit_on_error) + exit (EXIT_FAILURE); cmdline (argv, optind+1, argc); } } @@ -794,13 +855,6 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd) /* This counts the commands issued, starting at 1. */ command_num++; - if (echo_commands) { - printf ("%s", cmd); - for (i = 0; argv[i] != NULL; ++i) - printf (" %s", argv[i]); - printf ("\n"); - } - /* For | ... commands. Annoyingly we can't use popen(3) here. */ if (pipecmd) { int fd[2]; @@ -881,6 +935,9 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd) 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); @@ -920,10 +977,12 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd) void list_builtin_commands (void) { - /* help and quit should appear at the top */ + /* help, man 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", @@ -1008,6 +1067,12 @@ display_builtin_command (const char *cmd) " Glob runs with wildcards expanded in any\n" " command args. Note that the command is run repeatedly\n" " once for each expanded argument.\n")); + else if (STRCASEEQ (cmd, "man") || + STRCASEEQ (cmd, "manual")) + printf (_("man - read the manual\n" + " man\n" + "\n" + " Opens the manual page for guestfish.\n")); else if (STRCASEEQ (cmd, "help")) printf (_("help - display a list of commands or help on a command\n" " help cmd\n" @@ -1408,3 +1473,144 @@ resolve_win_path (const char *path) return ret; } + +/* Resolve the special FileIn paths ("-" or "-< or "END\n" in input. */ + size_t blen = strlen (buffer); + if (STREQLEN (buffer, endmarker, markerlen) && + (blen == markerlen || + (blen == markerlen+1 && buffer[markerlen] == '\n'))) + goto found_end; + + if (xwrite (fd, buffer, blen) == -1) { + if (!write_error) perror ("write"); + write_error = 1; + /* continue reading up to the end marker */ + } + } + + /* Reached EOF of stdin without finding the end marker, which + * is likely to be an error. + */ + fprintf (stderr, "%s: end of input reached without finding '%s'\n", + program_name, endmarker); + goto error2; + + found_end: + if (write_error) { + close (fd); + goto error2; + } + + if (close (fd) == -1) { + perror ("close"); + goto error2; + } + + return file_in_tmpfile; + + error2: + unlink (file_in_tmpfile); + + error1: + free (file_in_tmpfile); + file_in_tmpfile = NULL; + return NULL; +} + +void +free_file_in (char *s) +{ + if (file_in_tmpfile) { + if (unlink (file_in_tmpfile) == -1) + perror (file_in_tmpfile); + file_in_tmpfile = NULL; + } + + /* Free the device or file name which was strdup'd in file_in(). + * Note it's not immediately clear, but for -<< heredocs, + * s == file_in_tmpfile, so this frees up that buffer. + */ + free (s); +} + +/* Resolve the special FileOut paths ("-" or filename). + * The caller (cmds.c) will call free (str) after the command has run. + */ +char * +file_out (const char *arg) +{ + char *ret; + + if (STREQ (arg, "-")) + ret = strdup ("/dev/stdout"); + else + ret = strdup (arg); + + if (!ret) { + perror ("strdup"); + return NULL; + } + return ret; +}