int guestfs_impl_set_backend_setting (guestfs_h *g, const char *name, const char *value) { char *new_setting; size_t len; new_setting = safe_asprintf (g, "%s=%s", name, value); if (g->backend_settings == NULL) { g->backend_settings = safe_malloc (g, sizeof (char *)); g->backend_settings[0] = NULL; len = 0; } else { ignore_value (guestfs_clear_backend_setting (g, name)); len = guestfs_int_count_strings (g->backend_settings); } g->backend_settings = safe_realloc (g, g->backend_settings, (len+2) * sizeof (char *)); g->backend_settings[len++] = new_setting; g->backend_settings[len++] = NULL; return 0; }
int guestfs_impl_add_domain (guestfs_h *g, const char *domain_name, const struct guestfs_add_domain_argv *optargs) { virErrorPtr err; virConnectPtr conn = NULL; virDomainPtr dom = NULL; int r = -1; const char *libvirturi; int readonly; int live; int allowuuid; const char *readonlydisk; const char *iface; const char *cachemode; const char *discard; bool copyonread; struct guestfs_add_libvirt_dom_argv optargs2 = { .bitmask = 0 }; libvirturi = optargs->bitmask & GUESTFS_ADD_DOMAIN_LIBVIRTURI_BITMASK ? optargs->libvirturi : NULL; readonly = optargs->bitmask & GUESTFS_ADD_DOMAIN_READONLY_BITMASK ? optargs->readonly : 0; iface = optargs->bitmask & GUESTFS_ADD_DOMAIN_IFACE_BITMASK ? optargs->iface : NULL; live = optargs->bitmask & GUESTFS_ADD_DOMAIN_LIVE_BITMASK ? optargs->live : 0; allowuuid = optargs->bitmask & GUESTFS_ADD_DOMAIN_ALLOWUUID_BITMASK ? optargs->allowuuid : 0; readonlydisk = optargs->bitmask & GUESTFS_ADD_DOMAIN_READONLYDISK_BITMASK ? optargs->readonlydisk : NULL; cachemode = optargs->bitmask & GUESTFS_ADD_DOMAIN_CACHEMODE_BITMASK ? optargs->cachemode : NULL; discard = optargs->bitmask & GUESTFS_ADD_DOMAIN_DISCARD_BITMASK ? optargs->discard : NULL; copyonread = optargs->bitmask & GUESTFS_ADD_DOMAIN_COPYONREAD_BITMASK ? optargs->copyonread : false; if (live && readonly) { error (g, _("you cannot set both live and readonly flags")); return -1; } /* Connect to libvirt, find the domain. */ conn = guestfs_int_open_libvirt_connection (g, libvirturi, VIR_CONNECT_RO); if (!conn) { err = virGetLastError (); error (g, _("could not connect to libvirt (code %d, domain %d): %s"), err->code, err->domain, err->message); goto cleanup; } /* Suppress default behaviour of printing errors to stderr. Note * you can't set this to NULL to ignore errors; setting it to NULL * restores the default error handler ... */ virConnSetErrorFunc (conn, NULL, ignore_errors); /* Try UUID first. */ if (allowuuid) dom = virDomainLookupByUUIDString (conn, domain_name); /* Try ordinary domain name. */ if (!dom) dom = virDomainLookupByName (conn, domain_name); if (!dom) { err = virGetLastError (); error (g, _("no libvirt domain called '%s': %s"), domain_name, err->message); goto cleanup; } if (readonly) { optargs2.bitmask |= GUESTFS_ADD_LIBVIRT_DOM_READONLY_BITMASK; optargs2.readonly = readonly; } if (iface) { optargs2.bitmask |= GUESTFS_ADD_LIBVIRT_DOM_IFACE_BITMASK; optargs2.iface = iface; } if (live) { optargs2.bitmask |= GUESTFS_ADD_LIBVIRT_DOM_LIVE_BITMASK; optargs2.live = live; } if (readonlydisk) { optargs2.bitmask |= GUESTFS_ADD_LIBVIRT_DOM_READONLYDISK_BITMASK; optargs2.readonlydisk = readonlydisk; } if (cachemode) { optargs2.bitmask |= GUESTFS_ADD_LIBVIRT_DOM_CACHEMODE_BITMASK; optargs2.cachemode = cachemode; } if (discard) { optargs2.bitmask |= GUESTFS_ADD_LIBVIRT_DOM_DISCARD_BITMASK; optargs2.discard = discard; } if (copyonread) { optargs2.bitmask |= GUESTFS_ADD_LIBVIRT_DOM_COPYONREAD_BITMASK; optargs2.copyonread = copyonread; } r = guestfs_add_libvirt_dom_argv (g, dom, &optargs2); cleanup: if (dom) virDomainFree (dom); if (conn) virConnectClose (conn); return r; } static int add_disk (guestfs_h *g, const char *filename, const char *format, int readonly, const char *protocol, char *const *server, const char *username, void *data); static int connect_live (guestfs_h *g, virDomainPtr dom); enum readonlydisk { readonlydisk_error, readonlydisk_read, readonlydisk_write, readonlydisk_ignore, }; struct add_disk_data { int readonly; enum readonlydisk readonlydisk; /* Other args to pass through to add_drive_opts. */ struct guestfs_add_drive_opts_argv optargs; }; int guestfs_impl_add_libvirt_dom (guestfs_h *g, void *domvp, const struct guestfs_add_libvirt_dom_argv *optargs) { virDomainPtr dom = domvp; ssize_t r; int readonly; const char *iface; const char *cachemode; const char *discard; bool copyonread; int live; /* Default for back-compat reasons: */ enum readonlydisk readonlydisk = readonlydisk_write; size_t ckp; struct add_disk_data data; CLEANUP_XMLFREEDOC xmlDocPtr doc = NULL; CLEANUP_FREE char *label = NULL, *imagelabel = NULL; readonly = optargs->bitmask & GUESTFS_ADD_LIBVIRT_DOM_READONLY_BITMASK ? optargs->readonly : 0; iface = optargs->bitmask & GUESTFS_ADD_LIBVIRT_DOM_IFACE_BITMASK ? optargs->iface : NULL; live = optargs->bitmask & GUESTFS_ADD_LIBVIRT_DOM_LIVE_BITMASK ? optargs->live : 0; if ((optargs->bitmask & GUESTFS_ADD_LIBVIRT_DOM_READONLYDISK_BITMASK)) { if (STREQ (optargs->readonlydisk, "error")) readonlydisk = readonlydisk_error; else if (STREQ (optargs->readonlydisk, "read")) readonlydisk = readonlydisk_read; else if (STREQ (optargs->readonlydisk, "write")) readonlydisk = readonlydisk_write; else if (STREQ (optargs->readonlydisk, "ignore")) readonlydisk = readonlydisk_ignore; else { error (g, _("unknown readonlydisk parameter")); return -1; } } cachemode = optargs->bitmask & GUESTFS_ADD_LIBVIRT_DOM_CACHEMODE_BITMASK ? optargs->cachemode : NULL; discard = optargs->bitmask & GUESTFS_ADD_LIBVIRT_DOM_DISCARD_BITMASK ? optargs->discard : NULL; copyonread = optargs->bitmask & GUESTFS_ADD_LIBVIRT_DOM_COPYONREAD_BITMASK ? optargs->copyonread : false; if (live && readonly) { error (g, _("you cannot set both live and readonly flags")); return -1; } if (!readonly) { virDomainInfo info; virErrorPtr err; int vm_running; if (virDomainGetInfo (dom, &info) == -1) { err = virGetLastError (); error (g, _("error getting domain info: %s"), err->message); return -1; } vm_running = info.state != VIR_DOMAIN_SHUTOFF; if (vm_running) { /* If the caller specified the 'live' flag, then they want us to * try to connect to guestfsd if the domain is running. Note * that live readonly connections are not possible. */ if (live) return connect_live (g, dom); /* Dangerous to modify the disks of a running VM. */ error (g, _("error: domain is a live virtual machine.\n" "Writing to the disks of a running virtual machine can cause disk corruption.\n" "Either use read-only access, or if the guest is running the guestfsd daemon\n" "specify live access. In most libguestfs tools these options are --ro or\n" "--live respectively. Consult the documentation for further information.")); return -1; } } /* Domain XML. */ if ((doc = get_domain_xml (g, dom)) == NULL) return -1; /* Find and pass the SELinux security label to the libvirt back end. * Note this has to happen before adding the disks, since those may * use the label. */ if (libvirt_selinux_label (g, doc, &label, &imagelabel) == -1) return -1; if (label && imagelabel) { guestfs_set_backend_setting (g, "internal_libvirt_label", label); guestfs_set_backend_setting (g, "internal_libvirt_imagelabel", imagelabel); guestfs_set_backend_setting (g, "internal_libvirt_norelabel_disks", "1"); } else guestfs_clear_backend_setting (g, "internal_libvirt_norelabel_disks"); /* Add the disks. */ data.optargs.bitmask = 0; data.readonly = readonly; data.readonlydisk = readonlydisk; if (iface) { data.optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_IFACE_BITMASK; data.optargs.iface = iface; } if (cachemode) { data.optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_CACHEMODE_BITMASK; data.optargs.cachemode = cachemode; } if (discard) { data.optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_DISCARD_BITMASK; data.optargs.discard = discard; } if (copyonread) { data.optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_COPYONREAD_BITMASK; data.optargs.copyonread = copyonread; } /* Checkpoint the command line around the operation so that either * all disks are added or none are added. */ ckp = guestfs_int_checkpoint_drives (g); r = for_each_disk (g, doc, add_disk, &data); if (r == -1) guestfs_int_rollback_drives (g, ckp); return r; }