/** * 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); }
/** * Test if the qemu-img info command supports the C<-U> option to * disable locking. The result is memoized in the handle. * * Note this option was added in qemu 2.11. We can remove this test * when we can assume everyone is using qemu >= 2.11. */ static int qemu_img_supports_U_option (guestfs_h *g) { if (g->qemu_img_supports_U_option >= 0) return g->qemu_img_supports_U_option; CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g); int r; guestfs_int_cmd_add_string_unquoted (cmd, "qemu-img --help | " "grep -sqE -- '\\binfo\\b.*-U\\b'"); r = guestfs_int_cmd_run (cmd); if (r == -1) return -1; if (!WIFEXITED (r)) { guestfs_int_external_command_failed (g, r, "qemu-img info -U option test", NULL); return -1; } g->qemu_img_supports_U_option = WEXITSTATUS (r) == 0; return g->qemu_img_supports_U_option; }
/* Run uml_mkcow to create a COW overlay. */ static char * make_cow_overlay (guestfs_h *g, const char *original) { CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g); char *overlay; int r; if (guestfs_int_lazy_make_tmpdir (g) == -1) return NULL; overlay = safe_asprintf (g, "%s/overlay%d", g->tmpdir, g->unique++); guestfs_int_cmd_add_arg (cmd, "uml_mkcow"); guestfs_int_cmd_add_arg (cmd, overlay); guestfs_int_cmd_add_arg (cmd, original); r = guestfs_int_cmd_run (cmd); if (r == -1) { free (overlay); return NULL; } if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) { guestfs_int_external_command_failed (g, r, "uml_mkcow", original); free (overlay); return NULL; } return overlay; }
static int test_qemu (guestfs_h *g, struct qemu_data *data, struct version *qemu_version) { CLEANUP_CMD_CLOSE struct command *cmd1 = guestfs_int_new_command (g); CLEANUP_CMD_CLOSE struct command *cmd2 = guestfs_int_new_command (g); int r; guestfs_int_cmd_add_arg (cmd1, g->hv); guestfs_int_cmd_add_arg (cmd1, "-display"); guestfs_int_cmd_add_arg (cmd1, "none"); guestfs_int_cmd_add_arg (cmd1, "-help"); guestfs_int_cmd_set_stdout_callback (cmd1, read_all, &data->qemu_help, CMD_STDOUT_FLAG_WHOLE_BUFFER); r = guestfs_int_cmd_run (cmd1); if (r == -1 || !WIFEXITED (r) || WEXITSTATUS (r) != 0) goto error; parse_qemu_version (g, data->qemu_help, qemu_version); guestfs_int_cmd_add_arg (cmd2, g->hv); guestfs_int_cmd_add_arg (cmd2, "-display"); guestfs_int_cmd_add_arg (cmd2, "none"); guestfs_int_cmd_add_arg (cmd2, "-machine"); guestfs_int_cmd_add_arg (cmd2, #ifdef MACHINE_TYPE MACHINE_TYPE "," #endif "accel=kvm:tcg"); guestfs_int_cmd_add_arg (cmd2, "-device"); guestfs_int_cmd_add_arg (cmd2, "?"); guestfs_int_cmd_clear_capture_errors (cmd2); guestfs_int_cmd_set_stderr_to_stdout (cmd2); guestfs_int_cmd_set_stdout_callback (cmd2, read_all, &data->qemu_devices, CMD_STDOUT_FLAG_WHOLE_BUFFER); r = guestfs_int_cmd_run (cmd2); if (r == -1 || !WIFEXITED (r) || WEXITSTATUS (r) != 0) goto error; return 0; error: if (r == -1) return -1; guestfs_int_external_command_failed (g, r, g->hv, NULL); return -1; }
/* Recursively remove a temporary directory. If removal fails, just * return (it's a temporary directory so it'll eventually be cleaned * up by a temp cleaner). This is done using "rm -rf" because that's * simpler and safer. */ void guestfs_int_recursive_remove_dir (guestfs_h *g, const char *dir) { CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g); guestfs_int_cmd_add_arg (cmd, "rm"); guestfs_int_cmd_add_arg (cmd, "-rf"); guestfs_int_cmd_add_arg (cmd, dir); ignore_value (guestfs_int_cmd_run (cmd)); }
/* Run supermin --build and tell it to generate the * appliance. */ static int run_supermin_build (guestfs_h *g, const char *lockfile, const char *appliancedir, const char *supermin_path) { CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g); int r; #if 0 /* not supported in supermin 5 yet XXX */ uid_t uid = getuid (); uid_t euid = geteuid (); gid_t gid = getgid (); gid_t egid = getegid (); int pass_u_g_args = uid != euid || gid != egid; #endif guestfs_int_cmd_add_arg (cmd, SUPERMIN); guestfs_int_cmd_add_arg (cmd, "--build"); if (g->verbose) guestfs_int_cmd_add_arg (cmd, "--verbose"); guestfs_int_cmd_add_arg (cmd, "--if-newer"); guestfs_int_cmd_add_arg (cmd, "--lock"); guestfs_int_cmd_add_arg (cmd, lockfile); #if 0 if (pass_u_g_args) { guestfs_int_cmd_add_arg (cmd, "-u"); guestfs_int_cmd_add_arg_format (cmd, "%d", euid); guestfs_int_cmd_add_arg (cmd, "-g"); guestfs_int_cmd_add_arg_format (cmd, "%d", egid); } #endif guestfs_int_cmd_add_arg (cmd, "--copy-kernel"); guestfs_int_cmd_add_arg (cmd, "-f"); guestfs_int_cmd_add_arg (cmd, "ext2"); guestfs_int_cmd_add_arg (cmd, "--host-cpu"); guestfs_int_cmd_add_arg (cmd, host_cpu); #ifdef DTB_WILDCARD guestfs_int_cmd_add_arg (cmd, "--dtb"); guestfs_int_cmd_add_arg (cmd, DTB_WILDCARD); #endif guestfs_int_cmd_add_arg_format (cmd, "%s/supermin.d", supermin_path); guestfs_int_cmd_add_arg (cmd, "-o"); guestfs_int_cmd_add_arg (cmd, appliancedir); r = guestfs_int_cmd_run (cmd); if (r == -1) return -1; if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) { guestfs_int_external_command_failed (g, r, SUPERMIN, NULL); return -1; } return 0; }
/** * Return the location of firmware needed to boot the appliance. This * is aarch64 only currently, since that's the only architecture where * UEFI is mandatory (and that only for RHEL). * * C<*code> is initialized with the path to the read-only UEFI code * file. C<*vars> is initialized with the path to a copy of the UEFI * vars file (which is cleaned up automatically on exit). * * If C<*code> == C<*vars> == C<NULL> then no UEFI firmware is * available. * * C<*code> and C<*vars> should be freed by the caller. * * If the function returns C<-1> then there was a real error which * should cause appliance building to fail (no UEFI firmware is not an * error). * * See also F<v2v/utils.ml>:find_uefi_firmware */ int guestfs_int_get_uefi (guestfs_h *g, char **code, char **vars, int *flags) { #ifdef __aarch64__ size_t i; for (i = 0; guestfs_int_uefi_aarch64_firmware[i].code != NULL; ++i) { const char *codefile = guestfs_int_uefi_aarch64_firmware[i].code; const char *code_debug_file = guestfs_int_uefi_aarch64_firmware[i].code_debug; const char *varsfile = guestfs_int_uefi_aarch64_firmware[i].vars; if (access (codefile, R_OK) == 0 && access (varsfile, R_OK) == 0) { CLEANUP_CMD_CLOSE struct command *copycmd = guestfs_int_new_command (g); char *varst; int r; /* Make a copy of NVRAM variables file. You can't just map it * into the address space read-only as that triggers a different * path inside UEFI. */ varst = safe_asprintf (g, "%s/vars.fd.%d", g->tmpdir, ++g->unique); guestfs_int_cmd_add_arg (copycmd, "cp"); guestfs_int_cmd_add_arg (copycmd, varsfile); guestfs_int_cmd_add_arg (copycmd, varst); r = guestfs_int_cmd_run (copycmd); if (r == -1 || !WIFEXITED (r) || WEXITSTATUS (r) != 0) { free (varst); return -1; } /* If debugging is enabled and we can find the code file with * debugging enabled, use that instead. */ if (g->verbose && access (code_debug_file, R_OK) == 0) codefile = code_debug_file; /* Caller frees. */ *code = safe_strdup (g, codefile); *vars = varst; *flags = guestfs_int_uefi_aarch64_firmware[i].flags; return 0; } } #endif /* Not found. */ *code = *vars = NULL; *flags = 0; return 0; }
int guestfs_impl_umount_local (guestfs_h *g, const struct guestfs_umount_local_argv *optargs) { const char *retry; int r; CLEANUP_FREE char *localmountpoint = NULL; CLEANUP_CMD_CLOSE struct command *cmd = NULL; /* How many times should we try the fusermount command? */ if (optargs->bitmask & GUESTFS_UMOUNT_LOCAL_RETRY_BITMASK) retry = optargs->retry ? "--retry=5" : "--no-retry"; else retry = "--no-retry"; /* Make a local copy of g->localmountpoint. It could be freed from * under us by another thread, except when we are holding the lock. */ gl_lock_lock (mount_local_lock); if (g->localmountpoint) localmountpoint = safe_strdup (g, g->localmountpoint); else localmountpoint = NULL; gl_lock_unlock (mount_local_lock); if (!localmountpoint) { error (g, _("no filesystem is mounted")); return -1; } /* Run guestunmount --retry=... localmountpoint. */ cmd = guestfs_int_new_command (g); guestfs_int_cmd_add_arg (cmd, "guestunmount"); guestfs_int_cmd_add_arg (cmd, retry); guestfs_int_cmd_add_arg (cmd, localmountpoint); r = guestfs_int_cmd_run (cmd); if (r == -1) return -1; if (WIFEXITED (r) && WEXITSTATUS (r) == EXIT_SUCCESS) /* External fusermount succeeded. Note that the original thread * is responsible for setting g->localmountpoint to NULL. */ return 0; return -1; }
static json_t * get_json_output (guestfs_h *g, const char *filename) { CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g); int r; json_t *tree = NULL; guestfs_int_cmd_add_arg (cmd, "qemu-img"); guestfs_int_cmd_add_arg (cmd, "info"); switch (qemu_img_supports_U_option (g)) { case -1: return NULL; case 0: break; default: guestfs_int_cmd_add_arg (cmd, "-U"); } guestfs_int_cmd_add_arg (cmd, "--output"); guestfs_int_cmd_add_arg (cmd, "json"); if (filename[0] == '/') guestfs_int_cmd_add_arg (cmd, filename); else guestfs_int_cmd_add_arg_format (cmd, "./%s", filename); guestfs_int_cmd_set_stdout_callback (cmd, parse_json, &tree, CMD_STDOUT_FLAG_WHOLE_BUFFER); set_child_rlimits (cmd); r = guestfs_int_cmd_run (cmd); if (r == -1) return NULL; if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) { guestfs_int_external_command_failed (g, r, "qemu-img info", filename); return NULL; } if (tree == NULL) return NULL; /* parse_json callback already set an error */ if (tree == PARSE_JSON_NO_OUTPUT) { /* If this ever happened, it would indicate a bug in 'qemu-img info'. */ error (g, _("qemu-img info command produced no output, but didn't return an error status code")); return NULL; } return tree; /* caller must call json_decref (tree) */ }
int guestfs_int_get_uefi (guestfs_h *g, char **code, char **vars) { size_t i; for (i = 0; uefi_firmware[i] != NULL; i += 2) { const char *codefile = uefi_firmware[i]; const char *varsfile = uefi_firmware[i+1]; if (access (codefile, R_OK) == 0 && access (varsfile, R_OK) == 0) { CLEANUP_CMD_CLOSE struct command *copycmd = guestfs_int_new_command (g); char *varst; int r; /* Make a copy of NVRAM variables file. You can't just map it * into the address space read-only as that triggers a different * path inside UEFI. */ varst = safe_asprintf (g, "%s/vars.fd.%d", g->tmpdir, ++g->unique); guestfs_int_cmd_add_arg (copycmd, "cp"); guestfs_int_cmd_add_arg (copycmd, varsfile); guestfs_int_cmd_add_arg (copycmd, varst); r = guestfs_int_cmd_run (copycmd); if (r == -1 || !WIFEXITED (r) || WEXITSTATUS (r) != 0) { free (varst); return -1; } /* Caller frees. */ *code = safe_strdup (g, codefile); *vars = varst; return 0; } } /* Not found. */ *code = *vars = NULL; return 0; }
static int disk_create_qcow2 (guestfs_h *g, const char *orig_filename, int64_t size, const char *backingfile, const struct guestfs_disk_create_argv *optargs) { CLEANUP_FREE char *filename = NULL; const char *backingformat = NULL; const char *preallocation = NULL; const char *compat = NULL; int clustersize = -1; CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (optionsv); CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g); int r; /* If the filename is something like "file:foo" then qemu-img will * try to interpret that as "foo" in the file:/// protocol. To * avoid that, if the path is relative prefix it with "./" since * qemu-img won't try to interpret such a path. */ if (orig_filename[0] != '/') filename = safe_asprintf (g, "./%s", orig_filename); else filename = safe_strdup (g, orig_filename); if (optargs->bitmask & GUESTFS_DISK_CREATE_BACKINGFORMAT_BITMASK) { backingformat = optargs->backingformat; /* Conservative whitelist. This can be extended with other * valid formats as required. */ if (STRNEQ (backingformat, "raw") && STRNEQ (backingformat, "qcow2") && STRNEQ (backingformat, "vmdk")) { error (g, _("invalid value for backingformat parameter '%s'"), backingformat); return -1; } } if (optargs->bitmask & GUESTFS_DISK_CREATE_PREALLOCATION_BITMASK) { if (STREQ (optargs->preallocation, "off") || STREQ (optargs->preallocation, "sparse")) preallocation = "off"; else if (STREQ (optargs->preallocation, "metadata")) preallocation = "metadata"; else if (STREQ (optargs->preallocation, "full")) /* Ugh: https://lists.gnu.org/archive/html/qemu-devel/2014-08/msg03863.html */ preallocation = "falloc"; else { error (g, _("invalid value for preallocation parameter '%s'"), preallocation); return -1; } } if (optargs->bitmask & GUESTFS_DISK_CREATE_COMPAT_BITMASK) { compat = optargs->compat; if (STRNEQ (compat, "0.10") && STRNEQ (compat, "1.1")) { error (g, _("invalid value for compat parameter '%s'"), compat); return -1; } } if (optargs->bitmask & GUESTFS_DISK_CREATE_CLUSTERSIZE_BITMASK) { clustersize = optargs->clustersize; if (clustersize < 512 || clustersize > 2097152 || !is_power_of_2 ((unsigned) clustersize)) { error (g, _("invalid value for clustersize parameter '%d'"), clustersize); return -1; } } /* Assemble the qemu-img command line. */ guestfs_int_cmd_add_arg (cmd, "qemu-img"); guestfs_int_cmd_add_arg (cmd, "create"); guestfs_int_cmd_add_arg (cmd, "-f"); guestfs_int_cmd_add_arg (cmd, "qcow2"); /* -o parameter. */ if (backingfile) { CLEANUP_FREE char *p = qemu_escape_param (g, backingfile); guestfs_int_add_sprintf (g, &optionsv, "backing_file=%s", p); } if (backingformat) guestfs_int_add_sprintf (g, &optionsv, "backing_fmt=%s", backingformat); if (preallocation) guestfs_int_add_sprintf (g, &optionsv, "preallocation=%s", preallocation); if (compat) guestfs_int_add_sprintf (g, &optionsv, "compat=%s", compat); if (clustersize >= 0) guestfs_int_add_sprintf (g, &optionsv, "cluster_size=%d", clustersize); guestfs_int_end_stringsbuf (g, &optionsv); if (optionsv.size > 1) { CLEANUP_FREE char *options = guestfs_int_join_strings (",", optionsv.argv); guestfs_int_cmd_add_arg (cmd, "-o"); guestfs_int_cmd_add_arg (cmd, options); } /* Complete the command line. */ guestfs_int_cmd_add_arg (cmd, filename); if (size >= 0) guestfs_int_cmd_add_arg_format (cmd, "%" PRIi64, size); r = guestfs_int_cmd_run (cmd); if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) { guestfs_int_external_command_failed (g, r, "qemu-img", orig_filename); return -1; } return 0; }