backup: Wire up qemu full pull backup commands over QMP
[libvirt/ericb.git] / tools / virsh-secret.c
blobb34ae12bbedbec9949a9bc3367102e2269c26a7e
1 /*
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/>.
21 #include <config.h>
22 #include "virsh-secret.h"
24 #include "internal.h"
25 #include "base64.h"
26 #include "virbuffer.h"
27 #include "viralloc.h"
28 #include "virfile.h"
29 #include "virutil.h"
30 #include "virsecret.h"
31 #include "virstring.h"
32 #include "virtime.h"
33 #include "vsh-table.h"
34 #include "virenum.h"
36 static virSecretPtr
37 virshCommandOptSecret(vshControl *ctl, const vshCmd *cmd, const char **name)
39 virSecretPtr secret = NULL;
40 const char *n = NULL;
41 const char *optname = "secret";
42 virshControlPtr priv = ctl->privData;
44 if (vshCommandOptStringReq(ctl, cmd, optname, &n) < 0)
45 return NULL;
47 vshDebug(ctl, VSH_ERR_DEBUG,
48 "%s: found option <%s>: %s\n", cmd->def->name, optname, n);
50 if (name != NULL)
51 *name = n;
53 secret = virSecretLookupByUUIDString(priv->conn, n);
55 if (secret == NULL)
56 vshError(ctl, _("failed to get secret '%s'"), n);
58 return secret;
62 * "secret-define" command
64 static const vshCmdInfo info_secret_define[] = {
65 {.name = "help",
66 .data = N_("define or modify a secret from an XML file")
68 {.name = "desc",
69 .data = N_("Define or modify a secret.")
71 {.name = NULL}
74 static const vshCmdOptDef opts_secret_define[] = {
75 VIRSH_COMMON_OPT_FILE(N_("file containing secret attributes in XML")),
76 {.name = NULL}
79 static bool
80 cmdSecretDefine(vshControl *ctl, const vshCmd *cmd)
82 const char *from = NULL;
83 char *buffer;
84 virSecretPtr res;
85 char uuid[VIR_UUID_STRING_BUFLEN];
86 bool ret = false;
87 virshControlPtr priv = ctl->privData;
89 if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
90 return false;
92 if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0)
93 return false;
95 if (!(res = virSecretDefineXML(priv->conn, buffer, 0))) {
96 vshError(ctl, _("Failed to set attributes from %s"), from);
97 goto cleanup;
100 if (virSecretGetUUIDString(res, &(uuid[0])) < 0) {
101 vshError(ctl, "%s", _("Failed to get UUID of created secret"));
102 goto cleanup;
105 vshPrintExtra(ctl, _("Secret %s created\n"), uuid);
106 ret = true;
108 cleanup:
109 VIR_FREE(buffer);
110 if (res)
111 virSecretFree(res);
112 return ret;
116 * "secret-dumpxml" command
118 static const vshCmdInfo info_secret_dumpxml[] = {
119 {.name = "help",
120 .data = N_("secret attributes in XML")
122 {.name = "desc",
123 .data = N_("Output attributes of a secret as an XML dump to stdout.")
125 {.name = NULL}
128 static const vshCmdOptDef opts_secret_dumpxml[] = {
129 {.name = "secret",
130 .type = VSH_OT_DATA,
131 .flags = VSH_OFLAG_REQ,
132 .help = N_("secret UUID"),
133 .completer = virshSecretUUIDCompleter,
135 {.name = NULL}
138 static bool
139 cmdSecretDumpXML(vshControl *ctl, const vshCmd *cmd)
141 virSecretPtr secret;
142 bool ret = false;
143 char *xml;
145 secret = virshCommandOptSecret(ctl, cmd, NULL);
146 if (secret == NULL)
147 return false;
149 xml = virSecretGetXMLDesc(secret, 0);
150 if (xml == NULL)
151 goto cleanup;
152 vshPrint(ctl, "%s", xml);
153 VIR_FREE(xml);
154 ret = true;
156 cleanup:
157 virSecretFree(secret);
158 return ret;
162 * "secret-set-value" command
164 static const vshCmdInfo info_secret_set_value[] = {
165 {.name = "help",
166 .data = N_("set a secret value")
168 {.name = "desc",
169 .data = N_("Set a secret value.")
171 {.name = NULL}
174 static const vshCmdOptDef opts_secret_set_value[] = {
175 {.name = "secret",
176 .type = VSH_OT_DATA,
177 .flags = VSH_OFLAG_REQ,
178 .help = N_("secret UUID"),
179 .completer = virshSecretUUIDCompleter,
181 {.name = "base64",
182 .type = VSH_OT_DATA,
183 .flags = VSH_OFLAG_REQ,
184 .help = N_("base64-encoded secret value")
186 {.name = NULL}
189 static bool
190 cmdSecretSetValue(vshControl *ctl, const vshCmd *cmd)
192 virSecretPtr secret;
193 size_t value_size;
194 const char *base64 = NULL;
195 char *value;
196 int res;
197 bool ret = false;
199 if (!(secret = virshCommandOptSecret(ctl, cmd, NULL)))
200 return false;
202 if (vshCommandOptStringReq(ctl, cmd, "base64", &base64) < 0)
203 goto cleanup;
205 if (!base64_decode_alloc(base64, strlen(base64), &value, &value_size)) {
206 vshError(ctl, "%s", _("Invalid base64 data"));
207 goto cleanup;
209 if (value == NULL) {
210 vshError(ctl, "%s", _("Failed to allocate memory"));
211 goto cleanup;
214 res = virSecretSetValue(secret, (unsigned char *)value, value_size, 0);
215 memset(value, 0, value_size);
216 VIR_FREE(value);
218 if (res != 0) {
219 vshError(ctl, "%s", _("Failed to set secret value"));
220 goto cleanup;
222 vshPrintExtra(ctl, "%s", _("Secret value set\n"));
223 ret = true;
225 cleanup:
226 virSecretFree(secret);
227 return ret;
231 * "secret-get-value" command
233 static const vshCmdInfo info_secret_get_value[] = {
234 {.name = "help",
235 .data = N_("Output a secret value")
237 {.name = "desc",
238 .data = N_("Output a secret value to stdout.")
240 {.name = NULL}
243 static const vshCmdOptDef opts_secret_get_value[] = {
244 {.name = "secret",
245 .type = VSH_OT_DATA,
246 .flags = VSH_OFLAG_REQ,
247 .help = N_("secret UUID"),
248 .completer = virshSecretUUIDCompleter,
250 {.name = NULL}
253 static bool
254 cmdSecretGetValue(vshControl *ctl, const vshCmd *cmd)
256 virSecretPtr secret;
257 VIR_AUTODISPOSE_STR base64 = NULL;
258 unsigned char *value;
259 size_t value_size;
260 bool ret = false;
262 secret = virshCommandOptSecret(ctl, cmd, NULL);
263 if (secret == NULL)
264 return false;
266 value = virSecretGetValue(secret, &value_size, 0);
267 if (value == NULL)
268 goto cleanup;
270 if (!(base64 = virStringEncodeBase64(value, value_size)))
271 goto cleanup;
273 vshPrint(ctl, "%s", base64);
274 ret = true;
276 cleanup:
277 VIR_DISPOSE_N(value, value_size);
278 virSecretFree(secret);
279 return ret;
283 * "secret-undefine" command
285 static const vshCmdInfo info_secret_undefine[] = {
286 {.name = "help",
287 .data = N_("undefine a secret")
289 {.name = "desc",
290 .data = N_("Undefine a secret.")
292 {.name = NULL}
295 static const vshCmdOptDef opts_secret_undefine[] = {
296 {.name = "secret",
297 .type = VSH_OT_DATA,
298 .flags = VSH_OFLAG_REQ,
299 .help = N_("secret UUID"),
300 .completer = virshSecretUUIDCompleter,
302 {.name = NULL}
305 static bool
306 cmdSecretUndefine(vshControl *ctl, const vshCmd *cmd)
308 virSecretPtr secret;
309 bool ret = false;
310 const char *uuid;
312 secret = virshCommandOptSecret(ctl, cmd, &uuid);
313 if (secret == NULL)
314 return false;
316 if (virSecretUndefine(secret) < 0) {
317 vshError(ctl, _("Failed to delete secret %s"), uuid);
318 goto cleanup;
320 vshPrintExtra(ctl, _("Secret %s deleted\n"), uuid);
321 ret = true;
323 cleanup:
324 virSecretFree(secret);
325 return ret;
328 static int
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];
336 if (*sa && !*sb)
337 return -1;
339 if (!*sa)
340 return *sb != NULL;
342 virSecretGetUUIDString(*sa, uuid_sa);
343 virSecretGetUUIDString(*sb, uuid_sb);
345 return vshStrcasecmp(uuid_sa, uuid_sb);
348 struct virshSecretList {
349 virSecretPtr *secrets;
350 size_t nsecrets;
352 typedef struct virshSecretList *virshSecretListPtr;
354 static void
355 virshSecretListFree(virshSecretListPtr list)
357 size_t i;
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);
366 VIR_FREE(list);
369 static virshSecretListPtr
370 virshSecretListCollect(vshControl *ctl,
371 unsigned int flags)
373 virshSecretListPtr list = vshMalloc(ctl, sizeof(*list));
374 size_t i;
375 int ret;
376 virSecretPtr secret;
377 bool success = false;
378 size_t deleted = 0;
379 int nsecrets = 0;
380 char **uuids = NULL;
381 virshControlPtr priv = ctl->privData;
383 /* try the list with flags support (0.10.2 and later) */
384 if ((ret = virConnectListAllSecrets(priv->conn,
385 &list->secrets,
386 flags)) >= 0) {
387 list->nsecrets = ret;
388 goto finished;
391 /* check if the command is actually supported */
392 if (last_error && last_error->code == VIR_ERR_NO_SUPPORT)
393 goto fallback;
395 /* there was an error during the call */
396 vshError(ctl, "%s", _("Failed to list node secrets"));
397 goto cleanup;
400 fallback:
401 /* fall back to old method (0.10.1 and older) */
402 vshResetLibvirtError();
404 if (flags) {
405 vshError(ctl, "%s", _("Filtering is not supported by this libvirt"));
406 goto cleanup;
409 nsecrets = virConnectNumOfSecrets(priv->conn);
410 if (nsecrets < 0) {
411 vshError(ctl, "%s", _("Failed to count secrets"));
412 goto cleanup;
415 if (nsecrets == 0)
416 return list;
418 uuids = vshMalloc(ctl, sizeof(char *) * nsecrets);
420 nsecrets = virConnectListSecrets(priv->conn, uuids, nsecrets);
421 if (nsecrets < 0) {
422 vshError(ctl, "%s", _("Failed to list secrets"));
423 goto cleanup;
426 list->secrets = vshMalloc(ctl, sizeof(virSecretPtr) * (nsecrets));
427 list->nsecrets = 0;
429 /* get the secrets */
430 for (i = 0; i < nsecrets; i++) {
431 if (!(secret = virSecretLookupByUUIDString(priv->conn, uuids[i])))
432 continue;
433 list->secrets[list->nsecrets++] = secret;
436 /* truncate secrets that weren't found */
437 deleted = nsecrets - list->nsecrets;
439 finished:
440 /* sort the list */
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 */
446 if (deleted)
447 VIR_SHRINK_N(list->secrets, list->nsecrets, deleted);
449 success = true;
451 cleanup:
452 if (nsecrets > 0) {
453 for (i = 0; i < nsecrets; i++)
454 VIR_FREE(uuids[i]);
455 VIR_FREE(uuids);
458 if (!success) {
459 virshSecretListFree(list);
460 list = NULL;
463 return list;
467 * "secret-list" command
469 static const vshCmdInfo info_secret_list[] = {
470 {.name = "help",
471 .data = N_("list secrets")
473 {.name = "desc",
474 .data = N_("Returns a list of secrets")
476 {.name = NULL}
479 static const vshCmdOptDef opts_secret_list[] = {
480 {.name = "ephemeral",
481 .type = VSH_OT_BOOL,
482 .help = N_("list ephemeral secrets")
484 {.name = "no-ephemeral",
485 .type = VSH_OT_BOOL,
486 .help = N_("list non-ephemeral secrets")
488 {.name = "private",
489 .type = VSH_OT_BOOL,
490 .help = N_("list private secrets")
492 {.name = "no-private",
493 .type = VSH_OT_BOOL,
494 .help = N_("list non-private secrets")
496 {.name = NULL}
499 static bool
500 cmdSecretList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
502 size_t i;
503 virshSecretListPtr list = NULL;
504 bool ret = false;
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)))
521 return false;
523 table = vshTableNew(_("UUID"), _("Usage"), NULL);
524 if (!table)
525 goto cleanup;
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"));
537 goto cleanup;
540 if (usageType) {
541 virBufferStrcat(&buf, usageStr, " ",
542 virSecretGetUsageID(sec), NULL);
543 usage = virBufferContentAndReset(&buf);
544 if (!usage)
545 goto cleanup;
547 if (vshTableRowAppend(table, uuid, usage, NULL) < 0)
548 goto cleanup;
549 } else {
550 if (vshTableRowAppend(table, uuid, _("Unused"), NULL) < 0)
551 goto cleanup;
555 vshTablePrintToStdout(table, ctl);
557 ret = true;
559 cleanup:
560 vshTableFree(table);
561 virshSecretListFree(list);
562 return ret;
566 * "Secret-event" command
568 VIR_ENUM_DECL(virshSecretEvent);
569 VIR_ENUM_IMPL(virshSecretEvent,
570 VIR_SECRET_EVENT_LAST,
571 N_("Defined"),
572 N_("Undefined"));
574 static const char *
575 virshSecretEventToString(int event)
577 const char *str = virshSecretEventTypeToString(event);
578 return str ? _(str) : _("unknown");
581 struct virshSecretEventData {
582 vshControl *ctl;
583 bool loop;
584 bool timestamp;
585 int count;
586 virshSecretEventCallback *cb;
588 typedef struct virshSecretEventData virshSecretEventData;
590 static void
591 vshEventLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED,
592 virSecretPtr secret,
593 int event,
594 int detail ATTRIBUTE_UNUSED,
595 void *opaque)
597 virshSecretEventData *data = opaque;
598 char uuid[VIR_UUID_STRING_BUFLEN];
600 if (!data->loop && data->count)
601 return;
603 virSecretGetUUIDString(secret, uuid);
604 if (data->timestamp) {
605 char timestamp[VIR_TIME_STRING_BUFLEN];
607 if (virTimeStringNowRaw(timestamp) < 0)
608 timestamp[0] = '\0';
610 vshPrint(data->ctl, _("%s: event 'lifecycle' for secret %s: %s\n"),
611 timestamp, uuid, virshSecretEventToString(event));
612 } else {
613 vshPrint(data->ctl, _("event 'lifecycle' for secret %s: %s\n"),
614 uuid, virshSecretEventToString(event));
617 data->count++;
618 if (!data->loop)
619 vshEventDone(data->ctl);
622 static void
623 vshEventGenericPrint(virConnectPtr conn ATTRIBUTE_UNUSED,
624 virSecretPtr secret,
625 void *opaque)
627 virshSecretEventData *data = opaque;
628 char uuid[VIR_UUID_STRING_BUFLEN];
630 if (!data->loop && data->count)
631 return;
633 virSecretGetUUIDString(secret, uuid);
635 if (data->timestamp) {
636 char timestamp[VIR_TIME_STRING_BUFLEN];
638 if (virTimeStringNowRaw(timestamp) < 0)
639 timestamp[0] = '\0';
641 vshPrint(data->ctl, _("%s: event '%s' for secret %s\n"),
642 timestamp,
643 data->cb->name,
644 uuid);
645 } else {
646 vshPrint(data->ctl, _("event '%s' for secret %s\n"),
647 data->cb->name,
648 uuid);
651 data->count++;
652 if (!data->loop)
653 vshEventDone(data->ctl);
656 virshSecretEventCallback virshSecretEventCallbacks[] = {
657 { "lifecycle",
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[] = {
664 {.name = "help",
665 .data = N_("Secret Events")
667 {.name = "desc",
668 .data = N_("List event types, or wait for secret events to occur")
670 {.name = NULL}
673 static const vshCmdOptDef opts_secret_event[] = {
674 {.name = "secret",
675 .type = VSH_OT_STRING,
676 .help = N_("filter by secret name or uuid"),
677 .completer = virshSecretUUIDCompleter,
679 {.name = "event",
680 .type = VSH_OT_STRING,
681 .completer = virshSecretEventNameCompleter,
682 .help = N_("which event type to wait for")
684 {.name = "loop",
685 .type = VSH_OT_BOOL,
686 .help = N_("loop until timeout or interrupt, rather than one-shot")
688 {.name = "timeout",
689 .type = VSH_OT_INT,
690 .help = N_("timeout seconds")
692 {.name = "list",
693 .type = VSH_OT_BOOL,
694 .help = N_("list valid event types")
696 {.name = "timestamp",
697 .type = VSH_OT_BOOL,
698 .help = N_("show timestamp for each printed event")
700 {.name = NULL}
703 static bool
704 cmdSecretEvent(vshControl *ctl, const vshCmd *cmd)
706 virSecretPtr secret = NULL;
707 bool ret = false;
708 int eventId = -1;
709 int timeout = 0;
710 virshSecretEventData data;
711 const char *eventName = NULL;
712 int event;
713 virshControlPtr priv = ctl->privData;
715 if (vshCommandOptBool(cmd, "list")) {
716 size_t i;
718 for (i = 0; i < VIR_SECRET_EVENT_ID_LAST; i++)
719 vshPrint(ctl, "%s\n", virshSecretEventCallbacks[i].name);
720 return true;
723 if (vshCommandOptStringReq(ctl, cmd, "event", &eventName) < 0)
724 return false;
725 if (!eventName) {
726 vshError(ctl, "%s", _("either --list or --event <type> is required"));
727 return false;
729 for (event = 0; event < VIR_SECRET_EVENT_ID_LAST; event++)
730 if (STREQ(eventName, virshSecretEventCallbacks[event].name))
731 break;
732 if (event == VIR_SECRET_EVENT_ID_LAST) {
733 vshError(ctl, _("unknown event type %s"), eventName);
734 return false;
737 data.ctl = ctl;
738 data.loop = vshCommandOptBool(cmd, "loop");
739 data.timestamp = vshCommandOptBool(cmd, "timestamp");
740 data.count = 0;
741 data.cb = &virshSecretEventCallbacks[event];
742 if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
743 return false;
745 if (vshCommandOptBool(cmd, "secret"))
746 secret = virshCommandOptSecret(ctl, cmd, NULL);
747 if (vshEventStart(ctl, timeout) < 0)
748 goto cleanup;
750 if ((eventId = virConnectSecretEventRegisterAny(priv->conn, secret, event,
751 data.cb->cb,
752 &data, NULL)) < 0)
753 goto cleanup;
754 switch (vshEventWait(ctl)) {
755 case VSH_EVENT_INTERRUPT:
756 vshPrint(ctl, "%s", _("event loop interrupted\n"));
757 break;
758 case VSH_EVENT_TIMEOUT:
759 vshPrint(ctl, "%s", _("event loop timed out\n"));
760 break;
761 case VSH_EVENT_DONE:
762 break;
763 default:
764 goto cleanup;
766 vshPrint(ctl, _("events received: %d\n"), data.count);
767 if (data.count)
768 ret = true;
770 cleanup:
771 vshEventCleanup(ctl);
772 if (eventId >= 0 &&
773 virConnectSecretEventDeregisterAny(priv->conn, eventId) < 0)
774 ret = false;
775 if (secret)
776 virSecretFree(secret);
777 return ret;
780 const vshCmdDef secretCmds[] = {
781 {.name = "secret-define",
782 .handler = cmdSecretDefine,
783 .opts = opts_secret_define,
784 .info = info_secret_define,
785 .flags = 0
787 {.name = "secret-dumpxml",
788 .handler = cmdSecretDumpXML,
789 .opts = opts_secret_dumpxml,
790 .info = info_secret_dumpxml,
791 .flags = 0
793 {.name = "secret-event",
794 .handler = cmdSecretEvent,
795 .opts = opts_secret_event,
796 .info = info_secret_event,
797 .flags = 0
799 {.name = "secret-get-value",
800 .handler = cmdSecretGetValue,
801 .opts = opts_secret_get_value,
802 .info = info_secret_get_value,
803 .flags = 0
805 {.name = "secret-list",
806 .handler = cmdSecretList,
807 .opts = opts_secret_list,
808 .info = info_secret_list,
809 .flags = 0
811 {.name = "secret-set-value",
812 .handler = cmdSecretSetValue,
813 .opts = opts_secret_set_value,
814 .info = info_secret_set_value,
815 .flags = 0
817 {.name = "secret-undefine",
818 .handler = cmdSecretUndefine,
819 .opts = opts_secret_undefine,
820 .info = info_secret_undefine,
821 .flags = 0
823 {.name = NULL}