Index: virsh.c =================================================================== RCS file: /data/cvs/libvirt/src/virsh.c,v retrieving revision 1.73 diff -u -p -r1.73 virsh.c --- virsh.c 13 Apr 2007 08:04:08 -0000 1.73 +++ virsh.c 24 Apr 2007 12:53:56 -0000 @@ -24,11 +24,12 @@ #include #include #include +#include #include #include #include #include - +#include #include #include #include @@ -2403,6 +2404,202 @@ cmdVNCDisplay(vshControl * ctl, vshCmd * return ret; } +/* + * "clone" command + */ +static vshCmdInfo info_clone[] = { + {"syntax", "clone "}, + {"help", gettext_noop("clone a domain")}, + {"desc", gettext_noop("Clone a domain.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_clone[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("shut off state domain name")}, + {"devices", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("destination devices (comma separated)")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdClone(vshControl * ctl, vshCmd * cmd) +{ + xmlDocPtr xml = NULL; + xmlXPathObjectPtr obj = NULL; + xmlXPathContextPtr ctxt = NULL; + xmlNodePtr node = NULL; + virDomainPtr dom; + virDomainInfo info; + xmlChar *def=NULL, *src=NULL; + pid_t pid = 0; + char *doc, *devices, *dd_if, *dd_of; + char *str, *token, *saveptr; + char buffer[BUFSIZ]; + int status=0, i=0, nr=0, ret=FALSE, rtn=0; + int si = 4096; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(dom = vshCommandOptDomain(ctl, cmd, "domain", NULL))) + return FALSE; + + if (!(devices = vshCommandOptString(cmd, "devices", NULL))) + goto cleanup; + + virDomainGetInfo(dom, &info); + if(info.state != VIR_DOMAIN_SHUTOFF){ + vshError(ctl, FALSE, _("Cannot clone for %s state domain"), + vshDomainStateToString(info.state)); + goto cleanup; + } + + if(ctl->name){ + vshError(ctl, FALSE, _("Not supported for remote hypervisor")); + goto cleanup; + } + + doc = virDomainGetXMLDesc(dom, 0); + if (!doc) + goto cleanup; + + xml = xmlReadDoc((const xmlChar *) doc, "domain.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOWARNING); + free(doc); + if (!xml) + goto cleanup; + ctxt = xmlXPathNewContext(xml); + if (!ctxt) + goto cleanup; + + obj = xmlXPathEval(BAD_CAST "/domain/uuid", ctxt); + if ((obj == NULL) || (obj->type != XPATH_NODESET) || + (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr <= 0) || + (obj->nodesetval->nodeTab == NULL)) { + vshError(ctl, FALSE, _("Not supported for %s"), virDomainGetName(dom)); + goto cleanup; + } + node = obj->nodesetval->nodeTab[0]; + xmlUnlinkNode(node); + xmlFreeNode(node); + + obj = xmlXPathEval(BAD_CAST "/domain/devices/interface/mac", ctxt); + if ((obj == NULL) || (obj->type != XPATH_NODESET) || + (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr <= 0) || + (obj->nodesetval->nodeTab == NULL)) { + ; + } + else{ + node = obj->nodesetval->nodeTab[0]; + xmlUnlinkNode(node); + xmlFreeNode(node); + } + + obj = xmlXPathEval(BAD_CAST "/domain/name", ctxt); + if ((obj == NULL) || (obj->type != XPATH_NODESET) || + (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr <= 0) || + (obj->nodesetval->nodeTab == NULL)) { + vshError(ctl, FALSE, _("Not supported for %s"), virDomainGetName(dom)); + goto cleanup; + } + node = obj->nodesetval->nodeTab[0]->children; + + xmlTextConcat(node, (const xmlChar *)"_CLONE", 6); + + obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt); + if ((obj == NULL) || (obj->type != XPATH_NODESET) || + (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr <= 0) || + (obj->nodesetval->nodeTab == NULL)) { + vshError(ctl, FALSE, _("Not supported for %s"), virDomainGetName(dom)); + goto cleanup; + } + nr = obj->nodesetval->nodeNr; + + for(i = 0, str = devices; i < nr; i++, str = NULL){ + + memset(buffer, 0x00, sizeof(buffer)); + sprintf(buffer, "/domain/devices/disk[%d]/source/@dev", i+1); + + obj = xmlXPathEval(BAD_CAST buffer, ctxt); + if ((obj == NULL) || (obj->type != XPATH_NODESET) || + (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr <= 0) || + (obj->nodesetval->nodeTab == NULL)) { + vshError(ctl, FALSE, _("Not supported for %s"), virDomainGetName(dom)); + goto cleanup; + } + + node = obj->nodesetval->nodeTab[0]->children; + src = xmlNodeGetContent(node); + + token = strtok_r (str, ", ", &saveptr); + if (!token) { + if (i == 0){ + token = devices; + } + else{ + vshError(ctl, FALSE, _("Missing destination device for %s"), src); + goto cleanup; + } + } + + xmlNodeSetContent(node, (const xmlChar *)token); + + vshPrint(ctl, _("Cloning from %s to %s .....\n"), src, token); + + sprintf(buffer, "if=%s", src); + dd_if = strdup(buffer); + sprintf(buffer, "of=%s", token); + dd_of = strdup(buffer); + char *do_cmd[] = {(char *)"dd", dd_if, dd_of, (char *)"bs=1024", NULL}; + + pid = fork(); + if (pid == 0){ + close(1); + close(2); + + execvp(do_cmd[0], do_cmd); + vshError(ctl, FALSE, _("failed to exec %s\n"), do_cmd[0]); + _exit(EXIT_FAILURE); + } + + while(1){ + rtn = waitpid(pid, &status, 0); + if (rtn < 0) { + if (errno == EINTR) + continue; + } + break; + } + + if (dd_if) + free(dd_if); + if (dd_of) + free(dd_of); + } + + xmlDocDumpMemory(xml, &def, &si); + + dom = virDomainDefineXML(ctl->conn, (const char *)def); + if (dom == NULL) { + vshError(ctl, FALSE, _("Failed to define domain")); + goto cleanup; + } + +cleanup: + if (obj) + xmlXPathFreeObject(obj); + if (ctxt) + xmlXPathFreeContext(ctxt); + if (xml) + xmlFreeDoc(xml); + if (def) + xmlFree(def); + virDomainFree(dom); + + return ret; + +} + /* * "quit" command @@ -2467,6 +2664,7 @@ static vshCmdDef commands[] = { {"vcpupin", cmdVcpupin, opts_vcpupin, info_vcpupin}, {"version", cmdVersion, NULL, info_version}, {"vncdisplay", cmdVNCDisplay, opts_vncdisplay, info_vncdisplay}, + {"clone", cmdClone, opts_clone, info_clone}, {NULL, NULL, NULL, NULL} };