backup: Wire up qemu full pull backup commands over QMP
[libvirt/ericb.git] / tools / virsh-domain.c
blobd75c2e954a80567a8b92c83532114685b7251e27
1 /*
2 * virsh-domain.c: Commands to manage domain
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-domain.h"
23 #include "virsh-util.h"
25 #include <fcntl.h>
26 #include <poll.h>
27 #include <signal.h>
28 #include <sys/time.h>
30 #include <libxml/parser.h>
31 #include <libxml/tree.h>
32 #include <libxml/xpath.h>
33 #include <libxml/xmlsave.h>
35 #include "internal.h"
36 #include "virbitmap.h"
37 #include "virbuffer.h"
38 #include "c-ctype.h"
39 #include "conf/domain_conf.h"
40 #include "viralloc.h"
41 #include "vircommand.h"
42 #include "virfile.h"
43 #include "virjson.h"
44 #include "virkeycode.h"
45 #include "virmacaddr.h"
46 #include "virnetdevbandwidth.h"
47 #include "virprocess.h"
48 #include "virstring.h"
49 #include "virsh-console.h"
50 #include "virsh-domain-monitor.h"
51 #include "virerror.h"
52 #include "virtime.h"
53 #include "virtypedparam.h"
54 #include "virxml.h"
55 #include "virsh-nodedev.h"
56 #include "viruri.h"
57 #include "vsh-table.h"
58 #include "virenum.h"
60 /* Gnulib doesn't guarantee SA_SIGINFO support. */
61 #ifndef SA_SIGINFO
62 # define SA_SIGINFO 0
63 #endif
65 #define VIRSH_COMMON_OPT_DOMAIN_PERSISTENT \
66 {.name = "persistent", \
67 .type = VSH_OT_BOOL, \
68 .help = N_("make live change persistent") \
71 #define VIRSH_COMMON_OPT_DOMAIN_CONFIG \
72 VIRSH_COMMON_OPT_CONFIG(N_("affect next boot"))
74 #define VIRSH_COMMON_OPT_DOMAIN_LIVE \
75 VIRSH_COMMON_OPT_LIVE(N_("affect running domain"))
77 #define VIRSH_COMMON_OPT_DOMAIN_CURRENT \
78 VIRSH_COMMON_OPT_CURRENT(N_("affect current domain"))
81 static virDomainPtr
82 virshDomainDefine(virConnectPtr conn, const char *xml, unsigned int flags)
84 virDomainPtr dom;
85 if (flags) {
86 dom = virDomainDefineXMLFlags(conn, xml, flags);
87 /* If validate is the only flag, just drop it and
88 * try again.
90 if (!dom) {
91 if ((virGetLastErrorCode() == VIR_ERR_NO_SUPPORT) &&
92 (flags == VIR_DOMAIN_DEFINE_VALIDATE))
93 dom = virDomainDefineXML(conn, xml);
95 } else {
96 dom = virDomainDefineXML(conn, xml);
98 return dom;
101 VIR_ENUM_DECL(virshDomainVcpuState);
102 VIR_ENUM_IMPL(virshDomainVcpuState,
103 VIR_VCPU_LAST,
104 N_("offline"),
105 N_("running"),
106 N_("blocked"));
108 static const char *
109 virshDomainVcpuStateToString(int state)
111 const char *str = virshDomainVcpuStateTypeToString(state);
112 return str ? _(str) : _("no state");
116 * Determine number of CPU nodes present by trying
117 * virNodeGetCPUMap and falling back to virNodeGetInfo
118 * if needed.
120 static int
121 virshNodeGetCPUCount(virConnectPtr conn)
123 int ret;
124 virNodeInfo nodeinfo;
126 if ((ret = virNodeGetCPUMap(conn, NULL, NULL, 0)) < 0) {
127 /* fall back to nodeinfo */
128 vshResetLibvirtError();
129 if (virNodeGetInfo(conn, &nodeinfo) == 0)
130 ret = VIR_NODEINFO_MAXCPUS(nodeinfo);
132 return ret;
136 * "attach-device" command
138 static const vshCmdInfo info_attach_device[] = {
139 {.name = "help",
140 .data = N_("attach device from an XML file")
142 {.name = "desc",
143 .data = N_("Attach device from an XML <file>.")
145 {.name = NULL}
148 static const vshCmdOptDef opts_attach_device[] = {
149 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
150 VIRSH_COMMON_OPT_FILE(N_("XML file")),
151 VIRSH_COMMON_OPT_DOMAIN_PERSISTENT,
152 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
153 VIRSH_COMMON_OPT_DOMAIN_LIVE,
154 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
155 {.name = NULL}
158 static bool
159 cmdAttachDevice(vshControl *ctl, const vshCmd *cmd)
161 virDomainPtr dom;
162 const char *from = NULL;
163 char *buffer;
164 int rv;
165 bool ret = false;
166 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
167 bool current = vshCommandOptBool(cmd, "current");
168 bool config = vshCommandOptBool(cmd, "config");
169 bool live = vshCommandOptBool(cmd, "live");
170 bool persistent = vshCommandOptBool(cmd, "persistent");
172 VSH_EXCLUSIVE_OPTIONS_VAR(persistent, current);
174 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
175 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
177 if (config || persistent)
178 flags |= VIR_DOMAIN_AFFECT_CONFIG;
179 if (live)
180 flags |= VIR_DOMAIN_AFFECT_LIVE;
182 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
183 return false;
185 if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
186 goto cleanup;
188 if (persistent &&
189 virDomainIsActive(dom) == 1)
190 flags |= VIR_DOMAIN_AFFECT_LIVE;
192 if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) {
193 vshReportError(ctl);
194 goto cleanup;
197 if (flags || current)
198 rv = virDomainAttachDeviceFlags(dom, buffer, flags);
199 else
200 rv = virDomainAttachDevice(dom, buffer);
202 VIR_FREE(buffer);
204 if (rv < 0) {
205 vshError(ctl, _("Failed to attach device from %s"), from);
206 goto cleanup;
209 vshPrintExtra(ctl, "%s", _("Device attached successfully\n"));
210 ret = true;
212 cleanup:
213 virshDomainFree(dom);
214 return ret;
218 * "attach-disk" command
220 static const vshCmdInfo info_attach_disk[] = {
221 {.name = "help",
222 .data = N_("attach disk device")
224 {.name = "desc",
225 .data = N_("Attach new disk device.")
227 {.name = NULL}
230 static const vshCmdOptDef opts_attach_disk[] = {
231 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
232 {.name = "source",
233 .type = VSH_OT_DATA,
234 .flags = VSH_OFLAG_REQ | VSH_OFLAG_EMPTY_OK,
235 .help = N_("source of disk device")
237 {.name = "target",
238 .type = VSH_OT_DATA,
239 .flags = VSH_OFLAG_REQ,
240 .help = N_("target of disk device")
242 {.name = "targetbus",
243 .type = VSH_OT_STRING,
244 .help = N_("target bus of disk device")
246 {.name = "driver",
247 .type = VSH_OT_STRING,
248 .help = N_("driver of disk device")
250 {.name = "subdriver",
251 .type = VSH_OT_STRING,
252 .help = N_("subdriver of disk device")
254 {.name = "iothread",
255 .type = VSH_OT_STRING,
256 .help = N_("IOThread to be used by supported device")
258 {.name = "cache",
259 .type = VSH_OT_STRING,
260 .help = N_("cache mode of disk device")
262 {.name = "io",
263 .type = VSH_OT_STRING,
264 .help = N_("io policy of disk device")
266 {.name = "type",
267 .type = VSH_OT_STRING,
268 .help = N_("target device type")
270 {.name = "shareable",
271 .type = VSH_OT_ALIAS,
272 .help = "mode=shareable"
274 {.name = "mode",
275 .type = VSH_OT_STRING,
276 .help = N_("mode of device reading and writing")
278 {.name = "sourcetype",
279 .type = VSH_OT_STRING,
280 .help = N_("type of source (block|file)")
282 {.name = "serial",
283 .type = VSH_OT_STRING,
284 .help = N_("serial of disk device")
286 {.name = "wwn",
287 .type = VSH_OT_STRING,
288 .help = N_("wwn of disk device")
290 {.name = "alias",
291 .type = VSH_OT_STRING,
292 .help = N_("custom alias name of disk device")
294 {.name = "rawio",
295 .type = VSH_OT_BOOL,
296 .help = N_("needs rawio capability")
298 {.name = "address",
299 .type = VSH_OT_STRING,
300 .help = N_("address of disk device")
302 {.name = "multifunction",
303 .type = VSH_OT_BOOL,
304 .help = N_("use multifunction pci under specified address")
306 {.name = "print-xml",
307 .type = VSH_OT_BOOL,
308 .help = N_("print XML document rather than attach the disk")
310 VIRSH_COMMON_OPT_DOMAIN_PERSISTENT,
311 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
312 VIRSH_COMMON_OPT_DOMAIN_LIVE,
313 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
314 {.name = NULL}
317 enum {
318 DISK_ADDR_TYPE_INVALID,
319 DISK_ADDR_TYPE_PCI,
320 DISK_ADDR_TYPE_SCSI,
321 DISK_ADDR_TYPE_IDE,
322 DISK_ADDR_TYPE_CCW,
323 DISK_ADDR_TYPE_USB,
324 DISK_ADDR_TYPE_SATA,
327 struct PCIAddress {
328 unsigned int domain;
329 unsigned int bus;
330 unsigned int slot;
331 unsigned int function;
334 struct SCSIAddress {
335 unsigned int controller;
336 unsigned int bus;
337 unsigned long long unit;
340 struct IDEAddress {
341 unsigned int controller;
342 unsigned int bus;
343 unsigned int unit;
346 struct CCWAddress {
347 unsigned int cssid;
348 unsigned int ssid;
349 unsigned int devno;
352 struct USBAddress {
353 unsigned int bus;
354 unsigned int port;
357 struct SATAAddress {
358 unsigned int controller;
359 unsigned int bus;
360 unsigned long long unit;
363 struct DiskAddress {
364 int type;
365 union {
366 struct PCIAddress pci;
367 struct SCSIAddress scsi;
368 struct IDEAddress ide;
369 struct CCWAddress ccw;
370 struct USBAddress usb;
371 struct SATAAddress sata;
372 } addr;
375 static int str2PCIAddress(const char *str, struct PCIAddress *pciAddr)
377 char *domain, *bus, *slot, *function;
379 if (!pciAddr)
380 return -1;
381 if (!str)
382 return -1;
384 domain = (char *)str;
386 if (virStrToLong_uip(domain, &bus, 16, &pciAddr->domain) != 0)
387 return -1;
389 bus++;
390 if (virStrToLong_uip(bus, &slot, 16, &pciAddr->bus) != 0)
391 return -1;
393 slot++;
394 if (virStrToLong_uip(slot, &function, 16, &pciAddr->slot) != 0)
395 return -1;
397 function++;
398 if (virStrToLong_uip(function, NULL, 16, &pciAddr->function) != 0)
399 return -1;
401 return 0;
404 static int str2SCSIAddress(const char *str, struct SCSIAddress *scsiAddr)
406 char *controller, *bus, *unit;
408 if (!scsiAddr)
409 return -1;
410 if (!str)
411 return -1;
413 controller = (char *)str;
415 if (virStrToLong_uip(controller, &bus, 10, &scsiAddr->controller) != 0)
416 return -1;
418 bus++;
419 if (virStrToLong_uip(bus, &unit, 10, &scsiAddr->bus) != 0)
420 return -1;
422 unit++;
423 if (virStrToLong_ullp(unit, NULL, 10, &scsiAddr->unit) != 0)
424 return -1;
426 return 0;
429 static int str2IDEAddress(const char *str, struct IDEAddress *ideAddr)
431 char *controller, *bus, *unit;
433 if (!ideAddr)
434 return -1;
435 if (!str)
436 return -1;
438 controller = (char *)str;
440 if (virStrToLong_uip(controller, &bus, 10, &ideAddr->controller) != 0)
441 return -1;
443 bus++;
444 if (virStrToLong_uip(bus, &unit, 10, &ideAddr->bus) != 0)
445 return -1;
447 unit++;
448 if (virStrToLong_uip(unit, NULL, 10, &ideAddr->unit) != 0)
449 return -1;
451 return 0;
454 static int str2CCWAddress(const char *str, struct CCWAddress *ccwAddr)
456 char *cssid, *ssid, *devno;
458 if (!ccwAddr)
459 return -1;
460 if (!str)
461 return -1;
463 cssid = (char *)str;
465 if (virStrToLong_uip(cssid, &ssid, 16, &ccwAddr->cssid) != 0)
466 return -1;
468 ssid++;
469 if (virStrToLong_uip(ssid, &devno, 16, &ccwAddr->ssid) != 0)
470 return -1;
472 devno++;
473 if (virStrToLong_uip(devno, NULL, 16, &ccwAddr->devno) != 0)
474 return -1;
476 return 0;
479 static int str2USBAddress(const char *str, struct USBAddress *usbAddr)
481 char *bus, *port;
483 if (!usbAddr)
484 return -1;
485 if (!str)
486 return -1;
488 bus = (char *)str;
490 if (virStrToLong_uip(bus, &port, 10, &usbAddr->bus) != 0)
491 return -1;
493 port++;
494 if (virStrToLong_uip(port, NULL, 10, &usbAddr->port) != 0)
495 return -1;
497 return 0;
500 static int str2SATAAddress(const char *str, struct SATAAddress *sataAddr)
502 char *controller, *bus, *unit;
504 if (!sataAddr)
505 return -1;
506 if (!str)
507 return -1;
509 controller = (char *)str;
511 if (virStrToLong_uip(controller, &bus, 10, &sataAddr->controller) != 0)
512 return -1;
514 bus++;
515 if (virStrToLong_uip(bus, &unit, 10, &sataAddr->bus) != 0)
516 return -1;
518 unit++;
519 if (virStrToLong_ullp(unit, NULL, 10, &sataAddr->unit) != 0)
520 return -1;
522 return 0;
525 /* pci address pci:0000.00.0x0a.0 (domain:bus:slot:function)
526 * ide disk address: ide:00.00.0 (controller:bus:unit)
527 * scsi disk address: scsi:00.00.0 (controller:bus:unit)
528 * ccw disk address: ccw:0xfe.0.0000 (cssid:ssid:devno)
529 * usb disk address: usb:00.00 (bus:port)
530 * sata disk address: sata:00.00.0 (controller:bus:unit)
533 static int str2DiskAddress(const char *str, struct DiskAddress *diskAddr)
535 char *type, *addr;
537 if (!diskAddr)
538 return -1;
539 if (!str)
540 return -1;
542 type = (char *)str;
543 addr = strchr(type, ':');
544 if (!addr)
545 return -1;
547 if (STREQLEN(type, "pci", addr - type)) {
548 diskAddr->type = DISK_ADDR_TYPE_PCI;
549 return str2PCIAddress(addr + 1, &diskAddr->addr.pci);
550 } else if (STREQLEN(type, "scsi", addr - type)) {
551 diskAddr->type = DISK_ADDR_TYPE_SCSI;
552 return str2SCSIAddress(addr + 1, &diskAddr->addr.scsi);
553 } else if (STREQLEN(type, "ide", addr - type)) {
554 diskAddr->type = DISK_ADDR_TYPE_IDE;
555 return str2IDEAddress(addr + 1, &diskAddr->addr.ide);
556 } else if (STREQLEN(type, "ccw", addr - type)) {
557 diskAddr->type = DISK_ADDR_TYPE_CCW;
558 return str2CCWAddress(addr + 1, &diskAddr->addr.ccw);
559 } else if (STREQLEN(type, "usb", addr - type)) {
560 diskAddr->type = DISK_ADDR_TYPE_USB;
561 return str2USBAddress(addr + 1, &diskAddr->addr.usb);
562 } else if (STREQLEN(type, "sata", addr - type)) {
563 diskAddr->type = DISK_ADDR_TYPE_SATA;
564 return str2SATAAddress(addr + 1, &diskAddr->addr.sata);
567 return -1;
570 static bool
571 cmdAttachDisk(vshControl *ctl, const vshCmd *cmd)
573 virDomainPtr dom = NULL;
574 const char *source = NULL, *target = NULL, *driver = NULL,
575 *subdriver = NULL, *type = NULL, *mode = NULL,
576 *iothread = NULL, *cache = NULL, *io = NULL,
577 *serial = NULL, *straddr = NULL, *wwn = NULL,
578 *targetbus = NULL, *alias = NULL;
579 struct DiskAddress diskAddr;
580 bool isFile = false, functionReturn = false;
581 int ret;
582 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
583 const char *stype = NULL;
584 virBuffer buf = VIR_BUFFER_INITIALIZER;
585 char *xml = NULL;
586 struct stat st;
587 bool current = vshCommandOptBool(cmd, "current");
588 bool config = vshCommandOptBool(cmd, "config");
589 bool live = vshCommandOptBool(cmd, "live");
590 bool persistent = vshCommandOptBool(cmd, "persistent");
592 VSH_EXCLUSIVE_OPTIONS_VAR(persistent, current);
594 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
595 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
597 if (config || persistent)
598 flags |= VIR_DOMAIN_AFFECT_CONFIG;
599 if (live)
600 flags |= VIR_DOMAIN_AFFECT_LIVE;
602 if (vshCommandOptStringReq(ctl, cmd, "source", &source) < 0 ||
603 vshCommandOptStringReq(ctl, cmd, "target", &target) < 0 ||
604 vshCommandOptStringReq(ctl, cmd, "driver", &driver) < 0 ||
605 vshCommandOptStringReq(ctl, cmd, "subdriver", &subdriver) < 0 ||
606 vshCommandOptStringReq(ctl, cmd, "type", &type) < 0 ||
607 vshCommandOptStringReq(ctl, cmd, "mode", &mode) < 0 ||
608 vshCommandOptStringReq(ctl, cmd, "iothread", &iothread) < 0 ||
609 vshCommandOptStringReq(ctl, cmd, "cache", &cache) < 0 ||
610 vshCommandOptStringReq(ctl, cmd, "io", &io) < 0 ||
611 vshCommandOptStringReq(ctl, cmd, "serial", &serial) < 0 ||
612 vshCommandOptStringReq(ctl, cmd, "wwn", &wwn) < 0 ||
613 vshCommandOptStringReq(ctl, cmd, "address", &straddr) < 0 ||
614 vshCommandOptStringReq(ctl, cmd, "targetbus", &targetbus) < 0 ||
615 vshCommandOptStringReq(ctl, cmd, "alias", &alias) < 0 ||
616 vshCommandOptStringReq(ctl, cmd, "sourcetype", &stype) < 0)
617 goto cleanup;
619 if (!stype) {
620 if (driver && (STREQ(driver, "file") || STREQ(driver, "tap"))) {
621 isFile = true;
622 } else {
623 if (source && !stat(source, &st))
624 isFile = S_ISREG(st.st_mode) ? true : false;
626 } else if (STREQ(stype, "file")) {
627 isFile = true;
628 } else if (STRNEQ(stype, "block")) {
629 vshError(ctl, _("Unknown source type: '%s'"), stype);
630 goto cleanup;
633 if (mode) {
634 if (STRNEQ(mode, "readonly") && STRNEQ(mode, "shareable")) {
635 vshError(ctl, _("No support for %s in command 'attach-disk'"),
636 mode);
637 goto cleanup;
641 if (wwn && !virValidateWWN(wwn))
642 goto cleanup;
644 /* Make XML of disk */
645 virBufferAsprintf(&buf, "<disk type='%s'",
646 isFile ? "file" : "block");
647 if (type)
648 virBufferAsprintf(&buf, " device='%s'", type);
649 if (vshCommandOptBool(cmd, "rawio"))
650 virBufferAddLit(&buf, " rawio='yes'");
651 virBufferAddLit(&buf, ">\n");
652 virBufferAdjustIndent(&buf, 2);
654 if (driver || subdriver || iothread || cache || io) {
655 virBufferAddLit(&buf, "<driver");
657 if (driver)
658 virBufferAsprintf(&buf, " name='%s'", driver);
659 if (subdriver)
660 virBufferAsprintf(&buf, " type='%s'", subdriver);
661 if (iothread)
662 virBufferAsprintf(&buf, " iothread='%s'", iothread);
663 if (cache)
664 virBufferAsprintf(&buf, " cache='%s'", cache);
665 if (io)
666 virBufferAsprintf(&buf, " io='%s'", io);
668 virBufferAddLit(&buf, "/>\n");
671 if (source)
672 virBufferAsprintf(&buf, "<source %s='%s'/>\n",
673 isFile ? "file" : "dev", source);
674 virBufferAsprintf(&buf, "<target dev='%s'", target);
675 if (targetbus)
676 virBufferAsprintf(&buf, " bus='%s'", targetbus);
677 virBufferAddLit(&buf, "/>\n");
679 if (mode)
680 virBufferAsprintf(&buf, "<%s/>\n", mode);
682 if (serial)
683 virBufferAsprintf(&buf, "<serial>%s</serial>\n", serial);
685 if (alias)
686 virBufferAsprintf(&buf, "<alias name='%s'/>\n", alias);
688 if (wwn)
689 virBufferAsprintf(&buf, "<wwn>%s</wwn>\n", wwn);
691 if (straddr) {
692 if (str2DiskAddress(straddr, &diskAddr) != 0) {
693 vshError(ctl, _("Invalid address."));
694 goto cleanup;
697 if (STRPREFIX((const char *)target, "vd")) {
698 if (diskAddr.type == DISK_ADDR_TYPE_PCI) {
699 virBufferAsprintf(&buf,
700 "<address type='pci' domain='0x%04x'"
701 " bus ='0x%02x' slot='0x%02x' function='0x%0x'",
702 diskAddr.addr.pci.domain, diskAddr.addr.pci.bus,
703 diskAddr.addr.pci.slot, diskAddr.addr.pci.function);
704 if (vshCommandOptBool(cmd, "multifunction"))
705 virBufferAddLit(&buf, " multifunction='on'");
706 virBufferAddLit(&buf, "/>\n");
707 } else if (diskAddr.type == DISK_ADDR_TYPE_CCW) {
708 virBufferAsprintf(&buf,
709 "<address type='ccw' cssid='0x%02x'"
710 " ssid='0x%01x' devno='0x%04x' />\n",
711 diskAddr.addr.ccw.cssid, diskAddr.addr.ccw.ssid,
712 diskAddr.addr.ccw.devno);
713 } else {
714 vshError(ctl, "%s",
715 _("expecting a pci:0000.00.00.00 or ccw:00.0.0000 address."));
716 goto cleanup;
718 } else if (STRPREFIX((const char *)target, "sd")) {
719 if (diskAddr.type == DISK_ADDR_TYPE_SCSI) {
720 virBufferAsprintf(&buf,
721 "<address type='drive' controller='%u'"
722 " bus='%u' unit='%llu' />\n",
723 diskAddr.addr.scsi.controller, diskAddr.addr.scsi.bus,
724 diskAddr.addr.scsi.unit);
725 } else if (diskAddr.type == DISK_ADDR_TYPE_USB) {
726 virBufferAsprintf(&buf,
727 "<address type='usb' bus='%u' port='%u' />\n",
728 diskAddr.addr.usb.bus, diskAddr.addr.usb.port);
729 } else if (diskAddr.type == DISK_ADDR_TYPE_SATA) {
730 virBufferAsprintf(&buf,
731 "<address type='drive' controller='%u'"
732 " bus='%u' unit='%llu' />\n",
733 diskAddr.addr.sata.controller, diskAddr.addr.sata.bus,
734 diskAddr.addr.sata.unit);
735 } else {
736 vshError(ctl, "%s",
737 _("expecting a scsi:00.00.00 or usb:00.00 or sata:00.00.00 address."));
738 goto cleanup;
740 } else if (STRPREFIX((const char *)target, "hd")) {
741 if (diskAddr.type == DISK_ADDR_TYPE_IDE) {
742 virBufferAsprintf(&buf,
743 "<address type='drive' controller='%u'"
744 " bus='%u' unit='%u' />\n",
745 diskAddr.addr.ide.controller, diskAddr.addr.ide.bus,
746 diskAddr.addr.ide.unit);
747 } else {
748 vshError(ctl, "%s", _("expecting an ide:00.00.00 address."));
749 goto cleanup;
754 virBufferAdjustIndent(&buf, -2);
755 virBufferAddLit(&buf, "</disk>\n");
757 if (virBufferError(&buf)) {
758 vshError(ctl, "%s", _("Failed to allocate XML buffer"));
759 goto cleanup;
762 xml = virBufferContentAndReset(&buf);
764 if (vshCommandOptBool(cmd, "print-xml")) {
765 vshPrint(ctl, "%s", xml);
766 functionReturn = true;
767 goto cleanup;
770 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
771 goto cleanup;
773 if (persistent &&
774 virDomainIsActive(dom) == 1)
775 flags |= VIR_DOMAIN_AFFECT_LIVE;
777 if (flags || current)
778 ret = virDomainAttachDeviceFlags(dom, xml, flags);
779 else
780 ret = virDomainAttachDevice(dom, xml);
782 if (ret != 0) {
783 vshError(ctl, "%s", _("Failed to attach disk"));
784 } else {
785 vshPrintExtra(ctl, "%s", _("Disk attached successfully\n"));
786 functionReturn = true;
789 cleanup:
790 VIR_FREE(xml);
791 virshDomainFree(dom);
792 virBufferFreeAndReset(&buf);
793 return functionReturn;
797 * "attach-interface" command
799 static const vshCmdInfo info_attach_interface[] = {
800 {.name = "help",
801 .data = N_("attach network interface")
803 {.name = "desc",
804 .data = N_("Attach new network interface.")
806 {.name = NULL}
809 static const vshCmdOptDef opts_attach_interface[] = {
810 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
811 {.name = "type",
812 .type = VSH_OT_DATA,
813 .flags = VSH_OFLAG_REQ,
814 .help = N_("network interface type")
816 {.name = "source",
817 .type = VSH_OT_DATA,
818 .flags = VSH_OFLAG_REQ,
819 .help = N_("source of network interface")
821 {.name = "target",
822 .type = VSH_OT_STRING,
823 .help = N_("target network name")
825 {.name = "mac",
826 .type = VSH_OT_STRING,
827 .help = N_("MAC address")
829 {.name = "script",
830 .type = VSH_OT_STRING,
831 .help = N_("script used to bridge network interface")
833 {.name = "model",
834 .type = VSH_OT_STRING,
835 .help = N_("model type")
837 {.name = "alias",
838 .type = VSH_OT_STRING,
839 .help = N_("custom alias name of interface device")
841 {.name = "inbound",
842 .type = VSH_OT_STRING,
843 .help = N_("control domain's incoming traffics")
845 {.name = "outbound",
846 .type = VSH_OT_STRING,
847 .help = N_("control domain's outgoing traffics")
849 VIRSH_COMMON_OPT_DOMAIN_PERSISTENT,
850 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
851 VIRSH_COMMON_OPT_DOMAIN_LIVE,
852 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
853 {.name = "print-xml",
854 .type = VSH_OT_BOOL,
855 .help = N_("print XML document rather than attach the interface")
857 {.name = "managed",
858 .type = VSH_OT_BOOL,
859 .help = N_("libvirt will automatically detach/attach the device from/to host")
861 {.name = NULL}
864 /* parse inbound and outbound which are in the format of
865 * 'average,peak,burst,floor', in which peak and burst are optional,
866 * thus 'average,,burst' and 'average,peak' are also legal. */
868 #define VIRSH_PARSE_RATE_FIELD(index, name) \
869 do { \
870 if (index < ntok && \
871 *tok[index] != '\0' && \
872 virStrToLong_ullp(tok[index], NULL, 10, &rate->name) < 0) { \
873 vshError(ctl, _("field '%s' is malformed"), #name); \
874 goto cleanup; \
876 } while (0)
878 static int
879 virshParseRateStr(vshControl *ctl,
880 const char *rateStr,
881 virNetDevBandwidthRatePtr rate)
883 char **tok = NULL;
884 size_t ntok;
885 int ret = -1;
887 if (!(tok = virStringSplitCount(rateStr, ",", 0, &ntok)))
888 return -1;
890 if (ntok > 4) {
891 vshError(ctl, _("Rate string '%s' has too many fields"), rateStr);
892 goto cleanup;
895 VIRSH_PARSE_RATE_FIELD(0, average);
896 VIRSH_PARSE_RATE_FIELD(1, peak);
897 VIRSH_PARSE_RATE_FIELD(2, burst);
898 VIRSH_PARSE_RATE_FIELD(3, floor);
900 ret = 0;
901 cleanup:
902 virStringListFree(tok);
903 return ret;
906 #undef VIRSH_PARSE_RATE_FIELD
908 static bool
909 cmdAttachInterface(vshControl *ctl, const vshCmd *cmd)
911 virDomainPtr dom = NULL;
912 const char *mac = NULL, *target = NULL, *script = NULL,
913 *type = NULL, *source = NULL, *model = NULL,
914 *inboundStr = NULL, *outboundStr = NULL, *alias = NULL;
915 virNetDevBandwidthRate inbound, outbound;
916 virDomainNetType typ;
917 int ret;
918 bool functionReturn = false;
919 virBuffer buf = VIR_BUFFER_INITIALIZER;
920 char *xml = NULL;
921 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
922 bool current = vshCommandOptBool(cmd, "current");
923 bool config = vshCommandOptBool(cmd, "config");
924 bool live = vshCommandOptBool(cmd, "live");
925 bool persistent = vshCommandOptBool(cmd, "persistent");
926 bool managed = vshCommandOptBool(cmd, "managed");
928 VSH_EXCLUSIVE_OPTIONS_VAR(persistent, current);
930 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
931 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
933 if (config || persistent)
934 flags |= VIR_DOMAIN_AFFECT_CONFIG;
935 if (live)
936 flags |= VIR_DOMAIN_AFFECT_LIVE;
938 if (vshCommandOptStringReq(ctl, cmd, "type", &type) < 0 ||
939 vshCommandOptStringReq(ctl, cmd, "source", &source) < 0 ||
940 vshCommandOptStringReq(ctl, cmd, "target", &target) < 0 ||
941 vshCommandOptStringReq(ctl, cmd, "mac", &mac) < 0 ||
942 vshCommandOptStringReq(ctl, cmd, "script", &script) < 0 ||
943 vshCommandOptStringReq(ctl, cmd, "model", &model) < 0 ||
944 vshCommandOptStringReq(ctl, cmd, "alias", &alias) < 0 ||
945 vshCommandOptStringReq(ctl, cmd, "inbound", &inboundStr) < 0 ||
946 vshCommandOptStringReq(ctl, cmd, "outbound", &outboundStr) < 0)
947 goto cleanup;
949 /* check interface type */
950 if ((int)(typ = virDomainNetTypeFromString(type)) < 0) {
951 vshError(ctl, _("No support for %s in command 'attach-interface'"),
952 type);
953 goto cleanup;
956 if (inboundStr) {
957 memset(&inbound, 0, sizeof(inbound));
958 if (virshParseRateStr(ctl, inboundStr, &inbound) < 0)
959 goto cleanup;
960 if (!inbound.average && !inbound.floor) {
961 vshError(ctl, _("either inbound average or floor is mandatory"));
962 goto cleanup;
965 if (outboundStr) {
966 memset(&outbound, 0, sizeof(outbound));
967 if (virshParseRateStr(ctl, outboundStr, &outbound) < 0)
968 goto cleanup;
969 if (outbound.average == 0) {
970 vshError(ctl, _("outbound average is mandatory"));
971 goto cleanup;
973 if (outbound.floor) {
974 vshError(ctl, _("outbound floor is unsupported yet"));
975 goto cleanup;
979 /* Make XML of interface */
980 virBufferAsprintf(&buf, "<interface type='%s'", type);
982 if (managed)
983 virBufferAddLit(&buf, " managed='yes'>\n");
984 else
985 virBufferAddLit(&buf, ">\n");
986 virBufferAdjustIndent(&buf, 2);
988 switch (typ) {
989 case VIR_DOMAIN_NET_TYPE_NETWORK:
990 case VIR_DOMAIN_NET_TYPE_BRIDGE:
991 virBufferAsprintf(&buf, "<source %s='%s'/>\n",
992 virDomainNetTypeToString(typ), source);
993 break;
994 case VIR_DOMAIN_NET_TYPE_DIRECT:
995 virBufferAsprintf(&buf, "<source dev='%s'/>\n", source);
996 break;
997 case VIR_DOMAIN_NET_TYPE_HOSTDEV:
999 struct PCIAddress pciAddr = {0, 0, 0, 0};
1001 if (str2PCIAddress(source, &pciAddr) < 0) {
1002 vshError(ctl, _("cannot parse pci address '%s' for network "
1003 "interface"), source);
1004 goto cleanup;
1007 virBufferAddLit(&buf, "<source>\n");
1008 virBufferAdjustIndent(&buf, 2);
1009 virBufferAsprintf(&buf, "<address type='pci' domain='0x%04x'"
1010 " bus='0x%02x' slot='0x%02x' function='0x%d'/>\n",
1011 pciAddr.domain, pciAddr.bus,
1012 pciAddr.slot, pciAddr.function);
1013 virBufferAdjustIndent(&buf, -2);
1014 virBufferAddLit(&buf, "</source>\n");
1015 break;
1018 case VIR_DOMAIN_NET_TYPE_USER:
1019 case VIR_DOMAIN_NET_TYPE_ETHERNET:
1020 case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
1021 case VIR_DOMAIN_NET_TYPE_SERVER:
1022 case VIR_DOMAIN_NET_TYPE_CLIENT:
1023 case VIR_DOMAIN_NET_TYPE_MCAST:
1024 case VIR_DOMAIN_NET_TYPE_UDP:
1025 case VIR_DOMAIN_NET_TYPE_INTERNAL:
1026 case VIR_DOMAIN_NET_TYPE_LAST:
1027 vshError(ctl, _("No support for %s in command 'attach-interface'"),
1028 type);
1029 goto cleanup;
1030 break;
1033 if (target != NULL)
1034 virBufferAsprintf(&buf, "<target dev='%s'/>\n", target);
1035 if (mac != NULL)
1036 virBufferAsprintf(&buf, "<mac address='%s'/>\n", mac);
1037 if (script != NULL)
1038 virBufferAsprintf(&buf, "<script path='%s'/>\n", script);
1039 if (model != NULL)
1040 virBufferAsprintf(&buf, "<model type='%s'/>\n", model);
1042 if (alias != NULL)
1043 virBufferAsprintf(&buf, "<alias name='%s'/>\n", alias);
1045 if (inboundStr || outboundStr) {
1046 virBufferAddLit(&buf, "<bandwidth>\n");
1047 virBufferAdjustIndent(&buf, 2);
1048 if (inboundStr && (inbound.average || inbound.floor)) {
1049 virBufferAddLit(&buf, "<inbound");
1050 if (inbound.average > 0)
1051 virBufferAsprintf(&buf, " average='%llu'", inbound.average);
1052 if (inbound.peak > 0)
1053 virBufferAsprintf(&buf, " peak='%llu'", inbound.peak);
1054 if (inbound.burst > 0)
1055 virBufferAsprintf(&buf, " burst='%llu'", inbound.burst);
1056 if (inbound.floor > 0)
1057 virBufferAsprintf(&buf, " floor='%llu'", inbound.floor);
1058 virBufferAddLit(&buf, "/>\n");
1060 if (outboundStr && outbound.average > 0) {
1061 virBufferAsprintf(&buf, "<outbound average='%llu'", outbound.average);
1062 if (outbound.peak > 0)
1063 virBufferAsprintf(&buf, " peak='%llu'", outbound.peak);
1064 if (outbound.burst > 0)
1065 virBufferAsprintf(&buf, " burst='%llu'", outbound.burst);
1066 virBufferAddLit(&buf, "/>\n");
1068 virBufferAdjustIndent(&buf, -2);
1069 virBufferAddLit(&buf, "</bandwidth>\n");
1072 virBufferAdjustIndent(&buf, -2);
1073 virBufferAddLit(&buf, "</interface>\n");
1075 if (virBufferError(&buf)) {
1076 vshError(ctl, "%s", _("Failed to allocate XML buffer"));
1077 goto cleanup;
1080 xml = virBufferContentAndReset(&buf);
1082 if (vshCommandOptBool(cmd, "print-xml")) {
1083 vshPrint(ctl, "%s", xml);
1084 functionReturn = true;
1085 goto cleanup;
1088 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
1089 goto cleanup;
1091 if (persistent &&
1092 virDomainIsActive(dom) == 1)
1093 flags |= VIR_DOMAIN_AFFECT_LIVE;
1095 if (flags || current)
1096 ret = virDomainAttachDeviceFlags(dom, xml, flags);
1097 else
1098 ret = virDomainAttachDevice(dom, xml);
1100 if (ret != 0) {
1101 vshError(ctl, "%s", _("Failed to attach interface"));
1102 } else {
1103 vshPrintExtra(ctl, "%s", _("Interface attached successfully\n"));
1104 functionReturn = true;
1107 cleanup:
1108 VIR_FREE(xml);
1109 virshDomainFree(dom);
1110 virBufferFreeAndReset(&buf);
1111 return functionReturn;
1115 * "autostart" command
1117 static const vshCmdInfo info_autostart[] = {
1118 {.name = "help",
1119 .data = N_("autostart a domain")
1121 {.name = "desc",
1122 .data = N_("Configure a domain to be automatically started at boot.")
1124 {.name = NULL}
1127 static const vshCmdOptDef opts_autostart[] = {
1128 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_PERSISTENT),
1129 {.name = "disable",
1130 .type = VSH_OT_BOOL,
1131 .help = N_("disable autostarting")
1133 {.name = NULL}
1136 static bool
1137 cmdAutostart(vshControl *ctl, const vshCmd *cmd)
1139 virDomainPtr dom;
1140 const char *name;
1141 int autostart;
1143 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
1144 return false;
1146 autostart = !vshCommandOptBool(cmd, "disable");
1148 if (virDomainSetAutostart(dom, autostart) < 0) {
1149 if (autostart)
1150 vshError(ctl, _("Failed to mark domain %s as autostarted"), name);
1151 else
1152 vshError(ctl, _("Failed to unmark domain %s as autostarted"), name);
1153 virshDomainFree(dom);
1154 return false;
1157 if (autostart)
1158 vshPrintExtra(ctl, _("Domain %s marked as autostarted\n"), name);
1159 else
1160 vshPrintExtra(ctl, _("Domain %s unmarked as autostarted\n"), name);
1162 virshDomainFree(dom);
1163 return true;
1167 * "blkdeviotune" command
1169 static const vshCmdInfo info_blkdeviotune[] = {
1170 {.name = "help",
1171 .data = N_("Set or query a block device I/O tuning parameters.")
1173 {.name = "desc",
1174 .data = N_("Set or query disk I/O parameters such as block throttling.")
1176 {.name = NULL}
1179 static const vshCmdOptDef opts_blkdeviotune[] = {
1180 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
1181 {.name = "device",
1182 .type = VSH_OT_DATA,
1183 .flags = VSH_OFLAG_REQ,
1184 .completer = virshDomainDiskTargetCompleter,
1185 .help = N_("block device")
1187 {.name = "total_bytes_sec",
1188 .type = VSH_OT_ALIAS,
1189 .help = "total-bytes-sec"
1191 {.name = "total-bytes-sec",
1192 .type = VSH_OT_INT,
1193 .help = N_("total throughput limit, as scaled integer (default bytes)")
1195 {.name = "read_bytes_sec",
1196 .type = VSH_OT_ALIAS,
1197 .help = "read-bytes-sec"
1199 {.name = "read-bytes-sec",
1200 .type = VSH_OT_INT,
1201 .help = N_("read throughput limit, as scaled integer (default bytes)")
1203 {.name = "write_bytes_sec",
1204 .type = VSH_OT_ALIAS,
1205 .help = "write-bytes-sec"
1207 {.name = "write-bytes-sec",
1208 .type = VSH_OT_INT,
1209 .help = N_("write throughput limit, as scaled integer (default bytes)")
1211 {.name = "total_iops_sec",
1212 .type = VSH_OT_ALIAS,
1213 .help = "total-iops-sec"
1215 {.name = "total-iops-sec",
1216 .type = VSH_OT_INT,
1217 .help = N_("total I/O operations limit per second")
1219 {.name = "read_iops_sec",
1220 .type = VSH_OT_ALIAS,
1221 .help = "read-iops-sec"
1223 {.name = "read-iops-sec",
1224 .type = VSH_OT_INT,
1225 .help = N_("read I/O operations limit per second")
1227 {.name = "write_iops_sec",
1228 .type = VSH_OT_ALIAS,
1229 .help = "write-iops-sec"
1231 {.name = "write-iops-sec",
1232 .type = VSH_OT_INT,
1233 .help = N_("write I/O operations limit per second")
1235 {.name = "total_bytes_sec_max",
1236 .type = VSH_OT_ALIAS,
1237 .help = "total-bytes-sec-max"
1239 {.name = "total-bytes-sec-max",
1240 .type = VSH_OT_INT,
1241 .help = N_("total max, as scaled integer (default bytes)")
1243 {.name = "read_bytes_sec_max",
1244 .type = VSH_OT_ALIAS,
1245 .help = "read-bytes-sec-max"
1247 {.name = "read-bytes-sec-max",
1248 .type = VSH_OT_INT,
1249 .help = N_("read max, as scaled integer (default bytes)")
1251 {.name = "write_bytes_sec_max",
1252 .type = VSH_OT_ALIAS,
1253 .help = "write-bytes-sec-max"
1255 {.name = "write-bytes-sec-max",
1256 .type = VSH_OT_INT,
1257 .help = N_("write max, as scaled integer (default bytes)")
1259 {.name = "total_iops_sec_max",
1260 .type = VSH_OT_ALIAS,
1261 .help = "total-iops-sec-max"
1263 {.name = "total-iops-sec-max",
1264 .type = VSH_OT_INT,
1265 .help = N_("total I/O operations max")
1267 {.name = "read_iops_sec_max",
1268 .type = VSH_OT_ALIAS,
1269 .help = "read-iops-sec-max"
1271 {.name = "read-iops-sec-max",
1272 .type = VSH_OT_INT,
1273 .help = N_("read I/O operations max")
1275 {.name = "write_iops_sec_max",
1276 .type = VSH_OT_ALIAS,
1277 .help = "write-iops-sec-max"
1279 {.name = "write-iops-sec-max",
1280 .type = VSH_OT_INT,
1281 .help = N_("write I/O operations max")
1283 {.name = "size_iops_sec",
1284 .type = VSH_OT_ALIAS,
1285 .help = "size-iops-sec"
1287 {.name = "size-iops-sec",
1288 .type = VSH_OT_INT,
1289 .help = N_("I/O size in bytes")
1291 {.name = "group_name",
1292 .type = VSH_OT_ALIAS,
1293 .help = "group-name"
1295 {.name = "group-name",
1296 .type = VSH_OT_STRING,
1297 .help = N_("group name to share I/O quota between multiple drives")
1299 {.name = "total_bytes_sec_max_length",
1300 .type = VSH_OT_ALIAS,
1301 .help = "total-bytes-sec-max-length"
1303 {.name = "total-bytes-sec-max-length",
1304 .type = VSH_OT_INT,
1305 .help = N_("duration in seconds to allow total max bytes")
1307 {.name = "read_bytes_sec_max_length",
1308 .type = VSH_OT_ALIAS,
1309 .help = "read-bytes-sec-max-length"
1311 {.name = "read-bytes-sec-max-length",
1312 .type = VSH_OT_INT,
1313 .help = N_("duration in seconds to allow read max bytes")
1315 {.name = "write_bytes_sec_max_length",
1316 .type = VSH_OT_ALIAS,
1317 .help = "write-bytes-sec-max-length"
1319 {.name = "write-bytes-sec-max-length",
1320 .type = VSH_OT_INT,
1321 .help = N_("duration in seconds to allow write max bytes")
1323 {.name = "total_iops_sec_max_length",
1324 .type = VSH_OT_ALIAS,
1325 .help = "total-iops-sec-max-length"
1327 {.name = "total-iops-sec-max-length",
1328 .type = VSH_OT_INT,
1329 .help = N_("duration in seconds to allow total I/O operations max")
1331 {.name = "read_iops_sec_max_length",
1332 .type = VSH_OT_ALIAS,
1333 .help = "read-iops-sec-max-length"
1335 {.name = "read-iops-sec-max-length",
1336 .type = VSH_OT_INT,
1337 .help = N_("duration in seconds to allow read I/O operations max")
1339 {.name = "write_iops_sec_max_length",
1340 .type = VSH_OT_ALIAS,
1341 .help = "write-iops-sec-max-length"
1343 {.name = "write-iops-sec-max-length",
1344 .type = VSH_OT_INT,
1345 .help = N_("duration in seconds to allow write I/O operations max")
1347 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
1348 VIRSH_COMMON_OPT_DOMAIN_LIVE,
1349 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
1350 {.name = NULL}
1353 static bool
1354 cmdBlkdeviotune(vshControl *ctl, const vshCmd *cmd)
1356 virDomainPtr dom = NULL;
1357 const char *name, *disk;
1358 const char *group_name = NULL;
1359 unsigned long long value;
1360 int nparams = 0;
1361 int maxparams = 0;
1362 virTypedParameterPtr params = NULL;
1363 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
1364 size_t i;
1365 int rv = 0;
1366 bool current = vshCommandOptBool(cmd, "current");
1367 bool config = vshCommandOptBool(cmd, "config");
1368 bool live = vshCommandOptBool(cmd, "live");
1369 bool ret = false;
1371 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
1372 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
1374 if (config)
1375 flags |= VIR_DOMAIN_AFFECT_CONFIG;
1376 if (live)
1377 flags |= VIR_DOMAIN_AFFECT_LIVE;
1379 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
1380 goto cleanup;
1382 if (vshCommandOptStringReq(ctl, cmd, "device", &disk) < 0)
1383 goto cleanup;
1385 #define VSH_ADD_IOTUNE_SCALED(PARAM, CONST) \
1386 if ((rv = vshCommandOptScaledInt(ctl, cmd, #PARAM, &value, \
1387 1, ULLONG_MAX)) < 0) { \
1388 goto interror; \
1389 } else if (rv > 0) { \
1390 if (virTypedParamsAddULLong(&params, &nparams, &maxparams, \
1391 VIR_DOMAIN_BLOCK_IOTUNE_##CONST, \
1392 value) < 0) \
1393 goto save_error; \
1396 VSH_ADD_IOTUNE_SCALED(total-bytes-sec, TOTAL_BYTES_SEC);
1397 VSH_ADD_IOTUNE_SCALED(read-bytes-sec, READ_BYTES_SEC);
1398 VSH_ADD_IOTUNE_SCALED(write-bytes-sec, WRITE_BYTES_SEC);
1399 VSH_ADD_IOTUNE_SCALED(total-bytes-sec-max, TOTAL_BYTES_SEC_MAX);
1400 VSH_ADD_IOTUNE_SCALED(read-bytes-sec-max, READ_BYTES_SEC_MAX);
1401 VSH_ADD_IOTUNE_SCALED(write-bytes-sec-max, WRITE_BYTES_SEC_MAX);
1402 #undef VSH_ADD_IOTUNE_SCALED
1404 #define VSH_ADD_IOTUNE(PARAM, CONST) \
1405 if ((rv = vshCommandOptULongLong(ctl, cmd, #PARAM, &value)) < 0) { \
1406 goto interror; \
1407 } else if (rv > 0) { \
1408 if (virTypedParamsAddULLong(&params, &nparams, &maxparams, \
1409 VIR_DOMAIN_BLOCK_IOTUNE_##CONST, \
1410 value) < 0) \
1411 goto save_error; \
1414 VSH_ADD_IOTUNE(total-iops-sec, TOTAL_IOPS_SEC);
1415 VSH_ADD_IOTUNE(read-iops-sec, READ_IOPS_SEC);
1416 VSH_ADD_IOTUNE(write-iops-sec, WRITE_IOPS_SEC);
1417 VSH_ADD_IOTUNE(total-iops-sec-max, TOTAL_IOPS_SEC_MAX);
1418 VSH_ADD_IOTUNE(read-iops-sec-max, READ_IOPS_SEC_MAX);
1419 VSH_ADD_IOTUNE(write-iops-sec-max, WRITE_IOPS_SEC_MAX);
1420 VSH_ADD_IOTUNE(size-iops-sec, SIZE_IOPS_SEC);
1422 VSH_ADD_IOTUNE(total-bytes-sec-max-length, TOTAL_BYTES_SEC_MAX_LENGTH);
1423 VSH_ADD_IOTUNE(read-bytes-sec-max-length, READ_BYTES_SEC_MAX_LENGTH);
1424 VSH_ADD_IOTUNE(write-bytes-sec-max-length, WRITE_BYTES_SEC_MAX_LENGTH);
1425 VSH_ADD_IOTUNE(total-iops-sec-max-length, TOTAL_IOPS_SEC_MAX_LENGTH);
1426 VSH_ADD_IOTUNE(read-iops-sec-max-length, READ_IOPS_SEC_MAX_LENGTH);
1427 VSH_ADD_IOTUNE(write-iops-sec-max-length, WRITE_IOPS_SEC_MAX_LENGTH);
1428 #undef VSH_ADD_IOTUNE
1430 if (vshCommandOptStringReq(ctl, cmd, "group-name", &group_name) < 0) {
1431 vshError(ctl, "%s", _("Unable to parse group-name parameter"));
1432 goto cleanup;
1435 if (group_name) {
1436 if (virTypedParamsAddString(&params, &nparams, &maxparams,
1437 VIR_DOMAIN_BLOCK_IOTUNE_GROUP_NAME,
1438 group_name) < 0)
1439 goto save_error;
1443 if (nparams == 0) {
1444 if (virDomainGetBlockIoTune(dom, NULL, NULL, &nparams, flags) != 0) {
1445 vshError(ctl, "%s",
1446 _("Unable to get number of block I/O throttle parameters"));
1447 goto cleanup;
1450 if (nparams == 0) {
1451 ret = true;
1452 goto cleanup;
1455 params = vshCalloc(ctl, nparams, sizeof(*params));
1457 if (virDomainGetBlockIoTune(dom, disk, params, &nparams, flags) != 0) {
1458 vshError(ctl, "%s",
1459 _("Unable to get block I/O throttle parameters"));
1460 goto cleanup;
1463 for (i = 0; i < nparams; i++) {
1464 char *str = vshGetTypedParamValue(ctl, &params[i]);
1465 vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
1466 VIR_FREE(str);
1468 } else {
1469 if (virDomainSetBlockIoTune(dom, disk, params, nparams, flags) < 0)
1470 goto error;
1473 ret = true;
1475 cleanup:
1476 virTypedParamsFree(params, nparams);
1477 virshDomainFree(dom);
1478 return ret;
1480 save_error:
1481 vshSaveLibvirtError();
1482 error:
1483 vshError(ctl, "%s", _("Unable to change block I/O throttle"));
1484 goto cleanup;
1486 interror:
1487 vshError(ctl, "%s", _("Unable to parse integer parameter"));
1488 goto cleanup;
1492 * "blkiotune" command
1494 static const vshCmdInfo info_blkiotune[] = {
1495 {.name = "help",
1496 .data = N_("Get or set blkio parameters")
1498 {.name = "desc",
1499 .data = N_("Get or set the current blkio parameters for a guest"
1500 " domain.\n"
1501 " To get the blkio parameters use following command: \n\n"
1502 " virsh # blkiotune <domain>")
1504 {.name = NULL}
1507 static const vshCmdOptDef opts_blkiotune[] = {
1508 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
1509 {.name = "weight",
1510 .type = VSH_OT_INT,
1511 .help = N_("IO Weight")
1513 {.name = "device-weights",
1514 .type = VSH_OT_STRING,
1515 .help = N_("per-device IO Weights, in the form of /path/to/device,weight,...")
1517 {.name = "device-read-iops-sec",
1518 .type = VSH_OT_STRING,
1519 .help = N_("per-device read I/O limit per second, in the form of /path/to/device,read_iops_sec,...")
1521 {.name = "device-write-iops-sec",
1522 .type = VSH_OT_STRING,
1523 .help = N_("per-device write I/O limit per second, in the form of /path/to/device,write_iops_sec,...")
1525 {.name = "device-read-bytes-sec",
1526 .type = VSH_OT_STRING,
1527 .help = N_("per-device bytes read per second, in the form of /path/to/device,read_bytes_sec,...")
1529 {.name = "device-write-bytes-sec",
1530 .type = VSH_OT_STRING,
1531 .help = N_("per-device bytes wrote per second, in the form of /path/to/device,write_bytes_sec,...")
1533 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
1534 VIRSH_COMMON_OPT_DOMAIN_LIVE,
1535 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
1536 {.name = NULL}
1539 static bool
1540 cmdBlkiotune(vshControl * ctl, const vshCmd * cmd)
1542 virDomainPtr dom;
1543 const char *device_weight = NULL;
1544 const char *device_riops = NULL;
1545 const char *device_wiops = NULL;
1546 const char *device_rbps = NULL;
1547 const char *device_wbps = NULL;
1548 int weight = 0;
1549 int nparams = 0;
1550 int maxparams = 0;
1551 int rv = 0;
1552 size_t i;
1553 virTypedParameterPtr params = NULL;
1554 bool ret = false;
1555 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
1556 bool current = vshCommandOptBool(cmd, "current");
1557 bool config = vshCommandOptBool(cmd, "config");
1558 bool live = vshCommandOptBool(cmd, "live");
1560 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
1561 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
1563 if (config)
1564 flags |= VIR_DOMAIN_AFFECT_CONFIG;
1565 if (live)
1566 flags |= VIR_DOMAIN_AFFECT_LIVE;
1568 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
1569 return false;
1571 if ((rv = vshCommandOptInt(ctl, cmd, "weight", &weight)) < 0) {
1572 goto cleanup;
1573 } else if (rv > 0) {
1574 if (weight <= 0) {
1575 vshError(ctl, _("Invalid value of %d for I/O weight"), weight);
1576 goto cleanup;
1578 if (virTypedParamsAddUInt(&params, &nparams, &maxparams,
1579 VIR_DOMAIN_BLKIO_WEIGHT, weight) < 0)
1580 goto save_error;
1583 rv = vshCommandOptStringQuiet(ctl, cmd, "device-weights", &device_weight);
1584 if (rv < 0) {
1585 vshError(ctl, "%s", _("Unable to parse string parameter"));
1586 goto cleanup;
1587 } else if (rv > 0) {
1588 if (virTypedParamsAddString(&params, &nparams, &maxparams,
1589 VIR_DOMAIN_BLKIO_DEVICE_WEIGHT,
1590 device_weight) < 0)
1591 goto save_error;
1594 rv = vshCommandOptStringQuiet(ctl, cmd, "device-read-iops-sec", &device_riops);
1595 if (rv < 0) {
1596 vshError(ctl, "%s", _("Unable to parse string parameter"));
1597 goto cleanup;
1598 } else if (rv > 0) {
1599 if (virTypedParamsAddString(&params, &nparams, &maxparams,
1600 VIR_DOMAIN_BLKIO_DEVICE_READ_IOPS,
1601 device_riops) < 0)
1602 goto save_error;
1605 rv = vshCommandOptStringQuiet(ctl, cmd, "device-write-iops-sec", &device_wiops);
1606 if (rv < 0) {
1607 vshError(ctl, "%s", _("Unable to parse string parameter"));
1608 goto cleanup;
1609 } else if (rv > 0) {
1610 if (virTypedParamsAddString(&params, &nparams, &maxparams,
1611 VIR_DOMAIN_BLKIO_DEVICE_WRITE_IOPS,
1612 device_wiops) < 0)
1613 goto save_error;
1616 rv = vshCommandOptStringQuiet(ctl, cmd, "device-read-bytes-sec", &device_rbps);
1617 if (rv < 0) {
1618 vshError(ctl, "%s", _("Unable to parse string parameter"));
1619 goto cleanup;
1620 } else if (rv > 0) {
1621 if (virTypedParamsAddString(&params, &nparams, &maxparams,
1622 VIR_DOMAIN_BLKIO_DEVICE_READ_BPS,
1623 device_rbps) < 0)
1624 goto save_error;
1627 rv = vshCommandOptStringQuiet(ctl, cmd, "device-write-bytes-sec", &device_wbps);
1628 if (rv < 0) {
1629 vshError(ctl, "%s", _("Unable to parse string parameter"));
1630 goto cleanup;
1631 } else if (rv > 0) {
1632 if (virTypedParamsAddString(&params, &nparams, &maxparams,
1633 VIR_DOMAIN_BLKIO_DEVICE_WRITE_BPS,
1634 device_wbps) < 0)
1635 goto save_error;
1638 if (nparams == 0) {
1639 /* get the number of blkio parameters */
1640 if (virDomainGetBlkioParameters(dom, NULL, &nparams, flags) != 0) {
1641 vshError(ctl, "%s",
1642 _("Unable to get number of blkio parameters"));
1643 goto cleanup;
1646 if (nparams == 0) {
1647 /* nothing to output */
1648 ret = true;
1649 goto cleanup;
1652 /* now go get all the blkio parameters */
1653 params = vshCalloc(ctl, nparams, sizeof(*params));
1654 if (virDomainGetBlkioParameters(dom, params, &nparams, flags) != 0) {
1655 vshError(ctl, "%s", _("Unable to get blkio parameters"));
1656 goto cleanup;
1659 for (i = 0; i < nparams; i++) {
1660 char *str = vshGetTypedParamValue(ctl, &params[i]);
1661 vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
1662 VIR_FREE(str);
1664 } else {
1665 /* set the blkio parameters */
1666 if (virDomainSetBlkioParameters(dom, params, nparams, flags) < 0)
1667 goto error;
1670 ret = true;
1672 cleanup:
1673 virTypedParamsFree(params, nparams);
1674 virshDomainFree(dom);
1675 return ret;
1677 save_error:
1678 vshSaveLibvirtError();
1679 error:
1680 vshError(ctl, "%s", _("Unable to change blkio parameters"));
1681 goto cleanup;
1685 static void
1686 virshPrintJobProgress(const char *label, unsigned long long remaining,
1687 unsigned long long total)
1689 int progress;
1691 if (remaining == 0) {
1692 /* migration has completed */
1693 progress = 100;
1694 } else {
1695 /* use float to avoid overflow */
1696 progress = (int)(100.0 - remaining * 100.0 / total);
1697 if (progress >= 100) {
1698 /* migration has not completed, do not print [100 %] */
1699 progress = 99;
1703 /* see comments in vshError about why we must flush */
1704 fflush(stdout);
1705 fprintf(stderr, "\r%s: [%3d %%]", label, progress);
1706 fflush(stderr);
1709 static volatile sig_atomic_t intCaught;
1711 static void virshCatchInt(int sig ATTRIBUTE_UNUSED,
1712 siginfo_t *siginfo ATTRIBUTE_UNUSED,
1713 void *context ATTRIBUTE_UNUSED)
1715 intCaught = 1;
1719 typedef struct _virshBlockJobWaitData virshBlockJobWaitData;
1720 typedef virshBlockJobWaitData *virshBlockJobWaitDataPtr;
1721 struct _virshBlockJobWaitData {
1722 vshControl *ctl;
1723 virDomainPtr dom;
1724 const char *dev;
1725 const char *job_name;
1727 bool verbose;
1728 unsigned int timeout;
1729 bool async_abort;
1731 int cb_id;
1732 int cb_id2;
1733 int status;
1737 static void
1738 virshBlockJobStatusHandler(virConnectPtr conn ATTRIBUTE_UNUSED,
1739 virDomainPtr dom ATTRIBUTE_UNUSED,
1740 const char *disk,
1741 int type ATTRIBUTE_UNUSED,
1742 int status,
1743 void *opaque)
1745 virshBlockJobWaitDataPtr data = opaque;
1747 if (STREQ_NULLABLE(disk, data->dev))
1748 data->status = status;
1753 * virshBlockJobWaitInit:
1754 * @ctl: vsh control structure
1755 * @dom: domain object
1756 * @dev: block device name to wait for
1757 * @job_name: block job name to display in user-facing messages
1758 * @verbose: enable progress reporting
1759 * @timeout: number of milliseconds to wait before aborting the job
1760 * @async_abort: abort the job asynchronously
1762 * Prepares virsh for waiting for completion of a block job. This function
1763 * registers event handlers for block job events and prepares the data structures
1764 * for them. A call to virshBlockJobWait then waits for completion of the given
1765 * block job. This function should be tolerant to different versions of daemon
1766 * and the reporting capabilities of those.
1768 * Returns the data structure that holds data needed for block job waiting or
1769 * NULL in case of error.
1771 static virshBlockJobWaitDataPtr
1772 virshBlockJobWaitInit(vshControl *ctl,
1773 virDomainPtr dom,
1774 const char *dev,
1775 const char *job_name,
1776 bool verbose,
1777 unsigned int timeout,
1778 bool async_abort)
1780 virshBlockJobWaitDataPtr ret;
1781 virshControlPtr priv = ctl->privData;
1783 if (VIR_ALLOC(ret) < 0)
1784 return NULL;
1786 ret->ctl = ctl;
1787 ret->dom = dom;
1788 ret->dev = dev;
1789 ret->job_name = job_name;
1791 ret->async_abort = async_abort;
1792 ret->timeout = timeout;
1793 ret->verbose = verbose;
1795 ret->status = -1;
1797 virConnectDomainEventGenericCallback cb =
1798 VIR_DOMAIN_EVENT_CALLBACK(virshBlockJobStatusHandler);
1800 if ((ret->cb_id = virConnectDomainEventRegisterAny(priv->conn, dom,
1801 VIR_DOMAIN_EVENT_ID_BLOCK_JOB,
1802 cb, ret, NULL)) < 0)
1803 vshResetLibvirtError();
1805 if ((ret->cb_id2 = virConnectDomainEventRegisterAny(priv->conn, dom,
1806 VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2,
1807 cb, ret, NULL)) < 0)
1808 vshResetLibvirtError();
1810 return ret;
1814 static void
1815 virshBlockJobWaitFree(virshBlockJobWaitDataPtr data)
1817 virshControlPtr priv = NULL;
1819 if (!data)
1820 return;
1822 priv = data->ctl->privData;
1823 if (data->cb_id >= 0)
1824 virConnectDomainEventDeregisterAny(priv->conn, data->cb_id);
1825 if (data->cb_id2 >= 0)
1826 virConnectDomainEventDeregisterAny(priv->conn, data->cb_id2);
1828 VIR_FREE(data);
1833 * virshBlockJobWait:
1834 * @data: private data initialized by virshBlockJobWaitInit
1836 * Waits for the block job to complete. This function prefers to wait for a
1837 * matching VIR_DOMAIN_EVENT_ID_BLOCK_JOB or VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2
1838 * event from libvirt; however, it has a fallback mode should either of these
1839 * events not be available.
1841 * This function returns values from the virConnectDomainEventBlockJobStatus
1842 * enum or -1 in case of an internal error.
1844 * If the fallback mode is activated the returned event is
1845 * VIR_DOMAIN_BLOCK_JOB_COMPLETED if the block job vanishes or
1846 * VIR_DOMAIN_BLOCK_JOB_READY if the block job reaches 100%.
1848 static int
1849 virshBlockJobWait(virshBlockJobWaitDataPtr data)
1851 /* For two phase jobs like active commit or block copy, the marker reaches
1852 * 100% and an event fires. In case where virsh would not be able to match
1853 * the event to the given block job we will wait for the number of retries
1854 * before claiming that we entered synchronised phase */
1855 unsigned int retries = 5;
1857 struct sigaction sig_action;
1858 struct sigaction old_sig_action;
1859 sigset_t sigmask, oldsigmask;
1861 unsigned long long start = 0;
1862 unsigned long long curr = 0;
1864 unsigned int abort_flags = 0;
1865 int ret = -1;
1866 virDomainBlockJobInfo info, last;
1867 int result;
1869 if (!data)
1870 return 0;
1872 if (data->async_abort)
1873 abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
1875 sigemptyset(&sigmask);
1876 sigaddset(&sigmask, SIGINT);
1878 intCaught = 0;
1879 sig_action.sa_sigaction = virshCatchInt;
1880 sig_action.sa_flags = SA_SIGINFO;
1881 sigemptyset(&sig_action.sa_mask);
1882 sigaction(SIGINT, &sig_action, &old_sig_action);
1884 if (data->timeout && virTimeMillisNow(&start) < 0) {
1885 vshSaveLibvirtError();
1886 goto cleanup;
1889 last.cur = last.end = 0;
1891 while (true) {
1892 pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask);
1893 result = virDomainGetBlockJobInfo(data->dom, data->dev, &info, 0);
1894 pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
1896 if (result < 0) {
1897 vshError(data->ctl, _("failed to query job for disk %s"), data->dev);
1898 goto cleanup;
1901 /* If either callback could be registered and we've got an event, we can
1902 * can end the waiting loop */
1903 if ((data->cb_id >= 0 || data->cb_id2 >= 0) && data->status != -1) {
1904 ret = data->status;
1905 break;
1908 /* Fallback behaviour is only needed if one or both callbacks could not
1909 * be registered */
1910 if (data->cb_id < 0 || data->cb_id2 < 0) {
1911 /* If the block job vanishes, synthesize a COMPLETED event */
1912 if (result == 0) {
1913 ret = VIR_DOMAIN_BLOCK_JOB_COMPLETED;
1914 break;
1917 /* If the block job hits 100%, wait a little while for a possible
1918 * event from libvirt unless both callbacks could not be registered
1919 * in order to synthesize our own READY event */
1920 if (info.end == info.cur &&
1921 ((data->cb_id < 0 && data->cb_id2 < 0) || --retries == 0)) {
1922 ret = VIR_DOMAIN_BLOCK_JOB_READY;
1923 break;
1927 if (data->verbose && (info.cur != last.cur || info.end != last.end))
1928 virshPrintJobProgress(data->job_name, info.end - info.cur,
1929 info.end);
1930 last = info;
1932 if (data->timeout && virTimeMillisNow(&curr) < 0) {
1933 vshSaveLibvirtError();
1934 goto cleanup;
1937 if (intCaught || (data->timeout && (curr - start > data->timeout))) {
1938 if (virDomainBlockJobAbort(data->dom, data->dev, abort_flags) < 0) {
1939 vshError(data->ctl, _("failed to abort job for disk '%s'"),
1940 data->dev);
1941 goto cleanup;
1944 ret = VIR_DOMAIN_BLOCK_JOB_CANCELED;
1945 break;
1948 usleep(500 * 1000);
1951 /* print 100% completed */
1952 if (data->verbose &&
1953 (ret == VIR_DOMAIN_BLOCK_JOB_COMPLETED ||
1954 ret == VIR_DOMAIN_BLOCK_JOB_READY))
1955 virshPrintJobProgress(data->job_name, 0, 1);
1957 cleanup:
1958 sigaction(SIGINT, &old_sig_action, NULL);
1959 return ret;
1964 * "blockcommit" command
1966 static const vshCmdInfo info_blockcommit[] = {
1967 {.name = "help",
1968 .data = N_("Start a block commit operation.")
1970 {.name = "desc",
1971 .data = N_("Commit changes from a snapshot down to its backing image.")
1973 {.name = NULL}
1976 static const vshCmdOptDef opts_blockcommit[] = {
1977 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
1978 {.name = "path",
1979 .type = VSH_OT_DATA,
1980 .flags = VSH_OFLAG_REQ,
1981 .completer = virshDomainDiskTargetCompleter,
1982 .help = N_("fully-qualified path of disk")
1984 {.name = "bandwidth",
1985 .type = VSH_OT_INT,
1986 .help = N_("bandwidth limit in MiB/s")
1988 {.name = "base",
1989 .type = VSH_OT_STRING,
1990 .help = N_("path of base file to commit into (default bottom of chain)")
1992 {.name = "shallow",
1993 .type = VSH_OT_BOOL,
1994 .help = N_("use backing file of top as base")
1996 {.name = "top",
1997 .type = VSH_OT_STRING,
1998 .help = N_("path of top file to commit from (default top of chain)")
2000 {.name = "active",
2001 .type = VSH_OT_BOOL,
2002 .help = N_("trigger two-stage active commit of top file")
2004 {.name = "delete",
2005 .type = VSH_OT_BOOL,
2006 .help = N_("delete files that were successfully committed")
2008 {.name = "wait",
2009 .type = VSH_OT_BOOL,
2010 .help = N_("wait for job to complete "
2011 "(with --active, wait for job to sync)")
2013 {.name = "verbose",
2014 .type = VSH_OT_BOOL,
2015 .help = N_("with --wait, display the progress")
2017 {.name = "timeout",
2018 .type = VSH_OT_INT,
2019 .help = N_("implies --wait, abort if copy exceeds timeout (in seconds)")
2021 {.name = "pivot",
2022 .type = VSH_OT_BOOL,
2023 .help = N_("implies --active --wait, pivot when commit is synced")
2025 {.name = "keep-overlay",
2026 .type = VSH_OT_BOOL,
2027 .help = N_("implies --active --wait, quit when commit is synced")
2029 {.name = "async",
2030 .type = VSH_OT_BOOL,
2031 .help = N_("with --wait, don't wait for cancel to finish")
2033 {.name = "keep-relative",
2034 .type = VSH_OT_BOOL,
2035 .help = N_("keep the backing chain relatively referenced")
2037 {.name = "bytes",
2038 .type = VSH_OT_BOOL,
2039 .help = N_("the bandwidth limit is in bytes/s rather than MiB/s")
2041 {.name = NULL}
2044 static bool
2045 cmdBlockcommit(vshControl *ctl, const vshCmd *cmd)
2047 virDomainPtr dom = NULL;
2048 bool ret = false;
2049 bool verbose = vshCommandOptBool(cmd, "verbose");
2050 bool pivot = vshCommandOptBool(cmd, "pivot");
2051 bool finish = vshCommandOptBool(cmd, "keep-overlay");
2052 bool active = vshCommandOptBool(cmd, "active") || pivot || finish;
2053 bool blocking = vshCommandOptBool(cmd, "wait") || pivot || finish;
2054 bool async = vshCommandOptBool(cmd, "async");
2055 bool bytes = vshCommandOptBool(cmd, "bytes");
2056 int timeout = 0;
2057 const char *path = NULL;
2058 const char *base = NULL;
2059 const char *top = NULL;
2060 int abort_flags = 0;
2061 unsigned int flags = 0;
2062 unsigned long bandwidth = 0;
2063 virshBlockJobWaitDataPtr bjWait = NULL;
2065 VSH_EXCLUSIVE_OPTIONS("pivot", "keep-overlay");
2067 if (vshCommandOptStringReq(ctl, cmd, "path", &path) < 0)
2068 return false;
2070 if (vshCommandOptStringReq(ctl, cmd, "base", &base) < 0)
2071 return false;
2073 if (vshCommandOptStringReq(ctl, cmd, "top", &top) < 0)
2074 return false;
2076 if (vshBlockJobOptionBandwidth(ctl, cmd, bytes, &bandwidth) < 0)
2077 return false;
2079 if (bytes)
2080 flags |= VIR_DOMAIN_BLOCK_COMMIT_BANDWIDTH_BYTES;
2082 if (vshCommandOptBool(cmd, "shallow"))
2083 flags |= VIR_DOMAIN_BLOCK_COMMIT_SHALLOW;
2085 if (vshCommandOptBool(cmd, "delete"))
2086 flags |= VIR_DOMAIN_BLOCK_COMMIT_DELETE;
2088 if (active)
2089 flags |= VIR_DOMAIN_BLOCK_COMMIT_ACTIVE;
2091 if (vshCommandOptBool(cmd, "keep-relative"))
2092 flags |= VIR_DOMAIN_BLOCK_COMMIT_RELATIVE;
2094 if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
2095 return false;
2097 if (timeout)
2098 blocking = true;
2100 if (!blocking) {
2101 if (verbose) {
2102 vshError(ctl, "%s", _("--verbose requires at least one of --timeout, "
2103 "--wait, --pivot, or --keep-overlay"));
2104 return false;
2107 if (async) {
2108 vshError(ctl, "%s", _("--async requires at least one of --timeout, "
2109 "--wait, --pivot, or --keep-overlay"));
2110 return false;
2114 if (async)
2115 abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
2117 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
2118 return false;
2120 if (blocking &&
2121 !(bjWait = virshBlockJobWaitInit(ctl, dom, path, _("Block commit"),
2122 verbose, timeout, async)))
2123 goto cleanup;
2125 if (virDomainBlockCommit(dom, path, base, top, bandwidth, flags) < 0)
2126 goto cleanup;
2128 if (!blocking) {
2129 if (active)
2130 vshPrintExtra(ctl, "%s", _("Active Block Commit started"));
2131 else
2132 vshPrintExtra(ctl, "%s", _("Block Commit started"));
2134 ret = true;
2135 goto cleanup;
2138 /* Execution continues here only if --wait or friends were specified */
2139 switch (virshBlockJobWait(bjWait)) {
2140 case -1:
2141 goto cleanup;
2143 case VIR_DOMAIN_BLOCK_JOB_CANCELED:
2144 vshPrintExtra(ctl, "\n%s", _("Commit aborted"));
2145 goto cleanup;
2146 break;
2148 case VIR_DOMAIN_BLOCK_JOB_FAILED:
2149 vshError(ctl, "\n%s", _("Commit failed"));
2150 goto cleanup;
2151 break;
2153 case VIR_DOMAIN_BLOCK_JOB_READY:
2154 case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
2155 break;
2158 if (active) {
2159 if (pivot) {
2160 abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT;
2161 if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
2162 vshError(ctl, _("failed to pivot job for disk %s"), path);
2163 goto cleanup;
2166 vshPrintExtra(ctl, "\n%s", _("Successfully pivoted"));
2167 } else if (finish) {
2168 if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
2169 vshError(ctl, _("failed to finish job for disk %s"), path);
2170 goto cleanup;
2173 vshPrintExtra(ctl, "\n%s", _("Commit complete, overlay "
2174 "image kept"));
2175 } else {
2176 vshPrintExtra(ctl, "\n%s", _("Now in synchronized phase"));
2178 } else {
2179 vshPrintExtra(ctl, "\n%s", _("Commit complete"));
2182 ret = true;
2183 cleanup:
2184 virshDomainFree(dom);
2185 virshBlockJobWaitFree(bjWait);
2186 return ret;
2190 * "blockcopy" command
2192 static const vshCmdInfo info_blockcopy[] = {
2193 {.name = "help",
2194 .data = N_("Start a block copy operation.")
2196 {.name = "desc",
2197 .data = N_("Copy a disk backing image chain to dest.")
2199 {.name = NULL}
2202 static const vshCmdOptDef opts_blockcopy[] = {
2203 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
2204 {.name = "path",
2205 .type = VSH_OT_DATA,
2206 .flags = VSH_OFLAG_REQ,
2207 .completer = virshDomainDiskTargetCompleter,
2208 .help = N_("fully-qualified path of source disk")
2210 {.name = "dest",
2211 .type = VSH_OT_STRING,
2212 .help = N_("path of the copy to create")
2214 {.name = "bandwidth",
2215 .type = VSH_OT_INT,
2216 .help = N_("bandwidth limit in MiB/s")
2218 {.name = "shallow",
2219 .type = VSH_OT_BOOL,
2220 .help = N_("make the copy share a backing chain")
2222 {.name = "reuse-external",
2223 .type = VSH_OT_BOOL,
2224 .help = N_("reuse existing destination")
2226 {.name = "raw",
2227 .type = VSH_OT_ALIAS,
2228 .help = "format=raw"
2230 {.name = "blockdev",
2231 .type = VSH_OT_BOOL,
2232 .help = N_("copy destination is block device instead of regular file")
2234 {.name = "wait",
2235 .type = VSH_OT_BOOL,
2236 .help = N_("wait for job to reach mirroring phase")
2238 {.name = "verbose",
2239 .type = VSH_OT_BOOL,
2240 .help = N_("with --wait, display the progress")
2242 {.name = "timeout",
2243 .type = VSH_OT_INT,
2244 .help = N_("implies --wait, abort if copy exceeds timeout (in seconds)")
2246 {.name = "pivot",
2247 .type = VSH_OT_BOOL,
2248 .help = N_("implies --wait, pivot when mirroring starts")
2250 {.name = "finish",
2251 .type = VSH_OT_BOOL,
2252 .help = N_("implies --wait, quit when mirroring starts")
2254 {.name = "async",
2255 .type = VSH_OT_BOOL,
2256 .help = N_("with --wait, don't wait for cancel to finish")
2258 {.name = "xml",
2259 .type = VSH_OT_STRING,
2260 .help = N_("filename containing XML description of the copy destination")
2262 {.name = "format",
2263 .type = VSH_OT_STRING,
2264 .help = N_("format of the destination file")
2266 {.name = "granularity",
2267 .type = VSH_OT_INT,
2268 .help = N_("power-of-two granularity to use during the copy")
2270 {.name = "buf-size",
2271 .type = VSH_OT_INT,
2272 .help = N_("maximum amount of in-flight data during the copy")
2274 {.name = "bytes",
2275 .type = VSH_OT_BOOL,
2276 .help = N_("the bandwidth limit is in bytes/s rather than MiB/s")
2278 {.name = "transient-job",
2279 .type = VSH_OT_BOOL,
2280 .help = N_("the copy job is not persisted if VM is turned off")
2282 {.name = NULL}
2285 static bool
2286 cmdBlockcopy(vshControl *ctl, const vshCmd *cmd)
2288 virDomainPtr dom = NULL;
2289 const char *dest = NULL;
2290 const char *format = NULL;
2291 unsigned long bandwidth = 0;
2292 unsigned int granularity = 0;
2293 unsigned long long buf_size = 0;
2294 unsigned int flags = 0;
2295 bool ret = false;
2296 bool verbose = vshCommandOptBool(cmd, "verbose");
2297 bool pivot = vshCommandOptBool(cmd, "pivot");
2298 bool finish = vshCommandOptBool(cmd, "finish");
2299 bool blockdev = vshCommandOptBool(cmd, "blockdev");
2300 bool blocking = vshCommandOptBool(cmd, "wait") || finish || pivot;
2301 bool async = vshCommandOptBool(cmd, "async");
2302 bool bytes = vshCommandOptBool(cmd, "bytes");
2303 bool transientjob = vshCommandOptBool(cmd, "transient-job");
2304 int timeout = 0;
2305 const char *path = NULL;
2306 int abort_flags = 0;
2307 const char *xml = NULL;
2308 char *xmlstr = NULL;
2309 virTypedParameterPtr params = NULL;
2310 virshBlockJobWaitDataPtr bjWait = NULL;
2311 int nparams = 0;
2313 if (vshCommandOptStringReq(ctl, cmd, "path", &path) < 0)
2314 return false;
2315 if (vshCommandOptStringReq(ctl, cmd, "dest", &dest) < 0)
2316 return false;
2317 if (vshCommandOptStringReq(ctl, cmd, "xml", &xml) < 0)
2318 return false;
2319 if (vshCommandOptStringReq(ctl, cmd, "format", &format) < 0)
2320 return false;
2321 if (vshBlockJobOptionBandwidth(ctl, cmd, bytes, &bandwidth) < 0)
2322 return false;
2323 if (vshCommandOptUInt(ctl, cmd, "granularity", &granularity) < 0)
2324 return false;
2325 if (vshCommandOptULongLong(ctl, cmd, "buf-size", &buf_size) < 0)
2326 return false;
2327 /* Exploit that some VIR_DOMAIN_BLOCK_REBASE_* and
2328 * VIR_DOMAIN_BLOCK_COPY_* flags have the same values. */
2329 if (vshCommandOptBool(cmd, "shallow"))
2330 flags |= VIR_DOMAIN_BLOCK_REBASE_SHALLOW;
2331 if (vshCommandOptBool(cmd, "reuse-external"))
2332 flags |= VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT;
2333 if (transientjob)
2334 flags |= VIR_DOMAIN_BLOCK_COPY_TRANSIENT_JOB;
2335 if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
2336 return false;
2338 if (timeout)
2339 blocking = true;
2341 if (async)
2342 abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
2344 VSH_EXCLUSIVE_OPTIONS_VAR(dest, xml);
2345 VSH_EXCLUSIVE_OPTIONS_VAR(format, xml);
2346 VSH_EXCLUSIVE_OPTIONS_VAR(blockdev, xml);
2347 VSH_EXCLUSIVE_OPTIONS_VAR(pivot, finish);
2349 if (!dest && !xml) {
2350 vshError(ctl, "%s", _("need either --dest or --xml"));
2351 return false;
2354 if (!blocking) {
2355 if (verbose) {
2356 vshError(ctl, "%s", _("--verbose requires at least one of --timeout, "
2357 "--wait, --pivot, or --finish"));
2358 return false;
2361 if (async) {
2362 vshError(ctl, "%s", _("--async requires at least one of --timeout, "
2363 "--wait, --pivot, or --finish"));
2364 return false;
2368 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
2369 goto cleanup;
2371 if (blocking &&
2372 !(bjWait = virshBlockJobWaitInit(ctl, dom, path, _("Block Copy"),
2373 verbose, timeout, async)))
2374 goto cleanup;
2376 if (xml) {
2377 if (virFileReadAll(xml, VSH_MAX_XML_FILE, &xmlstr) < 0) {
2378 vshReportError(ctl);
2379 goto cleanup;
2383 if (granularity || buf_size || (format && STRNEQ(format, "raw")) || xml ||
2384 transientjob) {
2385 /* New API */
2386 if (bandwidth || granularity || buf_size) {
2387 params = vshCalloc(ctl, 3, sizeof(*params));
2388 if (bandwidth) {
2389 if (!bytes) {
2390 /* bandwidth is ulong MiB/s, but the typed parameter is
2391 * ullong bytes/s; make sure we don't overflow */
2392 unsigned long long limit = MIN(ULONG_MAX, ULLONG_MAX >> 20);
2393 if (bandwidth > limit) {
2394 vshError(ctl, _("bandwidth must be less than %llu"), limit);
2395 goto cleanup;
2398 bandwidth <<= 20ULL;
2400 if (virTypedParameterAssign(&params[nparams++],
2401 VIR_DOMAIN_BLOCK_COPY_BANDWIDTH,
2402 VIR_TYPED_PARAM_ULLONG,
2403 bandwidth) < 0)
2404 goto cleanup;
2406 if (granularity &&
2407 virTypedParameterAssign(&params[nparams++],
2408 VIR_DOMAIN_BLOCK_COPY_GRANULARITY,
2409 VIR_TYPED_PARAM_UINT,
2410 granularity) < 0)
2411 goto cleanup;
2412 if (buf_size &&
2413 virTypedParameterAssign(&params[nparams++],
2414 VIR_DOMAIN_BLOCK_COPY_BUF_SIZE,
2415 VIR_TYPED_PARAM_ULLONG,
2416 buf_size) < 0)
2417 goto cleanup;
2420 if (!xmlstr) {
2421 virBuffer buf = VIR_BUFFER_INITIALIZER;
2422 virBufferAsprintf(&buf, "<disk type='%s'>\n",
2423 blockdev ? "block" : "file");
2424 virBufferAdjustIndent(&buf, 2);
2425 virBufferAsprintf(&buf, "<source %s", blockdev ? "dev" : "file");
2426 virBufferEscapeString(&buf, "='%s'/>\n", dest);
2427 virBufferEscapeString(&buf, "<driver type='%s'/>\n", format);
2428 virBufferAdjustIndent(&buf, -2);
2429 virBufferAddLit(&buf, "</disk>\n");
2430 if (virBufferCheckError(&buf) < 0)
2431 goto cleanup;
2432 xmlstr = virBufferContentAndReset(&buf);
2435 if (virDomainBlockCopy(dom, path, xmlstr, params, nparams, flags) < 0)
2436 goto cleanup;
2437 } else {
2438 /* Old API */
2439 flags |= VIR_DOMAIN_BLOCK_REBASE_COPY;
2440 if (blockdev)
2441 flags |= VIR_DOMAIN_BLOCK_REBASE_COPY_DEV;
2442 if (STREQ_NULLABLE(format, "raw"))
2443 flags |= VIR_DOMAIN_BLOCK_REBASE_COPY_RAW;
2444 if (bytes)
2445 flags |= VIR_DOMAIN_BLOCK_REBASE_BANDWIDTH_BYTES;
2447 if (virDomainBlockRebase(dom, path, dest, bandwidth, flags) < 0)
2448 goto cleanup;
2451 if (!blocking) {
2452 vshPrintExtra(ctl, "%s", _("Block Copy started"));
2453 ret = true;
2454 goto cleanup;
2457 /* Execution continues here only if --wait or friends were specified */
2458 switch (virshBlockJobWait(bjWait)) {
2459 case -1:
2460 goto cleanup;
2462 case VIR_DOMAIN_BLOCK_JOB_CANCELED:
2463 vshPrintExtra(ctl, "\n%s", _("Copy aborted"));
2464 goto cleanup;
2465 break;
2467 case VIR_DOMAIN_BLOCK_JOB_FAILED:
2468 vshError(ctl, "\n%s", _("Copy failed"));
2469 goto cleanup;
2470 break;
2472 case VIR_DOMAIN_BLOCK_JOB_READY:
2473 case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
2474 break;
2477 if (pivot) {
2478 abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT;
2479 if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
2480 vshError(ctl, _("failed to pivot job for disk %s"), path);
2481 goto cleanup;
2484 vshPrintExtra(ctl, "\n%s", _("Successfully pivoted"));
2485 } else if (finish) {
2486 if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
2487 vshError(ctl, _("failed to finish job for disk %s"), path);
2488 goto cleanup;
2491 vshPrintExtra(ctl, "\n%s", _("Successfully copied"));
2492 } else {
2493 vshPrintExtra(ctl, "\n%s", _("Now in mirroring phase"));
2496 ret = true;
2498 cleanup:
2499 VIR_FREE(xmlstr);
2500 virTypedParamsFree(params, nparams);
2501 virshDomainFree(dom);
2502 virshBlockJobWaitFree(bjWait);
2503 return ret;
2507 * "blockjob" command
2509 static const vshCmdInfo info_blockjob[] = {
2510 {.name = "help",
2511 .data = N_("Manage active block operations")
2513 {.name = "desc",
2514 .data = N_("Query, adjust speed, or cancel active block operations.")
2516 {.name = NULL}
2519 static const vshCmdOptDef opts_blockjob[] = {
2520 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
2521 {.name = "path",
2522 .type = VSH_OT_DATA,
2523 .flags = VSH_OFLAG_REQ,
2524 .completer = virshDomainDiskTargetCompleter,
2525 .help = N_("fully-qualified path of disk")
2527 {.name = "abort",
2528 .type = VSH_OT_BOOL,
2529 .help = N_("abort the active job on the specified disk")
2531 {.name = "async",
2532 .type = VSH_OT_BOOL,
2533 .help = N_("implies --abort; request but don't wait for job end")
2535 {.name = "pivot",
2536 .type = VSH_OT_BOOL,
2537 .help = N_("implies --abort; conclude and pivot a copy or commit job")
2539 {.name = "info",
2540 .type = VSH_OT_BOOL,
2541 .help = N_("get active job information for the specified disk")
2543 {.name = "bytes",
2544 .type = VSH_OT_BOOL,
2545 .help = N_("get/set bandwidth in bytes rather than MiB/s")
2547 {.name = "raw",
2548 .type = VSH_OT_BOOL,
2549 .help = N_("implies --info; output details rather than human summary")
2551 {.name = "bandwidth",
2552 .type = VSH_OT_INT,
2553 .help = N_("set the bandwidth limit in MiB/s")
2555 {.name = NULL}
2558 VIR_ENUM_DECL(virshDomainBlockJob);
2559 VIR_ENUM_IMPL(virshDomainBlockJob,
2560 VIR_DOMAIN_BLOCK_JOB_TYPE_LAST,
2561 N_("Unknown job"),
2562 N_("Block Pull"),
2563 N_("Block Copy"),
2564 N_("Block Commit"),
2565 N_("Active Block Commit"),
2566 N_("Backup"),
2569 static const char *
2570 virshDomainBlockJobToString(int type)
2572 const char *str = virshDomainBlockJobTypeToString(type);
2573 return str ? _(str) : _("Unknown job");
2577 static bool
2578 virshBlockJobInfo(vshControl *ctl,
2579 virDomainPtr dom,
2580 const char *path,
2581 bool raw,
2582 bool bytes)
2584 virDomainBlockJobInfo info;
2585 virshControlPtr priv = ctl->privData;
2586 unsigned long long speed;
2587 unsigned int flags = 0;
2588 bool ret = false;
2589 int rc = -1;
2591 /* If bytes were requested, or if raw mode is not forcing a MiB/s
2592 * query and cache can't prove failure, then query bytes/sec. */
2593 if (bytes || !(raw || priv->blockJobNoBytes)) {
2594 flags |= VIR_DOMAIN_BLOCK_JOB_INFO_BANDWIDTH_BYTES;
2595 rc = virDomainGetBlockJobInfo(dom, path, &info, flags);
2596 if (rc < 0) {
2597 /* Check for particular errors, let all the rest be fatal. */
2598 switch (last_error->code) {
2599 case VIR_ERR_INVALID_ARG:
2600 priv->blockJobNoBytes = true;
2601 ATTRIBUTE_FALLTHROUGH;
2602 case VIR_ERR_OVERFLOW:
2603 if (!bytes && !raw) {
2604 /* try again with MiB/s, unless forcing bytes */
2605 vshResetLibvirtError();
2606 break;
2608 ATTRIBUTE_FALLTHROUGH;
2609 default:
2610 goto cleanup;
2613 speed = info.bandwidth;
2615 /* If we don't already have a query result, query for MiB/s */
2616 if (rc < 0) {
2617 flags &= ~VIR_DOMAIN_BLOCK_JOB_INFO_BANDWIDTH_BYTES;
2618 if ((rc = virDomainGetBlockJobInfo(dom, path, &info, flags)) < 0)
2619 goto cleanup;
2620 speed = info.bandwidth;
2621 /* Scale to bytes/s unless in raw mode */
2622 if (!raw) {
2623 speed <<= 20;
2624 if (speed >> 20 != info.bandwidth) {
2625 vshError(ctl, _("overflow in converting %ld MiB/s to bytes\n"),
2626 info.bandwidth);
2627 goto cleanup;
2632 if (rc == 0) {
2633 if (!raw)
2634 vshPrintExtra(ctl, _("No current block job for %s"), path);
2635 ret = true;
2636 goto cleanup;
2639 if (raw) {
2640 vshPrint(ctl, _(" type=%s\n bandwidth=%lu\n cur=%llu\n end=%llu\n"),
2641 virshDomainBlockJobTypeToString(info.type),
2642 info.bandwidth, info.cur, info.end);
2643 } else {
2644 virshPrintJobProgress(virshDomainBlockJobToString(info.type),
2645 info.end - info.cur, info.end);
2646 if (speed) {
2647 const char *unit;
2648 double val = vshPrettyCapacity(speed, &unit);
2649 vshPrint(ctl, _(" Bandwidth limit: %llu bytes/s (%-.3lf %s/s)"),
2650 speed, val, unit);
2652 vshPrint(ctl, "\n");
2655 ret = true;
2657 cleanup:
2658 return ret;
2662 static bool
2663 virshBlockJobSetSpeed(vshControl *ctl,
2664 const vshCmd *cmd,
2665 virDomainPtr dom,
2666 const char *path,
2667 bool bytes)
2669 unsigned long bandwidth;
2670 unsigned int flags = 0;
2672 if (bytes)
2673 flags |= VIR_DOMAIN_BLOCK_JOB_SPEED_BANDWIDTH_BYTES;
2675 if (vshBlockJobOptionBandwidth(ctl, cmd, bytes, &bandwidth) < 0)
2676 return false;
2678 if (virDomainBlockJobSetSpeed(dom, path, bandwidth, flags) < 0)
2679 return false;
2681 return true;
2685 static bool
2686 virshBlockJobAbort(virDomainPtr dom,
2687 const char *path,
2688 bool pivot,
2689 bool async)
2691 unsigned int flags = 0;
2693 if (async)
2694 flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
2695 if (pivot)
2696 flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT;
2698 if (virDomainBlockJobAbort(dom, path, flags) < 0)
2699 return false;
2701 return true;
2705 static bool
2706 cmdBlockjob(vshControl *ctl, const vshCmd *cmd)
2708 bool ret = false;
2709 bool raw = vshCommandOptBool(cmd, "raw");
2710 bool bytes = vshCommandOptBool(cmd, "bytes");
2711 bool abortMode = vshCommandOptBool(cmd, "abort");
2712 bool pivot = vshCommandOptBool(cmd, "pivot");
2713 bool async = vshCommandOptBool(cmd, "async");
2714 bool info = vshCommandOptBool(cmd, "info");
2715 bool bandwidth = vshCommandOptBool(cmd, "bandwidth");
2716 virDomainPtr dom = NULL;
2717 const char *path;
2719 VSH_EXCLUSIVE_OPTIONS("raw", "abort");
2720 VSH_EXCLUSIVE_OPTIONS_VAR(raw, pivot);
2721 VSH_EXCLUSIVE_OPTIONS_VAR(raw, async);
2722 VSH_EXCLUSIVE_OPTIONS_VAR(raw, bandwidth);
2724 VSH_EXCLUSIVE_OPTIONS("info", "abort");
2725 VSH_EXCLUSIVE_OPTIONS_VAR(info, pivot);
2726 VSH_EXCLUSIVE_OPTIONS_VAR(info, async);
2727 VSH_EXCLUSIVE_OPTIONS_VAR(info, bandwidth);
2729 VSH_EXCLUSIVE_OPTIONS("bytes", "abort");
2730 VSH_EXCLUSIVE_OPTIONS_VAR(bytes, pivot);
2731 VSH_EXCLUSIVE_OPTIONS_VAR(bytes, async);
2733 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
2734 goto cleanup;
2736 /* XXX Allow path to be optional to list info on all devices at once */
2737 if (vshCommandOptStringReq(ctl, cmd, "path", &path) < 0)
2738 goto cleanup;
2740 if (bandwidth)
2741 ret = virshBlockJobSetSpeed(ctl, cmd, dom, path, bytes);
2742 else if (abortMode || pivot || async)
2743 ret = virshBlockJobAbort(dom, path, pivot, async);
2744 else
2745 ret = virshBlockJobInfo(ctl, dom, path, raw, bytes);
2747 cleanup:
2748 virshDomainFree(dom);
2749 return ret;
2753 * "blockpull" command
2755 static const vshCmdInfo info_blockpull[] = {
2756 {.name = "help",
2757 .data = N_("Populate a disk from its backing image.")
2759 {.name = "desc",
2760 .data = N_("Populate a disk from its backing image.")
2762 {.name = NULL}
2765 static const vshCmdOptDef opts_blockpull[] = {
2766 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
2767 {.name = "path",
2768 .type = VSH_OT_DATA,
2769 .flags = VSH_OFLAG_REQ,
2770 .completer = virshDomainDiskTargetCompleter,
2771 .help = N_("fully-qualified path of disk")
2773 {.name = "bandwidth",
2774 .type = VSH_OT_INT,
2775 .help = N_("bandwidth limit in MiB/s")
2777 {.name = "base",
2778 .type = VSH_OT_STRING,
2779 .help = N_("path of backing file in chain for a partial pull")
2781 {.name = "wait",
2782 .type = VSH_OT_BOOL,
2783 .help = N_("wait for job to finish")
2785 {.name = "verbose",
2786 .type = VSH_OT_BOOL,
2787 .help = N_("with --wait, display the progress")
2789 {.name = "timeout",
2790 .type = VSH_OT_INT,
2791 .help = N_("with --wait, abort if pull exceeds timeout (in seconds)")
2793 {.name = "async",
2794 .type = VSH_OT_BOOL,
2795 .help = N_("with --wait, don't wait for cancel to finish")
2797 {.name = "keep-relative",
2798 .type = VSH_OT_BOOL,
2799 .help = N_("keep the backing chain relatively referenced")
2801 {.name = "bytes",
2802 .type = VSH_OT_BOOL,
2803 .help = N_("the bandwidth limit is in bytes/s rather than MiB/s")
2805 {.name = NULL}
2808 static bool
2809 cmdBlockpull(vshControl *ctl, const vshCmd *cmd)
2811 virDomainPtr dom = NULL;
2812 bool ret = false;
2813 bool blocking = vshCommandOptBool(cmd, "wait");
2814 bool verbose = vshCommandOptBool(cmd, "verbose");
2815 bool async = vshCommandOptBool(cmd, "async");
2816 bool bytes = vshCommandOptBool(cmd, "bytes");
2817 int timeout = 0;
2818 const char *path = NULL;
2819 const char *base = NULL;
2820 unsigned long bandwidth = 0;
2821 unsigned int flags = 0;
2822 virshBlockJobWaitDataPtr bjWait = NULL;
2824 VSH_REQUIRE_OPTION("verbose", "wait");
2825 VSH_REQUIRE_OPTION("async", "wait");
2827 if (vshCommandOptStringReq(ctl, cmd, "path", &path) < 0)
2828 return false;
2830 if (vshCommandOptStringReq(ctl, cmd, "base", &base) < 0)
2831 return false;
2833 if (vshBlockJobOptionBandwidth(ctl, cmd, bytes, &bandwidth) < 0)
2834 return false;
2836 if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
2837 return false;
2839 if (vshCommandOptBool(cmd, "keep-relative"))
2840 flags |= VIR_DOMAIN_BLOCK_REBASE_RELATIVE;
2842 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
2843 return false;
2845 if (blocking &&
2846 !(bjWait = virshBlockJobWaitInit(ctl, dom, path, _("Block Pull"),
2847 verbose, timeout, async)))
2848 goto cleanup;
2850 if (base || flags) {
2851 if (bytes)
2852 flags |= VIR_DOMAIN_BLOCK_REBASE_BANDWIDTH_BYTES;
2854 if (virDomainBlockRebase(dom, path, base, bandwidth, flags) < 0)
2855 goto cleanup;
2856 } else {
2857 if (bytes)
2858 flags |= VIR_DOMAIN_BLOCK_PULL_BANDWIDTH_BYTES;
2860 if (virDomainBlockPull(dom, path, bandwidth, flags) < 0)
2861 goto cleanup;
2864 if (!blocking) {
2865 vshPrintExtra(ctl, "%s", _("Block Pull started"));
2866 ret = true;
2867 goto cleanup;
2870 /* Execution continues here only if --wait or friends were specified */
2871 switch (virshBlockJobWait(bjWait)) {
2872 case -1:
2873 goto cleanup;
2875 case VIR_DOMAIN_BLOCK_JOB_CANCELED:
2876 vshPrintExtra(ctl, "\n%s", _("Pull aborted"));
2877 goto cleanup;
2878 break;
2880 case VIR_DOMAIN_BLOCK_JOB_FAILED:
2881 vshError(ctl, "\n%s", _("Pull failed"));
2882 goto cleanup;
2883 break;
2885 case VIR_DOMAIN_BLOCK_JOB_READY:
2886 case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
2887 vshPrintExtra(ctl, "\n%s", _("Pull complete"));
2888 break;
2891 ret = true;
2893 cleanup:
2894 virshDomainFree(dom);
2895 virshBlockJobWaitFree(bjWait);
2896 return ret;
2900 * "blockresize" command
2902 static const vshCmdInfo info_blockresize[] = {
2903 {.name = "help",
2904 .data = N_("Resize block device of domain.")
2906 {.name = "desc",
2907 .data = N_("Resize block device of domain.")
2909 {.name = NULL}
2912 static const vshCmdOptDef opts_blockresize[] = {
2913 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
2914 {.name = "path",
2915 .type = VSH_OT_DATA,
2916 .flags = VSH_OFLAG_REQ,
2917 .completer = virshDomainDiskTargetCompleter,
2918 .help = N_("Fully-qualified path of block device")
2920 {.name = "size",
2921 .type = VSH_OT_INT,
2922 .flags = VSH_OFLAG_REQ,
2923 .help = N_("New size of the block device, as scaled integer (default KiB)")
2925 {.name = NULL}
2928 static bool
2929 cmdBlockresize(vshControl *ctl, const vshCmd *cmd)
2931 virDomainPtr dom;
2932 const char *path = NULL;
2933 unsigned long long size = 0;
2934 unsigned int flags = 0;
2935 bool ret = false;
2937 if (vshCommandOptStringReq(ctl, cmd, "path", (const char **) &path) < 0)
2938 return false;
2940 if (vshCommandOptScaledInt(ctl, cmd, "size", &size, 1024, ULLONG_MAX) < 0)
2941 return false;
2943 /* Prefer the older interface of KiB. */
2944 if (size % 1024 == 0)
2945 size /= 1024;
2946 else
2947 flags |= VIR_DOMAIN_BLOCK_RESIZE_BYTES;
2949 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
2950 return false;
2952 if (virDomainBlockResize(dom, path, size, flags) < 0) {
2953 vshError(ctl, _("Failed to resize block device '%s'"), path);
2954 } else {
2955 vshPrintExtra(ctl, _("Block device '%s' is resized"), path);
2956 ret = true;
2959 virshDomainFree(dom);
2960 return ret;
2963 #ifndef WIN32
2965 * "console" command
2967 static const vshCmdInfo info_console[] = {
2968 {.name = "help",
2969 .data = N_("connect to the guest console")
2971 {.name = "desc",
2972 .data = N_("Connect the virtual serial console for the guest")
2974 {.name = NULL}
2977 static const vshCmdOptDef opts_console[] = {
2978 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
2979 {.name = "devname", /* sc_prohibit_devname */
2980 .type = VSH_OT_STRING,
2981 .help = N_("character device name")
2983 {.name = "force",
2984 .type = VSH_OT_BOOL,
2985 .help = N_("force console connection (disconnect already connected sessions)")
2987 {.name = "safe",
2988 .type = VSH_OT_BOOL,
2989 .help = N_("only connect if safe console handling is supported")
2991 {.name = NULL}
2994 static bool
2995 cmdRunConsole(vshControl *ctl, virDomainPtr dom,
2996 const char *name,
2997 unsigned int flags)
2999 bool ret = false;
3000 int state;
3001 virshControlPtr priv = ctl->privData;
3003 if ((state = virshDomainState(ctl, dom, NULL)) < 0) {
3004 vshError(ctl, "%s", _("Unable to get domain status"));
3005 goto cleanup;
3008 if (state == VIR_DOMAIN_SHUTOFF) {
3009 vshError(ctl, "%s", _("The domain is not running"));
3010 goto cleanup;
3013 if (!isatty(STDIN_FILENO)) {
3014 vshError(ctl, "%s", _("Cannot run interactive console without a controlling TTY"));
3015 goto cleanup;
3018 vshPrintExtra(ctl, _("Connected to domain %s\n"), virDomainGetName(dom));
3019 vshPrintExtra(ctl, _("Escape character is %s\n"), priv->escapeChar);
3020 fflush(stdout);
3021 if (virshRunConsole(ctl, dom, name, flags) == 0)
3022 ret = true;
3024 cleanup:
3026 return ret;
3029 static bool
3030 cmdConsole(vshControl *ctl, const vshCmd *cmd)
3032 virDomainPtr dom;
3033 bool ret = false;
3034 bool force = vshCommandOptBool(cmd, "force");
3035 bool safe = vshCommandOptBool(cmd, "safe");
3036 unsigned int flags = 0;
3037 const char *name = NULL;
3039 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
3040 return false;
3042 if (vshCommandOptStringReq(ctl, cmd, "devname", &name) < 0) /* sc_prohibit_devname */
3043 goto cleanup;
3045 if (force)
3046 flags |= VIR_DOMAIN_CONSOLE_FORCE;
3047 if (safe)
3048 flags |= VIR_DOMAIN_CONSOLE_SAFE;
3050 ret = cmdRunConsole(ctl, dom, name, flags);
3052 cleanup:
3053 virshDomainFree(dom);
3054 return ret;
3056 #endif /* WIN32 */
3058 /* "domif-setlink" command
3060 static const vshCmdInfo info_domif_setlink[] = {
3061 {.name = "help",
3062 .data = N_("set link state of a virtual interface")
3064 {.name = "desc",
3065 .data = N_("Set link state of a domain's virtual interface. This command "
3066 "wraps usage of update-device command.")
3068 {.name = NULL}
3071 static const vshCmdOptDef opts_domif_setlink[] = {
3072 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
3073 {.name = "interface",
3074 .type = VSH_OT_DATA,
3075 .flags = VSH_OFLAG_REQ,
3076 .completer = virshDomainInterfaceCompleter,
3077 .help = N_("interface device (MAC Address)")
3079 {.name = "state",
3080 .type = VSH_OT_DATA,
3081 .flags = VSH_OFLAG_REQ,
3082 .completer = virshDomainInterfaceStateCompleter,
3083 .help = N_("new state of the device")
3085 {.name = "persistent",
3086 .type = VSH_OT_ALIAS,
3087 .help = "config"
3089 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
3090 {.name = NULL}
3093 static bool
3094 cmdDomIfSetLink(vshControl *ctl, const vshCmd *cmd)
3096 virDomainPtr dom;
3097 const char *iface;
3098 const char *state;
3099 char *value;
3100 virMacAddr macaddr;
3101 const char *element;
3102 const char *attr;
3103 bool config;
3104 bool ret = false;
3105 unsigned int flags = 0;
3106 unsigned int xmlflags = 0;
3107 size_t i;
3108 xmlDocPtr xml = NULL;
3109 xmlXPathContextPtr ctxt = NULL;
3110 xmlXPathObjectPtr obj = NULL;
3111 xmlNodePtr cur = NULL;
3112 char *xml_buf = NULL;
3114 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
3115 return false;
3117 if (vshCommandOptStringReq(ctl, cmd, "interface", &iface) < 0 ||
3118 vshCommandOptStringReq(ctl, cmd, "state", &state) < 0)
3119 goto cleanup;
3121 config = vshCommandOptBool(cmd, "config");
3123 if (STRNEQ(state, "up") && STRNEQ(state, "down")) {
3124 vshError(ctl, _("invalid link state '%s'"), state);
3125 goto cleanup;
3128 if (config) {
3129 flags = VIR_DOMAIN_AFFECT_CONFIG;
3130 xmlflags |= VIR_DOMAIN_XML_INACTIVE;
3131 } else {
3132 flags = VIR_DOMAIN_AFFECT_LIVE;
3135 if (virDomainIsActive(dom) == 0)
3136 flags = VIR_DOMAIN_AFFECT_CONFIG;
3138 if (virshDomainGetXMLFromDom(ctl, dom, xmlflags, &xml, &ctxt) < 0)
3139 goto cleanup;
3141 obj = xmlXPathEval(BAD_CAST "/domain/devices/interface", ctxt);
3142 if (obj == NULL || obj->type != XPATH_NODESET ||
3143 obj->nodesetval == NULL || obj->nodesetval->nodeNr == 0) {
3144 vshError(ctl, _("Failed to extract interface information or no interfaces found"));
3145 goto cleanup;
3148 if (virMacAddrParse(iface, &macaddr) == 0) {
3149 element = "mac";
3150 attr = "address";
3151 } else {
3152 element = "target";
3153 attr = "dev";
3156 /* find interface with matching mac addr */
3157 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
3158 cur = obj->nodesetval->nodeTab[i]->children;
3160 while (cur) {
3161 if (cur->type == XML_ELEMENT_NODE &&
3162 virXMLNodeNameEqual(cur, element)) {
3163 value = virXMLPropString(cur, attr);
3165 if (STRCASEEQ(value, iface)) {
3166 VIR_FREE(value);
3167 goto hit;
3169 VIR_FREE(value);
3171 cur = cur->next;
3175 vshError(ctl, _("interface (%s: %s) not found"), element, iface);
3176 goto cleanup;
3178 hit:
3179 /* find and modify/add link state node */
3180 /* try to find <link> element */
3181 cur = obj->nodesetval->nodeTab[i]->children;
3183 while (cur) {
3184 if (cur->type == XML_ELEMENT_NODE &&
3185 virXMLNodeNameEqual(cur, "link")) {
3186 /* found, just modify the property */
3187 xmlSetProp(cur, BAD_CAST "state", BAD_CAST state);
3189 break;
3191 cur = cur->next;
3194 if (!cur) {
3195 /* element <link> not found, add one */
3196 cur = xmlNewChild(obj->nodesetval->nodeTab[i],
3197 NULL,
3198 BAD_CAST "link",
3199 NULL);
3200 if (!cur)
3201 goto cleanup;
3203 if (xmlNewProp(cur, BAD_CAST "state", BAD_CAST state) == NULL)
3204 goto cleanup;
3207 if (!(xml_buf = virXMLNodeToString(xml, obj->nodesetval->nodeTab[i]))) {
3208 vshSaveLibvirtError();
3209 vshError(ctl, _("Failed to create XML"));
3210 goto cleanup;
3213 if (virDomainUpdateDeviceFlags(dom, xml_buf, flags) < 0) {
3214 vshError(ctl, _("Failed to update interface link state"));
3215 goto cleanup;
3216 } else {
3217 vshPrintExtra(ctl, "%s", _("Device updated successfully\n"));
3218 ret = true;
3221 cleanup:
3222 xmlXPathFreeObject(obj);
3223 xmlXPathFreeContext(ctxt);
3224 xmlFreeDoc(xml);
3225 VIR_FREE(xml_buf);
3226 virshDomainFree(dom);
3228 return ret;
3231 /* "domiftune" command
3233 static const vshCmdInfo info_domiftune[] = {
3234 {.name = "help",
3235 .data = N_("get/set parameters of a virtual interface")
3237 {.name = "desc",
3238 .data = N_("Get/set parameters of a domain's virtual interface.")
3240 {.name = NULL}
3243 static const vshCmdOptDef opts_domiftune[] = {
3244 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
3245 {.name = "interface",
3246 .type = VSH_OT_DATA,
3247 .flags = VSH_OFLAG_REQ,
3248 .completer = virshDomainInterfaceCompleter,
3249 .help = N_("interface device (MAC Address)")
3251 {.name = "inbound",
3252 .type = VSH_OT_STRING,
3253 .help = N_("control domain's incoming traffics")
3255 {.name = "outbound",
3256 .type = VSH_OT_STRING,
3257 .help = N_("control domain's outgoing traffics")
3259 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
3260 VIRSH_COMMON_OPT_DOMAIN_LIVE,
3261 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
3262 {.name = NULL}
3265 static bool
3266 cmdDomIftune(vshControl *ctl, const vshCmd *cmd)
3268 virDomainPtr dom;
3269 const char *name = NULL, *device = NULL,
3270 *inboundStr = NULL, *outboundStr = NULL;
3271 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
3272 int nparams = 0;
3273 int maxparams = 0;
3274 virTypedParameterPtr params = NULL;
3275 bool ret = false;
3276 bool current = vshCommandOptBool(cmd, "current");
3277 bool config = vshCommandOptBool(cmd, "config");
3278 bool live = vshCommandOptBool(cmd, "live");
3279 virNetDevBandwidthRate inbound, outbound;
3280 size_t i;
3282 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
3283 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
3285 if (config)
3286 flags |= VIR_DOMAIN_AFFECT_CONFIG;
3287 if (live)
3288 flags |= VIR_DOMAIN_AFFECT_LIVE;
3290 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
3291 return false;
3293 if (vshCommandOptStringReq(ctl, cmd, "interface", &device) < 0)
3294 goto cleanup;
3296 if (vshCommandOptStringReq(ctl, cmd, "inbound", &inboundStr) < 0 ||
3297 vshCommandOptStringReq(ctl, cmd, "outbound", &outboundStr) < 0)
3298 goto cleanup;
3300 memset(&inbound, 0, sizeof(inbound));
3301 memset(&outbound, 0, sizeof(outbound));
3303 if (inboundStr) {
3304 if (virshParseRateStr(ctl, inboundStr, &inbound) < 0)
3305 goto cleanup;
3306 /* we parse the rate as unsigned long long, but the API
3307 * only accepts UINT */
3308 if (inbound.average > UINT_MAX || inbound.peak > UINT_MAX ||
3309 inbound.burst > UINT_MAX) {
3310 vshError(ctl, _("inbound rate larger than maximum %u"),
3311 UINT_MAX);
3312 goto cleanup;
3315 if ((!inbound.average && (inbound.burst || inbound.peak)) &&
3316 !inbound.floor) {
3317 vshError(ctl, _("either inbound average or floor is mandatory"));
3318 goto cleanup;
3321 if (virTypedParamsAddUInt(&params, &nparams, &maxparams,
3322 VIR_DOMAIN_BANDWIDTH_IN_AVERAGE,
3323 inbound.average) < 0)
3324 goto save_error;
3326 if (inbound.peak &&
3327 virTypedParamsAddUInt(&params, &nparams, &maxparams,
3328 VIR_DOMAIN_BANDWIDTH_IN_PEAK,
3329 inbound.peak) < 0)
3330 goto save_error;
3332 if (inbound.burst &&
3333 virTypedParamsAddUInt(&params, &nparams, &maxparams,
3334 VIR_DOMAIN_BANDWIDTH_IN_BURST,
3335 inbound.burst) < 0)
3336 goto save_error;
3338 if (inbound.floor &&
3339 virTypedParamsAddUInt(&params, &nparams, &maxparams,
3340 VIR_DOMAIN_BANDWIDTH_IN_FLOOR,
3341 inbound.floor) < 0)
3342 goto save_error;
3345 if (outboundStr) {
3346 if (virshParseRateStr(ctl, outboundStr, &outbound) < 0)
3347 goto cleanup;
3348 if (outbound.average > UINT_MAX || outbound.peak > UINT_MAX ||
3349 outbound.burst > UINT_MAX) {
3350 vshError(ctl, _("outbound rate larger than maximum %u"),
3351 UINT_MAX);
3352 goto cleanup;
3354 if (outbound.average == 0 && (outbound.burst || outbound.peak)) {
3355 vshError(ctl, _("outbound average is mandatory"));
3356 goto cleanup;
3359 if (outbound.floor) {
3360 vshError(ctl, _("outbound floor is unsupported yet"));
3361 goto cleanup;
3364 if (virTypedParamsAddUInt(&params, &nparams, &maxparams,
3365 VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE,
3366 outbound.average) < 0)
3367 goto save_error;
3369 if (outbound.peak &&
3370 virTypedParamsAddUInt(&params, &nparams, &maxparams,
3371 VIR_DOMAIN_BANDWIDTH_OUT_PEAK,
3372 outbound.peak) < 0)
3373 goto save_error;
3375 if (outbound.burst &&
3376 virTypedParamsAddUInt(&params, &nparams, &maxparams,
3377 VIR_DOMAIN_BANDWIDTH_OUT_BURST,
3378 outbound.burst) < 0)
3379 goto save_error;
3382 if (nparams == 0) {
3383 /* get the number of interface parameters */
3384 if (virDomainGetInterfaceParameters(dom, device, NULL, &nparams, flags) != 0) {
3385 vshError(ctl, "%s",
3386 _("Unable to get number of interface parameters"));
3387 goto cleanup;
3390 if (nparams == 0) {
3391 /* nothing to output */
3392 ret = true;
3393 goto cleanup;
3396 /* get all interface parameters */
3397 params = vshCalloc(ctl, nparams, sizeof(*params));
3398 if (virDomainGetInterfaceParameters(dom, device, params, &nparams, flags) != 0) {
3399 vshError(ctl, "%s", _("Unable to get interface parameters"));
3400 goto cleanup;
3403 for (i = 0; i < nparams; i++) {
3404 char *str = vshGetTypedParamValue(ctl, &params[i]);
3405 vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
3406 VIR_FREE(str);
3408 } else {
3409 if (virDomainSetInterfaceParameters(dom, device, params,
3410 nparams, flags) != 0)
3411 goto error;
3414 ret = true;
3416 cleanup:
3417 virTypedParamsFree(params, nparams);
3418 virshDomainFree(dom);
3419 return ret;
3421 save_error:
3422 vshSaveLibvirtError();
3423 error:
3424 vshError(ctl, "%s", _("Unable to set interface parameters"));
3425 goto cleanup;
3429 * "suspend" command
3431 static const vshCmdInfo info_suspend[] = {
3432 {.name = "help",
3433 .data = N_("suspend a domain")
3435 {.name = "desc",
3436 .data = N_("Suspend a running domain.")
3438 {.name = NULL}
3441 static const vshCmdOptDef opts_suspend[] = {
3442 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_RUNNING),
3443 {.name = NULL}
3446 static bool
3447 cmdSuspend(vshControl *ctl, const vshCmd *cmd)
3449 virDomainPtr dom;
3450 const char *name;
3451 bool ret = true;
3453 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
3454 return false;
3456 if (virDomainSuspend(dom) == 0) {
3457 vshPrintExtra(ctl, _("Domain %s suspended\n"), name);
3458 } else {
3459 vshError(ctl, _("Failed to suspend domain %s"), name);
3460 ret = false;
3463 virshDomainFree(dom);
3464 return ret;
3468 * "dompmsuspend" command
3470 static const vshCmdInfo info_dom_pm_suspend[] = {
3471 {.name = "help",
3472 .data = N_("suspend a domain gracefully using power management "
3473 "functions")
3475 {.name = "desc",
3476 .data = N_("Suspends a running domain using guest OS's power management. "
3477 "(Note: This requires a guest agent configured and running in "
3478 "the guest OS).")
3480 {.name = NULL}
3483 static const vshCmdOptDef opts_dom_pm_suspend[] = {
3484 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_RUNNING),
3485 {.name = "target",
3486 .type = VSH_OT_DATA,
3487 .flags = VSH_OFLAG_REQ,
3488 .help = N_("mem(Suspend-to-RAM), "
3489 "disk(Suspend-to-Disk), "
3490 "hybrid(Hybrid-Suspend)")
3492 {.name = "duration",
3493 .type = VSH_OT_INT,
3494 .flags = VSH_OFLAG_REQ_OPT,
3495 .help = N_("duration in seconds")
3497 {.name = NULL}
3500 static bool
3501 cmdDomPMSuspend(vshControl *ctl, const vshCmd *cmd)
3503 virDomainPtr dom;
3504 const char *name;
3505 bool ret = false;
3506 const char *target = NULL;
3507 unsigned int suspendTarget;
3508 unsigned long long duration = 0;
3510 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
3511 return false;
3513 if (vshCommandOptULongLong(ctl, cmd, "duration", &duration) < 0)
3514 goto cleanup;
3516 if (vshCommandOptStringReq(ctl, cmd, "target", &target) < 0)
3517 goto cleanup;
3519 if (STREQ(target, "mem")) {
3520 suspendTarget = VIR_NODE_SUSPEND_TARGET_MEM;
3521 } else if (STREQ(target, "disk")) {
3522 suspendTarget = VIR_NODE_SUSPEND_TARGET_DISK;
3523 } else if (STREQ(target, "hybrid")) {
3524 suspendTarget = VIR_NODE_SUSPEND_TARGET_HYBRID;
3525 } else {
3526 vshError(ctl, "%s", _("Invalid target"));
3527 goto cleanup;
3530 if (virDomainPMSuspendForDuration(dom, suspendTarget, duration, 0) < 0) {
3531 vshError(ctl, _("Domain %s could not be suspended"),
3532 virDomainGetName(dom));
3533 goto cleanup;
3536 vshPrintExtra(ctl, _("Domain %s successfully suspended"),
3537 virDomainGetName(dom));
3539 ret = true;
3541 cleanup:
3542 virshDomainFree(dom);
3543 return ret;
3547 * "dompmwakeup" command
3550 static const vshCmdInfo info_dom_pm_wakeup[] = {
3551 {.name = "help",
3552 .data = N_("wakeup a domain from pmsuspended state")
3554 {.name = "desc",
3555 .data = N_("Wakeup a domain that was previously suspended "
3556 "by power management.")
3558 {.name = NULL}
3561 static const vshCmdOptDef opts_dom_pm_wakeup[] = {
3562 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_OTHER),
3563 {.name = NULL}
3566 static bool
3567 cmdDomPMWakeup(vshControl *ctl, const vshCmd *cmd)
3569 virDomainPtr dom;
3570 const char *name;
3571 bool ret = false;
3572 unsigned int flags = 0;
3574 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
3575 return false;
3577 if (virDomainPMWakeup(dom, flags) < 0) {
3578 vshError(ctl, _("Domain %s could not be woken up"),
3579 virDomainGetName(dom));
3580 goto cleanup;
3583 vshPrintExtra(ctl, _("Domain %s successfully woken up"),
3584 virDomainGetName(dom));
3586 ret = true;
3588 cleanup:
3589 virshDomainFree(dom);
3590 return ret;
3594 * "undefine" command
3596 static const vshCmdInfo info_undefine[] = {
3597 {.name = "help",
3598 .data = N_("undefine a domain")
3600 {.name = "desc",
3601 .data = N_("Undefine an inactive domain, or convert persistent to transient.")
3603 {.name = NULL}
3606 static const vshCmdOptDef opts_undefine[] = {
3607 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_PERSISTENT),
3608 {.name = "managed-save",
3609 .type = VSH_OT_BOOL,
3610 .help = N_("remove domain managed state file")
3612 {.name = "storage",
3613 .type = VSH_OT_STRING,
3614 .help = N_("remove associated storage volumes (comma separated list of "
3615 "targets or source paths) (see domblklist)")
3617 {.name = "remove-all-storage",
3618 .type = VSH_OT_BOOL,
3619 .help = N_("remove all associated storage volumes (use with caution)")
3621 {.name = "delete-snapshots",
3622 .type = VSH_OT_ALIAS,
3623 .help = "delete-storage-volume-snapshots"
3625 {.name = "delete-storage-volume-snapshots",
3626 .type = VSH_OT_BOOL,
3627 .help = N_("delete snapshots associated with volume(s), requires "
3628 "--remove-all-storage (must be supported by storage driver)")
3630 {.name = "wipe-storage",
3631 .type = VSH_OT_BOOL,
3632 .help = N_("wipe data on the removed volumes")
3634 {.name = "snapshots-metadata",
3635 .type = VSH_OT_BOOL,
3636 .help = N_("remove all domain snapshot metadata (vm must be inactive)")
3638 {.name = "checkpoints-metadata",
3639 .type = VSH_OT_BOOL,
3640 .help = N_("remove all domain checkpoint metadata (vm must be inactive)")
3642 {.name = "nvram",
3643 .type = VSH_OT_BOOL,
3644 .help = N_("remove nvram file, if inactive")
3646 {.name = "keep-nvram",
3647 .type = VSH_OT_BOOL,
3648 .help = N_("keep nvram file, if inactive")
3650 {.name = NULL}
3653 typedef struct {
3654 virStorageVolPtr vol;
3655 char *source;
3656 char *target;
3657 } virshUndefineVolume;
3659 static bool
3660 cmdUndefine(vshControl *ctl, const vshCmd *cmd)
3662 virDomainPtr dom;
3663 bool ret = false;
3664 const char *name = NULL;
3665 /* Flags to attempt. */
3666 unsigned int flags = 0;
3667 unsigned int vol_flags = 0;
3668 /* User-requested actions. */
3669 bool managed_save = vshCommandOptBool(cmd, "managed-save");
3670 bool snapshots_metadata = vshCommandOptBool(cmd, "snapshots-metadata");
3671 bool checkpoints_metadata = vshCommandOptBool(cmd, "checkpoints-metadata");
3672 bool wipe_storage = vshCommandOptBool(cmd, "wipe-storage");
3673 bool remove_all_storage = vshCommandOptBool(cmd, "remove-all-storage");
3674 bool delete_snapshots = vshCommandOptBool(cmd, "delete-snapshots");
3675 bool nvram = vshCommandOptBool(cmd, "nvram");
3676 bool keep_nvram = vshCommandOptBool(cmd, "keep-nvram");
3677 /* Positive if these items exist. */
3678 int has_managed_save = 0;
3679 int has_snapshots_metadata = 0;
3680 int has_snapshots = 0;
3681 /* True if undefine will not strand data, even on older servers. */
3682 bool managed_save_safe = false;
3683 bool snapshots_safe = false;
3684 int rc = -1;
3685 int running;
3686 /* list of volumes to remove along with this domain */
3687 const char *vol_string = NULL; /* string containing volumes to delete */
3688 char **vol_list = NULL; /* tokenized vol_string */
3689 int nvol_list = 0;
3690 virshUndefineVolume *vols = NULL; /* info about the volumes to delete*/
3691 size_t nvols = 0;
3692 xmlDocPtr doc = NULL;
3693 xmlXPathContextPtr ctxt = NULL;
3694 xmlNodePtr *vol_nodes = NULL; /* XML nodes of volumes of the guest */
3695 int nvol_nodes;
3696 char *source = NULL;
3697 char *target = NULL;
3698 char *pool = NULL;
3699 size_t i;
3700 size_t j;
3701 virshControlPtr priv = ctl->privData;
3703 VSH_REQUIRE_OPTION("delete-snapshots", "remove-all-storage");
3704 VSH_EXCLUSIVE_OPTIONS("nvram", "keep-nvram");
3706 ignore_value(vshCommandOptStringQuiet(ctl, cmd, "storage", &vol_string));
3708 if (!(vol_string || remove_all_storage) && wipe_storage) {
3709 vshError(ctl,
3710 _("'--wipe-storage' requires '--storage <string>' or "
3711 "'--remove-all-storage'"));
3712 return false;
3715 if (delete_snapshots)
3716 vol_flags |= VIR_STORAGE_VOL_DELETE_WITH_SNAPSHOTS;
3718 if (managed_save) {
3719 flags |= VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
3720 managed_save_safe = true;
3722 if (snapshots_metadata) {
3723 flags |= VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA;
3724 snapshots_safe = true;
3726 if (checkpoints_metadata)
3727 flags |= VIR_DOMAIN_UNDEFINE_CHECKPOINTS_METADATA;
3728 if (nvram)
3729 flags |= VIR_DOMAIN_UNDEFINE_NVRAM;
3730 if (keep_nvram)
3731 flags |= VIR_DOMAIN_UNDEFINE_KEEP_NVRAM;
3733 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
3734 return false;
3736 /* Do some flag manipulation. The goal here is to disable bits
3737 * from flags to reduce the likelihood of a server rejecting
3738 * unknown flag bits, as well as to track conditions which are
3739 * safe by default for the given hypervisor and server version. */
3740 if ((running = virDomainIsActive(dom)) < 0)
3741 goto error;
3743 if (!running) {
3744 /* Undefine with snapshots only fails for inactive domains,
3745 * and managed save only exists on inactive domains; if
3746 * running, then we don't want to remove anything. */
3747 has_managed_save = virDomainHasManagedSaveImage(dom, 0);
3748 if (has_managed_save < 0) {
3749 if (last_error->code != VIR_ERR_NO_SUPPORT)
3750 goto error;
3751 vshResetLibvirtError();
3752 has_managed_save = 0;
3755 has_snapshots = virDomainSnapshotNum(dom, 0);
3756 if (has_snapshots < 0) {
3757 if (last_error->code != VIR_ERR_NO_SUPPORT)
3758 goto error;
3759 vshResetLibvirtError();
3760 has_snapshots = 0;
3762 if (has_snapshots) {
3763 has_snapshots_metadata
3764 = virDomainSnapshotNum(dom, VIR_DOMAIN_SNAPSHOT_LIST_METADATA);
3765 if (has_snapshots_metadata < 0) {
3766 /* The server did not know the new flag, assume that all
3767 snapshots have metadata. */
3768 vshResetLibvirtError();
3769 has_snapshots_metadata = has_snapshots;
3770 } else {
3771 /* The server knew the new flag, all aspects of
3772 * undefineFlags are safe. */
3773 managed_save_safe = snapshots_safe = true;
3777 if (!has_managed_save) {
3778 flags &= ~VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
3779 managed_save_safe = true;
3781 if (has_snapshots == 0)
3782 snapshots_safe = true;
3783 if (has_snapshots_metadata == 0) {
3784 flags &= ~VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA;
3785 snapshots_safe = true;
3788 /* Stash domain description for later use */
3789 if (vol_string || remove_all_storage) {
3790 if (running) {
3791 vshError(ctl,
3792 _("Storage volume deletion is supported only on "
3793 "stopped domains"));
3794 goto cleanup;
3797 if (vol_string && remove_all_storage) {
3798 vshError(ctl,
3799 _("Specified both --storage and --remove-all-storage"));
3800 goto cleanup;
3803 if (virshDomainGetXMLFromDom(ctl, dom, 0, &doc, &ctxt) < 0)
3804 goto cleanup;
3806 /* tokenize the string from user and save its parts into an array */
3807 if (vol_string &&
3808 (nvol_list = vshStringToArray(vol_string, &vol_list)) < 0)
3809 goto error;
3811 if ((nvol_nodes = virXPathNodeSet("./devices/disk", ctxt,
3812 &vol_nodes)) < 0)
3813 goto error;
3815 for (i = 0; i < nvol_nodes; i++) {
3816 ctxt->node = vol_nodes[i];
3817 virshUndefineVolume vol;
3818 VIR_FREE(source);
3819 VIR_FREE(target);
3820 VIR_FREE(pool);
3822 /* get volume source and target paths */
3823 if (!(target = virXPathString("string(./target/@dev)", ctxt)))
3824 goto error;
3826 if (!(source = virXPathString("string("
3827 "./source/@file|"
3828 "./source/@dir|"
3829 "./source/@name|"
3830 "./source/@dev|"
3831 "./source/@volume)", ctxt)))
3832 continue;
3834 pool = virXPathString("string(./source/@pool)", ctxt);
3836 /* lookup if volume was selected by user */
3837 if (vol_list) {
3838 bool found = false;
3839 for (j = 0; j < nvol_list; j++) {
3840 if (STREQ_NULLABLE(vol_list[j], target) ||
3841 STREQ_NULLABLE(vol_list[j], source)) {
3842 VIR_FREE(vol_list[j]);
3843 found = true;
3844 break;
3847 if (!found)
3848 continue;
3851 if (pool) {
3852 virStoragePoolPtr storagepool = NULL;
3854 if (!source) {
3855 vshError(ctl,
3856 _("Missing storage volume name for disk '%s'"),
3857 target);
3858 continue;
3861 if (!(storagepool = virStoragePoolLookupByName(priv->conn,
3862 pool))) {
3863 vshError(ctl,
3864 _("Storage pool '%s' for volume '%s' not found."),
3865 pool, target);
3866 vshResetLibvirtError();
3867 continue;
3870 vol.vol = virStorageVolLookupByName(storagepool, source);
3871 virStoragePoolFree(storagepool);
3873 } else {
3874 vol.vol = virStorageVolLookupByPath(priv->conn, source);
3877 if (!vol.vol) {
3878 vshError(ctl,
3879 _("Storage volume '%s'(%s) is not managed by libvirt. "
3880 "Remove it manually.\n"), target, source);
3881 vshResetLibvirtError();
3882 continue;
3885 vol.source = source;
3886 vol.target = target;
3887 source = NULL;
3888 target = NULL;
3889 if (VIR_APPEND_ELEMENT(vols, nvols, vol) < 0)
3890 goto cleanup;
3893 /* print volumes specified by user that were not found in domain definition */
3894 if (vol_list) {
3895 bool found = false;
3896 for (i = 0; i < nvol_list; i++) {
3897 if (vol_list[i]) {
3898 vshError(ctl,
3899 _("Volume '%s' was not found in domain's "
3900 "definition.\n"), vol_list[i]);
3901 found = true;
3905 if (found)
3906 goto cleanup;
3910 /* Generally we want to try the new API first. However, while
3911 * virDomainUndefineFlags was introduced at the same time as
3912 * VIR_DOMAIN_UNDEFINE_MANAGED_SAVE in 0.9.4, the
3913 * VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA flag was not present
3914 * until 0.9.5; skip to piecewise emulation if we couldn't prove
3915 * above that the new API is safe.
3916 * Moreover, only the newer UndefineFlags() API understands
3917 * the VIR_DOMAIN_UNDEFINE_NVRAM flag. So if user has
3918 * specified --nvram we must use the Flags() API. */
3919 if ((managed_save_safe && snapshots_safe) || nvram) {
3920 rc = virDomainUndefineFlags(dom, flags);
3921 if (rc == 0 || nvram ||
3922 (last_error->code != VIR_ERR_NO_SUPPORT &&
3923 last_error->code != VIR_ERR_INVALID_ARG))
3924 goto out;
3925 vshResetLibvirtError();
3928 /* The new API is unsupported or unsafe; fall back to doing things
3929 * piecewise. */
3930 if (has_managed_save) {
3931 if (!managed_save) {
3932 vshError(ctl, "%s",
3933 _("Refusing to undefine while domain managed save "
3934 "image exists"));
3935 goto cleanup;
3937 if (virDomainManagedSaveRemove(dom, 0) < 0) {
3938 vshReportError(ctl);
3939 goto cleanup;
3943 /* No way to emulate deletion of just snapshot metadata
3944 * without support for the newer flags. Oh well. */
3945 if (has_snapshots_metadata) {
3946 vshError(ctl,
3947 snapshots_metadata ?
3948 _("Unable to remove metadata of %d snapshots") :
3949 _("Refusing to undefine while %d snapshots exist"),
3950 has_snapshots_metadata);
3951 goto cleanup;
3954 rc = virDomainUndefine(dom);
3956 out:
3957 if (rc == 0) {
3958 vshPrintExtra(ctl, _("Domain %s has been undefined\n"), name);
3959 ret = true;
3960 } else {
3961 vshError(ctl, _("Failed to undefine domain %s"), name);
3962 goto cleanup;
3965 /* try to undefine storage volumes associated with this domain, if it's requested */
3966 if (nvols) {
3967 for (i = 0; i < nvols; i++) {
3968 if (wipe_storage) {
3969 vshPrintExtra(ctl, _("Wiping volume '%s'(%s) ... "),
3970 vols[i].target, vols[i].source);
3971 fflush(stdout);
3972 if (virStorageVolWipe(vols[i].vol, 0) < 0) {
3973 vshError(ctl, _("Failed! Volume not removed."));
3974 ret = false;
3975 continue;
3976 } else {
3977 vshPrintExtra(ctl, _("Done.\n"));
3981 /* delete the volume */
3982 if (virStorageVolDelete(vols[i].vol, vol_flags) < 0) {
3983 vshError(ctl, _("Failed to remove storage volume '%s'(%s)"),
3984 vols[i].target, vols[i].source);
3985 ret = false;
3986 } else {
3987 vshPrintExtra(ctl, _("Volume '%s'(%s) removed.\n"),
3988 vols[i].target, vols[i].source);
3993 cleanup:
3994 VIR_FREE(source);
3995 VIR_FREE(target);
3996 VIR_FREE(pool);
3997 for (i = 0; i < nvols; i++) {
3998 VIR_FREE(vols[i].source);
3999 VIR_FREE(vols[i].target);
4000 if (vols[i].vol)
4001 virStorageVolFree(vols[i].vol);
4003 VIR_FREE(vols);
4005 for (i = 0; i < nvol_list; i++)
4006 VIR_FREE(vol_list[i]);
4007 VIR_FREE(vol_list);
4009 VIR_FREE(vol_nodes);
4010 xmlFreeDoc(doc);
4011 xmlXPathFreeContext(ctxt);
4012 virshDomainFree(dom);
4013 return ret;
4015 error:
4016 vshReportError(ctl);
4017 goto cleanup;
4021 * "start" command
4023 static const vshCmdInfo info_start[] = {
4024 {.name = "help",
4025 .data = N_("start a (previously defined) inactive domain")
4027 {.name = "desc",
4028 .data = N_("Start a domain, either from the last managedsave\n"
4029 " state, or via a fresh boot if no managedsave state\n"
4030 " is present.")
4032 {.name = NULL}
4035 static const vshCmdOptDef opts_start[] = {
4036 VIRSH_COMMON_OPT_DOMAIN(N_("name of the inactive domain"),
4037 VIR_CONNECT_LIST_DOMAINS_SHUTOFF),
4038 #ifndef WIN32
4039 {.name = "console",
4040 .type = VSH_OT_BOOL,
4041 .help = N_("attach to console after creation")
4043 #endif
4044 {.name = "paused",
4045 .type = VSH_OT_BOOL,
4046 .help = N_("leave the guest paused after creation")
4048 {.name = "autodestroy",
4049 .type = VSH_OT_BOOL,
4050 .help = N_("automatically destroy the guest when virsh disconnects")
4052 {.name = "bypass-cache",
4053 .type = VSH_OT_BOOL,
4054 .help = N_("avoid file system cache when loading")
4056 {.name = "force-boot",
4057 .type = VSH_OT_BOOL,
4058 .help = N_("force fresh boot by discarding any managed save")
4060 {.name = "pass-fds",
4061 .type = VSH_OT_STRING,
4062 .help = N_("pass file descriptors N,M,... to the guest")
4064 {.name = NULL}
4067 static int
4068 cmdStartGetFDs(vshControl *ctl,
4069 const vshCmd *cmd,
4070 size_t *nfdsret,
4071 int **fdsret)
4073 const char *fdopt;
4074 char **fdlist = NULL;
4075 int *fds = NULL;
4076 size_t nfds = 0;
4077 size_t i;
4079 *nfdsret = 0;
4080 *fdsret = NULL;
4082 if (vshCommandOptStringQuiet(ctl, cmd, "pass-fds", &fdopt) <= 0)
4083 return 0;
4085 if (!(fdlist = virStringSplit(fdopt, ",", -1))) {
4086 vshError(ctl, _("Unable to split FD list '%s'"), fdopt);
4087 return -1;
4090 for (i = 0; fdlist[i] != NULL; i++) {
4091 int fd;
4092 if (virStrToLong_i(fdlist[i], NULL, 10, &fd) < 0) {
4093 vshError(ctl, _("Unable to parse FD number '%s'"), fdlist[i]);
4094 goto error;
4096 if (VIR_EXPAND_N(fds, nfds, 1) < 0) {
4097 vshError(ctl, "%s", _("Unable to allocate FD list"));
4098 goto error;
4100 fds[nfds - 1] = fd;
4103 virStringListFree(fdlist);
4105 *fdsret = fds;
4106 *nfdsret = nfds;
4107 return 0;
4109 error:
4110 virStringListFree(fdlist);
4111 VIR_FREE(fds);
4112 return -1;
4115 static bool
4116 cmdStart(vshControl *ctl, const vshCmd *cmd)
4118 virDomainPtr dom;
4119 bool ret = false;
4120 #ifndef WIN32
4121 bool console = vshCommandOptBool(cmd, "console");
4122 #endif
4123 unsigned int flags = VIR_DOMAIN_NONE;
4124 int rc;
4125 size_t nfds = 0;
4126 int *fds = NULL;
4128 if (!(dom = virshCommandOptDomainBy(ctl, cmd, NULL,
4129 VIRSH_BYNAME | VIRSH_BYUUID)))
4130 return false;
4132 if (virDomainGetID(dom) != (unsigned int)-1) {
4133 vshError(ctl, "%s", _("Domain is already active"));
4134 goto cleanup;
4137 if (cmdStartGetFDs(ctl, cmd, &nfds, &fds) < 0)
4138 goto cleanup;
4140 if (vshCommandOptBool(cmd, "paused"))
4141 flags |= VIR_DOMAIN_START_PAUSED;
4142 if (vshCommandOptBool(cmd, "autodestroy"))
4143 flags |= VIR_DOMAIN_START_AUTODESTROY;
4144 if (vshCommandOptBool(cmd, "bypass-cache"))
4145 flags |= VIR_DOMAIN_START_BYPASS_CACHE;
4146 if (vshCommandOptBool(cmd, "force-boot"))
4147 flags |= VIR_DOMAIN_START_FORCE_BOOT;
4149 /* We can emulate force boot, even for older servers that reject it. */
4150 if (flags & VIR_DOMAIN_START_FORCE_BOOT) {
4151 if ((nfds ?
4152 virDomainCreateWithFiles(dom, nfds, fds, flags) :
4153 virDomainCreateWithFlags(dom, flags)) == 0)
4154 goto started;
4155 if (last_error->code != VIR_ERR_NO_SUPPORT &&
4156 last_error->code != VIR_ERR_INVALID_ARG) {
4157 vshReportError(ctl);
4158 goto cleanup;
4160 vshResetLibvirtError();
4161 rc = virDomainHasManagedSaveImage(dom, 0);
4162 if (rc < 0) {
4163 /* No managed save image to remove */
4164 vshResetLibvirtError();
4165 } else if (rc > 0) {
4166 if (virDomainManagedSaveRemove(dom, 0) < 0) {
4167 vshReportError(ctl);
4168 goto cleanup;
4171 flags &= ~VIR_DOMAIN_START_FORCE_BOOT;
4174 /* Prefer older API unless we have to pass a flag. */
4175 if ((nfds ? virDomainCreateWithFiles(dom, nfds, fds, flags) :
4176 (flags ? virDomainCreateWithFlags(dom, flags)
4177 : virDomainCreate(dom))) < 0) {
4178 vshError(ctl, _("Failed to start domain %s"), virDomainGetName(dom));
4179 goto cleanup;
4182 started:
4183 vshPrintExtra(ctl, _("Domain %s started\n"),
4184 virDomainGetName(dom));
4185 #ifndef WIN32
4186 if (console && !cmdRunConsole(ctl, dom, NULL, 0))
4187 goto cleanup;
4188 #endif
4190 ret = true;
4192 cleanup:
4193 virshDomainFree(dom);
4194 VIR_FREE(fds);
4195 return ret;
4199 * "save" command
4201 static const vshCmdInfo info_save[] = {
4202 {.name = "help",
4203 .data = N_("save a domain state to a file")
4205 {.name = "desc",
4206 .data = N_("Save the RAM state of a running domain.")
4208 {.name = NULL}
4211 static const vshCmdOptDef opts_save[] = {
4212 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
4213 VIRSH_COMMON_OPT_FILE(N_("where to save the data")),
4214 {.name = "bypass-cache",
4215 .type = VSH_OT_BOOL,
4216 .help = N_("avoid file system cache when saving")
4218 {.name = "xml",
4219 .type = VSH_OT_STRING,
4220 .help = N_("filename containing updated XML for the target")
4222 {.name = "running",
4223 .type = VSH_OT_BOOL,
4224 .help = N_("set domain to be running on restore")
4226 {.name = "paused",
4227 .type = VSH_OT_BOOL,
4228 .help = N_("set domain to be paused on restore")
4230 {.name = "verbose",
4231 .type = VSH_OT_BOOL,
4232 .help = N_("display the progress of save")
4234 {.name = NULL}
4237 static void
4238 doSave(void *opaque)
4240 virshCtrlData *data = opaque;
4241 vshControl *ctl = data->ctl;
4242 const vshCmd *cmd = data->cmd;
4243 char ret = '1';
4244 virDomainPtr dom = NULL;
4245 const char *name = NULL;
4246 const char *to = NULL;
4247 unsigned int flags = 0;
4248 const char *xmlfile = NULL;
4249 char *xml = NULL;
4250 sigset_t sigmask, oldsigmask;
4252 sigemptyset(&sigmask);
4253 sigaddset(&sigmask, SIGINT);
4254 if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
4255 goto out_sig;
4257 if (vshCommandOptStringReq(ctl, cmd, "file", &to) < 0)
4258 goto out;
4260 if (vshCommandOptBool(cmd, "bypass-cache"))
4261 flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
4262 if (vshCommandOptBool(cmd, "running"))
4263 flags |= VIR_DOMAIN_SAVE_RUNNING;
4264 if (vshCommandOptBool(cmd, "paused"))
4265 flags |= VIR_DOMAIN_SAVE_PAUSED;
4267 if (vshCommandOptStringReq(ctl, cmd, "xml", &xmlfile) < 0)
4268 goto out;
4270 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
4271 goto out;
4273 if (xmlfile &&
4274 virFileReadAll(xmlfile, VSH_MAX_XML_FILE, &xml) < 0) {
4275 vshReportError(ctl);
4276 goto out;
4279 if (((flags || xml)
4280 ? virDomainSaveFlags(dom, to, xml, flags)
4281 : virDomainSave(dom, to)) < 0) {
4282 vshError(ctl, _("Failed to save domain %s to %s"), name, to);
4283 goto out;
4286 ret = '0';
4288 out:
4289 pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
4290 out_sig:
4291 virshDomainFree(dom);
4292 VIR_FREE(xml);
4293 ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
4296 typedef void (*jobWatchTimeoutFunc)(vshControl *ctl, virDomainPtr dom,
4297 void *opaque);
4299 static bool
4300 virshWatchJob(vshControl *ctl,
4301 virDomainPtr dom,
4302 bool verbose,
4303 int pipe_fd,
4304 int timeout_ms,
4305 jobWatchTimeoutFunc timeout_func,
4306 void *opaque,
4307 const char *label)
4309 struct sigaction sig_action;
4310 struct sigaction old_sig_action;
4311 struct pollfd pollfd[2] = {{.fd = pipe_fd, .events = POLLIN, .revents = 0},
4312 {.fd = STDIN_FILENO, .events = POLLIN, .revents = 0}};
4313 struct timeval start, curr;
4314 virDomainJobInfo jobinfo;
4315 int ret = -1;
4316 char retchar;
4317 bool functionReturn = false;
4318 sigset_t sigmask, oldsigmask;
4319 bool jobStarted = false;
4320 nfds_t npollfd = 2;
4322 sigemptyset(&sigmask);
4323 sigaddset(&sigmask, SIGINT);
4325 intCaught = 0;
4326 sig_action.sa_sigaction = virshCatchInt;
4327 sig_action.sa_flags = SA_SIGINFO;
4328 sigemptyset(&sig_action.sa_mask);
4329 sigaction(SIGINT, &sig_action, &old_sig_action);
4331 /* don't poll on STDIN if we are not using a terminal */
4332 if (!vshTTYAvailable(ctl))
4333 npollfd = 1;
4335 GETTIMEOFDAY(&start);
4336 while (1) {
4337 ret = poll((struct pollfd *)&pollfd, npollfd, 500);
4338 if (ret > 0) {
4339 if (pollfd[1].revents & POLLIN &&
4340 saferead(STDIN_FILENO, &retchar, sizeof(retchar)) > 0) {
4341 if (vshTTYIsInterruptCharacter(ctl, retchar))
4342 virDomainAbortJob(dom);
4343 continue;
4346 if (pollfd[0].revents & POLLIN &&
4347 saferead(pipe_fd, &retchar, sizeof(retchar)) > 0 &&
4348 retchar == '0') {
4349 if (verbose) {
4350 /* print [100 %] */
4351 virshPrintJobProgress(label, 0, 1);
4353 break;
4355 goto cleanup;
4358 if (ret < 0) {
4359 if (errno == EINTR) {
4360 if (intCaught) {
4361 virDomainAbortJob(dom);
4362 intCaught = 0;
4364 continue;
4366 goto cleanup;
4369 GETTIMEOFDAY(&curr);
4370 if (timeout_ms && (((int)(curr.tv_sec - start.tv_sec) * 1000 +
4371 (int)(curr.tv_usec - start.tv_usec) / 1000) >
4372 timeout_ms)) {
4373 /* suspend the domain when migration timeouts. */
4374 vshDebug(ctl, VSH_ERR_DEBUG, "%s timeout", label);
4375 if (timeout_func)
4376 (timeout_func)(ctl, dom, opaque);
4377 timeout_ms = 0;
4380 if (verbose || !jobStarted) {
4381 pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask);
4382 ret = virDomainGetJobInfo(dom, &jobinfo);
4383 pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
4384 if (ret == 0) {
4385 if (verbose && jobinfo.dataTotal > 0)
4386 virshPrintJobProgress(label, jobinfo.dataRemaining,
4387 jobinfo.dataTotal);
4389 if (!jobStarted &&
4390 (jobinfo.type == VIR_DOMAIN_JOB_BOUNDED ||
4391 jobinfo.type == VIR_DOMAIN_JOB_UNBOUNDED)) {
4392 vshTTYDisableInterrupt(ctl);
4393 jobStarted = true;
4395 } else {
4396 vshResetLibvirtError();
4401 functionReturn = true;
4403 cleanup:
4404 sigaction(SIGINT, &old_sig_action, NULL);
4405 vshTTYRestore(ctl);
4406 return functionReturn;
4409 static bool
4410 cmdSave(vshControl *ctl, const vshCmd *cmd)
4412 bool ret = false;
4413 virDomainPtr dom = NULL;
4414 int p[2] = {-1. -1};
4415 virThread workerThread;
4416 bool verbose = false;
4417 virshCtrlData data;
4418 const char *to = NULL;
4419 const char *name = NULL;
4421 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
4422 return false;
4424 if (vshCommandOptStringReq(ctl, cmd, "file", &to) < 0)
4425 goto cleanup;
4427 if (vshCommandOptBool(cmd, "verbose"))
4428 verbose = true;
4430 if (pipe(p) < 0)
4431 goto cleanup;
4433 data.ctl = ctl;
4434 data.cmd = cmd;
4435 data.writefd = p[1];
4437 if (virThreadCreate(&workerThread,
4438 true,
4439 doSave,
4440 &data) < 0)
4441 goto cleanup;
4443 ret = virshWatchJob(ctl, dom, verbose, p[0], 0, NULL, NULL, _("Save"));
4445 virThreadJoin(&workerThread);
4447 if (ret)
4448 vshPrintExtra(ctl, _("\nDomain %s saved to %s\n"), name, to);
4450 cleanup:
4451 virshDomainFree(dom);
4452 return ret;
4456 * "save-image-dumpxml" command
4458 static const vshCmdInfo info_save_image_dumpxml[] = {
4459 {.name = "help",
4460 .data = N_("saved state domain information in XML")
4462 {.name = "desc",
4463 .data = N_("Dump XML of domain information for a saved state file to stdout.")
4465 {.name = NULL}
4468 static const vshCmdOptDef opts_save_image_dumpxml[] = {
4469 VIRSH_COMMON_OPT_FILE(N_("saved state file to read")),
4470 {.name = "security-info",
4471 .type = VSH_OT_BOOL,
4472 .help = N_("include security sensitive information in XML dump")
4474 {.name = NULL}
4477 static bool
4478 cmdSaveImageDumpxml(vshControl *ctl, const vshCmd *cmd)
4480 const char *file = NULL;
4481 bool ret = false;
4482 unsigned int flags = 0;
4483 char *xml = NULL;
4484 virshControlPtr priv = ctl->privData;
4486 if (vshCommandOptBool(cmd, "security-info"))
4487 flags |= VIR_DOMAIN_XML_SECURE;
4489 if (vshCommandOptStringReq(ctl, cmd, "file", &file) < 0)
4490 return false;
4492 xml = virDomainSaveImageGetXMLDesc(priv->conn, file, flags);
4493 if (!xml)
4494 goto cleanup;
4496 vshPrint(ctl, "%s", xml);
4497 ret = true;
4499 cleanup:
4500 VIR_FREE(xml);
4501 return ret;
4505 * "save-image-define" command
4507 static const vshCmdInfo info_save_image_define[] = {
4508 {.name = "help",
4509 .data = N_("redefine the XML for a domain's saved state file")
4511 {.name = "desc",
4512 .data = N_("Replace the domain XML associated with a saved state file")
4514 {.name = NULL}
4517 static const vshCmdOptDef opts_save_image_define[] = {
4518 VIRSH_COMMON_OPT_FILE(N_("saved state file to modify")),
4519 {.name = "xml",
4520 .type = VSH_OT_DATA,
4521 .flags = VSH_OFLAG_REQ,
4522 .help = N_("filename containing updated XML for the target")
4524 {.name = "running",
4525 .type = VSH_OT_BOOL,
4526 .help = N_("set domain to be running on restore")
4528 {.name = "paused",
4529 .type = VSH_OT_BOOL,
4530 .help = N_("set domain to be paused on restore")
4532 {.name = NULL}
4535 static bool
4536 cmdSaveImageDefine(vshControl *ctl, const vshCmd *cmd)
4538 const char *file = NULL;
4539 bool ret = false;
4540 const char *xmlfile = NULL;
4541 char *xml = NULL;
4542 unsigned int flags = 0;
4543 virshControlPtr priv = ctl->privData;
4545 if (vshCommandOptBool(cmd, "running"))
4546 flags |= VIR_DOMAIN_SAVE_RUNNING;
4547 if (vshCommandOptBool(cmd, "paused"))
4548 flags |= VIR_DOMAIN_SAVE_PAUSED;
4550 if (vshCommandOptStringReq(ctl, cmd, "file", &file) < 0)
4551 return false;
4553 if (vshCommandOptStringReq(ctl, cmd, "xml", &xmlfile) < 0)
4554 return false;
4556 if (virFileReadAll(xmlfile, VSH_MAX_XML_FILE, &xml) < 0)
4557 goto cleanup;
4559 if (virDomainSaveImageDefineXML(priv->conn, file, xml, flags) < 0) {
4560 vshError(ctl, _("Failed to update %s"), file);
4561 goto cleanup;
4564 vshPrintExtra(ctl, _("State file %s updated.\n"), file);
4565 ret = true;
4567 cleanup:
4568 VIR_FREE(xml);
4569 return ret;
4573 * "save-image-edit" command
4575 static const vshCmdInfo info_save_image_edit[] = {
4576 {.name = "help",
4577 .data = N_("edit XML for a domain's saved state file")
4579 {.name = "desc",
4580 .data = N_("Edit the domain XML associated with a saved state file")
4582 {.name = NULL}
4585 static const vshCmdOptDef opts_save_image_edit[] = {
4586 VIRSH_COMMON_OPT_FILE(N_("saved state file to edit")),
4587 {.name = "running",
4588 .type = VSH_OT_BOOL,
4589 .help = N_("set domain to be running on restore")
4591 {.name = "paused",
4592 .type = VSH_OT_BOOL,
4593 .help = N_("set domain to be paused on restore")
4595 {.name = NULL}
4598 static bool
4599 cmdSaveImageEdit(vshControl *ctl, const vshCmd *cmd)
4601 const char *file = NULL;
4602 bool ret = false;
4603 unsigned int getxml_flags = VIR_DOMAIN_XML_SECURE;
4604 unsigned int define_flags = 0;
4605 virshControlPtr priv = ctl->privData;
4607 if (vshCommandOptBool(cmd, "running"))
4608 define_flags |= VIR_DOMAIN_SAVE_RUNNING;
4609 if (vshCommandOptBool(cmd, "paused"))
4610 define_flags |= VIR_DOMAIN_SAVE_PAUSED;
4612 /* Normally, we let the API reject mutually exclusive flags.
4613 * However, in the edit cycle, we let the user retry if the define
4614 * step fails, but the define step will always fail on invalid
4615 * flags, so we reject it up front to avoid looping. */
4616 VSH_EXCLUSIVE_OPTIONS("running", "paused");
4618 if (vshCommandOptStringReq(ctl, cmd, "file", &file) < 0)
4619 return false;
4621 #define EDIT_GET_XML \
4622 virDomainSaveImageGetXMLDesc(priv->conn, file, getxml_flags)
4623 #define EDIT_NOT_CHANGED \
4624 do { \
4625 vshPrintExtra(ctl, _("Saved image %s XML configuration " \
4626 "not changed.\n"), file); \
4627 ret = true; \
4628 goto edit_cleanup; \
4629 } while (0)
4630 #define EDIT_DEFINE \
4631 (virDomainSaveImageDefineXML(priv->conn, file, doc_edited, define_flags) == 0)
4632 #include "virsh-edit.c"
4634 vshPrintExtra(ctl, _("State file %s edited.\n"), file);
4635 ret = true;
4637 cleanup:
4638 return ret;
4642 * "managedsave" command
4644 static const vshCmdInfo info_managedsave[] = {
4645 {.name = "help",
4646 .data = N_("managed save of a domain state")
4648 {.name = "desc",
4649 .data = N_("Save and destroy a running domain, so it can be restarted from\n"
4650 " the same state at a later time. When the virsh 'start'\n"
4651 " command is next run for the domain, it will automatically\n"
4652 " be started from this saved state.")
4654 {.name = NULL}
4657 static const vshCmdOptDef opts_managedsave[] = {
4658 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
4659 {.name = "bypass-cache",
4660 .type = VSH_OT_BOOL,
4661 .help = N_("avoid file system cache when saving")
4663 {.name = "running",
4664 .type = VSH_OT_BOOL,
4665 .help = N_("set domain to be running on next start")
4667 {.name = "paused",
4668 .type = VSH_OT_BOOL,
4669 .help = N_("set domain to be paused on next start")
4671 {.name = "verbose",
4672 .type = VSH_OT_BOOL,
4673 .help = N_("display the progress of save")
4675 {.name = NULL}
4678 static void
4679 doManagedsave(void *opaque)
4681 char ret = '1';
4682 virshCtrlData *data = opaque;
4683 vshControl *ctl = data->ctl;
4684 const vshCmd *cmd = data->cmd;
4685 virDomainPtr dom = NULL;
4686 const char *name;
4687 unsigned int flags = 0;
4688 sigset_t sigmask, oldsigmask;
4690 sigemptyset(&sigmask);
4691 sigaddset(&sigmask, SIGINT);
4692 if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
4693 goto out_sig;
4695 if (vshCommandOptBool(cmd, "bypass-cache"))
4696 flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
4697 if (vshCommandOptBool(cmd, "running"))
4698 flags |= VIR_DOMAIN_SAVE_RUNNING;
4699 if (vshCommandOptBool(cmd, "paused"))
4700 flags |= VIR_DOMAIN_SAVE_PAUSED;
4702 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
4703 goto out;
4705 if (virDomainManagedSave(dom, flags) < 0) {
4706 vshError(ctl, _("Failed to save domain %s state"), name);
4707 goto out;
4710 ret = '0';
4711 out:
4712 pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
4713 out_sig:
4714 virshDomainFree(dom);
4715 ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
4718 static bool
4719 cmdManagedSave(vshControl *ctl, const vshCmd *cmd)
4721 virDomainPtr dom;
4722 int p[2] = { -1, -1};
4723 bool ret = false;
4724 bool verbose = false;
4725 const char *name = NULL;
4726 virshCtrlData data;
4727 virThread workerThread;
4729 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
4730 return false;
4732 if (vshCommandOptBool(cmd, "verbose"))
4733 verbose = true;
4735 if (pipe(p) < 0)
4736 goto cleanup;
4738 data.ctl = ctl;
4739 data.cmd = cmd;
4740 data.writefd = p[1];
4742 if (virThreadCreate(&workerThread,
4743 true,
4744 doManagedsave,
4745 &data) < 0)
4746 goto cleanup;
4748 ret = virshWatchJob(ctl, dom, verbose, p[0], 0,
4749 NULL, NULL, _("Managedsave"));
4751 virThreadJoin(&workerThread);
4753 if (ret)
4754 vshPrintExtra(ctl, _("\nDomain %s state saved by libvirt\n"), name);
4756 cleanup:
4757 virshDomainFree(dom);
4758 VIR_FORCE_CLOSE(p[0]);
4759 VIR_FORCE_CLOSE(p[1]);
4760 return ret;
4764 * "managedsave-remove" command
4766 static const vshCmdInfo info_managedsaveremove[] = {
4767 {.name = "help",
4768 .data = N_("Remove managed save of a domain")
4770 {.name = "desc",
4771 .data = N_("Remove an existing managed save state file from a domain")
4773 {.name = NULL}
4776 static const vshCmdOptDef opts_managedsaveremove[] = {
4777 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
4778 {.name = NULL}
4781 static bool
4782 cmdManagedSaveRemove(vshControl *ctl, const vshCmd *cmd)
4784 virDomainPtr dom;
4785 const char *name;
4786 bool ret = false;
4787 int hassave;
4789 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
4790 return false;
4792 hassave = virDomainHasManagedSaveImage(dom, 0);
4793 if (hassave < 0) {
4794 vshError(ctl, "%s", _("Failed to check for domain managed save image"));
4795 goto cleanup;
4798 if (hassave) {
4799 if (virDomainManagedSaveRemove(dom, 0) < 0) {
4800 vshError(ctl, _("Failed to remove managed save image for domain %s"),
4801 name);
4802 goto cleanup;
4804 else
4805 vshPrintExtra(ctl, _("Removed managedsave image for domain %s"), name);
4807 else
4808 vshPrintExtra(ctl, _("Domain %s has no manage save image; removal skipped"),
4809 name);
4811 ret = true;
4813 cleanup:
4814 virshDomainFree(dom);
4815 return ret;
4819 * "managedsave-edit" command
4821 static const vshCmdInfo info_managed_save_edit[] = {
4822 {.name = "help",
4823 .data = N_("edit XML for a domain's managed save state file")
4825 {.name = "desc",
4826 .data = N_("Edit the domain XML associated with the managed save state file")
4828 {.name = NULL}
4831 static const vshCmdOptDef opts_managed_save_edit[] = {
4832 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
4833 {.name = "running",
4834 .type = VSH_OT_BOOL,
4835 .help = N_("set domain to be running on start")
4837 {.name = "paused",
4838 .type = VSH_OT_BOOL,
4839 .help = N_("set domain to be paused on start")
4841 {.name = NULL}
4844 static bool
4845 cmdManagedSaveEdit(vshControl *ctl, const vshCmd *cmd)
4847 bool ret = false;
4848 virDomainPtr dom = NULL;
4849 unsigned int getxml_flags = VIR_DOMAIN_XML_SECURE;
4850 unsigned int define_flags = 0;
4852 if (vshCommandOptBool(cmd, "running"))
4853 define_flags |= VIR_DOMAIN_SAVE_RUNNING;
4854 if (vshCommandOptBool(cmd, "paused"))
4855 define_flags |= VIR_DOMAIN_SAVE_PAUSED;
4857 VSH_EXCLUSIVE_OPTIONS("running", "paused");
4859 dom = virshCommandOptDomain(ctl, cmd, NULL);
4860 if (dom == NULL)
4861 goto cleanup;
4863 #define EDIT_GET_XML virDomainManagedSaveGetXMLDesc(dom, getxml_flags)
4864 #define EDIT_NOT_CHANGED \
4865 do { \
4866 vshPrintExtra(ctl, _("Managed save image of domain %s XML configuration " \
4867 "not changed.\n"), virDomainGetName(dom)); \
4868 ret = true; \
4869 goto edit_cleanup; \
4870 } while (0)
4871 #define EDIT_DEFINE \
4872 (virDomainManagedSaveDefineXML(dom, doc_edited, define_flags) == 0)
4873 #include "virsh-edit.c"
4875 vshPrintExtra(ctl, _("Managed save image of Domain %s XML configuration edited.\n"),
4876 virDomainGetName(dom));
4877 ret = true;
4879 cleanup:
4880 virshDomainFree(dom);
4881 return ret;
4885 * "managedsave-dumpxml" command
4887 static const vshCmdInfo info_managed_save_dumpxml[] = {
4888 {.name = "help",
4889 .data = N_("Domain information of managed save state file in XML")
4891 {.name = "desc",
4892 .data = N_("Dump XML of domain information for a managed save state file to stdout.")
4894 {.name = NULL}
4897 static const vshCmdOptDef opts_managed_save_dumpxml[] = {
4898 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
4899 {.name = "security-info",
4900 .type = VSH_OT_BOOL,
4901 .help = N_("include security sensitive information in XML dump")
4903 {.name = NULL}
4906 static bool
4907 cmdManagedSaveDumpxml(vshControl *ctl, const vshCmd *cmd)
4909 bool ret = false;
4910 virDomainPtr dom = NULL;
4911 unsigned int flags = 0;
4912 char *xml = NULL;
4914 if (vshCommandOptBool(cmd, "security-info"))
4915 flags |= VIR_DOMAIN_XML_SECURE;
4917 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
4918 goto cleanup;
4920 if (!(xml = virDomainManagedSaveGetXMLDesc(dom, flags)))
4921 goto cleanup;
4923 vshPrint(ctl, "%s", xml);
4924 ret = true;
4926 cleanup:
4927 virshDomainFree(dom);
4928 VIR_FREE(xml);
4929 return ret;
4933 * "managedsave-define" command
4935 static const vshCmdInfo info_managed_save_define[] = {
4936 {.name = "help",
4937 .data = N_("redefine the XML for a domain's managed save state file")
4939 {.name = "desc",
4940 .data = N_("Replace the domain XML associated with a managed save state file")
4942 {.name = NULL}
4945 static const vshCmdOptDef opts_managed_save_define[] = {
4946 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
4947 {.name = "xml",
4948 .type = VSH_OT_DATA,
4949 .flags = VSH_OFLAG_REQ,
4950 .help = N_("filename containing updated XML for the target")
4952 {.name = "running",
4953 .type = VSH_OT_BOOL,
4954 .help = N_("set domain to be running on start")
4956 {.name = "paused",
4957 .type = VSH_OT_BOOL,
4958 .help = N_("set domain to be paused on start")
4960 {.name = NULL}
4963 static bool
4964 cmdManagedSaveDefine(vshControl *ctl, const vshCmd *cmd)
4966 bool ret = false;
4967 virDomainPtr dom = NULL;
4968 const char *xmlfile = NULL;
4969 char *xml = NULL;
4970 unsigned int flags = 0;
4972 if (vshCommandOptBool(cmd, "running"))
4973 flags |= VIR_DOMAIN_SAVE_RUNNING;
4974 if (vshCommandOptBool(cmd, "paused"))
4975 flags |= VIR_DOMAIN_SAVE_PAUSED;
4977 VSH_EXCLUSIVE_OPTIONS("running", "paused");
4979 if (vshCommandOptStringReq(ctl, cmd, "xml", &xmlfile) < 0)
4980 return false;
4982 if (virFileReadAll(xmlfile, VSH_MAX_XML_FILE, &xml) < 0)
4983 return false;
4985 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
4986 goto cleanup;
4988 if (virDomainManagedSaveDefineXML(dom, xml, flags) < 0) {
4989 vshError(ctl, _("Failed to update %s XML configuration"),
4990 virDomainGetName(dom));
4991 goto cleanup;
4994 vshPrintExtra(ctl, _("Managed save state file of domain %s updated.\n"),
4995 virDomainGetName(dom));
4996 ret = true;
4998 cleanup:
4999 virshDomainFree(dom);
5000 VIR_FREE(xml);
5001 return ret;
5005 * "schedinfo" command
5007 static const vshCmdInfo info_schedinfo[] = {
5008 {.name = "help",
5009 .data = N_("show/set scheduler parameters")
5011 {.name = "desc",
5012 .data = N_("Show/Set scheduler parameters.")
5014 {.name = NULL}
5017 static const vshCmdOptDef opts_schedinfo[] = {
5018 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
5019 {.name = "weight",
5020 .type = VSH_OT_INT,
5021 .flags = VSH_OFLAG_REQ_OPT,
5022 .help = N_("weight for XEN_CREDIT")
5024 {.name = "cap",
5025 .type = VSH_OT_INT,
5026 .flags = VSH_OFLAG_REQ_OPT,
5027 .help = N_("cap for XEN_CREDIT")
5029 VIRSH_COMMON_OPT_CURRENT(N_("get/set current scheduler info")),
5030 VIRSH_COMMON_OPT_CONFIG(N_("get/set value to be used on next boot")),
5031 VIRSH_COMMON_OPT_LIVE(N_("get/set value from running domain")),
5032 {.name = "set",
5033 .type = VSH_OT_ARGV,
5034 .flags = VSH_OFLAG_NONE,
5035 .help = N_("parameter=value")
5037 {.name = NULL}
5040 static int
5041 cmdSchedInfoUpdateOne(vshControl *ctl,
5042 virTypedParameterPtr src_params, int nsrc_params,
5043 virTypedParameterPtr *params,
5044 int *nparams, int *maxparams,
5045 const char *field, const char *value)
5047 virTypedParameterPtr param;
5048 int ret = -1;
5049 size_t i;
5051 for (i = 0; i < nsrc_params; i++) {
5052 param = &(src_params[i]);
5054 if (STRNEQ(field, param->field))
5055 continue;
5057 if (virTypedParamsAddFromString(params, nparams, maxparams,
5058 field, param->type,
5059 value) < 0) {
5060 vshSaveLibvirtError();
5061 goto cleanup;
5063 ret = 0;
5064 break;
5067 if (ret < 0)
5068 vshError(ctl, _("invalid scheduler option: %s"), field);
5070 cleanup:
5071 return ret;
5074 static int
5075 cmdSchedInfoUpdate(vshControl *ctl, const vshCmd *cmd,
5076 virTypedParameterPtr src_params, int nsrc_params,
5077 virTypedParameterPtr *update_params)
5079 char *set_field = NULL;
5080 char *set_val = NULL;
5081 const char *val = NULL;
5082 const vshCmdOpt *opt = NULL;
5083 virTypedParameterPtr params = NULL;
5084 int nparams = 0;
5085 int maxparams = 0;
5086 int ret = -1;
5087 int rv;
5089 while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
5090 set_field = vshStrdup(ctl, opt->data);
5091 if (!(set_val = strchr(set_field, '='))) {
5092 vshError(ctl, "%s", _("Invalid syntax for --set, "
5093 "expecting name=value"));
5094 goto cleanup;
5097 *set_val = '\0';
5098 set_val++;
5100 if (cmdSchedInfoUpdateOne(ctl, src_params, nsrc_params,
5101 &params, &nparams, &maxparams,
5102 set_field, set_val) < 0)
5103 goto cleanup;
5105 VIR_FREE(set_field);
5108 rv = vshCommandOptStringReq(ctl, cmd, "cap", &val);
5109 if (rv < 0 ||
5110 (val &&
5111 cmdSchedInfoUpdateOne(ctl, src_params, nsrc_params,
5112 &params, &nparams, &maxparams,
5113 "cap", val) < 0))
5114 goto cleanup;
5116 rv = vshCommandOptStringReq(ctl, cmd, "weight", &val);
5117 if (rv < 0 ||
5118 (val &&
5119 cmdSchedInfoUpdateOne(ctl, src_params, nsrc_params,
5120 &params, &nparams, &maxparams,
5121 "weight", val) < 0))
5122 goto cleanup;
5124 ret = nparams;
5125 *update_params = params;
5126 params = NULL;
5128 cleanup:
5129 VIR_FREE(set_field);
5130 virTypedParamsFree(params, nparams);
5131 return ret;
5134 static bool
5135 cmdSchedinfo(vshControl *ctl, const vshCmd *cmd)
5137 char *schedulertype;
5138 virDomainPtr dom;
5139 virTypedParameterPtr params = NULL;
5140 virTypedParameterPtr updates = NULL;
5141 int nparams = 0;
5142 int nupdates = 0;
5143 size_t i;
5144 int ret;
5145 bool ret_val = false;
5146 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
5147 bool current = vshCommandOptBool(cmd, "current");
5148 bool config = vshCommandOptBool(cmd, "config");
5149 bool live = vshCommandOptBool(cmd, "live");
5151 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
5152 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
5154 if (config)
5155 flags |= VIR_DOMAIN_AFFECT_CONFIG;
5156 if (live)
5157 flags |= VIR_DOMAIN_AFFECT_LIVE;
5159 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
5160 return false;
5162 /* Print SchedulerType */
5163 schedulertype = virDomainGetSchedulerType(dom, &nparams);
5164 if (schedulertype != NULL) {
5165 vshPrint(ctl, "%-15s: %s\n", _("Scheduler"), schedulertype);
5166 VIR_FREE(schedulertype);
5167 } else {
5168 vshPrint(ctl, "%-15s: %s\n", _("Scheduler"), _("Unknown"));
5169 goto cleanup;
5172 if (nparams) {
5173 params = vshMalloc(ctl, sizeof(*params) * nparams);
5175 memset(params, 0, sizeof(*params) * nparams);
5176 if (flags || current) {
5177 /* We cannot query both live and config at once, so settle
5178 on current in that case. If we are setting, then the
5179 two values should match when we re-query; otherwise, we
5180 report the error later. */
5181 ret = virDomainGetSchedulerParametersFlags(dom, params, &nparams,
5182 ((live && config) ? 0
5183 : flags));
5184 } else {
5185 ret = virDomainGetSchedulerParameters(dom, params, &nparams);
5187 if (ret == -1)
5188 goto cleanup;
5190 /* See if any params are being set */
5191 if ((nupdates = cmdSchedInfoUpdate(ctl, cmd, params, nparams,
5192 &updates)) < 0)
5193 goto cleanup;
5195 /* Update parameters & refresh data */
5196 if (nupdates > 0) {
5197 if (flags || current)
5198 ret = virDomainSetSchedulerParametersFlags(dom, updates,
5199 nupdates, flags);
5200 else
5201 ret = virDomainSetSchedulerParameters(dom, updates, nupdates);
5203 if (ret == -1)
5204 goto cleanup;
5206 if (flags || current)
5207 ret = virDomainGetSchedulerParametersFlags(dom, params,
5208 &nparams,
5209 ((live && config) ? 0
5210 : flags));
5211 else
5212 ret = virDomainGetSchedulerParameters(dom, params, &nparams);
5213 if (ret == -1)
5214 goto cleanup;
5215 } else {
5216 /* When not doing --set, --live and --config do not mix. */
5217 if (live && config) {
5218 vshError(ctl, "%s",
5219 _("cannot query both live and config at once"));
5220 goto cleanup;
5224 ret_val = true;
5225 for (i = 0; i < nparams; i++) {
5226 char *str = vshGetTypedParamValue(ctl, &params[i]);
5227 vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
5228 VIR_FREE(str);
5232 cleanup:
5233 virTypedParamsFree(params, nparams);
5234 virTypedParamsFree(updates, nupdates);
5235 virshDomainFree(dom);
5236 return ret_val;
5240 * "restore" command
5242 static const vshCmdInfo info_restore[] = {
5243 {.name = "help",
5244 .data = N_("restore a domain from a saved state in a file")
5246 {.name = "desc",
5247 .data = N_("Restore a domain.")
5249 {.name = NULL}
5252 static const vshCmdOptDef opts_restore[] = {
5253 VIRSH_COMMON_OPT_FILE(N_("the state to restore")),
5254 {.name = "bypass-cache",
5255 .type = VSH_OT_BOOL,
5256 .help = N_("avoid file system cache when restoring")
5258 {.name = "xml",
5259 .type = VSH_OT_STRING,
5260 .help = N_("filename containing updated XML for the target")
5262 {.name = "running",
5263 .type = VSH_OT_BOOL,
5264 .help = N_("restore domain into running state")
5266 {.name = "paused",
5267 .type = VSH_OT_BOOL,
5268 .help = N_("restore domain into paused state")
5270 {.name = NULL}
5273 static bool
5274 cmdRestore(vshControl *ctl, const vshCmd *cmd)
5276 const char *from = NULL;
5277 bool ret = false;
5278 unsigned int flags = 0;
5279 const char *xmlfile = NULL;
5280 char *xml = NULL;
5281 virshControlPtr priv = ctl->privData;
5283 if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
5284 return false;
5286 if (vshCommandOptBool(cmd, "bypass-cache"))
5287 flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
5288 if (vshCommandOptBool(cmd, "running"))
5289 flags |= VIR_DOMAIN_SAVE_RUNNING;
5290 if (vshCommandOptBool(cmd, "paused"))
5291 flags |= VIR_DOMAIN_SAVE_PAUSED;
5293 if (vshCommandOptStringReq(ctl, cmd, "xml", &xmlfile) < 0)
5294 return false;
5296 if (xmlfile &&
5297 virFileReadAll(xmlfile, VSH_MAX_XML_FILE, &xml) < 0)
5298 goto cleanup;
5300 if (((flags || xml)
5301 ? virDomainRestoreFlags(priv->conn, from, xml, flags)
5302 : virDomainRestore(priv->conn, from)) < 0) {
5303 vshError(ctl, _("Failed to restore domain from %s"), from);
5304 goto cleanup;
5307 vshPrintExtra(ctl, _("Domain restored from %s\n"), from);
5308 ret = true;
5310 cleanup:
5311 VIR_FREE(xml);
5312 return ret;
5316 * "dump" command
5318 static const vshCmdInfo info_dump[] = {
5319 {.name = "help",
5320 .data = N_("dump the core of a domain to a file for analysis")
5322 {.name = "desc",
5323 .data = N_("Core dump a domain.")
5325 {.name = NULL}
5328 static const vshCmdOptDef opts_dump[] = {
5329 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
5330 VIRSH_COMMON_OPT_FILE(N_("where to dump the core")),
5331 VIRSH_COMMON_OPT_LIVE(N_("perform a live core dump if supported")),
5332 {.name = "crash",
5333 .type = VSH_OT_BOOL,
5334 .help = N_("crash the domain after core dump")
5336 {.name = "bypass-cache",
5337 .type = VSH_OT_BOOL,
5338 .help = N_("avoid file system cache when dumping")
5340 {.name = "reset",
5341 .type = VSH_OT_BOOL,
5342 .help = N_("reset the domain after core dump")
5344 {.name = "verbose",
5345 .type = VSH_OT_BOOL,
5346 .help = N_("display the progress of dump")
5348 {.name = "memory-only",
5349 .type = VSH_OT_BOOL,
5350 .help = N_("dump domain's memory only")
5352 {.name = "format",
5353 .type = VSH_OT_STRING,
5354 .help = N_("specify the format of memory-only dump")
5356 {.name = NULL}
5359 static void
5360 doDump(void *opaque)
5362 char ret = '1';
5363 virshCtrlData *data = opaque;
5364 vshControl *ctl = data->ctl;
5365 const vshCmd *cmd = data->cmd;
5366 virDomainPtr dom = NULL;
5367 sigset_t sigmask, oldsigmask;
5368 const char *name = NULL;
5369 const char *to = NULL;
5370 unsigned int flags = 0;
5371 const char *format = NULL;
5372 unsigned int dumpformat = VIR_DOMAIN_CORE_DUMP_FORMAT_RAW;
5374 sigemptyset(&sigmask);
5375 sigaddset(&sigmask, SIGINT);
5376 if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
5377 goto out_sig;
5379 if (vshCommandOptStringReq(ctl, cmd, "file", &to) < 0)
5380 goto out;
5382 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
5383 goto out;
5385 if (vshCommandOptBool(cmd, "live"))
5386 flags |= VIR_DUMP_LIVE;
5387 if (vshCommandOptBool(cmd, "crash"))
5388 flags |= VIR_DUMP_CRASH;
5389 if (vshCommandOptBool(cmd, "bypass-cache"))
5390 flags |= VIR_DUMP_BYPASS_CACHE;
5391 if (vshCommandOptBool(cmd, "reset"))
5392 flags |= VIR_DUMP_RESET;
5393 if (vshCommandOptBool(cmd, "memory-only"))
5394 flags |= VIR_DUMP_MEMORY_ONLY;
5396 if (vshCommandOptBool(cmd, "format")) {
5397 if (!(flags & VIR_DUMP_MEMORY_ONLY)) {
5398 vshError(ctl, "%s", _("--format only works with --memory-only"));
5399 goto out;
5402 if (vshCommandOptStringQuiet(ctl, cmd, "format", &format) > 0) {
5403 if (STREQ(format, "kdump-zlib")) {
5404 dumpformat = VIR_DOMAIN_CORE_DUMP_FORMAT_KDUMP_ZLIB;
5405 } else if (STREQ(format, "kdump-lzo")) {
5406 dumpformat = VIR_DOMAIN_CORE_DUMP_FORMAT_KDUMP_LZO;
5407 } else if (STREQ(format, "kdump-snappy")) {
5408 dumpformat = VIR_DOMAIN_CORE_DUMP_FORMAT_KDUMP_SNAPPY;
5409 } else if (STREQ(format, "elf")) {
5410 dumpformat = VIR_DOMAIN_CORE_DUMP_FORMAT_RAW;
5411 } else {
5412 vshError(ctl, _("format '%s' is not supported, expecting "
5413 "'kdump-zlib', 'kdump-lzo', 'kdump-snappy' "
5414 "or 'elf'"), format);
5415 goto out;
5420 if (dumpformat != VIR_DOMAIN_CORE_DUMP_FORMAT_RAW) {
5421 if (virDomainCoreDumpWithFormat(dom, to, dumpformat, flags) < 0) {
5422 vshError(ctl, _("Failed to core dump domain %s to %s"), name, to);
5423 goto out;
5425 } else {
5426 if (virDomainCoreDump(dom, to, flags) < 0) {
5427 vshError(ctl, _("Failed to core dump domain %s to %s"), name, to);
5428 goto out;
5432 ret = '0';
5433 out:
5434 pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
5435 out_sig:
5436 if (dom)
5437 virshDomainFree(dom);
5438 ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
5441 static bool
5442 cmdDump(vshControl *ctl, const vshCmd *cmd)
5444 virDomainPtr dom;
5445 int p[2] = { -1, -1};
5446 bool ret = false;
5447 bool verbose = false;
5448 const char *name = NULL;
5449 const char *to = NULL;
5450 virshCtrlData data;
5451 virThread workerThread;
5453 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
5454 return false;
5456 if (vshCommandOptStringReq(ctl, cmd, "file", &to) < 0)
5457 goto cleanup;
5459 if (vshCommandOptBool(cmd, "verbose"))
5460 verbose = true;
5462 if (pipe(p) < 0)
5463 goto cleanup;
5465 data.ctl = ctl;
5466 data.cmd = cmd;
5467 data.writefd = p[1];
5469 if (virThreadCreate(&workerThread,
5470 true,
5471 doDump,
5472 &data) < 0)
5473 goto cleanup;
5475 ret = virshWatchJob(ctl, dom, verbose, p[0], 0, NULL, NULL, _("Dump"));
5477 virThreadJoin(&workerThread);
5479 if (ret)
5480 vshPrintExtra(ctl, _("\nDomain %s dumped to %s\n"), name, to);
5482 cleanup:
5483 virshDomainFree(dom);
5484 VIR_FORCE_CLOSE(p[0]);
5485 VIR_FORCE_CLOSE(p[1]);
5486 return ret;
5489 static const vshCmdInfo info_screenshot[] = {
5490 {.name = "help",
5491 .data = N_("take a screenshot of a current domain console and store it "
5492 "into a file")
5494 {.name = "desc",
5495 .data = N_("screenshot of a current domain console")
5497 {.name = NULL}
5500 static const vshCmdOptDef opts_screenshot[] = {
5501 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
5502 {.name = "file",
5503 .type = VSH_OT_STRING,
5504 .help = N_("where to store the screenshot")
5506 {.name = "screen",
5507 .type = VSH_OT_INT,
5508 .help = N_("ID of a screen to take screenshot of")
5510 {.name = NULL}
5514 * Generate string: '<domain name>-<timestamp>[<extension>]'
5516 static char *
5517 virshGenFileName(vshControl *ctl, virDomainPtr dom, const char *mime)
5519 char timestr[100];
5520 time_t cur_time;
5521 struct tm time_info;
5522 const char *ext = NULL;
5523 char *ret = NULL;
5525 if (!dom) {
5526 vshError(ctl, "%s", _("Invalid domain supplied"));
5527 return NULL;
5530 if (STREQ(mime, "image/x-portable-pixmap"))
5531 ext = ".ppm";
5532 else if (STREQ(mime, "image/png"))
5533 ext = ".png";
5534 /* add mime type here */
5536 time(&cur_time);
5537 localtime_r(&cur_time, &time_info);
5538 strftime(timestr, sizeof(timestr), "%Y-%m-%d-%H:%M:%S", &time_info);
5540 if (virAsprintf(&ret, "%s-%s%s", virDomainGetName(dom),
5541 timestr, NULLSTR_EMPTY(ext)) < 0) {
5542 vshError(ctl, "%s", _("Out of memory"));
5543 return NULL;
5546 return ret;
5549 static bool
5550 cmdScreenshot(vshControl *ctl, const vshCmd *cmd)
5552 virDomainPtr dom;
5553 const char *name = NULL;
5554 char *file = NULL;
5555 int fd = -1;
5556 virStreamPtr st = NULL;
5557 unsigned int screen = 0;
5558 unsigned int flags = 0; /* currently unused */
5559 bool ret = false;
5560 bool created = false;
5561 bool generated = false;
5562 char *mime = NULL;
5563 virshControlPtr priv = ctl->privData;
5565 if (vshCommandOptStringReq(ctl, cmd, "file", (const char **) &file) < 0)
5566 return false;
5568 if (vshCommandOptUInt(ctl, cmd, "screen", &screen) < 0)
5569 return false;
5571 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
5572 return false;
5574 if (!(st = virStreamNew(priv->conn, 0)))
5575 goto cleanup;
5577 mime = virDomainScreenshot(dom, st, screen, flags);
5578 if (!mime) {
5579 vshError(ctl, _("could not take a screenshot of %s"), name);
5580 goto cleanup;
5583 if (!file) {
5584 if (!(file = virshGenFileName(ctl, dom, mime)))
5585 goto cleanup;
5586 generated = true;
5589 if ((fd = open(file, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0) {
5590 if (errno != EEXIST ||
5591 (fd = open(file, O_WRONLY|O_TRUNC, 0666)) < 0) {
5592 vshError(ctl, _("cannot create file %s"), file);
5593 goto cleanup;
5595 } else {
5596 created = true;
5599 if (virStreamRecvAll(st, virshStreamSink, &fd) < 0) {
5600 vshError(ctl, _("could not receive data from domain %s"), name);
5601 goto cleanup;
5604 if (VIR_CLOSE(fd) < 0) {
5605 vshError(ctl, _("cannot close file %s"), file);
5606 goto cleanup;
5609 if (virStreamFinish(st) < 0) {
5610 vshError(ctl, _("cannot close stream on domain %s"), name);
5611 goto cleanup;
5614 vshPrintExtra(ctl, _("Screenshot saved to %s, with type of %s"), file, mime);
5615 ret = true;
5617 cleanup:
5618 if (!ret && created)
5619 unlink(file);
5620 if (generated)
5621 VIR_FREE(file);
5622 virshDomainFree(dom);
5623 if (st)
5624 virStreamFree(st);
5625 VIR_FORCE_CLOSE(fd);
5626 VIR_FREE(mime);
5627 return ret;
5631 * "set-lifecycle-action" command
5633 static const vshCmdInfo info_setLifecycleAction[] = {
5634 {.name = "help",
5635 .data = N_("change lifecycle actions")
5637 {.name = "desc",
5638 .data = N_("Change lifecycle actions for the guest domain.")
5640 {.name = NULL}
5643 static const vshCmdOptDef opts_setLifecycleAction[] = {
5644 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
5645 {.name = "type",
5646 .type = VSH_OT_DATA,
5647 .flags = VSH_OFLAG_REQ,
5648 .help = N_("lifecycle type to modify")
5650 {.name = "action",
5651 .type = VSH_OT_DATA,
5652 .flags = VSH_OFLAG_REQ,
5653 .help = N_("lifecycle action to set")
5655 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
5656 VIRSH_COMMON_OPT_DOMAIN_LIVE,
5657 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
5658 {.name = NULL}
5661 VIR_ENUM_IMPL(virDomainLifecycle,
5662 VIR_DOMAIN_LIFECYCLE_LAST,
5663 "poweroff",
5664 "reboot",
5665 "crash");
5667 VIR_ENUM_IMPL(virDomainLifecycleAction,
5668 VIR_DOMAIN_LIFECYCLE_ACTION_LAST,
5669 "destroy",
5670 "restart",
5671 "rename-restart",
5672 "preserve",
5673 "coredump-destroy",
5674 "coredump-restart");
5676 static bool
5677 cmdSetLifecycleAction(vshControl *ctl, const vshCmd *cmd)
5679 virDomainPtr dom;
5680 bool ret = true;
5681 bool config = vshCommandOptBool(cmd, "config");
5682 bool live = vshCommandOptBool(cmd, "live");
5683 bool current = vshCommandOptBool(cmd, "current");
5684 const char *typeStr;
5685 const char *actionStr;
5686 unsigned int type;
5687 unsigned int action;
5688 unsigned int flags = 0;
5689 int tmpVal;
5691 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
5692 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
5694 if (config)
5695 flags |= VIR_DOMAIN_AFFECT_CONFIG;
5696 if (live)
5697 flags |= VIR_DOMAIN_AFFECT_LIVE;
5699 if (vshCommandOptStringReq(ctl, cmd, "type", &typeStr) < 0 ||
5700 vshCommandOptStringReq(ctl, cmd, "action", &actionStr) < 0) {
5701 return false;
5704 if ((tmpVal = virDomainLifecycleTypeFromString(typeStr)) < 0) {
5705 vshError(ctl, _("Invalid lifecycle type '%s'."), typeStr);
5706 return false;
5708 type = tmpVal;
5710 if ((tmpVal = virDomainLifecycleActionTypeFromString(actionStr)) < 0) {
5711 vshError(ctl, _("Invalid lifecycle action '%s'."), actionStr);
5712 return false;
5714 action = tmpVal;
5716 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
5717 return false;
5719 if (virDomainSetLifecycleAction(dom, type, action, flags) < 0) {
5720 vshError(ctl, "%s", _("Unable to change lifecycle action."));
5721 ret = false;
5724 virshDomainFree(dom);
5725 return ret;
5729 * "set-user-password" command
5731 static const vshCmdInfo info_set_user_password[] = {
5732 {.name = "help",
5733 .data = N_("set the user password inside the domain")
5735 {.name = "desc",
5736 .data = N_("changes the password of the specified user inside the domain")
5738 {.name = NULL}
5741 static const vshCmdOptDef opts_set_user_password[] = {
5742 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
5743 {.name = "user",
5744 .type = VSH_OT_DATA,
5745 .flags = VSH_OFLAG_REQ,
5746 .help = N_("the username")
5748 {.name = "password",
5749 .type = VSH_OT_DATA,
5750 .flags = VSH_OFLAG_REQ,
5751 .help = N_("the new password")
5753 {.name = "encrypted",
5754 .type = VSH_OT_BOOL,
5755 .help = N_("the password is already encrypted")
5757 {.name = NULL}
5760 static bool
5761 cmdSetUserPassword(vshControl *ctl, const vshCmd *cmd)
5763 virDomainPtr dom;
5764 const char *name;
5765 const char *password = NULL;
5766 const char *user = NULL;
5767 unsigned int flags = 0;
5768 bool ret = false;
5770 if (vshCommandOptBool(cmd, "encrypted"))
5771 flags = VIR_DOMAIN_PASSWORD_ENCRYPTED;
5773 if (vshCommandOptStringReq(ctl, cmd, "user", &user) < 0)
5774 return false;
5776 if (vshCommandOptStringReq(ctl, cmd, "password", &password) < 0)
5777 return false;
5779 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
5780 return false;
5782 if (virDomainSetUserPassword(dom, user, password, flags) < 0)
5783 goto cleanup;
5785 vshPrintExtra(ctl, _("Password set successfully for %s in %s"), user, name);
5786 ret = true;
5788 cleanup:
5789 virshDomainFree(dom);
5790 return ret;
5793 * "resume" command
5795 static const vshCmdInfo info_resume[] = {
5796 {.name = "help",
5797 .data = N_("resume a domain")
5799 {.name = "desc",
5800 .data = N_("Resume a previously suspended domain.")
5802 {.name = NULL}
5805 static const vshCmdOptDef opts_resume[] = {
5806 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_PAUSED),
5807 {.name = NULL}
5810 static bool
5811 cmdResume(vshControl *ctl, const vshCmd *cmd)
5813 virDomainPtr dom;
5814 bool ret = true;
5815 const char *name;
5817 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
5818 return false;
5820 if (virDomainResume(dom) == 0) {
5821 vshPrintExtra(ctl, _("Domain %s resumed\n"), name);
5822 } else {
5823 vshError(ctl, _("Failed to resume domain %s"), name);
5824 ret = false;
5827 virshDomainFree(dom);
5828 return ret;
5832 * "shutdown" command
5834 static const vshCmdInfo info_shutdown[] = {
5835 {.name = "help",
5836 .data = N_("gracefully shutdown a domain")
5838 {.name = "desc",
5839 .data = N_("Run shutdown in the target domain.")
5841 {.name = NULL}
5844 static const vshCmdOptDef opts_shutdown[] = {
5845 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
5846 {.name = "mode",
5847 .type = VSH_OT_STRING,
5848 .completer = virshDomainShutdownModeCompleter,
5849 .help = N_("shutdown mode: acpi|agent|initctl|signal|paravirt")
5851 {.name = NULL}
5854 static bool
5855 cmdShutdown(vshControl *ctl, const vshCmd *cmd)
5857 virDomainPtr dom = NULL;
5858 bool ret = false;
5859 const char *name;
5860 const char *mode = NULL;
5861 int flags = 0;
5862 int rv;
5863 char **modes = NULL, **tmp;
5865 if (vshCommandOptStringReq(ctl, cmd, "mode", &mode) < 0)
5866 return false;
5868 if (mode && !(modes = virStringSplit(mode, ",", 0))) {
5869 vshError(ctl, "%s", _("Cannot parse mode string"));
5870 return false;
5873 tmp = modes;
5874 while (tmp && *tmp) {
5875 mode = *tmp;
5876 if (STREQ(mode, "acpi")) {
5877 flags |= VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN;
5878 } else if (STREQ(mode, "agent")) {
5879 flags |= VIR_DOMAIN_SHUTDOWN_GUEST_AGENT;
5880 } else if (STREQ(mode, "initctl")) {
5881 flags |= VIR_DOMAIN_SHUTDOWN_INITCTL;
5882 } else if (STREQ(mode, "signal")) {
5883 flags |= VIR_DOMAIN_SHUTDOWN_SIGNAL;
5884 } else if (STREQ(mode, "paravirt")) {
5885 flags |= VIR_DOMAIN_SHUTDOWN_PARAVIRT;
5886 } else {
5887 vshError(ctl, _("Unknown mode %s value, expecting "
5888 "'acpi', 'agent', 'initctl', 'signal', "
5889 "or 'paravirt'"), mode);
5890 goto cleanup;
5892 tmp++;
5895 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
5896 goto cleanup;
5898 if (flags)
5899 rv = virDomainShutdownFlags(dom, flags);
5900 else
5901 rv = virDomainShutdown(dom);
5902 if (rv == 0) {
5903 vshPrintExtra(ctl, _("Domain %s is being shutdown\n"), name);
5904 } else {
5905 vshError(ctl, _("Failed to shutdown domain %s"), name);
5906 goto cleanup;
5909 ret = true;
5910 cleanup:
5911 virshDomainFree(dom);
5912 virStringListFree(modes);
5913 return ret;
5917 * "reboot" command
5919 static const vshCmdInfo info_reboot[] = {
5920 {.name = "help",
5921 .data = N_("reboot a domain")
5923 {.name = "desc",
5924 .data = N_("Run a reboot command in the target domain.")
5926 {.name = NULL}
5929 static const vshCmdOptDef opts_reboot[] = {
5930 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
5931 {.name = "mode",
5932 .type = VSH_OT_STRING,
5933 .completer = virshDomainShutdownModeCompleter,
5934 .help = N_("shutdown mode: acpi|agent|initctl|signal|paravirt")
5936 {.name = NULL}
5939 static bool
5940 cmdReboot(vshControl *ctl, const vshCmd *cmd)
5942 virDomainPtr dom = NULL;
5943 bool ret = false;
5944 const char *name;
5945 const char *mode = NULL;
5946 int flags = 0;
5947 char **modes = NULL, **tmp;
5949 if (vshCommandOptStringReq(ctl, cmd, "mode", &mode) < 0)
5950 return false;
5952 if (mode && !(modes = virStringSplit(mode, ",", 0))) {
5953 vshError(ctl, "%s", _("Cannot parse mode string"));
5954 return false;
5957 tmp = modes;
5958 while (tmp && *tmp) {
5959 mode = *tmp;
5960 if (STREQ(mode, "acpi")) {
5961 flags |= VIR_DOMAIN_REBOOT_ACPI_POWER_BTN;
5962 } else if (STREQ(mode, "agent")) {
5963 flags |= VIR_DOMAIN_REBOOT_GUEST_AGENT;
5964 } else if (STREQ(mode, "initctl")) {
5965 flags |= VIR_DOMAIN_REBOOT_INITCTL;
5966 } else if (STREQ(mode, "signal")) {
5967 flags |= VIR_DOMAIN_REBOOT_SIGNAL;
5968 } else if (STREQ(mode, "paravirt")) {
5969 flags |= VIR_DOMAIN_REBOOT_PARAVIRT;
5970 } else {
5971 vshError(ctl, _("Unknown mode %s value, expecting "
5972 "'acpi', 'agent', 'initctl', 'signal' "
5973 "or 'paravirt'"), mode);
5974 goto cleanup;
5976 tmp++;
5979 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
5980 goto cleanup;
5982 if (virDomainReboot(dom, flags) == 0) {
5983 vshPrintExtra(ctl, _("Domain %s is being rebooted\n"), name);
5984 } else {
5985 vshError(ctl, _("Failed to reboot domain %s"), name);
5986 goto cleanup;
5989 ret = true;
5990 cleanup:
5991 virshDomainFree(dom);
5992 virStringListFree(modes);
5993 return ret;
5997 * "reset" command
5999 static const vshCmdInfo info_reset[] = {
6000 {.name = "help",
6001 .data = N_("reset a domain")
6003 {.name = "desc",
6004 .data = N_("Reset the target domain as if by power button")
6006 {.name = NULL}
6009 static const vshCmdOptDef opts_reset[] = {
6010 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
6011 {.name = NULL}
6014 static bool
6015 cmdReset(vshControl *ctl, const vshCmd *cmd)
6017 virDomainPtr dom;
6018 bool ret = true;
6019 const char *name;
6021 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
6022 return false;
6024 if (virDomainReset(dom, 0) == 0) {
6025 vshPrintExtra(ctl, _("Domain %s was reset\n"), name);
6026 } else {
6027 vshError(ctl, _("Failed to reset domain %s"), name);
6028 ret = false;
6031 virshDomainFree(dom);
6032 return ret;
6036 * "domjobinfo" command
6038 static const vshCmdInfo info_domjobinfo[] = {
6039 {.name = "help",
6040 .data = N_("domain job information")
6042 {.name = "desc",
6043 .data = N_("Returns information about jobs running on a domain.")
6045 {.name = NULL}
6048 static const vshCmdOptDef opts_domjobinfo[] = {
6049 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
6050 {.name = "completed",
6051 .type = VSH_OT_BOOL,
6052 .help = N_("return statistics of a recently completed job")
6054 {.name = NULL}
6057 VIR_ENUM_DECL(virshDomainJob);
6058 VIR_ENUM_IMPL(virshDomainJob,
6059 VIR_DOMAIN_JOB_LAST,
6060 N_("None"),
6061 N_("Bounded"),
6062 N_("Unbounded"),
6063 N_("Completed"),
6064 N_("Failed"),
6065 N_("Cancelled"));
6067 static const char *
6068 virshDomainJobToString(int type)
6070 const char *str = virshDomainJobTypeToString(type);
6071 return str ? _(str) : _("unknown");
6074 VIR_ENUM_DECL(virshDomainJobOperation);
6075 VIR_ENUM_IMPL(virshDomainJobOperation,
6076 VIR_DOMAIN_JOB_OPERATION_LAST,
6077 N_("Unknown"),
6078 N_("Start"),
6079 N_("Save"),
6080 N_("Restore"),
6081 N_("Incoming migration"),
6082 N_("Outgoing migration"),
6083 N_("Snapshot"),
6084 N_("Snapshot revert"),
6085 N_("Dump"),
6086 N_("Backup"),
6089 static const char *
6090 virshDomainJobOperationToString(int op)
6092 const char *str = virshDomainJobOperationTypeToString(op);
6093 return str ? _(str) : _("unknown");
6096 static bool
6097 cmdDomjobinfo(vshControl *ctl, const vshCmd *cmd)
6099 virDomainJobInfo info;
6100 virDomainPtr dom;
6101 bool ret = false;
6102 const char *unit;
6103 double val;
6104 virTypedParameterPtr params = NULL;
6105 int nparams = 0;
6106 unsigned long long value;
6107 unsigned int flags = 0;
6108 int ivalue;
6109 int op;
6110 int rc;
6112 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
6113 return false;
6115 if (vshCommandOptBool(cmd, "completed"))
6116 flags |= VIR_DOMAIN_JOB_STATS_COMPLETED;
6118 memset(&info, 0, sizeof(info));
6120 rc = virDomainGetJobStats(dom, &info.type, &params, &nparams, flags);
6121 if (rc == 0) {
6122 if (virTypedParamsGetULLong(params, nparams,
6123 VIR_DOMAIN_JOB_TIME_ELAPSED,
6124 &info.timeElapsed) < 0 ||
6125 virTypedParamsGetULLong(params, nparams,
6126 VIR_DOMAIN_JOB_TIME_REMAINING,
6127 &info.timeRemaining) < 0 ||
6128 virTypedParamsGetULLong(params, nparams,
6129 VIR_DOMAIN_JOB_DATA_TOTAL,
6130 &info.dataTotal) < 0 ||
6131 virTypedParamsGetULLong(params, nparams,
6132 VIR_DOMAIN_JOB_DATA_PROCESSED,
6133 &info.dataProcessed) < 0 ||
6134 virTypedParamsGetULLong(params, nparams,
6135 VIR_DOMAIN_JOB_DATA_REMAINING,
6136 &info.dataRemaining) < 0 ||
6137 virTypedParamsGetULLong(params, nparams,
6138 VIR_DOMAIN_JOB_MEMORY_TOTAL,
6139 &info.memTotal) < 0 ||
6140 virTypedParamsGetULLong(params, nparams,
6141 VIR_DOMAIN_JOB_MEMORY_PROCESSED,
6142 &info.memProcessed) < 0 ||
6143 virTypedParamsGetULLong(params, nparams,
6144 VIR_DOMAIN_JOB_MEMORY_REMAINING,
6145 &info.memRemaining) < 0 ||
6146 virTypedParamsGetULLong(params, nparams,
6147 VIR_DOMAIN_JOB_DISK_TOTAL,
6148 &info.fileTotal) < 0 ||
6149 virTypedParamsGetULLong(params, nparams,
6150 VIR_DOMAIN_JOB_DISK_PROCESSED,
6151 &info.fileProcessed) < 0 ||
6152 virTypedParamsGetULLong(params, nparams,
6153 VIR_DOMAIN_JOB_DISK_REMAINING,
6154 &info.fileRemaining) < 0)
6155 goto save_error;
6156 } else if (last_error->code == VIR_ERR_NO_SUPPORT) {
6157 if (flags) {
6158 vshError(ctl, "%s", _("Optional flags are not supported by the "
6159 "daemon"));
6160 goto cleanup;
6162 vshDebug(ctl, VSH_ERR_DEBUG, "detailed statistics not supported\n");
6163 vshResetLibvirtError();
6164 rc = virDomainGetJobInfo(dom, &info);
6166 if (rc < 0)
6167 goto cleanup;
6169 vshPrint(ctl, "%-17s %-12s\n", _("Job type:"),
6170 virshDomainJobToString(info.type));
6171 if (info.type != VIR_DOMAIN_JOB_BOUNDED &&
6172 info.type != VIR_DOMAIN_JOB_UNBOUNDED &&
6173 (!(flags & VIR_DOMAIN_JOB_STATS_COMPLETED) ||
6174 info.type != VIR_DOMAIN_JOB_COMPLETED)) {
6175 ret = true;
6176 goto cleanup;
6179 op = VIR_DOMAIN_JOB_OPERATION_UNKNOWN;
6180 if ((rc = virTypedParamsGetInt(params, nparams,
6181 VIR_DOMAIN_JOB_OPERATION, &op)) < 0)
6182 goto save_error;
6184 vshPrint(ctl, "%-17s %-12s\n", _("Operation:"),
6185 virshDomainJobOperationToString(op));
6187 vshPrint(ctl, "%-17s %-12llu ms\n", _("Time elapsed:"), info.timeElapsed);
6188 if ((rc = virTypedParamsGetULLong(params, nparams,
6189 VIR_DOMAIN_JOB_TIME_ELAPSED_NET,
6190 &value)) < 0) {
6191 goto save_error;
6192 } else if (rc) {
6193 vshPrint(ctl, "%-17s %-12llu ms\n", _("Time elapsed w/o network:"),
6194 value);
6197 if (info.type == VIR_DOMAIN_JOB_BOUNDED)
6198 vshPrint(ctl, "%-17s %-12llu ms\n", _("Time remaining:"),
6199 info.timeRemaining);
6201 if (info.dataTotal || info.dataRemaining || info.dataProcessed) {
6202 val = vshPrettyCapacity(info.dataProcessed, &unit);
6203 vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data processed:"), val, unit);
6204 val = vshPrettyCapacity(info.dataRemaining, &unit);
6205 vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data remaining:"), val, unit);
6206 val = vshPrettyCapacity(info.dataTotal, &unit);
6207 vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data total:"), val, unit);
6210 if (info.memTotal || info.memRemaining || info.memProcessed) {
6211 val = vshPrettyCapacity(info.memProcessed, &unit);
6212 vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory processed:"), val, unit);
6213 val = vshPrettyCapacity(info.memRemaining, &unit);
6214 vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory remaining:"), val, unit);
6215 val = vshPrettyCapacity(info.memTotal, &unit);
6216 vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory total:"), val, unit);
6218 if ((rc = virTypedParamsGetULLong(params, nparams,
6219 VIR_DOMAIN_JOB_MEMORY_BPS,
6220 &value)) < 0) {
6221 goto save_error;
6222 } else if (rc && value) {
6223 val = vshPrettyCapacity(value, &unit);
6224 vshPrint(ctl, "%-17s %-.3lf %s/s\n",
6225 _("Memory bandwidth:"), val, unit);
6228 if ((rc = virTypedParamsGetULLong(params, nparams,
6229 VIR_DOMAIN_JOB_MEMORY_DIRTY_RATE,
6230 &value)) < 0) {
6231 goto save_error;
6232 } else if (rc) {
6233 vshPrint(ctl, "%-17s %-12llu pages/s\n", _("Dirty rate:"), value);
6236 if ((rc = virTypedParamsGetULLong(params, nparams,
6237 VIR_DOMAIN_JOB_MEMORY_PAGE_SIZE,
6238 &value)) < 0) {
6239 goto save_error;
6240 } else if (rc) {
6241 vshPrint(ctl, "%-17s %-12llu bytes\n", _("Page size:"), value);
6244 if ((rc = virTypedParamsGetULLong(params, nparams,
6245 VIR_DOMAIN_JOB_MEMORY_ITERATION,
6246 &value)) < 0) {
6247 goto save_error;
6248 } else if (rc) {
6249 vshPrint(ctl, "%-17s %-12llu\n", _("Iteration:"), value);
6252 if ((rc = virTypedParamsGetULLong(params, nparams,
6253 VIR_DOMAIN_JOB_MEMORY_POSTCOPY_REQS,
6254 &value)) < 0) {
6255 goto save_error;
6256 } else if (rc) {
6257 vshPrint(ctl, "%-17s %-12llu\n", _("Postcopy requests:"), value);
6261 if (info.fileTotal || info.fileRemaining || info.fileProcessed) {
6262 val = vshPrettyCapacity(info.fileProcessed, &unit);
6263 vshPrint(ctl, "%-17s %-.3lf %s\n", _("File processed:"), val, unit);
6264 val = vshPrettyCapacity(info.fileRemaining, &unit);
6265 vshPrint(ctl, "%-17s %-.3lf %s\n", _("File remaining:"), val, unit);
6266 val = vshPrettyCapacity(info.fileTotal, &unit);
6267 vshPrint(ctl, "%-17s %-.3lf %s\n", _("File total:"), val, unit);
6269 if ((rc = virTypedParamsGetULLong(params, nparams,
6270 VIR_DOMAIN_JOB_DISK_BPS,
6271 &value)) < 0) {
6272 goto save_error;
6273 } else if (rc && value) {
6274 val = vshPrettyCapacity(value, &unit);
6275 vshPrint(ctl, "%-17s %-.3lf %s/s\n",
6276 _("File bandwidth:"), val, unit);
6280 if ((rc = virTypedParamsGetULLong(params, nparams,
6281 VIR_DOMAIN_JOB_MEMORY_CONSTANT,
6282 &value)) < 0) {
6283 goto save_error;
6284 } else if (rc) {
6285 vshPrint(ctl, "%-17s %-12llu\n", _("Constant pages:"), value);
6287 if ((rc = virTypedParamsGetULLong(params, nparams,
6288 VIR_DOMAIN_JOB_MEMORY_NORMAL,
6289 &value)) < 0) {
6290 goto save_error;
6291 } else if (rc) {
6292 vshPrint(ctl, "%-17s %-12llu\n", _("Normal pages:"), value);
6294 if ((rc = virTypedParamsGetULLong(params, nparams,
6295 VIR_DOMAIN_JOB_MEMORY_NORMAL_BYTES,
6296 &value)) < 0) {
6297 goto save_error;
6298 } else if (rc) {
6299 val = vshPrettyCapacity(value, &unit);
6300 vshPrint(ctl, "%-17s %-.3lf %s\n", _("Normal data:"), val, unit);
6303 if ((rc = virTypedParamsGetULLong(params, nparams,
6304 VIR_DOMAIN_JOB_DOWNTIME,
6305 &value)) < 0) {
6306 goto save_error;
6307 } else if (rc) {
6308 if (info.type == VIR_DOMAIN_JOB_COMPLETED) {
6309 vshPrint(ctl, "%-17s %-12llu ms\n",
6310 _("Total downtime:"), value);
6311 } else {
6312 vshPrint(ctl, "%-17s %-12llu ms\n",
6313 _("Expected downtime:"), value);
6317 if ((rc = virTypedParamsGetULLong(params, nparams,
6318 VIR_DOMAIN_JOB_DOWNTIME_NET,
6319 &value)) < 0)
6320 goto save_error;
6321 else if (rc)
6322 vshPrint(ctl, "%-17s %-12llu ms\n", _("Downtime w/o network:"), value);
6324 if ((rc = virTypedParamsGetULLong(params, nparams,
6325 VIR_DOMAIN_JOB_SETUP_TIME,
6326 &value)) < 0)
6327 goto save_error;
6328 else if (rc)
6329 vshPrint(ctl, "%-17s %-12llu ms\n", _("Setup time:"), value);
6331 if ((rc = virTypedParamsGetULLong(params, nparams,
6332 VIR_DOMAIN_JOB_COMPRESSION_CACHE,
6333 &value)) < 0) {
6334 goto save_error;
6335 } else if (rc) {
6336 val = vshPrettyCapacity(value, &unit);
6337 vshPrint(ctl, "%-17s %-.3lf %s\n", _("Compression cache:"), val, unit);
6339 if ((rc = virTypedParamsGetULLong(params, nparams,
6340 VIR_DOMAIN_JOB_COMPRESSION_BYTES,
6341 &value)) < 0) {
6342 goto save_error;
6343 } else if (rc) {
6344 val = vshPrettyCapacity(value, &unit);
6345 vshPrint(ctl, "%-17s %-.3lf %s\n", _("Compressed data:"), val, unit);
6347 if ((rc = virTypedParamsGetULLong(params, nparams,
6348 VIR_DOMAIN_JOB_COMPRESSION_PAGES,
6349 &value)) < 0) {
6350 goto save_error;
6351 } else if (rc) {
6352 vshPrint(ctl, "%-17s %-13llu\n", _("Compressed pages:"), value);
6354 if ((rc = virTypedParamsGetULLong(params, nparams,
6355 VIR_DOMAIN_JOB_COMPRESSION_CACHE_MISSES,
6356 &value)) < 0) {
6357 goto save_error;
6358 } else if (rc) {
6359 vshPrint(ctl, "%-17s %-13llu\n", _("Compression cache misses:"), value);
6361 if ((rc = virTypedParamsGetULLong(params, nparams,
6362 VIR_DOMAIN_JOB_COMPRESSION_OVERFLOW,
6363 &value)) < 0) {
6364 goto save_error;
6365 } else if (rc) {
6366 vshPrint(ctl, "%-17s %-13llu\n", _("Compression overflows:"), value);
6369 if ((rc = virTypedParamsGetInt(params, nparams,
6370 VIR_DOMAIN_JOB_AUTO_CONVERGE_THROTTLE,
6371 &ivalue)) < 0) {
6372 goto save_error;
6373 } else if (rc) {
6374 vshPrint(ctl, "%-17s %-13d\n", _("Auto converge throttle:"), ivalue);
6377 ret = true;
6379 cleanup:
6380 virshDomainFree(dom);
6381 virTypedParamsFree(params, nparams);
6382 return ret;
6384 save_error:
6385 vshSaveLibvirtError();
6386 goto cleanup;
6390 * "domjobabort" command
6392 static const vshCmdInfo info_domjobabort[] = {
6393 {.name = "help",
6394 .data = N_("abort active domain job")
6396 {.name = "desc",
6397 .data = N_("Aborts the currently running domain job")
6399 {.name = NULL}
6402 static const vshCmdOptDef opts_domjobabort[] = {
6403 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
6404 {.name = NULL}
6407 static bool
6408 cmdDomjobabort(vshControl *ctl, const vshCmd *cmd)
6410 virDomainPtr dom;
6411 bool ret = true;
6413 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
6414 return false;
6416 if (virDomainAbortJob(dom) < 0)
6417 ret = false;
6419 virshDomainFree(dom);
6420 return ret;
6424 * "vcpucount" command
6426 static const vshCmdInfo info_vcpucount[] = {
6427 {.name = "help",
6428 .data = N_("domain vcpu counts")
6430 {.name = "desc",
6431 .data = N_("Returns the number of virtual CPUs used by the domain.")
6433 {.name = NULL}
6436 static const vshCmdOptDef opts_vcpucount[] = {
6437 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
6438 {.name = "maximum",
6439 .type = VSH_OT_BOOL,
6440 .help = N_("get maximum count of vcpus")
6442 {.name = "active",
6443 .type = VSH_OT_BOOL,
6444 .help = N_("get number of currently active vcpus")
6446 VIRSH_COMMON_OPT_LIVE(N_("get value from running domain")),
6447 VIRSH_COMMON_OPT_CONFIG(N_("get value to be used on next boot")),
6448 VIRSH_COMMON_OPT_CURRENT(N_("get value according to current domain state")),
6449 {.name = "guest",
6450 .type = VSH_OT_BOOL,
6451 .help = N_("retrieve vcpu count from the guest instead of the hypervisor")
6453 {.name = NULL}
6457 * Collect the number of vCPUs for a guest possibly with fallback means.
6459 * Returns the count of vCPUs for a domain and certain flags. Returns -2 in case
6460 * of error. If @checkState is true, in case live stats can't be collected when
6461 * the domain is inactive or persistent stats can't be collected if domain is
6462 * transient -1 is returned and no error is reported.
6465 static int
6466 virshCPUCountCollect(vshControl *ctl,
6467 virDomainPtr dom,
6468 unsigned int flags,
6469 bool checkState)
6471 int ret = -2;
6472 virDomainInfo info;
6473 int count;
6474 xmlDocPtr xml = NULL;
6475 xmlXPathContextPtr ctxt = NULL;
6477 if (checkState &&
6478 ((flags & VIR_DOMAIN_AFFECT_LIVE && virDomainIsActive(dom) < 1) ||
6479 (flags & VIR_DOMAIN_AFFECT_CONFIG && virDomainIsPersistent(dom) < 1)))
6480 return -1;
6482 /* In all cases, try the new API first; if it fails because we are talking
6483 * to an older daemon, generally we try a fallback API before giving up.
6484 * --current requires the new API, since we don't know whether the domain is
6485 * running or inactive. */
6486 if ((count = virDomainGetVcpusFlags(dom, flags)) >= 0)
6487 return count;
6489 /* fallback code */
6490 if (!(last_error->code == VIR_ERR_NO_SUPPORT ||
6491 last_error->code == VIR_ERR_INVALID_ARG))
6492 goto cleanup;
6494 if (flags & VIR_DOMAIN_VCPU_GUEST) {
6495 vshError(ctl, "%s", _("Failed to retrieve vCPU count from the guest"));
6496 goto cleanup;
6499 if (!(flags & (VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG)) &&
6500 virDomainIsActive(dom) == 1)
6501 flags |= VIR_DOMAIN_AFFECT_LIVE;
6503 vshResetLibvirtError();
6505 if (flags & VIR_DOMAIN_AFFECT_LIVE) {
6506 if (flags & VIR_DOMAIN_VCPU_MAXIMUM) {
6507 count = virDomainGetMaxVcpus(dom);
6508 } else {
6509 if (virDomainGetInfo(dom, &info) < 0)
6510 goto cleanup;
6512 count = info.nrVirtCpu;
6514 } else {
6515 if (virshDomainGetXMLFromDom(ctl, dom, VIR_DOMAIN_XML_INACTIVE,
6516 &xml, &ctxt) < 0)
6517 goto cleanup;
6519 if (flags & VIR_DOMAIN_VCPU_MAXIMUM) {
6520 if (virXPathInt("string(/domain/vcpu)", ctxt, &count) < 0) {
6521 vshError(ctl, "%s", _("Failed to retrieve maximum vcpu count"));
6522 goto cleanup;
6524 } else {
6525 if (virXPathInt("string(/domain/vcpu/@current)", ctxt, &count) < 0) {
6526 vshError(ctl, "%s", _("Failed to retrieve current vcpu count"));
6527 goto cleanup;
6532 ret = count;
6533 cleanup:
6534 xmlXPathFreeContext(ctxt);
6535 xmlFreeDoc(xml);
6537 return ret;
6540 static bool
6541 cmdVcpucount(vshControl *ctl, const vshCmd *cmd)
6543 virDomainPtr dom;
6544 bool ret = false;
6545 bool maximum = vshCommandOptBool(cmd, "maximum");
6546 bool active = vshCommandOptBool(cmd, "active");
6547 bool config = vshCommandOptBool(cmd, "config");
6548 bool live = vshCommandOptBool(cmd, "live");
6549 bool current = vshCommandOptBool(cmd, "current");
6550 bool guest = vshCommandOptBool(cmd, "guest");
6551 bool all = maximum + active + current + config + live + guest == 0;
6552 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
6554 /* Backwards compatibility: prior to 0.9.4,
6555 * VIR_DOMAIN_AFFECT_CURRENT was unsupported, and --current meant
6556 * the opposite of --maximum. Translate the old '--current
6557 * --live' into the new '--active --live', while treating the new
6558 * '--maximum --current' correctly rather than rejecting it as
6559 * '--maximum --active'. */
6560 if (!maximum && !active && current)
6561 current = false;
6563 VSH_EXCLUSIVE_OPTIONS_VAR(live, config)
6564 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
6565 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
6566 VSH_EXCLUSIVE_OPTIONS_VAR(active, maximum);
6567 VSH_EXCLUSIVE_OPTIONS_VAR(guest, config);
6569 if (live)
6570 flags |= VIR_DOMAIN_AFFECT_LIVE;
6571 if (config)
6572 flags |= VIR_DOMAIN_AFFECT_CONFIG;
6573 if (maximum)
6574 flags |= VIR_DOMAIN_VCPU_MAXIMUM;
6575 if (guest)
6576 flags |= VIR_DOMAIN_VCPU_GUEST;
6578 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
6579 return false;
6581 if (all) {
6582 int conf_max = virshCPUCountCollect(ctl, dom,
6583 VIR_DOMAIN_AFFECT_CONFIG |
6584 VIR_DOMAIN_VCPU_MAXIMUM, true);
6585 int conf_cur = virshCPUCountCollect(ctl, dom,
6586 VIR_DOMAIN_AFFECT_CONFIG, true);
6587 int live_max = virshCPUCountCollect(ctl, dom,
6588 VIR_DOMAIN_AFFECT_LIVE |
6589 VIR_DOMAIN_VCPU_MAXIMUM, true);
6590 int live_cur = virshCPUCountCollect(ctl, dom,
6591 VIR_DOMAIN_AFFECT_LIVE, true);
6593 if (conf_max == -2 || conf_cur == -2 || live_max == -2 || live_cur == -2)
6594 goto cleanup;
6596 #define PRINT_COUNT(VAR, WHICH, STATE) if (VAR > 0) \
6597 vshPrint(ctl, "%-12s %-12s %3d\n", WHICH, STATE, VAR)
6598 PRINT_COUNT(conf_max, _("maximum"), _("config"));
6599 PRINT_COUNT(live_max, _("maximum"), _("live"));
6600 PRINT_COUNT(conf_cur, _("current"), _("config"));
6601 PRINT_COUNT(live_cur, _("current"), _("live"));
6602 #undef PRINT_COUNT
6604 } else {
6605 int count = virshCPUCountCollect(ctl, dom, flags, false);
6607 if (count < 0)
6608 goto cleanup;
6610 vshPrint(ctl, "%d\n", count);
6613 ret = true;
6615 cleanup:
6616 virshDomainFree(dom);
6617 return ret;
6621 * "vcpuinfo" command
6623 static const vshCmdInfo info_vcpuinfo[] = {
6624 {.name = "help",
6625 .data = N_("detailed domain vcpu information")
6627 {.name = "desc",
6628 .data = N_("Returns basic information about the domain virtual CPUs.")
6630 {.name = NULL}
6633 static const vshCmdOptDef opts_vcpuinfo[] = {
6634 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
6635 {.name = "pretty",
6636 .type = VSH_OT_BOOL,
6637 .help = N_("return human readable output")
6639 {.name = NULL}
6643 static int
6644 virshVcpuinfoPrintAffinity(vshControl *ctl,
6645 const unsigned char *cpumap,
6646 int maxcpu,
6647 bool pretty)
6649 char *str = NULL;
6650 size_t i;
6651 int ret = -1;
6653 vshPrint(ctl, "%-15s ", _("CPU Affinity:"));
6654 if (pretty) {
6655 if (!(str = virBitmapDataFormat(cpumap, VIR_CPU_MAPLEN(maxcpu))))
6656 goto cleanup;
6657 vshPrint(ctl, _("%s (out of %d)"), str, maxcpu);
6658 } else {
6659 for (i = 0; i < maxcpu; i++)
6660 vshPrint(ctl, "%c", VIR_CPU_USED(cpumap, i) ? 'y' : '-');
6662 vshPrint(ctl, "\n");
6664 ret = 0;
6666 cleanup:
6667 VIR_FREE(str);
6668 return ret;
6672 static virBitmapPtr
6673 virshDomainGetVcpuBitmap(vshControl *ctl,
6674 virDomainPtr dom,
6675 bool inactive)
6677 unsigned int flags = 0;
6678 virBitmapPtr ret = NULL;
6679 xmlDocPtr xml = NULL;
6680 xmlXPathContextPtr ctxt = NULL;
6681 xmlNodePtr *nodes = NULL;
6682 xmlNodePtr old;
6683 int nnodes;
6684 size_t i;
6685 unsigned int curvcpus = 0;
6686 unsigned int maxvcpus = 0;
6687 unsigned int vcpuid;
6688 char *online = NULL;
6690 if (inactive)
6691 flags |= VIR_DOMAIN_XML_INACTIVE;
6693 if (virshDomainGetXMLFromDom(ctl, dom, flags, &xml, &ctxt) < 0)
6694 goto cleanup;
6696 if (virXPathUInt("string(/domain/vcpu)", ctxt, &maxvcpus) < 0) {
6697 vshError(ctl, "%s", _("Failed to retrieve maximum vcpu count"));
6698 goto cleanup;
6701 ignore_value(virXPathUInt("string(/domain/vcpu/@current)", ctxt, &curvcpus));
6703 if (curvcpus == 0)
6704 curvcpus = maxvcpus;
6706 if (!(ret = virBitmapNew(maxvcpus)))
6707 goto cleanup;
6709 if ((nnodes = virXPathNodeSet("/domain/vcpus/vcpu", ctxt, &nodes)) <= 0) {
6710 /* if the specific vcpu state is missing provide a fallback */
6711 for (i = 0; i < curvcpus; i++)
6712 ignore_value(virBitmapSetBit(ret, i));
6714 goto cleanup;
6717 old = ctxt->node;
6719 for (i = 0; i < nnodes; i++) {
6720 ctxt->node = nodes[i];
6722 if (virXPathUInt("string(@id)", ctxt, &vcpuid) < 0 ||
6723 !(online = virXPathString("string(@enabled)", ctxt)))
6724 continue;
6726 if (STREQ(online, "yes"))
6727 ignore_value(virBitmapSetBit(ret, vcpuid));
6729 VIR_FREE(online);
6732 ctxt->node = old;
6734 if (virBitmapCountBits(ret) != curvcpus) {
6735 vshError(ctl, "%s", _("Failed to retrieve vcpu state bitmap"));
6736 virBitmapFree(ret);
6737 ret = NULL;
6740 cleanup:
6741 VIR_FREE(online);
6742 VIR_FREE(nodes);
6743 xmlXPathFreeContext(ctxt);
6744 xmlFreeDoc(xml);
6745 return ret;
6749 static bool
6750 virshVcpuinfoInactive(vshControl *ctl,
6751 virDomainPtr dom,
6752 int maxcpu,
6753 bool pretty)
6755 unsigned char *cpumaps = NULL;
6756 size_t cpumaplen;
6757 int ncpus;
6758 virBitmapPtr vcpus = NULL;
6759 ssize_t nextvcpu = -1;
6760 bool ret = false;
6761 bool first = true;
6763 if (!(vcpus = virshDomainGetVcpuBitmap(ctl, dom, true)))
6764 goto cleanup;
6766 cpumaplen = VIR_CPU_MAPLEN(maxcpu);
6767 cpumaps = vshMalloc(ctl, virBitmapSize(vcpus) * cpumaplen);
6769 if ((ncpus = virDomainGetVcpuPinInfo(dom, virBitmapSize(vcpus),
6770 cpumaps, cpumaplen,
6771 VIR_DOMAIN_AFFECT_CONFIG)) < 0)
6772 goto cleanup;
6774 while ((nextvcpu = virBitmapNextSetBit(vcpus, nextvcpu)) >= 0) {
6775 if (!first)
6776 vshPrint(ctl, "\n");
6777 first = false;
6779 vshPrint(ctl, "%-15s %zd\n", _("VCPU:"), nextvcpu);
6780 vshPrint(ctl, "%-15s %s\n", _("CPU:"), _("N/A"));
6781 vshPrint(ctl, "%-15s %s\n", _("State:"), _("N/A"));
6782 vshPrint(ctl, "%-15s %s\n", _("CPU time"), _("N/A"));
6784 if (virshVcpuinfoPrintAffinity(ctl,
6785 VIR_GET_CPUMAP(cpumaps, cpumaplen, nextvcpu),
6786 maxcpu, pretty) < 0)
6787 goto cleanup;
6790 ret = true;
6792 cleanup:
6793 virBitmapFree(vcpus);
6794 VIR_FREE(cpumaps);
6795 return ret;
6799 static bool
6800 cmdVcpuinfo(vshControl *ctl, const vshCmd *cmd)
6802 virDomainInfo info;
6803 virDomainPtr dom;
6804 virVcpuInfoPtr cpuinfo = NULL;
6805 unsigned char *cpumaps = NULL;
6806 int ncpus, maxcpu;
6807 size_t cpumaplen;
6808 bool ret = false;
6809 bool pretty = vshCommandOptBool(cmd, "pretty");
6810 int n;
6811 virshControlPtr priv = ctl->privData;
6813 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
6814 return false;
6816 if ((maxcpu = virshNodeGetCPUCount(priv->conn)) < 0)
6817 goto cleanup;
6819 if (virDomainGetInfo(dom, &info) != 0)
6820 goto cleanup;
6822 cpuinfo = vshMalloc(ctl, sizeof(virVcpuInfo)*info.nrVirtCpu);
6823 cpumaplen = VIR_CPU_MAPLEN(maxcpu);
6824 cpumaps = vshMalloc(ctl, info.nrVirtCpu * cpumaplen);
6826 if ((ncpus = virDomainGetVcpus(dom,
6827 cpuinfo, info.nrVirtCpu,
6828 cpumaps, cpumaplen)) < 0) {
6829 if (info.state != VIR_DOMAIN_SHUTOFF)
6830 goto cleanup;
6832 vshResetLibvirtError();
6834 /* for offline VMs we can return pinning information */
6835 ret = virshVcpuinfoInactive(ctl, dom, maxcpu, pretty);
6836 goto cleanup;
6839 for (n = 0; n < ncpus; n++) {
6840 vshPrint(ctl, "%-15s %d\n", _("VCPU:"), cpuinfo[n].number);
6841 vshPrint(ctl, "%-15s %d\n", _("CPU:"), cpuinfo[n].cpu);
6842 vshPrint(ctl, "%-15s %s\n", _("State:"),
6843 virshDomainVcpuStateToString(cpuinfo[n].state));
6844 if (cpuinfo[n].cpuTime != 0) {
6845 double cpuUsed = cpuinfo[n].cpuTime;
6847 cpuUsed /= 1000000000.0;
6849 vshPrint(ctl, "%-15s %.1lfs\n", _("CPU time:"), cpuUsed);
6852 if (virshVcpuinfoPrintAffinity(ctl, VIR_GET_CPUMAP(cpumaps, cpumaplen, n),
6853 maxcpu, pretty) < 0)
6854 goto cleanup;
6856 if (n < (ncpus - 1))
6857 vshPrint(ctl, "\n");
6860 ret = true;
6862 cleanup:
6863 VIR_FREE(cpumaps);
6864 VIR_FREE(cpuinfo);
6865 virshDomainFree(dom);
6866 return ret;
6870 * "vcpupin" command
6872 static const vshCmdInfo info_vcpupin[] = {
6873 {.name = "help",
6874 .data = N_("control or query domain vcpu affinity")
6876 {.name = "desc",
6877 .data = N_("Pin domain VCPUs to host physical CPUs.")
6879 {.name = NULL}
6882 static const vshCmdOptDef opts_vcpupin[] = {
6883 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
6884 {.name = "vcpu",
6885 .type = VSH_OT_INT,
6886 .help = N_("vcpu number")
6888 {.name = "cpulist",
6889 .type = VSH_OT_STRING,
6890 .flags = VSH_OFLAG_EMPTY_OK,
6891 .help = N_("host cpu number(s) to set, or omit option to query")
6893 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
6894 VIRSH_COMMON_OPT_DOMAIN_LIVE,
6895 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
6896 {.name = NULL}
6900 * Helper function to print vcpupin info.
6902 static bool
6903 virshPrintPinInfo(vshControl *ctl,
6904 unsigned char *cpumap,
6905 size_t cpumaplen)
6907 char *str = NULL;
6909 if (!(str = virBitmapDataFormat(cpumap, cpumaplen)))
6910 return false;
6912 vshPrint(ctl, "%s", str);
6913 VIR_FREE(str);
6914 return true;
6918 static bool
6919 virshVcpuPinQuery(vshControl *ctl,
6920 virDomainPtr dom,
6921 unsigned int vcpu,
6922 bool got_vcpu,
6923 int maxcpu,
6924 unsigned int flags)
6926 unsigned char *cpumap = NULL;
6927 unsigned int countFlags = flags | VIR_DOMAIN_VCPU_MAXIMUM;
6928 int cpumaplen;
6929 size_t i;
6930 int ncpus;
6931 bool ret = false;
6932 vshTablePtr table = NULL;
6934 if ((ncpus = virshCPUCountCollect(ctl, dom, countFlags, true)) < 0) {
6935 if (ncpus == -1) {
6936 if (flags & VIR_DOMAIN_AFFECT_LIVE)
6937 vshError(ctl, "%s", _("cannot get vcpupin for offline domain"));
6938 else
6939 vshError(ctl, "%s", _("cannot get vcpupin for transient domain"));
6941 goto cleanup;
6944 if (got_vcpu && vcpu >= ncpus) {
6945 if (flags & VIR_DOMAIN_AFFECT_LIVE ||
6946 (!(flags & VIR_DOMAIN_AFFECT_CONFIG) &&
6947 virDomainIsActive(dom) == 1))
6948 vshError(ctl,
6949 _("vcpu %d is out of range of live cpu count %d"),
6950 vcpu, ncpus);
6951 else
6952 vshError(ctl,
6953 _("vcpu %d is out of range of persistent cpu count %d"),
6954 vcpu, ncpus);
6955 goto cleanup;
6958 cpumaplen = VIR_CPU_MAPLEN(maxcpu);
6959 cpumap = vshMalloc(ctl, ncpus * cpumaplen);
6960 if ((ncpus = virDomainGetVcpuPinInfo(dom, ncpus, cpumap,
6961 cpumaplen, flags)) >= 0) {
6962 table = vshTableNew(_("VCPU"), _("CPU Affinity"), NULL);
6963 if (!table)
6964 goto cleanup;
6966 for (i = 0; i < ncpus; i++) {
6967 VIR_AUTOFREE(char *) pinInfo = NULL;
6968 VIR_AUTOFREE(char *) vcpuStr = NULL;
6969 if (got_vcpu && i != vcpu)
6970 continue;
6972 if (!(pinInfo = virBitmapDataFormat(VIR_GET_CPUMAP(cpumap, cpumaplen, i),
6973 cpumaplen)))
6974 goto cleanup;
6976 if (virAsprintf(&vcpuStr, "%zu", i) < 0)
6977 goto cleanup;
6979 if (vshTableRowAppend(table, vcpuStr, pinInfo, NULL) < 0)
6980 goto cleanup;
6983 vshTablePrintToStdout(table, ctl);
6986 ret = true;
6987 cleanup:
6988 vshTableFree(table);
6989 VIR_FREE(cpumap);
6990 return ret;
6994 static unsigned char *
6995 virshParseCPUList(vshControl *ctl, int *cpumaplen,
6996 const char *cpulist, int maxcpu)
6998 unsigned char *cpumap = NULL;
6999 virBitmapPtr map = NULL;
7001 if (cpulist[0] == 'r') {
7002 if (!(map = virBitmapNew(maxcpu)))
7003 return NULL;
7004 virBitmapSetAll(map);
7005 } else {
7006 if (virBitmapParse(cpulist, &map, 1024) < 0 ||
7007 virBitmapIsAllClear(map)) {
7008 vshError(ctl, _("Invalid cpulist '%s'"), cpulist);
7009 goto cleanup;
7011 int lastcpu = virBitmapLastSetBit(map);
7012 if (lastcpu >= maxcpu) {
7013 vshError(ctl, _("CPU %d in cpulist '%s' exceed the maxcpu %d"),
7014 lastcpu, cpulist, maxcpu);
7015 goto cleanup;
7019 if (virBitmapToData(map, &cpumap, cpumaplen) < 0)
7020 goto cleanup;
7022 cleanup:
7023 virBitmapFree(map);
7024 return cpumap;
7027 static bool
7028 cmdVcpuPin(vshControl *ctl, const vshCmd *cmd)
7030 virDomainPtr dom;
7031 unsigned int vcpu = 0;
7032 const char *cpulist = NULL;
7033 bool ret = false;
7034 unsigned char *cpumap = NULL;
7035 int cpumaplen;
7036 int maxcpu;
7037 bool config = vshCommandOptBool(cmd, "config");
7038 bool live = vshCommandOptBool(cmd, "live");
7039 bool current = vshCommandOptBool(cmd, "current");
7040 int got_vcpu;
7041 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
7042 virshControlPtr priv = ctl->privData;
7044 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
7045 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
7047 if (config)
7048 flags |= VIR_DOMAIN_AFFECT_CONFIG;
7049 if (live)
7050 flags |= VIR_DOMAIN_AFFECT_LIVE;
7052 if (vshCommandOptStringReq(ctl, cmd, "cpulist", &cpulist) < 0)
7053 return false;
7055 if (!cpulist)
7056 VSH_EXCLUSIVE_OPTIONS_VAR(live, config);
7058 if ((got_vcpu = vshCommandOptUInt(ctl, cmd, "vcpu", &vcpu)) < 0)
7059 return false;
7061 /* In pin mode, "vcpu" is necessary */
7062 if (cpulist && got_vcpu == 0) {
7063 vshError(ctl, "%s", _("vcpupin: Missing vCPU number in pin mode."));
7064 return false;
7067 if ((maxcpu = virshNodeGetCPUCount(priv->conn)) < 0)
7068 return false;
7070 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7071 return false;
7073 /* Query mode: show CPU affinity information then exit.*/
7074 if (!cpulist) {
7075 ret = virshVcpuPinQuery(ctl, dom, vcpu, got_vcpu, maxcpu, flags);
7076 goto cleanup;
7079 /* Pin mode: pinning specified vcpu to specified physical cpus*/
7080 if (!(cpumap = virshParseCPUList(ctl, &cpumaplen, cpulist, maxcpu)))
7081 goto cleanup;
7083 /* use old API without any explicit flags */
7084 if (flags == VIR_DOMAIN_AFFECT_CURRENT && !current) {
7085 if (virDomainPinVcpu(dom, vcpu, cpumap, cpumaplen) != 0)
7086 goto cleanup;
7087 } else {
7088 if (virDomainPinVcpuFlags(dom, vcpu, cpumap, cpumaplen, flags) != 0)
7089 goto cleanup;
7091 ret = true;
7093 cleanup:
7094 VIR_FREE(cpumap);
7095 virshDomainFree(dom);
7096 return ret;
7100 * "emulatorpin" command
7102 static const vshCmdInfo info_emulatorpin[] = {
7103 {.name = "help",
7104 .data = N_("control or query domain emulator affinity")
7106 {.name = "desc",
7107 .data = N_("Pin domain emulator threads to host physical CPUs.")
7109 {.name = NULL}
7112 static const vshCmdOptDef opts_emulatorpin[] = {
7113 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
7114 {.name = "cpulist",
7115 .type = VSH_OT_STRING,
7116 .flags = VSH_OFLAG_EMPTY_OK,
7117 .help = N_("host cpu number(s) to set, or omit option to query")
7119 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
7120 VIRSH_COMMON_OPT_DOMAIN_LIVE,
7121 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
7122 {.name = NULL}
7125 static bool
7126 cmdEmulatorPin(vshControl *ctl, const vshCmd *cmd)
7128 virDomainPtr dom;
7129 const char *cpulist = NULL;
7130 bool ret = false;
7131 unsigned char *cpumap = NULL;
7132 int cpumaplen;
7133 int maxcpu;
7134 bool config = vshCommandOptBool(cmd, "config");
7135 bool live = vshCommandOptBool(cmd, "live");
7136 bool current = vshCommandOptBool(cmd, "current");
7137 bool query = false; /* Query mode if no cpulist */
7138 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
7139 virshControlPtr priv = ctl->privData;
7141 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
7142 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
7144 if (config)
7145 flags |= VIR_DOMAIN_AFFECT_CONFIG;
7146 if (live)
7147 flags |= VIR_DOMAIN_AFFECT_LIVE;
7148 /* none of the options were specified */
7149 if (!current && !live && !config)
7150 flags = -1;
7152 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7153 return false;
7155 if (vshCommandOptStringReq(ctl, cmd, "cpulist", &cpulist) < 0) {
7156 virshDomainFree(dom);
7157 return false;
7159 query = !cpulist;
7161 if ((maxcpu = virshNodeGetCPUCount(priv->conn)) < 0) {
7162 virshDomainFree(dom);
7163 return false;
7166 /* Query mode: show CPU affinity information then exit.*/
7167 if (query) {
7168 /* When query mode and neither "live", "config" nor "current"
7169 * is specified, set VIR_DOMAIN_AFFECT_CURRENT as flags */
7170 if (flags == -1)
7171 flags = VIR_DOMAIN_AFFECT_CURRENT;
7173 cpumaplen = VIR_CPU_MAPLEN(maxcpu);
7174 cpumap = vshMalloc(ctl, cpumaplen);
7175 if (virDomainGetEmulatorPinInfo(dom, cpumap,
7176 cpumaplen, flags) >= 0) {
7177 vshPrintExtra(ctl, "%s %s\n", _("emulator:"), _("CPU Affinity"));
7178 vshPrintExtra(ctl, "----------------------------------\n");
7179 vshPrintExtra(ctl, " *: ");
7180 ret = virshPrintPinInfo(ctl, cpumap, cpumaplen);
7181 vshPrint(ctl, "\n");
7183 goto cleanup;
7186 /* Pin mode: pinning emulator threads to specified physical cpus*/
7187 if (!(cpumap = virshParseCPUList(ctl, &cpumaplen, cpulist, maxcpu)))
7188 goto cleanup;
7190 if (flags == -1)
7191 flags = VIR_DOMAIN_AFFECT_LIVE;
7193 if (virDomainPinEmulator(dom, cpumap, cpumaplen, flags) != 0)
7194 goto cleanup;
7196 ret = true;
7197 cleanup:
7198 VIR_FREE(cpumap);
7199 virshDomainFree(dom);
7200 return ret;
7204 * "setvcpus" command
7206 static const vshCmdInfo info_setvcpus[] = {
7207 {.name = "help",
7208 .data = N_("change number of virtual CPUs")
7210 {.name = "desc",
7211 .data = N_("Change the number of virtual CPUs in the guest domain.")
7213 {.name = NULL}
7216 static const vshCmdOptDef opts_setvcpus[] = {
7217 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
7218 {.name = "count",
7219 .type = VSH_OT_INT,
7220 .flags = VSH_OFLAG_REQ,
7221 .help = N_("number of virtual CPUs")
7223 {.name = "maximum",
7224 .type = VSH_OT_BOOL,
7225 .help = N_("set maximum limit on next boot")
7227 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
7228 VIRSH_COMMON_OPT_DOMAIN_LIVE,
7229 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
7230 {.name = "guest",
7231 .type = VSH_OT_BOOL,
7232 .help = N_("modify cpu state in the guest")
7234 {.name = "hotpluggable",
7235 .type = VSH_OT_BOOL,
7236 .help = N_("make added vcpus hot(un)pluggable")
7238 {.name = NULL}
7241 static bool
7242 cmdSetvcpus(vshControl *ctl, const vshCmd *cmd)
7244 virDomainPtr dom;
7245 unsigned int count = 0;
7246 bool ret = false;
7247 bool maximum = vshCommandOptBool(cmd, "maximum");
7248 bool config = vshCommandOptBool(cmd, "config");
7249 bool live = vshCommandOptBool(cmd, "live");
7250 bool current = vshCommandOptBool(cmd, "current");
7251 bool guest = vshCommandOptBool(cmd, "guest");
7252 bool hotpluggable = vshCommandOptBool(cmd, "hotpluggable");
7253 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
7255 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
7256 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
7257 VSH_EXCLUSIVE_OPTIONS_VAR(guest, config);
7259 VSH_REQUIRE_OPTION_VAR(maximum, config);
7261 if (config)
7262 flags |= VIR_DOMAIN_AFFECT_CONFIG;
7263 if (live)
7264 flags |= VIR_DOMAIN_AFFECT_LIVE;
7265 if (guest)
7266 flags |= VIR_DOMAIN_VCPU_GUEST;
7267 if (maximum)
7268 flags |= VIR_DOMAIN_VCPU_MAXIMUM;
7269 if (hotpluggable)
7270 flags |= VIR_DOMAIN_VCPU_HOTPLUGGABLE;
7272 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7273 return false;
7275 if (vshCommandOptUInt(ctl, cmd, "count", &count) < 0)
7276 goto cleanup;
7278 if (count == 0) {
7279 vshError(ctl, _("Can't set 0 processors for a VM"));
7280 goto cleanup;
7283 /* none of the options were specified */
7284 if (!current && flags == 0) {
7285 if (virDomainSetVcpus(dom, count) != 0)
7286 goto cleanup;
7287 } else {
7288 if (virDomainSetVcpusFlags(dom, count, flags) < 0)
7289 goto cleanup;
7292 ret = true;
7294 cleanup:
7295 virshDomainFree(dom);
7296 return ret;
7301 * "guestvcpus" command
7303 static const vshCmdInfo info_guestvcpus[] = {
7304 {.name = "help",
7305 .data = N_("query or modify state of vcpu in the guest (via agent)")
7307 {.name = "desc",
7308 .data = N_("Use the guest agent to query or set cpu state from guest's "
7309 "point of view")
7311 {.name = NULL}
7314 static const vshCmdOptDef opts_guestvcpus[] = {
7315 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
7316 {.name = "cpulist",
7317 .type = VSH_OT_STRING,
7318 .help = N_("list of cpus to enable or disable")
7320 {.name = "enable",
7321 .type = VSH_OT_BOOL,
7322 .help = N_("enable cpus specified by cpulist")
7324 {.name = "disable",
7325 .type = VSH_OT_BOOL,
7326 .help = N_("disable cpus specified by cpulist")
7328 {.name = NULL}
7331 static bool
7332 cmdGuestvcpus(vshControl *ctl, const vshCmd *cmd)
7334 virDomainPtr dom;
7335 bool enable = vshCommandOptBool(cmd, "enable");
7336 bool disable = vshCommandOptBool(cmd, "disable");
7337 virTypedParameterPtr params = NULL;
7338 unsigned int nparams = 0;
7339 const char *cpulist = NULL;
7340 int state = 0;
7341 size_t i;
7342 bool ret = false;
7344 VSH_EXCLUSIVE_OPTIONS_VAR(enable, disable);
7345 VSH_REQUIRE_OPTION("enable", "cpulist");
7346 VSH_REQUIRE_OPTION("disable", "cpulist");
7348 if (vshCommandOptStringReq(ctl, cmd, "cpulist", &cpulist))
7349 return false;
7351 if (cpulist && !(enable || disable)) {
7352 vshError(ctl, _("One of options --enable or --disable is required by "
7353 "option --cpulist"));
7354 return false;
7357 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7358 return false;
7360 if (enable)
7361 state = 1;
7363 if (cpulist) {
7364 if (virDomainSetGuestVcpus(dom, cpulist, state, 0) < 0)
7365 goto cleanup;
7366 } else {
7367 if (virDomainGetGuestVcpus(dom, &params, &nparams, 0) < 0)
7368 goto cleanup;
7370 for (i = 0; i < nparams; i++) {
7371 char *str = vshGetTypedParamValue(ctl, &params[i]);
7372 vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
7373 VIR_FREE(str);
7377 ret = true;
7379 cleanup:
7380 virTypedParamsFree(params, nparams);
7381 virshDomainFree(dom);
7382 return ret;
7387 * "setvcpu" command
7389 static const vshCmdInfo info_setvcpu[] = {
7390 {.name = "help",
7391 .data = N_("attach/detach vcpu or groups of threads")
7393 {.name = "desc",
7394 .data = N_("Add or remove vcpus")
7396 {.name = NULL}
7399 static const vshCmdOptDef opts_setvcpu[] = {
7400 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
7401 {.name = "vcpulist",
7402 .type = VSH_OT_DATA,
7403 .flags = VSH_OFLAG_REQ,
7404 .help = N_("ids of vcpus to manipulate")
7406 {.name = "enable",
7407 .type = VSH_OT_BOOL,
7408 .help = N_("enable cpus specified by cpumap")
7410 {.name = "disable",
7411 .type = VSH_OT_BOOL,
7412 .help = N_("disable cpus specified by cpumap")
7414 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
7415 VIRSH_COMMON_OPT_DOMAIN_LIVE,
7416 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
7417 {.name = NULL}
7420 static bool
7421 cmdSetvcpu(vshControl *ctl, const vshCmd *cmd)
7423 virDomainPtr dom;
7424 bool enable = vshCommandOptBool(cmd, "enable");
7425 bool disable = vshCommandOptBool(cmd, "disable");
7426 bool config = vshCommandOptBool(cmd, "config");
7427 bool live = vshCommandOptBool(cmd, "live");
7428 const char *vcpulist = NULL;
7429 int state = 0;
7430 bool ret = false;
7431 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
7433 VSH_EXCLUSIVE_OPTIONS_VAR(enable, disable);
7435 VSH_EXCLUSIVE_OPTIONS("current", "live");
7436 VSH_EXCLUSIVE_OPTIONS("current", "config");
7438 if (config)
7439 flags |= VIR_DOMAIN_AFFECT_CONFIG;
7440 if (live)
7441 flags |= VIR_DOMAIN_AFFECT_LIVE;
7443 if (!(enable || disable)) {
7444 vshError(ctl, "%s", _("one of --enable, --disable is required"));
7445 return false;
7448 if (vshCommandOptStringReq(ctl, cmd, "vcpulist", &vcpulist))
7449 return false;
7451 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7452 return false;
7454 if (enable)
7455 state = 1;
7457 if (virDomainSetVcpu(dom, vcpulist, state, flags) < 0)
7458 goto cleanup;
7460 ret = true;
7462 cleanup:
7463 virshDomainFree(dom);
7464 return ret;
7469 * "domblkthreshold" command
7471 static const vshCmdInfo info_domblkthreshold[] = {
7472 {.name = "help",
7473 .data = N_("set the threshold for block-threshold event for a given block "
7474 "device or it's backing chain element")
7476 {.name = "desc",
7477 .data = N_("set threshold for block-threshold event for a block device")
7479 {.name = NULL}
7482 static const vshCmdOptDef opts_domblkthreshold[] = {
7483 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
7484 {.name = "dev",
7485 .type = VSH_OT_DATA,
7486 .flags = VSH_OFLAG_REQ,
7487 .completer = virshDomainDiskTargetCompleter,
7488 .help = N_("device to set threshold for")
7490 {.name = "threshold",
7491 .type = VSH_OT_INT,
7492 .flags = VSH_OFLAG_REQ,
7493 .help = N_("threshold as a scaled number (by default bytes)")
7495 {.name = NULL}
7498 static bool
7499 cmdDomblkthreshold(vshControl *ctl, const vshCmd *cmd)
7501 unsigned long long threshold;
7502 const char *dev = NULL;
7503 virDomainPtr dom;
7504 bool ret = false;
7506 if (vshCommandOptStringReq(ctl, cmd, "dev", &dev))
7507 return false;
7509 if (vshCommandOptScaledInt(ctl, cmd, "threshold",
7510 &threshold, 1, ULLONG_MAX) < 0)
7511 return false;
7513 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7514 return false;
7516 if (virDomainSetBlockThreshold(dom, dev, threshold, 0) < 0)
7517 goto cleanup;
7519 ret = true;
7521 cleanup:
7522 virshDomainFree(dom);
7523 return ret;
7528 * "iothreadinfo" command
7530 static const vshCmdInfo info_iothreadinfo[] = {
7531 {.name = "help",
7532 .data = N_("view domain IOThreads")
7534 {.name = "desc",
7535 .data = N_("Returns basic information about the domain IOThreads.")
7537 {.name = NULL}
7539 static const vshCmdOptDef opts_iothreadinfo[] = {
7540 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
7541 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
7542 VIRSH_COMMON_OPT_DOMAIN_LIVE,
7543 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
7544 {.name = NULL}
7547 static bool
7548 cmdIOThreadInfo(vshControl *ctl, const vshCmd *cmd)
7550 virDomainPtr dom;
7551 bool config = vshCommandOptBool(cmd, "config");
7552 bool live = vshCommandOptBool(cmd, "live");
7553 bool current = vshCommandOptBool(cmd, "current");
7554 size_t niothreads = 0;
7555 virDomainIOThreadInfoPtr *info = NULL;
7556 size_t i;
7557 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
7558 vshTablePtr table = NULL;
7559 bool ret = false;
7560 int rc;
7562 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
7563 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
7565 if (config)
7566 flags |= VIR_DOMAIN_AFFECT_CONFIG;
7567 if (live)
7568 flags |= VIR_DOMAIN_AFFECT_LIVE;
7570 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7571 return false;
7573 if ((rc = virDomainGetIOThreadInfo(dom, &info, flags)) < 0) {
7574 vshError(ctl, _("Unable to get domain IOThreads information"));
7575 goto cleanup;
7577 niothreads = rc;
7579 if (niothreads == 0) {
7580 ret = true;
7581 vshPrintExtra(ctl, _("No IOThreads found for the domain"));
7582 goto cleanup;
7585 table = vshTableNew(_("IOThread ID"), _("CPU Affinity"), NULL);
7586 if (!table)
7587 goto cleanup;
7589 for (i = 0; i < niothreads; i++) {
7590 VIR_AUTOFREE(char *) pinInfo = NULL;
7591 VIR_AUTOFREE(char *) iothreadIdStr = NULL;
7593 if (virAsprintf(&iothreadIdStr, "%u", info[i]->iothread_id) < 0)
7594 goto cleanup;
7596 ignore_value(pinInfo = virBitmapDataFormat(info[i]->cpumap, info[i]->cpumaplen));
7598 if (vshTableRowAppend(table, iothreadIdStr, pinInfo ? pinInfo : "", NULL) < 0)
7599 goto cleanup;
7602 vshTablePrintToStdout(table, ctl);
7604 ret = true;
7606 cleanup:
7607 for (i = 0; i < niothreads; i++)
7608 virDomainIOThreadInfoFree(info[i]);
7609 VIR_FREE(info);
7610 vshTableFree(table);
7611 virshDomainFree(dom);
7612 return ret;
7616 * "iothreadpin" command
7618 static const vshCmdInfo info_iothreadpin[] = {
7619 {.name = "help",
7620 .data = N_("control domain IOThread affinity")
7622 {.name = "desc",
7623 .data = N_("Pin domain IOThreads to host physical CPUs.")
7625 {.name = NULL}
7628 static const vshCmdOptDef opts_iothreadpin[] = {
7629 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
7630 {.name = "iothread",
7631 .type = VSH_OT_INT,
7632 .flags = VSH_OFLAG_REQ,
7633 .help = N_("IOThread ID number")
7635 {.name = "cpulist",
7636 .type = VSH_OT_DATA,
7637 .flags = VSH_OFLAG_REQ,
7638 .help = N_("host cpu number(s) to set")
7640 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
7641 VIRSH_COMMON_OPT_DOMAIN_LIVE,
7642 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
7643 {.name = NULL}
7646 static bool
7647 cmdIOThreadPin(vshControl *ctl, const vshCmd *cmd)
7649 virDomainPtr dom;
7650 const char *cpulist = NULL;
7651 bool config = vshCommandOptBool(cmd, "config");
7652 bool live = vshCommandOptBool(cmd, "live");
7653 bool current = vshCommandOptBool(cmd, "current");
7654 unsigned int iothread_id = 0;
7655 int maxcpu;
7656 bool ret = false;
7657 unsigned char *cpumap = NULL;
7658 int cpumaplen;
7659 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
7660 virshControlPtr priv = ctl->privData;
7662 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
7663 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
7665 if (config)
7666 flags |= VIR_DOMAIN_AFFECT_CONFIG;
7667 if (live)
7668 flags |= VIR_DOMAIN_AFFECT_LIVE;
7670 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7671 return false;
7673 if (vshCommandOptUInt(ctl, cmd, "iothread", &iothread_id) < 0)
7674 goto cleanup;
7676 if (vshCommandOptStringReq(ctl, cmd, "cpulist", &cpulist) < 0)
7677 goto cleanup;
7679 if ((maxcpu = virshNodeGetCPUCount(priv->conn)) < 0)
7680 goto cleanup;
7682 if (!(cpumap = virshParseCPUList(ctl, &cpumaplen, cpulist, maxcpu)))
7683 goto cleanup;
7685 if (virDomainPinIOThread(dom, iothread_id,
7686 cpumap, cpumaplen, flags) != 0)
7687 goto cleanup;
7689 ret = true;
7691 cleanup:
7692 VIR_FREE(cpumap);
7693 virshDomainFree(dom);
7694 return ret;
7698 * "iothreadadd" command
7700 static const vshCmdInfo info_iothreadadd[] = {
7701 {.name = "help",
7702 .data = N_("add an IOThread to the guest domain")
7704 {.name = "desc",
7705 .data = N_("Add an IOThread to the guest domain.")
7707 {.name = NULL}
7710 static const vshCmdOptDef opts_iothreadadd[] = {
7711 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
7712 {.name = "id",
7713 .type = VSH_OT_INT,
7714 .flags = VSH_OFLAG_REQ,
7715 .help = N_("iothread for the new IOThread")
7717 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
7718 VIRSH_COMMON_OPT_DOMAIN_LIVE,
7719 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
7720 {.name = NULL}
7723 static bool
7724 cmdIOThreadAdd(vshControl *ctl, const vshCmd *cmd)
7726 virDomainPtr dom;
7727 int iothread_id = 0;
7728 bool ret = false;
7729 bool config = vshCommandOptBool(cmd, "config");
7730 bool live = vshCommandOptBool(cmd, "live");
7731 bool current = vshCommandOptBool(cmd, "current");
7732 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
7734 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
7735 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
7737 if (config)
7738 flags |= VIR_DOMAIN_AFFECT_CONFIG;
7739 if (live)
7740 flags |= VIR_DOMAIN_AFFECT_LIVE;
7742 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7743 return false;
7745 if (vshCommandOptInt(ctl, cmd, "id", &iothread_id) < 0)
7746 goto cleanup;
7747 if (iothread_id <= 0) {
7748 vshError(ctl, _("Invalid IOThread id value: '%d'"), iothread_id);
7749 goto cleanup;
7752 if (virDomainAddIOThread(dom, iothread_id, flags) < 0)
7753 goto cleanup;
7755 ret = true;
7757 cleanup:
7758 virshDomainFree(dom);
7759 return ret;
7764 * "iothreadset" command
7766 static const vshCmdInfo info_iothreadset[] = {
7767 {.name = "help",
7768 .data = N_("modifies an existing IOThread of the guest domain")
7770 {.name = "desc",
7771 .data = N_("Modifies an existing IOThread of the guest domain.")
7773 {.name = NULL}
7776 static const vshCmdOptDef opts_iothreadset[] = {
7777 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
7778 {.name = "id",
7779 .type = VSH_OT_INT,
7780 .flags = VSH_OFLAG_REQ,
7781 .help = N_("iothread id of existing IOThread")
7783 {.name = "poll-max-ns",
7784 .type = VSH_OT_INT,
7785 .help = N_("set the maximum IOThread polling time in ns")
7787 {.name = "poll-grow",
7788 .type = VSH_OT_INT,
7789 .help = N_("set the value to increase the IOThread polling time")
7791 {.name = "poll-shrink",
7792 .type = VSH_OT_INT,
7793 .help = N_("set the value for reduction of the IOThread polling time ")
7795 VIRSH_COMMON_OPT_DOMAIN_LIVE,
7796 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
7797 {.name = NULL}
7800 static bool
7801 cmdIOThreadSet(vshControl *ctl, const vshCmd *cmd)
7803 virDomainPtr dom;
7804 int id = 0;
7805 bool ret = false;
7806 bool live = vshCommandOptBool(cmd, "live");
7807 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
7808 virTypedParameterPtr params = NULL;
7809 int nparams = 0;
7810 int maxparams = 0;
7811 unsigned long long poll_max;
7812 unsigned int poll_val;
7813 int rc;
7815 if (live)
7816 flags |= VIR_DOMAIN_AFFECT_LIVE;
7818 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7819 return false;
7821 if (vshCommandOptInt(ctl, cmd, "id", &id) < 0)
7822 goto cleanup;
7823 if (id <= 0) {
7824 vshError(ctl, _("Invalid IOThread id value: '%d'"), id);
7825 goto cleanup;
7828 poll_val = 0;
7829 if ((rc = vshCommandOptULongLong(ctl, cmd, "poll-max-ns", &poll_max)) < 0)
7830 goto cleanup;
7831 if (rc > 0 && virTypedParamsAddULLong(&params, &nparams, &maxparams,
7832 VIR_DOMAIN_IOTHREAD_POLL_MAX_NS,
7833 poll_max) < 0)
7834 goto save_error;
7836 #define VSH_IOTHREAD_SET_UINT_PARAMS(opt, param) \
7837 poll_val = 0; \
7838 if ((rc = vshCommandOptUInt(ctl, cmd, opt, &poll_val)) < 0) \
7839 goto cleanup; \
7840 if (rc > 0 && \
7841 virTypedParamsAddUInt(&params, &nparams, &maxparams, \
7842 param, poll_val) < 0) \
7843 goto save_error;
7845 VSH_IOTHREAD_SET_UINT_PARAMS("poll-grow", VIR_DOMAIN_IOTHREAD_POLL_GROW)
7846 VSH_IOTHREAD_SET_UINT_PARAMS("poll-shrink", VIR_DOMAIN_IOTHREAD_POLL_SHRINK)
7848 #undef VSH_IOTHREAD_SET_UINT_PARAMS
7850 if (virDomainSetIOThreadParams(dom, id, params, nparams, flags) < 0)
7851 goto cleanup;
7853 ret = true;
7855 cleanup:
7856 virTypedParamsFree(params, nparams);
7857 virshDomainFree(dom);
7858 return ret;
7860 save_error:
7861 vshSaveLibvirtError();
7862 goto cleanup;
7867 * "iothreaddel" command
7869 static const vshCmdInfo info_iothreaddel[] = {
7870 {.name = "help",
7871 .data = N_("delete an IOThread from the guest domain")
7873 {.name = "desc",
7874 .data = N_("Delete an IOThread from the guest domain.")
7876 {.name = NULL}
7879 static const vshCmdOptDef opts_iothreaddel[] = {
7880 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
7881 {.name = "id",
7882 .type = VSH_OT_INT,
7883 .flags = VSH_OFLAG_REQ,
7884 .help = N_("iothread_id for the IOThread to delete")
7886 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
7887 VIRSH_COMMON_OPT_DOMAIN_LIVE,
7888 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
7889 {.name = NULL}
7892 static bool
7893 cmdIOThreadDel(vshControl *ctl, const vshCmd *cmd)
7895 virDomainPtr dom;
7896 int iothread_id = 0;
7897 bool ret = false;
7898 bool config = vshCommandOptBool(cmd, "config");
7899 bool live = vshCommandOptBool(cmd, "live");
7900 bool current = vshCommandOptBool(cmd, "current");
7901 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
7903 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
7904 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
7906 if (config)
7907 flags |= VIR_DOMAIN_AFFECT_CONFIG;
7908 if (live)
7909 flags |= VIR_DOMAIN_AFFECT_LIVE;
7911 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7912 return false;
7914 if (vshCommandOptInt(ctl, cmd, "id", &iothread_id) < 0)
7915 goto cleanup;
7916 if (iothread_id <= 0) {
7917 vshError(ctl, _("Invalid IOThread id value: '%d'"), iothread_id);
7918 goto cleanup;
7921 if (virDomainDelIOThread(dom, iothread_id, flags) < 0)
7922 goto cleanup;
7924 ret = true;
7926 cleanup:
7927 virshDomainFree(dom);
7928 return ret;
7932 * "cpu-stats" command
7934 static const vshCmdInfo info_cpu_stats[] = {
7935 {.name = "help",
7936 .data = N_("show domain cpu statistics")
7938 {.name = "desc",
7939 .data = N_("Display per-CPU and total statistics about the domain's CPUs")
7941 {.name = NULL}
7944 static const vshCmdOptDef opts_cpu_stats[] = {
7945 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
7946 {.name = "total",
7947 .type = VSH_OT_BOOL,
7948 .help = N_("Show total statistics only")
7950 {.name = "start",
7951 .type = VSH_OT_INT,
7952 .help = N_("Show statistics from this CPU")
7954 {.name = "count",
7955 .type = VSH_OT_INT,
7956 .help = N_("Number of shown CPUs at most")
7958 {.name = NULL}
7961 static void
7962 vshCPUStatsPrintField(vshControl *ctl,
7963 virTypedParameterPtr param)
7965 vshPrint(ctl, "\t%-12s ", param->field);
7966 if ((STREQ(param->field, VIR_DOMAIN_CPU_STATS_CPUTIME) ||
7967 STREQ(param->field, VIR_DOMAIN_CPU_STATS_VCPUTIME) ||
7968 STREQ(param->field, VIR_DOMAIN_CPU_STATS_USERTIME) ||
7969 STREQ(param->field, VIR_DOMAIN_CPU_STATS_SYSTEMTIME)) &&
7970 param->type == VIR_TYPED_PARAM_ULLONG) {
7971 vshPrint(ctl, "%9lld.%09lld seconds\n",
7972 param->value.ul / 1000000000,
7973 param->value.ul % 1000000000);
7974 } else {
7975 char *s = vshGetTypedParamValue(ctl, param);
7976 vshPrint(ctl, "%s\n", s);
7977 VIR_FREE(s);
7981 static bool
7982 cmdCPUStats(vshControl *ctl, const vshCmd *cmd)
7984 virDomainPtr dom;
7985 virTypedParameterPtr params = NULL;
7986 int max_id, cpu = 0, show_count = -1, nparams = 0, stats_per_cpu;
7987 size_t i, j;
7988 bool show_total = false, show_per_cpu = false;
7989 bool ret = false;
7990 int rv = 0;
7992 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7993 return false;
7995 show_total = vshCommandOptBool(cmd, "total");
7997 if ((rv = vshCommandOptInt(ctl, cmd, "start", &cpu)) < 0) {
7998 goto cleanup;
7999 } else if (rv > 0) {
8000 if (cpu < 0) {
8001 vshError(ctl, "%s", _("Invalid value for start CPU"));
8002 goto cleanup;
8004 show_per_cpu = true;
8007 if ((rv = vshCommandOptInt(ctl, cmd, "count", &show_count)) < 0) {
8008 goto cleanup;
8009 } else if (rv > 0) {
8010 if (show_count < 0) {
8011 vshError(ctl, "%s", _("Invalid value for number of CPUs to show"));
8012 goto cleanup;
8014 show_per_cpu = true;
8017 /* default show per_cpu and total */
8018 if (!show_total && !show_per_cpu) {
8019 show_total = true;
8020 show_per_cpu = true;
8023 if (!show_per_cpu) /* show total stats only */
8024 goto do_show_total;
8026 /* get number of cpus on the node */
8027 if ((max_id = virDomainGetCPUStats(dom, NULL, 0, 0, 0, 0)) < 0)
8028 goto failed_stats;
8030 if (cpu >= max_id) {
8031 vshError(ctl, "Start CPU %d is out of range (min: 0, max: %d)",
8032 cpu, max_id - 1);
8033 goto cleanup;
8036 if (show_count < 0 || show_count > max_id) {
8037 if (show_count > max_id)
8038 vshPrint(ctl, _("Only %d CPUs available to show\n"), max_id);
8039 show_count = max_id - cpu;
8042 /* get percpu information */
8043 if ((nparams = virDomainGetCPUStats(dom, NULL, 0, 0, 1, 0)) < 0)
8044 goto failed_stats;
8046 if (!nparams) {
8047 vshPrint(ctl, "%s", _("No per-CPU stats available"));
8048 if (show_total)
8049 goto do_show_total;
8050 goto cleanup;
8053 if (VIR_ALLOC_N(params, nparams * MIN(show_count, 128)) < 0)
8054 goto cleanup;
8056 while (show_count) {
8057 int ncpus = MIN(show_count, 128);
8059 if (virDomainGetCPUStats(dom, params, nparams, cpu, ncpus, 0) < 0)
8060 goto failed_stats;
8062 for (i = 0; i < ncpus; i++) {
8063 if (params[i * nparams].type == 0) /* this cpu is not in the map */
8064 continue;
8065 vshPrint(ctl, "CPU%zu:\n", cpu + i);
8067 for (j = 0; j < nparams; j++)
8068 vshCPUStatsPrintField(ctl, params + (i * nparams + j));
8070 cpu += ncpus;
8071 show_count -= ncpus;
8072 virTypedParamsClear(params, nparams * ncpus);
8074 VIR_FREE(params);
8076 if (!show_total) {
8077 ret = true;
8078 goto cleanup;
8081 do_show_total:
8082 /* get supported num of parameter for total statistics */
8083 if ((nparams = virDomainGetCPUStats(dom, NULL, 0, -1, 1, 0)) < 0)
8084 goto failed_stats;
8086 if (!nparams) {
8087 vshPrint(ctl, "%s", _("No total stats available"));
8088 goto cleanup;
8091 if (VIR_ALLOC_N(params, nparams) < 0)
8092 goto cleanup;
8094 /* passing start_cpu == -1 gives us domain's total status */
8095 if ((stats_per_cpu = virDomainGetCPUStats(dom, params, nparams,
8096 -1, 1, 0)) < 0)
8097 goto failed_stats;
8099 vshPrint(ctl, _("Total:\n"));
8100 for (i = 0; i < stats_per_cpu; i++)
8101 vshCPUStatsPrintField(ctl, params + i);
8103 ret = true;
8105 cleanup:
8106 virTypedParamsFree(params, nparams);
8107 virshDomainFree(dom);
8108 return ret;
8110 failed_stats:
8111 vshError(ctl, _("Failed to retrieve CPU statistics for domain '%s'"),
8112 virDomainGetName(dom));
8113 goto cleanup;
8117 * "create" command
8119 static const vshCmdInfo info_create[] = {
8120 {.name = "help",
8121 .data = N_("create a domain from an XML file")
8123 {.name = "desc",
8124 .data = N_("Create a domain.")
8126 {.name = NULL}
8129 static const vshCmdOptDef opts_create[] = {
8130 VIRSH_COMMON_OPT_FILE(N_("file containing an XML domain description")),
8131 #ifndef WIN32
8132 {.name = "console",
8133 .type = VSH_OT_BOOL,
8134 .help = N_("attach to console after creation")
8136 #endif
8137 {.name = "paused",
8138 .type = VSH_OT_BOOL,
8139 .help = N_("leave the guest paused after creation")
8141 {.name = "autodestroy",
8142 .type = VSH_OT_BOOL,
8143 .help = N_("automatically destroy the guest when virsh disconnects")
8145 {.name = "pass-fds",
8146 .type = VSH_OT_STRING,
8147 .help = N_("pass file descriptors N,M,... to the guest")
8149 {.name = "validate",
8150 .type = VSH_OT_BOOL,
8151 .help = N_("validate the XML against the schema")
8153 {.name = NULL}
8156 static bool
8157 cmdCreate(vshControl *ctl, const vshCmd *cmd)
8159 virDomainPtr dom;
8160 const char *from = NULL;
8161 bool ret = false;
8162 char *buffer;
8163 #ifndef WIN32
8164 bool console = vshCommandOptBool(cmd, "console");
8165 #endif
8166 unsigned int flags = 0;
8167 size_t nfds = 0;
8168 int *fds = NULL;
8169 virshControlPtr priv = ctl->privData;
8171 if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
8172 return false;
8174 if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0)
8175 return false;
8177 if (cmdStartGetFDs(ctl, cmd, &nfds, &fds) < 0)
8178 goto cleanup;
8180 if (vshCommandOptBool(cmd, "paused"))
8181 flags |= VIR_DOMAIN_START_PAUSED;
8182 if (vshCommandOptBool(cmd, "autodestroy"))
8183 flags |= VIR_DOMAIN_START_AUTODESTROY;
8184 if (vshCommandOptBool(cmd, "validate"))
8185 flags |= VIR_DOMAIN_START_VALIDATE;
8187 if (nfds)
8188 dom = virDomainCreateXMLWithFiles(priv->conn, buffer, nfds, fds, flags);
8189 else
8190 dom = virDomainCreateXML(priv->conn, buffer, flags);
8192 if (!dom) {
8193 vshError(ctl, _("Failed to create domain from %s"), from);
8194 goto cleanup;
8197 vshPrintExtra(ctl, _("Domain %s created from %s\n"),
8198 virDomainGetName(dom), from);
8199 #ifndef WIN32
8200 if (console)
8201 cmdRunConsole(ctl, dom, NULL, 0);
8202 #endif
8203 virshDomainFree(dom);
8204 ret = true;
8206 cleanup:
8207 VIR_FREE(buffer);
8208 VIR_FREE(fds);
8209 return ret;
8213 * "define" command
8215 static const vshCmdInfo info_define[] = {
8216 {.name = "help",
8217 .data = N_("define (but don't start) a domain from an XML file")
8219 {.name = "desc",
8220 .data = N_("Define a domain.")
8222 {.name = NULL}
8225 static const vshCmdOptDef opts_define[] = {
8226 VIRSH_COMMON_OPT_FILE(N_("file containing an XML domain description")),
8227 {.name = "validate",
8228 .type = VSH_OT_BOOL,
8229 .help = N_("validate the XML against the schema")
8231 {.name = NULL}
8234 static bool
8235 cmdDefine(vshControl *ctl, const vshCmd *cmd)
8237 virDomainPtr dom;
8238 const char *from = NULL;
8239 bool ret = true;
8240 char *buffer;
8241 unsigned int flags = 0;
8242 virshControlPtr priv = ctl->privData;
8244 if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
8245 return false;
8247 if (vshCommandOptBool(cmd, "validate"))
8248 flags |= VIR_DOMAIN_DEFINE_VALIDATE;
8250 if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0)
8251 return false;
8253 if (flags)
8254 dom = virDomainDefineXMLFlags(priv->conn, buffer, flags);
8255 else
8256 dom = virDomainDefineXML(priv->conn, buffer);
8257 VIR_FREE(buffer);
8259 if (dom != NULL) {
8260 vshPrintExtra(ctl, _("Domain %s defined from %s\n"),
8261 virDomainGetName(dom), from);
8262 virshDomainFree(dom);
8263 } else {
8264 vshError(ctl, _("Failed to define domain from %s"), from);
8265 ret = false;
8267 return ret;
8271 * "destroy" command
8273 static const vshCmdInfo info_destroy[] = {
8274 {.name = "help",
8275 .data = N_("destroy (stop) a domain")
8277 {.name = "desc",
8278 .data = N_("Forcefully stop a given domain, but leave its resources intact.")
8280 {.name = NULL}
8283 static const vshCmdOptDef opts_destroy[] = {
8284 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
8285 {.name = "graceful",
8286 .type = VSH_OT_BOOL,
8287 .help = N_("terminate gracefully")
8289 {.name = NULL}
8292 static bool
8293 cmdDestroy(vshControl *ctl, const vshCmd *cmd)
8295 virDomainPtr dom;
8296 bool ret = true;
8297 const char *name;
8298 unsigned int flags = 0;
8299 int result;
8301 if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
8302 return false;
8304 if (vshCommandOptBool(cmd, "graceful"))
8305 flags |= VIR_DOMAIN_DESTROY_GRACEFUL;
8307 if (flags)
8308 result = virDomainDestroyFlags(dom, VIR_DOMAIN_DESTROY_GRACEFUL);
8309 else
8310 result = virDomainDestroy(dom);
8312 if (result == 0) {
8313 vshPrintExtra(ctl, _("Domain %s destroyed\n"), name);
8314 } else {
8315 vshError(ctl, _("Failed to destroy domain %s"), name);
8316 ret = false;
8319 virshDomainFree(dom);
8320 return ret;
8324 * "desc" command for managing domain description and title
8326 static const vshCmdInfo info_desc[] = {
8327 {.name = "help",
8328 .data = N_("show or set domain's description or title")
8330 {.name = "desc",
8331 .data = N_("Allows setting or modifying the description or title of "
8332 "a domain.")
8334 {.name = NULL}
8337 static const vshCmdOptDef opts_desc[] = {
8338 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
8339 VIRSH_COMMON_OPT_LIVE(N_("modify/get running state")),
8340 VIRSH_COMMON_OPT_CONFIG(N_("modify/get persistent configuration")),
8341 VIRSH_COMMON_OPT_CURRENT(N_("modify/get current state configuration")),
8342 {.name = "title",
8343 .type = VSH_OT_BOOL,
8344 .help = N_("modify/get the title instead of description")
8346 {.name = "edit",
8347 .type = VSH_OT_BOOL,
8348 .help = N_("open an editor to modify the description")
8350 {.name = "new-desc",
8351 .type = VSH_OT_ARGV,
8352 .help = N_("message")
8354 {.name = NULL}
8357 static bool
8358 cmdDesc(vshControl *ctl, const vshCmd *cmd)
8360 virDomainPtr dom;
8361 bool config = vshCommandOptBool(cmd, "config");
8362 bool live = vshCommandOptBool(cmd, "live");
8363 bool current = vshCommandOptBool(cmd, "current");
8365 bool title = vshCommandOptBool(cmd, "title");
8366 bool edit = vshCommandOptBool(cmd, "edit");
8368 int state;
8369 int type;
8370 char *desc = NULL;
8371 char *desc_edited = NULL;
8372 char *tmp = NULL;
8373 char *tmpstr;
8374 const vshCmdOpt *opt = NULL;
8375 virBuffer buf = VIR_BUFFER_INITIALIZER;
8376 bool ret = false;
8377 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
8379 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
8380 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
8382 if (config)
8383 flags |= VIR_DOMAIN_AFFECT_CONFIG;
8384 if (live)
8385 flags |= VIR_DOMAIN_AFFECT_LIVE;
8387 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
8388 return false;
8390 if ((state = virshDomainState(ctl, dom, NULL)) < 0)
8391 goto cleanup;
8393 if (title)
8394 type = VIR_DOMAIN_METADATA_TITLE;
8395 else
8396 type = VIR_DOMAIN_METADATA_DESCRIPTION;
8398 while ((opt = vshCommandOptArgv(ctl, cmd, opt)))
8399 virBufferAsprintf(&buf, "%s ", opt->data);
8401 virBufferTrim(&buf, " ", -1);
8403 if (virBufferError(&buf)) {
8404 vshError(ctl, "%s", _("Failed to collect new description/title"));
8405 goto cleanup;
8407 desc = virBufferContentAndReset(&buf);
8409 if (edit || desc) {
8410 if (!desc) {
8411 desc = virshGetDomainDescription(ctl, dom, title,
8412 config?VIR_DOMAIN_XML_INACTIVE:0);
8413 if (!desc)
8414 goto cleanup;
8417 if (edit) {
8418 /* Create and open the temporary file. */
8419 if (!(tmp = vshEditWriteToTempFile(ctl, desc)))
8420 goto cleanup;
8422 /* Start the editor. */
8423 if (vshEditFile(ctl, tmp) == -1)
8424 goto cleanup;
8426 /* Read back the edited file. */
8427 if (!(desc_edited = vshEditReadBackFile(ctl, tmp)))
8428 goto cleanup;
8430 /* strip a possible newline at the end of file; some
8431 * editors enforce a newline, this makes editing the title
8432 * more convenient */
8433 if (title &&
8434 (tmpstr = strrchr(desc_edited, '\n')) &&
8435 *(tmpstr+1) == '\0')
8436 *tmpstr = '\0';
8438 /* Compare original XML with edited. Has it changed at all? */
8439 if (STREQ(desc, desc_edited)) {
8440 vshPrintExtra(ctl, "%s",
8441 title ? _("Domain title not changed\n") :
8442 _("Domain description not changed\n"));
8443 ret = true;
8444 goto cleanup;
8447 VIR_FREE(desc);
8448 VIR_STEAL_PTR(desc, desc_edited);
8451 if (virDomainSetMetadata(dom, type, desc, NULL, NULL, flags) < 0) {
8452 vshError(ctl, "%s",
8453 title ? _("Failed to set new domain title") :
8454 _("Failed to set new domain description"));
8455 goto cleanup;
8457 vshPrintExtra(ctl, "%s",
8458 title ? _("Domain title updated successfully") :
8459 _("Domain description updated successfully"));
8460 } else {
8461 desc = virshGetDomainDescription(ctl, dom, title,
8462 config?VIR_DOMAIN_XML_INACTIVE:0);
8463 if (!desc)
8464 goto cleanup;
8466 if (strlen(desc) > 0)
8467 vshPrint(ctl, "%s", desc);
8468 else
8469 vshPrintExtra(ctl,
8470 title ? _("No title for domain: %s") :
8471 _("No description for domain: %s"),
8472 virDomainGetName(dom));
8475 ret = true;
8476 cleanup:
8477 VIR_FREE(desc_edited);
8478 VIR_FREE(desc);
8479 if (tmp) {
8480 unlink(tmp);
8481 VIR_FREE(tmp);
8483 virshDomainFree(dom);
8484 return ret;
8488 static const vshCmdInfo info_metadata[] = {
8489 {.name = "help",
8490 .data = N_("show or set domain's custom XML metadata")
8492 {.name = "desc",
8493 .data = N_("Shows or modifies the XML metadata of a domain.")
8495 {.name = NULL}
8498 static const vshCmdOptDef opts_metadata[] = {
8499 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
8500 {.name = "uri",
8501 .type = VSH_OT_DATA,
8502 .flags = VSH_OFLAG_REQ,
8503 .help = N_("URI of the namespace")
8505 VIRSH_COMMON_OPT_LIVE(N_("modify/get running state")),
8506 VIRSH_COMMON_OPT_CONFIG(N_("modify/get persistent configuration")),
8507 VIRSH_COMMON_OPT_CURRENT(N_("modify/get current state configuration")),
8508 {.name = "edit",
8509 .type = VSH_OT_BOOL,
8510 .help = N_("use an editor to change the metadata")
8512 {.name = "key",
8513 .type = VSH_OT_STRING,
8514 .help = N_("key to be used as a namespace identifier"),
8516 {.name = "set",
8517 .type = VSH_OT_STRING,
8518 .help = N_("new metadata to set"),
8520 {.name = "remove",
8521 .type = VSH_OT_BOOL,
8522 .help = N_("remove the metadata corresponding to an uri")
8524 {.name = NULL}
8528 /* helper to add new metadata using the --edit option */
8529 static char *
8530 virshDomainGetEditMetadata(vshControl *ctl,
8531 virDomainPtr dom,
8532 const char *uri,
8533 unsigned int flags)
8535 char *ret;
8537 if (!(ret = virDomainGetMetadata(dom, VIR_DOMAIN_METADATA_ELEMENT,
8538 uri, flags))) {
8539 vshResetLibvirtError();
8540 ret = vshStrdup(ctl, "\n");
8543 return ret;
8547 static bool
8548 cmdMetadata(vshControl *ctl, const vshCmd *cmd)
8550 virDomainPtr dom;
8551 bool config = vshCommandOptBool(cmd, "config");
8552 bool live = vshCommandOptBool(cmd, "live");
8553 bool current = vshCommandOptBool(cmd, "current");
8554 bool edit = vshCommandOptBool(cmd, "edit");
8555 bool rem = vshCommandOptBool(cmd, "remove");
8556 const char *set = NULL;
8557 const char *uri = NULL;
8558 const char *key = NULL;
8559 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
8560 bool ret = false;
8562 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
8563 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
8564 VSH_EXCLUSIVE_OPTIONS("edit", "set");
8565 VSH_EXCLUSIVE_OPTIONS("remove", "set");
8566 VSH_EXCLUSIVE_OPTIONS("remove", "edit");
8568 if (config)
8569 flags |= VIR_DOMAIN_AFFECT_CONFIG;
8570 if (live)
8571 flags |= VIR_DOMAIN_AFFECT_LIVE;
8573 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
8574 return false;
8576 if (vshCommandOptStringReq(ctl, cmd, "uri", &uri) < 0 ||
8577 vshCommandOptStringReq(ctl, cmd, "key", &key) < 0 ||
8578 vshCommandOptStringReq(ctl, cmd, "set", &set) < 0)
8579 goto cleanup;
8581 if ((set || edit) && !key) {
8582 vshError(ctl, "%s",
8583 _("namespace key is required when modifying metadata"));
8584 goto cleanup;
8587 if (set || rem) {
8588 if (virDomainSetMetadata(dom, VIR_DOMAIN_METADATA_ELEMENT,
8589 set, key, uri, flags))
8590 goto cleanup;
8592 if (rem)
8593 vshPrintExtra(ctl, "%s\n", _("Metadata removed"));
8594 else
8595 vshPrintExtra(ctl, "%s\n", _("Metadata modified"));
8596 } else if (edit) {
8597 #define EDIT_GET_XML \
8598 virshDomainGetEditMetadata(ctl, dom, uri, flags)
8599 #define EDIT_NOT_CHANGED \
8600 do { \
8601 vshPrintExtra(ctl, "%s", _("Metadata not changed")); \
8602 ret = true; \
8603 goto edit_cleanup; \
8604 } while (0)
8606 #define EDIT_DEFINE \
8607 (virDomainSetMetadata(dom, VIR_DOMAIN_METADATA_ELEMENT, doc_edited, \
8608 key, uri, flags) == 0)
8609 #include "virsh-edit.c"
8611 vshPrintExtra(ctl, "%s\n", _("Metadata modified"));
8612 } else {
8613 char *data;
8614 /* get */
8615 if (!(data = virDomainGetMetadata(dom, VIR_DOMAIN_METADATA_ELEMENT,
8616 uri, flags)))
8617 goto cleanup;
8619 vshPrint(ctl, "%s\n", data);
8620 VIR_FREE(data);
8623 ret = true;
8625 cleanup:
8626 virshDomainFree(dom);
8627 return ret;
8632 * "inject-nmi" command
8634 static const vshCmdInfo info_inject_nmi[] = {
8635 {.name = "help",
8636 .data = N_("Inject NMI to the guest")
8638 {.name = "desc",
8639 .data = N_("Inject NMI to the guest domain.")
8641 {.name = NULL}
8644 static const vshCmdOptDef opts_inject_nmi[] = {
8645 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
8646 {.name = NULL}
8649 static bool
8650 cmdInjectNMI(vshControl *ctl, const vshCmd *cmd)
8652 virDomainPtr dom;
8653 bool ret = true;
8655 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
8656 return false;
8658 if (virDomainInjectNMI(dom, 0) < 0)
8659 ret = false;
8661 virshDomainFree(dom);
8662 return ret;
8666 * "send-key" command
8668 static const vshCmdInfo info_send_key[] = {
8669 {.name = "help",
8670 .data = N_("Send keycodes to the guest")
8672 {.name = "desc",
8673 .data = N_("Send keycodes (integers or symbolic names) to the guest")
8675 {.name = NULL}
8678 static const vshCmdOptDef opts_send_key[] = {
8679 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
8680 {.name = "codeset",
8681 .type = VSH_OT_STRING,
8682 .flags = VSH_OFLAG_REQ_OPT,
8683 .help = N_("the codeset of keycodes, default:linux")
8685 {.name = "holdtime",
8686 .type = VSH_OT_INT,
8687 .flags = VSH_OFLAG_REQ_OPT,
8688 .help = N_("the time (in milliseconds) how long the keys will be held")
8690 {.name = "keycode",
8691 .type = VSH_OT_ARGV,
8692 .flags = VSH_OFLAG_REQ,
8693 .help = N_("the key code")
8695 {.name = NULL}
8698 static int
8699 virshKeyCodeGetInt(const char *key_name)
8701 unsigned int val;
8703 if (virStrToLong_uip(key_name, NULL, 0, &val) < 0 || val > 0xffff)
8704 return -1;
8705 return val;
8708 static bool
8709 cmdSendKey(vshControl *ctl, const vshCmd *cmd)
8711 virDomainPtr dom;
8712 bool ret = false;
8713 const char *codeset_option;
8714 int codeset;
8715 unsigned int holdtime = 0;
8716 int count = 0;
8717 const vshCmdOpt *opt = NULL;
8718 int keycode;
8719 unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS];
8721 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
8722 return false;
8724 if (vshCommandOptStringQuiet(ctl, cmd, "codeset", &codeset_option) <= 0)
8725 codeset_option = "linux";
8727 if (vshCommandOptUInt(ctl, cmd, "holdtime", &holdtime) < 0)
8728 goto cleanup;
8730 /* The qnum codeset was originally called rfb, so we need to keep
8731 * accepting the old name for backwards compatibility reasons */
8732 if (STREQ(codeset_option, "rfb"))
8733 codeset_option = "qnum";
8735 codeset = virKeycodeSetTypeFromString(codeset_option);
8736 if (codeset < 0) {
8737 vshError(ctl, _("unknown codeset: '%s'"), codeset_option);
8738 goto cleanup;
8741 while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
8742 if (count == VIR_DOMAIN_SEND_KEY_MAX_KEYS) {
8743 vshError(ctl, _("too many keycodes"));
8744 goto cleanup;
8747 if ((keycode = virshKeyCodeGetInt(opt->data)) < 0) {
8748 if ((keycode = virKeycodeValueFromString(codeset, opt->data)) < 0) {
8749 vshError(ctl, _("invalid keycode: '%s'"), opt->data);
8750 goto cleanup;
8754 keycodes[count] = keycode;
8755 count++;
8758 if (!(virDomainSendKey(dom, codeset, holdtime, keycodes, count, 0) < 0))
8759 ret = true;
8761 cleanup:
8762 virshDomainFree(dom);
8763 return ret;
8767 * "send-process-signal" command
8769 static const vshCmdInfo info_send_process_signal[] = {
8770 {.name = "help",
8771 .data = N_("Send signals to processes")
8773 {.name = "desc",
8774 .data = N_("Send signals to processes in the guest")
8776 {.name = NULL}
8779 static const vshCmdOptDef opts_send_process_signal[] = {
8780 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
8781 {.name = "pid",
8782 .type = VSH_OT_DATA,
8783 .flags = VSH_OFLAG_REQ,
8784 .help = N_("the process ID")
8786 {.name = "signame",
8787 .type = VSH_OT_DATA,
8788 .flags = VSH_OFLAG_REQ,
8789 .help = N_("the signal number or name")
8791 {.name = NULL}
8794 VIR_ENUM_DECL(virDomainProcessSignal);
8795 VIR_ENUM_IMPL(virDomainProcessSignal,
8796 VIR_DOMAIN_PROCESS_SIGNAL_LAST,
8797 "nop", "hup", "int", "quit", "ill", /* 0-4 */
8798 "trap", "abrt", "bus", "fpe", "kill", /* 5-9 */
8799 "usr1", "segv", "usr2", "pipe", "alrm", /* 10-14 */
8800 "term", "stkflt", "chld", "cont", "stop", /* 15-19 */
8801 "tstp", "ttin", "ttou", "urg", "xcpu", /* 20-24 */
8802 "xfsz", "vtalrm", "prof", "winch", "poll", /* 25-29 */
8803 "pwr", "sys", "rt0", "rt1", "rt2", /* 30-34 */
8804 "rt3", "rt4", "rt5", "rt6", "rt7", /* 35-39 */
8805 "rt8", "rt9", "rt10", "rt11", "rt12", /* 40-44 */
8806 "rt13", "rt14", "rt15", "rt16", "rt17", /* 45-49 */
8807 "rt18", "rt19", "rt20", "rt21", "rt22", /* 50-54 */
8808 "rt23", "rt24", "rt25", "rt26", "rt27", /* 55-59 */
8809 "rt28", "rt29", "rt30", "rt31", "rt32"); /* 60-64 */
8811 static int getSignalNumber(vshControl *ctl, const char *signame)
8813 size_t i;
8814 int signum;
8815 char *lower = vshStrdup(ctl, signame);
8816 char *tmp = lower;
8818 for (i = 0; signame[i]; i++)
8819 lower[i] = c_tolower(signame[i]);
8821 if (virStrToLong_i(lower, NULL, 10, &signum) >= 0)
8822 goto cleanup;
8824 if (STRPREFIX(lower, "sig_"))
8825 lower += 4;
8826 else if (STRPREFIX(lower, "sig"))
8827 lower += 3;
8829 if ((signum = virDomainProcessSignalTypeFromString(lower)) >= 0)
8830 goto cleanup;
8832 signum = -1;
8833 cleanup:
8834 VIR_FREE(tmp);
8835 return signum;
8838 static bool
8839 cmdSendProcessSignal(vshControl *ctl, const vshCmd *cmd)
8841 virDomainPtr dom;
8842 bool ret = false;
8843 const char *signame;
8844 long long pid_value;
8845 int signum;
8847 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
8848 return false;
8850 if (vshCommandOptLongLong(ctl, cmd, "pid", &pid_value) < 0)
8851 goto cleanup;
8853 if (vshCommandOptStringReq(ctl, cmd, "signame", &signame) < 0)
8854 goto cleanup;
8856 if ((signum = getSignalNumber(ctl, signame)) < 0) {
8857 vshError(ctl, _("malformed signal name: %s"), signame);
8858 goto cleanup;
8861 if (virDomainSendProcessSignal(dom, pid_value, signum, 0) < 0)
8862 goto cleanup;
8864 ret = true;
8866 cleanup:
8867 virshDomainFree(dom);
8868 return ret;
8872 * "setmem" command
8874 static const vshCmdInfo info_setmem[] = {
8875 {.name = "help",
8876 .data = N_("change memory allocation")
8878 {.name = "desc",
8879 .data = N_("Change the current memory allocation in the guest domain.")
8881 {.name = NULL}
8884 static const vshCmdOptDef opts_setmem[] = {
8885 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
8886 {.name = "kilobytes",
8887 .type = VSH_OT_ALIAS,
8888 .help = "size"
8890 {.name = "size",
8891 .type = VSH_OT_INT,
8892 .flags = VSH_OFLAG_REQ,
8893 .help = N_("new memory size, as scaled integer (default KiB)")
8895 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
8896 VIRSH_COMMON_OPT_DOMAIN_LIVE,
8897 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
8898 {.name = NULL}
8901 static bool
8902 cmdSetmem(vshControl *ctl, const vshCmd *cmd)
8904 virDomainPtr dom;
8905 unsigned long long bytes = 0;
8906 unsigned long long max;
8907 unsigned long kibibytes = 0;
8908 bool ret = true;
8909 bool config = vshCommandOptBool(cmd, "config");
8910 bool live = vshCommandOptBool(cmd, "live");
8911 bool current = vshCommandOptBool(cmd, "current");
8912 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
8914 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
8915 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
8917 if (config)
8918 flags |= VIR_DOMAIN_AFFECT_CONFIG;
8919 if (live)
8920 flags |= VIR_DOMAIN_AFFECT_LIVE;
8921 /* none of the options were specified */
8922 if (!current && !live && !config)
8923 flags = -1;
8925 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
8926 return false;
8928 /* The API expects 'unsigned long' KiB, so depending on whether we
8929 * are 32-bit or 64-bit determines the maximum we can use. */
8930 if (sizeof(kibibytes) < sizeof(max))
8931 max = 1024ull * ULONG_MAX;
8932 else
8933 max = ULONG_MAX;
8934 if (vshCommandOptScaledInt(ctl, cmd, "size", &bytes, 1024, max) < 0) {
8935 virshDomainFree(dom);
8936 return false;
8938 kibibytes = VIR_DIV_UP(bytes, 1024);
8940 if (flags == -1) {
8941 if (virDomainSetMemory(dom, kibibytes) != 0)
8942 ret = false;
8943 } else {
8944 if (virDomainSetMemoryFlags(dom, kibibytes, flags) < 0)
8945 ret = false;
8948 virshDomainFree(dom);
8949 return ret;
8953 * "setmaxmem" command
8955 static const vshCmdInfo info_setmaxmem[] = {
8956 {.name = "help",
8957 .data = N_("change maximum memory limit")
8959 {.name = "desc",
8960 .data = N_("Change the maximum memory allocation limit in the guest domain.")
8962 {.name = NULL}
8965 static const vshCmdOptDef opts_setmaxmem[] = {
8966 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
8967 {.name = "kilobytes",
8968 .type = VSH_OT_ALIAS,
8969 .help = "size"
8971 {.name = "size",
8972 .type = VSH_OT_INT,
8973 .flags = VSH_OFLAG_REQ,
8974 .help = N_("new maximum memory size, as scaled integer (default KiB)")
8976 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
8977 VIRSH_COMMON_OPT_DOMAIN_LIVE,
8978 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
8979 {.name = NULL}
8982 static bool
8983 cmdSetmaxmem(vshControl *ctl, const vshCmd *cmd)
8985 virDomainPtr dom;
8986 unsigned long long bytes = 0;
8987 unsigned long long max;
8988 unsigned long kibibytes = 0;
8989 bool ret = true;
8990 bool config = vshCommandOptBool(cmd, "config");
8991 bool live = vshCommandOptBool(cmd, "live");
8992 bool current = vshCommandOptBool(cmd, "current");
8993 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT | VIR_DOMAIN_MEM_MAXIMUM;
8995 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
8996 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
8998 if (config)
8999 flags |= VIR_DOMAIN_AFFECT_CONFIG;
9000 if (live)
9001 flags |= VIR_DOMAIN_AFFECT_LIVE;
9002 /* none of the options were specified */
9003 if (!current && !live && !config)
9004 flags = -1;
9006 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
9007 return false;
9009 /* The API expects 'unsigned long' KiB, so depending on whether we
9010 * are 32-bit or 64-bit determines the maximum we can use. */
9011 if (sizeof(kibibytes) < sizeof(max))
9012 max = 1024ull * ULONG_MAX;
9013 else
9014 max = ULONG_MAX;
9015 if (vshCommandOptScaledInt(ctl, cmd, "size", &bytes, 1024, max) < 0) {
9016 virshDomainFree(dom);
9017 return false;
9019 kibibytes = VIR_DIV_UP(bytes, 1024);
9021 if (flags == -1) {
9022 if (virDomainSetMaxMemory(dom, kibibytes) != 0) {
9023 vshError(ctl, "%s", _("Unable to change MaxMemorySize"));
9024 ret = false;
9026 } else {
9027 if (virDomainSetMemoryFlags(dom, kibibytes, flags) < 0) {
9028 vshError(ctl, "%s", _("Unable to change MaxMemorySize"));
9029 ret = false;
9033 virshDomainFree(dom);
9034 return ret;
9038 * "memtune" command
9040 static const vshCmdInfo info_memtune[] = {
9041 {.name = "help",
9042 .data = N_("Get or set memory parameters")
9044 {.name = "desc",
9045 .data = N_("Get or set the current memory parameters for a guest"
9046 " domain.\n"
9047 " To get the memory parameters use following command: \n\n"
9048 " virsh # memtune <domain>")
9050 {.name = NULL}
9053 static const vshCmdOptDef opts_memtune[] = {
9054 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
9055 {.name = "hard-limit",
9056 .type = VSH_OT_INT,
9057 .help = N_("Max memory, as scaled integer (default KiB)")
9059 {.name = "soft-limit",
9060 .type = VSH_OT_INT,
9061 .help = N_("Memory during contention, as scaled integer (default KiB)")
9063 {.name = "swap-hard-limit",
9064 .type = VSH_OT_INT,
9065 .help = N_("Max memory plus swap, as scaled integer (default KiB)")
9067 {.name = "min-guarantee",
9068 .type = VSH_OT_INT,
9069 .help = N_("Min guaranteed memory, as scaled integer (default KiB)")
9071 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
9072 VIRSH_COMMON_OPT_DOMAIN_LIVE,
9073 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
9074 {.name = NULL}
9078 * virshMemtuneGetSize
9080 * @cmd: pointer to vshCmd
9081 * @name: name of a parameter for which we would like to get a value
9082 * @value: pointer to variable where the value will be stored
9084 * This function will parse virsh command line in order to load a value of
9085 * specified parameter. If the value is -1 we will handle it as unlimited and
9086 * use VIR_DOMAIN_MEMORY_PARAM_UNLIMITED instead.
9088 * Returns:
9089 * >0 if option found and valid
9090 * 0 if option not found and not required
9091 * <0 in all other cases
9093 static int
9094 virshMemtuneGetSize(vshControl *ctl, const vshCmd *cmd,
9095 const char *name, long long *value)
9097 int ret;
9098 unsigned long long tmp;
9099 const char *str;
9100 char *end;
9102 ret = vshCommandOptStringQuiet(ctl, cmd, name, &str);
9103 if (ret <= 0)
9104 return ret;
9105 if (virStrToLong_ll(str, &end, 10, value) < 0)
9106 return -1;
9107 if (*value < 0) {
9108 *value = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
9109 return 1;
9111 tmp = *value;
9112 if (virScaleInteger(&tmp, end, 1024, LLONG_MAX) < 0)
9113 return -1;
9114 *value = VIR_DIV_UP(tmp, 1024);
9115 return 1;
9118 static bool
9119 cmdMemtune(vshControl *ctl, const vshCmd *cmd)
9121 virDomainPtr dom;
9122 long long tmpVal;
9123 int nparams = 0;
9124 int maxparams = 0;
9125 int rc;
9126 size_t i;
9127 virTypedParameterPtr params = NULL;
9128 bool ret = false;
9129 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
9130 bool current = vshCommandOptBool(cmd, "current");
9131 bool config = vshCommandOptBool(cmd, "config");
9132 bool live = vshCommandOptBool(cmd, "live");
9134 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
9135 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
9137 if (config)
9138 flags |= VIR_DOMAIN_AFFECT_CONFIG;
9139 if (live)
9140 flags |= VIR_DOMAIN_AFFECT_LIVE;
9142 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
9143 return false;
9145 #define PARSE_MEMTUNE_PARAM(NAME, FIELD) \
9146 if ((rc = virshMemtuneGetSize(ctl, cmd, NAME, &tmpVal)) < 0) { \
9147 vshError(ctl, _("Unable to parse integer parameter %s"), NAME); \
9148 goto cleanup; \
9150 if (rc == 1) { \
9151 if (virTypedParamsAddULLong(&params, &nparams, &maxparams, \
9152 FIELD, tmpVal) < 0) \
9153 goto save_error; \
9157 PARSE_MEMTUNE_PARAM("hard-limit", VIR_DOMAIN_MEMORY_HARD_LIMIT);
9158 PARSE_MEMTUNE_PARAM("soft-limit", VIR_DOMAIN_MEMORY_SOFT_LIMIT);
9159 PARSE_MEMTUNE_PARAM("swap-hard-limit", VIR_DOMAIN_MEMORY_SWAP_HARD_LIMIT);
9160 PARSE_MEMTUNE_PARAM("min-guarantee", VIR_DOMAIN_MEMORY_MIN_GUARANTEE);
9162 #undef PARSE_MEMTUNE_PARAM
9164 if (nparams == 0) {
9165 /* get the number of memory parameters */
9166 if (virDomainGetMemoryParameters(dom, NULL, &nparams, flags) != 0) {
9167 vshError(ctl, "%s",
9168 _("Unable to get number of memory parameters"));
9169 goto cleanup;
9172 if (nparams == 0) {
9173 /* nothing to output */
9174 ret = true;
9175 goto cleanup;
9178 /* now go get all the memory parameters */
9179 params = vshCalloc(ctl, nparams, sizeof(*params));
9180 if (virDomainGetMemoryParameters(dom, params, &nparams, flags) != 0) {
9181 vshError(ctl, "%s", _("Unable to get memory parameters"));
9182 goto cleanup;
9185 for (i = 0; i < nparams; i++) {
9186 if (params[i].type == VIR_TYPED_PARAM_ULLONG &&
9187 params[i].value.ul == VIR_DOMAIN_MEMORY_PARAM_UNLIMITED) {
9188 vshPrint(ctl, "%-15s: %s\n", params[i].field, _("unlimited"));
9189 } else {
9190 char *str = vshGetTypedParamValue(ctl, &params[i]);
9191 vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
9192 VIR_FREE(str);
9195 } else {
9196 if (virDomainSetMemoryParameters(dom, params, nparams, flags) != 0)
9197 goto error;
9200 ret = true;
9202 cleanup:
9203 virTypedParamsFree(params, nparams);
9204 virshDomainFree(dom);
9205 return ret;
9207 save_error:
9208 vshSaveLibvirtError();
9209 error:
9210 vshError(ctl, "%s", _("Unable to change memory parameters"));
9211 goto cleanup;
9215 * "perf" command
9217 static const vshCmdInfo info_perf[] = {
9218 {.name = "help",
9219 .data = N_("Get or set perf event")
9221 {.name = "desc",
9222 .data = N_("Get or set the current perf events for a guest"
9223 " domain.\n"
9224 " To get the perf events list use following command: \n\n"
9225 " virsh # perf <domain>")
9227 {.name = NULL}
9230 static const vshCmdOptDef opts_perf[] = {
9231 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
9232 {.name = "enable",
9233 .type = VSH_OT_STRING,
9234 .help = N_("perf events which will be enabled")
9236 {.name = "disable",
9237 .type = VSH_OT_STRING,
9238 .help = N_("perf events which will be disabled")
9240 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
9241 VIRSH_COMMON_OPT_DOMAIN_LIVE,
9242 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
9243 {.name = NULL}
9246 static int
9247 virshParseEventStr(const char *event,
9248 bool state,
9249 virTypedParameterPtr *params,
9250 int *nparams,
9251 int *maxparams)
9253 char **tok = NULL;
9254 size_t i, ntok;
9255 int ret = -1;
9257 if (!(tok = virStringSplitCount(event, ",", 0, &ntok)))
9258 return -1;
9260 for (i = 0; i < ntok; i++) {
9261 if ((*tok[i] != '\0') &&
9262 virTypedParamsAddBoolean(params, nparams,
9263 maxparams, tok[i], state) < 0)
9264 goto cleanup;
9267 ret = 0;
9268 cleanup:
9269 virStringListFree(tok);
9270 return ret;
9273 static void
9274 virshPrintPerfStatus(vshControl *ctl, virTypedParameterPtr params, int nparams)
9276 size_t i;
9278 for (i = 0; i < nparams; i++) {
9279 if (params[i].type == VIR_TYPED_PARAM_BOOLEAN &&
9280 params[i].value.b) {
9281 vshPrintExtra(ctl, "%-15s: %s\n", params[i].field, _("enabled"));
9282 } else {
9283 vshPrintExtra(ctl, "%-15s: %s\n", params[i].field, _("disabled"));
9288 static bool
9289 cmdPerf(vshControl *ctl, const vshCmd *cmd)
9291 virDomainPtr dom;
9292 int nparams = 0;
9293 int maxparams = 0;
9294 virTypedParameterPtr params = NULL;
9295 bool ret = false;
9296 const char *enable = NULL, *disable = NULL;
9297 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
9298 bool current = vshCommandOptBool(cmd, "current");
9299 bool config = vshCommandOptBool(cmd, "config");
9300 bool live = vshCommandOptBool(cmd, "live");
9302 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
9303 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
9305 if (config)
9306 flags |= VIR_DOMAIN_AFFECT_CONFIG;
9307 if (live)
9308 flags |= VIR_DOMAIN_AFFECT_LIVE;
9310 if (vshCommandOptStringReq(ctl, cmd, "enable", &enable) < 0 ||
9311 vshCommandOptStringReq(ctl, cmd, "disable", &disable) < 0)
9312 return false;
9314 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
9315 return false;
9317 if (enable && virshParseEventStr(enable, true, &params,
9318 &nparams, &maxparams) < 0)
9319 goto cleanup;
9321 if (disable && virshParseEventStr(disable, false, &params,
9322 &nparams, &maxparams) < 0)
9323 goto cleanup;
9325 if (nparams == 0) {
9326 if (virDomainGetPerfEvents(dom, &params, &nparams, flags) != 0) {
9327 vshError(ctl, "%s", _("Unable to get perf events"));
9328 goto cleanup;
9330 virshPrintPerfStatus(ctl, params, nparams);
9331 } else {
9332 if (virDomainSetPerfEvents(dom, params, nparams, flags) != 0) {
9333 vshError(ctl, "%s", _("Unable to enable/disable perf events"));
9334 goto cleanup;
9335 } else {
9336 virshPrintPerfStatus(ctl, params, nparams);
9340 ret = true;
9341 cleanup:
9342 virTypedParamsFree(params, nparams);
9343 virshDomainFree(dom);
9344 return ret;
9349 * "numatune" command
9351 static const vshCmdInfo info_numatune[] = {
9352 {.name = "help",
9353 .data = N_("Get or set numa parameters")
9355 {.name = "desc",
9356 .data = N_("Get or set the current numa parameters for a guest"
9357 " domain.\n"
9358 " To get the numa parameters use following command: \n\n"
9359 " virsh # numatune <domain>")
9361 {.name = NULL}
9364 static const vshCmdOptDef opts_numatune[] = {
9365 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
9366 {.name = "mode",
9367 .type = VSH_OT_STRING,
9368 .help = N_("NUMA mode, one of strict, preferred and interleave \n"
9369 "or a number from the virDomainNumatuneMemMode enum")
9371 {.name = "nodeset",
9372 .type = VSH_OT_STRING,
9373 .help = N_("NUMA node selections to set")
9375 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
9376 VIRSH_COMMON_OPT_DOMAIN_LIVE,
9377 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
9378 {.name = NULL}
9381 static bool
9382 cmdNumatune(vshControl * ctl, const vshCmd * cmd)
9384 virDomainPtr dom;
9385 int nparams = 0;
9386 int maxparams = 0;
9387 size_t i;
9388 virTypedParameterPtr params = NULL;
9389 const char *nodeset = NULL;
9390 bool ret = false;
9391 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
9392 bool current = vshCommandOptBool(cmd, "current");
9393 bool config = vshCommandOptBool(cmd, "config");
9394 bool live = vshCommandOptBool(cmd, "live");
9395 const char *mode = NULL;
9397 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
9398 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
9400 if (config)
9401 flags |= VIR_DOMAIN_AFFECT_CONFIG;
9402 if (live)
9403 flags |= VIR_DOMAIN_AFFECT_LIVE;
9405 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
9406 return false;
9408 if (vshCommandOptStringReq(ctl, cmd, "nodeset", &nodeset) < 0)
9409 goto cleanup;
9411 if (nodeset &&
9412 virTypedParamsAddString(&params, &nparams, &maxparams,
9413 VIR_DOMAIN_NUMA_NODESET, nodeset) < 0)
9414 goto save_error;
9416 if (vshCommandOptStringReq(ctl, cmd, "mode", &mode) < 0)
9417 goto cleanup;
9419 if (mode) {
9420 int m;
9421 /* Accept string or integer, in case server understands newer
9422 * integer than what strings we were compiled with
9424 if ((m = virDomainNumatuneMemModeTypeFromString(mode)) < 0 &&
9425 virStrToLong_i(mode, NULL, 0, &m) < 0) {
9426 vshError(ctl, _("Invalid mode: %s"), mode);
9427 goto cleanup;
9430 if (virTypedParamsAddInt(&params, &nparams, &maxparams,
9431 VIR_DOMAIN_NUMA_MODE, m) < 0)
9432 goto save_error;
9435 if (nparams == 0) {
9436 /* get the number of numa parameters */
9437 if (virDomainGetNumaParameters(dom, NULL, &nparams, flags) != 0) {
9438 vshError(ctl, "%s",
9439 _("Unable to get number of memory parameters"));
9440 goto cleanup;
9443 if (nparams == 0) {
9444 /* nothing to output */
9445 ret = true;
9446 goto cleanup;
9449 /* now go get all the numa parameters */
9450 params = vshCalloc(ctl, nparams, sizeof(*params));
9451 if (virDomainGetNumaParameters(dom, params, &nparams, flags) != 0) {
9452 vshError(ctl, "%s", _("Unable to get numa parameters"));
9453 goto cleanup;
9456 for (i = 0; i < nparams; i++) {
9457 if (params[i].type == VIR_TYPED_PARAM_INT &&
9458 STREQ(params[i].field, VIR_DOMAIN_NUMA_MODE)) {
9459 vshPrint(ctl, "%-15s: %s\n", params[i].field,
9460 virDomainNumatuneMemModeTypeToString(params[i].value.i));
9461 } else {
9462 char *str = vshGetTypedParamValue(ctl, &params[i]);
9463 vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
9464 VIR_FREE(str);
9467 } else {
9468 if (virDomainSetNumaParameters(dom, params, nparams, flags) != 0)
9469 goto error;
9472 ret = true;
9474 cleanup:
9475 virTypedParamsFree(params, nparams);
9476 virshDomainFree(dom);
9477 return ret;
9479 save_error:
9480 vshSaveLibvirtError();
9481 error:
9482 vshError(ctl, "%s", _("Unable to change numa parameters"));
9483 goto cleanup;
9487 * "qemu-monitor-command" command
9489 static const vshCmdInfo info_qemu_monitor_command[] = {
9490 {.name = "help",
9491 .data = N_("QEMU Monitor Command")
9493 {.name = "desc",
9494 .data = N_("QEMU Monitor Command")
9496 {.name = NULL}
9499 static const vshCmdOptDef opts_qemu_monitor_command[] = {
9500 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
9501 {.name = "hmp",
9502 .type = VSH_OT_BOOL,
9503 .help = N_("command is in human monitor protocol")
9505 {.name = "pretty",
9506 .type = VSH_OT_BOOL,
9507 .help = N_("pretty-print any qemu monitor protocol output")
9509 {.name = "cmd",
9510 .type = VSH_OT_ARGV,
9511 .flags = VSH_OFLAG_REQ,
9512 .help = N_("command")
9514 {.name = NULL}
9517 static bool
9518 cmdQemuMonitorCommand(vshControl *ctl, const vshCmd *cmd)
9520 virDomainPtr dom = NULL;
9521 bool ret = false;
9522 char *monitor_cmd = NULL;
9523 char *result = NULL;
9524 unsigned int flags = 0;
9525 const vshCmdOpt *opt = NULL;
9526 virBuffer buf = VIR_BUFFER_INITIALIZER;
9527 virJSONValuePtr pretty = NULL;
9529 VSH_EXCLUSIVE_OPTIONS("hmp", "pretty");
9531 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
9532 return false;
9534 while ((opt = vshCommandOptArgv(ctl, cmd, opt)))
9535 virBufferAsprintf(&buf, "%s ", opt->data);
9537 virBufferTrim(&buf, " ", -1);
9539 if (virBufferError(&buf)) {
9540 vshError(ctl, "%s", _("Failed to collect command"));
9541 goto cleanup;
9543 monitor_cmd = virBufferContentAndReset(&buf);
9545 if (vshCommandOptBool(cmd, "hmp"))
9546 flags |= VIR_DOMAIN_QEMU_MONITOR_COMMAND_HMP;
9548 if (virDomainQemuMonitorCommand(dom, monitor_cmd, &result, flags) < 0)
9549 goto cleanup;
9551 if (vshCommandOptBool(cmd, "pretty")) {
9552 char *tmp;
9553 pretty = virJSONValueFromString(result);
9554 if (pretty && (tmp = virJSONValueToString(pretty, true))) {
9555 VIR_FREE(result);
9556 result = tmp;
9557 virTrimSpaces(result, NULL);
9558 } else {
9559 vshResetLibvirtError();
9562 vshPrint(ctl, "%s\n", result);
9564 ret = true;
9566 cleanup:
9567 VIR_FREE(result);
9568 VIR_FREE(monitor_cmd);
9569 virJSONValueFree(pretty);
9570 virshDomainFree(dom);
9572 return ret;
9576 * "qemu-monitor-event" command
9579 struct virshQemuEventData {
9580 vshControl *ctl;
9581 bool loop;
9582 bool pretty;
9583 bool timestamp;
9584 int count;
9586 typedef struct virshQemuEventData virshQemuEventData;
9588 static void
9589 virshEventQemuPrint(virConnectPtr conn ATTRIBUTE_UNUSED,
9590 virDomainPtr dom,
9591 const char *event,
9592 long long seconds,
9593 unsigned int micros,
9594 const char *details,
9595 void *opaque)
9597 virshQemuEventData *data = opaque;
9598 virJSONValuePtr pretty = NULL;
9599 char *str = NULL;
9601 if (!data->loop && data->count)
9602 return;
9603 if (data->pretty && details) {
9604 pretty = virJSONValueFromString(details);
9605 if (pretty && (str = virJSONValueToString(pretty, true)))
9606 details = str;
9609 if (data->timestamp) {
9610 char timestamp[VIR_TIME_STRING_BUFLEN];
9612 if (virTimeStringNowRaw(timestamp) < 0)
9613 timestamp[0] = '\0';
9615 vshPrint(data->ctl, "%s: event %s for domain %s: %s\n",
9616 timestamp, event, virDomainGetName(dom), NULLSTR(details));
9617 } else {
9618 vshPrint(data->ctl, "event %s at %lld.%06u for domain %s: %s\n",
9619 event, seconds, micros, virDomainGetName(dom), NULLSTR(details));
9622 data->count++;
9623 if (!data->loop)
9624 vshEventDone(data->ctl);
9626 VIR_FREE(str);
9629 static const vshCmdInfo info_qemu_monitor_event[] = {
9630 {.name = "help",
9631 .data = N_("QEMU Monitor Events")
9633 {.name = "desc",
9634 .data = N_("Listen for QEMU Monitor Events")
9636 {.name = NULL}
9639 static const vshCmdOptDef opts_qemu_monitor_event[] = {
9640 VIRSH_COMMON_OPT_DOMAIN_OT_STRING(N_("filter by domain name, id or uuid"),
9641 0, 0),
9642 {.name = "event",
9643 .type = VSH_OT_STRING,
9644 .help = N_("filter by event name")
9646 {.name = "pretty",
9647 .type = VSH_OT_BOOL,
9648 .help = N_("pretty-print any JSON output")
9650 {.name = "loop",
9651 .type = VSH_OT_BOOL,
9652 .help = N_("loop until timeout or interrupt, rather than one-shot")
9654 {.name = "timeout",
9655 .type = VSH_OT_INT,
9656 .help = N_("timeout seconds")
9658 {.name = "regex",
9659 .type = VSH_OT_BOOL,
9660 .help = N_("treat event as a regex rather than literal filter")
9662 {.name = "no-case",
9663 .type = VSH_OT_BOOL,
9664 .help = N_("treat event case-insensitively")
9666 {.name = "timestamp",
9667 .type = VSH_OT_BOOL,
9668 .help = N_("show timestamp for each printed event")
9670 {.name = NULL}
9673 static bool
9674 cmdQemuMonitorEvent(vshControl *ctl, const vshCmd *cmd)
9676 virDomainPtr dom = NULL;
9677 bool ret = false;
9678 unsigned int flags = 0;
9679 int eventId = -1;
9680 int timeout = 0;
9681 const char *event = NULL;
9682 virshQemuEventData data;
9683 virshControlPtr priv = ctl->privData;
9685 if (vshCommandOptBool(cmd, "regex"))
9686 flags |= VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_REGEX;
9687 if (vshCommandOptBool(cmd, "no-case"))
9688 flags |= VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_NOCASE;
9690 data.ctl = ctl;
9691 data.loop = vshCommandOptBool(cmd, "loop");
9692 data.pretty = vshCommandOptBool(cmd, "pretty");
9693 data.timestamp = vshCommandOptBool(cmd, "timestamp");
9694 data.count = 0;
9695 if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
9696 return false;
9697 if (vshCommandOptStringReq(ctl, cmd, "event", &event) < 0)
9698 return false;
9700 if (vshCommandOptBool(cmd, "domain"))
9701 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
9702 goto cleanup;
9704 if (vshEventStart(ctl, timeout) < 0)
9705 goto cleanup;
9707 if ((eventId = virConnectDomainQemuMonitorEventRegister(priv->conn, dom,
9708 event,
9709 virshEventQemuPrint,
9710 &data, NULL,
9711 flags)) < 0)
9712 goto cleanup;
9713 switch (vshEventWait(ctl)) {
9714 case VSH_EVENT_INTERRUPT:
9715 vshPrint(ctl, _("event loop interrupted\n"));
9716 break;
9717 case VSH_EVENT_TIMEOUT:
9718 vshPrint(ctl, _("event loop timed out\n"));
9719 break;
9720 case VSH_EVENT_DONE:
9721 break;
9722 default:
9723 goto cleanup;
9725 vshPrint(ctl, _("events received: %d\n"), data.count);
9726 if (data.count)
9727 ret = true;
9729 cleanup:
9730 vshEventCleanup(ctl);
9731 if (eventId >= 0 &&
9732 virConnectDomainQemuMonitorEventDeregister(priv->conn, eventId) < 0)
9733 ret = false;
9734 virshDomainFree(dom);
9736 return ret;
9740 * "qemu-attach" command
9742 static const vshCmdInfo info_qemu_attach[] = {
9743 {.name = "help",
9744 .data = N_("QEMU Attach")
9746 {.name = "desc",
9747 .data = N_("QEMU Attach")
9749 {.name = NULL}
9752 static const vshCmdOptDef opts_qemu_attach[] = {
9753 {.name = "pid",
9754 .type = VSH_OT_DATA,
9755 .flags = VSH_OFLAG_REQ,
9756 .help = N_("pid")
9758 {.name = NULL}
9761 static bool
9762 cmdQemuAttach(vshControl *ctl, const vshCmd *cmd)
9764 virDomainPtr dom = NULL;
9765 bool ret = false;
9766 unsigned int flags = 0;
9767 unsigned int pid_value; /* API uses unsigned int, not pid_t */
9768 virshControlPtr priv = ctl->privData;
9770 if (vshCommandOptUInt(ctl, cmd, "pid", &pid_value) <= 0)
9771 goto cleanup;
9773 if (!(dom = virDomainQemuAttach(priv->conn, pid_value, flags))) {
9774 vshError(ctl, _("Failed to attach to pid %u"), pid_value);
9775 goto cleanup;
9778 vshPrintExtra(ctl, _("Domain %s attached to pid %u\n"),
9779 virDomainGetName(dom), pid_value);
9780 virshDomainFree(dom);
9781 ret = true;
9783 cleanup:
9784 return ret;
9788 * "qemu-agent-command" command
9790 static const vshCmdInfo info_qemu_agent_command[] = {
9791 {.name = "help",
9792 .data = N_("QEMU Guest Agent Command")
9794 {.name = "desc",
9795 .data = N_("Run an arbitrary qemu guest agent command; use at your own risk")
9797 {.name = NULL}
9800 static const vshCmdOptDef opts_qemu_agent_command[] = {
9801 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
9802 {.name = "timeout",
9803 .type = VSH_OT_INT,
9804 .flags = VSH_OFLAG_REQ_OPT,
9805 .help = N_("timeout seconds. must be positive.")
9807 {.name = "async",
9808 .type = VSH_OT_BOOL,
9809 .help = N_("execute command without waiting for timeout")
9811 {.name = "block",
9812 .type = VSH_OT_BOOL,
9813 .help = N_("execute command without timeout")
9815 {.name = "pretty",
9816 .type = VSH_OT_BOOL,
9817 .help = N_("pretty-print the output")
9819 {.name = "cmd",
9820 .type = VSH_OT_ARGV,
9821 .flags = VSH_OFLAG_REQ,
9822 .help = N_("command")
9824 {.name = NULL}
9827 static bool
9828 cmdQemuAgentCommand(vshControl *ctl, const vshCmd *cmd)
9830 virDomainPtr dom = NULL;
9831 bool ret = false;
9832 char *guest_agent_cmd = NULL;
9833 char *result = NULL;
9834 int timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT;
9835 int judge = 0;
9836 unsigned int flags = 0;
9837 const vshCmdOpt *opt = NULL;
9838 virBuffer buf = VIR_BUFFER_INITIALIZER;
9839 virJSONValuePtr pretty = NULL;
9841 dom = virshCommandOptDomain(ctl, cmd, NULL);
9842 if (dom == NULL)
9843 goto cleanup;
9845 while ((opt = vshCommandOptArgv(ctl, cmd, opt)))
9846 virBufferAsprintf(&buf, "%s ", opt->data);
9848 virBufferTrim(&buf, " ", -1);
9850 if (virBufferError(&buf)) {
9851 vshError(ctl, "%s", _("Failed to collect command"));
9852 goto cleanup;
9854 guest_agent_cmd = virBufferContentAndReset(&buf);
9856 judge = vshCommandOptInt(ctl, cmd, "timeout", &timeout);
9857 if (judge < 0)
9858 goto cleanup;
9859 else if (judge > 0)
9860 judge = 1;
9861 if (judge && timeout < 1) {
9862 vshError(ctl, "%s", _("timeout must be positive"));
9863 goto cleanup;
9866 if (vshCommandOptBool(cmd, "async")) {
9867 timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT;
9868 judge++;
9870 if (vshCommandOptBool(cmd, "block")) {
9871 timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK;
9872 judge++;
9875 if (judge > 1) {
9876 vshError(ctl, "%s", _("timeout, async and block options are exclusive"));
9877 goto cleanup;
9880 result = virDomainQemuAgentCommand(dom, guest_agent_cmd, timeout, flags);
9881 if (!result)
9882 goto cleanup;
9884 if (vshCommandOptBool(cmd, "pretty")) {
9885 char *tmp;
9886 pretty = virJSONValueFromString(result);
9887 if (pretty && (tmp = virJSONValueToString(pretty, true))) {
9888 VIR_FREE(result);
9889 result = tmp;
9890 } else {
9891 vshResetLibvirtError();
9895 vshPrint(ctl, "%s\n", result);
9897 ret = true;
9899 cleanup:
9900 VIR_FREE(result);
9901 VIR_FREE(guest_agent_cmd);
9902 virshDomainFree(dom);
9904 return ret;
9908 * "lxc-enter-namespace" namespace
9910 static const vshCmdInfo info_lxc_enter_namespace[] = {
9911 {.name = "help",
9912 .data = N_("LXC Guest Enter Namespace")
9914 {.name = "desc",
9915 .data = N_("Run an arbitrary command in a lxc guest namespace; use at your own risk")
9917 {.name = NULL}
9920 static const vshCmdOptDef opts_lxc_enter_namespace[] = {
9921 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
9922 {.name = "noseclabel",
9923 .type = VSH_OT_BOOL,
9924 .help = N_("Do not change process security label")
9926 {.name = "cmd",
9927 .type = VSH_OT_ARGV,
9928 .flags = VSH_OFLAG_REQ,
9929 .help = N_("command to run")
9931 {.name = NULL}
9934 static bool
9935 cmdLxcEnterNamespace(vshControl *ctl, const vshCmd *cmd)
9937 virDomainPtr dom = NULL;
9938 bool ret = false;
9939 const vshCmdOpt *opt = NULL;
9940 char **cmdargv = NULL;
9941 size_t ncmdargv = 0;
9942 pid_t pid;
9943 int nfdlist;
9944 int *fdlist;
9945 size_t i;
9946 bool setlabel = true;
9947 virSecurityModelPtr secmodel = NULL;
9948 virSecurityLabelPtr seclabel = NULL;
9949 virshControlPtr priv = ctl->privData;
9951 dom = virshCommandOptDomain(ctl, cmd, NULL);
9952 if (dom == NULL)
9953 goto cleanup;
9955 if (vshCommandOptBool(cmd, "noseclabel"))
9956 setlabel = false;
9958 while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
9959 if (VIR_EXPAND_N(cmdargv, ncmdargv, 1) < 0) {
9960 vshError(ctl, _("%s: %d: failed to allocate argv"),
9961 __FILE__, __LINE__);
9963 cmdargv[ncmdargv-1] = opt->data;
9965 if (VIR_EXPAND_N(cmdargv, ncmdargv, 1) < 0) {
9966 vshError(ctl, _("%s: %d: failed to allocate argv"),
9967 __FILE__, __LINE__);
9969 cmdargv[ncmdargv - 1] = NULL;
9971 if ((nfdlist = virDomainLxcOpenNamespace(dom, &fdlist, 0)) < 0)
9972 goto cleanup;
9974 if (setlabel) {
9975 if (VIR_ALLOC(secmodel) < 0) {
9976 vshError(ctl, "%s", _("Failed to allocate security model"));
9977 goto cleanup;
9979 if (VIR_ALLOC(seclabel) < 0) {
9980 vshError(ctl, "%s", _("Failed to allocate security label"));
9981 goto cleanup;
9983 if (virNodeGetSecurityModel(priv->conn, secmodel) < 0)
9984 goto cleanup;
9985 if (virDomainGetSecurityLabel(dom, seclabel) < 0)
9986 goto cleanup;
9989 /* Fork once because we don't want to affect
9990 * virsh's namespace itself, and because user namespace
9991 * can only be changed in single-threaded process
9993 if ((pid = virFork()) < 0)
9994 goto cleanup;
9995 if (pid == 0) {
9996 int status;
9998 if (setlabel &&
9999 virDomainLxcEnterSecurityLabel(secmodel,
10000 seclabel,
10001 NULL,
10002 0) < 0)
10003 _exit(EXIT_CANCELED);
10005 if (virDomainLxcEnterCGroup(dom, 0) < 0)
10006 _exit(EXIT_CANCELED);
10008 if (virDomainLxcEnterNamespace(dom,
10009 nfdlist,
10010 fdlist,
10011 NULL,
10012 NULL,
10013 0) < 0)
10014 _exit(EXIT_CANCELED);
10016 /* Fork a second time because entering the
10017 * pid namespace only takes effect after fork
10019 if ((pid = virFork()) < 0)
10020 _exit(EXIT_CANCELED);
10021 if (pid == 0) {
10022 execv(cmdargv[0], cmdargv);
10023 _exit(errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
10025 if (virProcessWait(pid, &status, true) < 0)
10026 _exit(EXIT_CANNOT_INVOKE);
10027 virProcessExitWithStatus(status);
10028 } else {
10029 for (i = 0; i < nfdlist; i++)
10030 VIR_FORCE_CLOSE(fdlist[i]);
10031 VIR_FREE(fdlist);
10032 if (virProcessWait(pid, NULL, false) < 0) {
10033 vshReportError(ctl);
10034 goto cleanup;
10038 ret = true;
10040 cleanup:
10041 VIR_FREE(seclabel);
10042 VIR_FREE(secmodel);
10043 virshDomainFree(dom);
10044 VIR_FREE(cmdargv);
10045 return ret;
10049 * "dumpxml" command
10051 static const vshCmdInfo info_dumpxml[] = {
10052 {.name = "help",
10053 .data = N_("domain information in XML")
10055 {.name = "desc",
10056 .data = N_("Output the domain information as an XML dump to stdout.")
10058 {.name = NULL}
10061 static const vshCmdOptDef opts_dumpxml[] = {
10062 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
10063 {.name = "inactive",
10064 .type = VSH_OT_BOOL,
10065 .help = N_("show inactive defined XML")
10067 {.name = "security-info",
10068 .type = VSH_OT_BOOL,
10069 .help = N_("include security sensitive information in XML dump")
10071 {.name = "update-cpu",
10072 .type = VSH_OT_BOOL,
10073 .help = N_("update guest CPU according to host CPU")
10075 {.name = "migratable",
10076 .type = VSH_OT_BOOL,
10077 .help = N_("provide XML suitable for migrations")
10079 {.name = NULL}
10082 static bool
10083 cmdDumpXML(vshControl *ctl, const vshCmd *cmd)
10085 virDomainPtr dom;
10086 bool ret = true;
10087 char *dump;
10088 unsigned int flags = 0;
10089 bool inactive = vshCommandOptBool(cmd, "inactive");
10090 bool secure = vshCommandOptBool(cmd, "security-info");
10091 bool update = vshCommandOptBool(cmd, "update-cpu");
10092 bool migratable = vshCommandOptBool(cmd, "migratable");
10094 if (inactive)
10095 flags |= VIR_DOMAIN_XML_INACTIVE;
10096 if (secure)
10097 flags |= VIR_DOMAIN_XML_SECURE;
10098 if (update)
10099 flags |= VIR_DOMAIN_XML_UPDATE_CPU;
10100 if (migratable)
10101 flags |= VIR_DOMAIN_XML_MIGRATABLE;
10103 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
10104 return false;
10106 dump = virDomainGetXMLDesc(dom, flags);
10107 if (dump != NULL) {
10108 vshPrint(ctl, "%s", dump);
10109 VIR_FREE(dump);
10110 } else {
10111 ret = false;
10114 virshDomainFree(dom);
10115 return ret;
10119 * "domxml-from-native" command
10121 static const vshCmdInfo info_domxmlfromnative[] = {
10122 {.name = "help",
10123 .data = N_("Convert native config to domain XML")
10125 {.name = "desc",
10126 .data = N_("Convert native guest configuration format to domain XML format.")
10128 {.name = NULL}
10131 static const vshCmdOptDef opts_domxmlfromnative[] = {
10132 {.name = "format",
10133 .type = VSH_OT_DATA,
10134 .flags = VSH_OFLAG_REQ,
10135 .help = N_("source config data format")
10137 {.name = "config",
10138 .type = VSH_OT_DATA,
10139 .flags = VSH_OFLAG_REQ,
10140 .help = N_("config data file to import from")
10142 {.name = NULL}
10145 static bool
10146 cmdDomXMLFromNative(vshControl *ctl, const vshCmd *cmd)
10148 bool ret = true;
10149 const char *format = NULL;
10150 const char *configFile = NULL;
10151 char *configData;
10152 char *xmlData;
10153 unsigned int flags = 0;
10154 virshControlPtr priv = ctl->privData;
10156 if (vshCommandOptStringReq(ctl, cmd, "format", &format) < 0 ||
10157 vshCommandOptStringReq(ctl, cmd, "config", &configFile) < 0)
10158 return false;
10160 if (virFileReadAll(configFile, VSH_MAX_XML_FILE, &configData) < 0)
10161 return false;
10163 xmlData = virConnectDomainXMLFromNative(priv->conn, format, configData, flags);
10164 if (xmlData != NULL) {
10165 vshPrint(ctl, "%s", xmlData);
10166 VIR_FREE(xmlData);
10167 } else {
10168 ret = false;
10171 VIR_FREE(configData);
10172 return ret;
10176 * "domxml-to-native" command
10178 static const vshCmdInfo info_domxmltonative[] = {
10179 {.name = "help",
10180 .data = N_("Convert domain XML to native config")
10182 {.name = "desc",
10183 .data = N_("Convert domain XML config to a native guest configuration format.")
10185 {.name = NULL}
10188 static const vshCmdOptDef opts_domxmltonative[] = {
10189 {.name = "format",
10190 .type = VSH_OT_DATA,
10191 .flags = VSH_OFLAG_REQ,
10192 .help = N_("target config data type format")
10194 VIRSH_COMMON_OPT_DOMAIN_OT_STRING_FULL(VSH_OFLAG_REQ_OPT, 0),
10195 {.name = "xml",
10196 .type = VSH_OT_STRING,
10197 .help = N_("xml data file to export from")
10199 {.name = NULL}
10202 static bool
10203 cmdDomXMLToNative(vshControl *ctl, const vshCmd *cmd)
10205 bool ret = false;
10206 const char *format = NULL;
10207 const char *xmlFile = NULL;
10208 char *configData = NULL;
10209 char *xmlData = NULL;
10210 unsigned int flags = 0;
10211 virshControlPtr priv = ctl->privData;
10212 virDomainPtr dom = NULL;
10214 if (vshCommandOptStringReq(ctl, cmd, "format", &format) < 0 ||
10215 vshCommandOptStringReq(ctl, cmd, "xml", &xmlFile) < 0)
10216 return false;
10218 VSH_EXCLUSIVE_OPTIONS("domain", "xml");
10220 if (vshCommandOptBool(cmd, "domain") &&
10221 (!(dom = virshCommandOptDomain(ctl, cmd, NULL))))
10222 return false;
10224 if (dom) {
10225 xmlData = virDomainGetXMLDesc(dom, flags);
10226 } else if (xmlFile) {
10227 if (virFileReadAll(xmlFile, VSH_MAX_XML_FILE, &xmlData) < 0)
10228 goto cleanup;
10229 } else {
10230 vshError(ctl, "%s", _("need either domain or domain XML"));
10231 goto cleanup;
10234 if (!xmlData) {
10235 vshError(ctl, "%s", _("failed to retrieve XML"));
10236 goto cleanup;
10239 if (!(configData = virConnectDomainXMLToNative(priv->conn, format, xmlData, flags))) {
10240 goto cleanup;
10241 } else {
10242 vshPrint(ctl, "%s", configData);
10243 ret = true;
10246 cleanup:
10247 virshDomainFree(dom);
10248 VIR_FREE(xmlData);
10249 VIR_FREE(configData);
10250 return ret;
10254 * "domname" command
10256 static const vshCmdInfo info_domname[] = {
10257 {.name = "help",
10258 .data = N_("convert a domain id or UUID to domain name")
10260 {.name = "desc",
10261 .data = ""
10263 {.name = NULL}
10266 static const vshCmdOptDef opts_domname[] = {
10267 VIRSH_COMMON_OPT_DOMAIN(N_("domain id or uuid"), 0),
10268 {.name = NULL}
10271 static bool
10272 cmdDomname(vshControl *ctl, const vshCmd *cmd)
10274 virDomainPtr dom;
10276 if (!(dom = virshCommandOptDomainBy(ctl, cmd, NULL,
10277 VIRSH_BYID|VIRSH_BYUUID)))
10278 return false;
10280 vshPrint(ctl, "%s\n", virDomainGetName(dom));
10281 virshDomainFree(dom);
10282 return true;
10286 * "domrename" command
10288 static const vshCmdInfo info_domrename[] = {
10289 {.name = "help",
10290 .data = N_("rename a domain")
10292 {.name = "desc",
10293 .data = "Rename an inactive domain."
10295 {.name = NULL}
10298 static const vshCmdOptDef opts_domrename[] = {
10299 VIRSH_COMMON_OPT_DOMAIN(N_("domain name or uuid"),
10300 VIR_CONNECT_LIST_DOMAINS_INACTIVE),
10301 {.name = "new-name",
10302 .type = VSH_OT_DATA,
10303 .flags = VSH_OFLAG_REQ,
10304 .help = N_("new domain name")
10306 {.name = NULL}
10309 static bool
10310 cmdDomrename(vshControl *ctl, const vshCmd *cmd)
10312 virDomainPtr dom;
10313 const char *new_name = NULL;
10314 bool ret = false;
10316 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
10317 return ret;
10319 if (vshCommandOptStringReq(ctl, cmd, "new-name", &new_name) < 0)
10320 goto cleanup;
10322 if (virDomainRename(dom, new_name, 0) < 0)
10323 goto cleanup;
10325 vshPrintExtra(ctl, "Domain successfully renamed\n");
10326 ret = true;
10328 cleanup:
10329 virshDomainFree(dom);
10330 return ret;
10334 * "domid" command
10336 static const vshCmdInfo info_domid[] = {
10337 {.name = "help",
10338 .data = N_("convert a domain name or UUID to domain id")
10340 {.name = "desc",
10341 .data = ""
10343 {.name = NULL}
10346 static const vshCmdOptDef opts_domid[] = {
10347 VIRSH_COMMON_OPT_DOMAIN(N_("domain name or uuid"),
10348 VIR_CONNECT_LIST_DOMAINS_ACTIVE),
10349 {.name = NULL}
10352 static bool
10353 cmdDomid(vshControl *ctl, const vshCmd *cmd)
10355 virDomainPtr dom;
10356 unsigned int id;
10358 if (!(dom = virshCommandOptDomainBy(ctl, cmd, NULL,
10359 VIRSH_BYNAME|VIRSH_BYUUID)))
10360 return false;
10362 id = virDomainGetID(dom);
10363 if (id == ((unsigned int)-1))
10364 vshPrint(ctl, "%s\n", "-");
10365 else
10366 vshPrint(ctl, "%d\n", id);
10367 virshDomainFree(dom);
10368 return true;
10372 * "domuuid" command
10374 static const vshCmdInfo info_domuuid[] = {
10375 {.name = "help",
10376 .data = N_("convert a domain name or id to domain UUID")
10378 {.name = "desc",
10379 .data = ""
10381 {.name = NULL}
10384 static const vshCmdOptDef opts_domuuid[] = {
10385 VIRSH_COMMON_OPT_DOMAIN(N_("domain id or name"), 0),
10386 {.name = NULL}
10389 static bool
10390 cmdDomuuid(vshControl *ctl, const vshCmd *cmd)
10392 virDomainPtr dom;
10393 char uuid[VIR_UUID_STRING_BUFLEN];
10395 if (!(dom = virshCommandOptDomainBy(ctl, cmd, NULL,
10396 VIRSH_BYNAME|VIRSH_BYID)))
10397 return false;
10399 if (virDomainGetUUIDString(dom, uuid) != -1)
10400 vshPrint(ctl, "%s\n", uuid);
10401 else
10402 vshError(ctl, "%s", _("failed to get domain UUID"));
10404 virshDomainFree(dom);
10405 return true;
10409 * "migrate" command
10411 static const vshCmdInfo info_migrate[] = {
10412 {.name = "help",
10413 .data = N_("migrate domain to another host")
10415 {.name = "desc",
10416 .data = N_("Migrate domain to another host. Add --live for live migration.")
10418 {.name = NULL}
10421 static const vshCmdOptDef opts_migrate[] = {
10422 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
10423 {.name = "desturi",
10424 .type = VSH_OT_DATA,
10425 .flags = VSH_OFLAG_REQ,
10426 .help = N_("connection URI of the destination host as seen from the client(normal migration) or source(p2p migration)")
10428 VIRSH_COMMON_OPT_LIVE(N_("live migration")),
10429 {.name = "offline",
10430 .type = VSH_OT_BOOL,
10431 .help = N_("offline migration")
10433 {.name = "p2p",
10434 .type = VSH_OT_BOOL,
10435 .help = N_("peer-2-peer migration")
10437 {.name = "direct",
10438 .type = VSH_OT_BOOL,
10439 .help = N_("direct migration")
10441 {.name = "tunneled",
10442 .type = VSH_OT_ALIAS,
10443 .help = "tunnelled"
10445 {.name = "tunnelled",
10446 .type = VSH_OT_BOOL,
10447 .help = N_("tunnelled migration")
10449 {.name = "persistent",
10450 .type = VSH_OT_BOOL,
10451 .help = N_("persist VM on destination")
10453 {.name = "undefinesource",
10454 .type = VSH_OT_BOOL,
10455 .help = N_("undefine VM on source")
10457 {.name = "suspend",
10458 .type = VSH_OT_BOOL,
10459 .help = N_("do not restart the domain on the destination host")
10461 {.name = "copy-storage-all",
10462 .type = VSH_OT_BOOL,
10463 .help = N_("migration with non-shared storage with full disk copy")
10465 {.name = "copy-storage-inc",
10466 .type = VSH_OT_BOOL,
10467 .help = N_("migration with non-shared storage with incremental copy (same base image shared between source and destination)")
10469 {.name = "change-protection",
10470 .type = VSH_OT_BOOL,
10471 .help = N_("prevent any configuration changes to domain until migration ends")
10473 {.name = "unsafe",
10474 .type = VSH_OT_BOOL,
10475 .help = N_("force migration even if it may be unsafe")
10477 {.name = "verbose",
10478 .type = VSH_OT_BOOL,
10479 .help = N_("display the progress of migration")
10481 {.name = "compressed",
10482 .type = VSH_OT_BOOL,
10483 .help = N_("compress repeated pages during live migration")
10485 {.name = "auto-converge",
10486 .type = VSH_OT_BOOL,
10487 .help = N_("force convergence during live migration")
10489 {.name = "rdma-pin-all",
10490 .type = VSH_OT_BOOL,
10491 .help = N_("pin all memory before starting RDMA live migration")
10493 {.name = "abort-on-error",
10494 .type = VSH_OT_BOOL,
10495 .help = N_("abort on soft errors during migration")
10497 {.name = "postcopy",
10498 .type = VSH_OT_BOOL,
10499 .help = N_("enable post-copy migration; switch to it using migrate-postcopy command")
10501 {.name = "postcopy-after-precopy",
10502 .type = VSH_OT_BOOL,
10503 .help = N_("automatically switch to post-copy migration after one pass of pre-copy")
10505 {.name = "migrateuri",
10506 .type = VSH_OT_STRING,
10507 .help = N_("migration URI, usually can be omitted")
10509 {.name = "graphicsuri",
10510 .type = VSH_OT_STRING,
10511 .help = N_("graphics URI to be used for seamless graphics migration")
10513 {.name = "listen-address",
10514 .type = VSH_OT_STRING,
10515 .help = N_("listen address that destination should bind to for incoming migration")
10517 {.name = "dname",
10518 .type = VSH_OT_STRING,
10519 .help = N_("rename to new name during migration (if supported)")
10521 {.name = "timeout",
10522 .type = VSH_OT_INT,
10523 .help = N_("run action specified by --timeout-* option (suspend by "
10524 "default) if live migration exceeds timeout (in seconds)")
10526 {.name = "timeout-suspend",
10527 .type = VSH_OT_BOOL,
10528 .help = N_("suspend the guest after timeout")
10530 {.name = "timeout-postcopy",
10531 .type = VSH_OT_BOOL,
10532 .help = N_("switch to post-copy after timeout")
10534 {.name = "xml",
10535 .type = VSH_OT_STRING,
10536 .help = N_("filename containing updated XML for the target")
10538 {.name = "migrate-disks",
10539 .type = VSH_OT_STRING,
10540 .help = N_("comma separated list of disks to be migrated")
10542 {.name = "disks-port",
10543 .type = VSH_OT_INT,
10544 .help = N_("port to use by target server for incoming disks migration")
10546 {.name = "comp-methods",
10547 .type = VSH_OT_STRING,
10548 .help = N_("comma separated list of compression methods to be used")
10550 {.name = "comp-mt-level",
10551 .type = VSH_OT_INT,
10552 .help = N_("compress level for multithread compression")
10554 {.name = "comp-mt-threads",
10555 .type = VSH_OT_INT,
10556 .help = N_("number of compression threads for multithread compression")
10558 {.name = "comp-mt-dthreads",
10559 .type = VSH_OT_INT,
10560 .help = N_("number of decompression threads for multithread compression")
10562 {.name = "comp-xbzrle-cache",
10563 .type = VSH_OT_INT,
10564 .help = N_("page cache size for xbzrle compression")
10566 {.name = "auto-converge-initial",
10567 .type = VSH_OT_INT,
10568 .help = N_("initial CPU throttling rate for auto-convergence")
10570 {.name = "auto-converge-increment",
10571 .type = VSH_OT_INT,
10572 .help = N_("CPU throttling rate increment for auto-convergence")
10574 {.name = "persistent-xml",
10575 .type = VSH_OT_STRING,
10576 .help = N_("filename containing updated persistent XML for the target")
10578 {.name = "tls",
10579 .type = VSH_OT_BOOL,
10580 .help = N_("use TLS for migration")
10582 {.name = "postcopy-bandwidth",
10583 .type = VSH_OT_INT,
10584 .help = N_("post-copy migration bandwidth limit in MiB/s")
10586 {.name = "parallel",
10587 .type = VSH_OT_BOOL,
10588 .help = N_("enable parallel migration")
10590 {.name = "parallel-connections",
10591 .type = VSH_OT_INT,
10592 .help = N_("number of connections for parallel migration")
10594 {.name = NULL}
10597 static void
10598 doMigrate(void *opaque)
10600 char ret = '1';
10601 virDomainPtr dom = NULL;
10602 const char *desturi = NULL;
10603 const char *opt = NULL;
10604 int disksPort = 0;
10605 unsigned int flags = 0;
10606 virshCtrlData *data = opaque;
10607 vshControl *ctl = data->ctl;
10608 const vshCmd *cmd = data->cmd;
10609 sigset_t sigmask, oldsigmask;
10610 virTypedParameterPtr params = NULL;
10611 int nparams = 0;
10612 int maxparams = 0;
10613 int intOpt = 0;
10614 unsigned long long ullOpt = 0;
10615 int rv;
10616 virConnectPtr dconn = data->dconn;
10618 sigemptyset(&sigmask);
10619 sigaddset(&sigmask, SIGINT);
10620 if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
10621 goto out_sig;
10623 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
10624 goto out;
10626 if (vshCommandOptStringReq(ctl, cmd, "desturi", &desturi) < 0)
10627 goto out;
10629 if (vshCommandOptStringReq(ctl, cmd, "migrateuri", &opt) < 0)
10630 goto out;
10631 if (opt &&
10632 virTypedParamsAddString(&params, &nparams, &maxparams,
10633 VIR_MIGRATE_PARAM_URI, opt) < 0)
10634 goto save_error;
10636 if (vshCommandOptStringReq(ctl, cmd, "graphicsuri", &opt) < 0)
10637 goto out;
10638 if (opt &&
10639 virTypedParamsAddString(&params, &nparams, &maxparams,
10640 VIR_MIGRATE_PARAM_GRAPHICS_URI, opt) < 0)
10641 goto save_error;
10643 if (vshCommandOptStringReq(ctl, cmd, "listen-address", &opt) < 0)
10644 goto out;
10645 if (opt &&
10646 virTypedParamsAddString(&params, &nparams, &maxparams,
10647 VIR_MIGRATE_PARAM_LISTEN_ADDRESS, opt) < 0)
10648 goto save_error;
10650 if (vshCommandOptInt(ctl, cmd, "disks-port", &disksPort) < 0)
10651 goto out;
10652 if (disksPort &&
10653 virTypedParamsAddInt(&params, &nparams, &maxparams,
10654 VIR_MIGRATE_PARAM_DISKS_PORT, disksPort) < 0)
10655 goto save_error;
10657 if (vshCommandOptStringReq(ctl, cmd, "dname", &opt) < 0)
10658 goto out;
10659 if (opt &&
10660 virTypedParamsAddString(&params, &nparams, &maxparams,
10661 VIR_MIGRATE_PARAM_DEST_NAME, opt) < 0)
10662 goto save_error;
10664 if (vshCommandOptStringReq(ctl, cmd, "migrate-disks", &opt) < 0)
10665 goto out;
10666 if (opt) {
10667 char **val = NULL;
10669 val = virStringSplit(opt, ",", 0);
10671 if (virTypedParamsAddStringList(&params,
10672 &nparams,
10673 &maxparams,
10674 VIR_MIGRATE_PARAM_MIGRATE_DISKS,
10675 (const char **)val) < 0) {
10676 VIR_FREE(val);
10677 goto save_error;
10680 VIR_FREE(val);
10683 if (vshCommandOptStringReq(ctl, cmd, "comp-methods", &opt) < 0)
10684 goto out;
10685 if (opt) {
10686 char **val = virStringSplit(opt, ",", 0);
10688 if (virTypedParamsAddStringList(&params,
10689 &nparams,
10690 &maxparams,
10691 VIR_MIGRATE_PARAM_COMPRESSION,
10692 (const char **)val) < 0) {
10693 VIR_FREE(val);
10694 goto save_error;
10697 VIR_FREE(val);
10700 if ((rv = vshCommandOptInt(ctl, cmd, "comp-mt-level", &intOpt)) < 0) {
10701 goto out;
10702 } else if (rv > 0) {
10703 if (virTypedParamsAddInt(&params, &nparams, &maxparams,
10704 VIR_MIGRATE_PARAM_COMPRESSION_MT_LEVEL,
10705 intOpt) < 0)
10706 goto save_error;
10709 if ((rv = vshCommandOptInt(ctl, cmd, "comp-mt-threads", &intOpt)) < 0) {
10710 goto out;
10711 } else if (rv > 0) {
10712 if (virTypedParamsAddInt(&params, &nparams, &maxparams,
10713 VIR_MIGRATE_PARAM_COMPRESSION_MT_THREADS,
10714 intOpt) < 0)
10715 goto save_error;
10718 if ((rv = vshCommandOptInt(ctl, cmd, "comp-mt-dthreads", &intOpt)) < 0) {
10719 goto out;
10720 } else if (rv > 0) {
10721 if (virTypedParamsAddInt(&params, &nparams, &maxparams,
10722 VIR_MIGRATE_PARAM_COMPRESSION_MT_DTHREADS,
10723 intOpt) < 0)
10724 goto save_error;
10727 if ((rv = vshCommandOptULongLong(ctl, cmd, "comp-xbzrle-cache", &ullOpt)) < 0) {
10728 goto out;
10729 } else if (rv > 0) {
10730 if (virTypedParamsAddULLong(&params, &nparams, &maxparams,
10731 VIR_MIGRATE_PARAM_COMPRESSION_XBZRLE_CACHE,
10732 ullOpt) < 0)
10733 goto save_error;
10736 if (vshCommandOptStringReq(ctl, cmd, "xml", &opt) < 0)
10737 goto out;
10738 if (opt) {
10739 char *xml;
10741 if (virFileReadAll(opt, VSH_MAX_XML_FILE, &xml) < 0) {
10742 vshError(ctl, _("cannot read file '%s'"), opt);
10743 goto save_error;
10746 if (virTypedParamsAddString(&params, &nparams, &maxparams,
10747 VIR_MIGRATE_PARAM_DEST_XML, xml) < 0) {
10748 VIR_FREE(xml);
10749 goto save_error;
10751 VIR_FREE(xml);
10754 if (vshCommandOptStringReq(ctl, cmd, "persistent-xml", &opt) < 0)
10755 goto out;
10756 if (opt) {
10757 char *xml;
10759 if (virFileReadAll(opt, VSH_MAX_XML_FILE, &xml) < 0) {
10760 vshError(ctl, _("cannot read file '%s'"), opt);
10761 goto save_error;
10764 if (virTypedParamsAddString(&params, &nparams, &maxparams,
10765 VIR_MIGRATE_PARAM_PERSIST_XML, xml) < 0) {
10766 VIR_FREE(xml);
10767 goto save_error;
10769 VIR_FREE(xml);
10772 if ((rv = vshCommandOptInt(ctl, cmd, "auto-converge-initial", &intOpt)) < 0) {
10773 goto out;
10774 } else if (rv > 0) {
10775 if (virTypedParamsAddInt(&params, &nparams, &maxparams,
10776 VIR_MIGRATE_PARAM_AUTO_CONVERGE_INITIAL,
10777 intOpt) < 0)
10778 goto save_error;
10781 if ((rv = vshCommandOptInt(ctl, cmd, "auto-converge-increment", &intOpt)) < 0) {
10782 goto out;
10783 } else if (rv > 0) {
10784 if (virTypedParamsAddInt(&params, &nparams, &maxparams,
10785 VIR_MIGRATE_PARAM_AUTO_CONVERGE_INCREMENT,
10786 intOpt) < 0)
10787 goto save_error;
10790 if ((rv = vshCommandOptULongLong(ctl, cmd, "postcopy-bandwidth", &ullOpt)) < 0) {
10791 goto out;
10792 } else if (rv > 0) {
10793 if (virTypedParamsAddULLong(&params, &nparams, &maxparams,
10794 VIR_MIGRATE_PARAM_BANDWIDTH_POSTCOPY,
10795 ullOpt) < 0)
10796 goto save_error;
10799 if ((rv = vshCommandOptInt(ctl, cmd, "parallel-connections", &intOpt)) < 0) {
10800 goto out;
10801 } else if (rv > 0) {
10802 if (virTypedParamsAddInt(&params, &nparams, &maxparams,
10803 VIR_MIGRATE_PARAM_PARALLEL_CONNECTIONS,
10804 intOpt) < 0)
10805 goto save_error;
10808 if (vshCommandOptBool(cmd, "live"))
10809 flags |= VIR_MIGRATE_LIVE;
10810 if (vshCommandOptBool(cmd, "p2p"))
10811 flags |= VIR_MIGRATE_PEER2PEER;
10812 if (vshCommandOptBool(cmd, "tunnelled"))
10813 flags |= VIR_MIGRATE_TUNNELLED;
10815 if (vshCommandOptBool(cmd, "persistent"))
10816 flags |= VIR_MIGRATE_PERSIST_DEST;
10817 if (vshCommandOptBool(cmd, "undefinesource"))
10818 flags |= VIR_MIGRATE_UNDEFINE_SOURCE;
10820 if (vshCommandOptBool(cmd, "suspend"))
10821 flags |= VIR_MIGRATE_PAUSED;
10823 if (vshCommandOptBool(cmd, "copy-storage-all"))
10824 flags |= VIR_MIGRATE_NON_SHARED_DISK;
10826 if (vshCommandOptBool(cmd, "copy-storage-inc"))
10827 flags |= VIR_MIGRATE_NON_SHARED_INC;
10829 if (vshCommandOptBool(cmd, "change-protection"))
10830 flags |= VIR_MIGRATE_CHANGE_PROTECTION;
10832 if (vshCommandOptBool(cmd, "unsafe"))
10833 flags |= VIR_MIGRATE_UNSAFE;
10835 if (vshCommandOptBool(cmd, "compressed"))
10836 flags |= VIR_MIGRATE_COMPRESSED;
10838 if (vshCommandOptBool(cmd, "auto-converge"))
10839 flags |= VIR_MIGRATE_AUTO_CONVERGE;
10841 if (vshCommandOptBool(cmd, "rdma-pin-all"))
10842 flags |= VIR_MIGRATE_RDMA_PIN_ALL;
10844 if (vshCommandOptBool(cmd, "offline"))
10845 flags |= VIR_MIGRATE_OFFLINE;
10847 if (vshCommandOptBool(cmd, "abort-on-error"))
10848 flags |= VIR_MIGRATE_ABORT_ON_ERROR;
10850 if (vshCommandOptBool(cmd, "postcopy"))
10851 flags |= VIR_MIGRATE_POSTCOPY;
10853 if (vshCommandOptBool(cmd, "tls"))
10854 flags |= VIR_MIGRATE_TLS;
10856 if (vshCommandOptBool(cmd, "parallel"))
10857 flags |= VIR_MIGRATE_PARALLEL;
10859 if (flags & VIR_MIGRATE_PEER2PEER || vshCommandOptBool(cmd, "direct")) {
10860 if (virDomainMigrateToURI3(dom, desturi, params, nparams, flags) == 0)
10861 ret = '0';
10862 } else {
10863 /* For traditional live migration, connect to the destination host directly. */
10864 virDomainPtr ddom = NULL;
10866 if ((ddom = virDomainMigrate3(dom, dconn, params, nparams, flags))) {
10867 virshDomainFree(ddom);
10868 ret = '0';
10872 out:
10873 pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
10874 out_sig:
10875 virTypedParamsFree(params, nparams);
10876 virshDomainFree(dom);
10877 ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
10878 return;
10880 save_error:
10881 vshSaveLibvirtError();
10882 goto out;
10885 typedef enum {
10886 VIRSH_MIGRATE_TIMEOUT_DEFAULT,
10887 VIRSH_MIGRATE_TIMEOUT_SUSPEND,
10888 VIRSH_MIGRATE_TIMEOUT_POSTCOPY,
10889 } virshMigrateTimeoutAction;
10891 static void
10892 virshMigrateTimeout(vshControl *ctl,
10893 virDomainPtr dom,
10894 void *opaque)
10896 virshMigrateTimeoutAction action = *(virshMigrateTimeoutAction *) opaque;
10898 switch (action) {
10899 case VIRSH_MIGRATE_TIMEOUT_DEFAULT: /* unreachable */
10900 case VIRSH_MIGRATE_TIMEOUT_SUSPEND:
10901 vshDebug(ctl, VSH_ERR_DEBUG,
10902 "migration timed out; suspending domain\n");
10903 if (virDomainSuspend(dom) < 0)
10904 vshDebug(ctl, VSH_ERR_INFO, "suspending domain failed\n");
10905 break;
10907 case VIRSH_MIGRATE_TIMEOUT_POSTCOPY:
10908 vshDebug(ctl, VSH_ERR_DEBUG,
10909 "migration timed out; switching to post-copy\n");
10910 if (virDomainMigrateStartPostCopy(dom, 0) < 0)
10911 vshDebug(ctl, VSH_ERR_INFO, "switching to post-copy failed\n");
10912 break;
10916 static void
10917 virshMigrateIteration(virConnectPtr conn ATTRIBUTE_UNUSED,
10918 virDomainPtr dom,
10919 int iteration,
10920 void *opaque)
10922 vshControl *ctl = opaque;
10924 if (iteration == 2) {
10925 vshDebug(ctl, VSH_ERR_DEBUG,
10926 "iteration %d finished; switching to post-copy\n",
10927 iteration - 1);
10928 if (virDomainMigrateStartPostCopy(dom, 0) < 0)
10929 vshDebug(ctl, VSH_ERR_INFO, "switching to post-copy failed\n");
10933 static bool
10934 cmdMigrate(vshControl *ctl, const vshCmd *cmd)
10936 virDomainPtr dom = NULL;
10937 int p[2] = {-1, -1};
10938 virThread workerThread;
10939 bool verbose = false;
10940 bool functionReturn = false;
10941 int timeout = 0;
10942 virshMigrateTimeoutAction timeoutAction = VIRSH_MIGRATE_TIMEOUT_DEFAULT;
10943 bool live_flag = false;
10944 virshCtrlData data = { .dconn = NULL };
10945 virshControlPtr priv = ctl->privData;
10946 int iterEvent = -1;
10948 VSH_EXCLUSIVE_OPTIONS("live", "offline");
10949 VSH_EXCLUSIVE_OPTIONS("timeout-suspend", "timeout-postcopy");
10950 VSH_REQUIRE_OPTION("postcopy-after-precopy", "postcopy");
10951 VSH_REQUIRE_OPTION("timeout-postcopy", "postcopy");
10952 VSH_REQUIRE_OPTION("persistent-xml", "persistent");
10954 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
10955 return false;
10957 if (vshCommandOptBool(cmd, "verbose"))
10958 verbose = true;
10960 if (vshCommandOptBool(cmd, "live"))
10961 live_flag = true;
10962 if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) {
10963 goto cleanup;
10964 } else if (timeout > 0 && !live_flag) {
10965 vshError(ctl, "%s",
10966 _("migrate: Unexpected timeout for offline migration"));
10967 goto cleanup;
10970 if (vshCommandOptBool(cmd, "timeout-suspend"))
10971 timeoutAction = VIRSH_MIGRATE_TIMEOUT_SUSPEND;
10972 if (vshCommandOptBool(cmd, "timeout-postcopy"))
10973 timeoutAction = VIRSH_MIGRATE_TIMEOUT_POSTCOPY;
10974 if (timeout > 0) {
10975 if (timeoutAction == VIRSH_MIGRATE_TIMEOUT_DEFAULT)
10976 timeoutAction = VIRSH_MIGRATE_TIMEOUT_SUSPEND;
10977 } else if (timeoutAction) {
10978 vshError(ctl, "%s",
10979 _("migrate: Unexpected --timeout-* option without --timeout"));
10980 goto cleanup;
10983 if (vshCommandOptBool(cmd, "postcopy-after-precopy")) {
10984 iterEvent = virConnectDomainEventRegisterAny(
10985 priv->conn, dom,
10986 VIR_DOMAIN_EVENT_ID_MIGRATION_ITERATION,
10987 VIR_DOMAIN_EVENT_CALLBACK(virshMigrateIteration),
10988 ctl, NULL);
10989 if (iterEvent < 0)
10990 goto cleanup;
10993 if (pipe(p) < 0)
10994 goto cleanup;
10996 data.ctl = ctl;
10997 data.cmd = cmd;
10998 data.writefd = p[1];
11000 if (vshCommandOptBool(cmd, "p2p") || vshCommandOptBool(cmd, "direct")) {
11001 data.dconn = NULL;
11002 } else {
11003 /* For traditional live migration, connect to the destination host. */
11004 virConnectPtr dconn = NULL;
11005 const char *desturi = NULL;
11007 if (vshCommandOptStringReq(ctl, cmd, "desturi", &desturi) < 0)
11008 goto cleanup;
11010 dconn = virshConnect(ctl, desturi, false);
11011 if (!dconn)
11012 goto cleanup;
11014 data.dconn = dconn;
11017 if (virThreadCreate(&workerThread,
11018 true,
11019 doMigrate,
11020 &data) < 0)
11021 goto cleanup;
11022 functionReturn = virshWatchJob(ctl, dom, verbose, p[0], timeout,
11023 virshMigrateTimeout,
11024 &timeoutAction, _("Migration"));
11026 virThreadJoin(&workerThread);
11028 cleanup:
11029 if (data.dconn)
11030 virConnectClose(data.dconn);
11031 if (iterEvent != -1)
11032 virConnectDomainEventDeregisterAny(priv->conn, iterEvent);
11033 virshDomainFree(dom);
11034 VIR_FORCE_CLOSE(p[0]);
11035 VIR_FORCE_CLOSE(p[1]);
11036 return functionReturn;
11040 * "migrate-setmaxdowntime" command
11042 static const vshCmdInfo info_migrate_setmaxdowntime[] = {
11043 {.name = "help",
11044 .data = N_("set maximum tolerable downtime")
11046 {.name = "desc",
11047 .data = N_("Set maximum tolerable downtime of a domain which is being live-migrated to another host.")
11049 {.name = NULL}
11052 static const vshCmdOptDef opts_migrate_setmaxdowntime[] = {
11053 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
11054 {.name = "downtime",
11055 .type = VSH_OT_INT,
11056 .flags = VSH_OFLAG_REQ,
11057 .help = N_("maximum tolerable downtime (in milliseconds) for migration")
11059 {.name = NULL}
11062 static bool
11063 cmdMigrateSetMaxDowntime(vshControl *ctl, const vshCmd *cmd)
11065 virDomainPtr dom = NULL;
11066 unsigned long long downtime = 0;
11067 bool ret = false;
11069 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11070 return false;
11072 if (vshCommandOptULongLong(ctl, cmd, "downtime", &downtime) < 0)
11073 goto done;
11074 if (downtime < 1) {
11075 vshError(ctl, "%s", _("migrate: Invalid downtime"));
11076 goto done;
11079 if (virDomainMigrateSetMaxDowntime(dom, downtime, 0))
11080 goto done;
11082 ret = true;
11084 done:
11085 virshDomainFree(dom);
11086 return ret;
11091 * "migrate-getmaxdowntime" command
11093 static const vshCmdInfo info_migrate_getmaxdowntime[] = {
11094 {.name = "help",
11095 .data = N_("get maximum tolerable downtime")
11097 {.name = "desc",
11098 .data = N_("Get maximum tolerable downtime of a domain which is being live-migrated to another host.")
11100 {.name = NULL}
11103 static const vshCmdOptDef opts_migrate_getmaxdowntime[] = {
11104 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
11105 {.name = NULL}
11108 static bool
11109 cmdMigrateGetMaxDowntime(vshControl *ctl, const vshCmd *cmd)
11111 virDomainPtr dom = NULL;
11112 unsigned long long downtime;
11113 bool ret = false;
11115 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11116 return false;
11118 if (virDomainMigrateGetMaxDowntime(dom, &downtime, 0) < 0)
11119 goto done;
11121 vshPrint(ctl, "%llu\n", downtime);
11122 ret = true;
11124 done:
11125 virshDomainFree(dom);
11126 return ret;
11131 * "migrate-compcache" command
11133 static const vshCmdInfo info_migrate_compcache[] = {
11134 {.name = "help",
11135 .data = N_("get/set compression cache size")
11137 {.name = "desc",
11138 .data = N_("Get/set size of the cache (in bytes) used for compressing "
11139 "repeatedly transferred memory pages during live migration.")
11141 {.name = NULL}
11144 static const vshCmdOptDef opts_migrate_compcache[] = {
11145 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
11146 {.name = "size",
11147 .type = VSH_OT_INT,
11148 .flags = VSH_OFLAG_REQ_OPT,
11149 .help = N_("requested size of the cache (in bytes) used for compression")
11151 {.name = NULL}
11154 static bool
11155 cmdMigrateCompCache(vshControl *ctl, const vshCmd *cmd)
11157 virDomainPtr dom = NULL;
11158 unsigned long long size = 0;
11159 bool ret = false;
11160 const char *unit;
11161 double value;
11162 int rc;
11164 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11165 return false;
11167 rc = vshCommandOptULongLong(ctl, cmd, "size", &size);
11168 if (rc < 0) {
11169 goto cleanup;
11170 } else if (rc != 0) {
11171 if (virDomainMigrateSetCompressionCache(dom, size, 0) < 0)
11172 goto cleanup;
11175 if (virDomainMigrateGetCompressionCache(dom, &size, 0) < 0)
11176 goto cleanup;
11178 value = vshPrettyCapacity(size, &unit);
11179 vshPrint(ctl, _("Compression cache: %.3lf %s"), value, unit);
11181 ret = true;
11182 cleanup:
11183 virshDomainFree(dom);
11184 return ret;
11188 * "migrate-setspeed" command
11190 static const vshCmdInfo info_migrate_setspeed[] = {
11191 {.name = "help",
11192 .data = N_("Set the maximum migration bandwidth")
11194 {.name = "desc",
11195 .data = N_("Set the maximum migration bandwidth (in MiB/s) for a domain "
11196 "which is being migrated to another host.")
11198 {.name = NULL}
11201 static const vshCmdOptDef opts_migrate_setspeed[] = {
11202 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
11203 {.name = "bandwidth",
11204 .type = VSH_OT_INT,
11205 .flags = VSH_OFLAG_REQ,
11206 .help = N_("migration bandwidth limit in MiB/s")
11208 {.name = "postcopy",
11209 .type = VSH_OT_BOOL,
11210 .help = N_("set post-copy migration bandwidth")
11212 {.name = NULL}
11215 static bool
11216 cmdMigrateSetMaxSpeed(vshControl *ctl, const vshCmd *cmd)
11218 virDomainPtr dom = NULL;
11219 unsigned long bandwidth = 0;
11220 unsigned int flags = 0;
11221 bool ret = false;
11223 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11224 return false;
11226 if (vshCommandOptULWrap(ctl, cmd, "bandwidth", &bandwidth) < 0)
11227 goto done;
11229 if (vshCommandOptBool(cmd, "postcopy"))
11230 flags |= VIR_DOMAIN_MIGRATE_MAX_SPEED_POSTCOPY;
11232 if (virDomainMigrateSetMaxSpeed(dom, bandwidth, flags) < 0)
11233 goto done;
11235 ret = true;
11237 done:
11238 virshDomainFree(dom);
11239 return ret;
11243 * "migrate-getspeed" command
11245 static const vshCmdInfo info_migrate_getspeed[] = {
11246 {.name = "help",
11247 .data = N_("Get the maximum migration bandwidth")
11249 {.name = "desc",
11250 .data = N_("Get the maximum migration bandwidth (in MiB/s) for a domain.")
11252 {.name = NULL}
11255 static const vshCmdOptDef opts_migrate_getspeed[] = {
11256 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
11257 {.name = "postcopy",
11258 .type = VSH_OT_BOOL,
11259 .help = N_("get post-copy migration bandwidth")
11261 {.name = NULL}
11264 static bool
11265 cmdMigrateGetMaxSpeed(vshControl *ctl, const vshCmd *cmd)
11267 virDomainPtr dom = NULL;
11268 unsigned long bandwidth;
11269 unsigned int flags = 0;
11270 bool ret = false;
11272 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11273 return false;
11275 if (vshCommandOptBool(cmd, "postcopy"))
11276 flags |= VIR_DOMAIN_MIGRATE_MAX_SPEED_POSTCOPY;
11278 if (virDomainMigrateGetMaxSpeed(dom, &bandwidth, flags) < 0)
11279 goto done;
11281 vshPrint(ctl, "%lu\n", bandwidth);
11283 ret = true;
11285 done:
11286 virshDomainFree(dom);
11287 return ret;
11291 * "migrate-postcopy" command
11293 static const vshCmdInfo info_migrate_postcopy[] = {
11294 {.name = "help",
11295 .data = N_("Switch running migration from pre-copy to post-copy")
11297 {.name = "desc",
11298 .data = N_("Switch running migration from pre-copy to post-copy. "
11299 "The migration must have been started with --postcopy option.")
11301 {.name = NULL}
11304 static const vshCmdOptDef opts_migrate_postcopy[] = {
11305 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
11306 {.name = NULL}
11309 static bool
11310 cmdMigratePostCopy(vshControl *ctl, const vshCmd *cmd)
11312 virDomainPtr dom;
11313 bool ret = false;
11315 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11316 return false;
11318 if (virDomainMigrateStartPostCopy(dom, 0) < 0)
11319 goto cleanup;
11321 ret = true;
11323 cleanup:
11324 virshDomainFree(dom);
11325 return ret;
11329 * "domdisplay" command
11331 static const vshCmdInfo info_domdisplay[] = {
11332 {.name = "help",
11333 .data = N_("domain display connection URI")
11335 {.name = "desc",
11336 .data = N_("Output the IP address and port number "
11337 "for the graphical display.")
11339 {.name = NULL}
11342 static const vshCmdOptDef opts_domdisplay[] = {
11343 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
11344 {.name = "include-password",
11345 .type = VSH_OT_BOOL,
11346 .help = N_("includes the password into the connection URI if available")
11348 {.name = "type",
11349 .type = VSH_OT_STRING,
11350 .help = N_("select particular graphical display "
11351 "(e.g. \"vnc\", \"spice\", \"rdp\")")
11353 {.name = "all",
11354 .type = VSH_OT_BOOL,
11355 .help = N_("show all possible graphical displays")
11357 {.name = NULL}
11360 static bool
11361 cmdDomDisplay(vshControl *ctl, const vshCmd *cmd)
11363 xmlDocPtr xml = NULL;
11364 xmlXPathContextPtr ctxt = NULL;
11365 virDomainPtr dom;
11366 virBuffer buf = VIR_BUFFER_INITIALIZER;
11367 bool ret = false;
11368 char *xpath = NULL;
11369 char *listen_addr = NULL;
11370 int port, tls_port = 0;
11371 char *type_conn = NULL;
11372 char *sockpath = NULL;
11373 char *passwd = NULL;
11374 char *output = NULL;
11375 const char *scheme[] = { "vnc", "spice", "rdp", NULL };
11376 const char *type = NULL;
11377 int iter = 0;
11378 int tmp;
11379 int flags = 0;
11380 bool params = false;
11381 bool all = vshCommandOptBool(cmd, "all");
11382 const char *xpath_fmt = "string(/domain/devices/graphics[@type='%s']/%s)";
11383 virSocketAddr addr;
11385 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11386 return false;
11388 if (!virDomainIsActive(dom)) {
11389 vshError(ctl, _("Domain is not running"));
11390 goto cleanup;
11393 if (vshCommandOptBool(cmd, "include-password"))
11394 flags |= VIR_DOMAIN_XML_SECURE;
11396 if (vshCommandOptStringReq(ctl, cmd, "type", &type) < 0)
11397 goto cleanup;
11399 if (virshDomainGetXMLFromDom(ctl, dom, flags, &xml, &ctxt) < 0)
11400 goto cleanup;
11402 /* Attempt to grab our display info */
11403 for (iter = 0; scheme[iter] != NULL; iter++) {
11404 /* Particular scheme requested */
11405 if (!all && type && STRNEQ(type, scheme[iter]))
11406 continue;
11408 /* Create our XPATH lookup for the current display's port */
11409 VIR_FREE(xpath);
11410 if (virAsprintf(&xpath, xpath_fmt, scheme[iter], "@port") < 0)
11411 goto cleanup;
11413 /* Attempt to get the port number for the current graphics scheme */
11414 tmp = virXPathInt(xpath, ctxt, &port);
11415 VIR_FREE(xpath);
11417 /* If there is no port number for this type, then jump to the next
11418 * scheme */
11419 if (tmp)
11420 port = 0;
11422 /* Create our XPATH lookup for TLS Port (automatically skipped
11423 * for unsupported schemes */
11424 if (virAsprintf(&xpath, xpath_fmt, scheme[iter], "@tlsPort") < 0)
11425 goto cleanup;
11427 /* Attempt to get the TLS port number */
11428 tmp = virXPathInt(xpath, ctxt, &tls_port);
11429 VIR_FREE(xpath);
11430 if (tmp)
11431 tls_port = 0;
11433 /* Create our XPATH lookup for the current display's address */
11434 if (virAsprintf(&xpath, xpath_fmt, scheme[iter], "@listen") < 0)
11435 goto cleanup;
11437 /* Attempt to get the listening addr if set for the current
11438 * graphics scheme */
11439 VIR_FREE(listen_addr);
11440 listen_addr = virXPathString(xpath, ctxt);
11441 VIR_FREE(xpath);
11443 /* Create our XPATH lookup for the current spice type. */
11444 if (virAsprintf(&xpath, xpath_fmt, scheme[iter], "listen/@type") < 0)
11445 goto cleanup;
11447 /* Attempt to get the type of spice connection */
11448 VIR_FREE(type_conn);
11449 type_conn = virXPathString(xpath, ctxt);
11450 VIR_FREE(xpath);
11452 if (STREQ_NULLABLE(type_conn, "socket")) {
11453 if (!sockpath) {
11454 if (virAsprintf(&xpath, xpath_fmt, scheme[iter], "listen/@socket") < 0)
11455 goto cleanup;
11457 sockpath = virXPathString(xpath, ctxt);
11459 VIR_FREE(xpath);
11463 if (!port && !tls_port && !sockpath)
11464 continue;
11466 if (!listen_addr) {
11467 /* The subelement address - <listen address='xyz'/> -
11468 * *should* have been automatically backfilled into its
11469 * parent <graphics listen='xyz'> (which we just tried to
11470 * retrieve into listen_addr above) but in some cases it
11471 * isn't, so we also do an explicit check for the
11472 * subelement (which, by the way, doesn't exist on libvirt
11473 * < 0.9.4, so we really do need to check both places)
11475 if (virAsprintf(&xpath, xpath_fmt, scheme[iter], "listen/@address") < 0)
11476 goto cleanup;
11478 listen_addr = virXPathString(xpath, ctxt);
11479 VIR_FREE(xpath);
11480 } else {
11481 /* If listen_addr is 0.0.0.0 or [::] we should try to parse URI and set
11482 * listen_addr based on current URI. */
11483 if (virSocketAddrParse(&addr, listen_addr, AF_UNSPEC) > 0 &&
11484 virSocketAddrIsWildcard(&addr)) {
11486 virConnectPtr conn = ((virshControlPtr)(ctl->privData))->conn;
11487 char *uriStr = virConnectGetURI(conn);
11488 virURIPtr uri = NULL;
11490 if (uriStr) {
11491 uri = virURIParse(uriStr);
11492 VIR_FREE(uriStr);
11495 /* It's safe to free the listen_addr even if parsing of URI
11496 * fails, if there is no listen_addr we will print "localhost". */
11497 VIR_FREE(listen_addr);
11499 if (uri) {
11500 listen_addr = vshStrdup(ctl, uri->server);
11501 virURIFree(uri);
11506 /* We can query this info for all the graphics types since we'll
11507 * get nothing for the unsupported ones (just rdp for now).
11508 * Also the parameter '--include-password' was already taken
11509 * care of when getting the XML */
11511 /* Create our XPATH lookup for the password */
11512 if (virAsprintf(&xpath, xpath_fmt, scheme[iter], "@passwd") < 0)
11513 goto cleanup;
11515 /* Attempt to get the password */
11516 VIR_FREE(passwd);
11517 passwd = virXPathString(xpath, ctxt);
11518 VIR_FREE(xpath);
11520 /* Build up the full URI, starting with the scheme */
11521 if (sockpath)
11522 virBufferAsprintf(&buf, "%s+unix://", scheme[iter]);
11523 else
11524 virBufferAsprintf(&buf, "%s://", scheme[iter]);
11526 /* There is no user, so just append password if there's any */
11527 if (STREQ(scheme[iter], "vnc") && passwd)
11528 virBufferAsprintf(&buf, ":%s@", passwd);
11530 /* Then host name or IP */
11531 if (!listen_addr && !sockpath)
11532 virBufferAddLit(&buf, "localhost");
11533 else if (!sockpath && strchr(listen_addr, ':'))
11534 virBufferAsprintf(&buf, "[%s]", listen_addr);
11535 else if (sockpath)
11536 virBufferAsprintf(&buf, "%s", sockpath);
11537 else
11538 virBufferAsprintf(&buf, "%s", listen_addr);
11540 /* Free socket to prepare the pointer for the next iteration */
11541 VIR_FREE(sockpath);
11543 /* Add the port */
11544 if (port) {
11545 if (STREQ(scheme[iter], "vnc")) {
11546 /* VNC protocol handlers take their port number as
11547 * 'port' - 5900 */
11548 port -= 5900;
11551 virBufferAsprintf(&buf, ":%d", port);
11554 /* TLS Port */
11555 if (tls_port) {
11556 virBufferAsprintf(&buf,
11557 "?tls-port=%d",
11558 tls_port);
11559 params = true;
11562 if (STREQ(scheme[iter], "spice") && passwd) {
11563 virBufferAsprintf(&buf,
11564 "%spassword=%s",
11565 params ? "&" : "?",
11566 passwd);
11567 params = true;
11570 /* Ensure we can print our URI */
11571 if (virBufferError(&buf)) {
11572 vshError(ctl, "%s", _("Failed to create display URI"));
11573 goto cleanup;
11576 /* Print out our full URI */
11577 VIR_FREE(output);
11578 output = virBufferContentAndReset(&buf);
11579 vshPrint(ctl, "%s", output);
11581 /* We got what we came for so return successfully */
11582 ret = true;
11583 if (!all) {
11584 break;
11585 } else {
11586 vshPrint(ctl, "\n");
11590 if (!ret) {
11591 if (type)
11592 vshError(ctl, _("No graphical display with type '%s' found"), type);
11593 else
11594 vshError(ctl, _("No graphical display found"));
11597 cleanup:
11598 VIR_FREE(xpath);
11599 VIR_FREE(type_conn);
11600 VIR_FREE(sockpath);
11601 VIR_FREE(passwd);
11602 VIR_FREE(listen_addr);
11603 VIR_FREE(output);
11604 xmlXPathFreeContext(ctxt);
11605 xmlFreeDoc(xml);
11606 virshDomainFree(dom);
11607 return ret;
11611 * "vncdisplay" command
11613 static const vshCmdInfo info_vncdisplay[] = {
11614 {.name = "help",
11615 .data = N_("vnc display")
11617 {.name = "desc",
11618 .data = N_("Output the IP address and port number for the VNC display.")
11620 {.name = NULL}
11623 static const vshCmdOptDef opts_vncdisplay[] = {
11624 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
11625 {.name = NULL}
11628 static bool
11629 cmdVNCDisplay(vshControl *ctl, const vshCmd *cmd)
11631 xmlDocPtr xml = NULL;
11632 xmlXPathContextPtr ctxt = NULL;
11633 virDomainPtr dom;
11634 bool ret = false;
11635 int port = 0;
11636 char *listen_addr = NULL;
11638 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11639 return false;
11641 /* Check if the domain is active and don't rely on -1 for this */
11642 if (!virDomainIsActive(dom)) {
11643 vshError(ctl, _("Domain is not running"));
11644 goto cleanup;
11647 if (virshDomainGetXMLFromDom(ctl, dom, 0, &xml, &ctxt) < 0)
11648 goto cleanup;
11650 /* Get the VNC port */
11651 if (virXPathInt("string(/domain/devices/graphics[@type='vnc']/@port)",
11652 ctxt, &port)) {
11653 vshError(ctl, _("Failed to get VNC port. Is this domain using VNC?"));
11654 goto cleanup;
11657 listen_addr = virXPathString("string(/domain/devices/graphics"
11658 "[@type='vnc']/@listen)", ctxt);
11659 if (!listen_addr) {
11660 /* The subelement address - <listen address='xyz'/> -
11661 * *should* have been automatically backfilled into its
11662 * parent <graphics listen='xyz'> (which we just tried to
11663 * retrieve into listen_addr above) but in some cases it
11664 * isn't, so we also do an explicit check for the
11665 * subelement (which, by the way, doesn't exist on libvirt
11666 * < 0.9.4, so we really do need to check both places)
11668 listen_addr = virXPathString("string(/domain/devices/graphics"
11669 "[@type='vnc']/listen/@address)", ctxt);
11671 if (listen_addr == NULL || STREQ(listen_addr, "0.0.0.0"))
11672 vshPrint(ctl, ":%d\n", port-5900);
11673 else
11674 vshPrint(ctl, "%s:%d\n", listen_addr, port-5900);
11676 ret = true;
11678 cleanup:
11679 VIR_FREE(listen_addr);
11680 xmlXPathFreeContext(ctxt);
11681 xmlFreeDoc(xml);
11682 virshDomainFree(dom);
11683 return ret;
11687 * "ttyconsole" command
11689 static const vshCmdInfo info_ttyconsole[] = {
11690 {.name = "help",
11691 .data = N_("tty console")
11693 {.name = "desc",
11694 .data = N_("Output the device for the TTY console.")
11696 {.name = NULL}
11699 static const vshCmdOptDef opts_ttyconsole[] = {
11700 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
11701 {.name = NULL}
11704 static bool
11705 cmdTTYConsole(vshControl *ctl, const vshCmd *cmd)
11707 xmlDocPtr xml = NULL;
11708 xmlXPathContextPtr ctxt = NULL;
11709 bool ret = false;
11710 char *tty = NULL;
11712 if (virshDomainGetXML(ctl, cmd, 0, &xml, &ctxt) < 0)
11713 return false;
11715 if (!(tty = virXPathString("string(/domain/devices/console/@tty)", ctxt)))
11716 goto cleanup;
11718 vshPrint(ctl, "%s\n", tty);
11719 ret = true;
11721 cleanup:
11722 xmlXPathFreeContext(ctxt);
11723 xmlFreeDoc(xml);
11724 VIR_FREE(tty);
11725 return ret;
11729 * "domhostname" command
11731 static const vshCmdInfo info_domhostname[] = {
11732 {.name = "help",
11733 .data = N_("print the domain's hostname")
11735 {.name = "desc",
11736 .data = ""
11738 {.name = NULL}
11741 static const vshCmdOptDef opts_domhostname[] = {
11742 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
11743 {.name = NULL}
11746 static bool
11747 cmdDomHostname(vshControl *ctl, const vshCmd *cmd)
11749 char *hostname;
11750 virDomainPtr dom;
11751 bool ret = false;
11753 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11754 return false;
11756 hostname = virDomainGetHostname(dom, 0);
11757 if (hostname == NULL) {
11758 vshError(ctl, "%s", _("failed to get hostname"));
11759 goto error;
11762 vshPrint(ctl, "%s\n", hostname);
11763 ret = true;
11765 error:
11766 VIR_FREE(hostname);
11767 virshDomainFree(dom);
11768 return ret;
11772 * Check if n1 is superset of n2, meaning n1 contains all elements and
11773 * attributes as n2 at least. Including children.
11774 * @n1 first node
11775 * @n2 second node
11776 * returns true in case n1 covers n2, false otherwise.
11778 ATTRIBUTE_UNUSED
11779 static bool
11780 virshNodeIsSuperset(xmlNodePtr n1, xmlNodePtr n2)
11782 xmlNodePtr child1, child2;
11783 xmlAttrPtr attr;
11784 char *prop1, *prop2;
11785 bool found;
11786 bool visited;
11787 bool ret = false;
11788 long n1_child_size, n2_child_size, n1_iter;
11789 virBitmapPtr bitmap;
11791 if (!n1 && !n2)
11792 return true;
11794 if (!n1 || !n2)
11795 return false;
11797 if (!xmlStrEqual(n1->name, n2->name))
11798 return false;
11800 /* Iterate over n2 attributes and check if n1 contains them*/
11801 attr = n2->properties;
11802 while (attr) {
11803 if (attr->type == XML_ATTRIBUTE_NODE) {
11804 prop1 = virXMLPropString(n1, (const char *) attr->name);
11805 prop2 = virXMLPropString(n2, (const char *) attr->name);
11806 if (STRNEQ_NULLABLE(prop1, prop2)) {
11807 xmlFree(prop1);
11808 xmlFree(prop2);
11809 return false;
11811 xmlFree(prop1);
11812 xmlFree(prop2);
11814 attr = attr->next;
11817 n1_child_size = virXMLChildElementCount(n1);
11818 n2_child_size = virXMLChildElementCount(n2);
11819 if (n1_child_size < 0 || n2_child_size < 0 ||
11820 n1_child_size < n2_child_size)
11821 return false;
11823 if (n1_child_size == 0 && n2_child_size == 0)
11824 return true;
11826 if (!(bitmap = virBitmapNew(n1_child_size)))
11827 return false;
11829 child2 = n2->children;
11830 while (child2) {
11831 if (child2->type != XML_ELEMENT_NODE) {
11832 child2 = child2->next;
11833 continue;
11836 child1 = n1->children;
11837 n1_iter = 0;
11838 found = false;
11839 while (child1) {
11840 if (child1->type != XML_ELEMENT_NODE) {
11841 child1 = child1->next;
11842 continue;
11845 if (virBitmapGetBit(bitmap, n1_iter, &visited) < 0) {
11846 vshError(NULL, "%s", _("Bad child elements counting."));
11847 goto cleanup;
11850 if (visited) {
11851 child1 = child1->next;
11852 n1_iter++;
11853 continue;
11856 if (xmlStrEqual(child1->name, child2->name)) {
11857 found = true;
11858 if (virBitmapSetBit(bitmap, n1_iter) < 0) {
11859 vshError(NULL, "%s", _("Bad child elements counting."));
11860 goto cleanup;
11863 if (!virshNodeIsSuperset(child1, child2))
11864 goto cleanup;
11866 break;
11869 child1 = child1->next;
11870 n1_iter++;
11873 if (!found)
11874 goto cleanup;
11876 child2 = child2->next;
11879 ret = true;
11881 cleanup:
11882 virBitmapFree(bitmap);
11883 return ret;
11888 * "detach-device" command
11890 static const vshCmdInfo info_detach_device[] = {
11891 {.name = "help",
11892 .data = N_("detach device from an XML file")
11894 {.name = "desc",
11895 .data = N_("Detach device from an XML <file>")
11897 {.name = NULL}
11900 static const vshCmdOptDef opts_detach_device[] = {
11901 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
11902 VIRSH_COMMON_OPT_FILE(N_("XML file")),
11903 VIRSH_COMMON_OPT_DOMAIN_PERSISTENT,
11904 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
11905 VIRSH_COMMON_OPT_DOMAIN_LIVE,
11906 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
11907 {.name = NULL}
11910 static bool
11911 cmdDetachDevice(vshControl *ctl, const vshCmd *cmd)
11913 virDomainPtr dom = NULL;
11914 const char *from = NULL;
11915 char *buffer = NULL;
11916 int ret;
11917 bool funcRet = false;
11918 bool current = vshCommandOptBool(cmd, "current");
11919 bool config = vshCommandOptBool(cmd, "config");
11920 bool live = vshCommandOptBool(cmd, "live");
11921 bool persistent = vshCommandOptBool(cmd, "persistent");
11922 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
11924 VSH_EXCLUSIVE_OPTIONS_VAR(persistent, current);
11926 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
11927 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
11929 if (config || persistent)
11930 flags |= VIR_DOMAIN_AFFECT_CONFIG;
11931 if (live)
11932 flags |= VIR_DOMAIN_AFFECT_LIVE;
11934 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11935 return false;
11937 if (persistent &&
11938 virDomainIsActive(dom) == 1)
11939 flags |= VIR_DOMAIN_AFFECT_LIVE;
11941 if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
11942 goto cleanup;
11944 if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) {
11945 vshReportError(ctl);
11946 goto cleanup;
11949 if (flags != 0 || current)
11950 ret = virDomainDetachDeviceFlags(dom, buffer, flags);
11951 else
11952 ret = virDomainDetachDevice(dom, buffer);
11954 if (ret < 0) {
11955 vshError(ctl, _("Failed to detach device from %s"), from);
11956 goto cleanup;
11959 vshPrintExtra(ctl, "%s", _("Device detached successfully\n"));
11960 funcRet = true;
11962 cleanup:
11963 VIR_FREE(buffer);
11964 virshDomainFree(dom);
11965 return funcRet;
11970 * "detach-device-alias" command
11972 static const vshCmdInfo info_detach_device_alias[] = {
11973 {.name = "help",
11974 .data = N_("detach device from an alias")
11976 {.name = "desc",
11977 .data = N_("Detach device identified by the given alias from a domain")
11979 {.name = NULL}
11982 static const vshCmdOptDef opts_detach_device_alias[] = {
11983 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
11984 {.name = "alias",
11985 .type = VSH_OT_DATA,
11986 .flags = VSH_OFLAG_REQ,
11987 .completer = virshDomainDeviceAliasCompleter,
11988 .help = N_("device alias")
11990 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
11991 VIRSH_COMMON_OPT_DOMAIN_LIVE,
11992 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
11993 {.name = NULL}
11996 static bool
11997 cmdDetachDeviceAlias(vshControl *ctl, const vshCmd *cmd)
11999 virDomainPtr dom = NULL;
12000 const char *alias = NULL;
12001 bool current = vshCommandOptBool(cmd, "current");
12002 bool config = vshCommandOptBool(cmd, "config");
12003 bool live = vshCommandOptBool(cmd, "live");
12004 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
12005 bool ret = false;
12007 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
12008 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
12010 if (config)
12011 flags |= VIR_DOMAIN_AFFECT_CONFIG;
12012 if (live)
12013 flags |= VIR_DOMAIN_AFFECT_LIVE;
12015 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
12016 return false;
12018 if (vshCommandOptStringReq(ctl, cmd, "alias", &alias) < 0)
12019 goto cleanup;
12021 if (virDomainDetachDeviceAlias(dom, alias, flags) < 0) {
12022 vshError(ctl, _("Failed to detach device with alias %s"), alias);
12023 goto cleanup;
12026 vshPrintExtra(ctl, "%s", _("Device detach request sent successfully\n"));
12027 ret = true;
12029 cleanup:
12030 virshDomainFree(dom);
12031 return ret;
12036 * "update-device" command
12038 static const vshCmdInfo info_update_device[] = {
12039 {.name = "help",
12040 .data = N_("update device from an XML file")
12042 {.name = "desc",
12043 .data = N_("Update device from an XML <file>.")
12045 {.name = NULL}
12048 static const vshCmdOptDef opts_update_device[] = {
12049 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
12050 VIRSH_COMMON_OPT_FILE(N_("XML file")),
12051 VIRSH_COMMON_OPT_DOMAIN_PERSISTENT,
12052 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
12053 VIRSH_COMMON_OPT_DOMAIN_LIVE,
12054 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
12055 {.name = "force",
12056 .type = VSH_OT_BOOL,
12057 .help = N_("force device update")
12059 {.name = NULL}
12062 static bool
12063 cmdUpdateDevice(vshControl *ctl, const vshCmd *cmd)
12065 virDomainPtr dom;
12066 const char *from = NULL;
12067 char *buffer = NULL;
12068 bool ret = false;
12069 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
12070 bool current = vshCommandOptBool(cmd, "current");
12071 bool config = vshCommandOptBool(cmd, "config");
12072 bool live = vshCommandOptBool(cmd, "live");
12073 bool persistent = vshCommandOptBool(cmd, "persistent");
12075 VSH_EXCLUSIVE_OPTIONS_VAR(persistent, current);
12077 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
12078 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
12080 if (config || persistent)
12081 flags |= VIR_DOMAIN_AFFECT_CONFIG;
12082 if (live)
12083 flags |= VIR_DOMAIN_AFFECT_LIVE;
12085 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
12086 return false;
12088 if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
12089 goto cleanup;
12091 if (persistent &&
12092 virDomainIsActive(dom) == 1)
12093 flags |= VIR_DOMAIN_AFFECT_LIVE;
12095 if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) {
12096 vshReportError(ctl);
12097 goto cleanup;
12100 if (vshCommandOptBool(cmd, "force"))
12101 flags |= VIR_DOMAIN_DEVICE_MODIFY_FORCE;
12103 if (virDomainUpdateDeviceFlags(dom, buffer, flags) < 0) {
12104 vshError(ctl, _("Failed to update device from %s"), from);
12105 goto cleanup;
12108 vshPrintExtra(ctl, "%s", _("Device updated successfully\n"));
12109 ret = true;
12111 cleanup:
12112 VIR_FREE(buffer);
12113 virshDomainFree(dom);
12114 return ret;
12118 * "detach-interface" command
12120 static const vshCmdInfo info_detach_interface[] = {
12121 {.name = "help",
12122 .data = N_("detach network interface")
12124 {.name = "desc",
12125 .data = N_("Detach network interface.")
12127 {.name = NULL}
12130 static const vshCmdOptDef opts_detach_interface[] = {
12131 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
12132 {.name = "type",
12133 .type = VSH_OT_DATA,
12134 .flags = VSH_OFLAG_REQ,
12135 .help = N_("network interface type")
12137 {.name = "mac",
12138 .type = VSH_OT_STRING,
12139 .completer = virshDomainInterfaceCompleter,
12140 .completer_flags = VIRSH_DOMAIN_INTERFACE_COMPLETER_MAC,
12141 .help = N_("MAC address")
12143 VIRSH_COMMON_OPT_DOMAIN_PERSISTENT,
12144 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
12145 VIRSH_COMMON_OPT_DOMAIN_LIVE,
12146 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
12147 {.name = NULL}
12150 static bool
12151 virshDomainDetachInterface(char *doc,
12152 unsigned int flags,
12153 virDomainPtr dom,
12154 vshControl *ctl,
12155 bool current,
12156 const char *type,
12157 const char *mac)
12159 xmlDocPtr xml = NULL;
12160 xmlXPathObjectPtr obj = NULL;
12161 xmlXPathContextPtr ctxt = NULL;
12162 xmlNodePtr cur = NULL, matchNode = NULL;
12163 char *detach_xml = NULL, buf[64];
12164 int diff_mac, ret = -1;
12165 size_t i;
12167 if (!(xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt))) {
12168 vshError(ctl, "%s", _("Failed to get interface information"));
12169 goto cleanup;
12172 snprintf(buf, sizeof(buf), "/domain/devices/interface[@type='%s']", type);
12173 obj = xmlXPathEval(BAD_CAST buf, ctxt);
12174 if (obj == NULL || obj->type != XPATH_NODESET ||
12175 obj->nodesetval == NULL || obj->nodesetval->nodeNr == 0) {
12176 vshError(ctl, _("No interface found whose type is %s"), type);
12177 goto cleanup;
12180 if (!mac && obj->nodesetval->nodeNr > 1) {
12181 vshError(ctl, _("Domain has %d interfaces. Please specify which one "
12182 "to detach using --mac"), obj->nodesetval->nodeNr);
12183 goto cleanup;
12186 if (!mac) {
12187 matchNode = obj->nodesetval->nodeTab[0];
12188 goto hit;
12191 /* multiple possibilities, so search for matching mac */
12192 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
12193 cur = obj->nodesetval->nodeTab[i]->children;
12194 while (cur != NULL) {
12195 if (cur->type == XML_ELEMENT_NODE &&
12196 virXMLNodeNameEqual(cur, "mac")) {
12197 char *tmp_mac = virXMLPropString(cur, "address");
12198 diff_mac = virMacAddrCompare(tmp_mac, mac);
12199 VIR_FREE(tmp_mac);
12200 if (!diff_mac) {
12201 if (matchNode) {
12202 /* this is the 2nd match, so it's ambiguous */
12203 vshError(ctl, _("Domain has multiple interfaces matching "
12204 "MAC address %s. You must use detach-device and "
12205 "specify the device pci address to remove it."),
12206 mac);
12207 goto cleanup;
12209 matchNode = obj->nodesetval->nodeTab[i];
12212 cur = cur->next;
12215 if (!matchNode) {
12216 vshError(ctl, _("No interface with MAC address %s was found"), mac);
12217 goto cleanup;
12220 hit:
12221 if (!(detach_xml = virXMLNodeToString(xml, matchNode))) {
12222 vshSaveLibvirtError();
12223 goto cleanup;
12226 if (flags != 0 || current)
12227 ret = virDomainDetachDeviceFlags(dom, detach_xml, flags);
12228 else
12229 ret = virDomainDetachDevice(dom, detach_xml);
12231 cleanup:
12232 VIR_FREE(detach_xml);
12233 xmlFreeDoc(xml);
12234 xmlXPathFreeObject(obj);
12235 xmlXPathFreeContext(ctxt);
12236 return ret == 0;
12240 static bool
12241 cmdDetachInterface(vshControl *ctl, const vshCmd *cmd)
12243 virDomainPtr dom = NULL;
12244 char *doc_live = NULL, *doc_config = NULL;
12245 const char *mac = NULL, *type = NULL;
12246 int flags = 0;
12247 bool ret = false, affect_config, affect_live;
12248 bool current = vshCommandOptBool(cmd, "current");
12249 bool config = vshCommandOptBool(cmd, "config");
12250 bool live = vshCommandOptBool(cmd, "live");
12251 bool persistent = vshCommandOptBool(cmd, "persistent");
12253 VSH_EXCLUSIVE_OPTIONS_VAR(persistent, current);
12255 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
12256 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
12258 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
12259 return false;
12261 if (vshCommandOptStringReq(ctl, cmd, "type", &type) < 0)
12262 goto cleanup;
12264 if (vshCommandOptStringReq(ctl, cmd, "mac", &mac) < 0)
12265 goto cleanup;
12267 affect_config = (config || persistent);
12269 if (affect_config) {
12270 if (!(doc_config = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE)))
12271 goto cleanup;
12272 if (!(ret = virshDomainDetachInterface(doc_config,
12273 flags | VIR_DOMAIN_AFFECT_CONFIG,
12274 dom, ctl, current, type, mac)))
12275 goto cleanup;
12278 affect_live = (live || (persistent && virDomainIsActive(dom) == 1));
12280 if (affect_live || !affect_config) {
12281 flags = 0;
12283 if (affect_live)
12284 flags |= VIR_DOMAIN_AFFECT_LIVE;
12286 if (!(doc_live = virDomainGetXMLDesc(dom, 0)))
12287 goto cleanup;
12289 ret = virshDomainDetachInterface(doc_live, flags,
12290 dom, ctl, current, type, mac);
12293 cleanup:
12294 if (!ret) {
12295 vshError(ctl, "%s", _("Failed to detach interface"));
12296 } else {
12297 vshPrintExtra(ctl, "%s", _("Interface detached successfully\n"));
12299 VIR_FREE(doc_live);
12300 VIR_FREE(doc_config);
12301 virshDomainFree(dom);
12302 return ret;
12306 static void
12307 virshDiskDropBackingStore(xmlNodePtr disk_node)
12309 xmlNodePtr tmp;
12311 for (tmp = disk_node->children; tmp; tmp = tmp->next) {
12312 if (tmp->type != XML_ELEMENT_NODE)
12313 continue;
12315 if (virXMLNodeNameEqual(tmp, "backingStore")) {
12316 xmlUnlinkNode(tmp);
12317 xmlFreeNode(tmp);
12319 return;
12325 typedef enum {
12326 VIRSH_FIND_DISK_NORMAL,
12327 VIRSH_FIND_DISK_CHANGEABLE,
12328 } virshFindDiskType;
12330 /* Helper function to find disk device in XML doc. Returns the disk
12331 * node on success, or NULL on failure. Caller must free the result
12332 * @path: Fully-qualified path or target of disk device.
12333 * @type: Either VIRSH_FIND_DISK_NORMAL or VIRSH_FIND_DISK_CHANGEABLE.
12335 static xmlNodePtr
12336 virshFindDisk(const char *doc,
12337 const char *path,
12338 int type)
12340 xmlDocPtr xml = NULL;
12341 xmlXPathObjectPtr obj = NULL;
12342 xmlXPathContextPtr ctxt = NULL;
12343 xmlNodePtr cur = NULL;
12344 xmlNodePtr ret = NULL;
12345 size_t i;
12347 xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
12348 if (!xml) {
12349 vshError(NULL, "%s", _("Failed to get disk information"));
12350 goto cleanup;
12353 obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt);
12354 if (obj == NULL ||
12355 obj->type != XPATH_NODESET ||
12356 obj->nodesetval == NULL ||
12357 obj->nodesetval->nodeNr == 0) {
12358 vshError(NULL, "%s", _("Failed to get disk information"));
12359 goto cleanup;
12362 /* search disk using @path */
12363 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
12364 bool is_supported = true;
12366 if (type == VIRSH_FIND_DISK_CHANGEABLE) {
12367 xmlNodePtr n = obj->nodesetval->nodeTab[i];
12368 is_supported = false;
12370 /* Check if the disk is CDROM or floppy disk */
12371 if (virXMLNodeNameEqual(n, "disk")) {
12372 char *device_value = virXMLPropString(n, "device");
12374 if (STREQ(device_value, "cdrom") ||
12375 STREQ(device_value, "floppy"))
12376 is_supported = true;
12378 VIR_FREE(device_value);
12381 if (!is_supported)
12382 continue;
12385 cur = obj->nodesetval->nodeTab[i]->children;
12386 while (cur != NULL) {
12387 if (cur->type == XML_ELEMENT_NODE) {
12388 char *tmp = NULL;
12390 if (virXMLNodeNameEqual(cur, "source")) {
12391 if ((tmp = virXMLPropString(cur, "file")) ||
12392 (tmp = virXMLPropString(cur, "dev")) ||
12393 (tmp = virXMLPropString(cur, "dir")) ||
12394 (tmp = virXMLPropString(cur, "name"))) {
12396 } else if (virXMLNodeNameEqual(cur, "target")) {
12397 tmp = virXMLPropString(cur, "dev");
12400 if (STREQ_NULLABLE(tmp, path)) {
12401 ret = xmlCopyNode(obj->nodesetval->nodeTab[i], 1);
12402 /* drop backing store since they are not needed here */
12403 virshDiskDropBackingStore(ret);
12404 VIR_FREE(tmp);
12405 goto cleanup;
12407 VIR_FREE(tmp);
12409 cur = cur->next;
12413 vshError(NULL, _("No disk found whose source path or target is %s"), path);
12415 cleanup:
12416 xmlXPathFreeObject(obj);
12417 xmlXPathFreeContext(ctxt);
12418 xmlFreeDoc(xml);
12419 return ret;
12422 typedef enum {
12423 VIRSH_UPDATE_DISK_XML_EJECT,
12424 VIRSH_UPDATE_DISK_XML_INSERT,
12425 VIRSH_UPDATE_DISK_XML_UPDATE,
12426 } virshUpdateDiskXMLType;
12428 /* Helper function to prepare disk XML. Could be used for disk
12429 * detaching, media changing(ejecting, inserting, updating)
12430 * for changeable disk. Returns the processed XML as string on
12431 * success, or NULL on failure. Caller must free the result.
12433 static char *
12434 virshUpdateDiskXML(xmlNodePtr disk_node,
12435 const char *new_source,
12436 bool source_block,
12437 const char *target,
12438 virshUpdateDiskXMLType type)
12440 xmlNodePtr tmp = NULL;
12441 xmlNodePtr source = NULL;
12442 xmlNodePtr target_node = NULL;
12443 xmlNodePtr text_node = NULL;
12444 char *device_type = NULL;
12445 char *ret = NULL;
12446 char *startupPolicy = NULL;
12447 char *source_path = NULL;
12449 if (!disk_node)
12450 return NULL;
12452 device_type = virXMLPropString(disk_node, "device");
12454 if (!(STREQ_NULLABLE(device_type, "cdrom") ||
12455 STREQ_NULLABLE(device_type, "floppy"))) {
12456 vshError(NULL, _("The disk device '%s' is not removable"), target);
12457 goto cleanup;
12460 /* find the current source subelement */
12461 for (tmp = disk_node->children; tmp; tmp = tmp->next) {
12463 * Save the last text node before the <target/>. The
12464 * reasoning behind this is that the target node will be
12465 * present in this case and also has a proper indentation.
12467 if (!target_node && tmp->type == XML_TEXT_NODE)
12468 text_node = tmp;
12471 * We need only element nodes from now on.
12473 if (tmp->type != XML_ELEMENT_NODE)
12474 continue;
12476 if (virXMLNodeNameEqual(tmp, "source"))
12477 source = tmp;
12479 if (virXMLNodeNameEqual(tmp, "target"))
12480 target_node = tmp;
12483 * We've found all we needed.
12485 if (source && target_node)
12486 break;
12489 if (type == VIRSH_UPDATE_DISK_XML_EJECT) {
12490 if (!source) {
12491 vshError(NULL, _("The disk device '%s' doesn't have media"), target);
12492 goto cleanup;
12495 /* forcibly switch to empty file cdrom */
12496 source_block = false;
12497 new_source = NULL;
12498 } else if (!new_source) {
12499 vshError(NULL, _("New disk media source was not specified"));
12500 goto cleanup;
12503 if (source) {
12504 if (!(source_path = virXMLPropString(source, "file")) &&
12505 !(source_path = virXMLPropString(source, "dev")) &&
12506 !(source_path = virXMLPropString(source, "dir")) &&
12507 !(source_path = virXMLPropString(source, "pool")))
12508 source_path = virXMLPropString(source, "name");
12510 if (source_path && type == VIRSH_UPDATE_DISK_XML_INSERT) {
12511 vshError(NULL, _("The disk device '%s' already has media"), target);
12512 goto cleanup;
12515 startupPolicy = virXMLPropString(source, "startupPolicy");
12517 /* remove current source */
12518 xmlUnlinkNode(source);
12519 xmlFreeNode(source);
12520 source = NULL;
12523 /* set the correct disk type */
12524 if (source_block)
12525 xmlSetProp(disk_node, BAD_CAST "type", BAD_CAST "block");
12526 else
12527 xmlSetProp(disk_node, BAD_CAST "type", BAD_CAST "file");
12529 if (new_source) {
12530 /* create new source subelement */
12531 if (!(source = xmlNewNode(NULL, BAD_CAST "source"))) {
12532 vshError(NULL, _("Failed to allocate new source node"));
12533 goto cleanup;
12536 if (source_block)
12537 xmlNewProp(source, BAD_CAST "dev", BAD_CAST new_source);
12538 else
12539 xmlNewProp(source, BAD_CAST "file", BAD_CAST new_source);
12541 if (startupPolicy)
12542 xmlNewProp(source, BAD_CAST "startupPolicy", BAD_CAST startupPolicy);
12545 * So that the output XML looks nice in case anyone calls
12546 * 'change-media' with '--print-xml', let's attach the source
12547 * before target...
12549 xmlAddPrevSibling(target_node, source);
12552 * ... and duplicate the text node doing the indentation just
12553 * so it's more easily readable. And don't make it fatal.
12555 if ((tmp = xmlCopyNode(text_node, 0))) {
12556 if (!xmlAddPrevSibling(target_node, tmp))
12557 xmlFreeNode(tmp);
12561 if (!(ret = virXMLNodeToString(NULL, disk_node))) {
12562 vshSaveLibvirtError();
12563 goto cleanup;
12566 cleanup:
12567 VIR_FREE(device_type);
12568 VIR_FREE(startupPolicy);
12569 VIR_FREE(source_path);
12570 return ret;
12575 * "detach-disk" command
12577 static const vshCmdInfo info_detach_disk[] = {
12578 {.name = "help",
12579 .data = N_("detach disk device")
12581 {.name = "desc",
12582 .data = N_("Detach disk device.")
12584 {.name = NULL}
12587 static const vshCmdOptDef opts_detach_disk[] = {
12588 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
12589 {.name = "target",
12590 .type = VSH_OT_DATA,
12591 .flags = VSH_OFLAG_REQ,
12592 .completer = virshDomainDiskTargetCompleter,
12593 .help = N_("target of disk device")
12595 VIRSH_COMMON_OPT_DOMAIN_PERSISTENT,
12596 VIRSH_COMMON_OPT_DOMAIN_CONFIG,
12597 VIRSH_COMMON_OPT_DOMAIN_LIVE,
12598 VIRSH_COMMON_OPT_DOMAIN_CURRENT,
12599 {.name = "print-xml",
12600 .type = VSH_OT_BOOL,
12601 .help = N_("print XML document rather than detach the disk")
12603 {.name = NULL}
12606 static bool
12607 cmdDetachDisk(vshControl *ctl, const vshCmd *cmd)
12609 char *disk_xml = NULL;
12610 virDomainPtr dom = NULL;
12611 const char *target = NULL;
12612 char *doc = NULL;
12613 int ret;
12614 bool functionReturn = false;
12615 xmlNodePtr disk_node = NULL;
12616 bool current = vshCommandOptBool(cmd, "current");
12617 bool config = vshCommandOptBool(cmd, "config");
12618 bool live = vshCommandOptBool(cmd, "live");
12619 bool persistent = vshCommandOptBool(cmd, "persistent");
12620 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
12622 VSH_EXCLUSIVE_OPTIONS_VAR(persistent, current);
12624 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
12625 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
12627 if (config || persistent)
12628 flags |= VIR_DOMAIN_AFFECT_CONFIG;
12629 if (live)
12630 flags |= VIR_DOMAIN_AFFECT_LIVE;
12632 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
12633 return false;
12635 if (vshCommandOptStringReq(ctl, cmd, "target", &target) < 0)
12636 goto cleanup;
12638 if (flags == VIR_DOMAIN_AFFECT_CONFIG)
12639 doc = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
12640 else
12641 doc = virDomainGetXMLDesc(dom, 0);
12643 if (!doc)
12644 goto cleanup;
12646 if (persistent &&
12647 virDomainIsActive(dom) == 1)
12648 flags |= VIR_DOMAIN_AFFECT_LIVE;
12650 if (!(disk_node = virshFindDisk(doc, target, VIRSH_FIND_DISK_NORMAL)))
12651 goto cleanup;
12653 if (!(disk_xml = virXMLNodeToString(NULL, disk_node))) {
12654 vshSaveLibvirtError();
12655 goto cleanup;
12658 if (vshCommandOptBool(cmd, "print-xml")) {
12659 vshPrint(ctl, "%s", disk_xml);
12660 functionReturn = true;
12661 goto cleanup;
12664 if (flags != 0 || current)
12665 ret = virDomainDetachDeviceFlags(dom, disk_xml, flags);
12666 else
12667 ret = virDomainDetachDevice(dom, disk_xml);
12669 if (ret != 0) {
12670 vshError(ctl, "%s", _("Failed to detach disk"));
12671 goto cleanup;
12674 vshPrintExtra(ctl, "%s", _("Disk detached successfully\n"));
12675 functionReturn = true;
12677 cleanup:
12678 xmlFreeNode(disk_node);
12679 VIR_FREE(disk_xml);
12680 VIR_FREE(doc);
12681 virshDomainFree(dom);
12682 return functionReturn;
12686 * "edit" command
12688 static const vshCmdInfo info_edit[] = {
12689 {.name = "help",
12690 .data = N_("edit XML configuration for a domain")
12692 {.name = "desc",
12693 .data = N_("Edit the XML configuration for a domain.")
12695 {.name = NULL}
12698 static const vshCmdOptDef opts_edit[] = {
12699 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
12700 {.name = "skip-validate",
12701 .type = VSH_OT_BOOL,
12702 .help = N_("skip validation of the XML against the schema")
12704 {.name = NULL}
12707 static bool
12708 cmdEdit(vshControl *ctl, const vshCmd *cmd)
12710 bool ret = false;
12711 virDomainPtr dom = NULL;
12712 virDomainPtr dom_edited = NULL;
12713 unsigned int query_flags = VIR_DOMAIN_XML_SECURE | VIR_DOMAIN_XML_INACTIVE;
12714 unsigned int define_flags = VIR_DOMAIN_DEFINE_VALIDATE;
12715 virshControlPtr priv = ctl->privData;
12717 dom = virshCommandOptDomain(ctl, cmd, NULL);
12718 if (dom == NULL)
12719 goto cleanup;
12721 if (vshCommandOptBool(cmd, "skip-validate"))
12722 define_flags &= ~VIR_DOMAIN_DEFINE_VALIDATE;
12724 #define EDIT_GET_XML virDomainGetXMLDesc(dom, query_flags)
12725 #define EDIT_NOT_CHANGED \
12726 do { \
12727 vshPrintExtra(ctl, _("Domain %s XML configuration not changed.\n"), \
12728 virDomainGetName(dom)); \
12729 ret = true; \
12730 goto edit_cleanup; \
12731 } while (0)
12732 #define EDIT_DEFINE \
12733 (dom_edited = virshDomainDefine(priv->conn, doc_edited, define_flags))
12734 #define EDIT_RELAX \
12735 do { \
12736 define_flags &= ~VIR_DOMAIN_DEFINE_VALIDATE; \
12737 } while (0);
12739 #include "virsh-edit.c"
12740 #undef EDIT_RELAX
12742 vshPrintExtra(ctl, _("Domain %s XML configuration edited.\n"),
12743 virDomainGetName(dom_edited));
12745 ret = true;
12747 cleanup:
12748 virshDomainFree(dom);
12749 virshDomainFree(dom_edited);
12751 return ret;
12756 * "event" command
12758 VIR_ENUM_DECL(virshDomainEvent);
12759 VIR_ENUM_IMPL(virshDomainEvent,
12760 VIR_DOMAIN_EVENT_LAST,
12761 N_("Defined"),
12762 N_("Undefined"),
12763 N_("Started"),
12764 N_("Suspended"),
12765 N_("Resumed"),
12766 N_("Stopped"),
12767 N_("Shutdown"),
12768 N_("PMSuspended"),
12769 N_("Crashed"));
12771 static const char *
12772 virshDomainEventToString(int event)
12774 const char *str = virshDomainEventTypeToString(event);
12775 return str ? _(str) : _("unknown");
12778 VIR_ENUM_DECL(virshDomainEventDefined);
12779 VIR_ENUM_IMPL(virshDomainEventDefined,
12780 VIR_DOMAIN_EVENT_DEFINED_LAST,
12781 N_("Added"),
12782 N_("Updated"),
12783 N_("Renamed"),
12784 N_("Snapshot"));
12786 VIR_ENUM_DECL(virshDomainEventUndefined);
12787 VIR_ENUM_IMPL(virshDomainEventUndefined,
12788 VIR_DOMAIN_EVENT_UNDEFINED_LAST,
12789 N_("Removed"),
12790 N_("Renamed"));
12792 VIR_ENUM_DECL(virshDomainEventStarted);
12793 VIR_ENUM_IMPL(virshDomainEventStarted,
12794 VIR_DOMAIN_EVENT_STARTED_LAST,
12795 N_("Booted"),
12796 N_("Migrated"),
12797 N_("Restored"),
12798 N_("Snapshot"),
12799 N_("Event wakeup"));
12801 VIR_ENUM_DECL(virshDomainEventSuspended);
12802 VIR_ENUM_IMPL(virshDomainEventSuspended,
12803 VIR_DOMAIN_EVENT_SUSPENDED_LAST,
12804 N_("Paused"),
12805 N_("Migrated"),
12806 N_("I/O Error"),
12807 N_("Watchdog"),
12808 N_("Restored"),
12809 N_("Snapshot"),
12810 N_("API error"),
12811 N_("Post-copy"),
12812 N_("Post-copy Error"));
12814 VIR_ENUM_DECL(virshDomainEventResumed);
12815 VIR_ENUM_IMPL(virshDomainEventResumed,
12816 VIR_DOMAIN_EVENT_RESUMED_LAST,
12817 N_("Unpaused"),
12818 N_("Migrated"),
12819 N_("Snapshot"),
12820 N_("Post-copy"));
12822 VIR_ENUM_DECL(virshDomainEventStopped);
12823 VIR_ENUM_IMPL(virshDomainEventStopped,
12824 VIR_DOMAIN_EVENT_STOPPED_LAST,
12825 N_("Shutdown"),
12826 N_("Destroyed"),
12827 N_("Crashed"),
12828 N_("Migrated"),
12829 N_("Saved"),
12830 N_("Failed"),
12831 N_("Snapshot"));
12833 VIR_ENUM_DECL(virshDomainEventShutdown);
12834 VIR_ENUM_IMPL(virshDomainEventShutdown,
12835 VIR_DOMAIN_EVENT_SHUTDOWN_LAST,
12836 N_("Finished"),
12837 N_("Finished after guest request"),
12838 N_("Finished after host request"));
12840 VIR_ENUM_DECL(virshDomainEventPMSuspended);
12841 VIR_ENUM_IMPL(virshDomainEventPMSuspended,
12842 VIR_DOMAIN_EVENT_PMSUSPENDED_LAST,
12843 N_("Memory"),
12844 N_("Disk"));
12846 VIR_ENUM_DECL(virshDomainEventCrashed);
12847 VIR_ENUM_IMPL(virshDomainEventCrashed,
12848 VIR_DOMAIN_EVENT_CRASHED_LAST,
12849 N_("Panicked"));
12851 static const char *
12852 virshDomainEventDetailToString(int event, int detail)
12854 const char *str = NULL;
12855 switch ((virDomainEventType) event) {
12856 case VIR_DOMAIN_EVENT_DEFINED:
12857 str = virshDomainEventDefinedTypeToString(detail);
12858 break;
12859 case VIR_DOMAIN_EVENT_UNDEFINED:
12860 str = virshDomainEventUndefinedTypeToString(detail);
12861 break;
12862 case VIR_DOMAIN_EVENT_STARTED:
12863 str = virshDomainEventStartedTypeToString(detail);
12864 break;
12865 case VIR_DOMAIN_EVENT_SUSPENDED:
12866 str = virshDomainEventSuspendedTypeToString(detail);
12867 break;
12868 case VIR_DOMAIN_EVENT_RESUMED:
12869 str = virshDomainEventResumedTypeToString(detail);
12870 break;
12871 case VIR_DOMAIN_EVENT_STOPPED:
12872 str = virshDomainEventStoppedTypeToString(detail);
12873 break;
12874 case VIR_DOMAIN_EVENT_SHUTDOWN:
12875 str = virshDomainEventShutdownTypeToString(detail);
12876 break;
12877 case VIR_DOMAIN_EVENT_PMSUSPENDED:
12878 str = virshDomainEventPMSuspendedTypeToString(detail);
12879 break;
12880 case VIR_DOMAIN_EVENT_CRASHED:
12881 str = virshDomainEventCrashedTypeToString(detail);
12882 break;
12883 case VIR_DOMAIN_EVENT_LAST:
12884 break;
12886 return str ? _(str) : _("unknown");
12889 VIR_ENUM_DECL(virshDomainEventWatchdog);
12890 VIR_ENUM_IMPL(virshDomainEventWatchdog,
12891 VIR_DOMAIN_EVENT_WATCHDOG_LAST,
12892 N_("none"),
12893 N_("pause"),
12894 N_("reset"),
12895 N_("poweroff"),
12896 N_("shutdown"),
12897 N_("debug"),
12898 N_("inject-nmi"));
12900 static const char *
12901 virshDomainEventWatchdogToString(int action)
12903 const char *str = virshDomainEventWatchdogTypeToString(action);
12904 return str ? _(str) : _("unknown");
12907 VIR_ENUM_DECL(virshDomainEventIOError);
12908 VIR_ENUM_IMPL(virshDomainEventIOError,
12909 VIR_DOMAIN_EVENT_IO_ERROR_LAST,
12910 N_("none"),
12911 N_("pause"),
12912 N_("report"));
12914 static const char *
12915 virshDomainEventIOErrorToString(int action)
12917 const char *str = virshDomainEventIOErrorTypeToString(action);
12918 return str ? _(str) : _("unknown");
12921 VIR_ENUM_DECL(virshGraphicsPhase);
12922 VIR_ENUM_IMPL(virshGraphicsPhase,
12923 VIR_DOMAIN_EVENT_GRAPHICS_LAST,
12924 N_("connect"),
12925 N_("initialize"),
12926 N_("disconnect"));
12928 static const char *
12929 virshGraphicsPhaseToString(int phase)
12931 const char *str = virshGraphicsPhaseTypeToString(phase);
12932 return str ? _(str) : _("unknown");
12935 VIR_ENUM_DECL(virshGraphicsAddress);
12936 VIR_ENUM_IMPL(virshGraphicsAddress,
12937 VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_LAST,
12938 N_("IPv4"),
12939 N_("IPv6"),
12940 N_("unix"));
12942 static const char *
12943 virshGraphicsAddressToString(int family)
12945 const char *str = virshGraphicsAddressTypeToString(family);
12946 return str ? _(str) : _("unknown");
12949 VIR_ENUM_DECL(virshDomainBlockJobStatus);
12950 VIR_ENUM_IMPL(virshDomainBlockJobStatus,
12951 VIR_DOMAIN_BLOCK_JOB_LAST,
12952 N_("completed"),
12953 N_("failed"),
12954 N_("canceled"),
12955 N_("ready"));
12957 static const char *
12958 virshDomainBlockJobStatusToString(int status)
12960 const char *str = virshDomainBlockJobStatusTypeToString(status);
12961 return str ? _(str) : _("unknown");
12964 VIR_ENUM_DECL(virshDomainEventDiskChange);
12965 VIR_ENUM_IMPL(virshDomainEventDiskChange,
12966 VIR_DOMAIN_EVENT_DISK_CHANGE_LAST,
12967 N_("changed"),
12968 N_("dropped"));
12970 static const char *
12971 virshDomainEventDiskChangeToString(int reason)
12973 const char *str = virshDomainEventDiskChangeTypeToString(reason);
12974 return str ? _(str) : _("unknown");
12977 VIR_ENUM_DECL(virshDomainEventTrayChange);
12978 VIR_ENUM_IMPL(virshDomainEventTrayChange,
12979 VIR_DOMAIN_EVENT_TRAY_CHANGE_LAST,
12980 N_("opened"),
12981 N_("closed"));
12983 static const char *
12984 virshDomainEventTrayChangeToString(int reason)
12986 const char *str = virshDomainEventTrayChangeTypeToString(reason);
12987 return str ? _(str) : _("unknown");
12990 struct virshDomEventData {
12991 vshControl *ctl;
12992 bool loop;
12993 int *count;
12994 bool timestamp;
12995 virshDomainEventCallback *cb;
12996 int id;
12998 typedef struct virshDomEventData virshDomEventData;
13001 * virshEventPrint:
13003 * @data: opaque data passed to all event callbacks
13004 * @buf: string buffer describing the event
13006 * Print the event description found in @buf and update virshDomEventData.
13008 * This function resets @buf and frees all memory consumed by its content.
13010 static void
13011 virshEventPrint(virshDomEventData *data,
13012 virBufferPtr buf)
13014 char *msg;
13016 if (!(msg = virBufferContentAndReset(buf)))
13017 return;
13019 if (!data->loop && *data->count)
13020 goto cleanup;
13022 if (data->timestamp) {
13023 char timestamp[VIR_TIME_STRING_BUFLEN];
13025 if (virTimeStringNowRaw(timestamp) < 0)
13026 timestamp[0] = '\0';
13028 vshPrint(data->ctl, "%s: %s", timestamp, msg);
13029 } else {
13030 vshPrint(data->ctl, "%s", msg);
13033 (*data->count)++;
13034 if (!data->loop)
13035 vshEventDone(data->ctl);
13037 cleanup:
13038 VIR_FREE(msg);
13041 static void
13042 virshEventGenericPrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13043 virDomainPtr dom,
13044 void *opaque)
13046 virBuffer buf = VIR_BUFFER_INITIALIZER;
13048 virBufferAsprintf(&buf, _("event '%s' for domain %s\n"),
13049 ((virshDomEventData *) opaque)->cb->name,
13050 virDomainGetName(dom));
13051 virshEventPrint(opaque, &buf);
13054 static void
13055 virshEventLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13056 virDomainPtr dom,
13057 int event,
13058 int detail,
13059 void *opaque)
13061 virBuffer buf = VIR_BUFFER_INITIALIZER;
13063 virBufferAsprintf(&buf, _("event 'lifecycle' for domain %s: %s %s\n"),
13064 virDomainGetName(dom),
13065 virshDomainEventToString(event),
13066 virshDomainEventDetailToString(event, detail));
13067 virshEventPrint(opaque, &buf);
13070 static void
13071 virshEventRTCChangePrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13072 virDomainPtr dom,
13073 long long utcoffset,
13074 void *opaque)
13076 virBuffer buf = VIR_BUFFER_INITIALIZER;
13078 virBufferAsprintf(&buf, _("event 'rtc-change' for domain %s: %lld\n"),
13079 virDomainGetName(dom),
13080 utcoffset);
13081 virshEventPrint(opaque, &buf);
13084 static void
13085 virshEventWatchdogPrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13086 virDomainPtr dom,
13087 int action,
13088 void *opaque)
13090 virBuffer buf = VIR_BUFFER_INITIALIZER;
13092 virBufferAsprintf(&buf, _("event 'watchdog' for domain %s: %s\n"),
13093 virDomainGetName(dom),
13094 virshDomainEventWatchdogToString(action));
13095 virshEventPrint(opaque, &buf);
13098 static void
13099 virshEventIOErrorPrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13100 virDomainPtr dom,
13101 const char *srcPath,
13102 const char *devAlias,
13103 int action,
13104 void *opaque)
13106 virBuffer buf = VIR_BUFFER_INITIALIZER;
13108 virBufferAsprintf(&buf, _("event 'io-error' for domain %s: %s (%s) %s\n"),
13109 virDomainGetName(dom),
13110 srcPath,
13111 devAlias,
13112 virshDomainEventIOErrorToString(action));
13113 virshEventPrint(opaque, &buf);
13116 static void
13117 virshEventGraphicsPrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13118 virDomainPtr dom,
13119 int phase,
13120 const virDomainEventGraphicsAddress *local,
13121 const virDomainEventGraphicsAddress *remote,
13122 const char *authScheme,
13123 const virDomainEventGraphicsSubject *subject,
13124 void *opaque)
13126 virBuffer buf = VIR_BUFFER_INITIALIZER;
13127 size_t i;
13129 virBufferAsprintf(&buf, _("event 'graphics' for domain %s: "
13130 "%s local[%s %s %s] remote[%s %s %s] %s\n"),
13131 virDomainGetName(dom),
13132 virshGraphicsPhaseToString(phase),
13133 virshGraphicsAddressToString(local->family),
13134 local->node,
13135 local->service,
13136 virshGraphicsAddressToString(remote->family),
13137 remote->node,
13138 remote->service,
13139 authScheme);
13140 for (i = 0; i < subject->nidentity; i++) {
13141 virBufferAsprintf(&buf, "\t%s=%s\n",
13142 subject->identities[i].type,
13143 subject->identities[i].name);
13145 virshEventPrint(opaque, &buf);
13148 static void
13149 virshEventIOErrorReasonPrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13150 virDomainPtr dom,
13151 const char *srcPath,
13152 const char *devAlias,
13153 int action,
13154 const char *reason,
13155 void *opaque)
13157 virBuffer buf = VIR_BUFFER_INITIALIZER;
13159 virBufferAsprintf(&buf, _("event 'io-error-reason' for domain %s: "
13160 "%s (%s) %s due to %s\n"),
13161 virDomainGetName(dom),
13162 srcPath,
13163 devAlias,
13164 virshDomainEventIOErrorToString(action),
13165 reason);
13166 virshEventPrint(opaque, &buf);
13169 static void
13170 virshEventBlockJobPrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13171 virDomainPtr dom,
13172 const char *disk,
13173 int type,
13174 int status,
13175 void *opaque)
13177 virBuffer buf = VIR_BUFFER_INITIALIZER;
13179 virBufferAsprintf(&buf, _("event '%s' for domain %s: %s for %s %s\n"),
13180 ((virshDomEventData *) opaque)->cb->name,
13181 virDomainGetName(dom),
13182 virshDomainBlockJobToString(type),
13183 disk,
13184 virshDomainBlockJobStatusToString(status));
13185 virshEventPrint(opaque, &buf);
13188 static void
13189 virshEventDiskChangePrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13190 virDomainPtr dom,
13191 const char *oldSrc,
13192 const char *newSrc,
13193 const char *alias,
13194 int reason,
13195 void *opaque)
13197 virBuffer buf = VIR_BUFFER_INITIALIZER;
13199 virBufferAsprintf(&buf, _("event 'disk-change' for domain %s disk %s: "
13200 "%s -> %s: %s\n"),
13201 virDomainGetName(dom),
13202 alias,
13203 NULLSTR(oldSrc),
13204 NULLSTR(newSrc),
13205 virshDomainEventDiskChangeToString(reason));
13206 virshEventPrint(opaque, &buf);
13209 static void
13210 virshEventTrayChangePrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13211 virDomainPtr dom,
13212 const char *alias,
13213 int reason,
13214 void *opaque)
13216 virBuffer buf = VIR_BUFFER_INITIALIZER;
13218 virBufferAsprintf(&buf, _("event 'tray-change' for domain %s disk %s: %s\n"),
13219 virDomainGetName(dom),
13220 alias,
13221 virshDomainEventTrayChangeToString(reason));
13222 virshEventPrint(opaque, &buf);
13225 static void
13226 virshEventPMChangePrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13227 virDomainPtr dom,
13228 int reason ATTRIBUTE_UNUSED,
13229 void *opaque)
13231 /* As long as libvirt.h doesn't define any reasons, we might as
13232 * well treat all PM state changes as generic events. */
13233 virshEventGenericPrint(conn, dom, opaque);
13236 static void
13237 virshEventBalloonChangePrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13238 virDomainPtr dom,
13239 unsigned long long actual,
13240 void *opaque)
13242 virBuffer buf = VIR_BUFFER_INITIALIZER;
13244 virBufferAsprintf(&buf, _("event 'balloon-change' for domain %s: %lluKiB\n"),
13245 virDomainGetName(dom),
13246 actual);
13247 virshEventPrint(opaque, &buf);
13250 static void
13251 virshEventDeviceRemovedPrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13252 virDomainPtr dom,
13253 const char *alias,
13254 void *opaque)
13256 virBuffer buf = VIR_BUFFER_INITIALIZER;
13258 virBufferAsprintf(&buf, _("event 'device-removed' for domain %s: %s\n"),
13259 virDomainGetName(dom),
13260 alias);
13261 virshEventPrint(opaque, &buf);
13264 static void
13265 virshEventDeviceAddedPrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13266 virDomainPtr dom,
13267 const char *alias,
13268 void *opaque)
13270 virBuffer buf = VIR_BUFFER_INITIALIZER;
13272 virBufferAsprintf(&buf, _("event 'device-added' for domain %s: %s\n"),
13273 virDomainGetName(dom),
13274 alias);
13275 virshEventPrint(opaque, &buf);
13278 static void
13279 virshEventTunablePrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13280 virDomainPtr dom,
13281 virTypedParameterPtr params,
13282 int nparams,
13283 void *opaque)
13285 virBuffer buf = VIR_BUFFER_INITIALIZER;
13286 size_t i;
13287 char *value;
13289 virBufferAsprintf(&buf, _("event 'tunable' for domain %s:\n"),
13290 virDomainGetName(dom));
13291 for (i = 0; i < nparams; i++) {
13292 value = virTypedParameterToString(&params[i]);
13293 if (value) {
13294 virBufferAsprintf(&buf, "\t%s: %s\n", params[i].field, value);
13295 VIR_FREE(value);
13298 virshEventPrint(opaque, &buf);
13301 VIR_ENUM_DECL(virshEventAgentLifecycleState);
13302 VIR_ENUM_IMPL(virshEventAgentLifecycleState,
13303 VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_LAST,
13304 N_("unknown"),
13305 N_("connected"),
13306 N_("disconnected"));
13308 VIR_ENUM_DECL(virshEventAgentLifecycleReason);
13309 VIR_ENUM_IMPL(virshEventAgentLifecycleReason,
13310 VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_LAST,
13311 N_("unknown"),
13312 N_("domain started"),
13313 N_("channel event"));
13315 #define UNKNOWNSTR(str) (str ? str : N_("unsupported value"))
13316 static void
13317 virshEventAgentLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13318 virDomainPtr dom,
13319 int state,
13320 int reason,
13321 void *opaque)
13323 virBuffer buf = VIR_BUFFER_INITIALIZER;
13325 virBufferAsprintf(&buf, _("event 'agent-lifecycle' for domain %s: state: "
13326 "'%s' reason: '%s'\n"),
13327 virDomainGetName(dom),
13328 UNKNOWNSTR(virshEventAgentLifecycleStateTypeToString(state)),
13329 UNKNOWNSTR(virshEventAgentLifecycleReasonTypeToString(reason)));
13330 virshEventPrint(opaque, &buf);
13333 static void
13334 virshEventMigrationIterationPrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13335 virDomainPtr dom,
13336 int iteration,
13337 void *opaque)
13339 virBuffer buf = VIR_BUFFER_INITIALIZER;
13341 virBufferAsprintf(&buf, _("event 'migration-iteration' for domain %s: "
13342 "iteration: '%d'\n"),
13343 virDomainGetName(dom),
13344 iteration);
13346 virshEventPrint(opaque, &buf);
13349 static void
13350 virshEventJobCompletedPrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13351 virDomainPtr dom,
13352 virTypedParameterPtr params,
13353 int nparams,
13354 void *opaque)
13356 virBuffer buf = VIR_BUFFER_INITIALIZER;
13357 size_t i;
13358 char *value;
13360 virBufferAsprintf(&buf, _("event 'job-completed' for domain %s:\n"),
13361 virDomainGetName(dom));
13362 for (i = 0; i < nparams; i++) {
13363 value = virTypedParameterToString(&params[i]);
13364 if (value) {
13365 virBufferAsprintf(&buf, "\t%s: %s\n", params[i].field, value);
13366 VIR_FREE(value);
13369 virshEventPrint(opaque, &buf);
13373 static void
13374 virshEventDeviceRemovalFailedPrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13375 virDomainPtr dom,
13376 const char *alias,
13377 void *opaque)
13379 virBuffer buf = VIR_BUFFER_INITIALIZER;
13381 virBufferAsprintf(&buf, _("event 'device-removal-failed' for domain %s: %s\n"),
13382 virDomainGetName(dom),
13383 alias);
13384 virshEventPrint(opaque, &buf);
13387 VIR_ENUM_DECL(virshEventMetadataChangeType);
13388 VIR_ENUM_IMPL(virshEventMetadataChangeType,
13389 VIR_DOMAIN_METADATA_LAST,
13390 N_("description"),
13391 N_("title"),
13392 N_("element"));
13394 static void
13395 virshEventMetadataChangePrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13396 virDomainPtr dom,
13397 int type,
13398 const char *nsuri,
13399 void *opaque)
13401 virBuffer buf = VIR_BUFFER_INITIALIZER;
13403 virBufferAsprintf(&buf, _("event 'metdata-change' for domain %s: %s %s\n"),
13404 virDomainGetName(dom),
13405 UNKNOWNSTR(virshEventMetadataChangeTypeTypeToString(type)),
13406 NULLSTR(nsuri));
13407 virshEventPrint(opaque, &buf);
13411 static void
13412 virshEventBlockThresholdPrint(virConnectPtr conn ATTRIBUTE_UNUSED,
13413 virDomainPtr dom,
13414 const char *dev,
13415 const char *path,
13416 unsigned long long threshold,
13417 unsigned long long excess,
13418 void *opaque)
13420 virBuffer buf = VIR_BUFFER_INITIALIZER;
13422 virBufferAsprintf(&buf, _("event 'block-threshold' for domain %s: "
13423 "dev: %s(%s) %llu %llu\n"),
13424 virDomainGetName(dom),
13425 dev, NULLSTR(path), threshold, excess);
13426 virshEventPrint(opaque, &buf);
13430 virshDomainEventCallback virshDomainEventCallbacks[] = {
13431 { "lifecycle",
13432 VIR_DOMAIN_EVENT_CALLBACK(virshEventLifecyclePrint), },
13433 { "reboot", virshEventGenericPrint, },
13434 { "rtc-change",
13435 VIR_DOMAIN_EVENT_CALLBACK(virshEventRTCChangePrint), },
13436 { "watchdog",
13437 VIR_DOMAIN_EVENT_CALLBACK(virshEventWatchdogPrint), },
13438 { "io-error",
13439 VIR_DOMAIN_EVENT_CALLBACK(virshEventIOErrorPrint), },
13440 { "graphics",
13441 VIR_DOMAIN_EVENT_CALLBACK(virshEventGraphicsPrint), },
13442 { "io-error-reason",
13443 VIR_DOMAIN_EVENT_CALLBACK(virshEventIOErrorReasonPrint), },
13444 { "control-error", virshEventGenericPrint, },
13445 { "block-job",
13446 VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockJobPrint), },
13447 { "disk-change",
13448 VIR_DOMAIN_EVENT_CALLBACK(virshEventDiskChangePrint), },
13449 { "tray-change",
13450 VIR_DOMAIN_EVENT_CALLBACK(virshEventTrayChangePrint), },
13451 { "pm-wakeup",
13452 VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), },
13453 { "pm-suspend",
13454 VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), },
13455 { "balloon-change",
13456 VIR_DOMAIN_EVENT_CALLBACK(virshEventBalloonChangePrint), },
13457 { "pm-suspend-disk",
13458 VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), },
13459 { "device-removed",
13460 VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovedPrint), },
13461 { "block-job-2",
13462 VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockJobPrint), },
13463 { "tunable",
13464 VIR_DOMAIN_EVENT_CALLBACK(virshEventTunablePrint), },
13465 { "agent-lifecycle",
13466 VIR_DOMAIN_EVENT_CALLBACK(virshEventAgentLifecyclePrint), },
13467 { "device-added",
13468 VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceAddedPrint), },
13469 { "migration-iteration",
13470 VIR_DOMAIN_EVENT_CALLBACK(virshEventMigrationIterationPrint), },
13471 { "job-completed",
13472 VIR_DOMAIN_EVENT_CALLBACK(virshEventJobCompletedPrint), },
13473 { "device-removal-failed",
13474 VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovalFailedPrint), },
13475 { "metadata-change",
13476 VIR_DOMAIN_EVENT_CALLBACK(virshEventMetadataChangePrint), },
13477 { "block-threshold",
13478 VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockThresholdPrint), },
13480 verify(VIR_DOMAIN_EVENT_ID_LAST == ARRAY_CARDINALITY(virshDomainEventCallbacks));
13482 static const vshCmdInfo info_event[] = {
13483 {.name = "help",
13484 .data = N_("Domain Events")
13486 {.name = "desc",
13487 .data = N_("List event types, or wait for domain events to occur")
13489 {.name = NULL}
13492 static const vshCmdOptDef opts_event[] = {
13493 VIRSH_COMMON_OPT_DOMAIN_OT_STRING(N_("filter by domain name, id or uuid"),
13494 0, 0),
13495 {.name = "event",
13496 .type = VSH_OT_STRING,
13497 .completer = virshDomainEventNameCompleter,
13498 .help = N_("which event type to wait for")
13500 {.name = "all",
13501 .type = VSH_OT_BOOL,
13502 .help = N_("wait for all events instead of just one type")
13504 {.name = "loop",
13505 .type = VSH_OT_BOOL,
13506 .help = N_("loop until timeout or interrupt, rather than one-shot")
13508 {.name = "timeout",
13509 .type = VSH_OT_INT,
13510 .help = N_("timeout seconds")
13512 {.name = "list",
13513 .type = VSH_OT_BOOL,
13514 .help = N_("list valid event types")
13516 {.name = "timestamp",
13517 .type = VSH_OT_BOOL,
13518 .help = N_("show timestamp for each printed event")
13520 {.name = NULL}
13523 static bool
13524 cmdEvent(vshControl *ctl, const vshCmd *cmd)
13526 virDomainPtr dom = NULL;
13527 bool ret = false;
13528 int timeout = 0;
13529 virshDomEventData *data = NULL;
13530 size_t i;
13531 const char *eventName = NULL;
13532 int event = -1;
13533 bool all = vshCommandOptBool(cmd, "all");
13534 bool loop = vshCommandOptBool(cmd, "loop");
13535 bool timestamp = vshCommandOptBool(cmd, "timestamp");
13536 int count = 0;
13537 virshControlPtr priv = ctl->privData;
13539 if (vshCommandOptBool(cmd, "list")) {
13540 for (event = 0; event < VIR_DOMAIN_EVENT_ID_LAST; event++)
13541 vshPrint(ctl, "%s\n", virshDomainEventCallbacks[event].name);
13542 return true;
13545 if (vshCommandOptStringReq(ctl, cmd, "event", &eventName) < 0)
13546 return false;
13547 if (eventName) {
13548 for (event = 0; event < VIR_DOMAIN_EVENT_ID_LAST; event++)
13549 if (STREQ(eventName, virshDomainEventCallbacks[event].name))
13550 break;
13551 if (event == VIR_DOMAIN_EVENT_ID_LAST) {
13552 vshError(ctl, _("unknown event type %s"), eventName);
13553 return false;
13555 } else if (!all) {
13556 vshError(ctl, "%s",
13557 _("one of --list, --all, or --event <type> is required"));
13558 return false;
13561 if (all) {
13562 if (VIR_ALLOC_N(data, VIR_DOMAIN_EVENT_ID_LAST) < 0)
13563 goto cleanup;
13564 for (i = 0; i < VIR_DOMAIN_EVENT_ID_LAST; i++) {
13565 data[i].ctl = ctl;
13566 data[i].loop = loop;
13567 data[i].count = &count;
13568 data[i].timestamp = timestamp;
13569 data[i].cb = &virshDomainEventCallbacks[i];
13570 data[i].id = -1;
13572 } else {
13573 if (VIR_ALLOC_N(data, 1) < 0)
13574 goto cleanup;
13575 data[0].ctl = ctl;
13576 data[0].loop = vshCommandOptBool(cmd, "loop");
13577 data[0].count = &count;
13578 data[0].timestamp = timestamp;
13579 data[0].cb = &virshDomainEventCallbacks[event];
13580 data[0].id = -1;
13582 if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
13583 goto cleanup;
13585 if (vshCommandOptBool(cmd, "domain"))
13586 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
13587 goto cleanup;
13589 if (vshEventStart(ctl, timeout) < 0)
13590 goto cleanup;
13592 for (i = 0; i < (all ? VIR_DOMAIN_EVENT_ID_LAST : 1); i++) {
13593 if ((data[i].id = virConnectDomainEventRegisterAny(priv->conn, dom,
13594 all ? i : event,
13595 data[i].cb->cb,
13596 &data[i],
13597 NULL)) < 0) {
13598 /* When registering for all events: if the first
13599 * registration succeeds, silently ignore failures on all
13600 * later registrations on the assumption that the server
13601 * is older and didn't know quite as many events. */
13602 if (i)
13603 vshResetLibvirtError();
13604 else
13605 goto cleanup;
13608 switch (vshEventWait(ctl)) {
13609 case VSH_EVENT_INTERRUPT:
13610 vshPrint(ctl, "%s", _("event loop interrupted\n"));
13611 break;
13612 case VSH_EVENT_TIMEOUT:
13613 vshPrint(ctl, "%s", _("event loop timed out\n"));
13614 break;
13615 case VSH_EVENT_DONE:
13616 break;
13617 default:
13618 goto cleanup;
13620 vshPrint(ctl, _("events received: %d\n"), count);
13621 if (count)
13622 ret = true;
13624 cleanup:
13625 vshEventCleanup(ctl);
13626 if (data) {
13627 for (i = 0; i < (all ? VIR_DOMAIN_EVENT_ID_LAST : 1); i++) {
13628 if (data[i].id >= 0 &&
13629 virConnectDomainEventDeregisterAny(priv->conn, data[i].id) < 0)
13630 ret = false;
13632 VIR_FREE(data);
13634 virshDomainFree(dom);
13635 return ret;
13640 * "change-media" command
13642 static const vshCmdInfo info_change_media[] = {
13643 {.name = "help",
13644 .data = N_("Change media of CD or floppy drive")
13646 {.name = "desc",
13647 .data = N_("Change media of CD or floppy drive.")
13649 {.name = NULL}
13652 static const vshCmdOptDef opts_change_media[] = {
13653 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
13654 {.name = "path",
13655 .type = VSH_OT_DATA,
13656 .flags = VSH_OFLAG_REQ,
13657 .completer = virshDomainDiskTargetCompleter,
13658 .help = N_("Fully-qualified path or target of disk device")
13660 {.name = "source",
13661 .type = VSH_OT_STRING,
13662 .help = N_("source of the media")
13664 {.name = "eject",
13665 .type = VSH_OT_BOOL,
13666 .help = N_("Eject the media")
13668 {.name = "insert",
13669 .type = VSH_OT_BOOL,
13670 .help = N_("Insert the media")
13672 {.name = "update",
13673 .type = VSH_OT_BOOL,
13674 .help = N_("Update the media")
13676 VIRSH_COMMON_OPT_CURRENT(N_("can be either or both of --live and "
13677 "--config, depends on implementation "
13678 "hypervisor driver")),
13679 VIRSH_COMMON_OPT_LIVE(N_("alter live configuration of running domain")),
13680 VIRSH_COMMON_OPT_CONFIG(N_("alter persistent configuration, effect "
13681 "observed on next boot")),
13682 {.name = "force",
13683 .type = VSH_OT_BOOL,
13684 .help = N_("force media changing")
13686 {.name = "print-xml",
13687 .type = VSH_OT_BOOL,
13688 .help = N_("print XML document rather than change media")
13690 {.name = "block",
13691 .type = VSH_OT_BOOL,
13692 .help = N_("source media is a block device")
13694 {.name = NULL}
13697 static bool
13698 cmdChangeMedia(vshControl *ctl, const vshCmd *cmd)
13700 virDomainPtr dom = NULL;
13701 const char *source = NULL;
13702 const char *path = NULL;
13703 char *doc = NULL;
13704 xmlNodePtr disk_node = NULL;
13705 char *disk_xml = NULL;
13706 bool ret = false;
13707 virshUpdateDiskXMLType update_type;
13708 const char *action = NULL;
13709 const char *success_msg = NULL;
13710 bool config = vshCommandOptBool(cmd, "config");
13711 bool live = vshCommandOptBool(cmd, "live");
13712 bool current = vshCommandOptBool(cmd, "current");
13713 bool force = vshCommandOptBool(cmd, "force");
13714 bool eject = vshCommandOptBool(cmd, "eject");
13715 bool insert = vshCommandOptBool(cmd, "insert");
13716 bool update = vshCommandOptBool(cmd, "update");
13717 bool block = vshCommandOptBool(cmd, "block");
13718 unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
13720 VSH_EXCLUSIVE_OPTIONS_VAR(eject, insert);
13721 VSH_EXCLUSIVE_OPTIONS_VAR(eject, update);
13722 VSH_EXCLUSIVE_OPTIONS_VAR(insert, update);
13724 VSH_EXCLUSIVE_OPTIONS_VAR(eject, block);
13726 if (vshCommandOptStringReq(ctl, cmd, "source", &source) < 0)
13727 return false;
13729 /* Docs state that update without source is eject */
13730 if (update && !source) {
13731 update = false;
13732 eject = true;
13735 if (eject) {
13736 update_type = VIRSH_UPDATE_DISK_XML_EJECT;
13737 action = "eject";
13738 success_msg = _("Successfully ejected media.");
13741 if (insert) {
13742 update_type = VIRSH_UPDATE_DISK_XML_INSERT;
13743 action = "insert";
13744 success_msg = _("Successfully inserted media.");
13747 if (update || (!eject && !insert)) {
13748 update_type = VIRSH_UPDATE_DISK_XML_UPDATE;
13749 action = "update";
13750 success_msg = _("Successfully updated media.");
13753 VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
13754 VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
13756 if (config)
13757 flags |= VIR_DOMAIN_AFFECT_CONFIG;
13758 if (live)
13759 flags |= VIR_DOMAIN_AFFECT_LIVE;
13760 if (force)
13761 flags |= VIR_DOMAIN_DEVICE_MODIFY_FORCE;
13763 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
13764 return false;
13766 if (vshCommandOptStringReq(ctl, cmd, "path", &path) < 0)
13767 goto cleanup;
13769 if (flags & VIR_DOMAIN_AFFECT_CONFIG)
13770 doc = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
13771 else
13772 doc = virDomainGetXMLDesc(dom, 0);
13773 if (!doc)
13774 goto cleanup;
13776 if (!(disk_node = virshFindDisk(doc, path, VIRSH_FIND_DISK_CHANGEABLE)))
13777 goto cleanup;
13779 if (!(disk_xml = virshUpdateDiskXML(disk_node, source, block, path,
13780 update_type)))
13781 goto cleanup;
13783 if (vshCommandOptBool(cmd, "print-xml")) {
13784 vshPrint(ctl, "%s", disk_xml);
13785 } else {
13786 if (virDomainUpdateDeviceFlags(dom, disk_xml, flags) != 0) {
13787 vshError(ctl, _("Failed to complete action %s on media"), action);
13788 goto cleanup;
13791 vshPrint(ctl, "%s", success_msg);
13794 ret = true;
13796 cleanup:
13797 VIR_FREE(doc);
13798 xmlFreeNode(disk_node);
13799 VIR_FREE(disk_xml);
13800 virshDomainFree(dom);
13801 return ret;
13804 static const vshCmdInfo info_domfstrim[] = {
13805 {.name = "help",
13806 .data = N_("Invoke fstrim on domain's mounted filesystems.")
13808 {.name = "desc",
13809 .data = N_("Invoke fstrim on domain's mounted filesystems.")
13811 {.name = NULL}
13814 static const vshCmdOptDef opts_domfstrim[] = {
13815 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
13816 {.name = "minimum",
13817 .type = VSH_OT_INT,
13818 .help = N_("Just a hint to ignore contiguous "
13819 "free ranges smaller than this (Bytes)")
13821 {.name = "mountpoint",
13822 .type = VSH_OT_STRING,
13823 .help = N_("which mount point to trim")
13825 {.name = NULL}
13827 static bool
13828 cmdDomFSTrim(vshControl *ctl, const vshCmd *cmd)
13830 virDomainPtr dom = NULL;
13831 bool ret = false;
13832 unsigned long long minimum = 0;
13833 const char *mountPoint = NULL;
13834 unsigned int flags = 0;
13836 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
13837 return ret;
13839 if (vshCommandOptULongLong(ctl, cmd, "minimum", &minimum) < 0)
13840 goto cleanup;
13842 if (vshCommandOptStringReq(ctl, cmd, "mountpoint", &mountPoint) < 0)
13843 goto cleanup;
13845 if (virDomainFSTrim(dom, mountPoint, minimum, flags) < 0) {
13846 vshError(ctl, _("Unable to invoke fstrim"));
13847 goto cleanup;
13850 ret = true;
13852 cleanup:
13853 virshDomainFree(dom);
13854 return ret;
13857 static const vshCmdInfo info_domfsfreeze[] = {
13858 {.name = "help",
13859 .data = N_("Freeze domain's mounted filesystems.")
13861 {.name = "desc",
13862 .data = N_("Freeze domain's mounted filesystems.")
13864 {.name = NULL}
13867 static const vshCmdOptDef opts_domfsfreeze[] = {
13868 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
13869 {.name = "mountpoint",
13870 .type = VSH_OT_ARGV,
13871 .help = N_("mountpoint path to be frozen")
13873 {.name = NULL}
13875 static bool
13876 cmdDomFSFreeze(vshControl *ctl, const vshCmd *cmd)
13878 virDomainPtr dom = NULL;
13879 int ret = -1;
13880 const vshCmdOpt *opt = NULL;
13881 const char **mountpoints = NULL;
13882 size_t nmountpoints = 0;
13884 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
13885 return false;
13887 while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
13888 if (VIR_EXPAND_N(mountpoints, nmountpoints, 1) < 0) {
13889 vshError(ctl, _("%s: %d: failed to allocate mountpoints"),
13890 __FILE__, __LINE__);
13891 goto cleanup;
13893 mountpoints[nmountpoints-1] = opt->data;
13896 ret = virDomainFSFreeze(dom, mountpoints, nmountpoints, 0);
13897 if (ret < 0) {
13898 vshError(ctl, _("Unable to freeze filesystems"));
13899 goto cleanup;
13902 vshPrintExtra(ctl, _("Froze %d filesystem(s)\n"), ret);
13904 cleanup:
13905 VIR_FREE(mountpoints);
13906 virshDomainFree(dom);
13907 return ret >= 0;
13910 static const vshCmdInfo info_domfsthaw[] = {
13911 {.name = "help",
13912 .data = N_("Thaw domain's mounted filesystems.")
13914 {.name = "desc",
13915 .data = N_("Thaw domain's mounted filesystems.")
13917 {.name = NULL}
13920 static const vshCmdOptDef opts_domfsthaw[] = {
13921 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
13922 {.name = "mountpoint",
13923 .type = VSH_OT_ARGV,
13924 .help = N_("mountpoint path to be thawed")
13926 {.name = NULL}
13928 static bool
13929 cmdDomFSThaw(vshControl *ctl, const vshCmd *cmd)
13931 virDomainPtr dom = NULL;
13932 int ret = -1;
13933 const vshCmdOpt *opt = NULL;
13934 const char **mountpoints = NULL;
13935 size_t nmountpoints = 0;
13937 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
13938 return false;
13940 while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
13941 if (VIR_EXPAND_N(mountpoints, nmountpoints, 1) < 0) {
13942 vshError(ctl, _("%s: %d: failed to allocate mountpoints"),
13943 __FILE__, __LINE__);
13944 goto cleanup;
13946 mountpoints[nmountpoints-1] = opt->data;
13949 ret = virDomainFSThaw(dom, mountpoints, nmountpoints, 0);
13950 if (ret < 0) {
13951 vshError(ctl, _("Unable to thaw filesystems"));
13952 goto cleanup;
13955 vshPrintExtra(ctl, _("Thawed %d filesystem(s)\n"), ret);
13957 cleanup:
13958 VIR_FREE(mountpoints);
13959 virshDomainFree(dom);
13960 return ret >= 0;
13963 static const vshCmdInfo info_domfsinfo[] = {
13964 {.name = "help",
13965 .data = N_("Get information of domain's mounted filesystems.")
13967 {.name = "desc",
13968 .data = N_("Get information of domain's mounted filesystems.")
13970 {.name = NULL}
13973 static const vshCmdOptDef opts_domfsinfo[] = {
13974 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
13975 {.name = NULL}
13978 static bool
13979 cmdDomFSInfo(vshControl *ctl, const vshCmd *cmd)
13981 virDomainPtr dom = NULL;
13982 int rc = -1;
13983 size_t i, j;
13984 virDomainFSInfoPtr *info = NULL;
13985 vshTablePtr table = NULL;
13986 size_t ninfos = 0;
13987 bool ret = false;
13989 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
13990 return false;
13992 rc = virDomainGetFSInfo(dom, &info, 0);
13993 if (rc < 0) {
13994 vshError(ctl, _("Unable to get filesystem information"));
13995 goto cleanup;
13997 ninfos = rc;
13999 if (ninfos == 0) {
14000 ret = true;
14001 vshPrintExtra(ctl, _("No filesystems are mounted in the domain"));
14002 goto cleanup;
14005 if (info) {
14006 table = vshTableNew(_("Mountpoint"), _("Name"), _("Type"), _("Target"), NULL);
14007 if (!table)
14008 goto cleanup;
14010 for (i = 0; i < ninfos; i++) {
14011 virBuffer targetsBuff = VIR_BUFFER_INITIALIZER;
14012 VIR_AUTOFREE(char *) targets = NULL;
14014 for (j = 0; j < info[i]->ndevAlias; j++)
14015 virBufferAsprintf(&targetsBuff, "%s,", info[i]->devAlias[j]);
14016 virBufferTrim(&targetsBuff, ",", -1);
14018 targets = virBufferContentAndReset(&targetsBuff);
14020 if (vshTableRowAppend(table,
14021 info[i]->mountpoint,
14022 info[i]->name,
14023 info[i]->fstype,
14024 targets ? targets : "",
14025 NULL) < 0)
14026 goto cleanup;
14029 vshTablePrintToStdout(table, ctl);
14032 ret = true;
14034 cleanup:
14035 if (info) {
14036 for (i = 0; i < ninfos; i++)
14037 virDomainFSInfoFree(info[i]);
14038 VIR_FREE(info);
14040 vshTableFree(table);
14041 virshDomainFree(dom);
14042 return ret;
14047 * "backup-begin" command
14049 static const vshCmdInfo info_backup_begin[] = {
14050 {.name = "help",
14051 .data = N_("Start a disk backup of a live domain")
14053 {.name = "desc",
14054 .data = N_("Use XML to start a full or incremental disk backup of a live "
14055 "domain, optionally creating a checkpoint")
14057 {.name = NULL}
14060 static const vshCmdOptDef opts_backup_begin[] = {
14061 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
14062 {.name = "xmlfile",
14063 .type = VSH_OT_STRING,
14064 .help = N_("domain backup XML"),
14066 {.name = "checkpointxml",
14067 .type = VSH_OT_STRING,
14068 .help = N_("domain checkpoint XML"),
14070 {.name = "no-metadata",
14071 .type = VSH_OT_BOOL,
14072 .help = N_("create checkpoint but don't track metadata"),
14074 {.name = "quiesce",
14075 .type = VSH_OT_BOOL,
14076 .help = N_("quiesce guest's file systems"),
14078 /* TODO: --wait/--verbose/--timeout flags for push model backups? */
14079 {.name = NULL}
14082 static bool
14083 cmdBackupBegin(vshControl *ctl,
14084 const vshCmd *cmd)
14086 virDomainPtr dom = NULL;
14087 bool ret = false;
14088 const char *backup_from = NULL;
14089 char *backup_buffer = NULL;
14090 const char *check_from = NULL;
14091 char *check_buffer = NULL;
14092 unsigned int flags = 0;
14093 int id;
14095 if (vshCommandOptBool(cmd, "no-metadata"))
14096 flags |= VIR_DOMAIN_BACKUP_BEGIN_NO_METADATA;
14097 if (vshCommandOptBool(cmd, "quiesce"))
14098 flags |= VIR_DOMAIN_BACKUP_BEGIN_QUIESCE;
14100 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
14101 goto cleanup;
14103 if (vshCommandOptStringReq(ctl, cmd, "xmlfile", &backup_from) < 0)
14104 goto cleanup;
14105 if (!backup_from) {
14106 backup_buffer = vshStrdup(ctl, "<domainbackup/>");
14107 } else {
14108 if (virFileReadAll(backup_from, VSH_MAX_XML_FILE, &backup_buffer) < 0) {
14109 vshSaveLibvirtError();
14110 goto cleanup;
14114 if (vshCommandOptStringReq(ctl, cmd, "checkpointxml", &check_from) < 0)
14115 goto cleanup;
14116 if (check_from) {
14117 if (virFileReadAll(check_from, VSH_MAX_XML_FILE, &check_buffer) < 0) {
14118 vshSaveLibvirtError();
14119 goto cleanup;
14123 id = virDomainBackupBegin(dom, backup_buffer, check_buffer, flags);
14125 if (id < 0)
14126 goto cleanup;
14128 vshPrint(ctl, _("Backup id %d started\n"), id);
14129 if (backup_from)
14130 vshPrintExtra(ctl, _("backup used description from '%s'\n"),
14131 backup_from);
14132 if (check_buffer)
14133 vshPrintExtra(ctl, _("checkpoint created from '%s'\n"), check_from);
14135 ret = true;
14137 cleanup:
14138 VIR_FREE(backup_buffer);
14139 VIR_FREE(check_buffer);
14140 virshDomainFree(dom);
14142 return ret;
14145 /* TODO: backup-begin-as? */
14148 * "backup-dumpxml" command
14150 static const vshCmdInfo info_backup_dumpxml[] = {
14151 {.name = "help",
14152 .data = N_("Dump XML for an ongoing domain block backup job")
14154 {.name = "desc",
14155 .data = N_("Backup Dump XML")
14157 {.name = NULL}
14160 static const vshCmdOptDef opts_backup_dumpxml[] = {
14161 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
14162 {.name = "id",
14163 .type = VSH_OT_INT,
14164 .help = N_("backup job id"),
14165 /* TODO: Add API for listing active jobs, then adding a completer? */
14167 /* TODO - worth adding this flag?
14168 {.name = "checkpoint",
14169 .type = VSH_OT_BOOL,
14170 .help = N_("if the backup created a checkpoint, also dump that XML")
14173 {.name = NULL}
14176 static bool
14177 cmdBackupDumpXML(vshControl *ctl,
14178 const vshCmd *cmd)
14180 virDomainPtr dom = NULL;
14181 bool ret = false;
14182 char *xml = NULL;
14183 unsigned int flags = 0;
14184 int id = 0;
14186 if (vshCommandOptBool(cmd, "security-info"))
14187 flags |= VIR_DOMAIN_XML_SECURE;
14189 if (vshCommandOptInt(ctl, cmd, "id", &id) < 0)
14190 return false;
14192 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
14193 return false;
14195 if (!(xml = virDomainBackupGetXMLDesc(dom, id, flags)))
14196 goto cleanup;
14198 vshPrint(ctl, "%s", xml);
14199 ret = true;
14201 cleanup:
14202 VIR_FREE(xml);
14203 virshDomainFree(dom);
14205 return ret;
14210 * "backup-end" command
14212 static const vshCmdInfo info_backup_end[] = {
14213 {.name = "help",
14214 .data = N_("Conclude a disk backup of a live domain")
14216 {.name = "desc",
14217 .data = N_("End a domain block backup job")
14219 {.name = NULL}
14222 static const vshCmdOptDef opts_backup_end[] = {
14223 VIRSH_COMMON_OPT_DOMAIN_FULL(0),
14224 {.name = "id",
14225 .type = VSH_OT_INT,
14226 .help = N_("backup job id"),
14227 /* TODO: Add API for listing active jobs, then adding a completer? */
14229 {.name = "abort",
14230 .type = VSH_OT_BOOL,
14231 .help = N_("abandon a push model backup that has not yet completed")
14233 {.name = NULL}
14236 static bool
14237 cmdBackupEnd(vshControl *ctl, const vshCmd *cmd)
14239 virDomainPtr dom = NULL;
14240 bool ret = false;
14241 unsigned int flags = 0;
14242 int id = 0;
14243 int rc;
14245 if (vshCommandOptBool(cmd, "abort"))
14246 flags |= VIR_DOMAIN_BACKUP_END_ABORT;
14248 if (vshCommandOptInt(ctl, cmd, "id", &id) < 0)
14249 return false;
14251 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
14252 goto cleanup;
14254 rc = virDomainBackupEnd(dom, id, flags);
14256 if (rc < 0)
14257 goto cleanup;
14258 if (rc == 0)
14259 vshPrint(ctl, _("Backup id %d aborted"), id);
14260 else
14261 vshPrint(ctl, _("Backup id %d completed"), id);
14263 ret = true;
14265 cleanup:
14266 virshDomainFree(dom);
14268 return ret;
14272 const vshCmdDef domManagementCmds[] = {
14273 {.name = "attach-device",
14274 .handler = cmdAttachDevice,
14275 .opts = opts_attach_device,
14276 .info = info_attach_device,
14277 .flags = 0
14279 {.name = "attach-disk",
14280 .handler = cmdAttachDisk,
14281 .opts = opts_attach_disk,
14282 .info = info_attach_disk,
14283 .flags = 0
14285 {.name = "attach-interface",
14286 .handler = cmdAttachInterface,
14287 .opts = opts_attach_interface,
14288 .info = info_attach_interface,
14289 .flags = 0
14291 {.name = "autostart",
14292 .handler = cmdAutostart,
14293 .opts = opts_autostart,
14294 .info = info_autostart,
14295 .flags = 0
14297 {.name = "backup-begin",
14298 .handler = cmdBackupBegin,
14299 .opts = opts_backup_begin,
14300 .info = info_backup_begin,
14301 .flags = 0
14303 {.name = "backup-dumpxml",
14304 .handler = cmdBackupDumpXML,
14305 .opts = opts_backup_dumpxml,
14306 .info = info_backup_dumpxml,
14307 .flags = 0
14309 {.name = "backup-end",
14310 .handler = cmdBackupEnd,
14311 .opts = opts_backup_end,
14312 .info = info_backup_end,
14313 .flags = 0
14315 {.name = "blkdeviotune",
14316 .handler = cmdBlkdeviotune,
14317 .opts = opts_blkdeviotune,
14318 .info = info_blkdeviotune,
14319 .flags = 0
14321 {.name = "blkiotune",
14322 .handler = cmdBlkiotune,
14323 .opts = opts_blkiotune,
14324 .info = info_blkiotune,
14325 .flags = 0
14327 {.name = "blockcommit",
14328 .handler = cmdBlockcommit,
14329 .opts = opts_blockcommit,
14330 .info = info_blockcommit,
14331 .flags = 0
14333 {.name = "blockcopy",
14334 .handler = cmdBlockcopy,
14335 .opts = opts_blockcopy,
14336 .info = info_blockcopy,
14337 .flags = 0
14339 {.name = "blockjob",
14340 .handler = cmdBlockjob,
14341 .opts = opts_blockjob,
14342 .info = info_blockjob,
14343 .flags = 0
14345 {.name = "blockpull",
14346 .handler = cmdBlockpull,
14347 .opts = opts_blockpull,
14348 .info = info_blockpull,
14349 .flags = 0
14351 {.name = "blockresize",
14352 .handler = cmdBlockresize,
14353 .opts = opts_blockresize,
14354 .info = info_blockresize,
14355 .flags = 0
14357 {.name = "change-media",
14358 .handler = cmdChangeMedia,
14359 .opts = opts_change_media,
14360 .info = info_change_media,
14361 .flags = 0
14363 #ifndef WIN32
14364 {.name = "console",
14365 .handler = cmdConsole,
14366 .opts = opts_console,
14367 .info = info_console,
14368 .flags = 0
14370 #endif
14371 {.name = "cpu-stats",
14372 .handler = cmdCPUStats,
14373 .opts = opts_cpu_stats,
14374 .info = info_cpu_stats,
14375 .flags = 0
14377 {.name = "create",
14378 .handler = cmdCreate,
14379 .opts = opts_create,
14380 .info = info_create,
14381 .flags = 0
14383 {.name = "define",
14384 .handler = cmdDefine,
14385 .opts = opts_define,
14386 .info = info_define,
14387 .flags = 0
14389 {.name = "desc",
14390 .handler = cmdDesc,
14391 .opts = opts_desc,
14392 .info = info_desc,
14393 .flags = 0
14395 {.name = "destroy",
14396 .handler = cmdDestroy,
14397 .opts = opts_destroy,
14398 .info = info_destroy,
14399 .flags = 0
14401 {.name = "detach-device",
14402 .handler = cmdDetachDevice,
14403 .opts = opts_detach_device,
14404 .info = info_detach_device,
14405 .flags = 0
14407 {.name = "detach-device-alias",
14408 .handler = cmdDetachDeviceAlias,
14409 .opts = opts_detach_device_alias,
14410 .info = info_detach_device_alias,
14411 .flags = 0
14413 {.name = "detach-disk",
14414 .handler = cmdDetachDisk,
14415 .opts = opts_detach_disk,
14416 .info = info_detach_disk,
14417 .flags = 0
14419 {.name = "detach-interface",
14420 .handler = cmdDetachInterface,
14421 .opts = opts_detach_interface,
14422 .info = info_detach_interface,
14423 .flags = 0
14425 {.name = "domdisplay",
14426 .handler = cmdDomDisplay,
14427 .opts = opts_domdisplay,
14428 .info = info_domdisplay,
14429 .flags = 0
14431 {.name = "domfsfreeze",
14432 .handler = cmdDomFSFreeze,
14433 .opts = opts_domfsfreeze,
14434 .info = info_domfsfreeze,
14435 .flags = 0
14437 {.name = "domfsthaw",
14438 .handler = cmdDomFSThaw,
14439 .opts = opts_domfsthaw,
14440 .info = info_domfsthaw,
14441 .flags = 0
14443 {.name = "domfsinfo",
14444 .handler = cmdDomFSInfo,
14445 .opts = opts_domfsinfo,
14446 .info = info_domfsinfo,
14447 .flags = 0
14449 {.name = "domfstrim",
14450 .handler = cmdDomFSTrim,
14451 .opts = opts_domfstrim,
14452 .info = info_domfstrim,
14453 .flags = 0
14455 {.name = "domhostname",
14456 .handler = cmdDomHostname,
14457 .opts = opts_domhostname,
14458 .info = info_domhostname,
14459 .flags = 0
14461 {.name = "domid",
14462 .handler = cmdDomid,
14463 .opts = opts_domid,
14464 .info = info_domid,
14465 .flags = 0
14467 {.name = "domif-setlink",
14468 .handler = cmdDomIfSetLink,
14469 .opts = opts_domif_setlink,
14470 .info = info_domif_setlink,
14471 .flags = 0
14473 {.name = "domiftune",
14474 .handler = cmdDomIftune,
14475 .opts = opts_domiftune,
14476 .info = info_domiftune,
14477 .flags = 0
14479 {.name = "domjobabort",
14480 .handler = cmdDomjobabort,
14481 .opts = opts_domjobabort,
14482 .info = info_domjobabort,
14483 .flags = 0
14485 {.name = "domjobinfo",
14486 .handler = cmdDomjobinfo,
14487 .opts = opts_domjobinfo,
14488 .info = info_domjobinfo,
14489 .flags = 0
14491 {.name = "domname",
14492 .handler = cmdDomname,
14493 .opts = opts_domname,
14494 .info = info_domname,
14495 .flags = 0
14497 {.name = "domrename",
14498 .handler = cmdDomrename,
14499 .opts = opts_domrename,
14500 .info = info_domrename,
14501 .flags = 0
14503 {.name = "dompmsuspend",
14504 .handler = cmdDomPMSuspend,
14505 .opts = opts_dom_pm_suspend,
14506 .info = info_dom_pm_suspend,
14507 .flags = 0
14509 {.name = "dompmwakeup",
14510 .handler = cmdDomPMWakeup,
14511 .opts = opts_dom_pm_wakeup,
14512 .info = info_dom_pm_wakeup,
14513 .flags = 0
14515 {.name = "domuuid",
14516 .handler = cmdDomuuid,
14517 .opts = opts_domuuid,
14518 .info = info_domuuid,
14519 .flags = 0
14521 {.name = "domxml-from-native",
14522 .handler = cmdDomXMLFromNative,
14523 .opts = opts_domxmlfromnative,
14524 .info = info_domxmlfromnative,
14525 .flags = 0
14527 {.name = "domxml-to-native",
14528 .handler = cmdDomXMLToNative,
14529 .opts = opts_domxmltonative,
14530 .info = info_domxmltonative,
14531 .flags = 0
14533 {.name = "dump",
14534 .handler = cmdDump,
14535 .opts = opts_dump,
14536 .info = info_dump,
14537 .flags = 0
14539 {.name = "dumpxml",
14540 .handler = cmdDumpXML,
14541 .opts = opts_dumpxml,
14542 .info = info_dumpxml,
14543 .flags = 0
14545 {.name = "edit",
14546 .handler = cmdEdit,
14547 .opts = opts_edit,
14548 .info = info_edit,
14549 .flags = 0
14551 {.name = "event",
14552 .handler = cmdEvent,
14553 .opts = opts_event,
14554 .info = info_event,
14555 .flags = 0
14557 {.name = "inject-nmi",
14558 .handler = cmdInjectNMI,
14559 .opts = opts_inject_nmi,
14560 .info = info_inject_nmi,
14561 .flags = 0
14563 {.name = "iothreadinfo",
14564 .handler = cmdIOThreadInfo,
14565 .opts = opts_iothreadinfo,
14566 .info = info_iothreadinfo,
14567 .flags = 0
14569 {.name = "iothreadpin",
14570 .handler = cmdIOThreadPin,
14571 .opts = opts_iothreadpin,
14572 .info = info_iothreadpin,
14573 .flags = 0
14575 {.name = "iothreadadd",
14576 .handler = cmdIOThreadAdd,
14577 .opts = opts_iothreadadd,
14578 .info = info_iothreadadd,
14579 .flags = 0
14581 {.name = "iothreadset",
14582 .handler = cmdIOThreadSet,
14583 .opts = opts_iothreadset,
14584 .info = info_iothreadset,
14585 .flags = 0
14587 {.name = "iothreaddel",
14588 .handler = cmdIOThreadDel,
14589 .opts = opts_iothreaddel,
14590 .info = info_iothreaddel,
14591 .flags = 0
14593 {.name = "send-key",
14594 .handler = cmdSendKey,
14595 .opts = opts_send_key,
14596 .info = info_send_key,
14597 .flags = 0
14599 {.name = "send-process-signal",
14600 .handler = cmdSendProcessSignal,
14601 .opts = opts_send_process_signal,
14602 .info = info_send_process_signal,
14603 .flags = 0
14605 {.name = "lxc-enter-namespace",
14606 .handler = cmdLxcEnterNamespace,
14607 .opts = opts_lxc_enter_namespace,
14608 .info = info_lxc_enter_namespace,
14609 .flags = 0
14611 {.name = "managedsave",
14612 .handler = cmdManagedSave,
14613 .opts = opts_managedsave,
14614 .info = info_managedsave,
14615 .flags = 0
14617 {.name = "managedsave-remove",
14618 .handler = cmdManagedSaveRemove,
14619 .opts = opts_managedsaveremove,
14620 .info = info_managedsaveremove,
14621 .flags = 0
14623 {.name = "managedsave-edit",
14624 .handler = cmdManagedSaveEdit,
14625 .opts = opts_managed_save_edit,
14626 .info = info_managed_save_edit,
14627 .flags = 0
14629 {.name = "managedsave-dumpxml",
14630 .handler = cmdManagedSaveDumpxml,
14631 .opts = opts_managed_save_dumpxml,
14632 .info = info_managed_save_dumpxml,
14633 .flags = 0
14635 {.name = "managedsave-define",
14636 .handler = cmdManagedSaveDefine,
14637 .opts = opts_managed_save_define,
14638 .info = info_managed_save_define,
14639 .flags = 0
14641 {.name = "memtune",
14642 .handler = cmdMemtune,
14643 .opts = opts_memtune,
14644 .info = info_memtune,
14645 .flags = 0
14647 {.name = "perf",
14648 .handler = cmdPerf,
14649 .opts = opts_perf,
14650 .info = info_perf,
14651 .flags = 0
14653 {.name = "metadata",
14654 .handler = cmdMetadata,
14655 .opts = opts_metadata,
14656 .info = info_metadata,
14657 .flags = 0
14659 {.name = "migrate",
14660 .handler = cmdMigrate,
14661 .opts = opts_migrate,
14662 .info = info_migrate,
14663 .flags = 0
14665 {.name = "migrate-setmaxdowntime",
14666 .handler = cmdMigrateSetMaxDowntime,
14667 .opts = opts_migrate_setmaxdowntime,
14668 .info = info_migrate_setmaxdowntime,
14669 .flags = 0
14671 {.name = "migrate-getmaxdowntime",
14672 .handler = cmdMigrateGetMaxDowntime,
14673 .opts = opts_migrate_getmaxdowntime,
14674 .info = info_migrate_getmaxdowntime,
14675 .flags = 0
14677 {.name = "migrate-compcache",
14678 .handler = cmdMigrateCompCache,
14679 .opts = opts_migrate_compcache,
14680 .info = info_migrate_compcache,
14681 .flags = 0
14683 {.name = "migrate-setspeed",
14684 .handler = cmdMigrateSetMaxSpeed,
14685 .opts = opts_migrate_setspeed,
14686 .info = info_migrate_setspeed,
14687 .flags = 0
14689 {.name = "migrate-getspeed",
14690 .handler = cmdMigrateGetMaxSpeed,
14691 .opts = opts_migrate_getspeed,
14692 .info = info_migrate_getspeed,
14693 .flags = 0
14695 {.name = "migrate-postcopy",
14696 .handler = cmdMigratePostCopy,
14697 .opts = opts_migrate_postcopy,
14698 .info = info_migrate_postcopy,
14699 .flags = 0
14701 {.name = "numatune",
14702 .handler = cmdNumatune,
14703 .opts = opts_numatune,
14704 .info = info_numatune,
14705 .flags = 0
14707 {.name = "qemu-attach",
14708 .handler = cmdQemuAttach,
14709 .opts = opts_qemu_attach,
14710 .info = info_qemu_attach,
14711 .flags = 0
14713 {.name = "qemu-monitor-command",
14714 .handler = cmdQemuMonitorCommand,
14715 .opts = opts_qemu_monitor_command,
14716 .info = info_qemu_monitor_command,
14717 .flags = 0
14719 {.name = "qemu-monitor-event",
14720 .handler = cmdQemuMonitorEvent,
14721 .opts = opts_qemu_monitor_event,
14722 .info = info_qemu_monitor_event,
14723 .flags = 0
14725 {.name = "qemu-agent-command",
14726 .handler = cmdQemuAgentCommand,
14727 .opts = opts_qemu_agent_command,
14728 .info = info_qemu_agent_command,
14729 .flags = 0
14731 {.name = "reboot",
14732 .handler = cmdReboot,
14733 .opts = opts_reboot,
14734 .info = info_reboot,
14735 .flags = 0
14737 {.name = "reset",
14738 .handler = cmdReset,
14739 .opts = opts_reset,
14740 .info = info_reset,
14741 .flags = 0
14743 {.name = "restore",
14744 .handler = cmdRestore,
14745 .opts = opts_restore,
14746 .info = info_restore,
14747 .flags = 0
14749 {.name = "resume",
14750 .handler = cmdResume,
14751 .opts = opts_resume,
14752 .info = info_resume,
14753 .flags = 0
14755 {.name = "save",
14756 .handler = cmdSave,
14757 .opts = opts_save,
14758 .info = info_save,
14759 .flags = 0
14761 {.name = "save-image-define",
14762 .handler = cmdSaveImageDefine,
14763 .opts = opts_save_image_define,
14764 .info = info_save_image_define,
14765 .flags = 0
14767 {.name = "save-image-dumpxml",
14768 .handler = cmdSaveImageDumpxml,
14769 .opts = opts_save_image_dumpxml,
14770 .info = info_save_image_dumpxml,
14771 .flags = 0
14773 {.name = "save-image-edit",
14774 .handler = cmdSaveImageEdit,
14775 .opts = opts_save_image_edit,
14776 .info = info_save_image_edit,
14777 .flags = 0
14779 {.name = "schedinfo",
14780 .handler = cmdSchedinfo,
14781 .opts = opts_schedinfo,
14782 .info = info_schedinfo,
14783 .flags = 0
14785 {.name = "screenshot",
14786 .handler = cmdScreenshot,
14787 .opts = opts_screenshot,
14788 .info = info_screenshot,
14789 .flags = 0
14791 {.name = "set-lifecycle-action",
14792 .handler = cmdSetLifecycleAction,
14793 .opts = opts_setLifecycleAction,
14794 .info = info_setLifecycleAction,
14795 .flags = 0
14797 {.name = "set-user-password",
14798 .handler = cmdSetUserPassword,
14799 .opts = opts_set_user_password,
14800 .info = info_set_user_password,
14801 .flags = 0
14803 {.name = "setmaxmem",
14804 .handler = cmdSetmaxmem,
14805 .opts = opts_setmaxmem,
14806 .info = info_setmaxmem,
14807 .flags = 0
14809 {.name = "setmem",
14810 .handler = cmdSetmem,
14811 .opts = opts_setmem,
14812 .info = info_setmem,
14813 .flags = 0
14815 {.name = "setvcpus",
14816 .handler = cmdSetvcpus,
14817 .opts = opts_setvcpus,
14818 .info = info_setvcpus,
14819 .flags = 0
14821 {.name = "shutdown",
14822 .handler = cmdShutdown,
14823 .opts = opts_shutdown,
14824 .info = info_shutdown,
14825 .flags = 0
14827 {.name = "start",
14828 .handler = cmdStart,
14829 .opts = opts_start,
14830 .info = info_start,
14831 .flags = 0
14833 {.name = "suspend",
14834 .handler = cmdSuspend,
14835 .opts = opts_suspend,
14836 .info = info_suspend,
14837 .flags = 0
14839 {.name = "ttyconsole",
14840 .handler = cmdTTYConsole,
14841 .opts = opts_ttyconsole,
14842 .info = info_ttyconsole,
14843 .flags = 0
14845 {.name = "undefine",
14846 .handler = cmdUndefine,
14847 .opts = opts_undefine,
14848 .info = info_undefine,
14849 .flags = 0
14851 {.name = "update-device",
14852 .handler = cmdUpdateDevice,
14853 .opts = opts_update_device,
14854 .info = info_update_device,
14855 .flags = 0
14857 {.name = "vcpucount",
14858 .handler = cmdVcpucount,
14859 .opts = opts_vcpucount,
14860 .info = info_vcpucount,
14861 .flags = 0
14863 {.name = "vcpuinfo",
14864 .handler = cmdVcpuinfo,
14865 .opts = opts_vcpuinfo,
14866 .info = info_vcpuinfo,
14867 .flags = 0
14869 {.name = "vcpupin",
14870 .handler = cmdVcpuPin,
14871 .opts = opts_vcpupin,
14872 .info = info_vcpupin,
14873 .flags = 0
14875 {.name = "emulatorpin",
14876 .handler = cmdEmulatorPin,
14877 .opts = opts_emulatorpin,
14878 .info = info_emulatorpin,
14879 .flags = 0
14881 {.name = "vncdisplay",
14882 .handler = cmdVNCDisplay,
14883 .opts = opts_vncdisplay,
14884 .info = info_vncdisplay,
14885 .flags = 0
14887 {.name = "guestvcpus",
14888 .handler = cmdGuestvcpus,
14889 .opts = opts_guestvcpus,
14890 .info = info_guestvcpus,
14891 .flags = 0
14893 {.name = "setvcpu",
14894 .handler = cmdSetvcpu,
14895 .opts = opts_setvcpu,
14896 .info = info_setvcpu,
14897 .flags = 0
14899 {.name = "domblkthreshold",
14900 .handler = cmdDomblkthreshold,
14901 .opts = opts_domblkthreshold,
14902 .info = info_domblkthreshold,
14903 .flags = 0
14905 {.name = NULL}