Index: docs/virsh.pod =================================================================== RCS file: /data/cvs/libvirt/docs/virsh.pod,v retrieving revision 1.16 diff -u -r1.16 virsh.pod --- docs/virsh.pod 15 May 2008 06:12:32 -0000 1.16 +++ docs/virsh.pod 30 Jul 2008 09:54:41 -0000 @@ -277,6 +277,19 @@ Output the domain information as an XML dump to stdout, this format can be used by the B command. +=item B I + +Edit the XML configuration file for a domain. + +This is equivalent to: + virsh dumpxml domain > domain.xml + edit domain.xml + virsh define domain.xml +except that it does some error checking. + +The editor used can be supplied by the C<$EDITOR> environment +variable, or if that is not defined defaults to C. + =item B optional I<--live> I I I Migrate domain to another host. Add --live for live migration. The I @@ -480,6 +493,19 @@ Output the virtual network information as an XML dump to stdout. +=item B I + +Edit the XML configuration file for a network. + +This is equivalent to: + virsh net-dumpxml network > network.xml + edit network.xml + virsh define network.xml +except that it does some error checking. + +The editor used can be supplied by the C<$EDITOR> environment +variable, or if that is not defined defaults to C. + =item B optional I<--inactive> or I<--all> Returns the list of active networks, if I<--all> is specified this will also Index: src/virsh.c =================================================================== RCS file: /data/cvs/libvirt/src/virsh.c,v retrieving revision 1.157 diff -u -r1.157 virsh.c --- src/virsh.c 22 Jul 2008 16:12:01 -0000 1.157 +++ src/virsh.c 30 Jul 2008 09:54:46 -0000 @@ -5070,6 +5070,424 @@ } /* + * "edit" command + */ +static vshCmdInfo info_edit[] = { + {"syntax", "edit "}, + {"help", gettext_noop("edit XML configuration for a domain")}, + {"desc", gettext_noop("Edit the XML configuration for a domain.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_edit[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")}, + {NULL, 0, 0, NULL} +}; + +static char * +editWriteToTempFile (vshControl *ctl, const char *doc) +{ + char *ret; + int fd; + + ret = tempnam (NULL, "virsh"); + if (!ret) { + vshError(ctl, FALSE, + _("tempnam: failed to create temporary file: %s"), + strerror (errno)); + return NULL; + } + + fd = open (ret, O_EXCL|O_CREAT|O_WRONLY, 0600); + if (fd == -1) { + vshError(ctl, FALSE, + _("open: %s: failed to create temporary file: %s"), + ret, strerror (errno)); + free (ret); + return NULL; + } + + if (safewrite (fd, doc, strlen (doc)) == -1) { + vshError(ctl, FALSE, + _("write: %s: failed to create temporary file: %s"), + ret, strerror (errno)); + close (fd); + unlink (ret); + free (ret); + return NULL; + } + if (close (fd) == -1) { + vshError(ctl, FALSE, + _("close: %s: failed to create temporary file: %s"), + ret, strerror (errno)); + unlink (ret); + free (ret); + return NULL; + } + + /* Temporary filename: caller frees. */ + return ret; +} + +static int +editFile (vshControl *ctl, const char *filename) +{ + const char *editor; + char command[100]; + int command_ret; + + editor = getenv ("EDITOR"); + if (!editor) editor = "vi"; /* could be cruel & default to ed(1) here */ + + snprintf (command, sizeof command, "%s %s", editor, filename); + command_ret = system (command); + + if (command_ret == -1) { + vshError(ctl, FALSE, + "%s: %s", + command, strerror (errno)); + return -1; + } + if (command_ret != WEXITSTATUS (0)) { + vshError(ctl, FALSE, + _("%s: command exited with non-zero status"), command); + return -1; + } + return 0; +} + +static char * +editReadBackFile (vshControl *ctl, const char *filename) +{ + int fd; + struct stat statbuf; + char *ret; + + fd = open (filename, O_RDONLY); + if (fd == -1) { + vshError(ctl, FALSE, + _("open: %s: failed to read temporary file: %s"), + filename, strerror (errno)); + return NULL; + } + if (fstat (fd, &statbuf) == -1) { + vshError(ctl, FALSE, + _("stat: %s: failed to read temporary file: %s"), + filename, strerror (errno)); + close (fd); + return NULL; + } + + ret = malloc (statbuf.st_size + 1); + if (!ret) { + vshError(ctl, FALSE, + _("malloc: failed to allocate memory for file: %s"), + strerror (errno)); + close (fd); + return NULL; + } + + if (saferead (fd, ret, statbuf.st_size) == -1) { + vshError(ctl, FALSE, + _("read: %s: failed to read temporary file: %s"), + filename, strerror (errno)); + close (fd); + free (ret); + return NULL; + } + + ret[statbuf.st_size+1] = '\0'; + close (fd); + + return ret; +} + +static int +cmdEdit (vshControl *ctl, vshCmd *cmd) +{ + int ret = FALSE; + virDomainPtr dom = NULL; + char *tmp = NULL; + char *doc = NULL; + char *doc_edited = NULL; + char *doc_reread = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + goto cleanup; + + dom = vshCommandOptDomain (ctl, cmd, "domain", NULL); + if (dom == NULL) + goto cleanup; + + /* Get the XML configuration of the domain. */ + doc = virDomainGetXMLDesc (dom, 0); + if (!doc) + goto cleanup; + + /* Create and open the temporary file. */ + tmp = editWriteToTempFile (ctl, doc); + if (!tmp) goto cleanup; + + /* Start the editor. */ + if (editFile (ctl, tmp) == -1) goto cleanup; + + /* Read back the edited file. */ + doc_edited = editReadBackFile (ctl, tmp); + if (!doc_edited) goto cleanup; + + unlink (tmp); + tmp = NULL; + + /* Compare original XML with edited. Has it changed at all? */ + if (STREQ (doc, doc_edited)) { + vshPrint(ctl, _("Domain %s XML configuration not changed.\n"), + virDomainGetName (dom)); + ret = TRUE; + goto cleanup; + } + + /* Now re-read the domain XML. Did someone else change it while + * it was being edited? This also catches problems such as us + * losing a connection or the domain going away. + */ + doc_reread = virDomainGetXMLDesc (dom, 0); + if (!doc_reread) + goto cleanup; + + if (STRNEQ (doc, doc_reread)) { + vshError (ctl, FALSE, + _("ERROR: the XML configuration was changed by another user")); + goto cleanup; + } + + /* Everything checks out, so redefine the domain. */ + virDomainFree (dom); + dom = virDomainDefineXML (ctl->conn, doc_edited); + if (!dom) + goto cleanup; + + vshPrint(ctl, _("Domain %s XML configuration edited.\n"), + virDomainGetName(dom)); + + ret = TRUE; + + cleanup: + if (dom) + virDomainFree(dom); + + free (doc); + free (doc_edited); + free (doc_reread); + + if (tmp) { + unlink (tmp); + free (tmp); + } + + return ret; +} + +/* + * "net-edit" command + */ +static vshCmdInfo info_network_edit[] = { + {"syntax", "net-edit "}, + {"help", gettext_noop("edit XML configuration for a network")}, + {"desc", gettext_noop("Edit the XML configuration for a network.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_network_edit[] = { + {"network", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("network name, id or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdNetworkEdit (vshControl *ctl, vshCmd *cmd) +{ + int ret = FALSE; + virNetworkPtr network = NULL; + char *tmp = NULL; + char *doc = NULL; + char *doc_edited = NULL; + char *doc_reread = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + goto cleanup; + + network = vshCommandOptNetwork (ctl, cmd, "network", NULL); + if (network == NULL) + goto cleanup; + + /* Get the XML configuration. */ + doc = virNetworkGetXMLDesc (network, 0); + if (!doc) + goto cleanup; + + /* Create and open the temporary file. */ + tmp = editWriteToTempFile (ctl, doc); + if (!tmp) goto cleanup; + + /* Start the editor. */ + if (editFile (ctl, tmp) == -1) goto cleanup; + + /* Read back the edited file. */ + doc_edited = editReadBackFile (ctl, tmp); + if (!doc_edited) goto cleanup; + + unlink (tmp); + tmp = NULL; + + /* Compare original XML with edited. Has it changed at all? */ + if (STREQ (doc, doc_edited)) { + vshPrint(ctl, _("Network %s XML configuration not changed.\n"), + virNetworkGetName (network)); + ret = TRUE; + goto cleanup; + } + + /* Now re-read the XML. Did someone else change it while it was + * being edited? This also catches problems such as us losing a + * connection or the network going away. + */ + doc_reread = virNetworkGetXMLDesc (network, 0); + if (!doc_reread) + goto cleanup; + + if (STRNEQ (doc, doc_reread)) { + vshError (ctl, FALSE, + _("ERROR: the XML configuration was changed by another user")); + goto cleanup; + } + + /* Everything checks out, so redefine the network. */ + virNetworkFree (network); + network = virNetworkDefineXML (ctl->conn, doc_edited); + if (!network) + goto cleanup; + + vshPrint(ctl, _("Network %s XML configuration edited.\n"), + virNetworkGetName (network)); + + ret = TRUE; + + cleanup: + if (network) + virNetworkFree (network); + + free (doc); + free (doc_edited); + free (doc_reread); + + if (tmp) { + unlink (tmp); + free (tmp); + } + + return ret; +} + +/* + * "pool-edit" command + */ +static vshCmdInfo info_pool_edit[] = { + {"syntax", "pool-edit "}, + {"help", gettext_noop("edit XML configuration for a storage pool")}, + {"desc", gettext_noop("Edit the XML configuration for a storage pool.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_pool_edit[] = { + {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("pool name or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdPoolEdit (vshControl *ctl, vshCmd *cmd) +{ + int ret = FALSE; + virStoragePoolPtr pool = NULL; + char *tmp = NULL; + char *doc = NULL; + char *doc_edited = NULL; + char *doc_reread = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + goto cleanup; + + pool = vshCommandOptPool (ctl, cmd, "pool", NULL); + if (pool == NULL) + goto cleanup; + + /* Get the XML configuration. */ + doc = virStoragePoolGetXMLDesc (pool, 0); + if (!doc) + goto cleanup; + + /* Create and open the temporary file. */ + tmp = editWriteToTempFile (ctl, doc); + if (!tmp) goto cleanup; + + /* Start the editor. */ + if (editFile (ctl, tmp) == -1) goto cleanup; + + /* Read back the edited file. */ + doc_edited = editReadBackFile (ctl, tmp); + if (!doc_edited) goto cleanup; + + unlink (tmp); + tmp = NULL; + + /* Compare original XML with edited. Has it changed at all? */ + if (STREQ (doc, doc_edited)) { + vshPrint(ctl, _("Pool %s XML configuration not changed.\n"), + virStoragePoolGetName (pool)); + ret = TRUE; + goto cleanup; + } + + /* Now re-read the XML. Did someone else change it while it was + * being edited? This also catches problems such as us losing a + * connection or the pool going away. + */ + doc_reread = virStoragePoolGetXMLDesc (pool, 0); + if (!doc_reread) + goto cleanup; + + if (STRNEQ (doc, doc_reread)) { + vshError (ctl, FALSE, + _("ERROR: the XML configuration was changed by another user")); + goto cleanup; + } + + /* Everything checks out, so redefine the pool. */ + virStoragePoolFree (pool); + pool = virStoragePoolDefineXML (ctl->conn, doc_edited, 0); + if (!pool) + goto cleanup; + + vshPrint(ctl, _("Pool %s XML configuration edited.\n"), + virStoragePoolGetName (pool)); + + ret = TRUE; + + cleanup: + if (pool) + virStoragePoolFree (pool); + + free (doc); + free (doc_edited); + free (doc_reread); + + if (tmp) { + unlink (tmp); + free (tmp); + } + + return ret; +} + +/* * "quit" command */ static vshCmdInfo info_quit[] = { @@ -5112,6 +5530,7 @@ {"domblkstat", cmdDomblkstat, opts_domblkstat, info_domblkstat}, {"domifstat", cmdDomIfstat, opts_domifstat, info_domifstat}, {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml}, + {"edit", cmdEdit, opts_edit, info_edit}, {"freecell", cmdFreecell, opts_freecell, info_freecell}, {"hostname", cmdHostname, NULL, info_hostname}, {"list", cmdList, opts_list, info_list}, @@ -5122,6 +5541,7 @@ {"net-define", cmdNetworkDefine, opts_network_define, info_network_define}, {"net-destroy", cmdNetworkDestroy, opts_network_destroy, info_network_destroy}, {"net-dumpxml", cmdNetworkDumpXML, opts_network_dumpxml, info_network_dumpxml}, + {"net-edit", cmdNetworkEdit, opts_network_edit, info_network_edit}, {"net-list", cmdNetworkList, opts_network_list, info_network_list}, {"net-name", cmdNetworkName, opts_network_name, info_network_name}, {"net-start", cmdNetworkStart, opts_network_start, info_network_start}, @@ -5138,6 +5558,7 @@ {"pool-destroy", cmdPoolDestroy, opts_pool_destroy, info_pool_destroy}, {"pool-delete", cmdPoolDelete, opts_pool_delete, info_pool_delete}, {"pool-dumpxml", cmdPoolDumpXML, opts_pool_dumpxml, info_pool_dumpxml}, + {"pool-edit", cmdPoolEdit, opts_pool_edit, info_pool_edit}, {"pool-info", cmdPoolInfo, opts_pool_info, info_pool_info}, {"pool-list", cmdPoolList, opts_pool_list, info_pool_list}, {"pool-name", cmdPoolName, opts_pool_name, info_pool_name},