* src/virsh.c: Add virsh commands. * docs/virsh.pod, virsh.1: Update documentation. --- docs/virsh.pod | 43 ++++++++ src/virsh.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ virsh.1 | 34 ++++++- 3 files changed, 399 insertions(+), 1 deletions(-) diff --git a/docs/virsh.pod b/docs/virsh.pod index 10bb991..55ec64a 100644 --- a/docs/virsh.pod +++ b/docs/virsh.pod @@ -543,6 +543,49 @@ Convert a network name to network UUID. =back +=head1 SECRET COMMMANDS + +The following commands manipulate "secrets" (e.g. passwords, passphrases and +encryption keys). Libvirt can store secrets independently from their use, and +other objects (e.g. volumes or domains) can refer to the secrets for encryption +or possibly other uses. Secrets are identified using an UUID. See +L<http://libvirt.org/formatsecret.html> for documentation of the XML format +used to represent properties of secrets. + +=over 4 + +=item B<secret-define> I<file> + +Create a secret with the properties specified in I<file>, with no associated +secret value. If I<file> does not specify a UUID, choose one automatically. +If I<file> specifies an UUID of an existing secret, replace its properties by +properties defined in I<file>, without affecting the secret value. + +=item B<secret-dumpxml> I<secret> + +Output properties of I<secret> (specified by its UUID) as an XML dump to stdout. + +=item B<secret-set-value> I<secret> I<base64> + +Set the value associated with I<secret> (specified by its UUID) to the value +Base64-encoded value I<base64>. + +=item B<secret-get-value> I<secret> + +Output the value associated with I<secret> (specified by its UUID) to stdout, +encoded using Base64. + +=item B<secret-undefine> I<secret> + +Delete a I<secret> (specified by its UUID), including the associated value, if +any. + +=item B<secret-list> + +Output a list of UUIDs of known secrets to stdout. + +=back + =head1 ENVIRONMENT The following environment variables can be set to alter the behaviour diff --git a/src/virsh.c b/src/virsh.c index 910d860..9dc8857 100644 --- a/src/virsh.c +++ b/src/virsh.c @@ -41,6 +41,7 @@ #endif #include "internal.h" +#include "base64.h" #include "buf.h" #include "console.h" #include "util.h" @@ -271,6 +272,9 @@ static virStorageVolPtr vshCommandOptVolBy(vshControl *ctl, const vshCmd *cmd, vshCommandOptVolBy(_ctl, _cmd, _optname, _pooloptname, _name, \ VSH_BYUUID|VSH_BYNAME) +static virSecretPtr vshCommandOptSecret(vshControl *ctl, const vshCmd *cmd, + char **name); + static void vshPrintExtra(vshControl *ctl, const char *format, ...) ATTRIBUTE_FMT_PRINTF(2, 3); static void vshDebug(vshControl *ctl, int level, const char *format, ...) @@ -5249,9 +5253,291 @@ cmdVolPath(vshControl *ctl, const vshCmd *cmd) } +/* + * "secret-define" command + */ +static const vshCmdInfo info_secret_define[] = { + {"help", gettext_noop("define or modify a secret from an XML file")}, + {"desc", gettext_noop("Define or modify a secret.")}, + {NULL, NULL} +}; +static const vshCmdOptDef opts_secret_define[] = { + {"file", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("file containing secret attributes in XML")}, + {NULL, 0, 0, NULL} +}; +static int +cmdSecretDefine(vshControl *ctl, const vshCmd *cmd) +{ + char *from, *buffer, *uuid; + virSecretPtr res; + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + from = vshCommandOptString(cmd, "file", NULL); + if (!from) + return FALSE; + + if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) + return FALSE; + + res = virSecretDefineXML(ctl->conn, buffer, 0); + free (buffer); + + if (res == NULL) { + vshError(ctl, FALSE, _("Failed to set attributes from %s"), from); + return FALSE; + } + uuid = virSecretGetUUIDString(res); + if (uuid == NULL) { + vshError(ctl, FALSE, "%s", + _("Failed to get UUID of created secret")); + virSecretFree(res); + return FALSE; + } + vshPrint(ctl, _("Secret %s created\n"), uuid); + free(uuid); + virSecretFree(res); + return TRUE; +} + +/* + * "secret-dumpxml" command + */ +static const vshCmdInfo info_secret_dumpxml[] = { + {"help", gettext_noop("secret attributes in XML")}, + {"desc", gettext_noop("Output attributes of a secret as an XML dump to stdout.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_secret_dumpxml[] = { + {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("secret UUID")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdSecretDumpXML(vshControl *ctl, const vshCmd *cmd) +{ + virSecretPtr secret; + int ret = FALSE; + char *xml; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + secret = vshCommandOptSecret(ctl, cmd, NULL); + if (secret == NULL) + return FALSE; + + xml = virSecretGetXMLDesc(secret, 0); + if (xml == NULL) + goto cleanup; + printf("%s", xml); + free(xml); + ret = TRUE; + +cleanup: + virSecretFree(secret); + return ret; +} + +/* + * "secret-set-value" command + */ +static const vshCmdInfo info_secret_set_value[] = { + {"help", gettext_noop("set a secret value")}, + {"desc", gettext_noop("Set a secret value.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_secret_set_value[] = { + {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("secret UUID")}, + {"base64", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("base64-encoded secret value")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdSecretSetValue(vshControl *ctl, const vshCmd *cmd) +{ + virSecretPtr secret; + size_t value_size; + char *base64, *value; + int found, res, ret = FALSE; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + secret = vshCommandOptSecret(ctl, cmd, NULL); + if (secret == NULL) + return FALSE; + + base64 = vshCommandOptString(cmd, "base64", &found); + if (!base64) + goto cleanup; + + if (!base64_decode_alloc(base64, strlen(base64), &value, &value_size)) { + vshError(ctl, FALSE, _("Invalid base64 data")); + goto cleanup; + } + if (value == NULL) { + vshError(ctl, FALSE, "%s", _("Failed to allocate memory")); + return FALSE; + } + + res = virSecretSetValue(secret, (unsigned char *)value, value_size, 0); + memset(value, 0, value_size); + free (value); + + if (res != 0) { + vshError(ctl, FALSE, "%s", _("Failed to set secret value")); + goto cleanup; + } + vshPrint(ctl, "%s", _("Secret value set\n")); + ret = TRUE; + +cleanup: + virSecretFree(secret); + return ret; +} + +/* + * "secret-get-value" command + */ +static const vshCmdInfo info_secret_get_value[] = { + {"help", gettext_noop("Output a secret value")}, + {"desc", gettext_noop("Output a secret value to stdout.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_secret_get_value[] = { + {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("secret UUID")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdSecretGetValue(vshControl *ctl, const vshCmd *cmd) +{ + virSecretPtr secret; + char *base64; + unsigned char *value; + size_t value_size; + int ret = FALSE; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + secret = vshCommandOptSecret(ctl, cmd, NULL); + if (secret == NULL) + return FALSE; + + value = virSecretGetValue(secret, &value_size, 0); + if (value == NULL) + goto cleanup; + + base64_encode_alloc((char *)value, value_size, &base64); + memset(value, 0, value_size); + free(value); + + if (base64 == NULL) { + vshError(ctl, FALSE, "%s", _("Failed to allocate memory")); + goto cleanup; + } + printf("%s", base64); + memset(base64, 0, strlen(base64)); + free(base64); + ret = TRUE; + +cleanup: + virSecretFree(secret); + return ret; +} + +/* + * "secret-undefine" command + */ +static const vshCmdInfo info_secret_undefine[] = { + {"help", gettext_noop("undefine a secret")}, + {"desc", gettext_noop("Undefine a secret.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_secret_undefine[] = { + {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("secret UUID")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdSecretUndefine(vshControl *ctl, const vshCmd *cmd) +{ + virSecretPtr secret; + int ret = FALSE; + char *uuid; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + secret = vshCommandOptSecret(ctl, cmd, &uuid); + if (secret == NULL) + return FALSE; + + if (virSecretUndefine(secret) < 0) { + vshError(ctl, FALSE, _("Failed to delete secret %s"), uuid); + goto cleanup; + } + vshPrint(ctl, _("Secret %s deleted\n"), uuid); + ret = TRUE; + +cleanup: + virSecretFree(secret); + return ret; +} + +/* + * "secret-list" command + */ +static const vshCmdInfo info_secret_list[] = { + {"help", gettext_noop("list secrets")}, + {"desc", gettext_noop("Returns a list of secrets")}, + {NULL, NULL} +}; + +static int +cmdSecretList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + int maxuuids = 0, i; + char **uuids = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + maxuuids = virConnectNumOfSecrets(ctl->conn); + if (maxuuids < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list secrets")); + return FALSE; + } + uuids = vshMalloc(ctl, sizeof(*uuids) * maxuuids); + + maxuuids = virConnectListSecrets(ctl->conn, uuids, maxuuids); + if (maxuuids < 0) { + vshError(ctl, FALSE, "%s", _("Failed to list secrets")); + free(uuids); + return FALSE; + } + + qsort(uuids, maxuuids, sizeof(char *), namesorter); + + vshPrintExtra(ctl, "%s\n", _("UUID")); + vshPrintExtra(ctl, "-----------------------------------------\n"); + + for (i = 0; i < maxuuids; i++) { + vshPrint(ctl, "%-36s\n", uuids[i]); + free(uuids[i]); + } + free(uuids); + return TRUE; +} /* @@ -6931,6 +7217,14 @@ static const vshCmdDef commands[] = { {"pool-undefine", cmdPoolUndefine, opts_pool_undefine, info_pool_undefine}, {"pool-uuid", cmdPoolUuid, opts_pool_uuid, info_pool_uuid}, + {"secret-define", cmdSecretDefine, opts_secret_define, info_secret_define}, + {"secret-dumpxml", cmdSecretDumpXML, opts_secret_dumpxml, info_secret_dumpxml}, + {"secret-set-value", cmdSecretSetValue, opts_secret_set_value, info_secret_set_value}, + {"secret-get-value", cmdSecretGetValue, opts_secret_get_value, info_secret_get_value}, + {"secret-undefine", cmdSecretUndefine, opts_secret_undefine, info_secret_undefine}, + {"secret-list", cmdSecretList, NULL, info_secret_list}, + + #ifndef WIN32 {"pwd", cmdPwd, NULL, info_pwd}, #endif @@ -7490,6 +7784,35 @@ vshCommandOptVolBy(vshControl *ctl, const vshCmd *cmd, return vol; } +static virSecretPtr +vshCommandOptSecret(vshControl *ctl, const vshCmd *cmd, char **name) +{ + virSecretPtr secret = NULL; + char *n; + const char *optname = "secret"; + + if (!cmd_has_option (ctl, cmd, optname)) + return NULL; + + n = vshCommandOptString(cmd, optname, NULL); + if (n == NULL) { + vshError(ctl, FALSE, "%s", _("undefined secret UUID")); + return NULL; + } + + vshDebug(ctl, 5, "%s: found option <%s>: %s\n", cmd->def->name, optname, n); + + if (name != NULL) + *name = n; + + secret = virSecretLookupByUUIDString(ctl->conn, n); + + if (secret == NULL) + vshError(ctl, FALSE, _("failed to get secret '%s'"), n); + + return secret; +} + /* * Executes command(s) and returns return code from last command */ diff --git a/virsh.1 b/virsh.1 index 0a5b1c1..5731b4c 100644 --- a/virsh.1 +++ b/virsh.1 @@ -132,7 +132,7 @@ .\" ======================================================================== .\" .IX Title "VIRSH 1" -.TH VIRSH 1 "2009-08-11" "libvirt-0.7.0" "Virtualization Support" +.TH VIRSH 1 "2009-08-20" "libvirt-0.7.0" "Virtualization Support" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -593,6 +593,38 @@ Undefine the configuration for an inactive network. .IP "\fBnet-uuid\fR \fInetwork-name\fR" 4 .IX Item "net-uuid network-name" Convert a network name to network \s-1UUID\s0. +.SH "SECRET COMMMANDS" +.IX Header "SECRET COMMMANDS" +The following commands manipulate \*(L"secrets\*(R" (e.g. passwords, passphrases and +encryption keys). Libvirt can store secrets independently from their use, and +other objects (e.g. volumes or domains) can refer to the secrets for encryption +or possibly other uses. Secrets are identified using an \s-1UUID\s0. See +<http://libvirt.org/formatsecret.html> for documentation of the \s-1XML\s0 format +used to represent properties of secrets. +.IP "\fBsecret-define\fR \fIfile\fR" 4 +.IX Item "secret-define file" +Create a secret with the properties specified in \fIfile\fR, with no associated +secret value. If \fIfile\fR does not specify a \s-1UUID\s0, choose one automatically. +If \fIfile\fR specifies an \s-1UUID\s0 of an existing secret, replace its properties by +properties defined in \fIfile\fR, without affecting the secret value. +.IP "\fBsecret-dumpxml\fR \fIsecret\fR" 4 +.IX Item "secret-dumpxml secret" +Output properties of \fIsecret\fR (specified by its \s-1UUID\s0) as an \s-1XML\s0 dump to stdout. +.IP "\fBsecret-set-value\fR \fIsecret\fR \fIbase64\fR" 4 +.IX Item "secret-set-value secret base64" +Set the value associated with \fIsecret\fR (specified by its \s-1UUID\s0) to the value +Base64\-encoded value \fIbase64\fR. +.IP "\fBsecret-get-value\fR \fIsecret\fR" 4 +.IX Item "secret-get-value secret" +Output the value associated with \fIsecret\fR (specified by its \s-1UUID\s0) to stdout, +encoded using Base64. +.IP "\fBsecret-undefine\fR \fIsecret\fR" 4 +.IX Item "secret-undefine secret" +Delete a \fIsecret\fR (specified by its \s-1UUID\s0), including the associated value, if +any. +.IP "\fBsecret-list\fR" 4 +.IX Item "secret-list" +Output a list of UUIDs of known secrets to stdout. .SH "ENVIRONMENT" .IX Header "ENVIRONMENT" The following environment variables can be set to alter the behaviour -- 1.6.2.5 -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list