Esempio n. 1
0
int
main (int argc, char *argv[])
{
  guestfs_h *g;
  int r, default_memsize;

  /* What's the default memsize? */
  g = guestfs_create ();
  if (!g) error (EXIT_FAILURE, errno, "guestfs_create");
  default_memsize = guestfs_get_memsize (g);
  if (default_memsize == -1) exit (EXIT_FAILURE);
  guestfs_close (g);

  /* Check that guestfs_create parses the environment. */
  setenv ("LIBGUESTFS_MEMSIZE", "799", 1);
  g = guestfs_create ();
  if (!g) error (EXIT_FAILURE, errno, "guestfs_create");
  assert (guestfs_get_memsize (g) == 799);
  guestfs_close (g);

  /* Check that guestfs_create_flags with no flags parses the environment. */
  setenv ("LIBGUESTFS_MEMSIZE", "798", 1);
  g = guestfs_create_flags (0);
  if (!g) error (EXIT_FAILURE, errno, "guestfs_create_flags");
  assert (guestfs_get_memsize (g) == 798);
  guestfs_close (g);

  /* Check guestfs_create_flags + explicit guestfs_parse_environment. */
  setenv ("LIBGUESTFS_MEMSIZE", "797", 1);
  g = guestfs_create_flags (GUESTFS_CREATE_NO_ENVIRONMENT);
  assert (guestfs_get_memsize (g) == default_memsize);
  if (!g) error (EXIT_FAILURE, errno, "guestfs_create_flags");
  setenv ("LIBGUESTFS_MEMSIZE", "796", 1);
  r = guestfs_parse_environment (g);
  if (r == -1) exit (EXIT_FAILURE);
  assert (guestfs_get_memsize (g) == 796);
  guestfs_close (g);

  /* Check guestfs_parse_environment_list. */
  setenv ("LIBGUESTFS_MEMSIZE", "795", 1);
  g = guestfs_create_flags (GUESTFS_CREATE_NO_ENVIRONMENT);
  assert (guestfs_get_memsize (g) == default_memsize);
  if (!g) error (EXIT_FAILURE, errno, "guestfs_create_flags");
  setenv ("LIBGUESTFS_MEMSIZE", "794", 1);
  const char *local_environment[] = {
    "LIBGUESTFS_MEMSIZE=793",
    "LIBGUESTFS_MEMSIZE_NOT_REALLY_A_VARIABLE=1",
    "FOO=bar",
    "HOME=/homes",
    "BLAH",
    NULL
  };
  r = guestfs_parse_environment_list (g, (char **) local_environment);
  if (r == -1) exit (EXIT_FAILURE);
  assert (guestfs_get_memsize (g) == 793);
  guestfs_close (g);

  exit (EXIT_SUCCESS);
}
Esempio n. 2
0
int
main (int argc, char *argv[])
{
  guestfs_h *g = guestfs_create ();

  /* Call some non-daemon functions that have a String parameter, but
   * setting that parameter to NULL.  Previously this would cause a
   * segfault inside libguestfs.  After this bug was fixed, this
   * turned into an error message.
   */

  assert (guestfs_add_drive (g, NULL) == -1);
  assert (guestfs_config (g, NULL, NULL) == -1);

  /* This optional argument must not be NULL. */

  assert (guestfs_add_drive_opts (g, "/dev/null",
                                  GUESTFS_ADD_DRIVE_OPTS_FORMAT, NULL,
                                  -1) == -1);

  /* These can be safely set to NULL, should be no error. */

  assert (guestfs_set_path (g, NULL) == 0);
  assert (guestfs_set_append (g, NULL) == 0);
  assert (guestfs_set_qemu (g, NULL) == 0);

  guestfs_close (g);
  exit (EXIT_SUCCESS);
}
Esempio n. 3
0
int
main (int argc, char *argv[])
{
  guestfs_h *g;
  int r;

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

  /* If these fail, the default error handler will print an error
   * message to stderr, so we don't need to print anything.  This code
   * is very pedantic, but after all we are testing the details of the
   * C API.
   */

  if (guestfs_set_verbose (g, 1) == -1)
    exit (EXIT_FAILURE);
  r = guestfs_get_verbose (g);
  if (r == -1)
    exit (EXIT_FAILURE);
  if (!r)
    error (EXIT_FAILURE, 0, "set_verbose not true");
  if (guestfs_set_verbose (g, 0) == -1)
    exit (EXIT_FAILURE);
  r = guestfs_get_verbose (g);
  if (r == -1)
    exit (EXIT_FAILURE);
  if (r)
    error (EXIT_FAILURE, 0, "set_verbose not false");

  guestfs_close (g);

  exit (EXIT_SUCCESS);
}
Esempio n. 4
0
static void
usage (int exitcode)
{
  guestfs_h *g;
  int default_memsize = -1;

  g = guestfs_create ();
  if (g) {
    default_memsize = guestfs_get_memsize (g);
    guestfs_close (g);
  }

  fprintf (stderr,
           "boot-analysis: Trace and analyze the appliance boot process.\n"
           "Usage:\n"
           "  boot-analysis [--options]\n"
           "Options:\n"
           "  --help         Display this usage text and exit.\n"
           "  --append OPTS  Append OPTS to kernel command line.\n"
           "  --colour       Output colours, even if not a terminal.\n"
           "  -m MB\n"
           "  --memsize MB   Set memory size in MB (default: %d).\n"
           "  --smp N        Enable N virtual CPUs (default: 1).\n"
           "  -v|--verbose   Verbose output, useful for debugging.\n",
           default_memsize);
  exit (exitcode);
}
Esempio n. 5
0
static void
test_stringsbuf (void)
{
  guestfs_h *g;
  DECLARE_STRINGSBUF (sb);

  g = guestfs_create ();
  assert (g);

  guestfs_int_add_string (g, &sb, "aaa");
  guestfs_int_add_string (g, &sb, "bbb");
  guestfs_int_add_string (g, &sb, "ccc");
  guestfs_int_add_string (g, &sb, "");
  guestfs_int_end_stringsbuf (g, &sb);

  assert (sb.size == 5 /* 4 strings + terminating NULL */);
  assert (STREQ (sb.argv[0], "aaa"));
  assert (STREQ (sb.argv[1], "bbb"));
  assert (STREQ (sb.argv[2], "ccc"));
  assert (STREQ (sb.argv[3], ""));
  assert (sb.argv[4] == NULL);

  assert (guestfs_int_count_strings (sb.argv) == 4);

  guestfs_int_free_stringsbuf (&sb);
  guestfs_close (g);
}
Esempio n. 6
0
static void
test_match (void)
{
  guestfs_h *g;
  char *ret, *ret2;

  g = guestfs_create ();
  assert (g);

  assert (match (g, "aaaaab", test_match_re));
  assert (! match (g, "aaaaacb", test_match_re));
  assert (! match (g, "", test_match_re));

  ret = match1 (g, "aaab", test_match1_re);
  assert (STREQ (ret, "aaa"));
  free (ret);

  assert (! match1 (g, "aaacb", test_match1_re));
  assert (! match1 (g, "", test_match1_re));

  assert (match2 (g, "aaabc", test_match2_re, &ret, &ret2));
  assert (STREQ (ret, "aaa"));
  assert (STREQ (ret2, "b"));
  free (ret);
  free (ret2);

  guestfs_close (g);
}
Esempio n. 7
0
/* Guestfs.create */
CAMLprim value
ocaml_guestfs_create (void)
{
  CAMLparam0 ();
  CAMLlocal1 (gv);
  guestfs_h *g;
  value *v;

  g = guestfs_create ();
  if (g == NULL)
    caml_failwith ("failed to create guestfs handle");

  guestfs_set_error_handler (g, NULL, NULL);

  gv = Val_guestfs (g);

  /* Store the OCaml handle into the C handle.  This is only so we can
   * map the C handle to the OCaml handle in event_callback_wrapper.
   */
  v = guestfs_safe_malloc (g, sizeof *v);
  *v = gv;
  /* XXX This global root is generational, but we cannot rely on every
   * user having the OCaml 3.11 version which supports this.
   */
  caml_register_global_root (v);
  guestfs_set_private (g, "_ocaml_g", v);

  CAMLreturn (gv);
}
Esempio n. 8
0
/**
 * Test C<guestfs_int_new_command> etc.
 *
 * XXX These tests could be made much more thorough.  So far we simply
 * test that it's not obviously broken.
 */
