Ejemplo n.º 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);
}
Ejemplo n.º 2
0
/* Worker thread. */
static void *
start_thread (void *thread_data_vp)
{
  struct thread_data *thread_data = thread_data_vp;
  int quit = 0;
  int err;
  size_t i;
  guestfs_h *g;
  unsigned errors = 0;

  for (;;) {
    /* Take the next process. */
    err = pthread_mutex_lock (&mutex);
    if (err != 0) {
      fprintf (stderr, "%s: pthread_mutex_lock: %s",
               guestfs_int_program_name, strerror (err));
      goto error;
    }

    i = n;
    if (i > 0) {
      printf ("%zu to go ...          \r", n);
      fflush (stdout);

      n--;
    }
    else
      quit = 1;

    err = pthread_mutex_unlock (&mutex);
    if (err != 0) {
      fprintf (stderr, "%s: pthread_mutex_unlock: %s",
               guestfs_int_program_name, strerror (err));
      goto error;
    }

    if (quit)                   /* Work finished. */
      break;

    g = guestfs_create ();
    if (g == NULL) {
      perror ("guestfs_create");
      errors++;
      if (!ignore_errors)
        goto error;
    }

    guestfs_set_trace (g, trace);
    guestfs_set_verbose (g, verbose);

    if (guestfs_add_drive_ro (g, "/dev/null") == -1) {
      errors++;
      if (!ignore_errors)
        goto error;
    }

    if (guestfs_launch (g) == -1) {
      errors++;
      if (!ignore_errors)
        goto error;
    }

    if (guestfs_shutdown (g) == -1) {
      errors++;
      if (!ignore_errors)
        goto error;
    }

    guestfs_close (g);
  }

  if (errors > 0) {
    fprintf (stderr, "%s: thread %d: %u errors were ignored\n",
             guestfs_int_program_name, thread_data->thread_num, errors);
    goto error;
  }

  thread_data->r = 0;
  return &thread_data->r;

 error:
  thread_data->r = -1;
  return &thread_data->r;
}
Ejemplo n.º 3
0
static void
test_virtio_serial (void)
{
  int fd, r, eh;
  char tmpfile[] = "/tmp/speedtestXXXXXX";
  struct sigaction sa, old_sa;

  if (!virtio_serial_upload && !virtio_serial_download)
    return;

  /* Create a sparse file.  We could upload from /dev/zero, but we
   * won't get progress messages because libguestfs tests if the
   * source file is a regular file.
   */
  fd = mkstemp (tmpfile);
  if (fd == -1)
    error (EXIT_FAILURE, errno, "mkstemp: %s", tmpfile);
  if (ftruncate (fd, TEST_SERIAL_MAX_SIZE) == -1)
    error (EXIT_FAILURE, errno, "ftruncate");
  if (close (fd) == -1)
    error (EXIT_FAILURE, errno, "close");

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

  if (guestfs_add_drive_scratch (g, INT64_C (100*1024*1024), -1) == -1)
    exit (EXIT_FAILURE);

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

  /* Make and mount a filesystem which will be used by the download test. */
  if (guestfs_mkfs (g, "ext4", "/dev/sda") == -1)
    exit (EXIT_FAILURE);
  if (guestfs_mount (g, "/dev/sda", "/") == -1)
    exit (EXIT_FAILURE);

  /* Time out the upload after TEST_SERIAL_MAX_TIME seconds have passed. */
  memset (&sa, 0, sizeof sa);
  sa.sa_handler = stop_transfer;
  sa.sa_flags = SA_RESTART;
  sigaction (SIGALRM, &sa, &old_sa);

  /* Get progress messages, which will tell us how much data has been
   * transferred.
   */
  eh = guestfs_set_event_callback (g, progress_cb, GUESTFS_EVENT_PROGRESS,
                                   0, NULL);
  if (eh == -1)
    exit (EXIT_FAILURE);

  if (virtio_serial_upload) {
    gettimeofday (&start, NULL);
    rate = -1;
    operation = "upload";
    alarm (max_time_override > 0 ? max_time_override : TEST_SERIAL_MAX_TIME);

    /* For the upload test, upload the sparse file to /dev/null in the
     * appliance.  Hopefully this is mostly testing just virtio-serial.
     */
    guestfs_push_error_handler (g, NULL, NULL);
    r = guestfs_upload (g, tmpfile, "/dev/null");
    alarm (0);
    unlink (tmpfile);
    guestfs_pop_error_handler (g);

    /* It's possible that the upload will finish before the alarm fires,
     * or that the upload will be stopped by the alarm.
     */
    if (r == -1 && guestfs_last_errno (g) != EINTR) {
      fprintf (stderr,
               "%s: expecting upload command to return EINTR\n%s\n",
               guestfs_int_program_name, guestfs_last_error (g));
      exit (EXIT_FAILURE);
    }

    if (rate == -1) {
    rate_error:
      fprintf (stderr, "%s: internal error: progress callback was not called! (r=%d, errno=%d)\n",
               guestfs_int_program_name,
               r, guestfs_last_errno (g));
      exit (EXIT_FAILURE);
    }

    print_rate ("virtio-serial upload rate:", rate);
  }

  if (virtio_serial_download) {
    /* For the download test, download a sparse file within the
     * appliance to /dev/null on the host.
     */
    if (guestfs_touch (g, "/sparse") == -1)
      exit (EXIT_FAILURE);
    if (guestfs_truncate_size (g, "/sparse", TEST_SERIAL_MAX_SIZE) == -1)
      exit (EXIT_FAILURE);

    gettimeofday (&start, NULL);
    rate = -1;
    operation = "download";
    alarm (max_time_override > 0 ? max_time_override : TEST_SERIAL_MAX_TIME);
    guestfs_push_error_handler (g, NULL, NULL);
    r = guestfs_download (g, "/sparse", "/dev/null");
    alarm (0);
    guestfs_pop_error_handler (g);

    if (r == -1 && guestfs_last_errno (g) != EINTR) {
      fprintf (stderr,
               "%s: expecting download command to return EINTR\n%s\n",
               guestfs_int_program_name, guestfs_last_error (g));
      exit (EXIT_FAILURE);
    }

    if (rate == -1)
      goto rate_error;

    print_rate ("virtio-serial download rate:", rate);
  }

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

  guestfs_close (g);

  /* Restore SIGALRM signal handler. */
  sigaction (SIGALRM, &old_sa, NULL);
}
Ejemplo n.º 4
0
static void
test_block_device (void)
{
  int fd;
  char tmpfile[] = "/tmp/speedtestXXXXXX";
  CLEANUP_FREE char **devices = NULL;
  char *r;
  const char *argv[4];
  int t = max_time_override > 0 ? max_time_override : TEST_BLOCK_DEVICE_TIME;
  char tbuf[64];
  int64_t bytes_written, bytes_read;

  if (!block_device_write && !block_device_read)
    return;

  snprintf (tbuf, sizeof tbuf, "%d", t);

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

  /* Create a fully allocated backing file.  Note we are not testing
   * the speed of allocation on the host.
   */
  fd = mkstemp (tmpfile);
  if (fd == -1)
    error (EXIT_FAILURE, errno, "mkstemp: %s", tmpfile);
  close (fd);

  if (guestfs_disk_create (g, tmpfile, "raw",
                           INT64_C (1024*1024*1024),
                           GUESTFS_DISK_CREATE_PREALLOCATION, "full",
                           -1) == -1)
    exit (EXIT_FAILURE);

  if (guestfs_add_drive (g, tmpfile) == -1)
    exit (EXIT_FAILURE);

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

  devices = guestfs_list_devices (g);
  if (devices == NULL)
    exit (EXIT_FAILURE);
  if (devices[0] == NULL) {
    fprintf (stderr, "%s: expected guestfs_list_devices to return at least 1 device\n",
             guestfs_int_program_name);
    exit (EXIT_FAILURE);
  }

  if (block_device_write) {
    /* Test write speed. */
    argv[0] = devices[0];
    argv[1] = "w";
    argv[2] = tbuf;
    argv[3] = NULL;
    r = guestfs_debug (g, "device_speed", (char **) argv);
    if (r == NULL)
      exit (EXIT_FAILURE);

    if (sscanf (r, "%" SCNi64, &bytes_written) != 1) {
      fprintf (stderr, "%s: could not parse device_speed output\n",
               guestfs_int_program_name);
      exit (EXIT_FAILURE);
    }

    print_rate ("block device writes:", bytes_written / t);
  }

  if (block_device_read) {
    /* Test read speed. */
    argv[0] = devices[0];
    argv[1] = "r";
    argv[2] = tbuf;
    argv[3] = NULL;
    r = guestfs_debug (g, "device_speed", (char **) argv);
    if (r == NULL)
      exit (EXIT_FAILURE);

    if (sscanf (r, "%" SCNi64, &bytes_read) != 1) {
      fprintf (stderr, "%s: could not parse device_speed output\n",
               guestfs_int_program_name);
      exit (EXIT_FAILURE);
    }

    print_rate ("block device reads:", bytes_read / t);
  }

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

  guestfs_close (g);

  /* Remove temporary file. */
  unlink (tmpfile);
}
Ejemplo n.º 5
0
/* Worker thread. */
static void *
start_thread (void *thread_data_vp)
{
  struct thread_data *thread_data = thread_data_vp;
  int quit = 0;
  int err;
  size_t i;
  guestfs_h *g;
  unsigned errors = 0;
  char id[64];

  for (;;) {
    CLEANUP_FREE char *log_file = NULL;
    CLEANUP_FCLOSE FILE *log_fp = NULL;

    /* Take the next process. */
    err = pthread_mutex_lock (&mutex);
    if (err != 0) {
      fprintf (stderr, "%s: pthread_mutex_lock: %s",
               guestfs_int_program_name, strerror (err));
      goto error;
    }

    i = n;
    if (i > 0) {
      printf ("%zu to go ...          \r", n);
      fflush (stdout);

      n--;
    }
    else
      quit = 1;

    err = pthread_mutex_unlock (&mutex);
    if (err != 0) {
      fprintf (stderr, "%s: pthread_mutex_unlock: %s",
               guestfs_int_program_name, strerror (err));
      goto error;
    }

    if (quit)                   /* Work finished. */
      break;

    g = guestfs_create ();
    if (g == NULL) {
      perror ("guestfs_create");
      errors++;
      if (!ignore_errors)
        goto error;
    }

    /* Only if using --log, set up a callback.  See examples/debug-logging.c */
    if (log_template != NULL) {
      size_t j, k;

      log_file = malloc (log_file_size + 1);
      if (log_file == NULL) abort ();
      for (j = 0, k = 0; j < strlen (log_template); ++j) {
        if (log_template[j] == '%') {
          snprintf (&log_file[k], log_file_size - k, "%zu", i);
          k += strlen (&log_file[k]);
        }
        else
          log_file[k++] = log_template[j];
      }
      log_file[k] = '\0';
      log_fp = fopen (log_file, "w");
      if (log_fp == NULL) {
        perror (log_file);
        abort ();
      }
      guestfs_set_event_callback (g, message_callback,
                                  event_bitmask, 0, log_fp);
    }

    snprintf (id, sizeof id, "%zu", i);
    guestfs_set_identifier (g, id);

    guestfs_set_trace (g, trace);
    guestfs_set_verbose (g, verbose);

    if (guestfs_add_drive_ro (g, "/dev/null") == -1) {
      errors++;
      if (!ignore_errors)
        goto error;
    }

    if (guestfs_launch (g) == -1) {
      errors++;
      if (!ignore_errors)
        goto error;
    }

    if (guestfs_shutdown (g) == -1) {
      errors++;
      if (!ignore_errors)
        goto error;
    }

    guestfs_close (g);
  }

  if (errors > 0) {
    fprintf (stderr, "%s: thread %d: %u errors were ignored\n",
             guestfs_int_program_name, thread_data->thread_num, errors);
    goto error;
  }

  thread_data->r = 0;
  return &thread_data->r;

 error:
  thread_data->r = -1;
  return &thread_data->r;
}
Ejemplo n.º 6
0
int
guestfs__kill_subprocess (guestfs_h *g)
{
  return guestfs_shutdown (g);
}
Ejemplo n.º 7
0
void
guestfs_close (guestfs_h *g)
{
  struct qemu_param *qp, *qp_next;

  if (g->state == NO_HANDLE) {
    /* Not safe to call ANY callbacks here, so ... */
    fprintf (stderr, _("guestfs_close: called twice on the same handle\n"));
    return;
  }

  /* Remove the handle from the handles list. */
  gl_lock_lock (handles_lock);
  if (handles == g)
    handles = g->next;
  else {
    guestfs_h *gg;

    for (gg = handles; gg->next != g; gg = gg->next)
      ;
    gg->next = g->next;
  }
  gl_lock_unlock (handles_lock);

  if (g->trace) {
    const char trace_msg[] = "close";

    guestfs___call_callbacks_message (g, GUESTFS_EVENT_TRACE,
                                      trace_msg, strlen (trace_msg));
  }

  debug (g, "closing guestfs handle %p (state %d)", g, g->state);

  /* If we are valgrinding the daemon, then we *don't* want to kill
   * the subprocess because we want the final valgrind messages sent
   * when we close sockets below.  However for normal production use,
   * killing the subprocess is the right thing to do (in case the
   * daemon or qemu is not responding).
   */
#ifndef VALGRIND_DAEMON
  if (g->state != CONFIG)
    ignore_value (guestfs_shutdown (g));
#endif

  /* Run user close callbacks. */
  guestfs___call_callbacks_void (g, GUESTFS_EVENT_CLOSE);

  /* Remove whole temporary directory. */
  guestfs___remove_tmpdir (g->tmpdir);

  /* Mark the handle as dead and then free up all memory. */
  g->state = NO_HANDLE;

  free (g->events);
  g->nr_events = 0;
  g->events = NULL;

#if HAVE_FUSE
  guestfs___free_fuse (g);
#endif

  guestfs___free_inspect_info (g);
  guestfs___free_drives (&g->drives);

  for (qp = g->qemu_params; qp; qp = qp_next) {
    free (qp->qemu_param);
    free (qp->qemu_value);
    qp_next = qp->next;
    free (qp);
  }

  if (g->pda)
    hash_free (g->pda);
  free (g->tmpdir);
  free (g->last_error);
  free (g->path);
  free (g->qemu);
  free (g->append);
  free (g);
}
Ejemplo n.º 8
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);
}
Ejemplo n.º 9
0
int
main (int argc, char *argv[])
{
  pid_t md5pid;
  guestfs_h *g;
  int r;
  size_t i;
  int pipefd[2];
  char md5[33];
  int status;

  unlink ("streaming.fifo");
  if (mknod ("streaming.fifo", S_IFIFO | 0600, 0) == -1) {
    perror ("streaming.fifo");
    exit (EXIT_FAILURE);
  }

  if (test_start_nbdkit (NBDKIT_PLUGIN ("streaming"),
                         "pipe=streaming.fifo",
                         "size=640k",
                         NULL) == -1)
    exit (EXIT_FAILURE);

  /* Fork to run a second process which reads from streaming.fifo
   * and checks that the content is correct.
   */
  if (pipe (pipefd) == -1) {
    perror ("pipe");
    exit (EXIT_FAILURE);
  }

  md5pid = fork ();
  if (md5pid == -1) {
    perror ("fork");
    exit (EXIT_FAILURE);
  }

  if (md5pid == 0) {
    /* Child: run md5sum on the pipe. */
    char *argv[] = { "md5sum", NULL };

    close (0);
    open ("streaming.fifo", O_RDONLY);
    close (1);
    dup2 (pipefd[1], 1);
    close (pipefd[0]);

    execvp ("md5sum", argv);
    perror ("md5sum");
    _exit (EXIT_FAILURE);
  }

  close (pipefd[1]);

  g = guestfs_create ();
  if (g == NULL) {
    perror ("guestfs_create");
    exit (EXIT_FAILURE);
  }

  r = guestfs_add_drive_opts (g, "",
                              GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw",
                              GUESTFS_ADD_DRIVE_OPTS_PROTOCOL, "nbd",
                              GUESTFS_ADD_DRIVE_OPTS_SERVER, server,
                              -1);
  if (r == -1)
    exit (EXIT_FAILURE);

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

  /* Write linearly to the virtual disk. */
  memset (data, 1, sizeof data);
  for (i = 0; i < 10; ++i) {
    guestfs_pwrite_device (g, "/dev/sda", data, sizeof data,
                           i * sizeof data);
  }

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

  guestfs_close (g);

  /* We have to explicitly kill nbdkit here so that it closes the
   * pipe.
   */
  kill (pid, SIGINT);

  /* Check the hash computed by the child process. */
  if (read (pipefd[0], md5, 32) != 32) {
    perror ("read");
    exit (EXIT_FAILURE);
  }
  md5[32] = '\0';

  if (strcmp (md5, "2c7f81c580f7f9eb52c1bd2f6f493b6f") != 0) {
    fprintf (stderr, "unexpected hash: %s\n", md5);
    exit (EXIT_FAILURE);
  }

  if (waitpid (md5pid, &status, 0) == -1) {
    perror ("waitpid");
    exit (EXIT_FAILURE);
  }
  if (status != 0) {
    fprintf (stderr, "md5sum subprocess failed\n");
    exit (EXIT_FAILURE);
  }

  exit (EXIT_SUCCESS);
}
Ejemplo n.º 10
0
int
main (int argc, char *argv[])
{
  /* Set global program name that is not polluted with libtool artifacts.  */
  set_program_name (argv[0]);

  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEBASEDIR);
  textdomain (PACKAGE);

  enum { HELP_OPTION = CHAR_MAX + 1 };

  static const char *options = "a:c:d:qvVx";
  static const struct option long_options[] = {
    { "add", 1, 0, 'a' },
    { "filesystem", 1, 0, 0 },
    { "format", 2, 0, 0 },
    { "help", 0, 0, HELP_OPTION },
    { "lvm", 2, 0, 0 },
    { "partition", 2, 0, 0 },
    { "verbose", 0, 0, 'v' },
    { "version", 0, 0, 'V' },
    { "wipe", 0, 0, 0 },
    { 0, 0, 0, 0 }
  };
  struct drv *drvs = NULL;
  struct drv *drv;
  const char *format = NULL;
  int c;
  int option_index;
  int retry, retries;

  g = guestfs_create ();
  if (g == NULL) {
    fprintf (stderr, _("guestfs_create: failed to create handle\n"));
    exit (EXIT_FAILURE);
  }

  argv[0] = bad_cast (program_name);

  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, "format")) {
        if (!optarg || STREQ (optarg, ""))
          format = NULL;
        else
          format = optarg;
      } else if (STREQ (long_options[option_index].name, "filesystem")) {
        if (STREQ (optarg, "none"))
          filesystem = NULL;
        else if (optarg[0] == '-') { /* eg: --filesystem --lvm */
          fprintf (stderr, _("%s: no filesystem was specified\n"),
                   program_name);
          exit (EXIT_FAILURE);
        } else
          filesystem = optarg;
      } else if (STREQ (long_options[option_index].name, "lvm")) {
        if (vg || lv) {
          fprintf (stderr,
                   _("%s: --lvm option cannot be given multiple times\n"),
                   program_name);
          exit (EXIT_FAILURE);
        }
        if (optarg == NULL) {
          vg = strdup ("VG");
          lv = strdup ("LV");
          if (!vg || !lv) { perror ("strdup"); exit (EXIT_FAILURE); }
        }
        else if (STREQ (optarg, "none"))
          vg = lv = NULL;
        else
          parse_vg_lv (optarg);
      } else if (STREQ (long_options[option_index].name, "partition")) {
        if (optarg == NULL)
          partition = "DEFAULT";
        else if (STREQ (optarg, "none"))
          partition = NULL;
        else
          partition = optarg;
      } else if (STREQ (long_options[option_index].name, "wipe")) {
        wipe = 1;
      } else {
        fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
                 program_name, long_options[option_index].name, option_index);
        exit (EXIT_FAILURE);
      }
      break;

    case 'a':
      OPTION_a;
      break;

    case 'v':
      OPTION_v;
      break;

    case 'V':
      OPTION_V;
      break;

    case 'x':
      OPTION_x;
      break;

    case HELP_OPTION:
      usage (EXIT_SUCCESS);

    default:
      usage (EXIT_FAILURE);
    }
  }

  /* These are really constants, but they have to be variables for the
   * options parsing code.  Assert here that they have known-good
   * values.
   */
  assert (read_only == 0);
  assert (inspector == 0);
  assert (live == 0);

  /* Must be no extra arguments on the command line. */
  if (optind != argc)
    usage (EXIT_FAILURE);

  /* The user didn't specify any drives to format. */
  if (drvs == NULL)
    usage (EXIT_FAILURE);

  /* Because the libguestfs kernel can get stuck rereading the
   * partition table after things have been erased, we sometimes need
   * to completely restart the guest.  Hence this complex retry logic.
   */
  for (retries = 0; retries <= 1; ++retries) {
    /* Add domains/drives from the command line (for a single guest). */
    add_drives (drvs, 'a');

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

    /* Test if the wipefs API is available. */
    have_wipefs = feature_available (g, "wipefs");

    /* Perform the format. */
    retry = do_format ();
    if (!retry)
      break;

    if (retries == 0) {
      /* We're going to silently retry, after reopening the connection. */
      guestfs_h *g2;

      g2 = guestfs_create ();
      guestfs_set_verbose (g2, guestfs_get_verbose (g));
      guestfs_set_trace (g2, guestfs_get_trace (g));

      if (guestfs_shutdown (g) == -1)
        exit (EXIT_FAILURE);
      guestfs_close (g);
      g = g2;
    }
    else {
      /* Failed. */
      fprintf (stderr,
               _("%s: failed to rescan the disks after two attempts.  This\n"
                 "may mean there is some sort of partition table or disk\n"
                 "data which we are unable to remove.  If you think this\n"
                 "is a bug, please file a bug at http://libguestfs.org/\n"),
               program_name);
      exit (EXIT_FAILURE);
    }
  }

  /* Free up data structures. */
  free_drives (drvs);

  if (guestfs_shutdown (g) == -1)
    exit (EXIT_FAILURE);
  guestfs_close (g);

  exit (EXIT_SUCCESS);
}
Ejemplo n.º 11
0
int
run_reopen (const char *cmd, size_t argc, char *argv[])
{
  guestfs_h *g2;
  int r;
  const char *p;
  guestfs_error_handler_cb cb;
  void *cb_data;

  if (argc > 0) {
    fprintf (stderr, _("'reopen' command takes no parameters\n"));
    return -1;
  }

  if (guestfs_shutdown (g) == -1)
    return -1;

  /* Open the new handle first, so we can copy the settings from the
   * old one to the new one, and also so if it fails we still have an
   * open handle.
   */
  g2 = guestfs_create ();
  if (g2 == NULL) {
    fprintf (stderr, _("reopen: guestfs_create: failed to create handle\n"));
    return -1;
  }

  /* Now copy some of the settings from the old handle.  The settings
   * we copy are those which are set by guestfish itself.
   */
  cb = guestfs_get_error_handler (g, &cb_data);
  guestfs_set_error_handler (g2, cb, cb_data);

  r = guestfs_get_verbose (g);
  if (r >= 0)
    guestfs_set_verbose (g2, r);

  r = guestfs_get_trace (g);
  if (r >= 0)
    guestfs_set_trace (g2, r);

  r = guestfs_get_autosync (g);
  if (r >= 0)
    guestfs_set_autosync (g2, r);

  p = guestfs_get_path (g);
  if (p)
    guestfs_set_path (g2, p);

  r = guestfs_get_pgroup (g);
  if (r >= 0)
    guestfs_set_pgroup (g2, r);

  if (progress_bars)
    guestfs_set_event_callback (g2, progress_callback,
                                GUESTFS_EVENT_PROGRESS, 0, NULL);

  /* Close the original handle. */
  guestfs_close (g);
  g = g2;

  /* We don't bother copying event handlers over to the new handle,
   * but we have to reset the list because they were registered
   * against the old handle.
   */
  free_event_handlers ();
  init_event_handlers ();

  return 0;
}
Ejemplo n.º 12
0
int
main (int argc, char *argv[])
{
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEBASEDIR);
  textdomain (PACKAGE);

  /* We use random(3) below. */
  srandom (time (NULL));

  enum { HELP_OPTION = CHAR_MAX + 1 };

  static const char *options = "a:b:c:d:e:m:vVx";
  static const struct option long_options[] = {
    { "add", 1, 0, 'a' },
    { "backup", 1, 0, 'b' },
    { "connect", 1, 0, 'c' },
    { "domain", 1, 0, 'd' },
    { "echo-keys", 0, 0, 0 },
    { "edit", 1, 0, 'e' },
    { "expr", 1, 0, 'e' },
    { "format", 2, 0, 0 },
    { "help", 0, 0, HELP_OPTION },
    { "keys-from-stdin", 0, 0, 0 },
    { "long-options", 0, 0, 0 },
    { "mount", 1, 0, 'm' },
    { "short-options", 0, 0, 0 },
    { "verbose", 0, 0, 'v' },
    { "version", 0, 0, 'V' },
    { 0, 0, 0, 0 }
  };
  struct drv *drvs = NULL;
  struct drv *drv;
  struct mp *mps = NULL;
  struct mp *mp;
  char *p;
  const char *format = NULL;
  bool format_consumed = true;
  int c;
  int option_index;

  g = guestfs_create ();
  if (g == NULL) {
    fprintf (stderr, _("guestfs_create: failed to create handle\n"));
    exit (EXIT_FAILURE);
  }

  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, "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, "format")) {
        OPTION_format;
      } else {
        fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
                 guestfs_int_program_name,
                 long_options[option_index].name, option_index);
        exit (EXIT_FAILURE);
      }
      break;

    case 'a':
      OPTION_a;
      break;

    case 'b':
      if (backup_extension) {
        fprintf (stderr, _("%s: -b option given multiple times\n"),
                 guestfs_int_program_name);
        exit (EXIT_FAILURE);
      }
      backup_extension = optarg;
      break;

    case 'c':
      OPTION_c;
      break;

    case 'd':
      OPTION_d;
      break;

    case 'e':
      if (perl_expr) {
        fprintf (stderr, _("%s: -e option given multiple times\n"),
                 guestfs_int_program_name);
        exit (EXIT_FAILURE);
      }
      perl_expr = optarg;
      break;

    case 'm':
      OPTION_m;
      inspector = 0;
      break;

    case 'v':
      OPTION_v;
      break;

    case 'V':
      OPTION_V;
      break;

    case 'x':
      OPTION_x;
      break;

    case HELP_OPTION:
      usage (EXIT_SUCCESS);

    default:
      usage (EXIT_FAILURE);
    }
  }

  /* Old-style syntax?  There were no -a or -d options in the old
   * virt-edit which is how we detect this.
   */
  if (drvs == NULL) {
    /* argc - 1 because last parameter is the single filename. */
    while (optind < argc - 1) {
      if (strchr (argv[optind], '/') ||
          access (argv[optind], F_OK) == 0) { /* simulate -a option */
        drv = calloc (1, sizeof (struct drv));
        if (!drv) {
          perror ("calloc");
          exit (EXIT_FAILURE);
        }
        drv->type = drv_a;
        drv->a.filename = strdup (argv[optind]);
        if (!drv->a.filename) {
          perror ("strdup");
          exit (EXIT_FAILURE);
        }
        drv->next = drvs;
        drvs = drv;
      } else {                  /* simulate -d option */
        drv = calloc (1, sizeof (struct drv));
        if (!drv) {
          perror ("calloc");
          exit (EXIT_FAILURE);
        }
        drv->type = drv_d;
        drv->d.guest = argv[optind];
        drv->next = drvs;
        drvs = drv;
      }

      optind++;
    }
  }

  /* These are really constants, but they have to be variables for the
   * options parsing code.  Assert here that they have known-good
   * values.
   */
  assert (read_only == 0);
  assert (inspector == 1 || mps != NULL);
  assert (live == 0);

  /* User must specify at least one filename on the command line. */
  if (optind >= argc || argc - optind < 1)
    usage (EXIT_FAILURE);

  CHECK_OPTION_format_consumed;

  /* User must have specified some drives. */
  if (drvs == NULL)
    usage (EXIT_FAILURE);

  /* Add drives. */
  add_drives (drvs, 'a');

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

  if (mps != NULL)
    mount_mps (mps);
  else
    inspect_mount ();

  /* Free up data structures, no longer needed after this point. */
  free_drives (drvs);
  free_mps (mps);

  edit_files (argc - optind, &argv[optind]);

  /* Cleanly unmount the disks after editing. */
  if (guestfs_shutdown (g) == -1)
    exit (EXIT_FAILURE);

  guestfs_close (g);

  exit (EXIT_SUCCESS);
}
Ejemplo n.º 13
0
int
main (int argc, char *argv[])
{
    const char *src, *srcdir, *dest, *destdir;
    guestfs_h *destg;
    int fd[2];
    pthread_t srcthread;
    struct threaddata threaddata;
    int err;
    char fdname[128];
    struct timeval start_t, end_t;
    int64_t ms;

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

    src = argv[1];
    srcdir = argv[2];
    dest = argv[3];
    destdir = argv[4];

    /* Instead of downloading to local disk and uploading, we are going
     * to connect the source download and destination upload using a
     * pipe.  Create that pipe.
     */
    if (pipe (fd) == -1) {
        perror ("pipe");
        exit (EXIT_FAILURE);
    }

    /* We don't want the pipe to be passed to subprocesses. */
    if (fcntl (fd[0], F_SETFD, FD_CLOEXEC) == -1 ||
            fcntl (fd[1], F_SETFD, FD_CLOEXEC) == -1) {
        perror ("fcntl");
        exit (EXIT_FAILURE);
    }

    /* The libguestfs API is synchronous, so if we want to use two
     * handles concurrently, then we have to have two threads.  In this
     * case the main thread (this one) is handling the destination
     * domain (uploading), and we create one more thread to handle the
     * source domain (downloading).
     */
    threaddata.src = src;
    threaddata.srcdir = srcdir;
    threaddata.fd = fd[1];
    threaddata.mainthread = pthread_self ();
    err = pthread_create (&srcthread, NULL, start_srcthread, &threaddata);
    if (err != 0) {
        fprintf (stderr, "pthread_create: %s\n", strerror (err));
        exit (EXIT_FAILURE);
    }

    /* Open the destination domain. */
    destg = guestfs_create ();
    if (!destg) {
        perror ("failed to create libguestfs handle");
        pthread_cancel (srcthread);
        exit (EXIT_FAILURE);
    }
    if (open_guest (destg, dest, 0) == -1) {
        pthread_cancel (srcthread);
        exit (EXIT_FAILURE);
    }

    gettimeofday (&start_t, NULL);

    /* Begin the upload. */
    snprintf (fdname, sizeof fdname, "/dev/fd/%d", fd[0]);
    if (guestfs_tar_in (destg, fdname, destdir) == -1) {
        pthread_cancel (srcthread);
        exit (EXIT_FAILURE);
    }

    /* Close our end of the pipe.  The other thread will close the
     * other side of the pipe.
     */
    close (fd[0]);

    /* Wait for the other thread to finish. */
    err = pthread_join (srcthread, NULL);
    if (err != 0) {
        fprintf (stderr, "pthread_join: %s\n", strerror (err));
        exit (EXIT_FAILURE);
    }

    /* Clean up. */
    if (guestfs_shutdown (destg) == -1)
        exit (EXIT_FAILURE);
    guestfs_close (destg);

    gettimeofday (&end_t, NULL);

    /* Print the elapsed time. */
    ms = timeval_diff (&start_t, &end_t);
    printf ("copy finished, elapsed time (excluding launch) was "
            "%" PRIi64 ".%03" PRIi64 " s\n",
            ms / 1000, ms % 1000);

    exit (EXIT_SUCCESS);
}