2 * virsh-secret.c: Commands to manage secret
4 * Copyright (C) 2005, 2007-2016 Red Hat, Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see
18 * <http://www.gnu.org/licenses/>.
22 #include "virsh-secret.h"
26 #include "virbuffer.h"
30 #include "virsecret.h"
31 #include "virstring.h"
33 #include "vsh-table.h"
37 virshCommandOptSecret(vshControl
*ctl
, const vshCmd
*cmd
, const char **name
)
39 virSecretPtr secret
= NULL
;
41 const char *optname
= "secret";
42 virshControlPtr priv
= ctl
->privData
;
44 if (vshCommandOptStringReq(ctl
, cmd
, optname
, &n
) < 0)
47 vshDebug(ctl
, VSH_ERR_DEBUG
,
48 "%s: found option <%s>: %s\n", cmd
->def
->name
, optname
, n
);
53 secret
= virSecretLookupByUUIDString(priv
->conn
, n
);
56 vshError(ctl
, _("failed to get secret '%s'"), n
);
62 * "secret-define" command
64 static const vshCmdInfo info_secret_define
[] = {
66 .data
= N_("define or modify a secret from an XML file")
69 .data
= N_("Define or modify a secret.")
74 static const vshCmdOptDef opts_secret_define
[] = {
75 VIRSH_COMMON_OPT_FILE(N_("file containing secret attributes in XML")),
80 cmdSecretDefine(vshControl
*ctl
, const vshCmd
*cmd
)
82 const char *from
= NULL
;
85 char uuid
[VIR_UUID_STRING_BUFLEN
];
87 virshControlPtr priv
= ctl
->privData
;
89 if (vshCommandOptStringReq(ctl
, cmd
, "file", &from
) < 0)
92 if (virFileReadAll(from
, VSH_MAX_XML_FILE
, &buffer
) < 0)
95 if (!(res
= virSecretDefineXML(priv
->conn
, buffer
, 0))) {
96 vshError(ctl
, _("Failed to set attributes from %s"), from
);
100 if (virSecretGetUUIDString(res
, &(uuid
[0])) < 0) {
101 vshError(ctl
, "%s", _("Failed to get UUID of created secret"));
105 vshPrintExtra(ctl
, _("Secret %s created\n"), uuid
);
116 * "secret-dumpxml" command
118 static const vshCmdInfo info_secret_dumpxml
[] = {
120 .data
= N_("secret attributes in XML")
123 .data
= N_("Output attributes of a secret as an XML dump to stdout.")
128 static const vshCmdOptDef opts_secret_dumpxml
[] = {
131 .flags
= VSH_OFLAG_REQ
,
132 .help
= N_("secret UUID"),
133 .completer
= virshSecretUUIDCompleter
,
139 cmdSecretDumpXML(vshControl
*ctl
, const vshCmd
*cmd
)
145 secret
= virshCommandOptSecret(ctl
, cmd
, NULL
);
149 xml
= virSecretGetXMLDesc(secret
, 0);
152 vshPrint(ctl
, "%s", xml
);
157 virSecretFree(secret
);
162 * "secret-set-value" command
164 static const vshCmdInfo info_secret_set_value
[] = {
166 .data
= N_("set a secret value")
169 .data
= N_("Set a secret value.")
174 static const vshCmdOptDef opts_secret_set_value
[] = {
177 .flags
= VSH_OFLAG_REQ
,
178 .help
= N_("secret UUID"),
179 .completer
= virshSecretUUIDCompleter
,
183 .flags
= VSH_OFLAG_REQ
,
184 .help
= N_("base64-encoded secret value")
190 cmdSecretSetValue(vshControl
*ctl
, const vshCmd
*cmd
)
194 const char *base64
= NULL
;
199 if (!(secret
= virshCommandOptSecret(ctl
, cmd
, NULL
)))
202 if (vshCommandOptStringReq(ctl
, cmd
, "base64", &base64
) < 0)
205 if (!base64_decode_alloc(base64
, strlen(base64
), &value
, &value_size
)) {
206 vshError(ctl
, "%s", _("Invalid base64 data"));
210 vshError(ctl
, "%s", _("Failed to allocate memory"));
214 res
= virSecretSetValue(secret
, (unsigned char *)value
, value_size
, 0);
215 memset(value
, 0, value_size
);
219 vshError(ctl
, "%s", _("Failed to set secret value"));
222 vshPrintExtra(ctl
, "%s", _("Secret value set\n"));
226 virSecretFree(secret
);
231 * "secret-get-value" command
233 static const vshCmdInfo info_secret_get_value
[] = {
235 .data
= N_("Output a secret value")
238 .data
= N_("Output a secret value to stdout.")
243 static const vshCmdOptDef opts_secret_get_value
[] = {
246 .flags
= VSH_OFLAG_REQ
,
247 .help
= N_("secret UUID"),
248 .completer
= virshSecretUUIDCompleter
,
254 cmdSecretGetValue(vshControl
*ctl
, const vshCmd
*cmd
)
257 VIR_AUTODISPOSE_STR base64
= NULL
;
258 unsigned char *value
;
262 secret
= virshCommandOptSecret(ctl
, cmd
, NULL
);
266 value
= virSecretGetValue(secret
, &value_size
, 0);
270 if (!(base64
= virStringEncodeBase64(value
, value_size
)))
273 vshPrint(ctl
, "%s", base64
);
277 VIR_DISPOSE_N(value
, value_size
);
278 virSecretFree(secret
);
283 * "secret-undefine" command
285 static const vshCmdInfo info_secret_undefine
[] = {
287 .data
= N_("undefine a secret")
290 .data
= N_("Undefine a secret.")
295 static const vshCmdOptDef opts_secret_undefine
[] = {
298 .flags
= VSH_OFLAG_REQ
,
299 .help
= N_("secret UUID"),
300 .completer
= virshSecretUUIDCompleter
,
306 cmdSecretUndefine(vshControl
*ctl
, const vshCmd
*cmd
)
312 secret
= virshCommandOptSecret(ctl
, cmd
, &uuid
);
316 if (virSecretUndefine(secret
) < 0) {
317 vshError(ctl
, _("Failed to delete secret %s"), uuid
);
320 vshPrintExtra(ctl
, _("Secret %s deleted\n"), uuid
);
324 virSecretFree(secret
);
329 virshSecretSorter(const void *a
, const void *b
)
331 virSecretPtr
*sa
= (virSecretPtr
*) a
;
332 virSecretPtr
*sb
= (virSecretPtr
*) b
;
333 char uuid_sa
[VIR_UUID_STRING_BUFLEN
];
334 char uuid_sb
[VIR_UUID_STRING_BUFLEN
];
342 virSecretGetUUIDString(*sa
, uuid_sa
);
343 virSecretGetUUIDString(*sb
, uuid_sb
);
345 return vshStrcasecmp(uuid_sa
, uuid_sb
);
348 struct virshSecretList
{
349 virSecretPtr
*secrets
;
352 typedef struct virshSecretList
*virshSecretListPtr
;
355 virshSecretListFree(virshSecretListPtr list
)
359 if (list
&& list
->secrets
) {
360 for (i
= 0; i
< list
->nsecrets
; i
++) {
361 if (list
->secrets
[i
])
362 virSecretFree(list
->secrets
[i
]);
364 VIR_FREE(list
->secrets
);
369 static virshSecretListPtr
370 virshSecretListCollect(vshControl
*ctl
,
373 virshSecretListPtr list
= vshMalloc(ctl
, sizeof(*list
));
377 bool success
= false;
381 virshControlPtr priv
= ctl
->privData
;
383 /* try the list with flags support (0.10.2 and later) */
384 if ((ret
= virConnectListAllSecrets(priv
->conn
,
387 list
->nsecrets
= ret
;
391 /* check if the command is actually supported */
392 if (last_error
&& last_error
->code
== VIR_ERR_NO_SUPPORT
)
395 /* there was an error during the call */
396 vshError(ctl
, "%s", _("Failed to list node secrets"));
401 /* fall back to old method (0.10.1 and older) */
402 vshResetLibvirtError();
405 vshError(ctl
, "%s", _("Filtering is not supported by this libvirt"));
409 nsecrets
= virConnectNumOfSecrets(priv
->conn
);
411 vshError(ctl
, "%s", _("Failed to count secrets"));
418 uuids
= vshMalloc(ctl
, sizeof(char *) * nsecrets
);
420 nsecrets
= virConnectListSecrets(priv
->conn
, uuids
, nsecrets
);
422 vshError(ctl
, "%s", _("Failed to list secrets"));
426 list
->secrets
= vshMalloc(ctl
, sizeof(virSecretPtr
) * (nsecrets
));
429 /* get the secrets */
430 for (i
= 0; i
< nsecrets
; i
++) {
431 if (!(secret
= virSecretLookupByUUIDString(priv
->conn
, uuids
[i
])))
433 list
->secrets
[list
->nsecrets
++] = secret
;
436 /* truncate secrets that weren't found */
437 deleted
= nsecrets
- list
->nsecrets
;
441 if (list
->secrets
&& list
->nsecrets
)
442 qsort(list
->secrets
, list
->nsecrets
,
443 sizeof(*list
->secrets
), virshSecretSorter
);
445 /* truncate the list for not found secret objects */
447 VIR_SHRINK_N(list
->secrets
, list
->nsecrets
, deleted
);
453 for (i
= 0; i
< nsecrets
; i
++)
459 virshSecretListFree(list
);
467 * "secret-list" command
469 static const vshCmdInfo info_secret_list
[] = {
471 .data
= N_("list secrets")
474 .data
= N_("Returns a list of secrets")
479 static const vshCmdOptDef opts_secret_list
[] = {
480 {.name
= "ephemeral",
482 .help
= N_("list ephemeral secrets")
484 {.name
= "no-ephemeral",
486 .help
= N_("list non-ephemeral secrets")
490 .help
= N_("list private secrets")
492 {.name
= "no-private",
494 .help
= N_("list non-private secrets")
500 cmdSecretList(vshControl
*ctl
, const vshCmd
*cmd ATTRIBUTE_UNUSED
)
503 virshSecretListPtr list
= NULL
;
505 unsigned int flags
= 0;
506 vshTablePtr table
= NULL
;
508 if (vshCommandOptBool(cmd
, "ephemeral"))
509 flags
|= VIR_CONNECT_LIST_SECRETS_EPHEMERAL
;
511 if (vshCommandOptBool(cmd
, "no-ephemeral"))
512 flags
|= VIR_CONNECT_LIST_SECRETS_NO_EPHEMERAL
;
514 if (vshCommandOptBool(cmd
, "private"))
515 flags
|= VIR_CONNECT_LIST_SECRETS_PRIVATE
;
517 if (vshCommandOptBool(cmd
, "no-private"))
518 flags
|= VIR_CONNECT_LIST_SECRETS_NO_PRIVATE
;
520 if (!(list
= virshSecretListCollect(ctl
, flags
)))
523 table
= vshTableNew(_("UUID"), _("Usage"), NULL
);
527 for (i
= 0; i
< list
->nsecrets
; i
++) {
528 virSecretPtr sec
= list
->secrets
[i
];
529 int usageType
= virSecretGetUsageType(sec
);
530 const char *usageStr
= virSecretUsageTypeToString(usageType
);
531 char uuid
[VIR_UUID_STRING_BUFLEN
];
532 virBuffer buf
= VIR_BUFFER_INITIALIZER
;
533 VIR_AUTOFREE(char *) usage
= NULL
;
535 if (virSecretGetUUIDString(sec
, uuid
) < 0) {
536 vshError(ctl
, "%s", _("Failed to get uuid of secret"));
541 virBufferStrcat(&buf
, usageStr
, " ",
542 virSecretGetUsageID(sec
), NULL
);
543 usage
= virBufferContentAndReset(&buf
);
547 if (vshTableRowAppend(table
, uuid
, usage
, NULL
) < 0)
550 if (vshTableRowAppend(table
, uuid
, _("Unused"), NULL
) < 0)
555 vshTablePrintToStdout(table
, ctl
);
561 virshSecretListFree(list
);
566 * "Secret-event" command
568 VIR_ENUM_DECL(virshSecretEvent
);
569 VIR_ENUM_IMPL(virshSecretEvent
,
570 VIR_SECRET_EVENT_LAST
,
575 virshSecretEventToString(int event
)
577 const char *str
= virshSecretEventTypeToString(event
);
578 return str
? _(str
) : _("unknown");
581 struct virshSecretEventData
{
586 virshSecretEventCallback
*cb
;
588 typedef struct virshSecretEventData virshSecretEventData
;
591 vshEventLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED
,
594 int detail ATTRIBUTE_UNUSED
,
597 virshSecretEventData
*data
= opaque
;
598 char uuid
[VIR_UUID_STRING_BUFLEN
];
600 if (!data
->loop
&& data
->count
)
603 virSecretGetUUIDString(secret
, uuid
);
604 if (data
->timestamp
) {
605 char timestamp
[VIR_TIME_STRING_BUFLEN
];
607 if (virTimeStringNowRaw(timestamp
) < 0)
610 vshPrint(data
->ctl
, _("%s: event 'lifecycle' for secret %s: %s\n"),
611 timestamp
, uuid
, virshSecretEventToString(event
));
613 vshPrint(data
->ctl
, _("event 'lifecycle' for secret %s: %s\n"),
614 uuid
, virshSecretEventToString(event
));
619 vshEventDone(data
->ctl
);
623 vshEventGenericPrint(virConnectPtr conn ATTRIBUTE_UNUSED
,
627 virshSecretEventData
*data
= opaque
;
628 char uuid
[VIR_UUID_STRING_BUFLEN
];
630 if (!data
->loop
&& data
->count
)
633 virSecretGetUUIDString(secret
, uuid
);
635 if (data
->timestamp
) {
636 char timestamp
[VIR_TIME_STRING_BUFLEN
];
638 if (virTimeStringNowRaw(timestamp
) < 0)
641 vshPrint(data
->ctl
, _("%s: event '%s' for secret %s\n"),
646 vshPrint(data
->ctl
, _("event '%s' for secret %s\n"),
653 vshEventDone(data
->ctl
);
656 virshSecretEventCallback virshSecretEventCallbacks
[] = {
658 VIR_SECRET_EVENT_CALLBACK(vshEventLifecyclePrint
), },
659 { "value-changed", vshEventGenericPrint
, },
661 verify(VIR_SECRET_EVENT_ID_LAST
== ARRAY_CARDINALITY(virshSecretEventCallbacks
));
663 static const vshCmdInfo info_secret_event
[] = {
665 .data
= N_("Secret Events")
668 .data
= N_("List event types, or wait for secret events to occur")
673 static const vshCmdOptDef opts_secret_event
[] = {
675 .type
= VSH_OT_STRING
,
676 .help
= N_("filter by secret name or uuid"),
677 .completer
= virshSecretUUIDCompleter
,
680 .type
= VSH_OT_STRING
,
681 .completer
= virshSecretEventNameCompleter
,
682 .help
= N_("which event type to wait for")
686 .help
= N_("loop until timeout or interrupt, rather than one-shot")
690 .help
= N_("timeout seconds")
694 .help
= N_("list valid event types")
696 {.name
= "timestamp",
698 .help
= N_("show timestamp for each printed event")
704 cmdSecretEvent(vshControl
*ctl
, const vshCmd
*cmd
)
706 virSecretPtr secret
= NULL
;
710 virshSecretEventData data
;
711 const char *eventName
= NULL
;
713 virshControlPtr priv
= ctl
->privData
;
715 if (vshCommandOptBool(cmd
, "list")) {
718 for (i
= 0; i
< VIR_SECRET_EVENT_ID_LAST
; i
++)
719 vshPrint(ctl
, "%s\n", virshSecretEventCallbacks
[i
].name
);
723 if (vshCommandOptStringReq(ctl
, cmd
, "event", &eventName
) < 0)
726 vshError(ctl
, "%s", _("either --list or --event <type> is required"));
729 for (event
= 0; event
< VIR_SECRET_EVENT_ID_LAST
; event
++)
730 if (STREQ(eventName
, virshSecretEventCallbacks
[event
].name
))
732 if (event
== VIR_SECRET_EVENT_ID_LAST
) {
733 vshError(ctl
, _("unknown event type %s"), eventName
);
738 data
.loop
= vshCommandOptBool(cmd
, "loop");
739 data
.timestamp
= vshCommandOptBool(cmd
, "timestamp");
741 data
.cb
= &virshSecretEventCallbacks
[event
];
742 if (vshCommandOptTimeoutToMs(ctl
, cmd
, &timeout
) < 0)
745 if (vshCommandOptBool(cmd
, "secret"))
746 secret
= virshCommandOptSecret(ctl
, cmd
, NULL
);
747 if (vshEventStart(ctl
, timeout
) < 0)
750 if ((eventId
= virConnectSecretEventRegisterAny(priv
->conn
, secret
, event
,
754 switch (vshEventWait(ctl
)) {
755 case VSH_EVENT_INTERRUPT
:
756 vshPrint(ctl
, "%s", _("event loop interrupted\n"));
758 case VSH_EVENT_TIMEOUT
:
759 vshPrint(ctl
, "%s", _("event loop timed out\n"));
766 vshPrint(ctl
, _("events received: %d\n"), data
.count
);
771 vshEventCleanup(ctl
);
773 virConnectSecretEventDeregisterAny(priv
->conn
, eventId
) < 0)
776 virSecretFree(secret
);
780 const vshCmdDef secretCmds
[] = {
781 {.name
= "secret-define",
782 .handler
= cmdSecretDefine
,
783 .opts
= opts_secret_define
,
784 .info
= info_secret_define
,
787 {.name
= "secret-dumpxml",
788 .handler
= cmdSecretDumpXML
,
789 .opts
= opts_secret_dumpxml
,
790 .info
= info_secret_dumpxml
,
793 {.name
= "secret-event",
794 .handler
= cmdSecretEvent
,
795 .opts
= opts_secret_event
,
796 .info
= info_secret_event
,
799 {.name
= "secret-get-value",
800 .handler
= cmdSecretGetValue
,
801 .opts
= opts_secret_get_value
,
802 .info
= info_secret_get_value
,
805 {.name
= "secret-list",
806 .handler
= cmdSecretList
,
807 .opts
= opts_secret_list
,
808 .info
= info_secret_list
,
811 {.name
= "secret-set-value",
812 .handler
= cmdSecretSetValue
,
813 .opts
= opts_secret_set_value
,
814 .info
= info_secret_set_value
,
817 {.name
= "secret-undefine",
818 .handler
= cmdSecretUndefine
,
819 .opts
= opts_secret_undefine
,
820 .info
= info_secret_undefine
,