static void
test_command (void)
{
  guestfs_h *g;
  struct command *cmd;
  int r;

  g = guestfs_create ();
  assert (g);

  /* argv-style */
  cmd = guestfs_int_new_command (g);
  assert (cmd);
  guestfs_int_cmd_add_arg (cmd, "touch");
  guestfs_int_cmd_add_arg (cmd, "test-utils-test-command");
  r = guestfs_int_cmd_run (cmd);
  assert (r == 0);
  guestfs_int_cmd_close (cmd);

  /* system-style */
  cmd = guestfs_int_new_command (g);
  assert (cmd);
  guestfs_int_cmd_add_string_unquoted (cmd, "rm ");
  guestfs_int_cmd_add_string_quoted (cmd, "test-utils-test-command");
  r = guestfs_int_cmd_run (cmd);
  assert (r == 0);
  guestfs_int_cmd_close (cmd);

  guestfs_close (g);
}
Esempio n. 9
0
/* Common function to create the handle and set various defaults. */
static guestfs_h *
create_handle (void)
{
  guestfs_h *g;
  CLEANUP_FREE char *full_append = NULL;

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

  if (memsize != 0)
    if (guestfs_set_memsize (g, memsize) == -1)
      exit (EXIT_FAILURE);

  if (smp >= 2)
    if (guestfs_set_smp (g, smp) == -1)
      exit (EXIT_FAILURE);

  /* This changes some details in appliance/init and enables a
   * detailed trace of calls to initcall functions in the kernel.
   */
  if (asprintf (&full_append,
                "guestfs_boot_analysis=1 "
                "ignore_loglevel initcall_debug "
                "%s",
                append != NULL ? append : "") == -1)
    error (EXIT_FAILURE, errno, "asprintf");

  if (guestfs_set_append (g, full_append) == -1)
    exit (EXIT_FAILURE);

  return g;
}
Esempio n. 10
0
int
main (int argc, char *argv[])
{
  guestfs_h *g;
  const char *key;
  void *data;
  size_t count;

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

  if (guestfs_set_event_callback (g, close_callback, GUESTFS_EVENT_CLOSE,
                                  0, NULL) == -1)
    exit (EXIT_FAILURE);

  guestfs_set_private (g, PREFIX "a", (void *) 1);
  guestfs_set_private (g, PREFIX "b", (void *) 2);
  guestfs_set_private (g, PREFIX "c", (void *) 3);
  guestfs_set_private (g, PREFIX "a", (void *) 4); /* overwrites previous */

  /* Check we can fetch keys. */
  assert (guestfs_get_private (g, PREFIX "a") == (void *) 4);
  assert (guestfs_get_private (g, PREFIX "b") == (void *) 2);
  assert (guestfs_get_private (g, PREFIX "c") == (void *) 3);
  assert (guestfs_get_private (g, PREFIX "d") == NULL);

  /* Check we can count keys by iterating. */
  count = 0;
  data = guestfs_first_private (g, &key);
  while (data != NULL) {
    if (strncmp (key, PREFIX, strlen (PREFIX)) == 0)
      count++;
    data = guestfs_next_private (g, &key);
  }
  assert (count == 3);

  /* Delete some keys. */
  guestfs_set_private (g, PREFIX "a", NULL);
  guestfs_set_private (g, PREFIX "b", NULL);

  /* Count them again. */
  count = 0;
  data = guestfs_first_private (g, &key);
  while (data != NULL) {
    if (strncmp (key, PREFIX, strlen (PREFIX)) == 0)
      count++;
    data = guestfs_next_private (g, &key);
  }
  assert (count == 1);

  /* Closing should implicitly call the close_callback function. */
  guestfs_close (g);

  assert (close_callback_called == 1);

  exit (EXIT_SUCCESS);
}
Esempio n. 11
0
int
main (int argc, char *argv[])
{
    pthread_t thread[NR_THREADS];
    int data[NR_THREADS];
    int i, r, errors;
    guestfs_h *g;
    char *backend;

    /* Test is only meaningful if the backend "direct" is used. */
    g = guestfs_create ();
    if (!g)
        error (EXIT_FAILURE, errno, "guestfs_create");
    backend = guestfs_get_backend (g);
    if (backend == NULL) {
        guestfs_close (g);
        exit (EXIT_FAILURE);
    }
    if (STRNEQ (backend, "direct")) {
        fprintf (stderr, "%s: test skipped because backend isn't 'direct'.\n",
                 guestfs_int_program_name);
        free (backend);
        guestfs_close (g);
        exit (77);
    }
    free (backend);
    guestfs_close (g);

    /* Ensure error messages are not translated. */
    setenv ("LC_ALL", "C", 1);

    pthread_barrier_init (&barrier, NULL, NR_THREADS);

    /* Create the other threads which will set up their own libguestfs
     * handle then wait at a barrier before launching.
     */
    for (i = 0; i < NR_THREADS; ++i) {
        data[i] = i;
        r = pthread_create (&thread[i], NULL, start_thread, &data[i]);
        if (r != 0)
            error (EXIT_FAILURE, r, "pthread_create");
    }

    /* Wait for the threads to exit. */
    errors = 0;

    for (i = 0; i < NR_THREADS; ++i) {
        int *ret;

        r = pthread_join (thread[i], (void **) &ret);
        if (r != 0)
            error (EXIT_FAILURE, r, "pthread_join");
        if (*ret == -1)
            errors++;
    }

    exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
Esempio n. 12
0
int
main (int argc, char *argv[])
{
  guestfs_h *g;
  int r;
  char *data;
  size_t i, size;

  if (test_start_nbdkit ("-n", NBDKIT_PLUGIN ("file"), "file=file-data",
                         NULL) == -1)
    exit (EXIT_FAILURE);

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

  /* Using any exportname causes qemu to use the newstyle protocol. */
  r = guestfs_add_drive_opts (g, "/" /* exportname */,
                              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);

  /* Check the data in the file is \x01-\x08 repeated 512 times. */
  data = guestfs_pread_device (g, "/dev/sda", 8 * 512, 0, &size);
  if (!data)
    exit (EXIT_FAILURE);
  if (size != 8 * 512) {
    fprintf (stderr, "%s FAILED: unexpected size (actual: %zu, expected: 512)\n",
             program_name, size);
    exit (EXIT_FAILURE);
  }

  for (i = 0; i < 512 * 8; i += 8) {
    if (data[i] != 1 || data[i+1] != 2 ||
        data[i+2] != 3 || data[i+3] != 4 ||
        data[i+4] != 5 || data[i+5] != 6 ||
        data[i+6] != 7 || data[i+7] != 8) {
      fprintf (stderr, "%s FAILED: unexpected data returned at offset %zu\n",
               program_name, i);
      exit (EXIT_FAILURE);
    }
  }

  free (data);

  guestfs_close (g);
  exit (EXIT_SUCCESS);
}
Esempio n. 13
0
/* Create the handle, with attached disks. */
static guestfs_h *
create_handle (void)
{
  guestfs_h *g;

  g = guestfs_create ();
  if (g == NULL) {
    printf ("FAIL: guestfs_create\n");
    exit (EXIT_FAILURE);
  }

  if (guestfs_add_drive_scratch (g, 524288000, -1) == -1) {
    printf ("FAIL: guestfs_add_drive_scratch\n");
    exit (EXIT_FAILURE);
  }

  if (guestfs_add_drive_scratch (g, 52428800, -1) == -1) {
    printf ("FAIL: guestfs_add_drive_scratch\n");
    exit (EXIT_FAILURE);
  }

  if (guestfs_add_drive_scratch (g, 10485760, -1) == -1) {
    printf ("FAIL: guestfs_add_drive_scratch\n");
    exit (EXIT_FAILURE);
  }

  if (guestfs_add_drive_ro (g, "../data/test.iso") == -1) {
    printf ("FAIL: guestfs_add_drive_ro ../data/test.iso\n");
    exit (EXIT_FAILURE);
  }

  /* Set a timeout in case qemu hangs during launch (RHBZ#505329). */
  alarm (600);

  if (guestfs_launch (g) == -1) {
    printf ("FAIL: guestfs_launch\n");
    exit (EXIT_FAILURE);
  }

  /* Cancel previous alarm. */
  alarm (0);

  /* Create ext2 filesystem on /dev/sdb1 partition. */
  if (guestfs_part_disk (g, "/dev/sdb", "mbr") == -1) {
    printf ("FAIL: guestfs_part_disk\n");
    exit (EXIT_FAILURE);
  }
  if (guestfs_mkfs (g, "ext2", "/dev/sdb1") == -1) {
    printf ("FAIL: guestfs_mkfs (/dev/sdb1)\n");
    exit (EXIT_FAILURE);
  }

  return g;
}
Esempio n. 14
0
int
main (int argc, char *argv[])
{
  char *str;
  guestfs_h *g;
  char tmp[] = "/tmp/charsetXXXXXX";
  int fd;
  size_t i;
  struct filesystem *fs;

  /* Allow this test to be skipped. */
  str = getenv ("SKIP_TEST_CHARSET_FIDELITY");
  if (str && STREQ (str, "1")) {
    printf ("%s: test skipped because environment variable is set.\n",
            argv[0]);
    exit (77);
  }

  g = guestfs_create ();
  if (g == NULL)
    error (EXIT_FAILURE, 0, "failed to create handle");

  fd = mkstemp (tmp);
  if (fd == -1)
    error (EXIT_FAILURE, errno, "mkstemp");

  if (ftruncate (fd, 1024 * 1024 * 1024) == -1)
    error (EXIT_FAILURE, errno, "ftruncate: %s", tmp);

  if (close (fd) == -1)
    error (EXIT_FAILURE, errno, "close: %s", tmp);

  if (guestfs_add_drive_opts (g, tmp, -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);

  for (i = 0; i < sizeof filesystems / sizeof filesystems[0]; ++i) {
    fs = &filesystems[i];
    test_filesystem (g, fs);
  }

  guestfs_close (g);
  unlink (tmp);

  exit (EXIT_SUCCESS);
}
Esempio n. 15
0
int
main (int argc, char *argv[])
{
  guestfs_h *g;
  size_t i;
  int lengths[] = { 0, 1, 1024,
                    GUESTFS_ERROR_LEN-2, GUESTFS_ERROR_LEN-1,
                    GUESTFS_ERROR_LEN, GUESTFS_ERROR_LEN+1,
                    GUESTFS_ERROR_LEN+2,
                    GUESTFS_ERROR_LEN*2, -1 };
  char len_s[64];
  char *args[2];

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

  if (guestfs_add_drive (g, "/dev/null") == -1)
    exit (EXIT_FAILURE);

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

  guestfs_push_error_handler (g, NULL, NULL);

  for (i = 0; lengths[i] != -1; ++i) {
    snprintf (len_s, sizeof len_s, "%d", lengths[i]);
    args[0] = len_s;
    args[1] = NULL;

    if (guestfs_debug (g, "error", args) != NULL) {
      fprintf (stderr, "%s: unexpected return value from 'debug error'\n",
               argv[0]);
      exit (EXIT_FAILURE);
    }
    /* EROFS is a magic value returned by debug_error in the daemon. */
    if (guestfs_last_errno (g) != EROFS) {
      fprintf (stderr, "%s: unexpected error from 'debug error': %s\n",
               argv[0], guestfs_last_error (g));
      exit (EXIT_FAILURE);
    }
    /* else OK */
  }

  guestfs_pop_error_handler (g);
  guestfs_close (g);
  exit (EXIT_SUCCESS);
}
Esempio n. 16
0
int
main (int argc, char *argv[])
{
    guestfs_h *g;

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

    guestfs_close (g);

    exit (EXIT_SUCCESS);
}
Esempio n. 17
0
static size_t
add_disks_to_handle_reverse (struct disk *disk, size_t *errors_r)
{
  size_t nr_disks_added;

  if (disk == NULL)
    return 0;

  nr_disks_added = add_disks_to_handle_reverse (disk->next, errors_r);

  struct guestfs_add_drive_opts_argv optargs = { .bitmask = 0 };

  optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK;
  optargs.readonly = 1;

  if (disk->format) {
    optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK;
    optargs.format = disk->format;
  }

  if (guestfs_add_drive_opts_argv (g, disk->filename, &optargs) == -1) {
    (*errors_r)++;
    disk->failed = 1;
    return nr_disks_added;
  }

  return nr_disks_added+1;
}

/* Close and reopen the libguestfs handle. */
static void
reset_guestfs_handle (void)
{
  /* Copy the settings from the old handle. */
  int verbose = guestfs_get_verbose (g);
  int trace = guestfs_get_trace (g);

  guestfs_close (g);

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

  guestfs_set_verbose (g, verbose);
  guestfs_set_trace (g, trace);
}
Esempio n. 18
0
PyObject *
py_guestfs_create (PyObject *self, PyObject *args)
{
  guestfs_h *g;

  g = guestfs_create ();
  if (g == NULL) {
    PyErr_SetString (PyExc_RuntimeError,
                     "guestfs.create: failed to allocate handle");
    return NULL;
  }
  guestfs_set_error_handler (g, NULL, NULL);
  /* This can return NULL, but in that case put_handle will have
   * set the Python error string.
   */
  return put_handle (g);
}
Esempio n. 19
0
int
main (int argc, char *argv[])
{
  guestfs_h *g;
  const char *filename = "test.log";
  FILE *debugfp;

  debugfp = fopen (filename, "w");
  if (debugfp == NULL) {
    perror (filename);
    exit (EXIT_FAILURE);
  }

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

  if (guestfs_set_event_callback
      (g, debug_to_file,
       GUESTFS_EVENT_LIBRARY | GUESTFS_EVENT_APPLIANCE |
       GUESTFS_EVENT_WARNING | GUESTFS_EVENT_TRACE,
       0, debugfp) == -1)
    exit (EXIT_FAILURE);

  if (guestfs_set_verbose (g, 1) == -1)
    exit (EXIT_FAILURE);

  if (guestfs_set_trace (g, 1) == -1)
    exit (EXIT_FAILURE);

  if (guestfs_add_drive_opts (g, "/dev/null",
                              GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw",
                              GUESTFS_ADD_DRIVE_OPTS_READONLY, 1,
                              -1) == -1)
    exit (EXIT_FAILURE);

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

  guestfs_close (g);

  exit (EXIT_SUCCESS);
}
Esempio n. 20
0
/**
 * Test C<guestfs_int_qemu_escape_param>
 *
 * XXX I wanted to make this test run qemu, passing some parameters
 * which need to be escaped, but I cannot think of a way to do that
 * without launching a VM.
 */
static void
test_qemu_escape_param (void)
{
  CLEANUP_FREE char *ret1 = NULL, *ret2 = NULL, *ret3 = NULL;
  guestfs_h *g;

  g = guestfs_create ();
  assert (g);

  ret1 = guestfs_int_qemu_escape_param (g, "name,with,commas");
  assert (STREQ (ret1, "name,,with,,commas"));

  ret2 = guestfs_int_qemu_escape_param (g, ",,,,");
  assert (STREQ (ret2, ",,,,,,,,"));

  ret3 = guestfs_int_qemu_escape_param (g, "");
  assert (STREQ (ret3, ""));

  guestfs_close (g);
}
Esempio n. 21
0
int
main (int argc, char *argv[])
{
  void *lib;
  guestfs_create_t guestfs_create;
  guestfs_get_program_t guestfs_get_program;
  guestfs_close_t guestfs_close;
  guestfs_h *g;

  if (access (LIBRARY, X_OK) == -1) {
    fprintf (stderr, "test skipped because %s cannot be accessed: %m\n",
             LIBRARY);
    exit (77);
  }

  lib = dlopen (LIBRARY, RTLD_LAZY);
  if (lib == NULL) {
    fprintf (stderr, "could not open %s: %s\n", LIBRARY, dlerror ());
    exit (EXIT_FAILURE);
  }

  guestfs_create = read_symbol (lib, "guestfs_create");
  guestfs_get_program = read_symbol (lib, "guestfs_get_program");
  guestfs_close = read_symbol (lib, "guestfs_close");

  g = guestfs_create ();
  if (g == NULL) {
    fprintf (stderr, "failed to create handle\n");
    exit (EXIT_FAILURE);
  }
  printf ("program = %s\n", guestfs_get_program (g));

  guestfs_close (g);

  if (dlclose (lib) != 0) {
    fprintf (stderr, "could not close %s: %s\n", LIBRARY, dlerror ());
    exit (EXIT_FAILURE);
  }

  exit (EXIT_SUCCESS);
}
Esempio n. 22
0
/**
 * Test C<guestfs_int_getumask>.
 */
static void
test_getumask (void)
{
  guestfs_h *g;
  int orig_umask = umask (0777);

  g = guestfs_create ();
  assert (g);

  assert (guestfs_int_getumask (g) == 0777);
  umask (0022);
  assert (guestfs_int_getumask (g) == 0022);
  assert (guestfs_int_getumask (g) == 0022);
  umask (0222);
  assert (guestfs_int_getumask (g) == 0222);
  umask (0000);
  assert (guestfs_int_getumask (g) == 0000);

  umask (orig_umask);           /* Restore original umask. */
  guestfs_close (g);
}
Esempio n. 23
0
int
main (int argc, char *argv[])
{
  char *str;
  guestfs_h *g;
  size_t i;
  struct filesystem *fs;

  /* Allow this test to be skipped. */
  str = getenv (ourenvvar);
  if (str && STREQ (str, "1")) {
    printf ("%s: test skipped because environment variable is set.\n",
            program_name);
    exit (77);
  }

  g = guestfs_create ();
  if (g == NULL)
    error (EXIT_FAILURE, 0, "failed to create handle");

  if (guestfs_add_drive_scratch (g, 1024*1024*1024, -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);

  for (i = 0; i < sizeof filesystems / sizeof filesystems[0]; ++i) {
    fs = &filesystems[i];
    test_filesystem (g, fs);
  }

  guestfs_close (g);

  exit (EXIT_SUCCESS);
}
Esempio n. 24
0
static void *
start_srcthread (void *arg)
{
    struct threaddata *threaddata = arg;
    guestfs_h *srcg;
    char fdname[128];

    /* Open the source domain. */
    srcg = guestfs_create ();
    if (!srcg) {
        perror ("failed to create libguestfs handle");
        pthread_cancel (threaddata->mainthread);
        exit (EXIT_FAILURE);
    }
    if (open_guest (srcg, threaddata->src, 1) == -1) {
        pthread_cancel (threaddata->mainthread);
        exit (EXIT_FAILURE);
    }

    /* Begin the download. */
    snprintf (fdname, sizeof fdname, "/dev/fd/%d", threaddata->fd);
    if (guestfs_tar_out (srcg, threaddata->srcdir, fdname) == -1) {
        pthread_cancel (threaddata->mainthread);
        exit (EXIT_FAILURE);
    }

    /* Close the pipe; this will cause the receiver to finish the upload. */
    if (close (threaddata->fd) == -1) {
        pthread_cancel (threaddata->mainthread);
        exit (EXIT_FAILURE);
    }

    /* Clean up. */
    guestfs_close (srcg);

    return NULL;
}
Esempio n. 25
0
int
main (int argc, char *argv[])
{
  /* Current time for --time-days, --time-relative output. */
  time (&now);

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

  enum { HELP_OPTION = CHAR_MAX + 1 };

  static const char *options = "a:c:d:hlRvVx";
  static const struct option long_options[] = {
    { "add", 1, 0, 'a' },
    { "checksum", 2, 0, 0 },
    { "checksums", 2, 0, 0 },
    { "csv", 0, 0, 0 },
    { "connect", 1, 0, 'c' },
    { "domain", 1, 0, 'd' },
    { "echo-keys", 0, 0, 0 },
    { "extra-stat", 0, 0, 0 },
    { "extra-stats", 0, 0, 0 },
    { "format", 2, 0, 0 },
    { "help", 0, 0, HELP_OPTION },
    { "human-readable", 0, 0, 'h' },
    { "keys-from-stdin", 0, 0, 0 },
    { "long", 0, 0, 'l' },
    { "long-options", 0, 0, 0 },
    { "recursive", 0, 0, 'R' },
    { "time", 0, 0, 0 },
    { "times", 0, 0, 0 },
    { "time-days", 0, 0, 0 },
    { "time-relative", 0, 0, 0 },
    { "time-t", 0, 0, 0 },
    { "uid", 0, 0, 0 },
    { "uids", 0, 0, 0 },
    { "verbose", 0, 0, 'v' },
    { "version", 0, 0, 'V' },
    { 0, 0, 0, 0 }
  };
  struct drv *drvs = NULL;
  struct drv *drv;
  const char *format = NULL;
  int c;
  int option_index;
#define MODE_LS_L  1
#define MODE_LS_R  2
#define MODE_LS_LR (MODE_LS_L|MODE_LS_R)
  int mode = 0;

  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, "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")) {
        if (!optarg || STREQ (optarg, ""))
          format = NULL;
        else
          format = optarg;
      } else if (STREQ (long_options[option_index].name, "checksum") ||
                 STREQ (long_options[option_index].name, "checksums")) {
        if (!optarg || STREQ (optarg, ""))
          checksum = "md5";
        else
          checksum = optarg;
      } else if (STREQ (long_options[option_index].name, "csv")) {
        csv = 1;
      } else if (STREQ (long_options[option_index].name, "extra-stat") ||
                 STREQ (long_options[option_index].name, "extra-stats")) {
        enable_extra_stats = 1;
      } else if (STREQ (long_options[option_index].name, "time") ||
                 STREQ (long_options[option_index].name, "times")) {
        enable_times = 1;
      } else if (STREQ (long_options[option_index].name, "time-t")) {
        enable_times = 1;
        time_t_output = 1;
      } else if (STREQ (long_options[option_index].name, "time-relative")) {
        enable_times = 1;
        time_t_output = 1;
        time_relative = 1;
      } else if (STREQ (long_options[option_index].name, "time-days")) {
        enable_times = 1;
        time_t_output = 1;
        time_relative = 2;
      } else if (STREQ (long_options[option_index].name, "uid") ||
                 STREQ (long_options[option_index].name, "uids")) {
        enable_uids = 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 'c':
      OPTION_c;
      break;

    case 'd':
      OPTION_d;
      break;

    case 'h':
      human = 1;
      break;

    case 'l':
      mode |= MODE_LS_L;
      break;

    case 'R':
      mode |= MODE_LS_R;
      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-ls which is how we detect this.
   */
  if (drvs == NULL) {
    /* argc - 1 because last parameter is the single directory name. */
    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 ("malloc");
          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 ("malloc");
          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 == 1);
  assert (inspector == 1);
  assert (live == 0);

  /* Many flags only apply to -lR mode. */
  if (mode != MODE_LS_LR &&
      (csv || human || enable_uids || enable_times || enable_extra_stats ||
       checksum)) {
    fprintf (stderr, _("%s: used a flag which can only be combined with -lR mode\nFor more information, read the virt-ls(1) man page.\n"),
             program_name);
    exit (EXIT_FAILURE);
  }

  /* CSV && human is unsafe because spreadsheets fail to parse these
   * fields correctly.  (RHBZ#600977).
   */
  if (human && csv) {
    fprintf (stderr, _("%s: you cannot use -h and --csv options together.\n"),
             program_name);
    exit (EXIT_FAILURE);
  }

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

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

  /* Add drives, inspect and mount.  Note that inspector is always true,
   * and there is no -m option.
   */
  add_drives (drvs, 'a');

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

  inspect_mount ();

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

  unsigned errors = 0;

  while (optind < argc) {
    const char *dir = argv[optind];

    switch (mode) {
    case 0:                     /* no -l or -R option */
      if (do_ls (dir) == -1)
        errors++;
      break;

    case MODE_LS_L:             /* virt-ls -l */
      if (do_ls_l (dir) == -1)
        errors++;
      break;

    case MODE_LS_R:             /* virt-ls -R */
      if (do_ls_R (dir) == -1)
        errors++;
      break;

    case MODE_LS_LR:            /* virt-ls -lR */
      if (do_ls_lR (dir) == -1)
        errors++;
      break;

    default:
      abort ();                 /* can't happen */
    }

    optind++;
  }

  guestfs_close (g);

  exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
Esempio n. 26
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;
}
Esempio n. 27
0
int
main (int argc, char *argv[])
{
  pthread_t thread[NR_THREADS];
  int data[NR_THREADS];
  int i, r, errors;
  guestfs_h *g;
  char *attach_method;

  /* Test is only meaningful if the attach-method "appliance" is used. */
  g = guestfs_create ();
  if (!g) {
    perror ("guestfs_create");
    exit (EXIT_FAILURE);
  }
  attach_method = guestfs_get_attach_method (g);
  if (attach_method == NULL) {
    guestfs_close (g);
    exit (EXIT_FAILURE);
  }
  if (STRNEQ (attach_method, "appliance")) {
    fprintf (stderr, "%s: test skipped because attach method isn't 'appliance'.\n",
             argv[0]);
    free (attach_method);
    guestfs_close (g);
    exit (77);
  }
  free (attach_method);
  guestfs_close (g);

  /* Ensure error messages are not translated. */
  setenv ("LC_ALL", "C", 1);

  pthread_barrier_init (&barrier, NULL, NR_THREADS);

  /* Create the other threads which will set up their own libguestfs
   * handle then wait at a barrier before launching.
   */
  for (i = 0; i < NR_THREADS; ++i) {
    data[i] = i;
    r = pthread_create (&thread[i], NULL, start_thread, &data[i]);
    if (r != 0) {
      fprintf (stderr, "pthread_create: %s\n", strerror (r));
      exit (EXIT_FAILURE);
    }
  }

  /* Wait for the threads to exit. */
  errors = 0;

  for (i = 0; i < NR_THREADS; ++i) {
    int *ret;

    r = pthread_join (thread[i], (void **) &ret);
    if (r != 0) {
      fprintf (stderr, "pthread_join: %s\n", strerror (r));
      exit (EXIT_FAILURE);
    }
    if (*ret == -1)
      errors++;
  }

  exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
Esempio n. 28
0
static void *
start_thread (void *vi)
{
  guestfs_h *g;
  int r, thread_id = *(int *)vi;
  const char *error;

  g = guestfs_create ();
  if (g == NULL) {
    perror ("guestfs_create");
    *(int *)vi = -1;
    pthread_exit (vi);
  }

  if (guestfs_add_drive_opts (g, "/dev/null",
                              GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw",
                              GUESTFS_ADD_DRIVE_OPTS_READONLY, 1,
                              -1) == -1) {
    *(int *)vi = -1;
    pthread_exit (vi);
  }

  /* Fake out qemu. */
  if (guestfs_set_qemu (g, "/bin/true") == -1) {
    *(int *)vi = -1;
    pthread_exit (vi);
  }

  /* Wait for the other threads to finish starting up. */
  r = pthread_barrier_wait (&barrier);
  if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) {
    fprintf (stderr, "pthread_barrier_wait: [thread %d]: %s\n",
             thread_id, strerror (r));
    *(int *)vi = -1;
    pthread_exit (vi);
  }

  /* Launch the handle.  Because of the faked out qemu, we expect this
   * will fail with "child process died unexpectedly".  We are
   * interested in other failures.
   */
  guestfs_push_error_handler (g, NULL, NULL);
  r = guestfs_launch (g);
  error = guestfs_last_error (g);

  if (r == 0) { /* This should NOT happen. */
    fprintf (stderr, "rhbz790721: [thread %d]: "
             "strangeness in test: expected launch to fail, but it didn't!\n",
             thread_id);
    *(int *)vi = -1;
    pthread_exit (vi);
  }

  if (error == NULL) { /* This also should NOT happen. */
    fprintf (stderr, "rhbz790721: [thread %d]: "
             "strangeness in test: no error message!\n",
             thread_id);
    *(int *)vi = -1;
    pthread_exit (vi);
  }

  /* If this happens, it indicates a bug/race in the appliance
   * building code which is what this regression test is designed to
   * spot.
   */
  if (STRNEQ (error, "child process died unexpectedly")) {
    fprintf (stderr, "rhbz790721: [thread %d]: error: %s\n", thread_id, error);
    *(int *)vi = -1;
    pthread_exit (vi);
  }

  guestfs_pop_error_handler (g);

  /* Close the handle. */
  guestfs_close (g);

  *(int *)vi = 0;
  pthread_exit (vi);
}
int
main (int argc, char *argv[])
{
    guestfs_h *g;
    struct guestfs_internal_mountable *mountable;
    const char *devices[] = { "/dev/VG/LV", NULL };
    const char *feature[] = { "btrfs", NULL };

    g = guestfs_create ();
    if (g == NULL) {
        perror ("could not create handle");
        exit (EXIT_FAILURE);
    }

    if (guestfs_add_drive_scratch (g, 1024*1024*1024, -1) == -1) {
error:
        guestfs_close (g);
        exit (EXIT_FAILURE);
    }

    if (guestfs_launch (g) == -1) goto error;

    if (!guestfs_feature_available (g, (char **) feature)) {
        printf ("skipping test because btrfs is not available\n");
        guestfs_close (g);
        exit (77);
    }

    if (!guestfs_filesystem_available (g, "btrfs")) {
        printf ("skipping test because btrfs filesystem is not available\n");
        guestfs_close (g);
        exit (77);
    }

    if (guestfs_part_disk (g, "/dev/sda", "mbr") == -1) goto error;

    if (guestfs_pvcreate (g, "/dev/sda1") == -1) goto error;

    const char *pvs[] = { "/dev/sda1", NULL };
    if (guestfs_vgcreate (g, "VG", (char **) pvs) == -1) goto error;

    if (guestfs_lvcreate (g, "LV", "VG", 900) == -1) goto error;

    if (guestfs_mkfs_btrfs (g, (char * const *)devices, -1) == -1) goto error;

    if (guestfs_mount (g, "/dev/VG/LV", "/") == -1) goto error;

    if (guestfs_btrfs_subvolume_create (g, "/sv") == -1) goto error;

    mountable = guestfs_internal_parse_mountable (g, "/dev/VG/LV");
    if (mountable == NULL) goto error;

    if (mountable->im_type != MOUNTABLE_DEVICE ||
            STRNEQ ("/dev/VG/LV", mountable->im_device)) {
        fprintf (stderr, "incorrectly parsed /dev/VG/LV: im_device=%s\n",
                 mountable->im_device);
        goto error;
    }

    guestfs_free_internal_mountable (mountable);

    mountable = guestfs_internal_parse_mountable (g, "btrfsvol:/dev/VG/LV/sv");
    if (mountable == NULL) goto error;

    if (mountable->im_type != MOUNTABLE_BTRFSVOL ||
            STRNEQ ("/dev/VG/LV", mountable->im_device) ||
            STRNEQ ("sv", mountable->im_volume)) {
        fprintf (stderr, "incorrectly parsed /dev/VG/LV/sv: im_device=%s, im_volume=%s\n",
                 mountable->im_device, mountable->im_volume);
        goto error;
    }
    guestfs_free_internal_mountable (mountable);

    guestfs_close (g);

    exit (EXIT_SUCCESS);
}
Esempio n. 30
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);
}