예제 #1
0
int
main (int argc, char *argv[])
{
    guestfs_h *g;
    int fd, r;
    char tempdir[] = "/tmp/mlXXXXXX";
    pid_t pid;
    char *shell, *p;

    if (argc != 2) {
        usage ();
        exit (EXIT_FAILURE);
    }

    printf ("\n"
            "This is the 'mount-local' demonstration program.  Follow the\n"
            "instructions on screen.\n"
            "\n"
            "Creating and formatting the disk image, please wait a moment ...\n");
    fflush (stdout);

    /* Create the output disk image: raw sparse. */
    fd = open (argv[1], O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0644);
    if (fd == -1) {
        perror (argv[1]);
        exit (EXIT_FAILURE);
    }
    if (ftruncate (fd, SIZE_MB * 1024 * 1024) == -1) {
        perror ("truncate");
        close (fd);
        exit (EXIT_FAILURE);
    }
    if (close (fd) == -1) {
        perror ("close");
        exit (EXIT_FAILURE);
    }

    /* Guestfs handle. */
    g = guestfs_create ();
    if (g == NULL) {
        perror ("could not create libguestfs handle");
        exit (EXIT_FAILURE);
    }

    /* Create the disk image and format it with a partition and a filesystem. */
    if (guestfs_add_drive_opts (g, argv[1],
                                GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw",
                                -1) == -1)
        exit (EXIT_FAILURE);

    if (guestfs_launch (g) == -1)
        exit (EXIT_FAILURE);

    if (guestfs_part_disk (g, "/dev/sda", "mbr") == -1)
        exit (EXIT_FAILURE);

    if (guestfs_mkfs (g, "ext2", "/dev/sda1") == -1)
        exit (EXIT_FAILURE);

    /* Mount the empty filesystem. */
    if (guestfs_mount_options (g, MOUNT_OPTIONS, "/dev/sda1", "/") == -1)
        exit (EXIT_FAILURE);

    /* Create a file in the new filesystem. */
    if (guestfs_touch (g, "/PUT_FILES_AND_DIRECTORIES_HERE") == -1)
        exit (EXIT_FAILURE);

    /* Create a temporary mount directory. */
    if (mkdtemp (tempdir) == NULL) {
        perror ("mkdtemp");
        exit (EXIT_FAILURE);
    }

    /* Mount the filesystem. */
    if (guestfs_mount_local (g, tempdir, -1) == -1)
        exit (EXIT_FAILURE);

    /* Fork the shell for the user. */
    pid = fork ();
    if (pid == -1) {
        perror ("fork");
        exit (EXIT_FAILURE);
    }

    if (pid == 0) {               /* Child. */
        if (chdir (tempdir) == -1) {
            perror (tempdir);
            _exit (EXIT_FAILURE);
        }

        printf ("\n"
                "The *current directory* is a FUSE filesystem backed by the disk\n"
                "image which is managed by libguestfs.  Any files or directories\n"
                "you copy into here (up to %d MB) will be saved into the disk\n"
                "image.  You can also delete files, create certain special files\n"
                "and so on.\n"
                "\n"
                "When you have finished adding files, hit ^D or type 'exit' to\n"
                "exit the shell and return to the mount-local program.\n"
                "\n",
                SIZE_MB);

        shell = getenv ("SHELL");
        if (!shell)
            r = system ("/bin/sh");
        else {
            /* Set a magic prompt.  We only know how to do this for bash. */
            p = strrchr (shell, '/');
            if (p && strcmp (p+1, "bash") == 0) {
                size_t len = 64 + strlen (shell);
                char buf[len];

                snprintf (buf, len, "PS1='mount-local-shell> ' %s --norc -i", shell);
                r = system (buf);
            } else
                r = system (shell);
        }
        if (r == -1) {
            fprintf (stderr, "error: failed to run sub-shell (%s) "
                     "(is $SHELL set correctly?)\n",
                     shell);
            //FALLTHROUGH
        }

        chdir ("/");
        guestfs_umount_local (g, GUESTFS_UMOUNT_LOCAL_RETRY, 1, -1);
        _exit (EXIT_SUCCESS);
    }

    /* Note that we are *not* waiting for the child yet.  We want to
     * run the FUSE code in parallel with the subshell.
     */

    /* We're going to hide libguestfs errors here, but in a real program
     * you would probably want to log them somewhere.
     */
    guestfs_push_error_handler (g, NULL, NULL);

    /* Now run the FUSE thread. */
    if (guestfs_mount_local_run (g) == -1)
        exit (EXIT_FAILURE);

    guestfs_pop_error_handler (g);

    waitpid (pid, NULL, 0);

    /* Shutdown the handle explicitly so write errors can be detected. */
    if (guestfs_shutdown (g) == -1)
        exit (EXIT_FAILURE);

    guestfs_close (g);

    printf ("\n"
            "Any files or directories that you copied in have been saved into\n"
            "the disk image called '%s'.\n"
            "\n"
            "Try opening the disk image with guestfish to see those files:\n"
            "\n"
            "  guestfish -a %s -m /dev/sda1\n"
            "\n",
            argv[1], argv[1]);

    exit (EXIT_SUCCESS);
}
예제 #2
0
int
main (int argc, char *argv[])
{
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEBASEDIR);
  textdomain (PACKAGE);

  parse_config ();

  enum { HELP_OPTION = CHAR_MAX + 1 };

  /* The command line arguments are broadly compatible with (a subset
   * of) guestfish.  Thus we have to deal mainly with -a, -m and --ro.
   */
  static const char options[] = "a:c:d:im:no:rvVwx";
  static const struct option long_options[] = {
    { "add", 1, 0, 'a' },
    { "connect", 1, 0, 'c' },
    { "dir-cache-timeout", 1, 0, 0 },
    { "domain", 1, 0, 'd' },
    { "echo-keys", 0, 0, 0 },
    { "fd", 1, 0, 0 },
    { "format", 2, 0, 0 },
    { "fuse-help", 0, 0, 0 },
    { "help", 0, 0, HELP_OPTION },
    { "inspector", 0, 0, 'i' },
    { "keys-from-stdin", 0, 0, 0 },
    { "live", 0, 0, 0 },
    { "long-options", 0, 0, 0 },
    { "mount", 1, 0, 'm' },
    { "no-fork", 0, 0, 0 },
    { "no-sync", 0, 0, 'n' },
    { "option", 1, 0, 'o' },
    { "pid-file", 1, 0, 0 },
    { "ro", 0, 0, 'r' },
    { "rw", 0, 0, 'w' },
    { "selinux", 0, 0, 0 },
    { "short-options", 0, 0, 0 },
    { "trace", 0, 0, 'x' },
    { "verbose", 0, 0, 'v' },
    { "version", 0, 0, 'V' },
    { 0, 0, 0, 0 }
  };

  struct drv *drvs = NULL;
  struct mp *mps = NULL;
  struct mp *mp;
  char *p;
  const char *format = NULL;
  bool format_consumed = true;
  int c, r;
  int option_index;
  struct sigaction sa;

  int debug_calls = 0;
  int dir_cache_timeout = -1;
  int do_fork = 1;
  char *fuse_options = NULL;
  char *pid_file = NULL;
  int pipe_fd = -1;

  struct guestfs_mount_local_argv optargs;

  /* LC_ALL=C is required so we can parse error messages. */
  setenv ("LC_ALL", "C", 1);

  memset (&sa, 0, sizeof sa);
  sa.sa_handler = SIG_IGN;
  sa.sa_flags = SA_RESTART;
  sigaction (SIGPIPE, &sa, NULL);

  g = guestfs_create ();
  if (g == NULL)
    error (EXIT_FAILURE, errno, "guestfs_create");

  for (;;) {
    c = getopt_long (argc, argv, options, long_options, &option_index);
    if (c == -1) break;

    switch (c) {
    case 0:			/* options which are long only */
      if (STREQ (long_options[option_index].name, "long-options"))
        display_long_options (long_options);
      else if (STREQ (long_options[option_index].name, "short-options"))
        display_short_options (options);
      else if (STREQ (long_options[option_index].name, "dir-cache-timeout"))
        dir_cache_timeout = atoi (optarg);
      else if (STREQ (long_options[option_index].name, "fuse-help"))
        fuse_help ();
      else if (STREQ (long_options[option_index].name, "selinux")) {
        /* nothing */
      } else if (STREQ (long_options[option_index].name, "format")) {
        OPTION_format;
      } else if (STREQ (long_options[option_index].name, "keys-from-stdin")) {
        keys_from_stdin = 1;
      } else if (STREQ (long_options[option_index].name, "echo-keys")) {
        echo_keys = 1;
      } else if (STREQ (long_options[option_index].name, "live")) {
        live = 1;
      } else if (STREQ (long_options[option_index].name, "pid-file")) {
        pid_file = optarg;
      } else if (STREQ (long_options[option_index].name, "no-fork")) {
        do_fork = 0;
      } else if (STREQ (long_options[option_index].name, "fd")) {
        if (sscanf (optarg, "%d", &pipe_fd) != 1 || pipe_fd < 0)
          error (EXIT_FAILURE, 0,
                 _("unable to parse --fd option value: %s"), optarg);
      } else
        error (EXIT_FAILURE, 0,
               _("unknown long option: %s (%d)"),
               long_options[option_index].name, option_index);
      break;

    case 'a':
      OPTION_a;
      break;

    case 'c':
      OPTION_c;
      break;

    case 'd':
      OPTION_d;
      break;

    case 'i':
      OPTION_i;
      break;

    case 'm':
      OPTION_m;
      break;

    case 'n':
      OPTION_n;
      break;

    case 'o':
      fuse_opt_add_opt_escaped (&fuse_options, optarg);
      break;

    case 'r':
      OPTION_r;
      break;

    case 'v':
      OPTION_v;
      break;

    case 'V':
      OPTION_V;
      break;

    case 'w':
      OPTION_w;
      break;

    case 'x':
      OPTION_x;
      debug_calls = 1;
      do_fork = 0;
      break;

    case HELP_OPTION:
      usage (EXIT_SUCCESS);

    default:
      usage (EXIT_FAILURE);
    }
  }

  CHECK_OPTION_format_consumed;

  /* Check we have the right options. */
  if (!live) {
    if (drvs == NULL) {
      fprintf (stderr, _("%s: error: you must specify at least one -a or -d option.\n"),
               getprogname ());
      usage (EXIT_FAILURE);
    }
    if (!(mps || inspector)) {
      fprintf (stderr, _("%s: error: you must specify either -i at least one -m option.\n"),
               getprogname ());
      usage (EXIT_FAILURE);
    }
  } else {
    size_t count_d = 0, count_other = 0;
    struct drv *drv;

    if (read_only)
      error (EXIT_FAILURE, 0, _("--live is not compatible with --ro option"));

    if (inspector)
      error (EXIT_FAILURE, 0, _("--live is not compatible with -i option"));

    /* --live: make sure there was one -d option and no -a options */
    for (drv = drvs; drv; drv = drv->next) {
      if (drv->type == drv_d)
        count_d++;
      else
        count_other++;
    }

    if (count_d != 1)
      error (EXIT_FAILURE, 0,
             _("with --live, you must use exactly one -d option"));

    if (count_other != 0)
      error (EXIT_FAILURE, 0, _("--live is not compatible with -a option"));
  }

  /* We'd better have a mountpoint. */
  if (optind+1 != argc)
    error (EXIT_FAILURE, 0,
           _("you must specify a mountpoint in the host filesystem"));

  /* If we're forking, we can't use the recovery process. */
  if (guestfs_set_recovery_proc (g, !do_fork) == -1)
    exit (EXIT_FAILURE);

  /* Do the guest drives and mountpoints. */
  add_drives (drvs, 'a');
  if (guestfs_launch (g) == -1)
    exit (EXIT_FAILURE);
  if (inspector)
    inspect_mount ();
  mount_mps (mps);

  free_drives (drvs);
  free_mps (mps);

  /* FUSE example does this, not clear if it's necessary, but ... */
  if (guestfs_umask (g, 0) == -1)
    exit (EXIT_FAILURE);

  optargs.bitmask = 0;
  if (read_only) {
    optargs.bitmask |= GUESTFS_MOUNT_LOCAL_READONLY_BITMASK;
    optargs.readonly = 1;
  }
  if (debug_calls) {
    optargs.bitmask |= GUESTFS_MOUNT_LOCAL_DEBUGCALLS_BITMASK;
    optargs.debugcalls = 1;
  }
  if (dir_cache_timeout > 0) {
    optargs.bitmask |= GUESTFS_MOUNT_LOCAL_CACHETIMEOUT_BITMASK;
    optargs.cachetimeout = dir_cache_timeout;
  }
  if (fuse_options != NULL) {
    optargs.bitmask |= GUESTFS_MOUNT_LOCAL_OPTIONS_BITMASK;
    optargs.options = fuse_options;
  }

  if (guestfs_mount_local_argv (g, argv[optind], &optargs) == -1)
    exit (EXIT_FAILURE);

  /* Daemonize. */
  if (do_fork) {
    pid_t pid;
    int fd;

    pid = fork ();
    if (pid == -1)
      error (EXIT_FAILURE, errno, "fork");

    if (pid != 0) {             /* parent */
      if (write_pid_file (pid_file, pid) == -1)
        _exit (EXIT_FAILURE);
      if (write_pipe_fd (pipe_fd) == -1)
        _exit (EXIT_FAILURE);

      _exit (EXIT_SUCCESS);
    }

    /* Emulate what old fuse_daemonize used to do. */
    if (setsid () == -1)
      error (EXIT_FAILURE, errno, "setsid");

    ignore_value (chdir ("/"));

    fd = open ("/dev/null", O_RDWR);
    if (fd >= 0) {
      dup2 (fd, 0);
      dup2 (fd, 1);
      dup2 (fd, 2);
      if (fd > 2)
        close (fd);
    }
  }
  else {
    /* not forking, write PID file and pipe FD anyway */
    if (write_pid_file (pid_file, getpid ()) == -1)
      exit (EXIT_FAILURE);
    if (write_pipe_fd (pipe_fd) == -1)
      exit (EXIT_FAILURE);
  }

  /* At the last minute, remove the libguestfs error handler.  In code
   * above this point, the default error handler has been used which
   * sends all errors to stderr.  From now on, the FUSE code will
   * convert errors into error codes (errnos) when appropriate.
   */
  guestfs_push_error_handler (g, NULL, NULL);

  /* Main loop. */
  r = guestfs_mount_local_run (g);

  guestfs_pop_error_handler (g);

  /* Cleanup. */
  if (guestfs_shutdown (g) == -1)
    r = -1;
  guestfs_close (g);

  /* Don't delete PID file until the cleanup has been completed. */
  if (pid_file)
    unlink (pid_file);

  exit (r == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}