1 /*
2  * virsh-domain.c: Commands to manage domain
3  *
4  * Copyright (C) 2005, 2007-2016 Red Hat, Inc.
5  *
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.
10  *
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.
15  *
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/>.
19  */
20 
21 #include <config.h>
22 #include "virsh-domain.h"
23 #include "virsh-util.h"
24 
25 #include <fcntl.h>
26 #include <signal.h>
27 
28 #include <libxml/parser.h>
29 #include <libxml/xpath.h>
30 
31 #include "internal.h"
32 #include "virbitmap.h"
33 #include "virbuffer.h"
34 #include "conf/domain_conf.h"
35 #include "viralloc.h"
36 #include "vircommand.h"
37 #include "virfile.h"
38 #include "virjson.h"
39 #include "virkeycode.h"
40 #include "virmacaddr.h"
41 #include "virnetdevbandwidth.h"
42 #include "virprocess.h"
43 #include "virstring.h"
44 #include "virsh-console.h"
45 #include "virsh-domain-monitor.h"
46 #include "virsh-host.h"
47 #include "virerror.h"
48 #include "virtime.h"
49 #include "virtypedparam.h"
50 #include "virxml.h"
51 #include "viruri.h"
52 #include "vsh-table.h"
53 #include "virenum.h"
54 #include "virutil.h"
55 
56 enum virshAddressType {
57     VIRSH_ADDRESS_TYPE_PCI,
58     VIRSH_ADDRESS_TYPE_SCSI,
59     VIRSH_ADDRESS_TYPE_IDE,
60     VIRSH_ADDRESS_TYPE_CCW,
61     VIRSH_ADDRESS_TYPE_USB,
62     VIRSH_ADDRESS_TYPE_SATA,
63 
64     VIRSH_ADDRESS_TYPE_LAST
65 };
66 
67 VIR_ENUM_DECL(virshAddress);
68 VIR_ENUM_IMPL(virshAddress,
69               VIRSH_ADDRESS_TYPE_LAST,
70               "pci",
71               "scsi",
72               "ide",
73               "ccw",
74               "usb",
75               "sata");
76 
77 struct virshAddressPCI {
78     unsigned int domain;
79     unsigned int bus;
80     unsigned int slot;
81     unsigned int function;
82     bool multifunction;
83 };
84 
85 struct virshAddressDrive {
86     unsigned int controller;
87     unsigned int bus;
88     unsigned long long unit;
89 };
90 
91 struct virshAddressCCW {
92     unsigned int cssid;
93     unsigned int ssid;
94     unsigned int devno;
95 };
96 
97 struct virshAddressUSB {
98     unsigned int bus;
99     unsigned int port;
100 };
101 
102 struct virshAddress {
103     int type; /* enum virshAddressType */
104     union {
105         struct virshAddressPCI pci;
106         struct virshAddressDrive drive;
107         struct virshAddressCCW ccw;
108         struct virshAddressUSB usb;
109     } addr;
110 };
111 
112 
113 /* pci address pci:0000.00.0x0a.0 (domain:bus:slot:function)
114  * ide disk address: ide:00.00.0 (controller:bus:unit)
115  * scsi disk address: scsi:00.00.0 (controller:bus:unit)
116  * ccw disk address: ccw:0xfe.0.0000 (cssid:ssid:devno)
117  * usb disk address: usb:00.00 (bus:port)
118  * sata disk address: sata:00.00.0 (controller:bus:unit)
119  */
120 static int
virshAddressParse(const char * str,bool multifunction,struct virshAddress * addr)121 virshAddressParse(const char *str,
122                   bool multifunction,
123                   struct virshAddress *addr)
124 {
125     g_autofree char *type = g_strdup(str);
126     char *a = strchr(type, ':');
127 
128     if (!a)
129         return -1;
130 
131     *a = '\0';
132 
133     addr->type = virshAddressTypeFromString(type);
134 
135     switch ((enum virshAddressType) addr->type) {
136     case VIRSH_ADDRESS_TYPE_PCI:
137         addr->addr.pci.multifunction = multifunction;
138 
139         if (virStrToLong_uip(++a, &a, 16, &addr->addr.pci.domain) < 0 ||
140             virStrToLong_uip(++a, &a, 16, &addr->addr.pci.bus) < 0 ||
141             virStrToLong_uip(++a, &a, 16, &addr->addr.pci.slot) < 0 ||
142             virStrToLong_uip(++a, &a, 16, &addr->addr.pci.function) < 0)
143             return -1;
144         break;
145 
146     case VIRSH_ADDRESS_TYPE_SATA:
147     case VIRSH_ADDRESS_TYPE_IDE:
148     case VIRSH_ADDRESS_TYPE_SCSI:
149         if (virStrToLong_uip(++a, &a, 10, &addr->addr.drive.controller) < 0 ||
150             virStrToLong_uip(++a, &a, 10, &addr->addr.drive.bus) < 0 ||
151             virStrToLong_ullp(++a, &a, 10, &addr->addr.drive.unit) < 0)
152             return -1;
153         break;
154 
155     case VIRSH_ADDRESS_TYPE_CCW:
156         if (virStrToLong_uip(++a, &a, 16, &addr->addr.ccw.cssid) < 0 ||
157             virStrToLong_uip(++a, &a, 16, &addr->addr.ccw.ssid) < 0 ||
158             virStrToLong_uip(++a, &a, 16, &addr->addr.ccw.devno) < 0)
159             return -1;
160         break;
161 
162     case VIRSH_ADDRESS_TYPE_USB:
163         if (virStrToLong_uip(++a, &a, 10, &addr->addr.usb.bus) < 0 ||
164             virStrToLong_uip(++a, &a, 10, &addr->addr.usb.port) < 0)
165             return -1;
166         break;
167 
168     case VIRSH_ADDRESS_TYPE_LAST:
169     default:
170         return -1;
171     }
172 
173     return 0;
174 }
175 
176 
177 static void
virshAddressFormat(virBuffer * buf,struct virshAddress * addr)178 virshAddressFormat(virBuffer *buf,
179                    struct virshAddress *addr)
180 {
181     switch ((enum virshAddressType) addr->type) {
182     case VIRSH_ADDRESS_TYPE_PCI:
183         virBufferAsprintf(buf,
184                           "<address type='pci' domain='0x%04x' bus='0x%02x' slot='0x%02x' function='0x%0x'",
185                           addr->addr.pci.domain,
186                           addr->addr.pci.bus,
187                           addr->addr.pci.slot,
188                           addr->addr.pci.function);
189 
190         if (addr->addr.pci.multifunction)
191             virBufferAddLit(buf, " multifunction='on'");
192 
193         virBufferAddLit(buf, "/>\n");
194         break;
195 
196     case VIRSH_ADDRESS_TYPE_SATA:
197     case VIRSH_ADDRESS_TYPE_IDE:
198     case VIRSH_ADDRESS_TYPE_SCSI:
199         virBufferAsprintf(buf,
200                           "<address type='drive' controller='%u' bus='%u' unit='%llu'/>\n",
201                           addr->addr.drive.controller,
202                           addr->addr.drive.bus,
203                           addr->addr.drive.unit);
204         break;
205 
206     case VIRSH_ADDRESS_TYPE_CCW:
207         virBufferAsprintf(buf,
208                           "<address type='ccw' cssid='0x%02x' ssid='0x%01x' devno='0x%04x'/>\n",
209                           addr->addr.ccw.cssid,
210                           addr->addr.ccw.ssid,
211                           addr->addr.ccw.devno);
212         break;
213 
214     case VIRSH_ADDRESS_TYPE_USB:
215         virBufferAsprintf(buf,
216                           "<address type='usb' bus='%u' port='%u'/>\n",
217                           addr->addr.usb.bus,
218                           addr->addr.usb.port);
219         break;
220 
221     case VIRSH_ADDRESS_TYPE_LAST:
222     default:
223         return;
224     }
225 }
226 
227 
228 #define VIRSH_COMMON_OPT_DOMAIN_PERSISTENT \
229     {.name = "persistent", \
230      .type = VSH_OT_BOOL, \
231      .help = N_("make live change persistent") \
232     }
233 
234 #define VIRSH_COMMON_OPT_DOMAIN_CONFIG \
235     VIRSH_COMMON_OPT_CONFIG(N_("affect next boot"))
236 
237 #define VIRSH_COMMON_OPT_DOMAIN_LIVE \
238     VIRSH_COMMON_OPT_LIVE(N_("affect running domain"))
239 
240 #define VIRSH_COMMON_OPT_DOMAIN_CURRENT \
241     VIRSH_COMMON_OPT_CURRENT(N_("affect current domain"))
242 
243 
244 static virDomainPtr
virshDomainDefine(virConnectPtr conn,const char * xml,unsigned int flags)245 virshDomainDefine(virConnectPtr conn, const char *xml, unsigned int flags)
246 {
247     virDomainPtr dom;
248 
249     if (!flags)
250         return virDomainDefineXML(conn, xml);
251 
252     dom = virDomainDefineXMLFlags(conn, xml, flags);
253     /* If validate is the only flag, just drop it and
254      * try again.
255      */
256     if (!dom) {
257         if ((virGetLastErrorCode() == VIR_ERR_NO_SUPPORT) &&
258             (flags == VIR_DOMAIN_DEFINE_VALIDATE))
259             dom = virDomainDefineXML(conn, xml);
260     }
261     return dom;
262 }
263 
264 VIR_ENUM_DECL(virshDomainVcpuState);
265 VIR_ENUM_IMPL(virshDomainVcpuState,
266               VIR_VCPU_LAST,
267               N_("offline"),
268               N_("running"),
269               N_("blocked"));
270 
271 static const char *
virshDomainVcpuStateToString(int state)272 virshDomainVcpuStateToString(int state)
273 {
274     const char *str = virshDomainVcpuStateTypeToString(state);
275     return str ? _(str) : _("no state");
276 }
277 
278 /*
279  * Determine number of CPU nodes present by trying
280  * virNodeGetCPUMap and falling back to virNodeGetInfo
281  * if needed.
282  */
283 static int
virshNodeGetCPUCount(virConnectPtr conn)284 virshNodeGetCPUCount(virConnectPtr conn)
285 {
286     int ret;
287     virNodeInfo nodeinfo;
288 
289     if ((ret = virNodeGetCPUMap(conn, NULL, NULL, 0)) < 0) {
290         /* fall back to nodeinfo */
291         vshResetLibvirtError();
292         if (virNodeGetInfo(conn, &nodeinfo) == 0)
293             ret = VIR_NODEINFO_MAXCPUS(nodeinfo);
294     }
295     return ret;
296 }
297 
298 /*
299  * "attach-device" command
300  */
301 static const vshCmdInfo info_attach_device[] = {
302     {.name = "help",
303      .data = N_("attach device from an XML file")
304     },
305     {.name = "desc",
306      .data = N_("Attach device from an XML <file>.")
307     },
308     {.name = NULL}
309 };
310 
311 static const vshCmdOptDef opts_attach_device[] = {
312     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
313     VIRSH_COMMON_OPT_FILE(N_("XML file")),
314     VIRSH_COMMON_OPT_DOMAIN_PERSISTENT,
315     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
316     VIRSH_COMMON_OPT_DOMAIN_LIVE,
317     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
318     {.name = NULL}
319 };
320 
321 static bool
cmdAttachDevice(vshControl * ctl,const vshCmd * cmd)322 cmdAttachDevice(vshControl *ctl, const vshCmd *cmd)
323 {
324     g_autoptr(virshDomain) dom = NULL;
325     const char *from = NULL;
326     char *buffer;
327     int rv;
328     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
329     bool current = vshCommandOptBool(cmd, "current");
330     bool config = vshCommandOptBool(cmd, "config");
331     bool live = vshCommandOptBool(cmd, "live");
332     bool persistent = vshCommandOptBool(cmd, "persistent");
333 
334     VSH_EXCLUSIVE_OPTIONS_VAR(persistent, current);
335 
336     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
337     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
338 
339     if (config || persistent)
340         flags |= VIR_DOMAIN_AFFECT_CONFIG;
341     if (live)
342         flags |= VIR_DOMAIN_AFFECT_LIVE;
343 
344     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
345         return false;
346 
347     if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
348         return false;
349 
350     if (persistent &&
351         virDomainIsActive(dom) == 1)
352         flags |= VIR_DOMAIN_AFFECT_LIVE;
353 
354     if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) {
355         vshReportError(ctl);
356         return false;
357     }
358 
359     if (flags || current)
360         rv = virDomainAttachDeviceFlags(dom, buffer, flags);
361     else
362         rv = virDomainAttachDevice(dom, buffer);
363 
364     VIR_FREE(buffer);
365 
366     if (rv < 0) {
367         vshError(ctl, _("Failed to attach device from %s"), from);
368         return false;
369     }
370 
371     vshPrintExtra(ctl, "%s", _("Device attached successfully\n"));
372     return true;
373 }
374 
375 /*
376  * "attach-disk" command
377  */
378 static const vshCmdInfo info_attach_disk[] = {
379     {.name = "help",
380      .data = N_("attach disk device")
381     },
382     {.name = "desc",
383      .data = N_("Attach new disk device.")
384     },
385     {.name = NULL}
386 };
387 
388 static const vshCmdOptDef opts_attach_disk[] = {
389     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
390     {.name = "source",
391      .type = VSH_OT_DATA,
392      .flags = VSH_OFLAG_REQ | VSH_OFLAG_EMPTY_OK,
393      .help = N_("source of disk device or name of network disk")
394     },
395     {.name = "target",
396      .type = VSH_OT_DATA,
397      .flags = VSH_OFLAG_REQ,
398      .completer = virshCompleteEmpty,
399      .help = N_("target of disk device")
400     },
401     {.name = "targetbus",
402      .type = VSH_OT_STRING,
403      .help = N_("target bus of disk device")
404     },
405     {.name = "driver",
406      .type = VSH_OT_STRING,
407      .help = N_("driver of disk device")
408     },
409     {.name = "subdriver",
410      .type = VSH_OT_STRING,
411      .help = N_("subdriver of disk device")
412     },
413     {.name = "iothread",
414      .type = VSH_OT_STRING,
415      .completer = virshDomainIOThreadIdCompleter,
416      .help = N_("IOThread to be used by supported device")
417     },
418     {.name = "cache",
419      .type = VSH_OT_STRING,
420      .help = N_("cache mode of disk device")
421     },
422     {.name = "io",
423      .type = VSH_OT_STRING,
424      .help = N_("io policy of disk device")
425     },
426     {.name = "type",
427      .type = VSH_OT_STRING,
428      .help = N_("target device type")
429     },
430     {.name = "shareable",
431      .type = VSH_OT_ALIAS,
432      .help = "mode=shareable"
433     },
434     {.name = "mode",
435      .type = VSH_OT_STRING,
436      .help = N_("mode of device reading and writing")
437     },
438     {.name = "sourcetype",
439      .type = VSH_OT_STRING,
440      .help = N_("type of source (block|file|network)")
441     },
442     {.name = "serial",
443      .type = VSH_OT_STRING,
444      .completer = virshCompleteEmpty,
445      .help = N_("serial of disk device")
446     },
447     {.name = "wwn",
448      .type = VSH_OT_STRING,
449      .completer = virshCompleteEmpty,
450      .help = N_("wwn of disk device")
451     },
452     {.name = "alias",
453      .type = VSH_OT_STRING,
454      .completer = virshCompleteEmpty,
455      .help = N_("custom alias name of disk device")
456     },
457     {.name = "rawio",
458      .type = VSH_OT_BOOL,
459      .help = N_("needs rawio capability")
460     },
461     {.name = "address",
462      .type = VSH_OT_STRING,
463      .completer = virshCompleteEmpty,
464      .help = N_("address of disk device")
465     },
466     {.name = "multifunction",
467      .type = VSH_OT_BOOL,
468      .help = N_("use multifunction pci under specified address")
469     },
470     {.name = "print-xml",
471      .type = VSH_OT_BOOL,
472      .help = N_("print XML document rather than attach the disk")
473     },
474     {.name = "source-protocol",
475      .type = VSH_OT_STRING,
476      .help = N_("protocol used by disk device source")
477     },
478     {.name = "source-host-name",
479      .type = VSH_OT_STRING,
480      .completer = virshCompleteEmpty,
481      .help = N_("host name for source of disk device")
482     },
483     {.name = "source-host-transport",
484      .type = VSH_OT_STRING,
485      .help = N_("host transport for source of disk device")
486     },
487     {.name = "source-host-socket",
488      .type = VSH_OT_STRING,
489      .help = N_("host socket for source of disk device")
490     },
491     VIRSH_COMMON_OPT_DOMAIN_PERSISTENT,
492     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
493     VIRSH_COMMON_OPT_DOMAIN_LIVE,
494     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
495     {.name = NULL}
496 };
497 
498 
499 static int
cmdAttachDiskFormatAddress(vshControl * ctl,virBuffer * buf,const char * straddr,const char * target,bool multifunction)500 cmdAttachDiskFormatAddress(vshControl *ctl,
501                            virBuffer *buf,
502                            const char *straddr,
503                            const char *target,
504                            bool multifunction)
505 {
506     struct virshAddress diskAddr;
507 
508     if (virshAddressParse(straddr, multifunction, &diskAddr) < 0) {
509         vshError(ctl, _("Invalid address."));
510         return -1;
511     }
512 
513     if (STRPREFIX((const char *)target, "vd")) {
514         if (diskAddr.type != VIRSH_ADDRESS_TYPE_PCI &&
515             diskAddr.type != VIRSH_ADDRESS_TYPE_CCW) {
516             vshError(ctl, "%s",
517                      _("expecting a pci:0000.00.00.00 or ccw:00.0.0000 address."));
518             return -1;
519         }
520     } else if (STRPREFIX((const char *)target, "sd")) {
521         if (diskAddr.type != VIRSH_ADDRESS_TYPE_SCSI &&
522             diskAddr.type != VIRSH_ADDRESS_TYPE_USB &&
523             diskAddr.type != VIRSH_ADDRESS_TYPE_SATA) {
524             vshError(ctl, "%s",
525                      _("expecting a scsi:00.00.00 or usb:00.00 or sata:00.00.00 address."));
526             return -1;
527         }
528     } else if (STRPREFIX((const char *)target, "hd")) {
529         if (diskAddr.type != VIRSH_ADDRESS_TYPE_IDE) {
530             vshError(ctl, "%s", _("expecting an ide:00.00.00 address."));
531             return -1;
532         }
533     }
534 
535     virshAddressFormat(buf, &diskAddr);
536     return 0;
537 }
538 
539 
540 enum virshAttachDiskSourceType {
541     VIRSH_ATTACH_DISK_SOURCE_TYPE_NONE,
542     VIRSH_ATTACH_DISK_SOURCE_TYPE_FILE,
543     VIRSH_ATTACH_DISK_SOURCE_TYPE_BLOCK,
544     VIRSH_ATTACH_DISK_SOURCE_TYPE_NETWORK,
545 
546     VIRSH_ATTACH_DISK_SOURCE_TYPE_LAST
547 };
548 
549 VIR_ENUM_DECL(virshAttachDiskSource);
550 VIR_ENUM_IMPL(virshAttachDiskSource,
551               VIRSH_ATTACH_DISK_SOURCE_TYPE_LAST,
552               "",
553               "file",
554               "block",
555               "network");
556 
557 
558 static bool
cmdAttachDisk(vshControl * ctl,const vshCmd * cmd)559 cmdAttachDisk(vshControl *ctl, const vshCmd *cmd)
560 {
561     g_autoptr(virshDomain) dom = NULL;
562     const char *source = NULL;
563     const char *target = NULL;
564     const char *driver = NULL;
565     const char *subdriver = NULL;
566     const char *device = NULL;
567     const char *mode = NULL;
568     const char *iothread = NULL;
569     const char *cache = NULL;
570     const char *io = NULL;
571     const char *serial = NULL;
572     const char *straddr = NULL;
573     const char *wwn = NULL;
574     const char *targetbus = NULL;
575     const char *alias = NULL;
576     const char *source_protocol = NULL;
577     const char *host_name = NULL;
578     const char *host_transport = NULL;
579     const char *host_socket = NULL;
580     int ret;
581     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
582     const char *stype = NULL;
583     int type = VIR_STORAGE_TYPE_NONE;
584     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
585     g_auto(virBuffer) diskAttrBuf = VIR_BUFFER_INITIALIZER;
586     g_auto(virBuffer) diskChildBuf = VIR_BUFFER_INIT_CHILD(&buf);
587     g_auto(virBuffer) driverAttrBuf = VIR_BUFFER_INITIALIZER;
588     g_auto(virBuffer) sourceAttrBuf = VIR_BUFFER_INITIALIZER;
589     g_auto(virBuffer) sourceChildBuf = VIR_BUFFER_INIT_CHILD(&diskChildBuf);
590     g_auto(virBuffer) hostAttrBuf = VIR_BUFFER_INITIALIZER;
591     g_autofree char *xml = NULL;
592     struct stat st;
593     bool current = vshCommandOptBool(cmd, "current");
594     bool config = vshCommandOptBool(cmd, "config");
595     bool live = vshCommandOptBool(cmd, "live");
596     bool persistent = vshCommandOptBool(cmd, "persistent");
597     bool multifunction = vshCommandOptBool(cmd, "multifunction");
598 
599     VSH_EXCLUSIVE_OPTIONS_VAR(persistent, current);
600 
601     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
602     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
603 
604     VSH_REQUIRE_OPTION("source-host-name", "source-protocol");
605     VSH_REQUIRE_OPTION("source-host-transport", "source-protocol");
606     VSH_REQUIRE_OPTION("source-host-socket", "source-protocol");
607     VSH_REQUIRE_OPTION("source-host-socket", "source-host-transport");
608 
609     VSH_EXCLUSIVE_OPTIONS("source-host-name", "source-host-socket");
610 
611     if (config || persistent)
612         flags |= VIR_DOMAIN_AFFECT_CONFIG;
613     if (live)
614         flags |= VIR_DOMAIN_AFFECT_LIVE;
615 
616     if (vshCommandOptStringReq(ctl, cmd, "source", &source) < 0 ||
617         vshCommandOptStringReq(ctl, cmd, "target", &target) < 0 ||
618         vshCommandOptStringReq(ctl, cmd, "driver", &driver) < 0 ||
619         vshCommandOptStringReq(ctl, cmd, "subdriver", &subdriver) < 0 ||
620         vshCommandOptStringReq(ctl, cmd, "type", &device) < 0 ||
621         vshCommandOptStringReq(ctl, cmd, "mode", &mode) < 0 ||
622         vshCommandOptStringReq(ctl, cmd, "iothread", &iothread) < 0 ||
623         vshCommandOptStringReq(ctl, cmd, "cache", &cache) < 0 ||
624         vshCommandOptStringReq(ctl, cmd, "io", &io) < 0 ||
625         vshCommandOptStringReq(ctl, cmd, "serial", &serial) < 0 ||
626         vshCommandOptStringReq(ctl, cmd, "wwn", &wwn) < 0 ||
627         vshCommandOptStringReq(ctl, cmd, "address", &straddr) < 0 ||
628         vshCommandOptStringReq(ctl, cmd, "targetbus", &targetbus) < 0 ||
629         vshCommandOptStringReq(ctl, cmd, "alias", &alias) < 0 ||
630         vshCommandOptStringReq(ctl, cmd, "sourcetype", &stype) < 0 ||
631         vshCommandOptStringReq(ctl, cmd, "source-protocol", &source_protocol) < 0 ||
632         vshCommandOptStringReq(ctl, cmd, "source-host-name", &host_name) < 0 ||
633         vshCommandOptStringReq(ctl, cmd, "source-host-transport", &host_transport) < 0 ||
634         vshCommandOptStringReq(ctl, cmd, "source-host-socket", &host_socket) < 0)
635         return false;
636 
637     if (stype &&
638         (type = virshAttachDiskSourceTypeFromString(stype)) < 0) {
639         vshError(ctl, _("Unknown source type: '%s'"), stype);
640         return false;
641     }
642 
643     if (type == VIRSH_ATTACH_DISK_SOURCE_TYPE_NONE) {
644         if (source_protocol) {
645             type = VIRSH_ATTACH_DISK_SOURCE_TYPE_NETWORK;
646         } else  if (STRNEQ_NULLABLE(driver, "file") &&
647                     STRNEQ_NULLABLE(driver, "tap") &&
648                     source &&
649                     stat(source, &st) == 0 &&
650                     S_ISBLK(st.st_mode)) {
651             type = VIRSH_ATTACH_DISK_SOURCE_TYPE_BLOCK;
652         } else {
653             type = VIRSH_ATTACH_DISK_SOURCE_TYPE_FILE;
654         }
655     }
656 
657     if ((type == VIRSH_ATTACH_DISK_SOURCE_TYPE_NETWORK) != !!source_protocol) {
658         vshError(ctl, _("--source-protocol option requires --sourcetype network"));
659         return false;
660     }
661 
662     if (mode && STRNEQ(mode, "readonly") && STRNEQ(mode, "shareable")) {
663         vshError(ctl, _("No support for %s in command 'attach-disk'"), mode);
664         return false;
665     }
666 
667     if (wwn && !virValidateWWN(wwn))
668         return false;
669 
670     virBufferAsprintf(&diskAttrBuf, " type='%s'", virshAttachDiskSourceTypeToString(type));
671     virBufferEscapeString(&diskAttrBuf, " device='%s'", device);
672     if (vshCommandOptBool(cmd, "rawio"))
673         virBufferAddLit(&diskAttrBuf, " rawio='yes'");
674 
675     virBufferEscapeString(&driverAttrBuf, " name='%s'", driver);
676     virBufferEscapeString(&driverAttrBuf, " type='%s'", subdriver);
677     virBufferEscapeString(&driverAttrBuf, " iothread='%s'", iothread);
678     virBufferEscapeString(&driverAttrBuf, " cache='%s'", cache);
679     virBufferEscapeString(&driverAttrBuf, " io='%s'", io);
680 
681     virXMLFormatElement(&diskChildBuf, "driver", &driverAttrBuf, NULL);
682 
683     switch ((enum virshAttachDiskSourceType) type) {
684     case VIRSH_ATTACH_DISK_SOURCE_TYPE_FILE:
685         virBufferEscapeString(&sourceAttrBuf, " file='%s'", source);
686         break;
687 
688     case VIRSH_ATTACH_DISK_SOURCE_TYPE_BLOCK:
689         virBufferEscapeString(&sourceAttrBuf, " dev='%s'", source);
690         break;
691 
692     case VIRSH_ATTACH_DISK_SOURCE_TYPE_NETWORK:
693         virBufferEscapeString(&sourceAttrBuf, " protocol='%s'", source_protocol);
694         virBufferEscapeString(&sourceAttrBuf, " name='%s'", source);
695 
696         virBufferEscapeString(&hostAttrBuf, " transport='%s'", host_transport);
697         virBufferEscapeString(&hostAttrBuf, " socket='%s'", host_socket);
698 
699         if (host_name) {
700             g_autofree char *host_name_copy = g_strdup(host_name);
701             char *host_port = strchr(host_name_copy, ':');
702 
703             if (host_port) {
704                 *host_port = '\0';
705                 host_port++;
706             }
707 
708             virBufferEscapeString(&hostAttrBuf, " name='%s'", host_name_copy);
709             virBufferEscapeString(&hostAttrBuf, " port='%s'", host_port);
710         }
711         virXMLFormatElement(&sourceChildBuf, "host", &hostAttrBuf, NULL);
712         break;
713 
714     case VIRSH_ATTACH_DISK_SOURCE_TYPE_NONE:
715     case VIRSH_ATTACH_DISK_SOURCE_TYPE_LAST:
716         break;
717     }
718     virXMLFormatElement(&diskChildBuf, "source", &sourceAttrBuf, &sourceChildBuf);
719 
720     virBufferAsprintf(&diskChildBuf, "<target dev='%s'", target);
721     if (targetbus)
722         virBufferAsprintf(&diskChildBuf, " bus='%s'", targetbus);
723     virBufferAddLit(&diskChildBuf, "/>\n");
724 
725     if (mode)
726         virBufferAsprintf(&diskChildBuf, "<%s/>\n", mode);
727 
728     if (serial)
729         virBufferAsprintf(&diskChildBuf, "<serial>%s</serial>\n", serial);
730 
731     if (alias)
732         virBufferAsprintf(&diskChildBuf, "<alias name='%s'/>\n", alias);
733 
734     if (wwn)
735         virBufferAsprintf(&diskChildBuf, "<wwn>%s</wwn>\n", wwn);
736 
737     if (straddr &&
738         cmdAttachDiskFormatAddress(ctl, &diskChildBuf, straddr, target, multifunction) < 0)
739         return false;
740 
741     virXMLFormatElement(&buf, "disk", &diskAttrBuf, &diskChildBuf);
742 
743     xml = virBufferContentAndReset(&buf);
744 
745     if (vshCommandOptBool(cmd, "print-xml")) {
746         vshPrint(ctl, "%s", xml);
747         return true;
748     }
749 
750     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
751         return false;
752 
753     if (persistent &&
754         virDomainIsActive(dom) == 1)
755         flags |= VIR_DOMAIN_AFFECT_LIVE;
756 
757     if (flags || current)
758         ret = virDomainAttachDeviceFlags(dom, xml, flags);
759     else
760         ret = virDomainAttachDevice(dom, xml);
761 
762     if (ret < 0) {
763         vshError(ctl, "%s", _("Failed to attach disk"));
764         return false;
765     }
766 
767 
768     vshPrintExtra(ctl, "%s", _("Disk attached successfully\n"));
769     return true;
770 }
771 
772 /*
773  * "attach-interface" command
774  */
775 static const vshCmdInfo info_attach_interface[] = {
776     {.name = "help",
777      .data = N_("attach network interface")
778     },
779     {.name = "desc",
780      .data = N_("Attach new network interface.")
781     },
782     {.name = NULL}
783 };
784 
785 static const vshCmdOptDef opts_attach_interface[] = {
786     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
787     {.name = "type",
788      .type = VSH_OT_DATA,
789      .flags = VSH_OFLAG_REQ,
790      .help = N_("network interface type")
791     },
792     {.name = "source",
793      .type = VSH_OT_DATA,
794      .flags = VSH_OFLAG_REQ,
795      .help = N_("source of network interface")
796     },
797     {.name = "target",
798      .type = VSH_OT_STRING,
799      .completer = virshCompleteEmpty,
800      .help = N_("target network name")
801     },
802     {.name = "mac",
803      .type = VSH_OT_STRING,
804      .completer = virshCompleteEmpty,
805      .help = N_("MAC address")
806     },
807     {.name = "script",
808      .type = VSH_OT_STRING,
809      .help = N_("script used to bridge network interface")
810     },
811     {.name = "model",
812      .type = VSH_OT_STRING,
813      .help = N_("model type")
814     },
815     {.name = "alias",
816      .type = VSH_OT_STRING,
817      .completer = virshCompleteEmpty,
818      .help = N_("custom alias name of interface device")
819     },
820     {.name = "inbound",
821      .type = VSH_OT_STRING,
822      .completer = virshCompleteEmpty,
823      .help = N_("control domain's incoming traffics")
824     },
825     {.name = "outbound",
826      .type = VSH_OT_STRING,
827      .completer = virshCompleteEmpty,
828      .help = N_("control domain's outgoing traffics")
829     },
830     VIRSH_COMMON_OPT_DOMAIN_PERSISTENT,
831     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
832     VIRSH_COMMON_OPT_DOMAIN_LIVE,
833     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
834     {.name = "print-xml",
835      .type = VSH_OT_BOOL,
836      .help = N_("print XML document rather than attach the interface")
837     },
838     {.name = "managed",
839      .type = VSH_OT_BOOL,
840      .help = N_("libvirt will automatically detach/attach the device from/to host")
841     },
842     {.name = "source-mode",
843      .type = VSH_OT_STRING,
844      .completer = virshDomainInterfaceSourceModeCompleter,
845      .help = N_("mode attribute of <source/> element")
846     },
847     {.name = NULL}
848 };
849 
850 VIR_ENUM_IMPL(virshDomainInterfaceSourceMode,
851               VIRSH_DOMAIN_INTERFACE_SOURCE_MODE_LAST,
852               "server",
853               "client");
854 
855 /* parse inbound and outbound which are in the format of
856  * 'average,peak,burst,floor', in which peak and burst are optional,
857  * thus 'average,,burst' and 'average,peak' are also legal. */
858 
859 #define VIRSH_PARSE_RATE_FIELD(index, name) \
860     do { \
861         if (index < ntok && \
862             *tok[index] != '\0' && \
863             virStrToLong_ullp(tok[index], NULL, 10, &rate->name) < 0) { \
864             vshError(ctl, _("field '%s' is malformed"), #name); \
865             return -1; \
866         } \
867     } while (0)
868 
869 static int
virshParseRateStr(vshControl * ctl,const char * rateStr,virNetDevBandwidthRate * rate)870 virshParseRateStr(vshControl *ctl,
871                   const char *rateStr,
872                   virNetDevBandwidthRate *rate)
873 {
874     g_auto(GStrv) tok = NULL;
875     size_t ntok;
876 
877     if (!(tok = g_strsplit(rateStr, ",", 0)))
878         return -1;
879 
880     if ((ntok = g_strv_length(tok)) > 4) {
881         vshError(ctl, _("Rate string '%s' has too many fields"), rateStr);
882         return -1;
883     }
884 
885     VIRSH_PARSE_RATE_FIELD(0, average);
886     VIRSH_PARSE_RATE_FIELD(1, peak);
887     VIRSH_PARSE_RATE_FIELD(2, burst);
888     VIRSH_PARSE_RATE_FIELD(3, floor);
889 
890     return 0;
891 }
892 
893 #undef VIRSH_PARSE_RATE_FIELD
894 
895 static bool
cmdAttachInterface(vshControl * ctl,const vshCmd * cmd)896 cmdAttachInterface(vshControl *ctl, const vshCmd *cmd)
897 {
898     g_autoptr(virshDomain) dom = NULL;
899     const char *mac = NULL, *target = NULL, *script = NULL,
900                *type = NULL, *source = NULL, *model = NULL,
901                *inboundStr = NULL, *outboundStr = NULL, *alias = NULL;
902     const char *sourceModeStr = NULL;
903     int sourceMode = -1;
904     virNetDevBandwidthRate inbound, outbound;
905     virDomainNetType typ;
906     int ret;
907     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
908     g_autofree char *xml = NULL;
909     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
910     bool current = vshCommandOptBool(cmd, "current");
911     bool config = vshCommandOptBool(cmd, "config");
912     bool live = vshCommandOptBool(cmd, "live");
913     bool persistent = vshCommandOptBool(cmd, "persistent");
914     bool managed = vshCommandOptBool(cmd, "managed");
915 
916     VSH_EXCLUSIVE_OPTIONS_VAR(persistent, current);
917 
918     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
919     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
920 
921     if (config || persistent)
922         flags |= VIR_DOMAIN_AFFECT_CONFIG;
923     if (live)
924         flags |= VIR_DOMAIN_AFFECT_LIVE;
925 
926     if (vshCommandOptStringReq(ctl, cmd, "type", &type) < 0 ||
927         vshCommandOptStringReq(ctl, cmd, "source", &source) < 0 ||
928         vshCommandOptStringReq(ctl, cmd, "target", &target) < 0 ||
929         vshCommandOptStringReq(ctl, cmd, "mac", &mac) < 0 ||
930         vshCommandOptStringReq(ctl, cmd, "script", &script) < 0 ||
931         vshCommandOptStringReq(ctl, cmd, "model", &model) < 0 ||
932         vshCommandOptStringReq(ctl, cmd, "alias", &alias) < 0 ||
933         vshCommandOptStringReq(ctl, cmd, "inbound", &inboundStr) < 0 ||
934         vshCommandOptStringReq(ctl, cmd, "outbound", &outboundStr) < 0 ||
935         vshCommandOptStringReq(ctl, cmd, "source-mode", &sourceModeStr) < 0)
936         return false;
937 
938     /* check interface type */
939     if ((int)(typ = virDomainNetTypeFromString(type)) < 0) {
940         vshError(ctl, _("No support for %s in command 'attach-interface'"),
941                  type);
942         return false;
943     }
944 
945     if (sourceModeStr &&
946         (sourceMode = virshDomainInterfaceSourceModeTypeFromString(sourceModeStr)) < 0) {
947         vshError(ctl, _("Invalid source mode: %s"), sourceModeStr);
948         return false;
949     }
950 
951     if (inboundStr) {
952         memset(&inbound, 0, sizeof(inbound));
953         if (virshParseRateStr(ctl, inboundStr, &inbound) < 0)
954             return false;
955         if (!inbound.average && !inbound.floor) {
956             vshError(ctl, _("either inbound average or floor is mandatory"));
957             return false;
958         }
959     }
960     if (outboundStr) {
961         memset(&outbound, 0, sizeof(outbound));
962         if (virshParseRateStr(ctl, outboundStr, &outbound) < 0)
963             return false;
964         if (outbound.average == 0) {
965             vshError(ctl, _("outbound average is mandatory"));
966             return false;
967         }
968         if (outbound.floor) {
969             vshError(ctl, _("outbound floor is unsupported yet"));
970             return false;
971         }
972     }
973 
974     /* Make XML of interface */
975     virBufferAsprintf(&buf, "<interface type='%s'", type);
976 
977     if (managed)
978         virBufferAddLit(&buf, " managed='yes'>\n");
979     else
980         virBufferAddLit(&buf, ">\n");
981     virBufferAdjustIndent(&buf, 2);
982 
983     switch (typ) {
984     case VIR_DOMAIN_NET_TYPE_NETWORK:
985     case VIR_DOMAIN_NET_TYPE_BRIDGE:
986         virBufferAsprintf(&buf, "<source %s='%s'/>\n",
987                           virDomainNetTypeToString(typ), source);
988         break;
989     case VIR_DOMAIN_NET_TYPE_DIRECT:
990         virBufferAsprintf(&buf, "<source dev='%s'/>\n", source);
991         break;
992     case VIR_DOMAIN_NET_TYPE_HOSTDEV:
993     {
994         g_autofree char *pciaddrstr = g_strdup_printf("pci:%s", source);
995         struct virshAddress addr = { 0 };
996 
997         if (virshAddressParse(pciaddrstr, false, &addr) < 0) {
998             vshError(ctl, _("cannot parse pci address '%s' for network interface"),
999                      source);
1000             return false;
1001         }
1002 
1003         virBufferAddLit(&buf, "<source>\n");
1004         virBufferAdjustIndent(&buf, 2);
1005         virshAddressFormat(&buf, &addr);
1006         virBufferAdjustIndent(&buf, -2);
1007         virBufferAddLit(&buf, "</source>\n");
1008         break;
1009     }
1010 
1011     case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
1012         if (sourceMode < 0) {
1013             vshError(ctl, _("source-mode is mandatory"));
1014             return false;
1015         }
1016         virBufferAsprintf(&buf, "<source type='unix' path='%s' mode='%s'/>\n",
1017                           source,
1018                           virshDomainInterfaceSourceModeTypeToString(sourceMode));
1019         break;
1020 
1021     case VIR_DOMAIN_NET_TYPE_USER:
1022     case VIR_DOMAIN_NET_TYPE_ETHERNET:
1023     case VIR_DOMAIN_NET_TYPE_SERVER:
1024     case VIR_DOMAIN_NET_TYPE_CLIENT:
1025     case VIR_DOMAIN_NET_TYPE_MCAST:
1026     case VIR_DOMAIN_NET_TYPE_UDP:
1027     case VIR_DOMAIN_NET_TYPE_VDPA:
1028     case VIR_DOMAIN_NET_TYPE_INTERNAL:
1029     case VIR_DOMAIN_NET_TYPE_LAST:
1030         vshError(ctl, _("No support for %s in command 'attach-interface'"),
1031                  type);
1032         return false;
1033         break;
1034     }
1035 
1036     if (target != NULL)
1037         virBufferAsprintf(&buf, "<target dev='%s'/>\n", target);
1038     if (mac != NULL)
1039         virBufferAsprintf(&buf, "<mac address='%s'/>\n", mac);
1040     if (script != NULL)
1041         virBufferAsprintf(&buf, "<script path='%s'/>\n", script);
1042     if (model != NULL)
1043         virBufferAsprintf(&buf, "<model type='%s'/>\n", model);
1044 
1045     if (alias != NULL)
1046         virBufferAsprintf(&buf, "<alias name='%s'/>\n", alias);
1047 
1048     if (inboundStr || outboundStr) {
1049         virBufferAddLit(&buf, "<bandwidth>\n");
1050         virBufferAdjustIndent(&buf, 2);
1051         if (inboundStr && (inbound.average || inbound.floor)) {
1052             virBufferAddLit(&buf, "<inbound");
1053             if (inbound.average > 0)
1054                 virBufferAsprintf(&buf, " average='%llu'", inbound.average);
1055             if (inbound.peak > 0)
1056                 virBufferAsprintf(&buf, " peak='%llu'", inbound.peak);
1057             if (inbound.burst > 0)
1058                 virBufferAsprintf(&buf, " burst='%llu'", inbound.burst);
1059             if (inbound.floor > 0)
1060                 virBufferAsprintf(&buf, " floor='%llu'", inbound.floor);
1061             virBufferAddLit(&buf, "/>\n");
1062         }
1063         if (outboundStr && outbound.average > 0) {
1064             virBufferAsprintf(&buf, "<outbound average='%llu'", outbound.average);
1065             if (outbound.peak > 0)
1066                 virBufferAsprintf(&buf, " peak='%llu'", outbound.peak);
1067             if (outbound.burst > 0)
1068                 virBufferAsprintf(&buf, " burst='%llu'", outbound.burst);
1069             virBufferAddLit(&buf, "/>\n");
1070         }
1071         virBufferAdjustIndent(&buf, -2);
1072         virBufferAddLit(&buf, "</bandwidth>\n");
1073     }
1074 
1075     virBufferAdjustIndent(&buf, -2);
1076     virBufferAddLit(&buf, "</interface>\n");
1077 
1078     xml = virBufferContentAndReset(&buf);
1079 
1080     if (vshCommandOptBool(cmd, "print-xml")) {
1081         vshPrint(ctl, "%s", xml);
1082         return true;
1083     }
1084 
1085     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
1086         return false;
1087 
1088     if (persistent &&
1089         virDomainIsActive(dom) == 1)
1090         flags |= VIR_DOMAIN_AFFECT_LIVE;
1091 
1092     if (flags || current)
1093         ret = virDomainAttachDeviceFlags(dom, xml, flags);
1094     else
1095         ret = virDomainAttachDevice(dom, xml);
1096 
1097     if (ret != 0) {
1098         vshError(ctl, "%s", _("Failed to attach interface"));
1099         return false;
1100     }
1101 
1102     vshPrintExtra(ctl, "%s", _("Interface attached successfully\n"));
1103 
1104     return true;
1105 }
1106 
1107 /*
1108  * "autostart" command
1109  */
1110 static const vshCmdInfo info_autostart[] = {
1111     {.name = "help",
1112      .data = N_("autostart a domain")
1113     },
1114     {.name = "desc",
1115      .data = N_("Configure a domain to be automatically started at boot.")
1116     },
1117     {.name = NULL}
1118 };
1119 
1120 static const vshCmdOptDef opts_autostart[] = {
1121     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_PERSISTENT),
1122     {.name = "disable",
1123      .type = VSH_OT_BOOL,
1124      .help = N_("disable autostarting")
1125     },
1126     {.name = NULL}
1127 };
1128 
1129 static bool
cmdAutostart(vshControl * ctl,const vshCmd * cmd)1130 cmdAutostart(vshControl *ctl, const vshCmd *cmd)
1131 {
1132     g_autoptr(virshDomain) dom = NULL;
1133     const char *name;
1134     int autostart;
1135 
1136     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
1137         return false;
1138 
1139     autostart = !vshCommandOptBool(cmd, "disable");
1140 
1141     if (virDomainSetAutostart(dom, autostart) < 0) {
1142         if (autostart)
1143             vshError(ctl, _("Failed to mark domain '%s' as autostarted"), name);
1144         else
1145             vshError(ctl, _("Failed to unmark domain '%s' as autostarted"), name);
1146         return false;
1147     }
1148 
1149     if (autostart)
1150         vshPrintExtra(ctl, _("Domain '%s' marked as autostarted\n"), name);
1151     else
1152         vshPrintExtra(ctl, _("Domain '%s' unmarked as autostarted\n"), name);
1153 
1154     return true;
1155 }
1156 
1157 /*
1158  * "blkdeviotune" command
1159  */
1160 static const vshCmdInfo info_blkdeviotune[] = {
1161     {.name = "help",
1162      .data = N_("Set or query a block device I/O tuning parameters.")
1163     },
1164     {.name = "desc",
1165      .data = N_("Set or query disk I/O parameters such as block throttling.")
1166     },
1167     {.name = NULL}
1168 };
1169 
1170 static const vshCmdOptDef opts_blkdeviotune[] = {
1171     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
1172     {.name = "device",
1173      .type = VSH_OT_DATA,
1174      .flags = VSH_OFLAG_REQ,
1175      .completer = virshDomainDiskTargetCompleter,
1176      .help = N_("block device")
1177     },
1178     {.name = "total_bytes_sec",
1179      .type = VSH_OT_ALIAS,
1180      .help = "total-bytes-sec"
1181     },
1182     {.name = "total-bytes-sec",
1183      .type = VSH_OT_INT,
1184      .help = N_("total throughput limit, as scaled integer (default bytes)")
1185     },
1186     {.name = "read_bytes_sec",
1187      .type = VSH_OT_ALIAS,
1188      .help = "read-bytes-sec"
1189     },
1190     {.name = "read-bytes-sec",
1191      .type = VSH_OT_INT,
1192      .help = N_("read throughput limit, as scaled integer (default bytes)")
1193     },
1194     {.name = "write_bytes_sec",
1195      .type = VSH_OT_ALIAS,
1196      .help = "write-bytes-sec"
1197     },
1198     {.name = "write-bytes-sec",
1199      .type = VSH_OT_INT,
1200      .help =  N_("write throughput limit, as scaled integer (default bytes)")
1201     },
1202     {.name = "total_iops_sec",
1203      .type = VSH_OT_ALIAS,
1204      .help = "total-iops-sec"
1205     },
1206     {.name = "total-iops-sec",
1207      .type = VSH_OT_INT,
1208      .help = N_("total I/O operations limit per second")
1209     },
1210     {.name = "read_iops_sec",
1211      .type = VSH_OT_ALIAS,
1212      .help = "read-iops-sec"
1213     },
1214     {.name = "read-iops-sec",
1215      .type = VSH_OT_INT,
1216      .help = N_("read I/O operations limit per second")
1217     },
1218     {.name = "write_iops_sec",
1219      .type = VSH_OT_ALIAS,
1220      .help = "write-iops-sec"
1221     },
1222     {.name = "write-iops-sec",
1223      .type = VSH_OT_INT,
1224      .help = N_("write I/O operations limit per second")
1225     },
1226     {.name = "total_bytes_sec_max",
1227      .type = VSH_OT_ALIAS,
1228      .help = "total-bytes-sec-max"
1229     },
1230     {.name = "total-bytes-sec-max",
1231      .type = VSH_OT_INT,
1232      .help = N_("total max, as scaled integer (default bytes)")
1233     },
1234     {.name = "read_bytes_sec_max",
1235      .type = VSH_OT_ALIAS,
1236      .help = "read-bytes-sec-max"
1237     },
1238     {.name = "read-bytes-sec-max",
1239      .type = VSH_OT_INT,
1240      .help = N_("read max, as scaled integer (default bytes)")
1241     },
1242     {.name = "write_bytes_sec_max",
1243      .type = VSH_OT_ALIAS,
1244      .help = "write-bytes-sec-max"
1245     },
1246     {.name = "write-bytes-sec-max",
1247      .type = VSH_OT_INT,
1248      .help = N_("write max, as scaled integer (default bytes)")
1249     },
1250     {.name = "total_iops_sec_max",
1251      .type = VSH_OT_ALIAS,
1252      .help = "total-iops-sec-max"
1253     },
1254     {.name = "total-iops-sec-max",
1255      .type = VSH_OT_INT,
1256      .help = N_("total I/O operations max")
1257     },
1258     {.name = "read_iops_sec_max",
1259      .type = VSH_OT_ALIAS,
1260      .help = "read-iops-sec-max"
1261     },
1262     {.name = "read-iops-sec-max",
1263      .type = VSH_OT_INT,
1264      .help = N_("read I/O operations max")
1265     },
1266     {.name = "write_iops_sec_max",
1267      .type = VSH_OT_ALIAS,
1268      .help = "write-iops-sec-max"
1269     },
1270     {.name = "write-iops-sec-max",
1271      .type = VSH_OT_INT,
1272      .help = N_("write I/O operations max")
1273     },
1274     {.name = "size_iops_sec",
1275      .type = VSH_OT_ALIAS,
1276      .help = "size-iops-sec"
1277     },
1278     {.name = "size-iops-sec",
1279      .type = VSH_OT_INT,
1280      .help = N_("I/O size in bytes")
1281     },
1282     {.name = "group_name",
1283      .type = VSH_OT_ALIAS,
1284      .help = "group-name"
1285     },
1286     {.name = "group-name",
1287      .type = VSH_OT_STRING,
1288      .completer = virshCompleteEmpty,
1289      .help = N_("group name to share I/O quota between multiple drives")
1290     },
1291     {.name = "total_bytes_sec_max_length",
1292      .type = VSH_OT_ALIAS,
1293      .help = "total-bytes-sec-max-length"
1294     },
1295     {.name = "total-bytes-sec-max-length",
1296      .type = VSH_OT_INT,
1297      .help = N_("duration in seconds to allow total max bytes")
1298     },
1299     {.name = "read_bytes_sec_max_length",
1300      .type = VSH_OT_ALIAS,
1301      .help = "read-bytes-sec-max-length"
1302     },
1303     {.name = "read-bytes-sec-max-length",
1304      .type = VSH_OT_INT,
1305      .help = N_("duration in seconds to allow read max bytes")
1306     },
1307     {.name = "write_bytes_sec_max_length",
1308      .type = VSH_OT_ALIAS,
1309      .help = "write-bytes-sec-max-length"
1310     },
1311     {.name = "write-bytes-sec-max-length",
1312      .type = VSH_OT_INT,
1313      .help = N_("duration in seconds to allow write max bytes")
1314     },
1315     {.name = "total_iops_sec_max_length",
1316      .type = VSH_OT_ALIAS,
1317      .help = "total-iops-sec-max-length"
1318     },
1319     {.name = "total-iops-sec-max-length",
1320      .type = VSH_OT_INT,
1321      .help = N_("duration in seconds to allow total I/O operations max")
1322     },
1323     {.name = "read_iops_sec_max_length",
1324      .type = VSH_OT_ALIAS,
1325      .help = "read-iops-sec-max-length"
1326     },
1327     {.name = "read-iops-sec-max-length",
1328      .type = VSH_OT_INT,
1329      .help = N_("duration in seconds to allow read I/O operations max")
1330     },
1331     {.name = "write_iops_sec_max_length",
1332      .type = VSH_OT_ALIAS,
1333      .help = "write-iops-sec-max-length"
1334     },
1335     {.name = "write-iops-sec-max-length",
1336      .type = VSH_OT_INT,
1337      .help = N_("duration in seconds to allow write I/O operations max")
1338     },
1339     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
1340     VIRSH_COMMON_OPT_DOMAIN_LIVE,
1341     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
1342     {.name = NULL}
1343 };
1344 
1345 static bool
cmdBlkdeviotune(vshControl * ctl,const vshCmd * cmd)1346 cmdBlkdeviotune(vshControl *ctl, const vshCmd *cmd)
1347 {
1348     g_autoptr(virshDomain) dom = NULL;
1349     const char *name, *disk;
1350     const char *group_name = NULL;
1351     unsigned long long value;
1352     int nparams = 0;
1353     int maxparams = 0;
1354     virTypedParameterPtr params = NULL;
1355     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
1356     size_t i;
1357     int rv = 0;
1358     bool current = vshCommandOptBool(cmd, "current");
1359     bool config = vshCommandOptBool(cmd, "config");
1360     bool live = vshCommandOptBool(cmd, "live");
1361     bool ret = false;
1362 
1363     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
1364     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
1365 
1366     if (config)
1367         flags |= VIR_DOMAIN_AFFECT_CONFIG;
1368     if (live)
1369         flags |= VIR_DOMAIN_AFFECT_LIVE;
1370 
1371     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
1372         goto cleanup;
1373 
1374     if (vshCommandOptStringReq(ctl, cmd, "device", &disk) < 0)
1375         goto cleanup;
1376 
1377 #define VSH_ADD_IOTUNE_SCALED(PARAM, CONST) \
1378     if ((rv = vshCommandOptScaledInt(ctl, cmd, #PARAM, &value, \
1379                                      1, ULLONG_MAX)) < 0) { \
1380         goto interror; \
1381     } else if (rv > 0) { \
1382         if (virTypedParamsAddULLong(&params, &nparams, &maxparams, \
1383                                     VIR_DOMAIN_BLOCK_IOTUNE_##CONST, \
1384                                     value) < 0) \
1385             goto save_error; \
1386     }
1387 
1388     VSH_ADD_IOTUNE_SCALED(total-bytes-sec, TOTAL_BYTES_SEC);
1389     VSH_ADD_IOTUNE_SCALED(read-bytes-sec, READ_BYTES_SEC);
1390     VSH_ADD_IOTUNE_SCALED(write-bytes-sec, WRITE_BYTES_SEC);
1391     VSH_ADD_IOTUNE_SCALED(total-bytes-sec-max, TOTAL_BYTES_SEC_MAX);
1392     VSH_ADD_IOTUNE_SCALED(read-bytes-sec-max, READ_BYTES_SEC_MAX);
1393     VSH_ADD_IOTUNE_SCALED(write-bytes-sec-max, WRITE_BYTES_SEC_MAX);
1394 #undef VSH_ADD_IOTUNE_SCALED
1395 
1396 #define VSH_ADD_IOTUNE(PARAM, CONST) \
1397     if ((rv = vshCommandOptULongLong(ctl, cmd, #PARAM, &value)) < 0) { \
1398         goto interror; \
1399     } else if (rv > 0) { \
1400         if (virTypedParamsAddULLong(&params, &nparams, &maxparams, \
1401                                     VIR_DOMAIN_BLOCK_IOTUNE_##CONST, \
1402                                     value) < 0) \
1403             goto save_error; \
1404     }
1405 
1406     VSH_ADD_IOTUNE(total-iops-sec, TOTAL_IOPS_SEC);
1407     VSH_ADD_IOTUNE(read-iops-sec, READ_IOPS_SEC);
1408     VSH_ADD_IOTUNE(write-iops-sec, WRITE_IOPS_SEC);
1409     VSH_ADD_IOTUNE(total-iops-sec-max, TOTAL_IOPS_SEC_MAX);
1410     VSH_ADD_IOTUNE(read-iops-sec-max, READ_IOPS_SEC_MAX);
1411     VSH_ADD_IOTUNE(write-iops-sec-max, WRITE_IOPS_SEC_MAX);
1412     VSH_ADD_IOTUNE(size-iops-sec, SIZE_IOPS_SEC);
1413 
1414     VSH_ADD_IOTUNE(total-bytes-sec-max-length, TOTAL_BYTES_SEC_MAX_LENGTH);
1415     VSH_ADD_IOTUNE(read-bytes-sec-max-length, READ_BYTES_SEC_MAX_LENGTH);
1416     VSH_ADD_IOTUNE(write-bytes-sec-max-length, WRITE_BYTES_SEC_MAX_LENGTH);
1417     VSH_ADD_IOTUNE(total-iops-sec-max-length, TOTAL_IOPS_SEC_MAX_LENGTH);
1418     VSH_ADD_IOTUNE(read-iops-sec-max-length, READ_IOPS_SEC_MAX_LENGTH);
1419     VSH_ADD_IOTUNE(write-iops-sec-max-length, WRITE_IOPS_SEC_MAX_LENGTH);
1420 #undef VSH_ADD_IOTUNE
1421 
1422     if (vshCommandOptStringReq(ctl, cmd, "group-name", &group_name) < 0) {
1423         vshError(ctl, "%s", _("Unable to parse group-name parameter"));
1424         goto cleanup;
1425     }
1426 
1427     if (group_name) {
1428         if (virTypedParamsAddString(&params, &nparams, &maxparams,
1429                                     VIR_DOMAIN_BLOCK_IOTUNE_GROUP_NAME,
1430                                     group_name) < 0)
1431             goto save_error;
1432     }
1433 
1434 
1435     if (nparams == 0) {
1436         if (virDomainGetBlockIoTune(dom, NULL, NULL, &nparams, flags) != 0) {
1437             vshError(ctl, "%s",
1438                      _("Unable to get number of block I/O throttle parameters"));
1439             goto cleanup;
1440         }
1441 
1442         if (nparams == 0) {
1443             ret = true;
1444             goto cleanup;
1445         }
1446 
1447         params = g_new0(virTypedParameter, nparams);
1448 
1449         if (virDomainGetBlockIoTune(dom, disk, params, &nparams, flags) != 0) {
1450             vshError(ctl, "%s",
1451                      _("Unable to get block I/O throttle parameters"));
1452             goto cleanup;
1453         }
1454 
1455         for (i = 0; i < nparams; i++) {
1456             g_autofree char *str = vshGetTypedParamValue(ctl, &params[i]);
1457             vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
1458         }
1459     } else {
1460         if (virDomainSetBlockIoTune(dom, disk, params, nparams, flags) < 0)
1461             goto error;
1462     }
1463 
1464     ret = true;
1465 
1466  cleanup:
1467     virTypedParamsFree(params, nparams);
1468     return ret;
1469 
1470  save_error:
1471     vshSaveLibvirtError();
1472  error:
1473     vshError(ctl, "%s", _("Unable to change block I/O throttle"));
1474     goto cleanup;
1475 
1476  interror:
1477     vshError(ctl, "%s", _("Unable to parse integer parameter"));
1478     goto cleanup;
1479 }
1480 
1481 /*
1482  * "blkiotune" command
1483  */
1484 static const vshCmdInfo info_blkiotune[] = {
1485     {.name = "help",
1486      .data = N_("Get or set blkio parameters")
1487     },
1488     {.name = "desc",
1489      .data = N_("Get or set the current blkio parameters for a guest"
1490                 " domain.\n"
1491                 "    To get the blkio parameters use following command: \n\n"
1492                 "    virsh # blkiotune <domain>")
1493     },
1494     {.name = NULL}
1495 };
1496 
1497 static const vshCmdOptDef opts_blkiotune[] = {
1498     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
1499     {.name = "weight",
1500      .type = VSH_OT_INT,
1501      .help = N_("IO Weight")
1502     },
1503     {.name = "device-weights",
1504      .type = VSH_OT_STRING,
1505      .completer = virshCompleteEmpty,
1506      .help = N_("per-device IO Weights, in the form of /path/to/device,weight,...")
1507     },
1508     {.name = "device-read-iops-sec",
1509      .type = VSH_OT_STRING,
1510      .completer = virshCompleteEmpty,
1511      .help = N_("per-device read I/O limit per second, in the form of /path/to/device,read_iops_sec,...")
1512     },
1513     {.name = "device-write-iops-sec",
1514      .type = VSH_OT_STRING,
1515      .completer = virshCompleteEmpty,
1516      .help = N_("per-device write I/O limit per second, in the form of /path/to/device,write_iops_sec,...")
1517     },
1518     {.name = "device-read-bytes-sec",
1519      .type = VSH_OT_STRING,
1520      .completer = virshCompleteEmpty,
1521      .help = N_("per-device bytes read per second, in the form of /path/to/device,read_bytes_sec,...")
1522     },
1523     {.name = "device-write-bytes-sec",
1524      .type = VSH_OT_STRING,
1525      .completer = virshCompleteEmpty,
1526      .help = N_("per-device bytes wrote per second, in the form of /path/to/device,write_bytes_sec,...")
1527     },
1528     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
1529     VIRSH_COMMON_OPT_DOMAIN_LIVE,
1530     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
1531     {.name = NULL}
1532 };
1533 
1534 static bool
cmdBlkiotune(vshControl * ctl,const vshCmd * cmd)1535 cmdBlkiotune(vshControl * ctl, const vshCmd * cmd)
1536 {
1537     g_autoptr(virshDomain) dom = NULL;
1538     const char *device_weight = NULL;
1539     const char *device_riops = NULL;
1540     const char *device_wiops = NULL;
1541     const char *device_rbps = NULL;
1542     const char *device_wbps = NULL;
1543     int weight = 0;
1544     int nparams = 0;
1545     int maxparams = 0;
1546     int rv = 0;
1547     size_t i;
1548     virTypedParameterPtr params = NULL;
1549     bool ret = false;
1550     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
1551     bool current = vshCommandOptBool(cmd, "current");
1552     bool config = vshCommandOptBool(cmd, "config");
1553     bool live = vshCommandOptBool(cmd, "live");
1554 
1555     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
1556     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
1557 
1558     if (config)
1559         flags |= VIR_DOMAIN_AFFECT_CONFIG;
1560     if (live)
1561         flags |= VIR_DOMAIN_AFFECT_LIVE;
1562 
1563     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
1564         return false;
1565 
1566     if ((rv = vshCommandOptInt(ctl, cmd, "weight", &weight)) < 0) {
1567         goto cleanup;
1568     } else if (rv > 0) {
1569         if (weight <= 0) {
1570             vshError(ctl, _("Invalid value of %d for I/O weight"), weight);
1571             goto cleanup;
1572         }
1573         if (virTypedParamsAddUInt(&params, &nparams, &maxparams,
1574                                   VIR_DOMAIN_BLKIO_WEIGHT, weight) < 0)
1575             goto save_error;
1576     }
1577 
1578     rv = vshCommandOptStringQuiet(ctl, cmd, "device-weights", &device_weight);
1579     if (rv < 0) {
1580         vshError(ctl, "%s", _("Unable to parse string parameter"));
1581         goto cleanup;
1582     } else if (rv > 0) {
1583         if (virTypedParamsAddString(&params, &nparams, &maxparams,
1584                                     VIR_DOMAIN_BLKIO_DEVICE_WEIGHT,
1585                                     device_weight) < 0)
1586             goto save_error;
1587     }
1588 
1589     rv = vshCommandOptStringQuiet(ctl, cmd, "device-read-iops-sec", &device_riops);
1590     if (rv < 0) {
1591         vshError(ctl, "%s", _("Unable to parse string parameter"));
1592         goto cleanup;
1593     } else if (rv > 0) {
1594         if (virTypedParamsAddString(&params, &nparams, &maxparams,
1595                                     VIR_DOMAIN_BLKIO_DEVICE_READ_IOPS,
1596                                     device_riops) < 0)
1597             goto save_error;
1598     }
1599 
1600     rv = vshCommandOptStringQuiet(ctl, cmd, "device-write-iops-sec", &device_wiops);
1601     if (rv < 0) {
1602         vshError(ctl, "%s", _("Unable to parse string parameter"));
1603         goto cleanup;
1604     } else if (rv > 0) {
1605         if (virTypedParamsAddString(&params, &nparams, &maxparams,
1606                                     VIR_DOMAIN_BLKIO_DEVICE_WRITE_IOPS,
1607                                     device_wiops) < 0)
1608             goto save_error;
1609     }
1610 
1611     rv = vshCommandOptStringQuiet(ctl, cmd, "device-read-bytes-sec", &device_rbps);
1612     if (rv < 0) {
1613         vshError(ctl, "%s", _("Unable to parse string parameter"));
1614         goto cleanup;
1615     } else if (rv > 0) {
1616         if (virTypedParamsAddString(&params, &nparams, &maxparams,
1617                                     VIR_DOMAIN_BLKIO_DEVICE_READ_BPS,
1618                                     device_rbps) < 0)
1619             goto save_error;
1620     }
1621 
1622     rv = vshCommandOptStringQuiet(ctl, cmd, "device-write-bytes-sec", &device_wbps);
1623     if (rv < 0) {
1624         vshError(ctl, "%s", _("Unable to parse string parameter"));
1625         goto cleanup;
1626     } else if (rv > 0) {
1627         if (virTypedParamsAddString(&params, &nparams, &maxparams,
1628                                    VIR_DOMAIN_BLKIO_DEVICE_WRITE_BPS,
1629                                    device_wbps) < 0)
1630             goto save_error;
1631     }
1632 
1633     if (nparams == 0) {
1634         /* get the number of blkio parameters */
1635         if (virDomainGetBlkioParameters(dom, NULL, &nparams, flags) != 0) {
1636             vshError(ctl, "%s",
1637                      _("Unable to get number of blkio parameters"));
1638             goto cleanup;
1639         }
1640 
1641         if (nparams == 0) {
1642             /* nothing to output */
1643             ret = true;
1644             goto cleanup;
1645         }
1646 
1647         /* now go get all the blkio parameters */
1648         params = g_new0(virTypedParameter, nparams);
1649         if (virDomainGetBlkioParameters(dom, params, &nparams, flags) != 0) {
1650             vshError(ctl, "%s", _("Unable to get blkio parameters"));
1651             goto cleanup;
1652         }
1653 
1654         for (i = 0; i < nparams; i++) {
1655             g_autofree char *str = vshGetTypedParamValue(ctl, &params[i]);
1656             vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
1657         }
1658     } else {
1659         /* set the blkio parameters */
1660         if (virDomainSetBlkioParameters(dom, params, nparams, flags) < 0)
1661             goto error;
1662     }
1663 
1664     ret = true;
1665 
1666  cleanup:
1667     virTypedParamsFree(params, nparams);
1668     return ret;
1669 
1670  save_error:
1671     vshSaveLibvirtError();
1672  error:
1673     vshError(ctl, "%s", _("Unable to change blkio parameters"));
1674     goto cleanup;
1675 }
1676 
1677 
1678 static void
virshPrintJobProgress(const char * label,unsigned long long remaining,unsigned long long total)1679 virshPrintJobProgress(const char *label, unsigned long long remaining,
1680                       unsigned long long total)
1681 {
1682     int progress;
1683 
1684     if (remaining == 0) {
1685         /* migration has completed */
1686         progress = 100;
1687     } else {
1688         /* use float to avoid overflow */
1689         progress = (int)(100.0 - remaining * 100.0 / total);
1690         if (progress >= 100) {
1691             /* migration has not completed, do not print [100 %] */
1692             progress = 99;
1693         }
1694     }
1695 
1696     /* see comments in vshError about why we must flush */
1697     fflush(stdout);
1698     fprintf(stderr, "\r%s: [%3d %%]", label, progress);
1699     fflush(stderr);
1700 }
1701 
1702 static volatile sig_atomic_t intCaught;
1703 
1704 #ifndef WIN32
virshCatchInt(int sig G_GNUC_UNUSED,siginfo_t * siginfo G_GNUC_UNUSED,void * context G_GNUC_UNUSED)1705 static void virshCatchInt(int sig G_GNUC_UNUSED,
1706                           siginfo_t *siginfo G_GNUC_UNUSED,
1707                           void *context G_GNUC_UNUSED)
1708 {
1709     intCaught = 1;
1710 }
1711 #endif /* !WIN32 */
1712 
1713 
1714 typedef struct _virshBlockJobWaitData virshBlockJobWaitData;
1715 struct _virshBlockJobWaitData {
1716     vshControl *ctl;
1717     virDomainPtr dom;
1718     const char *dev;
1719     const char *job_name;
1720 
1721     bool verbose;
1722     unsigned int timeout;
1723     bool async_abort;
1724 
1725     int cb_id;
1726     int cb_id2;
1727     int status;
1728 };
1729 
1730 
1731 static void
virshBlockJobStatusHandler(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom G_GNUC_UNUSED,const char * disk,int type G_GNUC_UNUSED,int status,void * opaque)1732 virshBlockJobStatusHandler(virConnectPtr conn G_GNUC_UNUSED,
1733                            virDomainPtr dom G_GNUC_UNUSED,
1734                            const char *disk,
1735                            int type G_GNUC_UNUSED,
1736                            int status,
1737                            void *opaque)
1738 {
1739     virshBlockJobWaitData *data = opaque;
1740 
1741     if (STREQ_NULLABLE(disk, data->dev))
1742         data->status = status;
1743 }
1744 
1745 
1746 /**
1747  * virshBlockJobWaitInit:
1748  * @ctl: vsh control structure
1749  * @dom: domain object
1750  * @dev: block device name to wait for
1751  * @job_name: block job name to display in user-facing messages
1752  * @verbose: enable progress reporting
1753  * @timeout: number of milliseconds to wait before aborting the job
1754  * @async_abort: abort the job asynchronously
1755  *
1756  * Prepares virsh for waiting for completion of a block job. This function
1757  * registers event handlers for block job events and prepares the data structures
1758  * for them. A call to virshBlockJobWait then waits for completion of the given
1759  * block job. This function should be tolerant to different versions of daemon
1760  * and the reporting capabilities of those.
1761  *
1762  * Returns the data structure that holds data needed for block job waiting or
1763  * NULL in case of error.
1764  */
1765 static virshBlockJobWaitData *
virshBlockJobWaitInit(vshControl * ctl,virDomainPtr dom,const char * dev,const char * job_name,bool verbose,unsigned int timeout,bool async_abort)1766 virshBlockJobWaitInit(vshControl *ctl,
1767                       virDomainPtr dom,
1768                       const char *dev,
1769                       const char *job_name,
1770                       bool verbose,
1771                       unsigned int timeout,
1772                       bool async_abort)
1773 {
1774     virConnectDomainEventGenericCallback cb;
1775     virshBlockJobWaitData *ret;
1776     virshControl *priv = ctl->privData;
1777 
1778     ret = g_new0(virshBlockJobWaitData, 1);
1779 
1780     ret->ctl = ctl;
1781     ret->dom = dom;
1782     ret->dev = dev;
1783     ret->job_name = job_name;
1784 
1785     ret->async_abort = async_abort;
1786     ret->timeout = timeout;
1787     ret->verbose = verbose;
1788 
1789     ret->status = -1;
1790 
1791     cb = VIR_DOMAIN_EVENT_CALLBACK(virshBlockJobStatusHandler);
1792 
1793     if ((ret->cb_id = virConnectDomainEventRegisterAny(priv->conn, dom,
1794                                                        VIR_DOMAIN_EVENT_ID_BLOCK_JOB,
1795                                                        cb, ret, NULL)) < 0)
1796         vshResetLibvirtError();
1797 
1798     if ((ret->cb_id2 = virConnectDomainEventRegisterAny(priv->conn, dom,
1799                                                         VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2,
1800                                                         cb, ret, NULL)) < 0)
1801         vshResetLibvirtError();
1802 
1803     return ret;
1804 }
1805 
1806 
1807 static void
virshBlockJobWaitFree(virshBlockJobWaitData * data)1808 virshBlockJobWaitFree(virshBlockJobWaitData *data)
1809 {
1810     virshControl *priv = NULL;
1811 
1812     if (!data)
1813         return;
1814 
1815     priv = data->ctl->privData;
1816     if (data->cb_id >= 0)
1817         virConnectDomainEventDeregisterAny(priv->conn, data->cb_id);
1818     if (data->cb_id2 >= 0)
1819         virConnectDomainEventDeregisterAny(priv->conn, data->cb_id2);
1820 
1821     g_free(data);
1822 }
1823 
1824 
1825 /**
1826  * virshBlockJobWait:
1827  * @data: private data initialized by virshBlockJobWaitInit
1828  *
1829  * Waits for the block job to complete. This function prefers to wait for a
1830  * matching VIR_DOMAIN_EVENT_ID_BLOCK_JOB or VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2
1831  * event from libvirt; however, it has a fallback mode should either of these
1832  * events not be available.
1833  *
1834  * This function returns values from the virConnectDomainEventBlockJobStatus
1835  * enum or -1 in case of an internal error.
1836  *
1837  * If the fallback mode is activated the returned event is
1838  * VIR_DOMAIN_BLOCK_JOB_COMPLETED if the block job vanishes or
1839  * VIR_DOMAIN_BLOCK_JOB_READY if the block job reaches 100%.
1840  */
1841 static int
virshBlockJobWait(virshBlockJobWaitData * data)1842 virshBlockJobWait(virshBlockJobWaitData *data)
1843 {
1844     /* For two phase jobs like active commit or block copy, the marker reaches
1845      * 100% and an event fires. In case where virsh would not be able to match
1846      * the event to the given block job we will wait for the number of retries
1847      * before claiming that we entered synchronised phase */
1848     unsigned int retries = 5;
1849 #ifndef WIN32
1850     struct sigaction sig_action;
1851     struct sigaction old_sig_action;
1852     sigset_t sigmask, oldsigmask;
1853 #endif /* !WIN32 */
1854     unsigned long long start = 0;
1855     unsigned long long curr = 0;
1856 
1857     unsigned int abort_flags = 0;
1858     int ret = -1;
1859     virDomainBlockJobInfo info, last;
1860     int result;
1861 
1862     if (!data)
1863         return 0;
1864 
1865     if (data->async_abort)
1866         abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
1867 
1868 #ifndef WIN32
1869     sigemptyset(&sigmask);
1870     sigaddset(&sigmask, SIGINT);
1871 
1872     intCaught = 0;
1873     sig_action.sa_sigaction = virshCatchInt;
1874     sig_action.sa_flags = SA_SIGINFO;
1875     sigemptyset(&sig_action.sa_mask);
1876     sigaction(SIGINT, &sig_action, &old_sig_action);
1877 #endif /* !WIN32 */
1878 
1879     if (data->timeout && virTimeMillisNow(&start) < 0) {
1880         vshSaveLibvirtError();
1881         goto cleanup;
1882     }
1883 
1884     last.cur = last.end = 0;
1885 
1886     while (true) {
1887 #ifndef WIN32
1888         pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask);
1889 #endif /* !WIN32 */
1890         result = virDomainGetBlockJobInfo(data->dom, data->dev, &info, 0);
1891 #ifndef WIN32
1892         pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
1893 #endif /* !WIN32 */
1894 
1895         if (result < 0) {
1896             vshError(data->ctl, _("failed to query job for disk %s"), data->dev);
1897             goto cleanup;
1898         }
1899 
1900         /* If either callback could be registered and we've got an event, we can
1901          * can end the waiting loop */
1902         if ((data->cb_id >= 0 || data->cb_id2 >= 0) && data->status != -1) {
1903             ret = data->status;
1904             break;
1905         }
1906 
1907         /* Fallback behaviour is only needed if one or both callbacks could not
1908          * be registered */
1909         if (data->cb_id < 0 || data->cb_id2 < 0) {
1910             /* If the block job vanishes, synthesize a COMPLETED event */
1911             if (result == 0) {
1912                 ret = VIR_DOMAIN_BLOCK_JOB_COMPLETED;
1913                 break;
1914             }
1915 
1916             /* If the block job hits 100%, wait a little while for a possible
1917              * event from libvirt unless both callbacks could not be registered
1918              * in order to synthesize our own READY event */
1919             if (info.end == info.cur &&
1920                 ((data->cb_id < 0 && data->cb_id2 < 0) || --retries == 0)) {
1921                 ret = VIR_DOMAIN_BLOCK_JOB_READY;
1922                 break;
1923             }
1924         }
1925 
1926         if (data->verbose && (info.cur != last.cur || info.end != last.end))
1927             virshPrintJobProgress(data->job_name, info.end - info.cur,
1928                                   info.end);
1929         last = info;
1930 
1931         if (data->timeout && virTimeMillisNow(&curr) < 0) {
1932             vshSaveLibvirtError();
1933             goto cleanup;
1934         }
1935 
1936         if (intCaught || (data->timeout && (curr - start > data->timeout))) {
1937             if (virDomainBlockJobAbort(data->dom, data->dev, abort_flags) < 0) {
1938                 vshError(data->ctl, _("failed to abort job for disk '%s'"),
1939                          data->dev);
1940                 goto cleanup;
1941             }
1942 
1943             ret = VIR_DOMAIN_BLOCK_JOB_CANCELED;
1944             break;
1945         }
1946 
1947         g_usleep(500 * 1000);
1948     }
1949 
1950     /* print 100% completed */
1951     if (data->verbose &&
1952         (ret == VIR_DOMAIN_BLOCK_JOB_COMPLETED ||
1953          ret == VIR_DOMAIN_BLOCK_JOB_READY))
1954         virshPrintJobProgress(data->job_name, 0, 1);
1955 
1956  cleanup:
1957 #ifndef WIN32
1958     sigaction(SIGINT, &old_sig_action, NULL);
1959 #endif /* !WIN32 */
1960     return ret;
1961 }
1962 
1963 
1964 /*
1965  * "blockcommit" command
1966  */
1967 static const vshCmdInfo info_blockcommit[] = {
1968     {.name = "help",
1969      .data = N_("Start a block commit operation.")
1970     },
1971     {.name = "desc",
1972      .data = N_("Commit changes from a snapshot down to its backing image.")
1973     },
1974     {.name = NULL}
1975 };
1976 
1977 static const vshCmdOptDef opts_blockcommit[] = {
1978     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
1979     {.name = "path",
1980      .type = VSH_OT_DATA,
1981      .flags = VSH_OFLAG_REQ,
1982      .completer = virshDomainDiskTargetCompleter,
1983      .help = N_("fully-qualified path of disk")
1984     },
1985     {.name = "bandwidth",
1986      .type = VSH_OT_INT,
1987      .help = N_("bandwidth limit in MiB/s")
1988     },
1989     {.name = "base",
1990      .type = VSH_OT_STRING,
1991      .completer = virshDomainBlockjobBaseTopCompleter,
1992      .help = N_("path of base file to commit into (default bottom of chain)")
1993     },
1994     {.name = "shallow",
1995      .type = VSH_OT_BOOL,
1996      .help = N_("use backing file of top as base")
1997     },
1998     {.name = "top",
1999      .type = VSH_OT_STRING,
2000      .completer = virshDomainBlockjobBaseTopCompleter,
2001      .help = N_("path of top file to commit from (default top of chain)")
2002     },
2003     {.name = "active",
2004      .type = VSH_OT_BOOL,
2005      .help = N_("trigger two-stage active commit of top file")
2006     },
2007     {.name = "delete",
2008      .type = VSH_OT_BOOL,
2009      .help = N_("delete files that were successfully committed")
2010     },
2011     {.name = "wait",
2012      .type = VSH_OT_BOOL,
2013      .help = N_("wait for job to complete "
2014                 "(with --active, wait for job to sync)")
2015     },
2016     {.name = "verbose",
2017      .type = VSH_OT_BOOL,
2018      .help = N_("with --wait, display the progress")
2019     },
2020     {.name = "timeout",
2021      .type = VSH_OT_INT,
2022      .help = N_("implies --wait, abort if copy exceeds timeout (in seconds)")
2023     },
2024     {.name = "pivot",
2025      .type = VSH_OT_BOOL,
2026      .help = N_("implies --active --wait, pivot when commit is synced")
2027     },
2028     {.name = "keep-overlay",
2029      .type = VSH_OT_BOOL,
2030      .help = N_("implies --active --wait, quit when commit is synced")
2031     },
2032     {.name = "async",
2033      .type = VSH_OT_BOOL,
2034      .help = N_("with --wait, don't wait for cancel to finish")
2035     },
2036     {.name = "keep-relative",
2037      .type = VSH_OT_BOOL,
2038      .help = N_("keep the backing chain relatively referenced")
2039     },
2040     {.name = "bytes",
2041      .type = VSH_OT_BOOL,
2042      .help = N_("the bandwidth limit is in bytes/s rather than MiB/s")
2043     },
2044     {.name = NULL}
2045 };
2046 
2047 static bool
cmdBlockcommit(vshControl * ctl,const vshCmd * cmd)2048 cmdBlockcommit(vshControl *ctl, const vshCmd *cmd)
2049 {
2050     g_autoptr(virshDomain) dom = NULL;
2051     bool ret = false;
2052     bool verbose = vshCommandOptBool(cmd, "verbose");
2053     bool pivot = vshCommandOptBool(cmd, "pivot");
2054     bool finish = vshCommandOptBool(cmd, "keep-overlay");
2055     bool active = vshCommandOptBool(cmd, "active") || pivot || finish;
2056     bool blocking = vshCommandOptBool(cmd, "wait") || pivot || finish;
2057     bool async = vshCommandOptBool(cmd, "async");
2058     bool bytes = vshCommandOptBool(cmd, "bytes");
2059     int timeout = 0;
2060     const char *path = NULL;
2061     const char *base = NULL;
2062     const char *top = NULL;
2063     int abort_flags = 0;
2064     unsigned int flags = 0;
2065     unsigned long bandwidth = 0;
2066     virshBlockJobWaitData *bjWait = NULL;
2067 
2068     VSH_EXCLUSIVE_OPTIONS("pivot", "keep-overlay");
2069 
2070     if (vshCommandOptStringReq(ctl, cmd, "path", &path) < 0)
2071         return false;
2072 
2073     if (vshCommandOptStringReq(ctl, cmd, "base", &base) < 0)
2074         return false;
2075 
2076     if (vshCommandOptStringReq(ctl, cmd, "top", &top) < 0)
2077         return false;
2078 
2079     if (vshBlockJobOptionBandwidth(ctl, cmd, bytes, &bandwidth) < 0)
2080         return false;
2081 
2082     if (bytes)
2083         flags |= VIR_DOMAIN_BLOCK_COMMIT_BANDWIDTH_BYTES;
2084 
2085     if (vshCommandOptBool(cmd, "shallow"))
2086         flags |= VIR_DOMAIN_BLOCK_COMMIT_SHALLOW;
2087 
2088     if (vshCommandOptBool(cmd, "delete"))
2089         flags |= VIR_DOMAIN_BLOCK_COMMIT_DELETE;
2090 
2091     if (active)
2092         flags |= VIR_DOMAIN_BLOCK_COMMIT_ACTIVE;
2093 
2094    if (vshCommandOptBool(cmd, "keep-relative"))
2095         flags |= VIR_DOMAIN_BLOCK_COMMIT_RELATIVE;
2096 
2097     if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
2098         return false;
2099 
2100     if (timeout)
2101         blocking = true;
2102 
2103     if (!blocking) {
2104         if (verbose) {
2105             vshError(ctl, "%s", _("--verbose requires at least one of --timeout, "
2106                                   "--wait, --pivot, or --keep-overlay"));
2107             return false;
2108         }
2109 
2110         if (async) {
2111             vshError(ctl, "%s", _("--async requires at least one of --timeout, "
2112                                   "--wait, --pivot, or --keep-overlay"));
2113             return false;
2114         }
2115     }
2116 
2117     if (async)
2118         abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
2119 
2120     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
2121         return false;
2122 
2123     if (blocking &&
2124         !(bjWait = virshBlockJobWaitInit(ctl, dom, path, _("Block commit"),
2125                                          verbose, timeout, async)))
2126         goto cleanup;
2127 
2128     if (virDomainBlockCommit(dom, path, base, top, bandwidth, flags) < 0)
2129         goto cleanup;
2130 
2131     if (!blocking) {
2132         if (active)
2133             vshPrintExtra(ctl, "%s", _("Active Block Commit started"));
2134         else
2135             vshPrintExtra(ctl, "%s", _("Block Commit started"));
2136 
2137         ret = true;
2138         goto cleanup;
2139     }
2140 
2141     /* Execution continues here only if --wait or friends were specified */
2142     switch (virshBlockJobWait(bjWait)) {
2143         case -1:
2144             goto cleanup;
2145 
2146         case VIR_DOMAIN_BLOCK_JOB_CANCELED:
2147             vshPrintExtra(ctl, "\n%s", _("Commit aborted"));
2148             goto cleanup;
2149             break;
2150 
2151         case VIR_DOMAIN_BLOCK_JOB_FAILED:
2152             vshPrintExtra(ctl, "\n%s", _("Commit failed"));
2153             goto cleanup;
2154             break;
2155 
2156         case VIR_DOMAIN_BLOCK_JOB_READY:
2157         case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
2158             break;
2159     }
2160 
2161     if (active) {
2162         if (pivot) {
2163             abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT;
2164             if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
2165                 vshError(ctl, _("failed to pivot job for disk %s"), path);
2166                 goto cleanup;
2167             }
2168 
2169             vshPrintExtra(ctl, "\n%s", _("Successfully pivoted"));
2170         } else if (finish) {
2171             if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
2172                 vshError(ctl, _("failed to finish job for disk %s"), path);
2173                 goto cleanup;
2174             }
2175 
2176             vshPrintExtra(ctl, "\n%s", _("Commit complete, overlay "
2177                                          "image kept"));
2178         } else {
2179             vshPrintExtra(ctl, "\n%s", _("Now in synchronized phase"));
2180         }
2181     } else {
2182         vshPrintExtra(ctl, "\n%s", _("Commit complete"));
2183     }
2184 
2185     ret = true;
2186  cleanup:
2187     virshBlockJobWaitFree(bjWait);
2188     return ret;
2189 }
2190 
2191 /*
2192  * "blockcopy" command
2193  */
2194 static const vshCmdInfo info_blockcopy[] = {
2195     {.name = "help",
2196      .data = N_("Start a block copy operation.")
2197     },
2198     {.name = "desc",
2199      .data = N_("Copy a disk backing image chain to dest.")
2200     },
2201     {.name = NULL}
2202 };
2203 
2204 static const vshCmdOptDef opts_blockcopy[] = {
2205     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
2206     {.name = "path",
2207      .type = VSH_OT_DATA,
2208      .flags = VSH_OFLAG_REQ,
2209      .completer = virshDomainDiskTargetCompleter,
2210      .help = N_("fully-qualified path of source disk")
2211     },
2212     {.name = "dest",
2213      .type = VSH_OT_STRING,
2214      .help = N_("path of the copy to create")
2215     },
2216     {.name = "bandwidth",
2217      .type = VSH_OT_INT,
2218      .help = N_("bandwidth limit in MiB/s")
2219     },
2220     {.name = "shallow",
2221      .type = VSH_OT_BOOL,
2222      .help = N_("make the copy share a backing chain")
2223     },
2224     {.name = "reuse-external",
2225      .type = VSH_OT_BOOL,
2226      .help = N_("reuse existing destination")
2227     },
2228     {.name = "raw",
2229      .type = VSH_OT_ALIAS,
2230      .help = "format=raw"
2231     },
2232     {.name = "blockdev",
2233      .type = VSH_OT_BOOL,
2234      .help = N_("copy destination is block device instead of regular file")
2235     },
2236     {.name = "wait",
2237      .type = VSH_OT_BOOL,
2238      .help = N_("wait for job to reach mirroring phase")
2239     },
2240     {.name = "verbose",
2241      .type = VSH_OT_BOOL,
2242      .help = N_("with --wait, display the progress")
2243     },
2244     {.name = "timeout",
2245      .type = VSH_OT_INT,
2246      .help = N_("implies --wait, abort if copy exceeds timeout (in seconds)")
2247     },
2248     {.name = "pivot",
2249      .type = VSH_OT_BOOL,
2250      .help = N_("implies --wait, pivot when mirroring starts")
2251     },
2252     {.name = "finish",
2253      .type = VSH_OT_BOOL,
2254      .help = N_("implies --wait, quit when mirroring starts")
2255     },
2256     {.name = "async",
2257      .type = VSH_OT_BOOL,
2258      .help = N_("with --wait, don't wait for cancel to finish")
2259     },
2260     {.name = "xml",
2261      .type = VSH_OT_STRING,
2262      .completer = virshCompletePathLocalExisting,
2263      .help = N_("filename containing XML description of the copy destination")
2264     },
2265     {.name = "format",
2266      .type = VSH_OT_STRING,
2267      .flags = VSH_OFLAG_NONE,
2268      .completer = virshDomainStorageFileFormatCompleter,
2269      .help = N_("format of the destination file")
2270     },
2271     {.name = "granularity",
2272      .type = VSH_OT_INT,
2273      .help = N_("power-of-two granularity to use during the copy")
2274     },
2275     {.name = "buf-size",
2276      .type = VSH_OT_INT,
2277      .help = N_("maximum amount of in-flight data during the copy")
2278     },
2279     {.name = "bytes",
2280      .type = VSH_OT_BOOL,
2281      .help = N_("the bandwidth limit is in bytes/s rather than MiB/s")
2282     },
2283     {.name = "transient-job",
2284      .type = VSH_OT_BOOL,
2285      .help = N_("the copy job is not persisted if VM is turned off")
2286     },
2287     {.name = NULL}
2288 };
2289 
2290 static bool
cmdBlockcopy(vshControl * ctl,const vshCmd * cmd)2291 cmdBlockcopy(vshControl *ctl, const vshCmd *cmd)
2292 {
2293     g_autoptr(virshDomain) dom = NULL;
2294     const char *dest = NULL;
2295     const char *format = NULL;
2296     unsigned long bandwidth = 0;
2297     unsigned int granularity = 0;
2298     unsigned long long buf_size = 0;
2299     unsigned int flags = 0;
2300     bool ret = false;
2301     bool verbose = vshCommandOptBool(cmd, "verbose");
2302     bool pivot = vshCommandOptBool(cmd, "pivot");
2303     bool finish = vshCommandOptBool(cmd, "finish");
2304     bool blockdev = vshCommandOptBool(cmd, "blockdev");
2305     bool blocking = vshCommandOptBool(cmd, "wait") || finish || pivot;
2306     bool async = vshCommandOptBool(cmd, "async");
2307     bool bytes = vshCommandOptBool(cmd, "bytes");
2308     bool transientjob = vshCommandOptBool(cmd, "transient-job");
2309     int timeout = 0;
2310     const char *path = NULL;
2311     int abort_flags = 0;
2312     const char *xml = NULL;
2313     char *xmlstr = NULL;
2314     virTypedParameterPtr params = NULL;
2315     virshBlockJobWaitData *bjWait = NULL;
2316     int nparams = 0;
2317 
2318     if (vshCommandOptStringReq(ctl, cmd, "path", &path) < 0)
2319         return false;
2320     if (vshCommandOptStringReq(ctl, cmd, "dest", &dest) < 0)
2321         return false;
2322     if (vshCommandOptStringReq(ctl, cmd, "xml", &xml) < 0)
2323         return false;
2324     if (vshCommandOptStringReq(ctl, cmd, "format", &format) < 0)
2325         return false;
2326     if (vshBlockJobOptionBandwidth(ctl, cmd, bytes, &bandwidth) < 0)
2327         return false;
2328     if (vshCommandOptUInt(ctl, cmd, "granularity", &granularity) < 0)
2329         return false;
2330     if (vshCommandOptULongLong(ctl, cmd, "buf-size", &buf_size) < 0)
2331         return false;
2332     /* Exploit that some VIR_DOMAIN_BLOCK_REBASE_* and
2333      * VIR_DOMAIN_BLOCK_COPY_* flags have the same values.  */
2334     if (vshCommandOptBool(cmd, "shallow"))
2335         flags |= VIR_DOMAIN_BLOCK_REBASE_SHALLOW;
2336     if (vshCommandOptBool(cmd, "reuse-external"))
2337         flags |= VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT;
2338     if (transientjob)
2339         flags |= VIR_DOMAIN_BLOCK_COPY_TRANSIENT_JOB;
2340     if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
2341         return false;
2342 
2343     if (timeout)
2344         blocking = true;
2345 
2346     if (async)
2347         abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
2348 
2349     VSH_EXCLUSIVE_OPTIONS_VAR(dest, xml);
2350     VSH_EXCLUSIVE_OPTIONS_VAR(format, xml);
2351     VSH_EXCLUSIVE_OPTIONS_VAR(blockdev, xml);
2352     VSH_EXCLUSIVE_OPTIONS_VAR(pivot, finish);
2353 
2354     if (!dest && !xml) {
2355         vshError(ctl, "%s", _("need either --dest or --xml"));
2356         return false;
2357     }
2358 
2359     if (!blocking) {
2360         if (verbose) {
2361             vshError(ctl, "%s", _("--verbose requires at least one of --timeout, "
2362                                   "--wait, --pivot, or --finish"));
2363             return false;
2364         }
2365 
2366         if (async) {
2367             vshError(ctl, "%s", _("--async requires at least one of --timeout, "
2368                                   "--wait, --pivot, or --finish"));
2369             return false;
2370         }
2371     }
2372 
2373     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
2374         goto cleanup;
2375 
2376     if (blocking &&
2377         !(bjWait = virshBlockJobWaitInit(ctl, dom, path, _("Block Copy"),
2378                                          verbose, timeout, async)))
2379         goto cleanup;
2380 
2381     if (xml) {
2382         if (virFileReadAll(xml, VSH_MAX_XML_FILE, &xmlstr) < 0) {
2383             vshReportError(ctl);
2384             goto cleanup;
2385         }
2386     }
2387 
2388     if (granularity || buf_size || (format && STRNEQ(format, "raw")) || xml ||
2389         transientjob) {
2390         /* New API */
2391         if (bandwidth || granularity || buf_size) {
2392             params = g_new0(virTypedParameter, 3);
2393             if (bandwidth) {
2394                 if (!bytes) {
2395                     /* bandwidth is ulong MiB/s, but the typed parameter is
2396                      * ullong bytes/s; make sure we don't overflow */
2397                     unsigned long long limit = MIN(ULONG_MAX, ULLONG_MAX >> 20);
2398                     if (bandwidth > limit) {
2399                         vshError(ctl, _("bandwidth must be less than %llu"), limit);
2400                         goto cleanup;
2401                     }
2402 
2403                     bandwidth <<= 20ULL;
2404                 }
2405                 if (virTypedParameterAssign(&params[nparams++],
2406                                             VIR_DOMAIN_BLOCK_COPY_BANDWIDTH,
2407                                             VIR_TYPED_PARAM_ULLONG,
2408                                             bandwidth) < 0)
2409                     goto cleanup;
2410             }
2411             if (granularity &&
2412                 virTypedParameterAssign(&params[nparams++],
2413                                         VIR_DOMAIN_BLOCK_COPY_GRANULARITY,
2414                                         VIR_TYPED_PARAM_UINT,
2415                                         granularity) < 0)
2416                 goto cleanup;
2417             if (buf_size &&
2418                 virTypedParameterAssign(&params[nparams++],
2419                                         VIR_DOMAIN_BLOCK_COPY_BUF_SIZE,
2420                                         VIR_TYPED_PARAM_ULLONG,
2421                                         buf_size) < 0)
2422                 goto cleanup;
2423         }
2424 
2425         if (!xmlstr) {
2426             g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
2427             virBufferAsprintf(&buf, "<disk type='%s'>\n",
2428                               blockdev ? "block" : "file");
2429             virBufferAdjustIndent(&buf, 2);
2430             virBufferAsprintf(&buf, "<source %s", blockdev ? "dev" : "file");
2431             virBufferEscapeString(&buf, "='%s'/>\n", dest);
2432             virBufferEscapeString(&buf, "<driver type='%s'/>\n", format);
2433             virBufferAdjustIndent(&buf, -2);
2434             virBufferAddLit(&buf, "</disk>\n");
2435             xmlstr = virBufferContentAndReset(&buf);
2436         }
2437 
2438         if (virDomainBlockCopy(dom, path, xmlstr, params, nparams, flags) < 0)
2439             goto cleanup;
2440     } else {
2441         /* Old API */
2442         flags |= VIR_DOMAIN_BLOCK_REBASE_COPY;
2443         if (blockdev)
2444             flags |= VIR_DOMAIN_BLOCK_REBASE_COPY_DEV;
2445         if (STREQ_NULLABLE(format, "raw"))
2446             flags |= VIR_DOMAIN_BLOCK_REBASE_COPY_RAW;
2447         if (bytes)
2448             flags |= VIR_DOMAIN_BLOCK_REBASE_BANDWIDTH_BYTES;
2449 
2450         if (virDomainBlockRebase(dom, path, dest, bandwidth, flags) < 0)
2451             goto cleanup;
2452     }
2453 
2454     if (!blocking) {
2455         vshPrintExtra(ctl, "%s", _("Block Copy started"));
2456         ret = true;
2457         goto cleanup;
2458     }
2459 
2460     /* Execution continues here only if --wait or friends were specified */
2461     switch (virshBlockJobWait(bjWait)) {
2462         case -1:
2463             goto cleanup;
2464 
2465         case VIR_DOMAIN_BLOCK_JOB_CANCELED:
2466             vshPrintExtra(ctl, "\n%s", _("Copy aborted"));
2467             goto cleanup;
2468             break;
2469 
2470         case VIR_DOMAIN_BLOCK_JOB_FAILED:
2471             vshPrintExtra(ctl, "\n%s", _("Copy failed"));
2472             goto cleanup;
2473             break;
2474 
2475         case VIR_DOMAIN_BLOCK_JOB_READY:
2476         case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
2477             break;
2478     }
2479 
2480     if (pivot) {
2481         abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT;
2482         if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
2483             vshError(ctl, _("failed to pivot job for disk %s"), path);
2484             goto cleanup;
2485         }
2486 
2487         vshPrintExtra(ctl, "\n%s", _("Successfully pivoted"));
2488     } else if (finish) {
2489         if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
2490             vshError(ctl, _("failed to finish job for disk %s"), path);
2491             goto cleanup;
2492         }
2493 
2494         vshPrintExtra(ctl, "\n%s", _("Successfully copied"));
2495     } else {
2496         vshPrintExtra(ctl, "\n%s", _("Now in mirroring phase"));
2497     }
2498 
2499     ret = true;
2500 
2501  cleanup:
2502     VIR_FREE(xmlstr);
2503     virTypedParamsFree(params, nparams);
2504     virshBlockJobWaitFree(bjWait);
2505     return ret;
2506 }
2507 
2508 /*
2509  * "blockjob" command
2510  */
2511 static const vshCmdInfo info_blockjob[] = {
2512     {.name = "help",
2513      .data = N_("Manage active block operations")
2514     },
2515     {.name = "desc",
2516      .data = N_("Query, adjust speed, or cancel active block operations.")
2517     },
2518     {.name = NULL}
2519 };
2520 
2521 static const vshCmdOptDef opts_blockjob[] = {
2522     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
2523     {.name = "path",
2524      .type = VSH_OT_DATA,
2525      .flags = VSH_OFLAG_REQ,
2526      .completer = virshDomainDiskTargetCompleter,
2527      .help = N_("fully-qualified path of disk")
2528     },
2529     {.name = "abort",
2530      .type = VSH_OT_BOOL,
2531      .help = N_("abort the active job on the specified disk")
2532     },
2533     {.name = "async",
2534      .type = VSH_OT_BOOL,
2535      .help = N_("implies --abort; request but don't wait for job end")
2536     },
2537     {.name = "pivot",
2538      .type = VSH_OT_BOOL,
2539      .help = N_("implies --abort; conclude and pivot a copy or commit job")
2540     },
2541     {.name = "info",
2542      .type = VSH_OT_BOOL,
2543      .help = N_("get active job information for the specified disk")
2544     },
2545     {.name = "bytes",
2546      .type = VSH_OT_BOOL,
2547      .help = N_("get/set bandwidth in bytes rather than MiB/s")
2548     },
2549     {.name = "raw",
2550      .type = VSH_OT_BOOL,
2551      .help = N_("implies --info; output details rather than human summary")
2552     },
2553     {.name = "bandwidth",
2554      .type = VSH_OT_INT,
2555      .help = N_("set the bandwidth limit in MiB/s")
2556     },
2557     {.name = NULL}
2558 };
2559 
2560 VIR_ENUM_DECL(virshDomainBlockJob);
2561 VIR_ENUM_IMPL(virshDomainBlockJob,
2562               VIR_DOMAIN_BLOCK_JOB_TYPE_LAST,
2563               N_("Unknown job"),
2564               N_("Block Pull"),
2565               N_("Block Copy"),
2566               N_("Block Commit"),
2567               N_("Active Block Commit"),
2568               N_("Backup"),
2569 );
2570 
2571 static const char *
virshDomainBlockJobToString(int type)2572 virshDomainBlockJobToString(int type)
2573 {
2574     const char *str = virshDomainBlockJobTypeToString(type);
2575     return str ? _(str) : _("Unknown job");
2576 }
2577 
2578 
2579 static bool
virshBlockJobInfo(vshControl * ctl,virDomainPtr dom,const char * path,bool raw,bool bytes)2580 virshBlockJobInfo(vshControl *ctl,
2581                   virDomainPtr dom,
2582                   const char *path,
2583                   bool raw,
2584                   bool bytes)
2585 {
2586     virDomainBlockJobInfo info;
2587     virshControl *priv = ctl->privData;
2588     unsigned long long speed;
2589     unsigned int flags = 0;
2590     int rc = -1;
2591 
2592     /* If bytes were requested, or if raw mode is not forcing a MiB/s
2593      * query and cache can't prove failure, then query bytes/sec.  */
2594     if (bytes || !(raw || priv->blockJobNoBytes)) {
2595         flags |= VIR_DOMAIN_BLOCK_JOB_INFO_BANDWIDTH_BYTES;
2596         rc = virDomainGetBlockJobInfo(dom, path, &info, flags);
2597         if (rc < 0) {
2598             /* Check for particular errors, let all the rest be fatal. */
2599             switch (last_error->code) {
2600             case VIR_ERR_INVALID_ARG:
2601                 priv->blockJobNoBytes = true;
2602                 G_GNUC_FALLTHROUGH;
2603             case VIR_ERR_OVERFLOW:
2604                 if (!bytes && !raw) {
2605                     /* try again with MiB/s, unless forcing bytes */
2606                     vshResetLibvirtError();
2607                     break;
2608                 }
2609                 G_GNUC_FALLTHROUGH;
2610             default:
2611                 return false;
2612             }
2613         }
2614         speed = info.bandwidth;
2615     }
2616     /* If we don't already have a query result, query for MiB/s */
2617     if (rc < 0) {
2618         flags &= ~VIR_DOMAIN_BLOCK_JOB_INFO_BANDWIDTH_BYTES;
2619         if ((rc = virDomainGetBlockJobInfo(dom, path, &info, flags)) < 0)
2620             return false;
2621         speed = info.bandwidth;
2622         /* Scale to bytes/s unless in raw mode */
2623         if (!raw) {
2624             speed <<= 20;
2625             if (speed >> 20 != info.bandwidth) {
2626                 vshError(ctl, _("overflow in converting %ld MiB/s to bytes\n"),
2627                          info.bandwidth);
2628                 return false;
2629             }
2630         }
2631     }
2632 
2633     if (rc == 0) {
2634         if (!raw)
2635             vshPrintExtra(ctl, _("No current block job for %s"), path);
2636         return true;
2637     }
2638 
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);
2651         }
2652         vshPrint(ctl, "\n");
2653     }
2654 
2655     return true;
2656 }
2657 
2658 
2659 static bool
virshBlockJobSetSpeed(vshControl * ctl,const vshCmd * cmd,virDomainPtr dom,const char * path,bool bytes)2660 virshBlockJobSetSpeed(vshControl *ctl,
2661                       const vshCmd *cmd,
2662                       virDomainPtr dom,
2663                       const char *path,
2664                       bool bytes)
2665 {
2666     unsigned long bandwidth;
2667     unsigned int flags = 0;
2668 
2669     if (bytes)
2670         flags |= VIR_DOMAIN_BLOCK_JOB_SPEED_BANDWIDTH_BYTES;
2671 
2672     if (vshBlockJobOptionBandwidth(ctl, cmd, bytes, &bandwidth) < 0)
2673         return false;
2674 
2675     if (virDomainBlockJobSetSpeed(dom, path, bandwidth, flags) < 0)
2676         return false;
2677 
2678     return true;
2679 }
2680 
2681 
2682 static bool
virshBlockJobAbort(virDomainPtr dom,const char * path,bool pivot,bool async)2683 virshBlockJobAbort(virDomainPtr dom,
2684                    const char *path,
2685                    bool pivot,
2686                    bool async)
2687 {
2688     unsigned int flags = 0;
2689 
2690     if (async)
2691         flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
2692     if (pivot)
2693         flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT;
2694 
2695     if (virDomainBlockJobAbort(dom, path, flags) < 0)
2696         return false;
2697 
2698     return true;
2699 }
2700 
2701 
2702 static bool
cmdBlockjob(vshControl * ctl,const vshCmd * cmd)2703 cmdBlockjob(vshControl *ctl, const vshCmd *cmd)
2704 {
2705     bool raw = vshCommandOptBool(cmd, "raw");
2706     bool bytes = vshCommandOptBool(cmd, "bytes");
2707     bool abortMode = vshCommandOptBool(cmd, "abort");
2708     bool pivot = vshCommandOptBool(cmd, "pivot");
2709     bool async = vshCommandOptBool(cmd, "async");
2710     bool info = vshCommandOptBool(cmd, "info");
2711     bool bandwidth = vshCommandOptBool(cmd, "bandwidth");
2712     g_autoptr(virshDomain) dom = NULL;
2713     const char *path;
2714 
2715     VSH_EXCLUSIVE_OPTIONS("raw", "abort");
2716     VSH_EXCLUSIVE_OPTIONS_VAR(raw, pivot);
2717     VSH_EXCLUSIVE_OPTIONS_VAR(raw, async);
2718     VSH_EXCLUSIVE_OPTIONS_VAR(raw, bandwidth);
2719 
2720     VSH_EXCLUSIVE_OPTIONS("info", "abort");
2721     VSH_EXCLUSIVE_OPTIONS_VAR(info, pivot);
2722     VSH_EXCLUSIVE_OPTIONS_VAR(info, async);
2723     VSH_EXCLUSIVE_OPTIONS_VAR(info, bandwidth);
2724 
2725     VSH_EXCLUSIVE_OPTIONS("bytes", "abort");
2726     VSH_EXCLUSIVE_OPTIONS_VAR(bytes, pivot);
2727     VSH_EXCLUSIVE_OPTIONS_VAR(bytes, async);
2728 
2729     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
2730         return false;
2731 
2732     /* XXX Allow path to be optional to list info on all devices at once */
2733     if (vshCommandOptStringReq(ctl, cmd, "path", &path) < 0)
2734         return false;
2735 
2736     if (bandwidth)
2737         return virshBlockJobSetSpeed(ctl, cmd, dom, path, bytes);
2738     if (abortMode || pivot || async)
2739         return virshBlockJobAbort(dom, path, pivot, async);
2740     return virshBlockJobInfo(ctl, dom, path, raw, bytes);
2741 }
2742 
2743 /*
2744  * "blockpull" command
2745  */
2746 static const vshCmdInfo info_blockpull[] = {
2747     {.name = "help",
2748      .data = N_("Populate a disk from its backing image.")
2749     },
2750     {.name = "desc",
2751      .data = N_("Populate a disk from its backing image.")
2752     },
2753     {.name = NULL}
2754 };
2755 
2756 static const vshCmdOptDef opts_blockpull[] = {
2757     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
2758     {.name = "path",
2759      .type = VSH_OT_DATA,
2760      .flags = VSH_OFLAG_REQ,
2761      .completer = virshDomainDiskTargetCompleter,
2762      .help = N_("fully-qualified path of disk")
2763     },
2764     {.name = "bandwidth",
2765      .type = VSH_OT_INT,
2766      .help = N_("bandwidth limit in MiB/s")
2767     },
2768     {.name = "base",
2769      .type = VSH_OT_STRING,
2770      .completer = virshDomainBlockjobBaseTopCompleter,
2771      .help = N_("path of backing file in chain for a partial pull")
2772     },
2773     {.name = "wait",
2774      .type = VSH_OT_BOOL,
2775      .help = N_("wait for job to finish")
2776     },
2777     {.name = "verbose",
2778      .type = VSH_OT_BOOL,
2779      .help = N_("with --wait, display the progress")
2780     },
2781     {.name = "timeout",
2782      .type = VSH_OT_INT,
2783      .help = N_("with --wait, abort if pull exceeds timeout (in seconds)")
2784     },
2785     {.name = "async",
2786      .type = VSH_OT_BOOL,
2787      .help = N_("with --wait, don't wait for cancel to finish")
2788     },
2789     {.name = "keep-relative",
2790      .type = VSH_OT_BOOL,
2791      .help = N_("keep the backing chain relatively referenced")
2792     },
2793     {.name = "bytes",
2794      .type = VSH_OT_BOOL,
2795      .help = N_("the bandwidth limit is in bytes/s rather than MiB/s")
2796     },
2797     {.name = NULL}
2798 };
2799 
2800 static bool
cmdBlockpull(vshControl * ctl,const vshCmd * cmd)2801 cmdBlockpull(vshControl *ctl, const vshCmd *cmd)
2802 {
2803     g_autoptr(virshDomain) dom = NULL;
2804     bool ret = false;
2805     bool blocking = vshCommandOptBool(cmd, "wait");
2806     bool verbose = vshCommandOptBool(cmd, "verbose");
2807     bool async = vshCommandOptBool(cmd, "async");
2808     bool bytes = vshCommandOptBool(cmd, "bytes");
2809     int timeout = 0;
2810     const char *path = NULL;
2811     const char *base = NULL;
2812     unsigned long bandwidth = 0;
2813     unsigned int flags = 0;
2814     virshBlockJobWaitData *bjWait = NULL;
2815 
2816     VSH_REQUIRE_OPTION("verbose", "wait");
2817     VSH_REQUIRE_OPTION("async", "wait");
2818 
2819     if (vshCommandOptStringReq(ctl, cmd, "path", &path) < 0)
2820         return false;
2821 
2822     if (vshCommandOptStringReq(ctl, cmd, "base", &base) < 0)
2823         return false;
2824 
2825     if (vshBlockJobOptionBandwidth(ctl, cmd, bytes, &bandwidth) < 0)
2826         return false;
2827 
2828     if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
2829         return false;
2830 
2831     if (vshCommandOptBool(cmd, "keep-relative"))
2832         flags |= VIR_DOMAIN_BLOCK_REBASE_RELATIVE;
2833 
2834     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
2835         return false;
2836 
2837     if (blocking &&
2838         !(bjWait = virshBlockJobWaitInit(ctl, dom, path, _("Block Pull"),
2839                                          verbose, timeout, async)))
2840         goto cleanup;
2841 
2842     if (base || flags) {
2843         if (bytes)
2844             flags |= VIR_DOMAIN_BLOCK_REBASE_BANDWIDTH_BYTES;
2845 
2846         if (virDomainBlockRebase(dom, path, base, bandwidth, flags) < 0)
2847             goto cleanup;
2848     } else {
2849         if (bytes)
2850             flags |= VIR_DOMAIN_BLOCK_PULL_BANDWIDTH_BYTES;
2851 
2852         if (virDomainBlockPull(dom, path, bandwidth, flags) < 0)
2853             goto cleanup;
2854     }
2855 
2856     if (!blocking) {
2857         vshPrintExtra(ctl, "%s", _("Block Pull started"));
2858         ret = true;
2859         goto cleanup;
2860     }
2861 
2862     /* Execution continues here only if --wait or friends were specified */
2863     switch (virshBlockJobWait(bjWait)) {
2864         case -1:
2865             goto cleanup;
2866 
2867         case VIR_DOMAIN_BLOCK_JOB_CANCELED:
2868             vshPrintExtra(ctl, "\n%s", _("Pull aborted"));
2869             goto cleanup;
2870             break;
2871 
2872         case VIR_DOMAIN_BLOCK_JOB_FAILED:
2873             vshPrintExtra(ctl, "\n%s", _("Pull failed"));
2874             goto cleanup;
2875             break;
2876 
2877         case VIR_DOMAIN_BLOCK_JOB_READY:
2878         case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
2879             vshPrintExtra(ctl, "\n%s", _("Pull complete"));
2880             break;
2881     }
2882 
2883     ret = true;
2884 
2885  cleanup:
2886     virshBlockJobWaitFree(bjWait);
2887     return ret;
2888 }
2889 
2890 /*
2891  * "blockresize" command
2892  */
2893 static const vshCmdInfo info_blockresize[] = {
2894     {.name = "help",
2895      .data = N_("Resize block device of domain.")
2896     },
2897     {.name = "desc",
2898      .data = N_("Resize block device of domain.")
2899     },
2900     {.name = NULL}
2901 };
2902 
2903 static const vshCmdOptDef opts_blockresize[] = {
2904     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
2905     {.name = "path",
2906      .type = VSH_OT_DATA,
2907      .flags = VSH_OFLAG_REQ,
2908      .completer = virshDomainDiskTargetCompleter,
2909      .help = N_("Fully-qualified path of block device")
2910     },
2911     {.name = "size",
2912      .type = VSH_OT_INT,
2913      .flags = VSH_OFLAG_REQ,
2914      .help = N_("New size of the block device, as scaled integer (default KiB)")
2915     },
2916     {.name = NULL}
2917 };
2918 
2919 static bool
cmdBlockresize(vshControl * ctl,const vshCmd * cmd)2920 cmdBlockresize(vshControl *ctl, const vshCmd *cmd)
2921 {
2922     g_autoptr(virshDomain) dom = NULL;
2923     const char *path = NULL;
2924     unsigned long long size = 0;
2925     unsigned int flags = 0;
2926 
2927     if (vshCommandOptStringReq(ctl, cmd, "path", (const char **) &path) < 0)
2928         return false;
2929 
2930     if (vshCommandOptScaledInt(ctl, cmd, "size", &size, 1024, ULLONG_MAX) < 0)
2931         return false;
2932 
2933     /* Prefer the older interface of KiB.  */
2934     if (size % 1024 == 0)
2935         size /= 1024;
2936     else
2937         flags |= VIR_DOMAIN_BLOCK_RESIZE_BYTES;
2938 
2939     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
2940         return false;
2941 
2942     if (virDomainBlockResize(dom, path, size, flags) < 0) {
2943         vshError(ctl, _("Failed to resize block device '%s'"), path);
2944         return false;
2945     }
2946 
2947     vshPrintExtra(ctl, _("Block device '%s' is resized"), path);
2948     return true;
2949 }
2950 
2951 #ifndef WIN32
2952 /*
2953  * "console" command
2954  */
2955 static const vshCmdInfo info_console[] = {
2956     {.name = "help",
2957      .data = N_("connect to the guest console")
2958     },
2959     {.name = "desc",
2960      .data = N_("Connect the virtual serial console for the guest")
2961     },
2962     {.name = NULL}
2963 };
2964 
2965 static const vshCmdOptDef opts_console[] = {
2966     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
2967     {.name = "devname", /* sc_prohibit_devname */
2968      .type = VSH_OT_STRING,
2969      .completer = virshDomainConsoleCompleter,
2970      .help = N_("character device name")
2971     },
2972     {.name = "force",
2973      .type = VSH_OT_BOOL,
2974      .help =  N_("force console connection (disconnect already connected sessions)")
2975     },
2976     {.name = "safe",
2977      .type = VSH_OT_BOOL,
2978      .help =  N_("only connect if safe console handling is supported")
2979     },
2980     {.name = NULL}
2981 };
2982 
2983 static bool
cmdRunConsole(vshControl * ctl,virDomainPtr dom,const char * name,unsigned int flags)2984 cmdRunConsole(vshControl *ctl, virDomainPtr dom,
2985               const char *name,
2986               unsigned int flags)
2987 {
2988     int state;
2989     virshControl *priv = ctl->privData;
2990 
2991     if ((state = virshDomainState(ctl, dom, NULL)) < 0) {
2992         vshError(ctl, "%s", _("Unable to get domain status"));
2993         return false;
2994     }
2995 
2996     if (state == VIR_DOMAIN_SHUTOFF) {
2997         vshError(ctl, "%s", _("The domain is not running"));
2998         return false;
2999     }
3000 
3001     if (!isatty(STDIN_FILENO)) {
3002         vshError(ctl, "%s", _("Cannot run interactive console without a controlling TTY"));
3003         return false;
3004     }
3005 
3006     vshPrintExtra(ctl, _("Connected to domain '%s'\n"), virDomainGetName(dom));
3007     vshPrintExtra(ctl, _("Escape character is %s"), priv->escapeChar);
3008     if (priv->escapeChar[0] == '^')
3009         vshPrintExtra(ctl, " (Ctrl + %c)", priv->escapeChar[1]);
3010     vshPrintExtra(ctl, "\n");
3011     fflush(stdout);
3012     if (virshRunConsole(ctl, dom, name, flags) == 0)
3013         return true;
3014 
3015     return false;
3016 }
3017 
3018 static bool
cmdConsole(vshControl * ctl,const vshCmd * cmd)3019 cmdConsole(vshControl *ctl, const vshCmd *cmd)
3020 {
3021     g_autoptr(virshDomain) dom = NULL;
3022     bool force = vshCommandOptBool(cmd, "force");
3023     bool safe = vshCommandOptBool(cmd, "safe");
3024     unsigned int flags = 0;
3025     const char *name = NULL;
3026 
3027     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
3028         return false;
3029 
3030     if (vshCommandOptStringReq(ctl, cmd, "devname", &name) < 0) /* sc_prohibit_devname */
3031         return false;
3032 
3033     if (force)
3034         flags |= VIR_DOMAIN_CONSOLE_FORCE;
3035     if (safe)
3036         flags |= VIR_DOMAIN_CONSOLE_SAFE;
3037 
3038     return cmdRunConsole(ctl, dom, name, flags);
3039 }
3040 #endif /* WIN32 */
3041 
3042 /* "domif-setlink" command
3043  */
3044 static const vshCmdInfo info_domif_setlink[] = {
3045     {.name = "help",
3046      .data = N_("set link state of a virtual interface")
3047     },
3048     {.name = "desc",
3049      .data = N_("Set link state of a domain's virtual interface. This command "
3050                 "wraps usage of update-device command.")
3051     },
3052     {.name = NULL}
3053 };
3054 
3055 static const vshCmdOptDef opts_domif_setlink[] = {
3056     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
3057     {.name = "interface",
3058      .type = VSH_OT_DATA,
3059      .flags = VSH_OFLAG_REQ,
3060      .completer = virshDomainInterfaceCompleter,
3061      .help = N_("interface device (MAC Address)")
3062     },
3063     {.name = "state",
3064      .type = VSH_OT_DATA,
3065      .flags = VSH_OFLAG_REQ,
3066      .completer = virshDomainInterfaceStateCompleter,
3067      .help = N_("new state of the device")
3068     },
3069     {.name = "persistent",
3070      .type = VSH_OT_ALIAS,
3071      .help = "config"
3072     },
3073     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
3074     {.name = NULL}
3075 };
3076 
3077 static bool
cmdDomIfSetLink(vshControl * ctl,const vshCmd * cmd)3078 cmdDomIfSetLink(vshControl *ctl, const vshCmd *cmd)
3079 {
3080     g_autoptr(virshDomain) dom = NULL;
3081     const char *iface;
3082     const char *state;
3083     virMacAddr macaddr;
3084     const char *element;
3085     const char *attr;
3086     bool config;
3087     unsigned int flags = 0;
3088     unsigned int xmlflags = 0;
3089     size_t i;
3090     g_autoptr(xmlDoc) xml = NULL;
3091     g_autoptr(xmlXPathContext) ctxt = NULL;
3092     g_autoptr(xmlXPathObject) obj = NULL;
3093     xmlNodePtr cur = NULL;
3094     g_autofree char *xml_buf = NULL;
3095 
3096     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
3097         return false;
3098 
3099     if (vshCommandOptStringReq(ctl, cmd, "interface", &iface) < 0 ||
3100         vshCommandOptStringReq(ctl, cmd, "state", &state) < 0)
3101         return false;
3102 
3103     config = vshCommandOptBool(cmd, "config");
3104 
3105     if (STRNEQ(state, "up") && STRNEQ(state, "down")) {
3106         vshError(ctl, _("invalid link state '%s'"), state);
3107         return false;
3108     }
3109 
3110     if (config) {
3111         flags = VIR_DOMAIN_AFFECT_CONFIG;
3112         xmlflags |= VIR_DOMAIN_XML_INACTIVE;
3113     } else {
3114         flags = VIR_DOMAIN_AFFECT_LIVE;
3115     }
3116 
3117     if (virDomainIsActive(dom) == 0)
3118         flags = VIR_DOMAIN_AFFECT_CONFIG;
3119 
3120     if (virshDomainGetXMLFromDom(ctl, dom, xmlflags, &xml, &ctxt) < 0)
3121         return false;
3122 
3123     obj = xmlXPathEval(BAD_CAST "/domain/devices/interface", ctxt);
3124     if (obj == NULL || obj->type != XPATH_NODESET ||
3125         obj->nodesetval == NULL || obj->nodesetval->nodeNr == 0) {
3126         vshError(ctl, _("Failed to extract interface information or no interfaces found"));
3127         return false;
3128     }
3129 
3130     if (virMacAddrParse(iface, &macaddr) == 0) {
3131         element = "mac";
3132         attr = "address";
3133     } else {
3134         element = "target";
3135         attr = "dev";
3136     }
3137 
3138     /* find interface with matching mac addr */
3139     for (i = 0; i < obj->nodesetval->nodeNr; i++) {
3140         cur = obj->nodesetval->nodeTab[i]->children;
3141 
3142         while (cur) {
3143             if (cur->type == XML_ELEMENT_NODE &&
3144                 virXMLNodeNameEqual(cur, element)) {
3145                 g_autofree char *value = virXMLPropString(cur, attr);
3146 
3147                 if (STRCASEEQ(value, iface))
3148                     goto hit;
3149             }
3150             cur = cur->next;
3151         }
3152     }
3153 
3154     vshError(ctl, _("interface (%s: %s) not found"), element, iface);
3155     return false;
3156 
3157  hit:
3158     /* find and modify/add link state node */
3159     /* try to find <link> element */
3160     cur = obj->nodesetval->nodeTab[i]->children;
3161 
3162     while (cur) {
3163         if (cur->type == XML_ELEMENT_NODE &&
3164             virXMLNodeNameEqual(cur, "link")) {
3165             /* found, just modify the property */
3166             xmlSetProp(cur, BAD_CAST "state", BAD_CAST state);
3167 
3168             break;
3169         }
3170         cur = cur->next;
3171     }
3172 
3173     if (!cur) {
3174         /* element <link> not found, add one */
3175         cur = xmlNewChild(obj->nodesetval->nodeTab[i],
3176                           NULL,
3177                           BAD_CAST "link",
3178                           NULL);
3179         if (!cur)
3180             return false;
3181 
3182         if (xmlNewProp(cur, BAD_CAST "state", BAD_CAST state) == NULL)
3183             return false;
3184     }
3185 
3186     if (!(xml_buf = virXMLNodeToString(xml, obj->nodesetval->nodeTab[i]))) {
3187         vshSaveLibvirtError();
3188         vshError(ctl, _("Failed to create XML"));
3189         return false;
3190     }
3191 
3192     if (virDomainUpdateDeviceFlags(dom, xml_buf, flags) < 0) {
3193         vshError(ctl, _("Failed to update interface link state"));
3194         return false;
3195     }
3196 
3197     vshPrintExtra(ctl, "%s", _("Device updated successfully\n"));
3198 
3199     return true;
3200 }
3201 
3202 /* "domiftune" command
3203  */
3204 static const vshCmdInfo info_domiftune[] = {
3205     {.name = "help",
3206      .data = N_("get/set parameters of a virtual interface")
3207     },
3208     {.name = "desc",
3209      .data = N_("Get/set parameters of a domain's virtual interface.")
3210     },
3211     {.name = NULL}
3212 };
3213 
3214 static const vshCmdOptDef opts_domiftune[] = {
3215     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
3216     {.name = "interface",
3217      .type = VSH_OT_DATA,
3218      .flags = VSH_OFLAG_REQ,
3219      .completer = virshDomainInterfaceCompleter,
3220      .help = N_("interface device (MAC Address)")
3221     },
3222     {.name = "inbound",
3223      .type = VSH_OT_STRING,
3224      .completer = virshCompleteEmpty,
3225      .help = N_("control domain's incoming traffics")
3226     },
3227     {.name = "outbound",
3228      .type = VSH_OT_STRING,
3229      .completer = virshCompleteEmpty,
3230      .help = N_("control domain's outgoing traffics")
3231     },
3232     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
3233     VIRSH_COMMON_OPT_DOMAIN_LIVE,
3234     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
3235     {.name = NULL}
3236 };
3237 
3238 static bool
cmdDomIftune(vshControl * ctl,const vshCmd * cmd)3239 cmdDomIftune(vshControl *ctl, const vshCmd *cmd)
3240 {
3241     g_autoptr(virshDomain) dom = NULL;
3242     const char *name = NULL, *device = NULL,
3243                *inboundStr = NULL, *outboundStr = NULL;
3244     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
3245     int nparams = 0;
3246     int maxparams = 0;
3247     virTypedParameterPtr params = NULL;
3248     bool ret = false;
3249     bool current = vshCommandOptBool(cmd, "current");
3250     bool config = vshCommandOptBool(cmd, "config");
3251     bool live = vshCommandOptBool(cmd, "live");
3252     virNetDevBandwidthRate inbound, outbound;
3253     size_t i;
3254 
3255     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
3256     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
3257 
3258     if (config)
3259         flags |= VIR_DOMAIN_AFFECT_CONFIG;
3260     if (live)
3261         flags |= VIR_DOMAIN_AFFECT_LIVE;
3262 
3263     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
3264         return false;
3265 
3266     if (vshCommandOptStringReq(ctl, cmd, "interface", &device) < 0)
3267         goto cleanup;
3268 
3269     if (vshCommandOptStringReq(ctl, cmd, "inbound", &inboundStr) < 0 ||
3270         vshCommandOptStringReq(ctl, cmd, "outbound", &outboundStr) < 0)
3271         goto cleanup;
3272 
3273     memset(&inbound, 0, sizeof(inbound));
3274     memset(&outbound, 0, sizeof(outbound));
3275 
3276     if (inboundStr) {
3277         if (virshParseRateStr(ctl, inboundStr, &inbound) < 0)
3278             goto cleanup;
3279         /* we parse the rate as unsigned long long, but the API
3280          * only accepts UINT */
3281         if (inbound.average > UINT_MAX || inbound.peak > UINT_MAX ||
3282             inbound.burst > UINT_MAX) {
3283             vshError(ctl, _("inbound rate larger than maximum %u"),
3284                      UINT_MAX);
3285             goto cleanup;
3286         }
3287 
3288         if ((!inbound.average && (inbound.burst || inbound.peak)) &&
3289             !inbound.floor) {
3290             vshError(ctl, _("either inbound average or floor is mandatory"));
3291             goto cleanup;
3292         }
3293 
3294         if (virTypedParamsAddUInt(&params, &nparams, &maxparams,
3295                                   VIR_DOMAIN_BANDWIDTH_IN_AVERAGE,
3296                                   inbound.average) < 0)
3297             goto save_error;
3298 
3299         if (inbound.peak &&
3300             virTypedParamsAddUInt(&params, &nparams, &maxparams,
3301                                   VIR_DOMAIN_BANDWIDTH_IN_PEAK,
3302                                   inbound.peak) < 0)
3303             goto save_error;
3304 
3305         if (inbound.burst &&
3306             virTypedParamsAddUInt(&params, &nparams, &maxparams,
3307                                   VIR_DOMAIN_BANDWIDTH_IN_BURST,
3308                                   inbound.burst) < 0)
3309             goto save_error;
3310 
3311         if (inbound.floor &&
3312             virTypedParamsAddUInt(&params, &nparams, &maxparams,
3313                                   VIR_DOMAIN_BANDWIDTH_IN_FLOOR,
3314                                   inbound.floor) < 0)
3315             goto save_error;
3316     }
3317 
3318     if (outboundStr) {
3319         if (virshParseRateStr(ctl, outboundStr, &outbound) < 0)
3320             goto cleanup;
3321         if (outbound.average > UINT_MAX || outbound.peak > UINT_MAX ||
3322             outbound.burst > UINT_MAX) {
3323             vshError(ctl, _("outbound rate larger than maximum %u"),
3324                      UINT_MAX);
3325             goto cleanup;
3326         }
3327         if (outbound.average == 0 && (outbound.burst || outbound.peak)) {
3328             vshError(ctl, _("outbound average is mandatory"));
3329             goto cleanup;
3330         }
3331 
3332         if (outbound.floor) {
3333             vshError(ctl, _("outbound floor is unsupported yet"));
3334             goto cleanup;
3335         }
3336 
3337         if (virTypedParamsAddUInt(&params, &nparams, &maxparams,
3338                                   VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE,
3339                                   outbound.average) < 0)
3340             goto save_error;
3341 
3342         if (outbound.peak &&
3343             virTypedParamsAddUInt(&params, &nparams, &maxparams,
3344                                   VIR_DOMAIN_BANDWIDTH_OUT_PEAK,
3345                                   outbound.peak) < 0)
3346             goto save_error;
3347 
3348         if (outbound.burst &&
3349             virTypedParamsAddUInt(&params, &nparams, &maxparams,
3350                                   VIR_DOMAIN_BANDWIDTH_OUT_BURST,
3351                                   outbound.burst) < 0)
3352             goto save_error;
3353     }
3354 
3355     if (nparams == 0) {
3356         /* get the number of interface parameters */
3357         if (virDomainGetInterfaceParameters(dom, device, NULL, &nparams, flags) != 0) {
3358             vshError(ctl, "%s",
3359                      _("Unable to get number of interface parameters"));
3360             goto cleanup;
3361         }
3362 
3363         if (nparams == 0) {
3364             /* nothing to output */
3365             ret = true;
3366             goto cleanup;
3367         }
3368 
3369         /* get all interface parameters */
3370         params = g_new0(virTypedParameter, nparams);
3371         if (virDomainGetInterfaceParameters(dom, device, params, &nparams, flags) != 0) {
3372             vshError(ctl, "%s", _("Unable to get interface parameters"));
3373             goto cleanup;
3374         }
3375 
3376         for (i = 0; i < nparams; i++) {
3377             g_autofree char *str = vshGetTypedParamValue(ctl, &params[i]);
3378             vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
3379         }
3380     } else {
3381         if (virDomainSetInterfaceParameters(dom, device, params,
3382                                             nparams, flags) != 0)
3383             goto error;
3384     }
3385 
3386     ret = true;
3387 
3388  cleanup:
3389     virTypedParamsFree(params, nparams);
3390     return ret;
3391 
3392  save_error:
3393     vshSaveLibvirtError();
3394  error:
3395     vshError(ctl, "%s", _("Unable to set interface parameters"));
3396     goto cleanup;
3397 }
3398 
3399 /*
3400  * "suspend" command
3401  */
3402 static const vshCmdInfo info_suspend[] = {
3403     {.name = "help",
3404      .data = N_("suspend a domain")
3405     },
3406     {.name = "desc",
3407      .data = N_("Suspend a running domain.")
3408     },
3409     {.name = NULL}
3410 };
3411 
3412 static const vshCmdOptDef opts_suspend[] = {
3413     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_RUNNING),
3414     {.name = NULL}
3415 };
3416 
3417 static bool
cmdSuspend(vshControl * ctl,const vshCmd * cmd)3418 cmdSuspend(vshControl *ctl, const vshCmd *cmd)
3419 {
3420     g_autoptr(virshDomain) dom = NULL;
3421     const char *name;
3422 
3423     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
3424         return false;
3425 
3426     if (virDomainSuspend(dom) != 0) {
3427         vshError(ctl, _("Failed to suspend domain '%s'"), name);
3428         return false;
3429     }
3430 
3431     vshPrintExtra(ctl, _("Domain '%s' suspended\n"), name);
3432     return true;
3433 }
3434 
3435 /*
3436  * "dompmsuspend" command
3437  */
3438 static const vshCmdInfo info_dom_pm_suspend[] = {
3439     {.name = "help",
3440      .data = N_("suspend a domain gracefully using power management "
3441                 "functions")
3442     },
3443     {.name = "desc",
3444      .data = N_("Suspends a running domain using guest OS's power management. "
3445                 "(Note: This requires a guest agent configured and running in "
3446                 "the guest OS).")
3447     },
3448     {.name = NULL}
3449 };
3450 
3451 static const vshCmdOptDef opts_dom_pm_suspend[] = {
3452     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_RUNNING),
3453     {.name = "target",
3454      .type = VSH_OT_DATA,
3455      .flags = VSH_OFLAG_REQ,
3456      .completer = virshNodeSuspendTargetCompleter,
3457      .help = N_("mem(Suspend-to-RAM), "
3458                 "disk(Suspend-to-Disk), "
3459                 "hybrid(Hybrid-Suspend)")
3460     },
3461     {.name = "duration",
3462      .type = VSH_OT_INT,
3463      .flags = VSH_OFLAG_REQ_OPT,
3464      .help = N_("duration in seconds")
3465     },
3466     {.name = NULL}
3467 };
3468 
3469 static bool
cmdDomPMSuspend(vshControl * ctl,const vshCmd * cmd)3470 cmdDomPMSuspend(vshControl *ctl, const vshCmd *cmd)
3471 {
3472     g_autoptr(virshDomain) dom = NULL;
3473     const char *name;
3474     const char *target = NULL;
3475     int suspendTarget;
3476     unsigned long long duration = 0;
3477 
3478     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
3479         return false;
3480 
3481     if (vshCommandOptULongLong(ctl, cmd, "duration", &duration) < 0)
3482         return false;
3483 
3484     if (vshCommandOptStringReq(ctl, cmd, "target", &target) < 0)
3485         return false;
3486 
3487     if ((suspendTarget = virshNodeSuspendTargetTypeFromString(target)) < 0) {
3488         vshError(ctl, "%s", _("Invalid target"));
3489         return false;
3490     }
3491 
3492     if (virDomainPMSuspendForDuration(dom, suspendTarget, duration, 0) < 0) {
3493         vshError(ctl, _("Domain '%s' could not be suspended"),
3494                  virDomainGetName(dom));
3495         return false;
3496     }
3497 
3498     vshPrintExtra(ctl, _("Domain '%s' successfully suspended"),
3499              virDomainGetName(dom));
3500 
3501     return true;
3502 }
3503 
3504 /*
3505  * "dompmwakeup" command
3506  */
3507 
3508 static const vshCmdInfo info_dom_pm_wakeup[] = {
3509     {.name = "help",
3510      .data = N_("wakeup a domain from pmsuspended state")
3511     },
3512     {.name = "desc",
3513      .data = N_("Wakeup a domain that was previously suspended "
3514                 "by power management.")
3515     },
3516     {.name = NULL}
3517 };
3518 
3519 static const vshCmdOptDef opts_dom_pm_wakeup[] = {
3520     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_OTHER),
3521     {.name = NULL}
3522 };
3523 
3524 static bool
cmdDomPMWakeup(vshControl * ctl,const vshCmd * cmd)3525 cmdDomPMWakeup(vshControl *ctl, const vshCmd *cmd)
3526 {
3527     g_autoptr(virshDomain) dom = NULL;
3528     const char *name;
3529     unsigned int flags = 0;
3530 
3531     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
3532         return false;
3533 
3534     if (virDomainPMWakeup(dom, flags) < 0) {
3535         vshError(ctl, _("Domain '%s' could not be woken up"),
3536                  virDomainGetName(dom));
3537         return false;
3538     }
3539 
3540     vshPrintExtra(ctl, _("Domain '%s' successfully woken up"),
3541                   virDomainGetName(dom));
3542 
3543     return true;
3544 }
3545 
3546 /*
3547  * "undefine" command
3548  */
3549 static const vshCmdInfo info_undefine[] = {
3550     {.name = "help",
3551      .data = N_("undefine a domain")
3552     },
3553     {.name = "desc",
3554      .data = N_("Undefine an inactive domain, or convert persistent to transient.")
3555     },
3556     {.name = NULL}
3557 };
3558 
3559 static const vshCmdOptDef opts_undefine[] = {
3560     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_PERSISTENT),
3561     {.name = "managed-save",
3562      .type = VSH_OT_BOOL,
3563      .help = N_("remove domain managed state file")
3564     },
3565     {.name = "storage",
3566      .type = VSH_OT_STRING,
3567      .completer = virshDomainUndefineStorageDisksCompleter,
3568      .help = N_("remove associated storage volumes (comma separated list of "
3569                 "targets or source paths) (see domblklist)")
3570     },
3571     {.name = "remove-all-storage",
3572      .type = VSH_OT_BOOL,
3573      .help = N_("remove all associated storage volumes (use with caution)")
3574     },
3575     {.name = "delete-snapshots",
3576      .type = VSH_OT_ALIAS,
3577      .help = "delete-storage-volume-snapshots"
3578     },
3579     {.name = "delete-storage-volume-snapshots",
3580      .type = VSH_OT_BOOL,
3581      .help = N_("delete snapshots associated with volume(s), requires "
3582                 "--remove-all-storage (must be supported by storage driver)")
3583     },
3584     {.name = "wipe-storage",
3585      .type = VSH_OT_BOOL,
3586      .help = N_("wipe data on the removed volumes")
3587     },
3588     {.name = "snapshots-metadata",
3589      .type = VSH_OT_BOOL,
3590      .help = N_("remove all domain snapshot metadata (vm must be inactive)")
3591     },
3592     {.name = "checkpoints-metadata",
3593      .type = VSH_OT_BOOL,
3594      .help = N_("remove all domain checkpoint metadata (vm must be inactive)")
3595     },
3596     {.name = "nvram",
3597      .type = VSH_OT_BOOL,
3598      .help = N_("remove nvram file")
3599     },
3600     {.name = "keep-nvram",
3601      .type = VSH_OT_BOOL,
3602      .help = N_("keep nvram file")
3603     },
3604     {.name = NULL}
3605 };
3606 
3607 typedef struct {
3608     virStorageVolPtr vol;
3609     char *source;
3610     char *target;
3611 } virshUndefineVolume;
3612 
3613 static bool
cmdUndefine(vshControl * ctl,const vshCmd * cmd)3614 cmdUndefine(vshControl *ctl, const vshCmd *cmd)
3615 {
3616     g_autoptr(virshDomain) dom = NULL;
3617     bool ret = false;
3618     const char *name = NULL;
3619     /* Flags to attempt.  */
3620     unsigned int flags = 0;
3621     unsigned int vol_flags = 0;
3622     /* User-requested actions.  */
3623     bool managed_save = vshCommandOptBool(cmd, "managed-save");
3624     bool snapshots_metadata = vshCommandOptBool(cmd, "snapshots-metadata");
3625     bool checkpoints_metadata = vshCommandOptBool(cmd, "checkpoints-metadata");
3626     bool wipe_storage = vshCommandOptBool(cmd, "wipe-storage");
3627     bool remove_all_storage = vshCommandOptBool(cmd, "remove-all-storage");
3628     bool delete_snapshots = vshCommandOptBool(cmd, "delete-storage-volume-snapshots");
3629     bool nvram = vshCommandOptBool(cmd, "nvram");
3630     bool keep_nvram = vshCommandOptBool(cmd, "keep-nvram");
3631     /* Positive if these items exist.  */
3632     int has_managed_save = 0;
3633     int has_snapshots_metadata = 0;
3634     int has_snapshots = 0;
3635     /* True if undefine will not strand data, even on older servers.  */
3636     bool managed_save_safe = false;
3637     bool snapshots_safe = false;
3638     int rc = -1;
3639     int running;
3640     /* list of volumes to remove along with this domain */
3641     const char *vol_string = NULL;  /* string containing volumes to delete */
3642     char **vol_list = NULL;         /* tokenized vol_string */
3643     int nvol_list = 0;
3644     virshUndefineVolume *vols = NULL; /* info about the volumes to delete */
3645     size_t nvols = 0;
3646     g_autoptr(xmlDoc) doc = NULL;
3647     g_autoptr(xmlXPathContext) ctxt = NULL;
3648     xmlNodePtr *vol_nodes = NULL;   /* XML nodes of volumes of the guest */
3649     int nvol_nodes;
3650     size_t i;
3651     size_t j;
3652     virshControl *priv = ctl->privData;
3653 
3654     VSH_REQUIRE_OPTION("delete-storage-volume-snapshots", "remove-all-storage");
3655     VSH_EXCLUSIVE_OPTIONS("nvram", "keep-nvram");
3656 
3657     ignore_value(vshCommandOptStringQuiet(ctl, cmd, "storage", &vol_string));
3658 
3659     if (!(vol_string || remove_all_storage) && wipe_storage) {
3660         vshError(ctl,
3661                  _("'--wipe-storage' requires '--storage <string>' or "
3662                    "'--remove-all-storage'"));
3663         return false;
3664     }
3665 
3666     if (delete_snapshots)
3667         vol_flags |= VIR_STORAGE_VOL_DELETE_WITH_SNAPSHOTS;
3668 
3669     if (managed_save) {
3670         flags |= VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
3671         managed_save_safe = true;
3672     }
3673     if (snapshots_metadata) {
3674         flags |= VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA;
3675         snapshots_safe = true;
3676     }
3677     if (checkpoints_metadata)
3678         flags |= VIR_DOMAIN_UNDEFINE_CHECKPOINTS_METADATA;
3679     if (nvram)
3680         flags |= VIR_DOMAIN_UNDEFINE_NVRAM;
3681     if (keep_nvram)
3682         flags |= VIR_DOMAIN_UNDEFINE_KEEP_NVRAM;
3683 
3684     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
3685         return false;
3686 
3687     /* Do some flag manipulation.  The goal here is to disable bits
3688      * from flags to reduce the likelihood of a server rejecting
3689      * unknown flag bits, as well as to track conditions which are
3690      * safe by default for the given hypervisor and server version.  */
3691     if ((running = virDomainIsActive(dom)) < 0)
3692         goto error;
3693 
3694     if (!running) {
3695         /* Undefine with snapshots only fails for inactive domains,
3696          * and managed save only exists on inactive domains; if
3697          * running, then we don't want to remove anything.  */
3698         has_managed_save = virDomainHasManagedSaveImage(dom, 0);
3699         if (has_managed_save < 0) {
3700             if (last_error->code != VIR_ERR_NO_SUPPORT)
3701                 goto error;
3702             vshResetLibvirtError();
3703             has_managed_save = 0;
3704         }
3705 
3706         has_snapshots = virDomainSnapshotNum(dom, 0);
3707         if (has_snapshots < 0) {
3708             if (last_error->code != VIR_ERR_NO_SUPPORT)
3709                 goto error;
3710             vshResetLibvirtError();
3711             has_snapshots = 0;
3712         }
3713         if (has_snapshots) {
3714             has_snapshots_metadata
3715                 = virDomainSnapshotNum(dom, VIR_DOMAIN_SNAPSHOT_LIST_METADATA);
3716             if (has_snapshots_metadata < 0) {
3717                 /* The server did not know the new flag, assume that all
3718                    snapshots have metadata.  */
3719                 vshResetLibvirtError();
3720                 has_snapshots_metadata = has_snapshots;
3721             } else {
3722                 /* The server knew the new flag, all aspects of
3723                  * undefineFlags are safe.  */
3724                 managed_save_safe = snapshots_safe = true;
3725             }
3726         }
3727     }
3728     if (!has_managed_save) {
3729         flags &= ~VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
3730         managed_save_safe = true;
3731     }
3732     if (has_snapshots == 0)
3733         snapshots_safe = true;
3734     if (has_snapshots_metadata == 0) {
3735         flags &= ~VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA;
3736         snapshots_safe = true;
3737     }
3738 
3739     /* Stash domain description for later use */
3740     if (vol_string || remove_all_storage) {
3741         if (running) {
3742             vshError(ctl,
3743                      _("Storage volume deletion is supported only on "
3744                        "stopped domains"));
3745             goto cleanup;
3746         }
3747 
3748         if (vol_string && remove_all_storage) {
3749             vshError(ctl,
3750                      _("Specified both --storage and --remove-all-storage"));
3751             goto cleanup;
3752         }
3753 
3754         if (virshDomainGetXMLFromDom(ctl, dom, 0, &doc, &ctxt) < 0)
3755             goto cleanup;
3756 
3757         /* tokenize the string from user and save its parts into an array */
3758         if (vol_string &&
3759             (nvol_list = vshStringToArray(vol_string, &vol_list)) < 0)
3760             goto error;
3761 
3762         if ((nvol_nodes = virXPathNodeSet("./devices/disk", ctxt,
3763                                           &vol_nodes)) < 0)
3764             goto error;
3765 
3766         for (i = 0; i < nvol_nodes; i++) {
3767             g_autofree char *source = NULL;
3768             g_autofree char *target = NULL;
3769             g_autofree char *pool = NULL;
3770             virshUndefineVolume vol;
3771 
3772             ctxt->node = vol_nodes[i];
3773 
3774             /* get volume source and target paths */
3775             if (!(target = virXPathString("string(./target/@dev)", ctxt)))
3776                 goto error;
3777 
3778             if (!(source = virXPathString("string("
3779                                           "./source/@file|"
3780                                           "./source/@dir|"
3781                                           "./source/@name|"
3782                                           "./source/@dev|"
3783                                           "./source/@volume)", ctxt)))
3784                 continue;
3785 
3786             pool = virXPathString("string(./source/@pool)", ctxt);
3787 
3788             /* lookup if volume was selected by user */
3789             if (vol_list) {
3790                 bool found = false;
3791                 for (j = 0; j < nvol_list; j++) {
3792                     if (STREQ_NULLABLE(vol_list[j], target) ||
3793                         STREQ_NULLABLE(vol_list[j], source)) {
3794                         VIR_FREE(vol_list[j]);
3795                         found = true;
3796                         break;
3797                     }
3798                 }
3799                 if (!found)
3800                     continue;
3801             }
3802 
3803             if (pool) {
3804                 g_autoptr(virshStoragePool) storagepool = NULL;
3805 
3806                 if (!source) {
3807                     vshError(ctl,
3808                              _("Missing storage volume name for disk '%s'"),
3809                              target);
3810                     continue;
3811                 }
3812 
3813                 if (!(storagepool = virStoragePoolLookupByName(priv->conn,
3814                                                                pool))) {
3815                     vshError(ctl,
3816                              _("Storage pool '%s' for volume '%s' not found."),
3817                              pool, target);
3818                     vshResetLibvirtError();
3819                     continue;
3820                 }
3821 
3822                 vol.vol = virStorageVolLookupByName(storagepool, source);
3823 
3824             } else {
3825                vol.vol = virStorageVolLookupByPath(priv->conn, source);
3826             }
3827 
3828             if (!vol.vol) {
3829                 vshError(ctl,
3830                          _("Storage volume '%s'(%s) is not managed by libvirt. "
3831                            "Remove it manually.\n"), target, source);
3832                 vshResetLibvirtError();
3833                 continue;
3834             }
3835 
3836             vol.source = g_steal_pointer(&source);
3837             vol.target = g_steal_pointer(&target);
3838             VIR_APPEND_ELEMENT(vols, nvols, vol);
3839         }
3840 
3841         /* print volumes specified by user that were not found in domain definition */
3842         if (vol_list) {
3843             bool found = false;
3844             for (i = 0; i < nvol_list; i++) {
3845                 if (vol_list[i]) {
3846                     vshError(ctl,
3847                              _("Volume '%s' was not found in domain's "
3848                                "definition.\n"), vol_list[i]);
3849                     found = true;
3850                 }
3851             }
3852 
3853             if (found)
3854                 goto cleanup;
3855         }
3856     }
3857 
3858     /* Generally we want to try the new API first.  However, while
3859      * virDomainUndefineFlags was introduced at the same time as
3860      * VIR_DOMAIN_UNDEFINE_MANAGED_SAVE in 0.9.4, the
3861      * VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA flag was not present
3862      * until 0.9.5; skip to piecewise emulation if we couldn't prove
3863      * above that the new API is safe.
3864      * Moreover, only the newer UndefineFlags() API understands
3865      * the VIR_DOMAIN_UNDEFINE_NVRAM flag. So if user has
3866      * specified --nvram we must use the Flags() API. */
3867     if ((managed_save_safe && snapshots_safe) || nvram) {
3868         rc = virDomainUndefineFlags(dom, flags);
3869         if (rc == 0 || nvram ||
3870             (last_error->code != VIR_ERR_NO_SUPPORT &&
3871              last_error->code != VIR_ERR_INVALID_ARG))
3872             goto out;
3873         vshResetLibvirtError();
3874     }
3875 
3876     /* The new API is unsupported or unsafe; fall back to doing things
3877      * piecewise.  */
3878     if (has_managed_save) {
3879         if (!managed_save) {
3880             vshError(ctl, "%s",
3881                      _("Refusing to undefine while domain managed save "
3882                        "image exists"));
3883             goto cleanup;
3884         }
3885         if (virDomainManagedSaveRemove(dom, 0) < 0) {
3886             vshReportError(ctl);
3887             goto cleanup;
3888         }
3889     }
3890 
3891     /* No way to emulate deletion of just snapshot metadata
3892      * without support for the newer flags.  Oh well.  */
3893     if (has_snapshots_metadata) {
3894         vshError(ctl,
3895                  snapshots_metadata ?
3896                  _("Unable to remove metadata of %d snapshots") :
3897                  _("Refusing to undefine while %d snapshots exist"),
3898                  has_snapshots_metadata);
3899         goto cleanup;
3900     }
3901 
3902     rc = virDomainUndefine(dom);
3903 
3904  out:
3905     if (rc == 0) {
3906         vshPrintExtra(ctl, _("Domain '%s' has been undefined\n"), name);
3907         ret = true;
3908     } else {
3909         vshError(ctl, _("Failed to undefine domain '%s'"), name);
3910         goto cleanup;
3911     }
3912 
3913     /* try to undefine storage volumes associated with this domain, if it's requested */
3914     if (nvols) {
3915         for (i = 0; i < nvols; i++) {
3916             if (wipe_storage) {
3917                 vshPrintExtra(ctl, _("Wiping volume '%s'(%s) ... "),
3918                               vols[i].target, vols[i].source);
3919                 fflush(stdout);
3920                 if (virStorageVolWipe(vols[i].vol, 0) < 0) {
3921                     vshError(ctl, _("Failed! Volume not removed."));
3922                     ret = false;
3923                     continue;
3924                 } else {
3925                     vshPrintExtra(ctl, _("Done.\n"));
3926                 }
3927             }
3928 
3929             /* delete the volume */
3930             if (virStorageVolDelete(vols[i].vol, vol_flags) < 0) {
3931                 vshError(ctl, _("Failed to remove storage volume '%s'(%s)"),
3932                          vols[i].target, vols[i].source);
3933                 ret = false;
3934             } else {
3935                 vshPrintExtra(ctl, _("Volume '%s'(%s) removed.\n"),
3936                               vols[i].target, vols[i].source);
3937             }
3938         }
3939     }
3940 
3941  cleanup:
3942     for (i = 0; i < nvols; i++) {
3943         VIR_FREE(vols[i].source);
3944         VIR_FREE(vols[i].target);
3945         virshStorageVolFree(vols[i].vol);
3946     }
3947     VIR_FREE(vols);
3948 
3949     for (i = 0; i < nvol_list; i++)
3950         VIR_FREE(vol_list[i]);
3951     VIR_FREE(vol_list);
3952 
3953     VIR_FREE(vol_nodes);
3954     return ret;
3955 
3956  error:
3957     vshReportError(ctl);
3958     goto cleanup;
3959 }
3960 
3961 /*
3962  * "start" command
3963  */
3964 static const vshCmdInfo info_start[] = {
3965     {.name = "help",
3966      .data = N_("start a (previously defined) inactive domain")
3967     },
3968     {.name = "desc",
3969      .data = N_("Start a domain, either from the last managedsave\n"
3970                 "    state, or via a fresh boot if no managedsave state\n"
3971                 "    is present.")
3972     },
3973     {.name = NULL}
3974 };
3975 
3976 static const vshCmdOptDef opts_start[] = {
3977     VIRSH_COMMON_OPT_DOMAIN(N_("name of the inactive domain"),
3978                             VIR_CONNECT_LIST_DOMAINS_SHUTOFF),
3979 #ifndef WIN32
3980     {.name = "console",
3981      .type = VSH_OT_BOOL,
3982      .help = N_("attach to console after creation")
3983     },
3984 #endif
3985     {.name = "paused",
3986      .type = VSH_OT_BOOL,
3987      .help = N_("leave the guest paused after creation")
3988     },
3989     {.name = "autodestroy",
3990      .type = VSH_OT_BOOL,
3991      .help = N_("automatically destroy the guest when virsh disconnects")
3992     },
3993     {.name = "bypass-cache",
3994      .type = VSH_OT_BOOL,
3995      .help = N_("avoid file system cache when loading")
3996     },
3997     {.name = "force-boot",
3998      .type = VSH_OT_BOOL,
3999      .help = N_("force fresh boot by discarding any managed save")
4000     },
4001     {.name = "pass-fds",
4002      .type = VSH_OT_STRING,
4003      .completer = virshCompleteEmpty,
4004      .help = N_("pass file descriptors N,M,... to the guest")
4005     },
4006     {.name = NULL}
4007 };
4008 
4009 static int
cmdStartGetFDs(vshControl * ctl,const vshCmd * cmd,size_t * nfdsret,int ** fdsret)4010 cmdStartGetFDs(vshControl *ctl,
4011                const vshCmd *cmd,
4012                size_t *nfdsret,
4013                int **fdsret)
4014 {
4015     const char *fdopt;
4016     g_auto(GStrv) fdlist = NULL;
4017     int *fds = NULL;
4018     size_t nfds = 0;
4019     size_t i;
4020 
4021     *nfdsret = 0;
4022     *fdsret = NULL;
4023 
4024     if (vshCommandOptStringQuiet(ctl, cmd, "pass-fds", &fdopt) <= 0)
4025         return 0;
4026 
4027     if (!(fdlist = g_strsplit(fdopt, ",", -1))) {
4028         vshError(ctl, _("Unable to split FD list '%s'"), fdopt);
4029         return -1;
4030     }
4031 
4032     for (i = 0; fdlist[i] != NULL; i++) {
4033         int fd;
4034         if (virStrToLong_i(fdlist[i], NULL, 10, &fd) < 0) {
4035             vshError(ctl, _("Unable to parse FD number '%s'"), fdlist[i]);
4036             goto error;
4037         }
4038         VIR_EXPAND_N(fds, nfds, 1);
4039         fds[nfds - 1] = fd;
4040     }
4041 
4042     *fdsret = fds;
4043     *nfdsret = nfds;
4044     return 0;
4045 
4046  error:
4047     VIR_FREE(fds);
4048     return -1;
4049 }
4050 
4051 static bool
cmdStart(vshControl * ctl,const vshCmd * cmd)4052 cmdStart(vshControl *ctl, const vshCmd *cmd)
4053 {
4054     g_autoptr(virshDomain) dom = NULL;
4055 #ifndef WIN32
4056     bool console = vshCommandOptBool(cmd, "console");
4057 #endif
4058     unsigned int flags = VIR_DOMAIN_NONE;
4059     int rc;
4060     size_t nfds = 0;
4061     g_autofree int *fds = NULL;
4062 
4063     if (!(dom = virshCommandOptDomainBy(ctl, cmd, NULL,
4064                                         VIRSH_BYNAME | VIRSH_BYUUID)))
4065         return false;
4066 
4067     if (virDomainGetID(dom) != (unsigned int)-1) {
4068         vshError(ctl, "%s", _("Domain is already active"));
4069         return false;
4070     }
4071 
4072     if (cmdStartGetFDs(ctl, cmd, &nfds, &fds) < 0)
4073         return false;
4074 
4075     if (vshCommandOptBool(cmd, "paused"))
4076         flags |= VIR_DOMAIN_START_PAUSED;
4077     if (vshCommandOptBool(cmd, "autodestroy"))
4078         flags |= VIR_DOMAIN_START_AUTODESTROY;
4079     if (vshCommandOptBool(cmd, "bypass-cache"))
4080         flags |= VIR_DOMAIN_START_BYPASS_CACHE;
4081     if (vshCommandOptBool(cmd, "force-boot"))
4082         flags |= VIR_DOMAIN_START_FORCE_BOOT;
4083 
4084     /* We can emulate force boot, even for older servers that reject it.  */
4085     if (flags & VIR_DOMAIN_START_FORCE_BOOT) {
4086         if ((nfds ?
4087              virDomainCreateWithFiles(dom, nfds, fds, flags) :
4088              virDomainCreateWithFlags(dom, flags)) == 0)
4089             goto started;
4090         if (last_error->code != VIR_ERR_NO_SUPPORT &&
4091             last_error->code != VIR_ERR_INVALID_ARG) {
4092             vshReportError(ctl);
4093             return false;
4094         }
4095         vshResetLibvirtError();
4096         rc = virDomainHasManagedSaveImage(dom, 0);
4097         if (rc < 0) {
4098             /* No managed save image to remove */
4099             vshResetLibvirtError();
4100         } else if (rc > 0) {
4101             if (virDomainManagedSaveRemove(dom, 0) < 0) {
4102                 vshReportError(ctl);
4103                 return false;
4104             }
4105         }
4106         flags &= ~VIR_DOMAIN_START_FORCE_BOOT;
4107     }
4108 
4109     /* Prefer older API unless we have to pass a flag.  */
4110     if ((nfds ? virDomainCreateWithFiles(dom, nfds, fds, flags) :
4111          (flags ? virDomainCreateWithFlags(dom, flags)
4112           : virDomainCreate(dom))) < 0) {
4113         vshError(ctl, _("Failed to start domain '%s'"), virDomainGetName(dom));
4114         return false;
4115     }
4116 
4117  started:
4118     vshPrintExtra(ctl, _("Domain '%s' started\n"),
4119                   virDomainGetName(dom));
4120 #ifndef WIN32
4121     if (console && !cmdRunConsole(ctl, dom, NULL, 0))
4122         return false;
4123 #endif
4124 
4125     return true;
4126 }
4127 
4128 /*
4129  * "save" command
4130  */
4131 static const vshCmdInfo info_save[] = {
4132     {.name = "help",
4133      .data = N_("save a domain state to a file")
4134     },
4135     {.name = "desc",
4136      .data = N_("Save the RAM state of a running domain.")
4137     },
4138     {.name = NULL}
4139 };
4140 
4141 static const vshCmdOptDef opts_save[] = {
4142     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
4143     {.name = "file",
4144      .type = VSH_OT_DATA,
4145      .flags = VSH_OFLAG_REQ,
4146      .help = N_("where to save the data")
4147     },
4148     {.name = "bypass-cache",
4149      .type = VSH_OT_BOOL,
4150      .help = N_("avoid file system cache when saving")
4151     },
4152     {.name = "xml",
4153      .type = VSH_OT_STRING,
4154      .completer = virshCompletePathLocalExisting,
4155      .help = N_("filename containing updated XML for the target")
4156     },
4157     {.name = "running",
4158      .type = VSH_OT_BOOL,
4159      .help = N_("set domain to be running on restore")
4160     },
4161     {.name = "paused",
4162      .type = VSH_OT_BOOL,
4163      .help = N_("set domain to be paused on restore")
4164     },
4165     {.name = "verbose",
4166      .type = VSH_OT_BOOL,
4167      .help = N_("display the progress of save")
4168     },
4169     {.name = NULL}
4170 };
4171 
4172 static void
doSave(void * opaque)4173 doSave(void *opaque)
4174 {
4175     virshCtrlData *data = opaque;
4176     vshControl *ctl = data->ctl;
4177     const vshCmd *cmd = data->cmd;
4178     g_autoptr(virshDomain) dom = NULL;
4179     const char *name = NULL;
4180     const char *to = NULL;
4181     unsigned int flags = 0;
4182     const char *xmlfile = NULL;
4183     g_autofree char *xml = NULL;
4184 #ifndef WIN32
4185     sigset_t sigmask, oldsigmask;
4186 
4187     sigemptyset(&sigmask);
4188     sigaddset(&sigmask, SIGINT);
4189     if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) != 0)
4190         goto out_sig;
4191 #endif /* !WIN32 */
4192 
4193     if (vshCommandOptStringReq(ctl, cmd, "file", &to) < 0)
4194         goto out;
4195 
4196     if (vshCommandOptBool(cmd, "bypass-cache"))
4197         flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
4198     if (vshCommandOptBool(cmd, "running"))
4199         flags |= VIR_DOMAIN_SAVE_RUNNING;
4200     if (vshCommandOptBool(cmd, "paused"))
4201         flags |= VIR_DOMAIN_SAVE_PAUSED;
4202 
4203     if (vshCommandOptStringReq(ctl, cmd, "xml", &xmlfile) < 0)
4204         goto out;
4205 
4206     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
4207         goto out;
4208 
4209     if (xmlfile &&
4210         virFileReadAll(xmlfile, VSH_MAX_XML_FILE, &xml) < 0) {
4211         vshReportError(ctl);
4212         goto out;
4213     }
4214 
4215     if (((flags || xml)
4216          ? virDomainSaveFlags(dom, to, xml, flags)
4217          : virDomainSave(dom, to)) < 0) {
4218         vshError(ctl, _("Failed to save domain '%s' to %s"), name, to);
4219         goto out;
4220     }
4221 
4222     data->ret = 0;
4223 
4224  out:
4225 #ifndef WIN32
4226     pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
4227  out_sig:
4228 #endif /* !WIN32 */
4229     g_main_loop_quit(data->eventLoop);
4230 }
4231 
4232 typedef void (*jobWatchTimeoutFunc)(vshControl *ctl, virDomainPtr dom,
4233                                     void *opaque);
4234 
4235 struct virshWatchData {
4236     vshControl *ctl;
4237     virDomainPtr dom;
4238     jobWatchTimeoutFunc timeout_func;
4239     void *opaque;
4240     const char *label;
4241     GIOChannel *stdin_ioc;
4242     bool jobStarted;
4243     bool verbose;
4244 };
4245 
4246 static gboolean
virshWatchTimeout(gpointer opaque)4247 virshWatchTimeout(gpointer opaque)
4248 {
4249     struct virshWatchData *data = opaque;
4250 
4251     /* suspend the domain when migration timeouts. */
4252     vshDebug(data->ctl, VSH_ERR_DEBUG, "watchJob: timeout\n");
4253     if (data->timeout_func)
4254         (data->timeout_func)(data->ctl, data->dom, data->opaque);
4255 
4256     return G_SOURCE_REMOVE;
4257 }
4258 
4259 
4260 static gboolean
virshWatchProgress(gpointer opaque)4261 virshWatchProgress(gpointer opaque)
4262 {
4263     struct virshWatchData *data = opaque;
4264     virDomainJobInfo jobinfo;
4265     int ret;
4266 #ifndef WIN32
4267     sigset_t sigmask, oldsigmask;
4268 
4269     sigemptyset(&sigmask);
4270     sigaddset(&sigmask, SIGINT);
4271 
4272     pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask);
4273 #endif /* !WIN32 */
4274     vshDebug(data->ctl, VSH_ERR_DEBUG, "%s",
4275              "watchJob: progress update\n");
4276     ret = virDomainGetJobInfo(data->dom, &jobinfo);
4277 #ifndef WIN32
4278     pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
4279 #endif /* !WIN32 */
4280 
4281     if (ret == 0) {
4282         if (data->verbose && jobinfo.dataTotal > 0)
4283             virshPrintJobProgress(data->label, jobinfo.dataRemaining,
4284                                   jobinfo.dataTotal);
4285 
4286         if (!data->jobStarted &&
4287             (jobinfo.type == VIR_DOMAIN_JOB_BOUNDED ||
4288              jobinfo.type == VIR_DOMAIN_JOB_UNBOUNDED)) {
4289             vshTTYDisableInterrupt(data->ctl);
4290             data->jobStarted = true;
4291 
4292             if (!data->verbose) {
4293                 vshDebug(data->ctl, VSH_ERR_DEBUG,
4294                          "watchJob: job started, disabling callback\n");
4295                 return G_SOURCE_REMOVE;
4296             }
4297         }
4298     } else {
4299         vshResetLibvirtError();
4300     }
4301 
4302     return G_SOURCE_CONTINUE;
4303 }
4304 
4305 
4306 static gboolean
virshWatchInterrupt(GIOChannel * source G_GNUC_UNUSED,GIOCondition condition,gpointer opaque)4307 virshWatchInterrupt(GIOChannel *source G_GNUC_UNUSED,
4308                     GIOCondition condition,
4309                     gpointer opaque)
4310 {
4311     struct virshWatchData *data = opaque;
4312     char retchar;
4313     gsize nread = 0;
4314 
4315     vshDebug(data->ctl, VSH_ERR_DEBUG,
4316              "watchJob: stdin data %d\n", condition);
4317     if (condition & G_IO_IN) {
4318         g_io_channel_read_chars(data->stdin_ioc,
4319                                 &retchar,
4320                                 sizeof(retchar),
4321                                 &nread,
4322                                 NULL);
4323 
4324         vshDebug(data->ctl, VSH_ERR_DEBUG,
4325                  "watchJob: got %zu characters\n", nread);
4326         if (nread == 1 &&
4327             vshTTYIsInterruptCharacter(data->ctl, retchar)) {
4328             virDomainAbortJob(data->dom);
4329             return G_SOURCE_REMOVE;
4330         }
4331     }
4332 
4333     if (condition & (G_IO_ERR | G_IO_HUP)) {
4334         virDomainAbortJob(data->dom);
4335         return G_SOURCE_REMOVE;
4336     }
4337 
4338     return G_SOURCE_CONTINUE;
4339 }
4340 
4341 
4342 static void
virshWatchJob(vshControl * ctl,virDomainPtr dom,bool verbose,GMainLoop * eventLoop,int * job_err,int timeout_secs,jobWatchTimeoutFunc timeout_func,void * opaque,const char * label)4343 virshWatchJob(vshControl *ctl,
4344               virDomainPtr dom,
4345               bool verbose,
4346               GMainLoop *eventLoop,
4347               int *job_err,
4348               int timeout_secs,
4349               jobWatchTimeoutFunc timeout_func,
4350               void *opaque,
4351               const char *label)
4352 {
4353 #ifndef WIN32
4354     struct sigaction sig_action;
4355     struct sigaction old_sig_action;
4356 #endif /* !WIN32 */
4357     g_autoptr(GSource) timeout_src = NULL;
4358     g_autoptr(GSource) progress_src = NULL;
4359     g_autoptr(GSource) stdin_src = NULL;
4360     struct virshWatchData data = {
4361         .ctl = ctl,
4362         .dom = dom,
4363         .timeout_func = timeout_func,
4364         .opaque = opaque,
4365         .label = label,
4366         .stdin_ioc = NULL,
4367         .jobStarted = false,
4368         .verbose = verbose,
4369     };
4370 
4371 #ifndef WIN32
4372     intCaught = 0;
4373     sig_action.sa_sigaction = virshCatchInt;
4374     sig_action.sa_flags = SA_SIGINFO;
4375     sigemptyset(&sig_action.sa_mask);
4376     sigaction(SIGINT, &sig_action, &old_sig_action);
4377 #endif /* !WIN32 */
4378 
4379     /* don't poll on STDIN if we are not using a terminal */
4380     if (vshTTYAvailable(ctl)) {
4381         vshDebug(ctl, VSH_ERR_DEBUG, "%s",
4382                  "watchJob: on TTY, enabling Ctrl-c processing\n");
4383 #ifdef WIN32
4384         data.stdin_ioc = g_io_channel_win32_new_fd(STDIN_FILENO);
4385 #else
4386         data.stdin_ioc = g_io_channel_unix_new(STDIN_FILENO);
4387 #endif
4388         stdin_src = g_io_create_watch(data.stdin_ioc, G_IO_IN);
4389         g_source_set_callback(stdin_src,
4390                               (GSourceFunc)virshWatchInterrupt,
4391                               &data, NULL);
4392         g_source_attach(stdin_src,
4393                         g_main_loop_get_context(eventLoop));
4394     }
4395 
4396     if (timeout_secs) {
4397         vshDebug(ctl, VSH_ERR_DEBUG,
4398                  "watchJob: setting timeout of %d secs\n", timeout_secs);
4399         timeout_src = g_timeout_source_new_seconds(timeout_secs);
4400         g_source_set_callback(timeout_src,
4401                               virshWatchTimeout,
4402                               &data, NULL);
4403         g_source_attach(timeout_src,
4404                         g_main_loop_get_context(eventLoop));
4405     }
4406 
4407     progress_src = g_timeout_source_new(500);
4408     g_source_set_callback(progress_src,
4409                           virshWatchProgress,
4410                           &data, NULL);
4411     g_source_attach(progress_src,
4412                     g_main_loop_get_context(eventLoop));
4413 
4414     g_main_loop_run(eventLoop);
4415 
4416     vshDebug(ctl, VSH_ERR_DEBUG,
4417              "watchJob: job done, status %d\n", *job_err);
4418     if (*job_err == 0 && verbose) /* print [100 %] */
4419         virshPrintJobProgress(label, 0, 1);
4420 
4421     if (timeout_src)
4422         g_source_destroy(timeout_src);
4423     g_source_destroy(progress_src);
4424     if (stdin_src)
4425         g_source_destroy(stdin_src);
4426 
4427 #ifndef WIN32
4428     sigaction(SIGINT, &old_sig_action, NULL);
4429 #endif /* !WIN32 */
4430     vshTTYRestore(ctl);
4431     if (data.stdin_ioc)
4432         g_io_channel_unref(data.stdin_ioc);
4433 }
4434 
4435 static bool
cmdSave(vshControl * ctl,const vshCmd * cmd)4436 cmdSave(vshControl *ctl, const vshCmd *cmd)
4437 {
4438     g_autoptr(virshDomain) dom = NULL;
4439     virThread workerThread;
4440     bool verbose = false;
4441     const char *to = NULL;
4442     const char *name = NULL;
4443     g_autoptr(GMainContext) eventCtxt = g_main_context_new();
4444     g_autoptr(GMainLoop) eventLoop = g_main_loop_new(eventCtxt, FALSE);
4445     virshCtrlData data = {
4446         .ctl = ctl,
4447         .cmd = cmd,
4448         .eventLoop = eventLoop,
4449         .ret = -1,
4450     };
4451 
4452     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
4453         return false;
4454 
4455     if (vshCommandOptStringReq(ctl, cmd, "file", &to) < 0)
4456         return false;
4457 
4458     if (vshCommandOptBool(cmd, "verbose"))
4459         verbose = true;
4460 
4461     if (virThreadCreate(&workerThread,
4462                         true,
4463                         doSave,
4464                         &data) < 0)
4465         return false;
4466 
4467     virshWatchJob(ctl, dom, verbose, eventLoop,
4468                   &data.ret, 0, NULL, NULL, _("Save"));
4469 
4470     virThreadJoin(&workerThread);
4471 
4472     if (!data.ret)
4473         vshPrintExtra(ctl, _("\nDomain '%s' saved to %s\n"), name, to);
4474 
4475     return !data.ret;
4476 }
4477 
4478 /*
4479  * "save-image-dumpxml" command
4480  */
4481 static const vshCmdInfo info_save_image_dumpxml[] = {
4482     {.name = "help",
4483      .data = N_("saved state domain information in XML")
4484     },
4485     {.name = "desc",
4486      .data = N_("Dump XML of domain information for a saved state file to stdout.")
4487     },
4488     {.name = NULL}
4489 };
4490 
4491 static const vshCmdOptDef opts_save_image_dumpxml[] = {
4492     {.name = "file",
4493      .type = VSH_OT_DATA,
4494      .flags = VSH_OFLAG_REQ,
4495      .help = N_("saved state file to read")
4496     },
4497     {.name = "security-info",
4498      .type = VSH_OT_BOOL,
4499      .help = N_("include security sensitive information in XML dump")
4500     },
4501     {.name = NULL}
4502 };
4503 
4504 static bool
cmdSaveImageDumpxml(vshControl * ctl,const vshCmd * cmd)4505 cmdSaveImageDumpxml(vshControl *ctl, const vshCmd *cmd)
4506 {
4507     const char *file = NULL;
4508     unsigned int flags = 0;
4509     g_autofree char *xml = NULL;
4510     virshControl *priv = ctl->privData;
4511 
4512     if (vshCommandOptBool(cmd, "security-info"))
4513         flags |= VIR_DOMAIN_XML_SECURE;
4514 
4515     if (vshCommandOptStringReq(ctl, cmd, "file", &file) < 0)
4516         return false;
4517 
4518     xml = virDomainSaveImageGetXMLDesc(priv->conn, file, flags);
4519     if (!xml)
4520         return false;
4521 
4522     vshPrint(ctl, "%s", xml);
4523     return true;
4524 }
4525 
4526 /*
4527  * "save-image-define" command
4528  */
4529 static const vshCmdInfo info_save_image_define[] = {
4530     {.name = "help",
4531      .data = N_("redefine the XML for a domain's saved state file")
4532     },
4533     {.name = "desc",
4534      .data = N_("Replace the domain XML associated with a saved state file")
4535     },
4536     {.name = NULL}
4537 };
4538 
4539 static const vshCmdOptDef opts_save_image_define[] = {
4540     {.name = "file",
4541      .type = VSH_OT_DATA,
4542      .flags = VSH_OFLAG_REQ,
4543      .help = N_("saved state file to modify")
4544     },
4545     {.name = "xml",
4546      .type = VSH_OT_DATA,
4547      .flags = VSH_OFLAG_REQ,
4548      .completer = virshCompletePathLocalExisting,
4549      .help = N_("filename containing updated XML for the target")
4550     },
4551     {.name = "running",
4552      .type = VSH_OT_BOOL,
4553      .help = N_("set domain to be running on restore")
4554     },
4555     {.name = "paused",
4556      .type = VSH_OT_BOOL,
4557      .help = N_("set domain to be paused on restore")
4558     },
4559     {.name = NULL}
4560 };
4561 
4562 static bool
cmdSaveImageDefine(vshControl * ctl,const vshCmd * cmd)4563 cmdSaveImageDefine(vshControl *ctl, const vshCmd *cmd)
4564 {
4565     const char *file = NULL;
4566     const char *xmlfile = NULL;
4567     g_autofree char *xml = NULL;
4568     unsigned int flags = 0;
4569     virshControl *priv = ctl->privData;
4570 
4571     if (vshCommandOptBool(cmd, "running"))
4572         flags |= VIR_DOMAIN_SAVE_RUNNING;
4573     if (vshCommandOptBool(cmd, "paused"))
4574         flags |= VIR_DOMAIN_SAVE_PAUSED;
4575 
4576     if (vshCommandOptStringReq(ctl, cmd, "file", &file) < 0)
4577         return false;
4578 
4579     if (vshCommandOptStringReq(ctl, cmd, "xml", &xmlfile) < 0)
4580         return false;
4581 
4582     if (virFileReadAll(xmlfile, VSH_MAX_XML_FILE, &xml) < 0)
4583         return false;
4584 
4585     if (virDomainSaveImageDefineXML(priv->conn, file, xml, flags) < 0) {
4586         vshError(ctl, _("Failed to update %s"), file);
4587         return false;
4588     }
4589 
4590     vshPrintExtra(ctl, _("State file %s updated.\n"), file);
4591     return true;
4592 }
4593 
4594 /*
4595  * "save-image-edit" command
4596  */
4597 static const vshCmdInfo info_save_image_edit[] = {
4598     {.name = "help",
4599      .data = N_("edit XML for a domain's saved state file")
4600     },
4601     {.name = "desc",
4602      .data = N_("Edit the domain XML associated with a saved state file")
4603     },
4604     {.name = NULL}
4605 };
4606 
4607 static const vshCmdOptDef opts_save_image_edit[] = {
4608     {.name = "file",
4609      .type = VSH_OT_DATA,
4610      .flags = VSH_OFLAG_REQ,
4611      .help = N_("saved state file to edit")
4612     },
4613     {.name = "running",
4614      .type = VSH_OT_BOOL,
4615      .help = N_("set domain to be running on restore")
4616     },
4617     {.name = "paused",
4618      .type = VSH_OT_BOOL,
4619      .help = N_("set domain to be paused on restore")
4620     },
4621     {.name = NULL}
4622 };
4623 
4624 static bool
cmdSaveImageEdit(vshControl * ctl,const vshCmd * cmd)4625 cmdSaveImageEdit(vshControl *ctl, const vshCmd *cmd)
4626 {
4627     const char *file = NULL;
4628     bool ret = false;
4629     unsigned int getxml_flags = VIR_DOMAIN_XML_SECURE;
4630     unsigned int define_flags = 0;
4631     virshControl *priv = ctl->privData;
4632 
4633     if (vshCommandOptBool(cmd, "running"))
4634         define_flags |= VIR_DOMAIN_SAVE_RUNNING;
4635     if (vshCommandOptBool(cmd, "paused"))
4636         define_flags |= VIR_DOMAIN_SAVE_PAUSED;
4637 
4638     /* Normally, we let the API reject mutually exclusive flags.
4639      * However, in the edit cycle, we let the user retry if the define
4640      * step fails, but the define step will always fail on invalid
4641      * flags, so we reject it up front to avoid looping.  */
4642     VSH_EXCLUSIVE_OPTIONS("running", "paused");
4643 
4644     if (vshCommandOptStringReq(ctl, cmd, "file", &file) < 0)
4645         return false;
4646 
4647 #define EDIT_GET_XML \
4648     virDomainSaveImageGetXMLDesc(priv->conn, file, getxml_flags)
4649 #define EDIT_NOT_CHANGED \
4650     do { \
4651         vshPrintExtra(ctl, _("Saved image %s XML configuration " \
4652                              "not changed.\n"), file); \
4653         ret = true; \
4654         goto edit_cleanup; \
4655     } while (0)
4656 #define EDIT_DEFINE \
4657     (virDomainSaveImageDefineXML(priv->conn, file, doc_edited, define_flags) == 0)
4658 #include "virsh-edit.c"
4659 
4660     vshPrintExtra(ctl, _("State file %s edited.\n"), file);
4661     ret = true;
4662 
4663  cleanup:
4664     return ret;
4665 }
4666 
4667 /*
4668  * "managedsave" command
4669  */
4670 static const vshCmdInfo info_managedsave[] = {
4671     {.name = "help",
4672      .data = N_("managed save of a domain state")
4673     },
4674     {.name = "desc",
4675      .data = N_("Save and destroy a running domain, so it can be restarted from\n"
4676                 "    the same state at a later time.  When the virsh 'start'\n"
4677                 "    command is next run for the domain, it will automatically\n"
4678                 "    be started from this saved state.")
4679     },
4680     {.name = NULL}
4681 };
4682 
4683 static const vshCmdOptDef opts_managedsave[] = {
4684     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
4685     {.name = "bypass-cache",
4686      .type = VSH_OT_BOOL,
4687      .help = N_("avoid file system cache when saving")
4688     },
4689     {.name = "running",
4690      .type = VSH_OT_BOOL,
4691      .help = N_("set domain to be running on next start")
4692     },
4693     {.name = "paused",
4694      .type = VSH_OT_BOOL,
4695      .help = N_("set domain to be paused on next start")
4696     },
4697     {.name = "verbose",
4698      .type = VSH_OT_BOOL,
4699      .help = N_("display the progress of save")
4700     },
4701     {.name = NULL}
4702 };
4703 
4704 static void
doManagedsave(void * opaque)4705 doManagedsave(void *opaque)
4706 {
4707     virshCtrlData *data = opaque;
4708     vshControl *ctl = data->ctl;
4709     const vshCmd *cmd = data->cmd;
4710     g_autoptr(virshDomain) dom = NULL;
4711     const char *name;
4712     unsigned int flags = 0;
4713 #ifndef WIN32
4714     sigset_t sigmask, oldsigmask;
4715 
4716     sigemptyset(&sigmask);
4717     sigaddset(&sigmask, SIGINT);
4718     if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) != 0)
4719         goto out_sig;
4720 #endif /* !WIN32 */
4721 
4722     if (vshCommandOptBool(cmd, "bypass-cache"))
4723         flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
4724     if (vshCommandOptBool(cmd, "running"))
4725         flags |= VIR_DOMAIN_SAVE_RUNNING;
4726     if (vshCommandOptBool(cmd, "paused"))
4727         flags |= VIR_DOMAIN_SAVE_PAUSED;
4728 
4729     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
4730         goto out;
4731 
4732     if (virDomainManagedSave(dom, flags) < 0) {
4733         vshError(ctl, _("Failed to save domain '%s' state"), name);
4734         goto out;
4735     }
4736 
4737     data->ret = 0;
4738  out:
4739 #ifndef WIN32
4740     pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
4741  out_sig:
4742 #endif /* !WIN32 */
4743     g_main_loop_quit(data->eventLoop);
4744 }
4745 
4746 static bool
cmdManagedSave(vshControl * ctl,const vshCmd * cmd)4747 cmdManagedSave(vshControl *ctl, const vshCmd *cmd)
4748 {
4749     g_autoptr(virshDomain) dom = NULL;
4750     bool verbose = false;
4751     const char *name = NULL;
4752     virThread workerThread;
4753     g_autoptr(GMainContext) eventCtxt = g_main_context_new();
4754     g_autoptr(GMainLoop) eventLoop = g_main_loop_new(eventCtxt, FALSE);
4755     virshCtrlData data = {
4756         .ctl = ctl,
4757         .cmd = cmd,
4758         .eventLoop = eventLoop,
4759         .ret = -1,
4760     };
4761 
4762     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
4763         return false;
4764 
4765     if (vshCommandOptBool(cmd, "verbose"))
4766         verbose = true;
4767 
4768     if (virThreadCreate(&workerThread,
4769                         true,
4770                         doManagedsave,
4771                         &data) < 0)
4772         return false;
4773 
4774     virshWatchJob(ctl, dom, verbose, eventLoop,
4775                   &data.ret, 0, NULL, NULL, _("Managedsave"));
4776 
4777     virThreadJoin(&workerThread);
4778 
4779     if (!data.ret)
4780         vshPrintExtra(ctl, _("\nDomain '%s' state saved by libvirt\n"), name);
4781 
4782     return !data.ret;
4783 }
4784 
4785 /*
4786  * "managedsave-remove" command
4787  */
4788 static const vshCmdInfo info_managedsaveremove[] = {
4789     {.name = "help",
4790      .data = N_("Remove managed save of a domain")
4791     },
4792     {.name = "desc",
4793      .data = N_("Remove an existing managed save state file from a domain")
4794     },
4795     {.name = NULL}
4796 };
4797 
4798 static const vshCmdOptDef opts_managedsaveremove[] = {
4799     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE),
4800     {.name = NULL}
4801 };
4802 
4803 static bool
cmdManagedSaveRemove(vshControl * ctl,const vshCmd * cmd)4804 cmdManagedSaveRemove(vshControl *ctl, const vshCmd *cmd)
4805 {
4806     g_autoptr(virshDomain) dom = NULL;
4807     const char *name;
4808     int hassave;
4809 
4810     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
4811         return false;
4812 
4813     hassave = virDomainHasManagedSaveImage(dom, 0);
4814     if (hassave < 0) {
4815         vshError(ctl, "%s", _("Failed to check for domain managed save image"));
4816         return false;
4817     }
4818 
4819     if (hassave == 0) {
4820         vshPrintExtra(ctl, _("Domain '%s' has no manage save image; removal skipped"),
4821                       name);
4822         return true;
4823     }
4824 
4825     if (virDomainManagedSaveRemove(dom, 0) < 0) {
4826         vshError(ctl, _("Failed to remove managed save image for domain '%s'"),
4827                  name);
4828         return false;
4829     }
4830 
4831     vshPrintExtra(ctl, _("Removed managedsave image for domain '%s'"), name);
4832 
4833     return true;
4834 }
4835 
4836 /*
4837  * "managedsave-edit" command
4838  */
4839 static const vshCmdInfo info_managed_save_edit[] = {
4840     {.name = "help",
4841      .data = N_("edit XML for a domain's managed save state file")
4842     },
4843     {.name = "desc",
4844      .data = N_("Edit the domain XML associated with the managed save state file")
4845     },
4846     {.name = NULL}
4847 };
4848 
4849 static const vshCmdOptDef opts_managed_save_edit[] = {
4850     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE),
4851     {.name = "running",
4852      .type = VSH_OT_BOOL,
4853      .help = N_("set domain to be running on start")
4854     },
4855     {.name = "paused",
4856      .type = VSH_OT_BOOL,
4857      .help = N_("set domain to be paused on start")
4858     },
4859     {.name = NULL}
4860 };
4861 
4862 static bool
cmdManagedSaveEdit(vshControl * ctl,const vshCmd * cmd)4863 cmdManagedSaveEdit(vshControl *ctl, const vshCmd *cmd)
4864 {
4865     bool ret = false;
4866     g_autoptr(virshDomain) dom = NULL;
4867     unsigned int getxml_flags = VIR_DOMAIN_XML_SECURE;
4868     unsigned int define_flags = 0;
4869 
4870     if (vshCommandOptBool(cmd, "running"))
4871         define_flags |= VIR_DOMAIN_SAVE_RUNNING;
4872     if (vshCommandOptBool(cmd, "paused"))
4873         define_flags |= VIR_DOMAIN_SAVE_PAUSED;
4874 
4875     VSH_EXCLUSIVE_OPTIONS("running", "paused");
4876 
4877     dom = virshCommandOptDomain(ctl, cmd, NULL);
4878     if (dom == NULL)
4879         return false;
4880 
4881 #define EDIT_GET_XML virDomainManagedSaveGetXMLDesc(dom, getxml_flags)
4882 #define EDIT_NOT_CHANGED \
4883     do { \
4884         vshPrintExtra(ctl, _("Managed save image of domain '%s' XML configuration " \
4885                              "not changed.\n"), virDomainGetName(dom)); \
4886         ret = true; \
4887         goto edit_cleanup; \
4888     } while (0)
4889 #define EDIT_DEFINE \
4890     (virDomainManagedSaveDefineXML(dom, doc_edited, define_flags) == 0)
4891 #include "virsh-edit.c"
4892 
4893     vshPrintExtra(ctl, _("Managed save image of Domain '%s' XML configuration edited.\n"),
4894                   virDomainGetName(dom));
4895     ret = true;
4896 
4897  cleanup:
4898     return ret;
4899 }
4900 
4901 /*
4902  * "managedsave-dumpxml" command
4903  */
4904 static const vshCmdInfo info_managed_save_dumpxml[] = {
4905    {.name = "help",
4906     .data = N_("Domain information of managed save state file in XML")
4907    },
4908    {.name = "desc",
4909     .data = N_("Dump XML of domain information for a managed save state file to stdout.")
4910    },
4911    {.name = NULL}
4912 };
4913 
4914 static const vshCmdOptDef opts_managed_save_dumpxml[] = {
4915     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE),
4916     {.name = "security-info",
4917      .type = VSH_OT_BOOL,
4918      .help = N_("include security sensitive information in XML dump")
4919     },
4920     {.name = NULL}
4921 };
4922 
4923 static bool
cmdManagedSaveDumpxml(vshControl * ctl,const vshCmd * cmd)4924 cmdManagedSaveDumpxml(vshControl *ctl, const vshCmd *cmd)
4925 {
4926     g_autoptr(virshDomain) dom = NULL;
4927     unsigned int flags = 0;
4928     g_autofree char *xml = NULL;
4929 
4930     if (vshCommandOptBool(cmd, "security-info"))
4931         flags |= VIR_DOMAIN_XML_SECURE;
4932 
4933     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
4934         return false;
4935 
4936     if (!(xml = virDomainManagedSaveGetXMLDesc(dom, flags)))
4937         return false;
4938 
4939     vshPrint(ctl, "%s", xml);
4940     return true;
4941 }
4942 
4943 /*
4944  * "managedsave-define" command
4945  */
4946 static const vshCmdInfo info_managed_save_define[] = {
4947     {.name = "help",
4948      .data = N_("redefine the XML for a domain's managed save state file")
4949     },
4950     {.name = "desc",
4951      .data = N_("Replace the domain XML associated with a managed save state file")
4952     },
4953     {.name = NULL}
4954 };
4955 
4956 static const vshCmdOptDef opts_managed_save_define[] = {
4957     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE),
4958     {.name = "xml",
4959      .type = VSH_OT_DATA,
4960      .flags = VSH_OFLAG_REQ,
4961      .completer = virshCompletePathLocalExisting,
4962      .help = N_("filename containing updated XML for the target")
4963     },
4964     {.name = "running",
4965      .type = VSH_OT_BOOL,
4966      .help = N_("set domain to be running on start")
4967     },
4968     {.name = "paused",
4969      .type = VSH_OT_BOOL,
4970      .help = N_("set domain to be paused on start")
4971     },
4972     {.name = NULL}
4973 };
4974 
4975 static bool
cmdManagedSaveDefine(vshControl * ctl,const vshCmd * cmd)4976 cmdManagedSaveDefine(vshControl *ctl, const vshCmd *cmd)
4977 {
4978     g_autoptr(virshDomain) dom = NULL;
4979     const char *xmlfile = NULL;
4980     g_autofree char *xml = NULL;
4981     unsigned int flags = 0;
4982 
4983     if (vshCommandOptBool(cmd, "running"))
4984         flags |= VIR_DOMAIN_SAVE_RUNNING;
4985     if (vshCommandOptBool(cmd, "paused"))
4986         flags |= VIR_DOMAIN_SAVE_PAUSED;
4987 
4988     VSH_EXCLUSIVE_OPTIONS("running", "paused");
4989 
4990     if (vshCommandOptStringReq(ctl, cmd, "xml", &xmlfile) < 0)
4991         return false;
4992 
4993     if (virFileReadAll(xmlfile, VSH_MAX_XML_FILE, &xml) < 0)
4994         return false;
4995 
4996     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
4997         return false;
4998 
4999     if (virDomainManagedSaveDefineXML(dom, xml, flags) < 0) {
5000         vshError(ctl, _("Failed to update %s XML configuration"),
5001                         virDomainGetName(dom));
5002         return false;
5003     }
5004 
5005     vshPrintExtra(ctl, _("Managed save state file of domain '%s' updated.\n"),
5006                          virDomainGetName(dom));
5007     return true;
5008 }
5009 
5010 /*
5011  * "schedinfo" command
5012  */
5013 static const vshCmdInfo info_schedinfo[] = {
5014     {.name = "help",
5015      .data = N_("show/set scheduler parameters")
5016     },
5017     {.name = "desc",
5018      .data = N_("Show/Set scheduler parameters.")
5019     },
5020     {.name = NULL}
5021 };
5022 
5023 static const vshCmdOptDef opts_schedinfo[] = {
5024     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
5025     {.name = "weight",
5026      .type = VSH_OT_INT,
5027      .flags = VSH_OFLAG_REQ_OPT,
5028      .help = N_("weight for XEN_CREDIT")
5029     },
5030     {.name = "cap",
5031      .type = VSH_OT_INT,
5032      .flags = VSH_OFLAG_REQ_OPT,
5033      .help = N_("cap for XEN_CREDIT")
5034     },
5035     VIRSH_COMMON_OPT_CURRENT(N_("get/set current scheduler info")),
5036     VIRSH_COMMON_OPT_CONFIG(N_("get/set value to be used on next boot")),
5037     VIRSH_COMMON_OPT_LIVE(N_("get/set value from running domain")),
5038     {.name = "set",
5039      .type = VSH_OT_ARGV,
5040      .flags = VSH_OFLAG_NONE,
5041      .help = N_("parameter=value")
5042     },
5043     {.name = NULL}
5044 };
5045 
5046 static int
cmdSchedInfoUpdateOne(vshControl * ctl,virTypedParameterPtr src_params,int nsrc_params,virTypedParameterPtr * params,int * nparams,int * maxparams,const char * field,const char * value)5047 cmdSchedInfoUpdateOne(vshControl *ctl,
5048                       virTypedParameterPtr src_params, int nsrc_params,
5049                       virTypedParameterPtr *params,
5050                       int *nparams, int *maxparams,
5051                       const char *field, const char *value)
5052 {
5053     virTypedParameterPtr param;
5054     size_t i;
5055 
5056     for (i = 0; i < nsrc_params; i++) {
5057         param = &(src_params[i]);
5058 
5059         if (STRNEQ(field, param->field))
5060             continue;
5061 
5062         if (virTypedParamsAddFromString(params, nparams, maxparams,
5063                                         field, param->type,
5064                                         value) < 0) {
5065             vshSaveLibvirtError();
5066             return -1;
5067         }
5068         return 0;
5069     }
5070 
5071     vshError(ctl, _("invalid scheduler option: %s"), field);
5072     return -1;
5073 }
5074 
5075 static int
cmdSchedInfoUpdate(vshControl * ctl,const vshCmd * cmd,virTypedParameterPtr src_params,int nsrc_params,virTypedParameterPtr * update_params)5076 cmdSchedInfoUpdate(vshControl *ctl, const vshCmd *cmd,
5077                    virTypedParameterPtr src_params, int nsrc_params,
5078                    virTypedParameterPtr *update_params)
5079 {
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;
5088 
5089     while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
5090         g_autofree char *set_field = g_strdup(opt->data);
5091 
5092         if (!(set_val = strchr(set_field, '='))) {
5093             vshError(ctl, "%s", _("Invalid syntax for --set, "
5094                                   "expecting name=value"));
5095             goto cleanup;
5096         }
5097 
5098         *set_val = '\0';
5099         set_val++;
5100 
5101         if (cmdSchedInfoUpdateOne(ctl, src_params, nsrc_params,
5102                                   &params, &nparams, &maxparams,
5103                                   set_field, set_val) < 0)
5104             goto cleanup;
5105     }
5106 
5107     rv = vshCommandOptStringReq(ctl, cmd, "cap", &val);
5108     if (rv < 0 ||
5109         (val &&
5110          cmdSchedInfoUpdateOne(ctl, src_params, nsrc_params,
5111                                &params, &nparams, &maxparams,
5112                                "cap", val) < 0))
5113         goto cleanup;
5114 
5115     rv = vshCommandOptStringReq(ctl, cmd, "weight", &val);
5116     if (rv < 0 ||
5117         (val &&
5118          cmdSchedInfoUpdateOne(ctl, src_params, nsrc_params,
5119                                &params, &nparams, &maxparams,
5120                                "weight", val) < 0))
5121         goto cleanup;
5122 
5123     ret = nparams;
5124     *update_params = g_steal_pointer(&params);
5125 
5126  cleanup:
5127     virTypedParamsFree(params, nparams);
5128     return ret;
5129 }
5130 
5131 static bool
cmdSchedinfo(vshControl * ctl,const vshCmd * cmd)5132 cmdSchedinfo(vshControl *ctl, const vshCmd *cmd)
5133 {
5134     g_autofree char *schedulertype = NULL;
5135     g_autoptr(virshDomain) dom = NULL;
5136     virTypedParameterPtr params = NULL;
5137     virTypedParameterPtr updates = NULL;
5138     int nparams = 0;
5139     int nupdates = 0;
5140     size_t i;
5141     bool ret_val = false;
5142     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
5143     bool current = vshCommandOptBool(cmd, "current");
5144     bool config = vshCommandOptBool(cmd, "config");
5145     bool live = vshCommandOptBool(cmd, "live");
5146 
5147     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
5148     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
5149 
5150     if (config)
5151         flags |= VIR_DOMAIN_AFFECT_CONFIG;
5152     if (live)
5153         flags |= VIR_DOMAIN_AFFECT_LIVE;
5154 
5155     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
5156         return false;
5157 
5158     /* Print SchedulerType */
5159     if (!(schedulertype = virDomainGetSchedulerType(dom, &nparams))) {
5160         vshPrint(ctl, "%-15s: %s\n", _("Scheduler"), _("Unknown"));
5161         goto cleanup;
5162     }
5163 
5164     vshPrint(ctl, "%-15s: %s\n", _("Scheduler"), schedulertype);
5165 
5166     if (!nparams)
5167         goto cleanup;
5168 
5169     params = g_new0(virTypedParameter, nparams);
5170     memset(params, 0, sizeof(*params) * nparams);
5171 
5172     if (flags || current) {
5173         /* We cannot query both live and config at once, so settle
5174            on current in that case.  If we are setting, then the
5175            two values should match when we re-query; otherwise, we
5176            report the error later.  */
5177         if (virDomainGetSchedulerParametersFlags(dom, params, &nparams,
5178                                                  ((live && config) ? 0 : flags)) == -1)
5179             goto cleanup;
5180     } else {
5181         if (virDomainGetSchedulerParameters(dom, params, &nparams) == -1)
5182             goto cleanup;
5183     }
5184 
5185     /* See if any params are being set */
5186     if ((nupdates = cmdSchedInfoUpdate(ctl, cmd, params, nparams,
5187                                        &updates)) < 0)
5188         goto cleanup;
5189 
5190     /* Update parameters & refresh data */
5191     if (nupdates > 0) {
5192         if (flags || current) {
5193             if (virDomainSetSchedulerParametersFlags(dom, updates,
5194                                                      nupdates, flags) == -1)
5195                 goto cleanup;
5196 
5197             if (virDomainGetSchedulerParametersFlags(dom, params, &nparams,
5198                                                      ((live && config) ? 0 : flags)) == -1)
5199                 goto cleanup;
5200         } else {
5201             if (virDomainSetSchedulerParameters(dom, updates, nupdates) == -1)
5202                 goto cleanup;
5203 
5204             if (virDomainGetSchedulerParameters(dom, params, &nparams) == -1)
5205                 goto cleanup;
5206         }
5207     } else {
5208         /* When not doing --set, --live and --config do not mix.  */
5209         if (live && config) {
5210             vshError(ctl, "%s",
5211                      _("cannot query both live and config at once"));
5212             goto cleanup;
5213         }
5214     }
5215 
5216     ret_val = true;
5217     for (i = 0; i < nparams; i++) {
5218         g_autofree char *str = vshGetTypedParamValue(ctl, &params[i]);
5219         vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
5220     }
5221 
5222  cleanup:
5223     virTypedParamsFree(params, nparams);
5224     virTypedParamsFree(updates, nupdates);
5225     return ret_val;
5226 }
5227 
5228 /*
5229  * "restore" command
5230  */
5231 static const vshCmdInfo info_restore[] = {
5232     {.name = "help",
5233      .data = N_("restore a domain from a saved state in a file")
5234     },
5235     {.name = "desc",
5236      .data = N_("Restore a domain.")
5237     },
5238     {.name = NULL}
5239 };
5240 
5241 static const vshCmdOptDef opts_restore[] = {
5242     {.name = "file",
5243      .type = VSH_OT_DATA,
5244      .flags = VSH_OFLAG_REQ,
5245      .help = N_("the state to restore")
5246     },
5247     {.name = "bypass-cache",
5248      .type = VSH_OT_BOOL,
5249      .help = N_("avoid file system cache when restoring")
5250     },
5251     {.name = "xml",
5252      .type = VSH_OT_STRING,
5253      .completer = virshCompletePathLocalExisting,
5254      .help = N_("filename containing updated XML for the target")
5255     },
5256     {.name = "running",
5257      .type = VSH_OT_BOOL,
5258      .help = N_("restore domain into running state")
5259     },
5260     {.name = "paused",
5261      .type = VSH_OT_BOOL,
5262      .help = N_("restore domain into paused state")
5263     },
5264     {.name = NULL}
5265 };
5266 
5267 static bool
cmdRestore(vshControl * ctl,const vshCmd * cmd)5268 cmdRestore(vshControl *ctl, const vshCmd *cmd)
5269 {
5270     const char *from = NULL;
5271     unsigned int flags = 0;
5272     const char *xmlfile = NULL;
5273     g_autofree char *xml = NULL;
5274     virshControl *priv = ctl->privData;
5275 
5276     if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
5277         return false;
5278 
5279     if (vshCommandOptBool(cmd, "bypass-cache"))
5280         flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
5281     if (vshCommandOptBool(cmd, "running"))
5282         flags |= VIR_DOMAIN_SAVE_RUNNING;
5283     if (vshCommandOptBool(cmd, "paused"))
5284         flags |= VIR_DOMAIN_SAVE_PAUSED;
5285 
5286     if (vshCommandOptStringReq(ctl, cmd, "xml", &xmlfile) < 0)
5287         return false;
5288 
5289     if (xmlfile &&
5290         virFileReadAll(xmlfile, VSH_MAX_XML_FILE, &xml) < 0)
5291         return false;
5292 
5293     if (((flags || xml)
5294          ? virDomainRestoreFlags(priv->conn, from, xml, flags)
5295          : virDomainRestore(priv->conn, from)) < 0) {
5296         vshError(ctl, _("Failed to restore domain from %s"), from);
5297         return false;
5298     }
5299 
5300     vshPrintExtra(ctl, _("Domain restored from %s\n"), from);
5301     return true;
5302 }
5303 
5304 /*
5305  * "dump" command
5306  */
5307 static const vshCmdInfo info_dump[] = {
5308     {.name = "help",
5309      .data = N_("dump the core of a domain to a file for analysis")
5310     },
5311     {.name = "desc",
5312      .data = N_("Core dump a domain.")
5313     },
5314     {.name = NULL}
5315 };
5316 
5317 static const vshCmdOptDef opts_dump[] = {
5318     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
5319     {.name = "file",
5320      .type = VSH_OT_DATA,
5321      .flags = VSH_OFLAG_REQ,
5322      .help = N_("where to dump the core")
5323     },
5324     VIRSH_COMMON_OPT_LIVE(N_("perform a live core dump if supported")),
5325     {.name = "crash",
5326      .type = VSH_OT_BOOL,
5327      .help = N_("crash the domain after core dump")
5328     },
5329     {.name = "bypass-cache",
5330      .type = VSH_OT_BOOL,
5331      .help = N_("avoid file system cache when dumping")
5332     },
5333     {.name = "reset",
5334      .type = VSH_OT_BOOL,
5335      .help = N_("reset the domain after core dump")
5336     },
5337     {.name = "verbose",
5338      .type = VSH_OT_BOOL,
5339      .help = N_("display the progress of dump")
5340     },
5341     {.name = "memory-only",
5342      .type = VSH_OT_BOOL,
5343      .help = N_("dump domain's memory only")
5344     },
5345     {.name = "format",
5346      .type = VSH_OT_STRING,
5347      .flags = VSH_OFLAG_NONE,
5348      .completer = virshDomainCoreDumpFormatCompleter,
5349      .help = N_("specify the format of memory-only dump")
5350     },
5351     {.name = NULL}
5352 };
5353 
5354 VIR_ENUM_IMPL(virshDomainCoreDumpFormat,
5355               VIR_DOMAIN_CORE_DUMP_FORMAT_LAST,
5356               "elf",
5357               "kdump-zlib",
5358               "kdump-lzo",
5359               "kdump-snappy",
5360               "win-dmp");
5361 
5362 static void
doDump(void * opaque)5363 doDump(void *opaque)
5364 {
5365     virshCtrlData *data = opaque;
5366     vshControl *ctl = data->ctl;
5367     const vshCmd *cmd = data->cmd;
5368     g_autoptr(virshDomain) dom = NULL;
5369     const char *name = NULL;
5370     const char *to = NULL;
5371     unsigned int flags = 0;
5372     const char *format = NULL;
5373     int dumpformat = VIR_DOMAIN_CORE_DUMP_FORMAT_RAW;
5374 #ifndef WIN32
5375     sigset_t sigmask, oldsigmask;
5376 
5377     sigemptyset(&sigmask);
5378     sigaddset(&sigmask, SIGINT);
5379     if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) != 0)
5380         goto out_sig;
5381 #endif /* !WIN32 */
5382 
5383     if (vshCommandOptStringReq(ctl, cmd, "file", &to) < 0)
5384         goto out;
5385 
5386     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
5387         goto out;
5388 
5389     if (vshCommandOptBool(cmd, "live"))
5390         flags |= VIR_DUMP_LIVE;
5391     if (vshCommandOptBool(cmd, "crash"))
5392         flags |= VIR_DUMP_CRASH;
5393     if (vshCommandOptBool(cmd, "bypass-cache"))
5394         flags |= VIR_DUMP_BYPASS_CACHE;
5395     if (vshCommandOptBool(cmd, "reset"))
5396         flags |= VIR_DUMP_RESET;
5397     if (vshCommandOptBool(cmd, "memory-only"))
5398         flags |= VIR_DUMP_MEMORY_ONLY;
5399 
5400     if (vshCommandOptBool(cmd, "format")) {
5401         if (!(flags & VIR_DUMP_MEMORY_ONLY)) {
5402             vshError(ctl, "%s", _("--format only works with --memory-only"));
5403             goto out;
5404         }
5405 
5406         if (vshCommandOptStringQuiet(ctl, cmd, "format", &format) > 0) {
5407             if ((dumpformat = virshDomainCoreDumpFormatTypeFromString(format)) < 0) {
5408                 vshError(ctl, _("format '%s' is not supported, expecting "
5409                                 "'kdump-zlib', 'kdump-lzo', 'kdump-snappy', "
5410                                 "'win-dmp' or 'elf'"), format);
5411                 goto out;
5412             }
5413         }
5414     }
5415 
5416     if (dumpformat != VIR_DOMAIN_CORE_DUMP_FORMAT_RAW) {
5417         if (virDomainCoreDumpWithFormat(dom, to, dumpformat, flags) < 0) {
5418             vshError(ctl, _("Failed to core dump domain '%s' to %s"), name, to);
5419             goto out;
5420         }
5421     } else {
5422         if (virDomainCoreDump(dom, to, flags) < 0) {
5423             vshError(ctl, _("Failed to core dump domain '%s' to %s"), name, to);
5424             goto out;
5425         }
5426     }
5427 
5428     data->ret = 0;
5429  out:
5430 #ifndef WIN32
5431     pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
5432  out_sig:
5433 #endif /* !WIN32 */
5434     g_main_loop_quit(data->eventLoop);
5435 }
5436 
5437 static bool
cmdDump(vshControl * ctl,const vshCmd * cmd)5438 cmdDump(vshControl *ctl, const vshCmd *cmd)
5439 {
5440     g_autoptr(virshDomain) dom = NULL;
5441     bool verbose = false;
5442     const char *name = NULL;
5443     const char *to = NULL;
5444     virThread workerThread;
5445     g_autoptr(GMainContext) eventCtxt = g_main_context_new();
5446     g_autoptr(GMainLoop) eventLoop = g_main_loop_new(eventCtxt, FALSE);
5447     virshCtrlData data = {
5448         .ctl = ctl,
5449         .cmd = cmd,
5450         .eventLoop = eventLoop,
5451         .ret = -1,
5452     };
5453 
5454     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
5455         return false;
5456 
5457     if (vshCommandOptStringReq(ctl, cmd, "file", &to) < 0)
5458         return false;
5459 
5460     if (vshCommandOptBool(cmd, "verbose"))
5461         verbose = true;
5462 
5463     if (virThreadCreate(&workerThread,
5464                         true,
5465                         doDump,
5466                         &data) < 0)
5467         return false;
5468 
5469     virshWatchJob(ctl, dom, verbose, eventLoop,
5470                   &data.ret, 0, NULL, NULL, _("Dump"));
5471 
5472     virThreadJoin(&workerThread);
5473 
5474     if (data.ret)
5475         return false;
5476 
5477     vshPrintExtra(ctl, _("\nDomain '%s' dumped to %s\n"), name, to);
5478 
5479     return true;
5480 }
5481 
5482 static const vshCmdInfo info_screenshot[] = {
5483     {.name = "help",
5484      .data = N_("take a screenshot of a current domain console and store it "
5485                 "into a file")
5486     },
5487     {.name = "desc",
5488      .data = N_("screenshot of a current domain console")
5489     },
5490     {.name = NULL}
5491 };
5492 
5493 static const vshCmdOptDef opts_screenshot[] = {
5494     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
5495     {.name = "file",
5496      .type = VSH_OT_STRING,
5497      .completer = virshCompletePathLocalExisting,
5498      .help = N_("where to store the screenshot")
5499     },
5500     {.name = "screen",
5501      .type = VSH_OT_INT,
5502      .help = N_("ID of a screen to take screenshot of")
5503     },
5504     {.name = NULL}
5505 };
5506 
5507 /**
5508  * Generate string: '<domain name>-<timestamp>[<extension>]'
5509  */
5510 static char *
virshGenFileName(vshControl * ctl,virDomainPtr dom,const char * mime)5511 virshGenFileName(vshControl *ctl, virDomainPtr dom, const char *mime)
5512 {
5513     g_autoptr(GDateTime) now = g_date_time_new_now_local();
5514     g_autofree char *nowstr = NULL;
5515     const char *ext = NULL;
5516 
5517     if (!dom) {
5518         vshError(ctl, "%s", _("Invalid domain supplied"));
5519         return NULL;
5520     }
5521 
5522     if (STREQ(mime, "image/x-portable-pixmap"))
5523         ext = ".ppm";
5524     else if (STREQ(mime, "image/png"))
5525         ext = ".png";
5526     /* add mime type here */
5527 
5528     nowstr = g_date_time_format(now, "%Y-%m-%d-%H:%M:%S");
5529 
5530     return g_strdup_printf("%s-%s%s", virDomainGetName(dom),
5531                            nowstr, NULLSTR_EMPTY(ext));
5532 }
5533 
5534 static bool
cmdScreenshot(vshControl * ctl,const vshCmd * cmd)5535 cmdScreenshot(vshControl *ctl, const vshCmd *cmd)
5536 {
5537     g_autoptr(virshDomain) dom = NULL;
5538     const char *name = NULL;
5539     char *file = NULL;
5540     VIR_AUTOCLOSE fd = -1;
5541     g_autoptr(virshStream) st = NULL;
5542     unsigned int screen = 0;
5543     unsigned int flags = 0; /* currently unused */
5544     bool ret = false;
5545     bool created = false;
5546     bool generated = false;
5547     char *mime = NULL;
5548     virshControl *priv = ctl->privData;
5549     virshStreamCallbackData cbdata;
5550 
5551     if (vshCommandOptStringReq(ctl, cmd, "file", (const char **) &file) < 0)
5552         return false;
5553 
5554     if (vshCommandOptUInt(ctl, cmd, "screen", &screen) < 0)
5555         return false;
5556 
5557     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
5558         return false;
5559 
5560     if (!(st = virStreamNew(priv->conn, 0)))
5561         goto cleanup;
5562 
5563     mime = virDomainScreenshot(dom, st, screen, flags);
5564     if (!mime) {
5565         vshError(ctl, _("could not take a screenshot of %s"), name);
5566         goto cleanup;
5567     }
5568 
5569     if (!file) {
5570         if (!(file = virshGenFileName(ctl, dom, mime)))
5571             goto cleanup;
5572         generated = true;
5573     }
5574 
5575     if ((fd = open(file, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0) {
5576         if (errno != EEXIST ||
5577             (fd = open(file, O_WRONLY|O_TRUNC, 0666)) < 0) {
5578             vshError(ctl, _("cannot create file %s"), file);
5579             goto cleanup;
5580         }
5581     } else {
5582         created = true;
5583     }
5584 
5585     cbdata.ctl = ctl;
5586     cbdata.fd = fd;
5587 
5588     if (virStreamRecvAll(st, virshStreamSink, &cbdata) < 0) {
5589         vshError(ctl, _("could not receive data from domain '%s'"), name);
5590         goto cleanup;
5591     }
5592 
5593     if (VIR_CLOSE(fd) < 0) {
5594         vshError(ctl, _("cannot close file %s"), file);
5595         goto cleanup;
5596     }
5597 
5598     if (virStreamFinish(st) < 0) {
5599         vshError(ctl, _("cannot close stream on domain '%s'"), name);
5600         goto cleanup;
5601     }
5602 
5603     vshPrintExtra(ctl, _("Screenshot saved to %s, with type of %s"), file, mime);
5604     ret = true;
5605 
5606  cleanup:
5607     if (!ret && created)
5608         unlink(file);
5609     if (generated)
5610         VIR_FREE(file);
5611     VIR_FREE(mime);
5612     return ret;
5613 }
5614 
5615 /*
5616  * "set-lifecycle-action" command
5617  */
5618 static const vshCmdInfo info_setLifecycleAction[] = {
5619     {.name = "help",
5620      .data = N_("change lifecycle actions")
5621     },
5622     {.name = "desc",
5623      .data = N_("Change lifecycle actions for the guest domain.")
5624     },
5625     {.name = NULL}
5626 };
5627 
5628 static const vshCmdOptDef opts_setLifecycleAction[] = {
5629     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
5630     {.name = "type",
5631      .type = VSH_OT_DATA,
5632      .flags = VSH_OFLAG_REQ,
5633      .completer = virshDomainLifecycleCompleter,
5634      .help = N_("lifecycle type to modify")
5635     },
5636     {.name = "action",
5637      .type = VSH_OT_DATA,
5638      .flags = VSH_OFLAG_REQ,
5639      .completer = virshDomainLifecycleActionCompleter,
5640      .help = N_("lifecycle action to set")
5641     },
5642     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
5643     VIRSH_COMMON_OPT_DOMAIN_LIVE,
5644     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
5645     {.name = NULL}
5646 };
5647 
5648 VIR_ENUM_IMPL(virshDomainLifecycle,
5649               VIR_DOMAIN_LIFECYCLE_LAST,
5650               "poweroff",
5651               "reboot",
5652               "crash");
5653 
5654 VIR_ENUM_IMPL(virshDomainLifecycleAction,
5655               VIR_DOMAIN_LIFECYCLE_ACTION_LAST,
5656               "destroy",
5657               "restart",
5658               "rename-restart",
5659               "preserve",
5660               "coredump-destroy",
5661               "coredump-restart");
5662 
5663 static bool
cmdSetLifecycleAction(vshControl * ctl,const vshCmd * cmd)5664 cmdSetLifecycleAction(vshControl *ctl, const vshCmd *cmd)
5665 {
5666     g_autoptr(virshDomain) dom = NULL;
5667     bool config = vshCommandOptBool(cmd, "config");
5668     bool live = vshCommandOptBool(cmd, "live");
5669     bool current = vshCommandOptBool(cmd, "current");
5670     const char *typeStr;
5671     const char *actionStr;
5672     unsigned int type;
5673     unsigned int action;
5674     unsigned int flags = 0;
5675     int tmpVal;
5676 
5677     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
5678     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
5679 
5680     if (config)
5681         flags |= VIR_DOMAIN_AFFECT_CONFIG;
5682     if (live)
5683         flags |= VIR_DOMAIN_AFFECT_LIVE;
5684 
5685     if (vshCommandOptStringReq(ctl, cmd, "type", &typeStr) < 0 ||
5686         vshCommandOptStringReq(ctl, cmd, "action", &actionStr) < 0) {
5687         return false;
5688     }
5689 
5690     if ((tmpVal = virshDomainLifecycleTypeFromString(typeStr)) < 0) {
5691         vshError(ctl, _("Invalid lifecycle type '%s'."), typeStr);
5692         return false;
5693     }
5694     type = tmpVal;
5695 
5696     if ((tmpVal = virshDomainLifecycleActionTypeFromString(actionStr)) < 0) {
5697         vshError(ctl, _("Invalid lifecycle action '%s'."), actionStr);
5698         return false;
5699     }
5700     action = tmpVal;
5701 
5702     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
5703         return false;
5704 
5705     if (virDomainSetLifecycleAction(dom, type, action, flags) < 0) {
5706         vshError(ctl, "%s", _("Unable to change lifecycle action."));
5707         return false;
5708     }
5709     return true;
5710 }
5711 
5712 /*
5713  * "set-user-password" command
5714  */
5715 static const vshCmdInfo info_set_user_password[] = {
5716     {.name = "help",
5717      .data = N_("set the user password inside the domain")
5718     },
5719     {.name = "desc",
5720      .data = N_("changes the password of the specified user inside the domain")
5721     },
5722     {.name = NULL}
5723 };
5724 
5725 static const vshCmdOptDef opts_set_user_password[] = {
5726     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
5727     {.name = "user",
5728      .type = VSH_OT_DATA,
5729      .flags = VSH_OFLAG_REQ,
5730      .help = N_("the username")
5731     },
5732     {.name = "password",
5733      .type = VSH_OT_DATA,
5734      .flags = VSH_OFLAG_REQ,
5735      .completer = virshCompleteEmpty,
5736      .help = N_("the new password")
5737     },
5738     {.name = "encrypted",
5739      .type = VSH_OT_BOOL,
5740      .help = N_("the password is already encrypted")
5741     },
5742     {.name = NULL}
5743 };
5744 
5745 static bool
cmdSetUserPassword(vshControl * ctl,const vshCmd * cmd)5746 cmdSetUserPassword(vshControl *ctl, const vshCmd *cmd)
5747 {
5748     g_autoptr(virshDomain) dom = NULL;
5749     const char *name;
5750     const char *password = NULL;
5751     const char *user = NULL;
5752     unsigned int flags = 0;
5753 
5754     if (vshCommandOptBool(cmd, "encrypted"))
5755         flags = VIR_DOMAIN_PASSWORD_ENCRYPTED;
5756 
5757     if (vshCommandOptStringReq(ctl, cmd, "user", &user) < 0)
5758         return false;
5759 
5760     if (vshCommandOptStringReq(ctl, cmd, "password", &password) < 0)
5761         return false;
5762 
5763     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
5764         return false;
5765 
5766     if (virDomainSetUserPassword(dom, user, password, flags) < 0)
5767         return false;
5768 
5769     vshPrintExtra(ctl, _("Password set successfully for %s in %s"), user, name);
5770     return true;
5771 }
5772 /*
5773  * "resume" command
5774  */
5775 static const vshCmdInfo info_resume[] = {
5776     {.name = "help",
5777      .data = N_("resume a domain")
5778     },
5779     {.name = "desc",
5780      .data = N_("Resume a previously suspended domain.")
5781     },
5782     {.name = NULL}
5783 };
5784 
5785 static const vshCmdOptDef opts_resume[] = {
5786     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_PAUSED),
5787     {.name = NULL}
5788 };
5789 
5790 static bool
cmdResume(vshControl * ctl,const vshCmd * cmd)5791 cmdResume(vshControl *ctl, const vshCmd *cmd)
5792 {
5793     g_autoptr(virshDomain) dom = NULL;
5794     const char *name;
5795 
5796     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
5797         return false;
5798 
5799     if (virDomainResume(dom) != 0) {
5800         vshError(ctl, _("Failed to resume domain '%s'"), name);
5801         return false;
5802     }
5803 
5804     vshPrintExtra(ctl, _("Domain '%s' resumed\n"), name);
5805     return true;
5806 }
5807 
5808 /*
5809  * "shutdown" command
5810  */
5811 static const vshCmdInfo info_shutdown[] = {
5812     {.name = "help",
5813      .data = N_("gracefully shutdown a domain")
5814     },
5815     {.name = "desc",
5816      .data = N_("Run shutdown in the target domain.")
5817     },
5818     {.name = NULL}
5819 };
5820 
5821 static const vshCmdOptDef opts_shutdown[] = {
5822     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
5823     {.name = "mode",
5824      .type = VSH_OT_STRING,
5825      .completer = virshDomainShutdownModeCompleter,
5826      .help = N_("shutdown mode: acpi|agent|initctl|signal|paravirt")
5827     },
5828     {.name = NULL}
5829 };
5830 
5831 static bool
cmdShutdown(vshControl * ctl,const vshCmd * cmd)5832 cmdShutdown(vshControl *ctl, const vshCmd *cmd)
5833 {
5834     g_autoptr(virshDomain) dom = NULL;
5835     const char *name;
5836     const char *mode = NULL;
5837     int flags = 0;
5838     int rv;
5839     g_auto(GStrv) modes = NULL;
5840     char **tmp;
5841 
5842     if (vshCommandOptStringReq(ctl, cmd, "mode", &mode) < 0)
5843         return false;
5844 
5845     if (mode && !(modes = g_strsplit(mode, ",", 0))) {
5846         vshError(ctl, "%s", _("Cannot parse mode string"));
5847         return false;
5848     }
5849 
5850     tmp = modes;
5851     while (tmp && *tmp) {
5852         mode = *tmp;
5853         if (STREQ(mode, "acpi")) {
5854             flags |= VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN;
5855         } else if (STREQ(mode, "agent")) {
5856             flags |= VIR_DOMAIN_SHUTDOWN_GUEST_AGENT;
5857         } else if (STREQ(mode, "initctl")) {
5858             flags |= VIR_DOMAIN_SHUTDOWN_INITCTL;
5859         } else if (STREQ(mode, "signal")) {
5860             flags |= VIR_DOMAIN_SHUTDOWN_SIGNAL;
5861         } else if (STREQ(mode, "paravirt")) {
5862             flags |= VIR_DOMAIN_SHUTDOWN_PARAVIRT;
5863         } else {
5864             vshError(ctl, _("Unknown mode %s value, expecting "
5865                             "'acpi', 'agent', 'initctl', 'signal', "
5866                             "or 'paravirt'"), mode);
5867             return false;
5868         }
5869         tmp++;
5870     }
5871 
5872     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
5873         return false;
5874 
5875     if (flags)
5876         rv = virDomainShutdownFlags(dom, flags);
5877     else
5878         rv = virDomainShutdown(dom);
5879 
5880     if (rv != 0) {
5881         vshError(ctl, _("Failed to shutdown domain '%s'"), name);
5882         return false;
5883     }
5884 
5885     vshPrintExtra(ctl, _("Domain '%s' is being shutdown\n"), name);
5886     return true;
5887 }
5888 
5889 /*
5890  * "reboot" command
5891  */
5892 static const vshCmdInfo info_reboot[] = {
5893     {.name = "help",
5894      .data = N_("reboot a domain")
5895     },
5896     {.name = "desc",
5897      .data = N_("Run a reboot command in the target domain.")
5898     },
5899     {.name = NULL}
5900 };
5901 
5902 static const vshCmdOptDef opts_reboot[] = {
5903     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
5904     {.name = "mode",
5905      .type = VSH_OT_STRING,
5906      .completer = virshDomainShutdownModeCompleter,
5907      .help = N_("shutdown mode: acpi|agent|initctl|signal|paravirt")
5908     },
5909     {.name = NULL}
5910 };
5911 
5912 static bool
cmdReboot(vshControl * ctl,const vshCmd * cmd)5913 cmdReboot(vshControl *ctl, const vshCmd *cmd)
5914 {
5915     g_autoptr(virshDomain) dom = NULL;
5916     const char *name;
5917     const char *mode = NULL;
5918     int flags = 0;
5919     g_auto(GStrv) modes = NULL;
5920     char **tmp;
5921 
5922     if (vshCommandOptStringReq(ctl, cmd, "mode", &mode) < 0)
5923         return false;
5924 
5925     if (mode && !(modes = g_strsplit(mode, ",", 0))) {
5926         vshError(ctl, "%s", _("Cannot parse mode string"));
5927         return false;
5928     }
5929 
5930     tmp = modes;
5931     while (tmp && *tmp) {
5932         mode = *tmp;
5933         if (STREQ(mode, "acpi")) {
5934             flags |= VIR_DOMAIN_REBOOT_ACPI_POWER_BTN;
5935         } else if (STREQ(mode, "agent")) {
5936             flags |= VIR_DOMAIN_REBOOT_GUEST_AGENT;
5937         } else if (STREQ(mode, "initctl")) {
5938             flags |= VIR_DOMAIN_REBOOT_INITCTL;
5939         } else if (STREQ(mode, "signal")) {
5940             flags |= VIR_DOMAIN_REBOOT_SIGNAL;
5941         } else if (STREQ(mode, "paravirt")) {
5942             flags |= VIR_DOMAIN_REBOOT_PARAVIRT;
5943         } else {
5944             vshError(ctl, _("Unknown mode %s value, expecting "
5945                             "'acpi', 'agent', 'initctl', 'signal' "
5946                             "or 'paravirt'"), mode);
5947             return false;
5948         }
5949         tmp++;
5950     }
5951 
5952     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
5953         return false;
5954 
5955     if (virDomainReboot(dom, flags) != 0) {
5956         vshError(ctl, _("Failed to reboot domain '%s'"), name);
5957         return false;
5958     }
5959 
5960     vshPrintExtra(ctl, _("Domain '%s' is being rebooted\n"), name);
5961     return true;
5962 }
5963 
5964 /*
5965  * "reset" command
5966  */
5967 static const vshCmdInfo info_reset[] = {
5968     {.name = "help",
5969      .data = N_("reset a domain")
5970     },
5971     {.name = "desc",
5972      .data = N_("Reset the target domain as if by power button")
5973     },
5974     {.name = NULL}
5975 };
5976 
5977 static const vshCmdOptDef opts_reset[] = {
5978     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
5979     {.name = NULL}
5980 };
5981 
5982 static bool
cmdReset(vshControl * ctl,const vshCmd * cmd)5983 cmdReset(vshControl *ctl, const vshCmd *cmd)
5984 {
5985     g_autoptr(virshDomain) dom = NULL;
5986     const char *name;
5987 
5988     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
5989         return false;
5990 
5991     if (virDomainReset(dom, 0) != 0) {
5992         vshError(ctl, _("Failed to reset domain '%s'"), name);
5993         return false;
5994     }
5995 
5996     vshPrintExtra(ctl, _("Domain '%s' was reset\n"), name);
5997     return true;
5998 }
5999 
6000 /*
6001  * "domjobinfo" command
6002  */
6003 static const vshCmdInfo info_domjobinfo[] = {
6004     {.name = "help",
6005      .data = N_("domain job information")
6006     },
6007     {.name = "desc",
6008      .data = N_("Returns information about jobs running on a domain.")
6009     },
6010     {.name = NULL}
6011 };
6012 
6013 static const vshCmdOptDef opts_domjobinfo[] = {
6014     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
6015     {.name = "completed",
6016      .type = VSH_OT_BOOL,
6017      .help = N_("return statistics of a recently completed job")
6018     },
6019     {.name = "keep-completed",
6020      .type = VSH_OT_BOOL,
6021      .help = N_("don't destroy statistics of a recently completed job when reading")
6022     },
6023     {.name = "anystats",
6024      .type = VSH_OT_BOOL,
6025      .help = N_("print statistics for any kind of job (even failed ones)")
6026     },
6027     {.name = "rawstats",
6028      .type = VSH_OT_BOOL,
6029      .help = N_("print the raw data returned by libvirt")
6030     },
6031     {.name = NULL}
6032 };
6033 
6034 VIR_ENUM_DECL(virshDomainJob);
6035 VIR_ENUM_IMPL(virshDomainJob,
6036               VIR_DOMAIN_JOB_LAST,
6037               N_("None"),
6038               N_("Bounded"),
6039               N_("Unbounded"),
6040               N_("Completed"),
6041               N_("Failed"),
6042               N_("Cancelled"));
6043 
6044 static const char *
virshDomainJobToString(int type)6045 virshDomainJobToString(int type)
6046 {
6047     const char *str = virshDomainJobTypeToString(type);
6048     return str ? _(str) : _("unknown");
6049 }
6050 
6051 VIR_ENUM_DECL(virshDomainJobOperation);
6052 VIR_ENUM_IMPL(virshDomainJobOperation,
6053               VIR_DOMAIN_JOB_OPERATION_LAST,
6054               N_("Unknown"),
6055               N_("Start"),
6056               N_("Save"),
6057               N_("Restore"),
6058               N_("Incoming migration"),
6059               N_("Outgoing migration"),
6060               N_("Snapshot"),
6061               N_("Snapshot revert"),
6062               N_("Dump"),
6063               N_("Backup"),
6064 );
6065 
6066 static const char *
virshDomainJobOperationToString(int op)6067 virshDomainJobOperationToString(int op)
6068 {
6069     const char *str = virshDomainJobOperationTypeToString(op);
6070     return str ? _(str) : _("unknown");
6071 }
6072 
6073 
6074 static int
virshDomainJobStatsToDomainJobInfo(virTypedParameterPtr params,int nparams,virDomainJobInfo * info)6075 virshDomainJobStatsToDomainJobInfo(virTypedParameterPtr params,
6076                                    int nparams,
6077                                    virDomainJobInfo *info)
6078 {
6079     if (virTypedParamsGetULLong(params, nparams, VIR_DOMAIN_JOB_TIME_ELAPSED,
6080                                 &info->timeElapsed) < 0 ||
6081         virTypedParamsGetULLong(params, nparams, VIR_DOMAIN_JOB_TIME_REMAINING,
6082                                 &info->timeRemaining) < 0 ||
6083         virTypedParamsGetULLong(params, nparams, VIR_DOMAIN_JOB_DATA_TOTAL,
6084                                 &info->dataTotal) < 0 ||
6085         virTypedParamsGetULLong(params, nparams, VIR_DOMAIN_JOB_DATA_PROCESSED,
6086                                 &info->dataProcessed) < 0 ||
6087         virTypedParamsGetULLong(params, nparams, VIR_DOMAIN_JOB_DATA_REMAINING,
6088                                 &info->dataRemaining) < 0 ||
6089         virTypedParamsGetULLong(params, nparams, VIR_DOMAIN_JOB_MEMORY_TOTAL,
6090                                 &info->memTotal) < 0 ||
6091         virTypedParamsGetULLong(params, nparams, VIR_DOMAIN_JOB_MEMORY_PROCESSED,
6092                                 &info->memProcessed) < 0 ||
6093         virTypedParamsGetULLong(params, nparams, VIR_DOMAIN_JOB_MEMORY_REMAINING,
6094                                 &info->memRemaining) < 0 ||
6095         virTypedParamsGetULLong(params, nparams, VIR_DOMAIN_JOB_DISK_TOTAL,
6096                                 &info->fileTotal) < 0 ||
6097         virTypedParamsGetULLong(params, nparams, VIR_DOMAIN_JOB_DISK_PROCESSED,
6098                                 &info->fileProcessed) < 0 ||
6099         virTypedParamsGetULLong(params, nparams, VIR_DOMAIN_JOB_DISK_REMAINING,
6100                                 &info->fileRemaining) < 0) {
6101         vshSaveLibvirtError();
6102         return -1;
6103     }
6104 
6105     return 0;
6106 }
6107 
6108 
6109 static bool
cmdDomjobinfo(vshControl * ctl,const vshCmd * cmd)6110 cmdDomjobinfo(vshControl *ctl, const vshCmd *cmd)
6111 {
6112     virDomainJobInfo info;
6113     g_autoptr(virshDomain) dom = NULL;
6114     bool ret = false;
6115     const char *unit;
6116     double val;
6117     virTypedParameterPtr params = NULL;
6118     int nparams = 0;
6119     unsigned long long value;
6120     unsigned int flags = 0;
6121     int ivalue;
6122     const char *svalue;
6123     int op;
6124     int rc;
6125     size_t i;
6126     bool rawstats = vshCommandOptBool(cmd, "rawstats");
6127 
6128     VSH_REQUIRE_OPTION("keep-completed", "completed");
6129 
6130     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
6131         return false;
6132 
6133     if (vshCommandOptBool(cmd, "completed"))
6134         flags |= VIR_DOMAIN_JOB_STATS_COMPLETED;
6135 
6136     if (vshCommandOptBool(cmd, "keep-completed"))
6137         flags |= VIR_DOMAIN_JOB_STATS_KEEP_COMPLETED;
6138 
6139     memset(&info, 0, sizeof(info));
6140 
6141     rc = virDomainGetJobStats(dom, &info.type, &params, &nparams, flags);
6142     if (rc == 0) {
6143         if (virshDomainJobStatsToDomainJobInfo(params, nparams, &info) < 0)
6144             goto cleanup;
6145     } else if (last_error->code == VIR_ERR_NO_SUPPORT) {
6146         if (flags != 0 || rawstats) {
6147             vshError(ctl, "%s",
6148                      _("Optional flags or --rawstats are not supported by the daemon"));
6149             goto cleanup;
6150         }
6151         vshDebug(ctl, VSH_ERR_DEBUG, "detailed statistics not supported\n");
6152         vshResetLibvirtError();
6153         rc = virDomainGetJobInfo(dom, &info);
6154     }
6155     if (rc < 0)
6156         goto cleanup;
6157 
6158     if (rawstats) {
6159         vshPrint(ctl, "Job type: %d\n\n", info.type);
6160 
6161         for (i = 0; i < nparams; i++) {
6162             g_autofree char *par = virTypedParameterToString(&params[i]);
6163             vshPrint(ctl, "%s: %s\n", params[i].field, NULLSTR(par));
6164         }
6165 
6166         ret = true;
6167         goto cleanup;
6168     }
6169 
6170     vshPrint(ctl, "%-17s %-12s\n", _("Job type:"),
6171              virshDomainJobToString(info.type));
6172 
6173     if (info.type == VIR_DOMAIN_JOB_NONE) {
6174         ret = true;
6175         goto cleanup;
6176     }
6177 
6178     op = VIR_DOMAIN_JOB_OPERATION_UNKNOWN;
6179     if ((rc = virTypedParamsGetInt(params, nparams,
6180                                    VIR_DOMAIN_JOB_OPERATION, &op)) < 0)
6181         goto save_error;
6182 
6183     vshPrint(ctl, "%-17s %-12s\n", _("Operation:"),
6184              virshDomainJobOperationToString(op));
6185 
6186     if (!vshCommandOptBool(cmd, "anystats") &&
6187         info.type != VIR_DOMAIN_JOB_BOUNDED &&
6188         info.type != VIR_DOMAIN_JOB_UNBOUNDED &&
6189         (!(flags & VIR_DOMAIN_JOB_STATS_COMPLETED) ||
6190          info.type != VIR_DOMAIN_JOB_COMPLETED)) {
6191         ret = true;
6192         goto cleanup;
6193     }
6194 
6195     vshPrint(ctl, "%-17s %-12llu ms\n", _("Time elapsed:"), info.timeElapsed);
6196     if ((rc = virTypedParamsGetULLong(params, nparams,
6197                                       VIR_DOMAIN_JOB_TIME_ELAPSED_NET,
6198                                       &value)) < 0) {
6199         goto save_error;
6200     } else if (rc) {
6201         vshPrint(ctl, "%-17s %-12llu ms\n", _("Time elapsed w/o network:"),
6202                  value);
6203     }
6204 
6205     if (info.type == VIR_DOMAIN_JOB_BOUNDED)
6206         vshPrint(ctl, "%-17s %-12llu ms\n", _("Time remaining:"),
6207                  info.timeRemaining);
6208 
6209     if (info.dataTotal || info.dataRemaining || info.dataProcessed) {
6210         val = vshPrettyCapacity(info.dataProcessed, &unit);
6211         vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data processed:"), val, unit);
6212         val = vshPrettyCapacity(info.dataRemaining, &unit);
6213         vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data remaining:"), val, unit);
6214         val = vshPrettyCapacity(info.dataTotal, &unit);
6215         vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data total:"), val, unit);
6216     }
6217 
6218     if (info.memTotal || info.memRemaining || info.memProcessed) {
6219         val = vshPrettyCapacity(info.memProcessed, &unit);
6220         vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory processed:"), val, unit);
6221         val = vshPrettyCapacity(info.memRemaining, &unit);
6222         vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory remaining:"), val, unit);
6223         val = vshPrettyCapacity(info.memTotal, &unit);
6224         vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory total:"), val, unit);
6225 
6226         if ((rc = virTypedParamsGetULLong(params, nparams,
6227                                           VIR_DOMAIN_JOB_MEMORY_BPS,
6228                                           &value)) < 0) {
6229             goto save_error;
6230         } else if (rc && value) {
6231             val = vshPrettyCapacity(value, &unit);
6232             vshPrint(ctl, "%-17s %-.3lf %s/s\n",
6233                      _("Memory bandwidth:"), val, unit);
6234         }
6235 
6236         if ((rc = virTypedParamsGetULLong(params, nparams,
6237                                           VIR_DOMAIN_JOB_MEMORY_DIRTY_RATE,
6238                                           &value)) < 0) {
6239             goto save_error;
6240         } else if (rc) {
6241             vshPrint(ctl, "%-17s %-12llu pages/s\n", _("Dirty rate:"), value);
6242         }
6243 
6244         if ((rc = virTypedParamsGetULLong(params, nparams,
6245                                           VIR_DOMAIN_JOB_MEMORY_PAGE_SIZE,
6246                                           &value)) < 0) {
6247             goto save_error;
6248         } else if (rc) {
6249             vshPrint(ctl, "%-17s %-12llu bytes\n", _("Page size:"), value);
6250         }
6251 
6252         if ((rc = virTypedParamsGetULLong(params, nparams,
6253                                           VIR_DOMAIN_JOB_MEMORY_ITERATION,
6254                                           &value)) < 0) {
6255             goto save_error;
6256         } else if (rc) {
6257             vshPrint(ctl, "%-17s %-12llu\n", _("Iteration:"), value);
6258         }
6259 
6260         if ((rc = virTypedParamsGetULLong(params, nparams,
6261                                           VIR_DOMAIN_JOB_MEMORY_POSTCOPY_REQS,
6262                                           &value)) < 0) {
6263             goto save_error;
6264         } else if (rc) {
6265             vshPrint(ctl, "%-17s %-12llu\n", _("Postcopy requests:"), value);
6266         }
6267     }
6268 
6269     if (info.fileTotal || info.fileRemaining || info.fileProcessed) {
6270         val = vshPrettyCapacity(info.fileProcessed, &unit);
6271         vshPrint(ctl, "%-17s %-.3lf %s\n", _("File processed:"), val, unit);
6272         val = vshPrettyCapacity(info.fileRemaining, &unit);
6273         vshPrint(ctl, "%-17s %-.3lf %s\n", _("File remaining:"), val, unit);
6274         val = vshPrettyCapacity(info.fileTotal, &unit);
6275         vshPrint(ctl, "%-17s %-.3lf %s\n", _("File total:"), val, unit);
6276 
6277         if ((rc = virTypedParamsGetULLong(params, nparams,
6278                                           VIR_DOMAIN_JOB_DISK_BPS,
6279                                           &value)) < 0) {
6280             goto save_error;
6281         } else if (rc && value) {
6282             val = vshPrettyCapacity(value, &unit);
6283             vshPrint(ctl, "%-17s %-.3lf %s/s\n",
6284                      _("File bandwidth:"), val, unit);
6285         }
6286     }
6287 
6288     if ((rc = virTypedParamsGetULLong(params, nparams,
6289                                       VIR_DOMAIN_JOB_MEMORY_CONSTANT,
6290                                       &value)) < 0) {
6291         goto save_error;
6292     } else if (rc) {
6293         vshPrint(ctl, "%-17s %-12llu\n", _("Constant pages:"), value);
6294     }
6295     if ((rc = virTypedParamsGetULLong(params, nparams,
6296                                       VIR_DOMAIN_JOB_MEMORY_NORMAL,
6297                                       &value)) < 0) {
6298         goto save_error;
6299     } else if (rc) {
6300         vshPrint(ctl, "%-17s %-12llu\n", _("Normal pages:"), value);
6301     }
6302     if ((rc = virTypedParamsGetULLong(params, nparams,
6303                                       VIR_DOMAIN_JOB_MEMORY_NORMAL_BYTES,
6304                                       &value)) < 0) {
6305         goto save_error;
6306     } else if (rc) {
6307         val = vshPrettyCapacity(value, &unit);
6308         vshPrint(ctl, "%-17s %-.3lf %s\n", _("Normal data:"), val, unit);
6309     }
6310 
6311     if ((rc = virTypedParamsGetULLong(params, nparams,
6312                                       VIR_DOMAIN_JOB_DOWNTIME,
6313                                       &value)) < 0) {
6314         goto save_error;
6315     } else if (rc) {
6316         if (info.type == VIR_DOMAIN_JOB_COMPLETED) {
6317             vshPrint(ctl, "%-17s %-12llu ms\n",
6318                      _("Total downtime:"), value);
6319         } else {
6320             vshPrint(ctl, "%-17s %-12llu ms\n",
6321                      _("Expected downtime:"), value);
6322         }
6323     }
6324 
6325     if ((rc = virTypedParamsGetULLong(params, nparams,
6326                                       VIR_DOMAIN_JOB_DOWNTIME_NET,
6327                                       &value)) < 0)
6328         goto save_error;
6329     else if (rc)
6330         vshPrint(ctl, "%-17s %-12llu ms\n", _("Downtime w/o network:"), value);
6331 
6332     if ((rc = virTypedParamsGetULLong(params, nparams,
6333                                       VIR_DOMAIN_JOB_SETUP_TIME,
6334                                       &value)) < 0)
6335         goto save_error;
6336     else if (rc)
6337         vshPrint(ctl, "%-17s %-12llu ms\n", _("Setup time:"), value);
6338 
6339     if ((rc = virTypedParamsGetULLong(params, nparams,
6340                                       VIR_DOMAIN_JOB_COMPRESSION_CACHE,
6341                                       &value)) < 0) {
6342         goto save_error;
6343     } else if (rc) {
6344         val = vshPrettyCapacity(value, &unit);
6345         vshPrint(ctl, "%-17s %-.3lf %s\n", _("Compression cache:"), val, unit);
6346     }
6347     if ((rc = virTypedParamsGetULLong(params, nparams,
6348                                       VIR_DOMAIN_JOB_COMPRESSION_BYTES,
6349                                       &value)) < 0) {
6350         goto save_error;
6351     } else if (rc) {
6352         val = vshPrettyCapacity(value, &unit);
6353         vshPrint(ctl, "%-17s %-.3lf %s\n", _("Compressed data:"), val, unit);
6354     }
6355     if ((rc = virTypedParamsGetULLong(params, nparams,
6356                                       VIR_DOMAIN_JOB_COMPRESSION_PAGES,
6357                                       &value)) < 0) {
6358         goto save_error;
6359     } else if (rc) {
6360         vshPrint(ctl, "%-17s %-13llu\n", _("Compressed pages:"), value);
6361     }
6362     if ((rc = virTypedParamsGetULLong(params, nparams,
6363                                       VIR_DOMAIN_JOB_COMPRESSION_CACHE_MISSES,
6364                                       &value)) < 0) {
6365         goto save_error;
6366     } else if (rc) {
6367         vshPrint(ctl, "%-17s %-13llu\n", _("Compression cache misses:"), value);
6368     }
6369     if ((rc = virTypedParamsGetULLong(params, nparams,
6370                                       VIR_DOMAIN_JOB_COMPRESSION_OVERFLOW,
6371                                       &value)) < 0) {
6372         goto save_error;
6373     } else if (rc) {
6374         vshPrint(ctl, "%-17s %-13llu\n", _("Compression overflows:"), value);
6375     }
6376 
6377     if ((rc = virTypedParamsGetInt(params, nparams,
6378                                    VIR_DOMAIN_JOB_AUTO_CONVERGE_THROTTLE,
6379                                    &ivalue)) < 0) {
6380         goto save_error;
6381     } else if (rc) {
6382         vshPrint(ctl, "%-17s %-13d\n", _("Auto converge throttle:"), ivalue);
6383     }
6384 
6385     if ((rc = virTypedParamsGetULLong(params, nparams,
6386                                       VIR_DOMAIN_JOB_DISK_TEMP_USED,
6387                                       &value)) < 0) {
6388         goto save_error;
6389     } else if (rc) {
6390         val = vshPrettyCapacity(value, &unit);
6391         vshPrint(ctl, "%-17s %-.3lf %s\n", _("Temporary disk space use:"), val, unit);
6392     }
6393 
6394     if ((rc = virTypedParamsGetULLong(params, nparams,
6395                                       VIR_DOMAIN_JOB_DISK_TEMP_TOTAL,
6396                                       &value)) < 0) {
6397         goto save_error;
6398     } else if (rc) {
6399         val = vshPrettyCapacity(value, &unit);
6400         vshPrint(ctl, "%-17s %-.3lf %s\n", _("Temporary disk space total:"), val, unit);
6401     }
6402 
6403     if ((rc = virTypedParamsGetString(params, nparams, VIR_DOMAIN_JOB_ERRMSG,
6404                                       &svalue)) < 0) {
6405         goto save_error;
6406     } else if (rc == 1) {
6407         vshPrint(ctl, "%-17s %s\n", _("Error message:"), svalue);
6408     }
6409 
6410     ret = true;
6411 
6412  cleanup:
6413     virTypedParamsFree(params, nparams);
6414     return ret;
6415 
6416  save_error:
6417     vshSaveLibvirtError();
6418     goto cleanup;
6419 }
6420 
6421 /*
6422  * "domjobabort" command
6423  */
6424 static const vshCmdInfo info_domjobabort[] = {
6425     {.name = "help",
6426      .data = N_("abort active domain job")
6427     },
6428     {.name = "desc",
6429      .data = N_("Aborts the currently running domain job")
6430     },
6431     {.name = NULL}
6432 };
6433 
6434 static const vshCmdOptDef opts_domjobabort[] = {
6435     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
6436     {.name = NULL}
6437 };
6438 
6439 static bool
cmdDomjobabort(vshControl * ctl,const vshCmd * cmd)6440 cmdDomjobabort(vshControl *ctl, const vshCmd *cmd)
6441 {
6442     g_autoptr(virshDomain) dom = NULL;
6443 
6444     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
6445         return false;
6446 
6447     if (virDomainAbortJob(dom) < 0)
6448         return false;
6449 
6450     return true;
6451 }
6452 
6453 /*
6454  * "vcpucount" command
6455  */
6456 static const vshCmdInfo info_vcpucount[] = {
6457     {.name = "help",
6458      .data = N_("domain vcpu counts")
6459     },
6460     {.name = "desc",
6461      .data = N_("Returns the number of virtual CPUs used by the domain.")
6462     },
6463     {.name = NULL}
6464 };
6465 
6466 static const vshCmdOptDef opts_vcpucount[] = {
6467     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
6468     {.name = "maximum",
6469      .type = VSH_OT_BOOL,
6470      .help = N_("get maximum count of vcpus")
6471     },
6472     {.name = "active",
6473      .type = VSH_OT_BOOL,
6474      .help = N_("get number of currently active vcpus")
6475     },
6476     VIRSH_COMMON_OPT_LIVE(N_("get value from running domain")),
6477     VIRSH_COMMON_OPT_CONFIG(N_("get value to be used on next boot")),
6478     VIRSH_COMMON_OPT_CURRENT(N_("get value according to current domain state")),
6479     {.name = "guest",
6480      .type = VSH_OT_BOOL,
6481      .help = N_("retrieve vcpu count from the guest instead of the hypervisor")
6482     },
6483     {.name = NULL}
6484 };
6485 
6486 /**
6487  * Collect the number of vCPUs for a guest possibly with fallback means.
6488  *
6489  * Returns the count of vCPUs for a domain and certain flags. Returns -2 in case
6490  * of error. If @checkState is true, in case live stats can't be collected when
6491  * the domain is inactive or persistent stats can't be collected if domain is
6492  * transient -1 is returned and no error is reported.
6493  */
6494 
6495 static int
virshCPUCountCollect(vshControl * ctl,virDomainPtr dom,unsigned int flags,bool checkState)6496 virshCPUCountCollect(vshControl *ctl,
6497                      virDomainPtr dom,
6498                      unsigned int flags,
6499                      bool checkState)
6500 {
6501     virDomainInfo info;
6502     int count;
6503     g_autoptr(xmlDoc) xml = NULL;
6504     g_autoptr(xmlXPathContext) ctxt = NULL;
6505 
6506     if (checkState &&
6507         ((flags & VIR_DOMAIN_AFFECT_LIVE && virDomainIsActive(dom) < 1) ||
6508          (flags & VIR_DOMAIN_AFFECT_CONFIG && virDomainIsPersistent(dom) < 1)))
6509         return -1;
6510 
6511     /* In all cases, try the new API first; if it fails because we are talking
6512      * to an older daemon, generally we try a fallback API before giving up.
6513      * --current requires the new API, since we don't know whether the domain is
6514      *  running or inactive. */
6515     if ((count = virDomainGetVcpusFlags(dom, flags)) >= 0)
6516         return count;
6517 
6518     /* fallback code */
6519     if (!(last_error->code == VIR_ERR_NO_SUPPORT ||
6520           last_error->code == VIR_ERR_INVALID_ARG))
6521         return -2;
6522 
6523     if (flags & VIR_DOMAIN_VCPU_GUEST) {
6524         vshError(ctl, "%s", _("Failed to retrieve vCPU count from the guest"));
6525         return -2;
6526     }
6527 
6528     if (!(flags & (VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG)) &&
6529         virDomainIsActive(dom) == 1)
6530         flags |= VIR_DOMAIN_AFFECT_LIVE;
6531 
6532     vshResetLibvirtError();
6533 
6534     if (flags & VIR_DOMAIN_AFFECT_LIVE) {
6535         if (flags & VIR_DOMAIN_VCPU_MAXIMUM)
6536             return virDomainGetMaxVcpus(dom);
6537 
6538        if (virDomainGetInfo(dom, &info) < 0)
6539            return -2;
6540 
6541        return info.nrVirtCpu;
6542     }
6543 
6544     if (virshDomainGetXMLFromDom(ctl, dom, VIR_DOMAIN_XML_INACTIVE,
6545                                  &xml, &ctxt) < 0)
6546         return -2;
6547 
6548     if (flags & VIR_DOMAIN_VCPU_MAXIMUM) {
6549         if (virXPathInt("string(/domain/vcpu)", ctxt, &count) < 0) {
6550             vshError(ctl, "%s", _("Failed to retrieve maximum vcpu count"));
6551             return -2;
6552         }
6553     } else {
6554         if (virXPathInt("string(/domain/vcpu/@current)", ctxt, &count) < 0) {
6555             vshError(ctl, "%s", _("Failed to retrieve current vcpu count"));
6556             return -2;
6557         }
6558     }
6559 
6560     return count;
6561 }
6562 
6563 static bool
cmdVcpucount(vshControl * ctl,const vshCmd * cmd)6564 cmdVcpucount(vshControl *ctl, const vshCmd *cmd)
6565 {
6566     g_autoptr(virshDomain) dom = NULL;
6567     bool maximum = vshCommandOptBool(cmd, "maximum");
6568     bool active = vshCommandOptBool(cmd, "active");
6569     bool config = vshCommandOptBool(cmd, "config");
6570     bool live = vshCommandOptBool(cmd, "live");
6571     bool current = vshCommandOptBool(cmd, "current");
6572     bool guest = vshCommandOptBool(cmd, "guest");
6573     bool all = maximum + active + current + config + live + guest == 0;
6574     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
6575 
6576     /* Backwards compatibility: prior to 0.9.4,
6577      * VIR_DOMAIN_AFFECT_CURRENT was unsupported, and --current meant
6578      * the opposite of --maximum.  Translate the old '--current
6579      * --live' into the new '--active --live', while treating the new
6580      * '--maximum --current' correctly rather than rejecting it as
6581      * '--maximum --active'.  */
6582     if (!maximum && !active && current)
6583         current = false;
6584 
6585     VSH_EXCLUSIVE_OPTIONS_VAR(live, config)
6586     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
6587     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
6588     VSH_EXCLUSIVE_OPTIONS_VAR(active, maximum);
6589     VSH_EXCLUSIVE_OPTIONS_VAR(guest, config);
6590 
6591     if (live)
6592         flags |= VIR_DOMAIN_AFFECT_LIVE;
6593     if (config)
6594         flags |= VIR_DOMAIN_AFFECT_CONFIG;
6595     if (maximum)
6596         flags |= VIR_DOMAIN_VCPU_MAXIMUM;
6597     if (guest)
6598         flags |= VIR_DOMAIN_VCPU_GUEST;
6599 
6600     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
6601         return false;
6602 
6603     if (all) {
6604         int conf_max = virshCPUCountCollect(ctl, dom,
6605                                             VIR_DOMAIN_AFFECT_CONFIG |
6606                                             VIR_DOMAIN_VCPU_MAXIMUM, true);
6607         int conf_cur = virshCPUCountCollect(ctl, dom,
6608                                             VIR_DOMAIN_AFFECT_CONFIG, true);
6609         int live_max = virshCPUCountCollect(ctl, dom,
6610                                             VIR_DOMAIN_AFFECT_LIVE |
6611                                             VIR_DOMAIN_VCPU_MAXIMUM, true);
6612         int live_cur = virshCPUCountCollect(ctl, dom,
6613                                             VIR_DOMAIN_AFFECT_LIVE, true);
6614 
6615         if (conf_max == -2 || conf_cur == -2 || live_max == -2 || live_cur ==  -2)
6616             return false;
6617 
6618 #define PRINT_COUNT(VAR, WHICH, STATE) if (VAR > 0) \
6619     vshPrint(ctl, "%-12s %-12s %3d\n", WHICH, STATE, VAR)
6620         PRINT_COUNT(conf_max, _("maximum"), _("config"));
6621         PRINT_COUNT(live_max, _("maximum"), _("live"));
6622         PRINT_COUNT(conf_cur, _("current"), _("config"));
6623         PRINT_COUNT(live_cur, _("current"), _("live"));
6624 #undef PRINT_COUNT
6625 
6626     } else {
6627         int count = virshCPUCountCollect(ctl, dom, flags, false);
6628 
6629         if (count < 0)
6630             return false;
6631 
6632         vshPrint(ctl, "%d\n", count);
6633     }
6634 
6635     return true;
6636 }
6637 
6638 /*
6639  * "vcpuinfo" command
6640  */
6641 static const vshCmdInfo info_vcpuinfo[] = {
6642     {.name = "help",
6643      .data = N_("detailed domain vcpu information")
6644     },
6645     {.name = "desc",
6646      .data = N_("Returns basic information about the domain virtual CPUs.")
6647     },
6648     {.name = NULL}
6649 };
6650 
6651 static const vshCmdOptDef opts_vcpuinfo[] = {
6652     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
6653     {.name = "pretty",
6654      .type = VSH_OT_BOOL,
6655      .help = N_("return human readable output")
6656     },
6657     {.name = NULL}
6658 };
6659 
6660 
6661 static int
virshVcpuinfoPrintAffinity(vshControl * ctl,const unsigned char * cpumap,int maxcpu,bool pretty)6662 virshVcpuinfoPrintAffinity(vshControl *ctl,
6663                            const unsigned char *cpumap,
6664                            int maxcpu,
6665                            bool pretty)
6666 {
6667     g_autofree char *str = NULL;
6668     size_t i;
6669 
6670     vshPrint(ctl, "%-15s ", _("CPU Affinity:"));
6671     if (pretty) {
6672         if (!(str = virBitmapDataFormat(cpumap, VIR_CPU_MAPLEN(maxcpu))))
6673             return -1;
6674         vshPrint(ctl, _("%s (out of %d)"), str, maxcpu);
6675     } else {
6676         for (i = 0; i < maxcpu; i++)
6677             vshPrint(ctl, "%c", VIR_CPU_USED(cpumap, i) ? 'y' : '-');
6678     }
6679     vshPrint(ctl, "\n");
6680 
6681     return 0;
6682 }
6683 
6684 
6685 static virBitmap *
virshDomainGetVcpuBitmap(vshControl * ctl,virDomainPtr dom,bool inactive)6686 virshDomainGetVcpuBitmap(vshControl *ctl,
6687                          virDomainPtr dom,
6688                          bool inactive)
6689 {
6690     unsigned int flags = 0;
6691     g_autoptr(virBitmap) cpumap = NULL;
6692     g_autoptr(xmlDoc) xml = NULL;
6693     g_autoptr(xmlXPathContext) ctxt = NULL;
6694     g_autofree xmlNodePtr *nodes = NULL;
6695     int nnodes;
6696     size_t i;
6697     unsigned int curvcpus = 0;
6698     unsigned int maxvcpus = 0;
6699     unsigned int vcpuid;
6700 
6701     if (inactive)
6702         flags |= VIR_DOMAIN_XML_INACTIVE;
6703 
6704     if (virshDomainGetXMLFromDom(ctl, dom, flags, &xml, &ctxt) < 0)
6705         return NULL;
6706 
6707     if (virXPathUInt("string(/domain/vcpu)", ctxt, &maxvcpus) < 0) {
6708         vshError(ctl, "%s", _("Failed to retrieve maximum vcpu count"));
6709         return NULL;
6710     }
6711 
6712     ignore_value(virXPathUInt("string(/domain/vcpu/@current)", ctxt, &curvcpus));
6713 
6714     if (curvcpus == 0)
6715         curvcpus = maxvcpus;
6716 
6717     cpumap = virBitmapNew(maxvcpus);
6718 
6719     if ((nnodes = virXPathNodeSet("/domain/vcpus/vcpu", ctxt, &nodes)) <= 0) {
6720         /* if the specific vcpu state is missing provide a fallback */
6721         for (i = 0; i < curvcpus; i++)
6722             ignore_value(virBitmapSetBit(cpumap, i));
6723 
6724         return g_steal_pointer(&cpumap);
6725     }
6726 
6727     for (i = 0; i < nnodes; i++) {
6728         g_autofree char *online = NULL;
6729 
6730         ctxt->node = nodes[i];
6731 
6732         if (virXPathUInt("string(@id)", ctxt, &vcpuid) < 0 ||
6733             !(online = virXPathString("string(@enabled)", ctxt)))
6734             continue;
6735 
6736         if (STREQ(online, "yes"))
6737             ignore_value(virBitmapSetBit(cpumap, vcpuid));
6738     }
6739 
6740     if (virBitmapCountBits(cpumap) != curvcpus) {
6741         vshError(ctl, "%s", _("Failed to retrieve vcpu state bitmap"));
6742         return NULL;
6743     }
6744 
6745     return g_steal_pointer(&cpumap);
6746 }
6747 
6748 
6749 static bool
virshVcpuinfoInactive(vshControl * ctl,virDomainPtr dom,int maxcpu,bool pretty)6750 virshVcpuinfoInactive(vshControl *ctl,
6751                       virDomainPtr dom,
6752                       int maxcpu,
6753                       bool pretty)
6754 {
6755     g_autofree unsigned char *cpumaps = NULL;
6756     size_t cpumaplen;
6757     g_autoptr(virBitmap) vcpus = NULL;
6758     ssize_t nextvcpu = -1;
6759     bool first = true;
6760 
6761     if (!(vcpus = virshDomainGetVcpuBitmap(ctl, dom, true)))
6762         return false;
6763 
6764     cpumaplen = VIR_CPU_MAPLEN(maxcpu);
6765     cpumaps = g_new0(unsigned char, virBitmapSize(vcpus) * cpumaplen);
6766 
6767     if (virDomainGetVcpuPinInfo(dom, virBitmapSize(vcpus),
6768                                 cpumaps, cpumaplen,
6769                                 VIR_DOMAIN_AFFECT_CONFIG) < 0)
6770         return false;
6771 
6772     while ((nextvcpu = virBitmapNextSetBit(vcpus, nextvcpu)) >= 0) {
6773         if (!first)
6774             vshPrint(ctl, "\n");
6775         first = false;
6776 
6777         vshPrint(ctl, "%-15s %zd\n", _("VCPU:"), nextvcpu);
6778         vshPrint(ctl, "%-15s %s\n", _("CPU:"), _("N/A"));
6779         vshPrint(ctl, "%-15s %s\n", _("State:"), _("N/A"));
6780         vshPrint(ctl, "%-15s %s\n", _("CPU time"), _("N/A"));
6781 
6782         if (virshVcpuinfoPrintAffinity(ctl,
6783                                        VIR_GET_CPUMAP(cpumaps, cpumaplen, nextvcpu),
6784                                        maxcpu, pretty) < 0)
6785             return false;
6786     }
6787 
6788     return true;
6789 }
6790 
6791 
6792 static bool
cmdVcpuinfo(vshControl * ctl,const vshCmd * cmd)6793 cmdVcpuinfo(vshControl *ctl, const vshCmd *cmd)
6794 {
6795     virDomainInfo info;
6796     g_autoptr(virshDomain) dom = NULL;
6797     g_autofree virVcpuInfoPtr cpuinfo = NULL;
6798     g_autofree unsigned char *cpumaps = NULL;
6799     int ncpus, maxcpu;
6800     size_t cpumaplen;
6801     bool pretty = vshCommandOptBool(cmd, "pretty");
6802     int n;
6803     virshControl *priv = ctl->privData;
6804 
6805     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
6806         return false;
6807 
6808     if ((maxcpu = virshNodeGetCPUCount(priv->conn)) < 0)
6809         return false;
6810 
6811     if (virDomainGetInfo(dom, &info) != 0)
6812         return false;
6813 
6814     cpuinfo = g_new0(virVcpuInfo, info.nrVirtCpu);
6815     cpumaplen = VIR_CPU_MAPLEN(maxcpu);
6816     cpumaps = g_new0(unsigned char, info.nrVirtCpu * cpumaplen);
6817 
6818     if ((ncpus = virDomainGetVcpus(dom,
6819                                    cpuinfo, info.nrVirtCpu,
6820                                    cpumaps, cpumaplen)) < 0) {
6821         if (info.state != VIR_DOMAIN_SHUTOFF)
6822             return false;
6823 
6824         vshResetLibvirtError();
6825 
6826         /* for offline VMs we can return pinning information */
6827         return virshVcpuinfoInactive(ctl, dom, maxcpu, pretty);
6828     }
6829 
6830     for (n = 0; n < ncpus; n++) {
6831         vshPrint(ctl, "%-15s %d\n", _("VCPU:"), cpuinfo[n].number);
6832         vshPrint(ctl, "%-15s %d\n", _("CPU:"), cpuinfo[n].cpu);
6833         vshPrint(ctl, "%-15s %s\n", _("State:"),
6834                  virshDomainVcpuStateToString(cpuinfo[n].state));
6835         if (cpuinfo[n].cpuTime != 0) {
6836             double cpuUsed = cpuinfo[n].cpuTime;
6837 
6838             cpuUsed /= 1000000000.0;
6839 
6840             vshPrint(ctl, "%-15s %.1lfs\n", _("CPU time:"), cpuUsed);
6841         }
6842 
6843         if (virshVcpuinfoPrintAffinity(ctl, VIR_GET_CPUMAP(cpumaps, cpumaplen, n),
6844                                        maxcpu, pretty) < 0)
6845             return false;
6846 
6847         if (n < (ncpus - 1))
6848             vshPrint(ctl, "\n");
6849     }
6850 
6851     return true;
6852 }
6853 
6854 /*
6855  * "vcpupin" command
6856  */
6857 static const vshCmdInfo info_vcpupin[] = {
6858     {.name = "help",
6859      .data = N_("control or query domain vcpu affinity")
6860     },
6861     {.name = "desc",
6862      .data = N_("Pin domain VCPUs to host physical CPUs.")
6863     },
6864     {.name = NULL}
6865 };
6866 
6867 static const vshCmdOptDef opts_vcpupin[] = {
6868     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
6869     {.name = "vcpu",
6870      .type = VSH_OT_INT,
6871      .completer = virshDomainVcpuCompleter,
6872      .help = N_("vcpu number")
6873     },
6874     {.name = "cpulist",
6875      .type = VSH_OT_STRING,
6876      .flags = VSH_OFLAG_EMPTY_OK,
6877      .completer = virshDomainCpulistCompleter,
6878      .help = N_("host cpu number(s) to set, or omit option to query")
6879     },
6880     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
6881     VIRSH_COMMON_OPT_DOMAIN_LIVE,
6882     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
6883     {.name = NULL}
6884 };
6885 
6886 /*
6887  * Helper function to print vcpupin info.
6888  */
6889 static bool
virshPrintPinInfo(vshControl * ctl,unsigned char * cpumap,size_t cpumaplen)6890 virshPrintPinInfo(vshControl *ctl,
6891                   unsigned char *cpumap,
6892                   size_t cpumaplen)
6893 {
6894     g_autofree char *str = NULL;
6895 
6896     if (!(str = virBitmapDataFormat(cpumap, cpumaplen)))
6897         return false;
6898 
6899     vshPrint(ctl, "%s", str);
6900     return true;
6901 }
6902 
6903 
6904 static bool
virshVcpuPinQuery(vshControl * ctl,virDomainPtr dom,unsigned int vcpu,bool got_vcpu,int maxcpu,unsigned int flags)6905 virshVcpuPinQuery(vshControl *ctl,
6906                   virDomainPtr dom,
6907                   unsigned int vcpu,
6908                   bool got_vcpu,
6909                   int maxcpu,
6910                   unsigned int flags)
6911 {
6912     g_autofree unsigned char *cpumap = NULL;
6913     unsigned int countFlags = flags | VIR_DOMAIN_VCPU_MAXIMUM;
6914     int cpumaplen;
6915     size_t i;
6916     int ncpus;
6917     g_autoptr(vshTable) table = NULL;
6918 
6919     if ((ncpus = virshCPUCountCollect(ctl, dom, countFlags, true)) < 0) {
6920         if (ncpus == -1) {
6921             if (flags & VIR_DOMAIN_AFFECT_LIVE)
6922                 vshError(ctl, "%s", _("cannot get vcpupin for offline domain"));
6923             else
6924                 vshError(ctl, "%s", _("cannot get vcpupin for transient domain"));
6925         }
6926         return false;
6927     }
6928 
6929     if (got_vcpu && vcpu >= ncpus) {
6930         if (flags & VIR_DOMAIN_AFFECT_LIVE ||
6931             (!(flags & VIR_DOMAIN_AFFECT_CONFIG) &&
6932              virDomainIsActive(dom) == 1))
6933             vshError(ctl,
6934                      _("vcpu %d is out of range of live cpu count %d"),
6935                      vcpu, ncpus);
6936         else
6937             vshError(ctl,
6938                      _("vcpu %d is out of range of persistent cpu count %d"),
6939                      vcpu, ncpus);
6940         return false;
6941     }
6942 
6943     cpumaplen = VIR_CPU_MAPLEN(maxcpu);
6944     cpumap = g_new0(unsigned char, ncpus * cpumaplen);
6945     if ((ncpus = virDomainGetVcpuPinInfo(dom, ncpus, cpumap,
6946                                          cpumaplen, flags)) >= 0) {
6947         table = vshTableNew(_("VCPU"), _("CPU Affinity"), NULL);
6948         if (!table)
6949             return false;
6950 
6951         for (i = 0; i < ncpus; i++) {
6952             g_autofree char *pinInfo = NULL;
6953             g_autofree char *vcpuStr = NULL;
6954             if (got_vcpu && i != vcpu)
6955                 continue;
6956 
6957             if (!(pinInfo = virBitmapDataFormat(VIR_GET_CPUMAP(cpumap, cpumaplen, i),
6958                                                 cpumaplen)))
6959                 return false;
6960 
6961             vcpuStr = g_strdup_printf("%zu", i);
6962 
6963             if (vshTableRowAppend(table, vcpuStr, pinInfo, NULL) < 0)
6964                 return false;
6965         }
6966 
6967         vshTablePrintToStdout(table, ctl);
6968     }
6969 
6970     return true;
6971 }
6972 
6973 
6974 static unsigned char *
virshParseCPUList(vshControl * ctl,int * cpumaplen,const char * cpulist,int maxcpu)6975 virshParseCPUList(vshControl *ctl, int *cpumaplen,
6976                   const char *cpulist, int maxcpu)
6977 {
6978     unsigned char *cpumap = NULL;
6979     virBitmap *map = NULL;
6980 
6981     if (cpulist[0] == 'r') {
6982         map = virBitmapNew(maxcpu);
6983         virBitmapSetAll(map);
6984     } else {
6985         int lastcpu;
6986 
6987         if (virBitmapParse(cpulist, &map, 1024) < 0 ||
6988             virBitmapIsAllClear(map)) {
6989             vshError(ctl, _("Invalid cpulist '%s'"), cpulist);
6990             goto cleanup;
6991         }
6992         lastcpu = virBitmapLastSetBit(map);
6993         if (lastcpu >= maxcpu) {
6994             vshError(ctl, _("CPU %d in cpulist '%s' exceed the maxcpu %d"),
6995                      lastcpu, cpulist, maxcpu);
6996             goto cleanup;
6997         }
6998     }
6999 
7000     if (virBitmapToData(map, &cpumap, cpumaplen) < 0)
7001         goto cleanup;
7002 
7003  cleanup:
7004     virBitmapFree(map);
7005     return cpumap;
7006 }
7007 
7008 static bool
cmdVcpuPin(vshControl * ctl,const vshCmd * cmd)7009 cmdVcpuPin(vshControl *ctl, const vshCmd *cmd)
7010 {
7011     g_autoptr(virshDomain) dom = NULL;
7012     unsigned int vcpu = 0;
7013     const char *cpulist = NULL;
7014     g_autofree unsigned char *cpumap = NULL;
7015     int cpumaplen;
7016     int maxcpu;
7017     bool config = vshCommandOptBool(cmd, "config");
7018     bool live = vshCommandOptBool(cmd, "live");
7019     bool current = vshCommandOptBool(cmd, "current");
7020     int got_vcpu;
7021     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
7022     virshControl *priv = ctl->privData;
7023 
7024     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
7025     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
7026 
7027     if (config)
7028         flags |= VIR_DOMAIN_AFFECT_CONFIG;
7029     if (live)
7030         flags |= VIR_DOMAIN_AFFECT_LIVE;
7031 
7032     if (vshCommandOptStringReq(ctl, cmd, "cpulist", &cpulist) < 0)
7033         return false;
7034 
7035     if (!cpulist)
7036         VSH_EXCLUSIVE_OPTIONS_VAR(live, config);
7037 
7038     if ((got_vcpu = vshCommandOptUInt(ctl, cmd, "vcpu", &vcpu)) < 0)
7039         return false;
7040 
7041     /* In pin mode, "vcpu" is necessary */
7042     if (cpulist && got_vcpu == 0) {
7043         vshError(ctl, "%s", _("vcpupin: Missing vCPU number in pin mode."));
7044         return false;
7045     }
7046 
7047     if ((maxcpu = virshNodeGetCPUCount(priv->conn)) < 0)
7048         return false;
7049 
7050     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7051         return false;
7052 
7053     /* Query mode: show CPU affinity information then exit.*/
7054     if (!cpulist) {
7055         return virshVcpuPinQuery(ctl, dom, vcpu, got_vcpu, maxcpu, flags);
7056     }
7057 
7058     /* Pin mode: pinning specified vcpu to specified physical cpus */
7059     if (!(cpumap = virshParseCPUList(ctl, &cpumaplen, cpulist, maxcpu)))
7060         return false;
7061 
7062     /* use old API without any explicit flags */
7063     if (flags == VIR_DOMAIN_AFFECT_CURRENT && !current) {
7064         if (virDomainPinVcpu(dom, vcpu, cpumap, cpumaplen) != 0)
7065             return false;
7066     } else {
7067         if (virDomainPinVcpuFlags(dom, vcpu, cpumap, cpumaplen, flags) != 0)
7068             return false;
7069     }
7070 
7071     return true;
7072 }
7073 
7074 /*
7075  * "emulatorpin" command
7076  */
7077 static const vshCmdInfo info_emulatorpin[] = {
7078     {.name = "help",
7079      .data = N_("control or query domain emulator affinity")
7080     },
7081     {.name = "desc",
7082      .data = N_("Pin domain emulator threads to host physical CPUs.")
7083     },
7084     {.name = NULL}
7085 };
7086 
7087 static const vshCmdOptDef opts_emulatorpin[] = {
7088     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
7089     {.name = "cpulist",
7090      .type = VSH_OT_STRING,
7091      .flags = VSH_OFLAG_EMPTY_OK,
7092      .completer = virshDomainCpulistCompleter,
7093      .help = N_("host cpu number(s) to set, or omit option to query")
7094     },
7095     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
7096     VIRSH_COMMON_OPT_DOMAIN_LIVE,
7097     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
7098     {.name = NULL}
7099 };
7100 
7101 static bool
cmdEmulatorPin(vshControl * ctl,const vshCmd * cmd)7102 cmdEmulatorPin(vshControl *ctl, const vshCmd *cmd)
7103 {
7104     g_autoptr(virshDomain) dom = NULL;
7105     const char *cpulist = NULL;
7106     g_autofree unsigned char *cpumap = NULL;
7107     int cpumaplen;
7108     int maxcpu;
7109     bool config = vshCommandOptBool(cmd, "config");
7110     bool live = vshCommandOptBool(cmd, "live");
7111     bool current = vshCommandOptBool(cmd, "current");
7112     bool query = false; /* Query mode if no cpulist */
7113     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
7114     virshControl *priv = ctl->privData;
7115 
7116     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
7117     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
7118 
7119     if (config)
7120         flags |= VIR_DOMAIN_AFFECT_CONFIG;
7121     if (live)
7122         flags |= VIR_DOMAIN_AFFECT_LIVE;
7123     /* none of the options were specified */
7124     if (!current && !live && !config)
7125         flags = -1;
7126 
7127     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7128         return false;
7129 
7130     if (vshCommandOptStringReq(ctl, cmd, "cpulist", &cpulist) < 0) {
7131         return false;
7132     }
7133     query = !cpulist;
7134 
7135     if ((maxcpu = virshNodeGetCPUCount(priv->conn)) < 0) {
7136         return false;
7137     }
7138 
7139     /* Query mode: show CPU affinity information then exit.*/
7140     if (query) {
7141         bool ret = false;
7142 
7143         /* When query mode and neither "live", "config" nor "current"
7144          * is specified, set VIR_DOMAIN_AFFECT_CURRENT as flags */
7145         if (flags == -1)
7146             flags = VIR_DOMAIN_AFFECT_CURRENT;
7147 
7148         cpumaplen = VIR_CPU_MAPLEN(maxcpu);
7149         cpumap = g_new0(unsigned char, cpumaplen);
7150         if (virDomainGetEmulatorPinInfo(dom, cpumap,
7151                                         cpumaplen, flags) >= 0) {
7152             vshPrintExtra(ctl, "%s %s\n", _("emulator:"), _("CPU Affinity"));
7153             vshPrintExtra(ctl, "----------------------------------\n");
7154             vshPrintExtra(ctl, "       *: ");
7155             ret = virshPrintPinInfo(ctl, cpumap, cpumaplen);
7156             vshPrint(ctl, "\n");
7157         }
7158         return ret;
7159     }
7160 
7161     /* Pin mode: pinning emulator threads to specified physical cpus */
7162     if (!(cpumap = virshParseCPUList(ctl, &cpumaplen, cpulist, maxcpu)))
7163         return false;
7164 
7165     if (flags == -1)
7166         flags = VIR_DOMAIN_AFFECT_LIVE;
7167 
7168     if (virDomainPinEmulator(dom, cpumap, cpumaplen, flags) != 0)
7169         return false;
7170 
7171     return true;
7172 }
7173 
7174 /*
7175  * "setvcpus" command
7176  */
7177 static const vshCmdInfo info_setvcpus[] = {
7178     {.name = "help",
7179      .data = N_("change number of virtual CPUs")
7180     },
7181     {.name = "desc",
7182      .data = N_("Change the number of virtual CPUs in the guest domain.")
7183     },
7184     {.name = NULL}
7185 };
7186 
7187 static const vshCmdOptDef opts_setvcpus[] = {
7188     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
7189     {.name = "count",
7190      .type = VSH_OT_INT,
7191      .flags = VSH_OFLAG_REQ,
7192      .help = N_("number of virtual CPUs")
7193     },
7194     {.name = "maximum",
7195      .type = VSH_OT_BOOL,
7196      .help = N_("set maximum limit on next boot")
7197     },
7198     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
7199     VIRSH_COMMON_OPT_DOMAIN_LIVE,
7200     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
7201     {.name = "guest",
7202      .type = VSH_OT_BOOL,
7203      .help = N_("modify cpu state in the guest")
7204     },
7205     {.name = "hotpluggable",
7206      .type = VSH_OT_BOOL,
7207      .help = N_("make added vcpus hot(un)pluggable")
7208     },
7209     {.name = NULL}
7210 };
7211 
7212 static bool
cmdSetvcpus(vshControl * ctl,const vshCmd * cmd)7213 cmdSetvcpus(vshControl *ctl, const vshCmd *cmd)
7214 {
7215     g_autoptr(virshDomain) dom = NULL;
7216     unsigned int count = 0;
7217     bool maximum = vshCommandOptBool(cmd, "maximum");
7218     bool config = vshCommandOptBool(cmd, "config");
7219     bool live = vshCommandOptBool(cmd, "live");
7220     bool current = vshCommandOptBool(cmd, "current");
7221     bool guest = vshCommandOptBool(cmd, "guest");
7222     bool hotpluggable = vshCommandOptBool(cmd, "hotpluggable");
7223     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
7224 
7225     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
7226     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
7227     VSH_EXCLUSIVE_OPTIONS_VAR(guest, config);
7228 
7229     VSH_REQUIRE_OPTION_VAR(maximum, config);
7230 
7231     if (config)
7232         flags |= VIR_DOMAIN_AFFECT_CONFIG;
7233     if (live)
7234         flags |= VIR_DOMAIN_AFFECT_LIVE;
7235     if (guest)
7236         flags |= VIR_DOMAIN_VCPU_GUEST;
7237     if (maximum)
7238         flags |= VIR_DOMAIN_VCPU_MAXIMUM;
7239     if (hotpluggable)
7240         flags |= VIR_DOMAIN_VCPU_HOTPLUGGABLE;
7241 
7242     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7243         return false;
7244 
7245     if (vshCommandOptUInt(ctl, cmd, "count", &count) < 0)
7246         return false;
7247 
7248     if (count == 0) {
7249         vshError(ctl, _("Can't set 0 processors for a VM"));
7250         return false;
7251     }
7252 
7253     /* none of the options were specified */
7254     if (!current && flags == 0) {
7255         if (virDomainSetVcpus(dom, count) != 0)
7256             return false;
7257     } else {
7258         if (virDomainSetVcpusFlags(dom, count, flags) < 0)
7259             return false;
7260     }
7261 
7262     return true;
7263 }
7264 
7265 
7266 /*
7267  * "guestvcpus" command
7268  */
7269 static const vshCmdInfo info_guestvcpus[] = {
7270     {.name = "help",
7271      .data = N_("query or modify state of vcpu in the guest (via agent)")
7272     },
7273     {.name = "desc",
7274      .data = N_("Use the guest agent to query or set cpu state from guest's "
7275                 "point of view")
7276     },
7277     {.name = NULL}
7278 };
7279 
7280 static const vshCmdOptDef opts_guestvcpus[] = {
7281     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
7282     {.name = "cpulist",
7283      .type = VSH_OT_STRING,
7284      .completer = virshDomainVcpulistViaAgentCompleter,
7285      .help = N_("list of cpus to enable or disable")
7286     },
7287     {.name = "enable",
7288      .type = VSH_OT_BOOL,
7289      .help = N_("enable cpus specified by cpulist")
7290     },
7291     {.name = "disable",
7292      .type = VSH_OT_BOOL,
7293      .help = N_("disable cpus specified by cpulist")
7294     },
7295     {.name = NULL}
7296 };
7297 
7298 static bool
cmdGuestvcpus(vshControl * ctl,const vshCmd * cmd)7299 cmdGuestvcpus(vshControl *ctl, const vshCmd *cmd)
7300 {
7301     g_autoptr(virshDomain) dom = NULL;
7302     bool enable = vshCommandOptBool(cmd, "enable");
7303     bool disable = vshCommandOptBool(cmd, "disable");
7304     virTypedParameterPtr params = NULL;
7305     unsigned int nparams = 0;
7306     const char *cpulist = NULL;
7307     int state = 0;
7308     size_t i;
7309     bool ret = false;
7310 
7311     VSH_EXCLUSIVE_OPTIONS_VAR(enable, disable);
7312     VSH_REQUIRE_OPTION("enable", "cpulist");
7313     VSH_REQUIRE_OPTION("disable", "cpulist");
7314 
7315     if (vshCommandOptStringReq(ctl, cmd, "cpulist", &cpulist))
7316         return false;
7317 
7318     if (cpulist && !(enable || disable)) {
7319         vshError(ctl, _("One of options --enable or --disable is required by "
7320                         "option --cpulist"));
7321         return false;
7322     }
7323 
7324     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7325         return false;
7326 
7327     if (enable)
7328         state = 1;
7329 
7330     if (cpulist) {
7331         if (virDomainSetGuestVcpus(dom, cpulist, state, 0) < 0)
7332             goto cleanup;
7333     } else {
7334         if (virDomainGetGuestVcpus(dom, &params, &nparams, 0) < 0)
7335             goto cleanup;
7336 
7337         for (i = 0; i < nparams; i++) {
7338             char *str = vshGetTypedParamValue(ctl, &params[i]);
7339             vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
7340             VIR_FREE(str);
7341         }
7342     }
7343 
7344     ret = true;
7345 
7346  cleanup:
7347     virTypedParamsFree(params, nparams);
7348     return ret;
7349 }
7350 
7351 
7352 /*
7353  * "setvcpu" command
7354  */
7355 static const vshCmdInfo info_setvcpu[] = {
7356     {.name = "help",
7357      .data = N_("attach/detach vcpu or groups of threads")
7358     },
7359     {.name = "desc",
7360      .data = N_("Add or remove vcpus")
7361     },
7362     {.name = NULL}
7363 };
7364 
7365 static const vshCmdOptDef opts_setvcpu[] = {
7366     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
7367     {.name = "vcpulist",
7368      .type = VSH_OT_DATA,
7369      .flags = VSH_OFLAG_REQ,
7370      .completer = virshDomainVcpulistCompleter,
7371      .help = N_("ids of vcpus to manipulate")
7372     },
7373     {.name = "enable",
7374      .type = VSH_OT_BOOL,
7375      .help = N_("enable cpus specified by cpumap")
7376     },
7377     {.name = "disable",
7378      .type = VSH_OT_BOOL,
7379      .help = N_("disable cpus specified by cpumap")
7380     },
7381     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
7382     VIRSH_COMMON_OPT_DOMAIN_LIVE,
7383     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
7384     {.name = NULL}
7385 };
7386 
7387 static bool
cmdSetvcpu(vshControl * ctl,const vshCmd * cmd)7388 cmdSetvcpu(vshControl *ctl, const vshCmd *cmd)
7389 {
7390     g_autoptr(virshDomain) dom = NULL;
7391     bool enable = vshCommandOptBool(cmd, "enable");
7392     bool disable = vshCommandOptBool(cmd, "disable");
7393     bool config = vshCommandOptBool(cmd, "config");
7394     bool live = vshCommandOptBool(cmd, "live");
7395     const char *vcpulist = NULL;
7396     int state = 0;
7397     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
7398 
7399     VSH_EXCLUSIVE_OPTIONS_VAR(enable, disable);
7400 
7401     VSH_EXCLUSIVE_OPTIONS("current", "live");
7402     VSH_EXCLUSIVE_OPTIONS("current", "config");
7403 
7404     if (config)
7405         flags |= VIR_DOMAIN_AFFECT_CONFIG;
7406     if (live)
7407         flags |= VIR_DOMAIN_AFFECT_LIVE;
7408 
7409     if (!(enable || disable)) {
7410         vshError(ctl, "%s", _("one of --enable, --disable is required"));
7411         return false;
7412     }
7413 
7414     if (vshCommandOptStringReq(ctl, cmd, "vcpulist", &vcpulist))
7415         return false;
7416 
7417     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7418         return false;
7419 
7420     if (enable)
7421         state = 1;
7422 
7423     if (virDomainSetVcpu(dom, vcpulist, state, flags) < 0)
7424         return false;
7425 
7426     return true;
7427 }
7428 
7429 
7430 /*
7431  * "domblkthreshold" command
7432  */
7433 static const vshCmdInfo info_domblkthreshold[] = {
7434     {.name = "help",
7435      .data = N_("set the threshold for block-threshold event for a given block "
7436                 "device or it's backing chain element")
7437     },
7438     {.name = "desc",
7439      .data = N_("set threshold for block-threshold event for a block device")
7440     },
7441     {.name = NULL}
7442 };
7443 
7444 static const vshCmdOptDef opts_domblkthreshold[] = {
7445     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
7446     {.name = "dev",
7447      .type = VSH_OT_DATA,
7448      .flags = VSH_OFLAG_REQ,
7449      .completer = virshDomainDiskTargetCompleter,
7450      .help = N_("device to set threshold for")
7451     },
7452     {.name = "threshold",
7453      .type = VSH_OT_INT,
7454      .flags = VSH_OFLAG_REQ,
7455      .help = N_("threshold as a scaled number (by default bytes)")
7456     },
7457     {.name = NULL}
7458 };
7459 
7460 static bool
cmdDomblkthreshold(vshControl * ctl,const vshCmd * cmd)7461 cmdDomblkthreshold(vshControl *ctl, const vshCmd *cmd)
7462 {
7463     unsigned long long threshold;
7464     const char *dev = NULL;
7465     g_autoptr(virshDomain) dom = NULL;
7466 
7467     if (vshCommandOptStringReq(ctl, cmd, "dev", &dev))
7468         return false;
7469 
7470     if (vshCommandOptScaledInt(ctl, cmd, "threshold",
7471                                &threshold, 1, ULLONG_MAX) < 0)
7472         return false;
7473 
7474     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7475         return false;
7476 
7477     if (virDomainSetBlockThreshold(dom, dev, threshold, 0) < 0)
7478         return false;
7479 
7480     return true;
7481 }
7482 
7483 
7484 /*
7485  * "iothreadinfo" command
7486  */
7487 static const vshCmdInfo info_iothreadinfo[] = {
7488     {.name = "help",
7489      .data = N_("view domain IOThreads")
7490     },
7491     {.name = "desc",
7492      .data = N_("Returns basic information about the domain IOThreads.")
7493     },
7494     {.name = NULL}
7495 };
7496 static const vshCmdOptDef opts_iothreadinfo[] = {
7497     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
7498     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
7499     VIRSH_COMMON_OPT_DOMAIN_LIVE,
7500     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
7501     {.name = NULL}
7502 };
7503 
7504 static bool
cmdIOThreadInfo(vshControl * ctl,const vshCmd * cmd)7505 cmdIOThreadInfo(vshControl *ctl, const vshCmd *cmd)
7506 {
7507     g_autoptr(virshDomain) dom = NULL;
7508     bool config = vshCommandOptBool(cmd, "config");
7509     bool live = vshCommandOptBool(cmd, "live");
7510     bool current = vshCommandOptBool(cmd, "current");
7511     size_t niothreads = 0;
7512     virDomainIOThreadInfoPtr *info = NULL;
7513     size_t i;
7514     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
7515     g_autoptr(vshTable) table = NULL;
7516     bool ret = false;
7517     int rc;
7518 
7519     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
7520     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
7521 
7522     if (config)
7523         flags |= VIR_DOMAIN_AFFECT_CONFIG;
7524     if (live)
7525         flags |= VIR_DOMAIN_AFFECT_LIVE;
7526 
7527     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7528         return false;
7529 
7530     if ((rc = virDomainGetIOThreadInfo(dom, &info, flags)) < 0) {
7531         vshError(ctl, _("Unable to get domain IOThreads information"));
7532         goto cleanup;
7533     }
7534     niothreads = rc;
7535 
7536     if (niothreads == 0) {
7537         ret = true;
7538         vshPrintExtra(ctl, _("No IOThreads found for the domain"));
7539         goto cleanup;
7540     }
7541 
7542     table = vshTableNew(_("IOThread ID"), _("CPU Affinity"), NULL);
7543     if (!table)
7544         goto cleanup;
7545 
7546     for (i = 0; i < niothreads; i++) {
7547         g_autofree char *pinInfo = NULL;
7548         g_autofree char *iothreadIdStr = NULL;
7549 
7550         iothreadIdStr = g_strdup_printf("%u", info[i]->iothread_id);
7551 
7552         ignore_value(pinInfo = virBitmapDataFormat(info[i]->cpumap, info[i]->cpumaplen));
7553 
7554         if (vshTableRowAppend(table, iothreadIdStr, pinInfo ? pinInfo : "", NULL) < 0)
7555             goto cleanup;
7556     }
7557 
7558     vshTablePrintToStdout(table, ctl);
7559 
7560     ret = true;
7561 
7562  cleanup:
7563     for (i = 0; i < niothreads; i++)
7564         virDomainIOThreadInfoFree(info[i]);
7565     VIR_FREE(info);
7566     return ret;
7567 }
7568 
7569 /*
7570  * "iothreadpin" command
7571  */
7572 static const vshCmdInfo info_iothreadpin[] = {
7573     {.name = "help",
7574      .data = N_("control domain IOThread affinity")
7575     },
7576     {.name = "desc",
7577      .data = N_("Pin domain IOThreads to host physical CPUs.")
7578     },
7579     {.name = NULL}
7580 };
7581 
7582 static const vshCmdOptDef opts_iothreadpin[] = {
7583     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
7584     {.name = "iothread",
7585      .type = VSH_OT_INT,
7586      .flags = VSH_OFLAG_REQ,
7587      .completer = virshDomainIOThreadIdCompleter,
7588      .help = N_("IOThread ID number")
7589     },
7590     {.name = "cpulist",
7591      .type = VSH_OT_DATA,
7592      .flags = VSH_OFLAG_REQ,
7593      .completer = virshDomainCpulistCompleter,
7594      .help = N_("host cpu number(s) to set")
7595     },
7596     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
7597     VIRSH_COMMON_OPT_DOMAIN_LIVE,
7598     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
7599     {.name = NULL}
7600 };
7601 
7602 static bool
cmdIOThreadPin(vshControl * ctl,const vshCmd * cmd)7603 cmdIOThreadPin(vshControl *ctl, const vshCmd *cmd)
7604 {
7605     g_autoptr(virshDomain) dom = NULL;
7606     const char *cpulist = NULL;
7607     bool config = vshCommandOptBool(cmd, "config");
7608     bool live = vshCommandOptBool(cmd, "live");
7609     bool current = vshCommandOptBool(cmd, "current");
7610     unsigned int iothread_id = 0;
7611     int maxcpu;
7612     g_autofree unsigned char *cpumap = NULL;
7613     int cpumaplen;
7614     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
7615     virshControl *priv = ctl->privData;
7616 
7617     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
7618     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
7619 
7620     if (config)
7621         flags |= VIR_DOMAIN_AFFECT_CONFIG;
7622     if (live)
7623         flags |= VIR_DOMAIN_AFFECT_LIVE;
7624 
7625     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7626         return false;
7627 
7628     if (vshCommandOptUInt(ctl, cmd, "iothread", &iothread_id) < 0)
7629         return false;
7630 
7631     if (vshCommandOptStringReq(ctl, cmd, "cpulist", &cpulist) < 0)
7632         return false;
7633 
7634     if ((maxcpu = virshNodeGetCPUCount(priv->conn)) < 0)
7635         return false;
7636 
7637     if (!(cpumap = virshParseCPUList(ctl, &cpumaplen, cpulist, maxcpu)))
7638         return false;
7639 
7640     if (virDomainPinIOThread(dom, iothread_id,
7641                              cpumap, cpumaplen, flags) != 0)
7642         return false;
7643 
7644     return true;
7645 }
7646 
7647 /*
7648  * "iothreadadd" command
7649  */
7650 static const vshCmdInfo info_iothreadadd[] = {
7651     {.name = "help",
7652      .data = N_("add an IOThread to the guest domain")
7653     },
7654     {.name = "desc",
7655      .data = N_("Add an IOThread to the guest domain.")
7656     },
7657     {.name = NULL}
7658 };
7659 
7660 static const vshCmdOptDef opts_iothreadadd[] = {
7661     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
7662     {.name = "id",
7663      .type = VSH_OT_INT,
7664      .flags = VSH_OFLAG_REQ,
7665      .help = N_("iothread for the new IOThread")
7666     },
7667     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
7668     VIRSH_COMMON_OPT_DOMAIN_LIVE,
7669     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
7670     {.name = NULL}
7671 };
7672 
7673 static bool
cmdIOThreadAdd(vshControl * ctl,const vshCmd * cmd)7674 cmdIOThreadAdd(vshControl *ctl, const vshCmd *cmd)
7675 {
7676     g_autoptr(virshDomain) dom = NULL;
7677     int iothread_id = 0;
7678     bool config = vshCommandOptBool(cmd, "config");
7679     bool live = vshCommandOptBool(cmd, "live");
7680     bool current = vshCommandOptBool(cmd, "current");
7681     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
7682 
7683     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
7684     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
7685 
7686     if (config)
7687         flags |= VIR_DOMAIN_AFFECT_CONFIG;
7688     if (live)
7689         flags |= VIR_DOMAIN_AFFECT_LIVE;
7690 
7691     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7692         return false;
7693 
7694     if (vshCommandOptInt(ctl, cmd, "id", &iothread_id) < 0)
7695         return false;
7696     if (iothread_id <= 0) {
7697         vshError(ctl, _("Invalid IOThread id value: '%d'"), iothread_id);
7698         return false;
7699     }
7700 
7701     if (virDomainAddIOThread(dom, iothread_id, flags) < 0)
7702         return false;
7703 
7704     return true;
7705 }
7706 
7707 
7708  /*
7709  * "iothreadset" command
7710  */
7711 static const vshCmdInfo info_iothreadset[] = {
7712     {.name = "help",
7713      .data = N_("modifies an existing IOThread of the guest domain")
7714     },
7715     {.name = "desc",
7716      .data = N_("Modifies an existing IOThread of the guest domain.")
7717     },
7718     {.name = NULL}
7719 };
7720 
7721 static const vshCmdOptDef opts_iothreadset[] = {
7722     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
7723     {.name = "id",
7724      .type = VSH_OT_INT,
7725      .flags = VSH_OFLAG_REQ,
7726      .completer = virshDomainIOThreadIdCompleter,
7727      .help = N_("iothread id of existing IOThread")
7728     },
7729     {.name = "poll-max-ns",
7730      .type = VSH_OT_INT,
7731      .help = N_("set the maximum IOThread polling time in ns")
7732     },
7733     {.name = "poll-grow",
7734      .type = VSH_OT_INT,
7735      .help = N_("set the value to increase the IOThread polling time")
7736     },
7737     {.name = "poll-shrink",
7738      .type = VSH_OT_INT,
7739      .help = N_("set the value for reduction of the IOThread polling time ")
7740     },
7741     VIRSH_COMMON_OPT_DOMAIN_LIVE,
7742     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
7743     {.name = NULL}
7744 };
7745 
7746 static bool
cmdIOThreadSet(vshControl * ctl,const vshCmd * cmd)7747 cmdIOThreadSet(vshControl *ctl, const vshCmd *cmd)
7748 {
7749     g_autoptr(virshDomain) dom = NULL;
7750     int id = 0;
7751     bool ret = false;
7752     bool live = vshCommandOptBool(cmd, "live");
7753     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
7754     virTypedParameterPtr params = NULL;
7755     int nparams = 0;
7756     int maxparams = 0;
7757     unsigned long long poll_max;
7758     unsigned int poll_val;
7759     int rc;
7760 
7761     if (live)
7762         flags |= VIR_DOMAIN_AFFECT_LIVE;
7763 
7764     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7765         return false;
7766 
7767     if (vshCommandOptInt(ctl, cmd, "id", &id) < 0)
7768         goto cleanup;
7769     if (id <= 0) {
7770         vshError(ctl, _("Invalid IOThread id value: '%d'"), id);
7771         goto cleanup;
7772     }
7773 
7774     poll_val = 0;
7775     if ((rc = vshCommandOptULongLong(ctl, cmd, "poll-max-ns", &poll_max)) < 0)
7776         goto cleanup;
7777     if (rc > 0 && virTypedParamsAddULLong(&params, &nparams, &maxparams,
7778                                           VIR_DOMAIN_IOTHREAD_POLL_MAX_NS,
7779                                           poll_max) < 0)
7780         goto save_error;
7781 
7782 #define VSH_IOTHREAD_SET_UINT_PARAMS(opt, param) \
7783     poll_val = 0; \
7784     if ((rc = vshCommandOptUInt(ctl, cmd, opt, &poll_val)) < 0) \
7785         goto cleanup; \
7786     if (rc > 0 && \
7787         virTypedParamsAddUInt(&params, &nparams, &maxparams, \
7788                               param, poll_val) < 0) \
7789         goto save_error;
7790 
7791     VSH_IOTHREAD_SET_UINT_PARAMS("poll-grow", VIR_DOMAIN_IOTHREAD_POLL_GROW)
7792     VSH_IOTHREAD_SET_UINT_PARAMS("poll-shrink", VIR_DOMAIN_IOTHREAD_POLL_SHRINK)
7793 
7794 #undef VSH_IOTHREAD_SET_UINT_PARAMS
7795 
7796     if (virDomainSetIOThreadParams(dom, id, params, nparams, flags) < 0)
7797         goto cleanup;
7798 
7799     ret = true;
7800 
7801  cleanup:
7802     virTypedParamsFree(params, nparams);
7803     return ret;
7804 
7805  save_error:
7806     vshSaveLibvirtError();
7807     goto cleanup;
7808 }
7809 
7810 
7811 /*
7812  * "iothreaddel" command
7813  */
7814 static const vshCmdInfo info_iothreaddel[] = {
7815     {.name = "help",
7816      .data = N_("delete an IOThread from the guest domain")
7817     },
7818     {.name = "desc",
7819      .data = N_("Delete an IOThread from the guest domain.")
7820     },
7821     {.name = NULL}
7822 };
7823 
7824 static const vshCmdOptDef opts_iothreaddel[] = {
7825     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
7826     {.name = "id",
7827      .type = VSH_OT_INT,
7828      .flags = VSH_OFLAG_REQ,
7829      .completer = virshDomainIOThreadIdCompleter,
7830      .help = N_("iothread_id for the IOThread to delete")
7831     },
7832     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
7833     VIRSH_COMMON_OPT_DOMAIN_LIVE,
7834     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
7835     {.name = NULL}
7836 };
7837 
7838 static bool
cmdIOThreadDel(vshControl * ctl,const vshCmd * cmd)7839 cmdIOThreadDel(vshControl *ctl, const vshCmd *cmd)
7840 {
7841     g_autoptr(virshDomain) dom = NULL;
7842     int iothread_id = 0;
7843     bool config = vshCommandOptBool(cmd, "config");
7844     bool live = vshCommandOptBool(cmd, "live");
7845     bool current = vshCommandOptBool(cmd, "current");
7846     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
7847 
7848     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
7849     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
7850 
7851     if (config)
7852         flags |= VIR_DOMAIN_AFFECT_CONFIG;
7853     if (live)
7854         flags |= VIR_DOMAIN_AFFECT_LIVE;
7855 
7856     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7857         return false;
7858 
7859     if (vshCommandOptInt(ctl, cmd, "id", &iothread_id) < 0)
7860         return false;
7861     if (iothread_id <= 0) {
7862         vshError(ctl, _("Invalid IOThread id value: '%d'"), iothread_id);
7863         return false;
7864     }
7865 
7866     if (virDomainDelIOThread(dom, iothread_id, flags) < 0)
7867         return false;
7868 
7869     return true;
7870 }
7871 
7872 /*
7873  * "cpu-stats" command
7874  */
7875 static const vshCmdInfo info_cpu_stats[] = {
7876     {.name = "help",
7877      .data = N_("show domain cpu statistics")
7878     },
7879     {.name = "desc",
7880      .data = N_("Display per-CPU and total statistics about the domain's CPUs")
7881     },
7882     {.name = NULL}
7883 };
7884 
7885 static const vshCmdOptDef opts_cpu_stats[] = {
7886     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
7887     {.name = "total",
7888      .type = VSH_OT_BOOL,
7889      .help = N_("Show total statistics only")
7890     },
7891     {.name = "start",
7892      .type = VSH_OT_INT,
7893      .help = N_("Show statistics from this CPU")
7894     },
7895     {.name = "count",
7896      .type = VSH_OT_INT,
7897      .help = N_("Number of shown CPUs at most")
7898     },
7899     {.name = NULL}
7900 };
7901 
7902 static void
vshCPUStatsPrintField(vshControl * ctl,virTypedParameterPtr param)7903 vshCPUStatsPrintField(vshControl *ctl,
7904                       virTypedParameterPtr param)
7905 {
7906     vshPrint(ctl, "\t%-12s ", param->field);
7907     if ((STREQ(param->field, VIR_DOMAIN_CPU_STATS_CPUTIME) ||
7908          STREQ(param->field, VIR_DOMAIN_CPU_STATS_VCPUTIME) ||
7909          STREQ(param->field, VIR_DOMAIN_CPU_STATS_USERTIME) ||
7910          STREQ(param->field, VIR_DOMAIN_CPU_STATS_SYSTEMTIME)) &&
7911         param->type == VIR_TYPED_PARAM_ULLONG) {
7912         vshPrint(ctl, "%9lld.%09lld seconds\n",
7913                  param->value.ul / 1000000000,
7914                  param->value.ul % 1000000000);
7915     } else {
7916         g_autofree char *s = vshGetTypedParamValue(ctl, param);
7917         vshPrint(ctl, "%s\n", s);
7918     }
7919 }
7920 
7921 static bool
cmdCPUStats(vshControl * ctl,const vshCmd * cmd)7922 cmdCPUStats(vshControl *ctl, const vshCmd *cmd)
7923 {
7924     g_autoptr(virshDomain) dom = NULL;
7925     virTypedParameterPtr params = NULL;
7926     int max_id, cpu = 0, show_count = -1, nparams = 0, stats_per_cpu;
7927     size_t i, j;
7928     bool show_total = false, show_per_cpu = false;
7929     bool ret = false;
7930     int rv = 0;
7931 
7932     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
7933         return false;
7934 
7935     show_total = vshCommandOptBool(cmd, "total");
7936 
7937     if ((rv = vshCommandOptInt(ctl, cmd, "start", &cpu)) < 0) {
7938         goto cleanup;
7939     } else if (rv > 0) {
7940         if (cpu < 0) {
7941             vshError(ctl, "%s", _("Invalid value for start CPU"));
7942             goto cleanup;
7943         }
7944         show_per_cpu = true;
7945     }
7946 
7947     if ((rv = vshCommandOptInt(ctl, cmd, "count", &show_count)) < 0) {
7948         goto cleanup;
7949     } else if (rv > 0) {
7950         if (show_count < 0) {
7951             vshError(ctl, "%s", _("Invalid value for number of CPUs to show"));
7952             goto cleanup;
7953         }
7954         show_per_cpu = true;
7955     }
7956 
7957     /* default show per_cpu and total */
7958     if (!show_total && !show_per_cpu) {
7959         show_total = true;
7960         show_per_cpu = true;
7961     }
7962 
7963     if (!show_per_cpu) /* show total stats only */
7964         goto do_show_total;
7965 
7966     /* get number of cpus on the node */
7967     if ((max_id = virDomainGetCPUStats(dom, NULL, 0, 0, 0, 0)) < 0)
7968         goto failed_stats;
7969 
7970     if (cpu >= max_id) {
7971         vshError(ctl, "Start CPU %d is out of range (min: 0, max: %d)",
7972                  cpu, max_id - 1);
7973         goto cleanup;
7974     }
7975 
7976     if (show_count < 0 || show_count > max_id) {
7977         if (show_count > max_id)
7978             vshPrint(ctl, _("Only %d CPUs available to show\n"), max_id);
7979         show_count = max_id - cpu;
7980     }
7981 
7982     /* get percpu information */
7983     if ((nparams = virDomainGetCPUStats(dom, NULL, 0, 0, 1, 0)) < 0)
7984         goto failed_stats;
7985 
7986     if (!nparams) {
7987         vshPrint(ctl, "%s", _("No per-CPU stats available"));
7988         if (show_total)
7989             goto do_show_total;
7990         goto cleanup;
7991     }
7992 
7993     params = g_new0(virTypedParameter, nparams * MIN(show_count, 128));
7994 
7995     while (show_count) {
7996         int ncpus = MIN(show_count, 128);
7997 
7998         if (virDomainGetCPUStats(dom, params, nparams, cpu, ncpus, 0) < 0)
7999             goto failed_stats;
8000 
8001         for (i = 0; i < ncpus; i++) {
8002             if (params[i * nparams].type == 0) /* this cpu is not in the map */
8003                 continue;
8004             vshPrint(ctl, "CPU%zu:\n", cpu + i);
8005 
8006             for (j = 0; j < nparams; j++)
8007                 vshCPUStatsPrintField(ctl, params + (i * nparams + j));
8008         }
8009         cpu += ncpus;
8010         show_count -= ncpus;
8011         virTypedParamsClear(params, nparams * ncpus);
8012     }
8013     VIR_FREE(params);
8014 
8015     if (!show_total) {
8016         ret = true;
8017         goto cleanup;
8018     }
8019 
8020  do_show_total:
8021     /* get supported num of parameter for total statistics */
8022     if ((nparams = virDomainGetCPUStats(dom, NULL, 0, -1, 1, 0)) < 0)
8023         goto failed_stats;
8024 
8025     if (!nparams) {
8026         vshPrint(ctl, "%s", _("No total stats available"));
8027         goto cleanup;
8028     }
8029 
8030     params = g_new0(virTypedParameter, nparams);
8031 
8032     /* passing start_cpu == -1 gives us domain's total status */
8033     if ((stats_per_cpu = virDomainGetCPUStats(dom, params, nparams,
8034                                               -1, 1, 0)) < 0)
8035         goto failed_stats;
8036 
8037     vshPrint(ctl, _("Total:\n"));
8038     for (i = 0; i < stats_per_cpu; i++)
8039         vshCPUStatsPrintField(ctl, params + i);
8040 
8041     ret = true;
8042 
8043  cleanup:
8044     virTypedParamsFree(params, nparams);
8045     return ret;
8046 
8047  failed_stats:
8048     vshError(ctl, _("Failed to retrieve CPU statistics for domain '%s'"),
8049              virDomainGetName(dom));
8050     goto cleanup;
8051 }
8052 
8053 /*
8054  * "create" command
8055  */
8056 static const vshCmdInfo info_create[] = {
8057     {.name = "help",
8058      .data = N_("create a domain from an XML file")
8059     },
8060     {.name = "desc",
8061      .data = N_("Create a domain.")
8062     },
8063     {.name = NULL}
8064 };
8065 
8066 static const vshCmdOptDef opts_create[] = {
8067     VIRSH_COMMON_OPT_FILE(N_("file containing an XML domain description")),
8068 #ifndef WIN32
8069     {.name = "console",
8070      .type = VSH_OT_BOOL,
8071      .help = N_("attach to console after creation")
8072     },
8073 #endif
8074     {.name = "paused",
8075      .type = VSH_OT_BOOL,
8076      .help = N_("leave the guest paused after creation")
8077     },
8078     {.name = "autodestroy",
8079      .type = VSH_OT_BOOL,
8080      .help = N_("automatically destroy the guest when virsh disconnects")
8081     },
8082     {.name = "pass-fds",
8083      .type = VSH_OT_STRING,
8084      .completer = virshCompleteEmpty,
8085      .help = N_("pass file descriptors N,M,... to the guest")
8086     },
8087     {.name = "validate",
8088      .type = VSH_OT_BOOL,
8089      .help = N_("validate the XML against the schema")
8090     },
8091     {.name = NULL}
8092 };
8093 
8094 static bool
cmdCreate(vshControl * ctl,const vshCmd * cmd)8095 cmdCreate(vshControl *ctl, const vshCmd *cmd)
8096 {
8097     g_autoptr(virshDomain) dom = NULL;
8098     const char *from = NULL;
8099     g_autofree char *buffer = NULL;
8100 #ifndef WIN32
8101     bool console = vshCommandOptBool(cmd, "console");
8102 #endif
8103     unsigned int flags = 0;
8104     size_t nfds = 0;
8105     g_autofree int *fds = NULL;
8106     virshControl *priv = ctl->privData;
8107 
8108     if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
8109         return false;
8110 
8111     if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0)
8112         return false;
8113 
8114     if (cmdStartGetFDs(ctl, cmd, &nfds, &fds) < 0)
8115         return false;
8116 
8117     if (vshCommandOptBool(cmd, "paused"))
8118         flags |= VIR_DOMAIN_START_PAUSED;
8119     if (vshCommandOptBool(cmd, "autodestroy"))
8120         flags |= VIR_DOMAIN_START_AUTODESTROY;
8121     if (vshCommandOptBool(cmd, "validate"))
8122         flags |= VIR_DOMAIN_START_VALIDATE;
8123 
8124     if (nfds)
8125         dom = virDomainCreateXMLWithFiles(priv->conn, buffer, nfds, fds, flags);
8126     else
8127         dom = virDomainCreateXML(priv->conn, buffer, flags);
8128 
8129     if (!dom) {
8130         vshError(ctl, _("Failed to create domain from %s"), from);
8131         return false;
8132     }
8133 
8134     vshPrintExtra(ctl, _("Domain '%s' created from %s\n"),
8135                   virDomainGetName(dom), from);
8136 #ifndef WIN32
8137     if (console)
8138         cmdRunConsole(ctl, dom, NULL, 0);
8139 #endif
8140     return true;
8141 }
8142 
8143 /*
8144  * "define" command
8145  */
8146 static const vshCmdInfo info_define[] = {
8147     {.name = "help",
8148      .data = N_("define (but don't start) a domain from an XML file")
8149     },
8150     {.name = "desc",
8151      .data = N_("Define a domain.")
8152     },
8153     {.name = NULL}
8154 };
8155 
8156 static const vshCmdOptDef opts_define[] = {
8157     VIRSH_COMMON_OPT_FILE(N_("file containing an XML domain description")),
8158     {.name = "validate",
8159      .type = VSH_OT_BOOL,
8160      .help = N_("validate the XML against the schema")
8161     },
8162     {.name = NULL}
8163 };
8164 
8165 static bool
cmdDefine(vshControl * ctl,const vshCmd * cmd)8166 cmdDefine(vshControl *ctl, const vshCmd *cmd)
8167 {
8168     g_autoptr(virshDomain) dom = NULL;
8169     const char *from = NULL;
8170     char *buffer;
8171     unsigned int flags = 0;
8172     virshControl *priv = ctl->privData;
8173 
8174     if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
8175         return false;
8176 
8177     if (vshCommandOptBool(cmd, "validate"))
8178         flags |= VIR_DOMAIN_DEFINE_VALIDATE;
8179 
8180     if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0)
8181         return false;
8182 
8183     if (flags)
8184         dom = virDomainDefineXMLFlags(priv->conn, buffer, flags);
8185     else
8186         dom = virDomainDefineXML(priv->conn, buffer);
8187     VIR_FREE(buffer);
8188 
8189     if (!dom) {
8190         vshError(ctl, _("Failed to define domain from %s"), from);
8191         return false;
8192     }
8193 
8194     vshPrintExtra(ctl, _("Domain '%s' defined from %s\n"),
8195                   virDomainGetName(dom), from);
8196     return true;
8197 }
8198 
8199 /*
8200  * "destroy" command
8201  */
8202 static const vshCmdInfo info_destroy[] = {
8203     {.name = "help",
8204      .data = N_("destroy (stop) a domain")
8205     },
8206     {.name = "desc",
8207      .data = N_("Forcefully stop a given domain, but leave its resources intact.")
8208     },
8209     {.name = NULL}
8210 };
8211 
8212 static const vshCmdOptDef opts_destroy[] = {
8213     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
8214     {.name = "graceful",
8215      .type = VSH_OT_BOOL,
8216      .help = N_("terminate gracefully")
8217     },
8218     {.name = NULL}
8219 };
8220 
8221 static bool
cmdDestroy(vshControl * ctl,const vshCmd * cmd)8222 cmdDestroy(vshControl *ctl, const vshCmd *cmd)
8223 {
8224     g_autoptr(virshDomain) dom = NULL;
8225     const char *name;
8226     unsigned int flags = 0;
8227     int result;
8228 
8229     if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
8230         return false;
8231 
8232     if (vshCommandOptBool(cmd, "graceful"))
8233        flags |= VIR_DOMAIN_DESTROY_GRACEFUL;
8234 
8235     if (flags)
8236        result = virDomainDestroyFlags(dom, VIR_DOMAIN_DESTROY_GRACEFUL);
8237     else
8238        result = virDomainDestroy(dom);
8239 
8240     if (result < 0) {
8241         vshError(ctl, _("Failed to destroy domain '%s'"), name);
8242         return false;
8243     }
8244 
8245     vshPrintExtra(ctl, _("Domain '%s' destroyed\n"), name);
8246     return true;
8247 }
8248 
8249 /*
8250  * "desc" command for managing domain description and title
8251  */
8252 static const vshCmdInfo info_desc[] = {
8253     {.name = "help",
8254      .data = N_("show or set domain's description or title")
8255     },
8256     {.name = "desc",
8257      .data = N_("Allows setting or modifying the description or title of "
8258                 "a domain.")
8259     },
8260     {.name = NULL}
8261 };
8262 
8263 static const vshCmdOptDef opts_desc[] = {
8264     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
8265     VIRSH_COMMON_OPT_LIVE(N_("modify/get running state")),
8266     VIRSH_COMMON_OPT_CONFIG(N_("modify/get persistent configuration")),
8267     VIRSH_COMMON_OPT_CURRENT(N_("modify/get current state configuration")),
8268     {.name = "title",
8269      .type = VSH_OT_BOOL,
8270      .help = N_("modify/get the title instead of description")
8271     },
8272     {.name = "edit",
8273      .type = VSH_OT_BOOL,
8274      .help = N_("open an editor to modify the description")
8275     },
8276     {.name = "new-desc",
8277      .type = VSH_OT_ARGV,
8278      .help = N_("message")
8279     },
8280     {.name = NULL}
8281 };
8282 
8283 static bool
cmdDesc(vshControl * ctl,const vshCmd * cmd)8284 cmdDesc(vshControl *ctl, const vshCmd *cmd)
8285 {
8286     g_autoptr(virshDomain) dom = NULL;
8287     bool config = vshCommandOptBool(cmd, "config");
8288     bool live = vshCommandOptBool(cmd, "live");
8289     bool current = vshCommandOptBool(cmd, "current");
8290 
8291     bool title = vshCommandOptBool(cmd, "title");
8292     bool edit = vshCommandOptBool(cmd, "edit");
8293 
8294     int state;
8295     int type;
8296     char *desc = NULL;
8297     char *desc_edited = NULL;
8298     char *tmp = NULL;
8299     char *tmpstr;
8300     const vshCmdOpt *opt = NULL;
8301     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
8302     bool ret = false;
8303     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
8304 
8305     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
8306     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
8307 
8308     if (config)
8309         flags |= VIR_DOMAIN_AFFECT_CONFIG;
8310     if (live)
8311         flags |= VIR_DOMAIN_AFFECT_LIVE;
8312 
8313     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
8314         return false;
8315 
8316     if ((state = virshDomainState(ctl, dom, NULL)) < 0)
8317         goto cleanup;
8318 
8319     if (title)
8320         type = VIR_DOMAIN_METADATA_TITLE;
8321     else
8322         type = VIR_DOMAIN_METADATA_DESCRIPTION;
8323 
8324     while ((opt = vshCommandOptArgv(ctl, cmd, opt)))
8325         virBufferAsprintf(&buf, "%s ", opt->data);
8326 
8327     virBufferTrim(&buf, " ");
8328 
8329     desc = virBufferContentAndReset(&buf);
8330 
8331     if (edit || desc) {
8332         if (!desc) {
8333                 desc = virshGetDomainDescription(ctl, dom, title,
8334                                            config?VIR_DOMAIN_XML_INACTIVE:0);
8335                 if (!desc)
8336                     goto cleanup;
8337         }
8338 
8339         if (edit) {
8340             /* Create and open the temporary file. */
8341             if (!(tmp = vshEditWriteToTempFile(ctl, desc)))
8342                 goto cleanup;
8343 
8344             /* Start the editor. */
8345             if (vshEditFile(ctl, tmp) == -1)
8346                 goto cleanup;
8347 
8348             /* Read back the edited file. */
8349             if (!(desc_edited = vshEditReadBackFile(ctl, tmp)))
8350                 goto cleanup;
8351 
8352             /* strip a possible newline at the end of file; some
8353              * editors enforce a newline, this makes editing the title
8354              * more convenient */
8355             if (title &&
8356                 (tmpstr = strrchr(desc_edited, '\n')) &&
8357                 *(tmpstr+1) == '\0')
8358                 *tmpstr = '\0';
8359 
8360             /* Compare original XML with edited.  Has it changed at all? */
8361             if (STREQ(desc, desc_edited)) {
8362                 vshPrintExtra(ctl, "%s",
8363                               title ? _("Domain title not changed\n") :
8364                                       _("Domain description not changed\n"));
8365                 ret = true;
8366                 goto cleanup;
8367             }
8368 
8369             VIR_FREE(desc);
8370             desc = g_steal_pointer(&desc_edited);
8371         }
8372 
8373         if (virDomainSetMetadata(dom, type, desc, NULL, NULL, flags) < 0) {
8374             vshError(ctl, "%s",
8375                      title ? _("Failed to set new domain title") :
8376                              _("Failed to set new domain description"));
8377             goto cleanup;
8378         }
8379         vshPrintExtra(ctl, "%s",
8380                       title ? _("Domain title updated successfully") :
8381                               _("Domain description updated successfully"));
8382     } else {
8383         desc = virshGetDomainDescription(ctl, dom, title,
8384                                        config?VIR_DOMAIN_XML_INACTIVE:0);
8385         if (!desc)
8386             goto cleanup;
8387 
8388         if (strlen(desc) > 0)
8389             vshPrint(ctl, "%s", desc);
8390         else
8391             vshPrintExtra(ctl,
8392                           title ? _("No title for domain: %s") :
8393                                   _("No description for domain: %s"),
8394                           virDomainGetName(dom));
8395     }
8396 
8397     ret = true;
8398  cleanup:
8399     VIR_FREE(desc_edited);
8400     VIR_FREE(desc);
8401     if (tmp) {
8402         unlink(tmp);
8403         VIR_FREE(tmp);
8404     }
8405     return ret;
8406 }
8407 
8408 
8409 static const vshCmdInfo info_metadata[] = {
8410     {.name = "help",
8411      .data = N_("show or set domain's custom XML metadata")
8412     },
8413     {.name = "desc",
8414      .data = N_("Shows or modifies the XML metadata of a domain.")
8415     },
8416     {.name = NULL}
8417 };
8418 
8419 static const vshCmdOptDef opts_metadata[] = {
8420     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
8421     {.name = "uri",
8422      .type = VSH_OT_DATA,
8423      .flags = VSH_OFLAG_REQ,
8424      .help = N_("URI of the namespace")
8425     },
8426     VIRSH_COMMON_OPT_LIVE(N_("modify/get running state")),
8427     VIRSH_COMMON_OPT_CONFIG(N_("modify/get persistent configuration")),
8428     VIRSH_COMMON_OPT_CURRENT(N_("modify/get current state configuration")),
8429     {.name = "edit",
8430      .type = VSH_OT_BOOL,
8431      .help = N_("use an editor to change the metadata")
8432     },
8433     {.name = "key",
8434      .type = VSH_OT_STRING,
8435      .help = N_("key to be used as a namespace identifier"),
8436     },
8437     {.name = "set",
8438      .type = VSH_OT_STRING,
8439      .completer = virshCompleteEmpty,
8440      .help = N_("new metadata to set"),
8441     },
8442     {.name = "remove",
8443      .type = VSH_OT_BOOL,
8444      .help = N_("remove the metadata corresponding to an uri")
8445     },
8446     {.name = NULL}
8447 };
8448 
8449 
8450 /* helper to add new metadata using the --edit option */
8451 static char *
virshDomainGetEditMetadata(vshControl * ctl G_GNUC_UNUSED,virDomainPtr dom,const char * uri,unsigned int flags)8452 virshDomainGetEditMetadata(vshControl *ctl G_GNUC_UNUSED,
8453                            virDomainPtr dom,
8454                            const char *uri,
8455                            unsigned int flags)
8456 {
8457     char *ret;
8458 
8459     if (!(ret = virDomainGetMetadata(dom, VIR_DOMAIN_METADATA_ELEMENT,
8460                                      uri, flags))) {
8461         vshResetLibvirtError();
8462         ret = g_strdup("\n");
8463     }
8464 
8465     return ret;
8466 }
8467 
8468 
8469 static bool
cmdMetadata(vshControl * ctl,const vshCmd * cmd)8470 cmdMetadata(vshControl *ctl, const vshCmd *cmd)
8471 {
8472     g_autoptr(virshDomain) dom = NULL;
8473     bool config = vshCommandOptBool(cmd, "config");
8474     bool live = vshCommandOptBool(cmd, "live");
8475     bool current = vshCommandOptBool(cmd, "current");
8476     bool edit = vshCommandOptBool(cmd, "edit");
8477     bool rem = vshCommandOptBool(cmd, "remove");
8478     const char *set = NULL;
8479     const char *uri = NULL;
8480     const char *key = NULL;
8481     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
8482     bool ret = false;
8483 
8484     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
8485     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
8486     VSH_EXCLUSIVE_OPTIONS("edit", "set");
8487     VSH_EXCLUSIVE_OPTIONS("remove", "set");
8488     VSH_EXCLUSIVE_OPTIONS("remove", "edit");
8489 
8490     if (config)
8491         flags |= VIR_DOMAIN_AFFECT_CONFIG;
8492     if (live)
8493         flags |= VIR_DOMAIN_AFFECT_LIVE;
8494 
8495     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
8496         return false;
8497 
8498     if (vshCommandOptStringReq(ctl, cmd, "uri", &uri) < 0 ||
8499         vshCommandOptStringReq(ctl, cmd, "key", &key) < 0 ||
8500         vshCommandOptStringReq(ctl, cmd, "set", &set) < 0)
8501         return false;
8502 
8503     if ((set || edit) && !key) {
8504         vshError(ctl, "%s",
8505                  _("namespace key is required when modifying metadata"));
8506         return false;
8507     }
8508 
8509     if (set || rem) {
8510         if (virDomainSetMetadata(dom, VIR_DOMAIN_METADATA_ELEMENT,
8511                                  set, key, uri, flags))
8512             return false;
8513 
8514         if (rem)
8515             vshPrintExtra(ctl, "%s\n", _("Metadata removed"));
8516         else
8517             vshPrintExtra(ctl, "%s\n", _("Metadata modified"));
8518     } else if (edit) {
8519 #define EDIT_GET_XML \
8520         virshDomainGetEditMetadata(ctl, dom, uri, flags)
8521 #define EDIT_NOT_CHANGED \
8522         do { \
8523             vshPrintExtra(ctl, "%s", _("Metadata not changed")); \
8524             ret = true; \
8525             goto edit_cleanup; \
8526         } while (0)
8527 
8528 #define EDIT_DEFINE \
8529         (virDomainSetMetadata(dom, VIR_DOMAIN_METADATA_ELEMENT, doc_edited, \
8530                               key, uri, flags) == 0)
8531 #include "virsh-edit.c"
8532 
8533         vshPrintExtra(ctl, "%s\n", _("Metadata modified"));
8534     } else {
8535         g_autofree char *data = NULL;
8536         /* get */
8537         if (!(data = virDomainGetMetadata(dom, VIR_DOMAIN_METADATA_ELEMENT,
8538                                           uri, flags)))
8539             return false;
8540 
8541         vshPrint(ctl, "%s\n", data);
8542     }
8543 
8544     ret = true;
8545 
8546  cleanup:
8547     return ret;
8548 }
8549 
8550 
8551 /*
8552  * "inject-nmi" command
8553  */
8554 static const vshCmdInfo info_inject_nmi[] = {
8555     {.name = "help",
8556      .data = N_("Inject NMI to the guest")
8557     },
8558     {.name = "desc",
8559      .data = N_("Inject NMI to the guest domain.")
8560     },
8561     {.name = NULL}
8562 };
8563 
8564 static const vshCmdOptDef opts_inject_nmi[] = {
8565     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
8566     {.name = NULL}
8567 };
8568 
8569 static bool
cmdInjectNMI(vshControl * ctl,const vshCmd * cmd)8570 cmdInjectNMI(vshControl *ctl, const vshCmd *cmd)
8571 {
8572     g_autoptr(virshDomain) dom = NULL;
8573 
8574     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
8575         return false;
8576 
8577     if (virDomainInjectNMI(dom, 0) < 0)
8578         return false;
8579 
8580     return true;
8581 }
8582 
8583 /*
8584  * "send-key" command
8585  */
8586 static const vshCmdInfo info_send_key[] = {
8587     {.name = "help",
8588      .data = N_("Send keycodes to the guest")
8589     },
8590     {.name = "desc",
8591      .data = N_("Send keycodes (integers or symbolic names) to the guest")
8592     },
8593     {.name = NULL}
8594 };
8595 
8596 static const vshCmdOptDef opts_send_key[] = {
8597     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
8598     {.name = "codeset",
8599      .type = VSH_OT_STRING,
8600      .flags = VSH_OFLAG_REQ_OPT,
8601      .completer = virshCodesetNameCompleter,
8602      .help = N_("the codeset of keycodes, default:linux")
8603     },
8604     {.name = "holdtime",
8605      .type = VSH_OT_INT,
8606      .flags = VSH_OFLAG_REQ_OPT,
8607      .help = N_("the time (in milliseconds) how long the keys will be held")
8608     },
8609     {.name = "keycode",
8610      .type = VSH_OT_ARGV,
8611      .flags = VSH_OFLAG_REQ,
8612      .completer = virshKeycodeNameCompleter,
8613      .help = N_("the key code")
8614     },
8615     {.name = NULL}
8616 };
8617 
8618 static int
virshKeyCodeGetInt(const char * key_name)8619 virshKeyCodeGetInt(const char *key_name)
8620 {
8621     unsigned int val;
8622 
8623     if (virStrToLong_uip(key_name, NULL, 0, &val) < 0 || val > 0xffff)
8624         return -1;
8625     return val;
8626 }
8627 
8628 static bool
cmdSendKey(vshControl * ctl,const vshCmd * cmd)8629 cmdSendKey(vshControl *ctl, const vshCmd *cmd)
8630 {
8631     g_autoptr(virshDomain) dom = NULL;
8632     const char *codeset_option;
8633     int codeset;
8634     unsigned int holdtime = 0;
8635     int count = 0;
8636     const vshCmdOpt *opt = NULL;
8637     int keycode;
8638     unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS];
8639 
8640     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
8641         return false;
8642 
8643     if (vshCommandOptStringQuiet(ctl, cmd, "codeset", &codeset_option) <= 0)
8644         codeset_option = "linux";
8645 
8646     if (vshCommandOptUInt(ctl, cmd, "holdtime", &holdtime) < 0)
8647         return false;
8648 
8649     /* The qnum codeset was originally called rfb, so we need to keep
8650      * accepting the old name for backwards compatibility reasons */
8651     if (STREQ(codeset_option, "rfb"))
8652         codeset_option = "qnum";
8653 
8654     codeset = virKeycodeSetTypeFromString(codeset_option);
8655     if (codeset < 0) {
8656         vshError(ctl, _("unknown codeset: '%s'"), codeset_option);
8657         return false;
8658     }
8659 
8660     while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
8661         if (count == VIR_DOMAIN_SEND_KEY_MAX_KEYS) {
8662             vshError(ctl, _("too many keycodes"));
8663             return false;
8664         }
8665 
8666         if ((keycode = virshKeyCodeGetInt(opt->data)) < 0) {
8667             if ((keycode = virKeycodeValueFromString(codeset, opt->data)) < 0) {
8668                 vshError(ctl, _("invalid keycode: '%s'"), opt->data);
8669                 return false;
8670             }
8671         }
8672 
8673         keycodes[count] = keycode;
8674         count++;
8675     }
8676 
8677     if (virDomainSendKey(dom, codeset, holdtime, keycodes, count, 0) < 0)
8678         return false;
8679 
8680     return true;
8681 }
8682 
8683 /*
8684  * "send-process-signal" command
8685  */
8686 static const vshCmdInfo info_send_process_signal[] = {
8687     {.name = "help",
8688      .data = N_("Send signals to processes")
8689     },
8690     {.name = "desc",
8691      .data = N_("Send signals to processes in the guest")
8692     },
8693     {.name = NULL}
8694 };
8695 
8696 static const vshCmdOptDef opts_send_process_signal[] = {
8697     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
8698     {.name = "pid",
8699      .type = VSH_OT_DATA,
8700      .flags = VSH_OFLAG_REQ,
8701      .completer = virshCompleteEmpty,
8702      .help = N_("the process ID")
8703     },
8704     {.name = "signame",
8705      .type = VSH_OT_DATA,
8706      .flags = VSH_OFLAG_REQ,
8707      .completer = virshDomainSignalCompleter,
8708      .help = N_("the signal number or name")
8709     },
8710     {.name = NULL}
8711 };
8712 
8713 VIR_ENUM_IMPL(virshDomainProcessSignal,
8714               VIR_DOMAIN_PROCESS_SIGNAL_LAST,
8715                "nop",    "hup",  "int",  "quit",  "ill", /* 0-4 */
8716               "trap",   "abrt",  "bus",   "fpe", "kill", /* 5-9 */
8717               "usr1",   "segv", "usr2",  "pipe", "alrm", /* 10-14 */
8718               "term", "stkflt", "chld",  "cont", "stop", /* 15-19 */
8719               "tstp",   "ttin", "ttou",   "urg", "xcpu", /* 20-24 */
8720               "xfsz", "vtalrm", "prof", "winch", "poll", /* 25-29 */
8721                "pwr",    "sys",  "rt0",   "rt1",  "rt2", /* 30-34 */
8722                "rt3",    "rt4",  "rt5",   "rt6",  "rt7", /* 35-39 */
8723                "rt8",    "rt9", "rt10",  "rt11", "rt12", /* 40-44 */
8724               "rt13",   "rt14", "rt15",  "rt16", "rt17", /* 45-49 */
8725               "rt18",   "rt19", "rt20",  "rt21", "rt22", /* 50-54 */
8726               "rt23",   "rt24", "rt25",  "rt26", "rt27", /* 55-59 */
8727               "rt28",   "rt29", "rt30",  "rt31", "rt32"); /* 60-64 */
8728 
getSignalNumber(const char * signame)8729 static int getSignalNumber(const char *signame)
8730 {
8731     size_t i;
8732     int signum;
8733     g_autofree char *str = g_strdup(signame);
8734     char *p = str;
8735 
8736     for (i = 0; signame[i]; i++)
8737         p[i] = g_ascii_tolower(signame[i]);
8738 
8739     if (virStrToLong_i(p, NULL, 10, &signum) >= 0)
8740         return signum;
8741 
8742     if (STRPREFIX(p, "sig_"))
8743         p += 4;
8744     else if (STRPREFIX(p, "sig"))
8745         p += 3;
8746 
8747     return virshDomainProcessSignalTypeFromString(p);
8748 }
8749 
8750 static bool
cmdSendProcessSignal(vshControl * ctl,const vshCmd * cmd)8751 cmdSendProcessSignal(vshControl *ctl, const vshCmd *cmd)
8752 {
8753     g_autoptr(virshDomain) dom = NULL;
8754     const char *signame;
8755     long long pid_value;
8756     int signum;
8757 
8758     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
8759         return false;
8760 
8761     if (vshCommandOptLongLong(ctl, cmd, "pid", &pid_value) < 0)
8762         return false;
8763 
8764     if (vshCommandOptStringReq(ctl, cmd, "signame", &signame) < 0)
8765         return false;
8766 
8767     if ((signum = getSignalNumber(signame)) < 0) {
8768         vshError(ctl, _("malformed signal name: %s"), signame);
8769         return false;
8770     }
8771 
8772     if (virDomainSendProcessSignal(dom, pid_value, signum, 0) < 0)
8773         return false;
8774 
8775     return true;
8776 }
8777 
8778 /*
8779  * "setmem" command
8780  */
8781 static const vshCmdInfo info_setmem[] = {
8782     {.name = "help",
8783      .data = N_("change memory allocation")
8784     },
8785     {.name = "desc",
8786      .data = N_("Change the current memory allocation in the guest domain.")
8787     },
8788     {.name = NULL}
8789 };
8790 
8791 static const vshCmdOptDef opts_setmem[] = {
8792     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
8793     {.name = "kilobytes",
8794      .type = VSH_OT_ALIAS,
8795      .help = "size"
8796     },
8797     {.name = "size",
8798      .type = VSH_OT_INT,
8799      .flags = VSH_OFLAG_REQ,
8800      .help = N_("new memory size, as scaled integer (default KiB)")
8801     },
8802     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
8803     VIRSH_COMMON_OPT_DOMAIN_LIVE,
8804     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
8805     {.name = NULL}
8806 };
8807 
8808 static bool
cmdSetmem(vshControl * ctl,const vshCmd * cmd)8809 cmdSetmem(vshControl *ctl, const vshCmd *cmd)
8810 {
8811     g_autoptr(virshDomain) dom = NULL;
8812     unsigned long long bytes = 0;
8813     unsigned long long max;
8814     unsigned long kibibytes = 0;
8815     bool config = vshCommandOptBool(cmd, "config");
8816     bool live = vshCommandOptBool(cmd, "live");
8817     bool current = vshCommandOptBool(cmd, "current");
8818     unsigned int flags = VIR_DOMAIN_AFFECT_LIVE;
8819 
8820     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
8821     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
8822 
8823     if (config || live || current) {
8824         flags = VIR_DOMAIN_AFFECT_CURRENT;
8825 
8826         if (config)
8827             flags |= VIR_DOMAIN_AFFECT_CONFIG;
8828 
8829         if (live)
8830             flags |= VIR_DOMAIN_AFFECT_LIVE;
8831     }
8832 
8833     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
8834         return false;
8835 
8836     /* The API expects 'unsigned long' KiB, so depending on whether we
8837      * are 32-bit or 64-bit determines the maximum we can use.  */
8838     if (sizeof(kibibytes) < sizeof(max))
8839         max = 1024ull * ULONG_MAX;
8840     else
8841         max = ULONG_MAX;
8842     if (vshCommandOptScaledInt(ctl, cmd, "size", &bytes, 1024, max) < 0)
8843         return false;
8844     kibibytes = VIR_DIV_UP(bytes, 1024);
8845 
8846     if (virDomainSetMemoryFlags(dom, kibibytes, flags) < 0)
8847         return false;
8848 
8849     return true;
8850 }
8851 
8852 /*
8853  * "setmaxmem" command
8854  */
8855 static const vshCmdInfo info_setmaxmem[] = {
8856     {.name = "help",
8857      .data = N_("change maximum memory limit")
8858     },
8859     {.name = "desc",
8860      .data = N_("Change the maximum memory allocation limit in the guest domain.")
8861     },
8862     {.name = NULL}
8863 };
8864 
8865 static const vshCmdOptDef opts_setmaxmem[] = {
8866     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
8867     {.name = "kilobytes",
8868      .type = VSH_OT_ALIAS,
8869      .help = "size"
8870     },
8871     {.name = "size",
8872      .type = VSH_OT_INT,
8873      .flags = VSH_OFLAG_REQ,
8874      .help = N_("new maximum memory size, as scaled integer (default KiB)")
8875     },
8876     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
8877     VIRSH_COMMON_OPT_DOMAIN_LIVE,
8878     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
8879     {.name = NULL}
8880 };
8881 
8882 static bool
cmdSetmaxmem(vshControl * ctl,const vshCmd * cmd)8883 cmdSetmaxmem(vshControl *ctl, const vshCmd *cmd)
8884 {
8885     g_autoptr(virshDomain) dom = NULL;
8886     unsigned long long bytes = 0;
8887     unsigned long long max;
8888     unsigned long kibibytes = 0;
8889     bool config = vshCommandOptBool(cmd, "config");
8890     bool live = vshCommandOptBool(cmd, "live");
8891     bool current = vshCommandOptBool(cmd, "current");
8892     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
8893 
8894     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
8895     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
8896 
8897     if (config)
8898         flags |= VIR_DOMAIN_AFFECT_CONFIG;
8899     if (live)
8900         flags |= VIR_DOMAIN_AFFECT_LIVE;
8901 
8902     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
8903         return false;
8904 
8905     /* The API expects 'unsigned long' KiB, so depending on whether we
8906      * are 32-bit or 64-bit determines the maximum we can use.  */
8907     if (sizeof(kibibytes) < sizeof(max))
8908         max = 1024ull * ULONG_MAX;
8909     else
8910         max = ULONG_MAX;
8911     if (vshCommandOptScaledInt(ctl, cmd, "size", &bytes, 1024, max) < 0)
8912         return false;
8913     kibibytes = VIR_DIV_UP(bytes, 1024);
8914 
8915     if (virDomainSetMemoryFlags(dom, kibibytes, flags | VIR_DOMAIN_MEM_MAXIMUM) < 0) {
8916         vshError(ctl, "%s", _("Unable to change MaxMemorySize"));
8917         return false;
8918     }
8919 
8920     return true;
8921 }
8922 
8923 
8924 /*
8925  * "update-memory-device" command
8926  */
8927 static const vshCmdInfo info_update_memory_device[] = {
8928     {.name = "help",
8929      .data = N_("update memory device of a domain")
8930     },
8931     {.name = "desc",
8932      .data = N_("Update values of a memory device of a domain")
8933     },
8934     {.name = NULL}
8935 };
8936 
8937 static const vshCmdOptDef opts_update_memory_device[] = {
8938     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
8939     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
8940     VIRSH_COMMON_OPT_DOMAIN_LIVE,
8941     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
8942     {.name = "print-xml",
8943      .type = VSH_OT_BOOL,
8944      .help = N_("print updated memory device XML instead of executing the change")
8945     },
8946     {.name = "alias",
8947      .type = VSH_OT_STRING,
8948      .completer = virshDomainDeviceAliasCompleter,
8949      .help = N_("memory device alias")
8950     },
8951     {.name = "node",
8952      .type = VSH_OT_INT,
8953      .help = N_("memory device target node")
8954     },
8955     {.name = "requested-size",
8956      .type = VSH_OT_INT,
8957      .help = N_("new value of <requested/> size, as scaled integer (default KiB)")
8958     },
8959     {.name = NULL}
8960 };
8961 
8962 static int
virshGetUpdatedMemoryXML(char ** updatedMemoryXML,vshControl * ctl,const vshCmd * cmd,virDomainPtr dom,unsigned int flags)8963 virshGetUpdatedMemoryXML(char **updatedMemoryXML,
8964                          vshControl *ctl,
8965                          const vshCmd *cmd,
8966                          virDomainPtr dom,
8967                          unsigned int flags)
8968 {
8969     const char *alias = NULL;
8970     bool nodeOpt = false;
8971     unsigned int node = 0;
8972     g_autoptr(xmlDoc) doc = NULL;
8973     g_autoptr(xmlXPathContext) ctxt = NULL;
8974     g_autofree char *xpath = NULL;
8975     int nmems;
8976     g_autofree xmlNodePtr *mems = NULL;
8977     unsigned int domainXMLFlags = 0;
8978 
8979     if (flags & VIR_DOMAIN_AFFECT_CONFIG)
8980         domainXMLFlags |= VIR_DOMAIN_XML_INACTIVE;
8981 
8982     if (virshDomainGetXMLFromDom(ctl, dom, domainXMLFlags, &doc, &ctxt) < 0)
8983         return -1;
8984 
8985     nodeOpt = vshCommandOptBool(cmd, "node");
8986     if (vshCommandOptStringReq(ctl, cmd, "alias", &alias) < 0 ||
8987         vshCommandOptUInt(ctl, cmd, "node", &node) < 0) {
8988         return -1;
8989     }
8990 
8991     if (nodeOpt) {
8992         xpath = g_strdup_printf("/domain/devices/memory[./target/node='%u']", node);
8993     } else if (alias) {
8994         xpath = g_strdup_printf("/domain/devices/memory[./alias/@name='%s']", alias);
8995     } else {
8996         xpath = g_strdup("/domain/devices/memory");
8997     }
8998 
8999     nmems = virXPathNodeSet(xpath, ctxt, &mems);
9000     if (nmems < 0) {
9001         vshSaveLibvirtError();
9002         return -1;
9003     } else if (nmems == 0) {
9004         vshError(ctl, _("no memory device found"));
9005         return -1;
9006     } else if (nmems > 1) {
9007         vshError(ctl, _("multiple memory devices found, use --alias or --node to select one"));
9008         return -1;
9009     }
9010 
9011     ctxt->node = mems[0];
9012 
9013     if (vshCommandOptBool(cmd, "requested-size")) {
9014         xmlNodePtr requestedSizeNode;
9015         g_autofree char *kibibytesStr = NULL;
9016         unsigned long long bytes = 0;
9017         unsigned long kibibytes = 0;
9018 
9019         if (vshCommandOptScaledInt(ctl, cmd, "requested-size", &bytes, 1024, ULLONG_MAX) < 0)
9020             return -1;
9021         kibibytes = VIR_DIV_UP(bytes, 1024);
9022 
9023         requestedSizeNode = virXPathNode("./target/requested", ctxt);
9024 
9025         if (!requestedSizeNode) {
9026             vshError(ctl, _("virtio-mem device is missing <requested/>"));
9027             return -1;
9028         }
9029 
9030         kibibytesStr = g_strdup_printf("%lu", kibibytes);
9031         xmlNodeSetContent(requestedSizeNode, BAD_CAST kibibytesStr);
9032     }
9033 
9034     if (!(*updatedMemoryXML = virXMLNodeToString(doc, mems[0]))) {
9035         vshSaveLibvirtError();
9036         return -1;
9037     }
9038 
9039     return 0;
9040 }
9041 
9042 static bool
cmdUpdateMemoryDevice(vshControl * ctl,const vshCmd * cmd)9043 cmdUpdateMemoryDevice(vshControl *ctl, const vshCmd *cmd)
9044 {
9045     g_autoptr(virshDomain) dom = NULL;
9046     bool config = vshCommandOptBool(cmd, "config");
9047     bool live = vshCommandOptBool(cmd, "live");
9048     bool current = vshCommandOptBool(cmd, "current");
9049     g_autofree char *updatedMemoryXML = NULL;
9050     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
9051 
9052     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
9053     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
9054     VSH_EXCLUSIVE_OPTIONS("node", "alias");
9055 
9056     if (config)
9057         flags |= VIR_DOMAIN_AFFECT_CONFIG;
9058     if (live)
9059         flags |= VIR_DOMAIN_AFFECT_LIVE;
9060 
9061     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
9062         return false;
9063 
9064     if (virshGetUpdatedMemoryXML(&updatedMemoryXML, ctl, cmd, dom, flags) < 0)
9065         return false;
9066 
9067     if (vshCommandOptBool(cmd, "print-xml")) {
9068         vshPrint(ctl, "%s", updatedMemoryXML);
9069     } else {
9070         if (virDomainUpdateDeviceFlags(dom, updatedMemoryXML, flags) < 0)
9071             return false;
9072     }
9073 
9074     return true;
9075 }
9076 
9077 
9078 /*
9079  * "memtune" command
9080  */
9081 static const vshCmdInfo info_memtune[] = {
9082     {.name = "help",
9083      .data = N_("Get or set memory parameters")
9084     },
9085     {.name = "desc",
9086      .data = N_("Get or set the current memory parameters for a guest"
9087                 " domain.\n"
9088                 "    To get the memory parameters use following command: \n\n"
9089                 "    virsh # memtune <domain>")
9090     },
9091     {.name = NULL}
9092 };
9093 
9094 static const vshCmdOptDef opts_memtune[] = {
9095     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
9096     {.name = "hard-limit",
9097      .type = VSH_OT_INT,
9098      .help = N_("Max memory, as scaled integer (default KiB)")
9099     },
9100     {.name = "soft-limit",
9101      .type = VSH_OT_INT,
9102      .help = N_("Memory during contention, as scaled integer (default KiB)")
9103     },
9104     {.name = "swap-hard-limit",
9105      .type = VSH_OT_INT,
9106      .help = N_("Max memory plus swap, as scaled integer (default KiB)")
9107     },
9108     {.name = "min-guarantee",
9109      .type = VSH_OT_INT,
9110      .help = N_("Min guaranteed memory, as scaled integer (default KiB)")
9111     },
9112     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
9113     VIRSH_COMMON_OPT_DOMAIN_LIVE,
9114     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
9115     {.name = NULL}
9116 };
9117 
9118 /**
9119  * virshMemtuneGetSize
9120  *
9121  * @cmd: pointer to vshCmd
9122  * @name: name of a parameter for which we would like to get a value
9123  * @value: pointer to variable where the value will be stored
9124  *
9125  * This function will parse virsh command line in order to load a value of
9126  * specified parameter. If the value is -1 we will handle it as unlimited and
9127  * use VIR_DOMAIN_MEMORY_PARAM_UNLIMITED instead.
9128  *
9129  * Returns:
9130  *  >0 if option found and valid
9131  *  0 if option not found and not required
9132  *  <0 in all other cases
9133  */
9134 static int
virshMemtuneGetSize(vshControl * ctl,const vshCmd * cmd,const char * name,long long * value)9135 virshMemtuneGetSize(vshControl *ctl, const vshCmd *cmd,
9136                     const char *name, long long *value)
9137 {
9138     int ret;
9139     unsigned long long tmp;
9140     const char *str;
9141     char *end;
9142 
9143     ret = vshCommandOptStringQuiet(ctl, cmd, name, &str);
9144     if (ret <= 0)
9145         return ret;
9146     if (virStrToLong_ll(str, &end, 10, value) < 0)
9147         return -1;
9148     if (*value < 0) {
9149         *value = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
9150         return 1;
9151     }
9152     tmp = *value;
9153     if (virScaleInteger(&tmp, end, 1024, LLONG_MAX) < 0)
9154         return -1;
9155     *value = VIR_DIV_UP(tmp, 1024);
9156     return 1;
9157 }
9158 
9159 static bool
cmdMemtune(vshControl * ctl,const vshCmd * cmd)9160 cmdMemtune(vshControl *ctl, const vshCmd *cmd)
9161 {
9162     g_autoptr(virshDomain) dom = NULL;
9163     long long tmpVal;
9164     int nparams = 0;
9165     int maxparams = 0;
9166     int rc;
9167     size_t i;
9168     virTypedParameterPtr params = NULL;
9169     bool ret = false;
9170     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
9171     bool current = vshCommandOptBool(cmd, "current");
9172     bool config = vshCommandOptBool(cmd, "config");
9173     bool live = vshCommandOptBool(cmd, "live");
9174 
9175     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
9176     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
9177 
9178     if (config)
9179         flags |= VIR_DOMAIN_AFFECT_CONFIG;
9180     if (live)
9181         flags |= VIR_DOMAIN_AFFECT_LIVE;
9182 
9183     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
9184         return false;
9185 
9186 #define PARSE_MEMTUNE_PARAM(NAME, FIELD) \
9187     if ((rc = virshMemtuneGetSize(ctl, cmd, NAME, &tmpVal)) < 0) { \
9188         vshError(ctl, _("Unable to parse integer parameter %s"), NAME); \
9189         goto cleanup; \
9190     } \
9191     if (rc == 1) { \
9192         if (virTypedParamsAddULLong(&params, &nparams, &maxparams, \
9193                                     FIELD, tmpVal) < 0) \
9194             goto save_error; \
9195     }
9196 
9197 
9198     PARSE_MEMTUNE_PARAM("hard-limit", VIR_DOMAIN_MEMORY_HARD_LIMIT);
9199     PARSE_MEMTUNE_PARAM("soft-limit", VIR_DOMAIN_MEMORY_SOFT_LIMIT);
9200     PARSE_MEMTUNE_PARAM("swap-hard-limit", VIR_DOMAIN_MEMORY_SWAP_HARD_LIMIT);
9201     PARSE_MEMTUNE_PARAM("min-guarantee", VIR_DOMAIN_MEMORY_MIN_GUARANTEE);
9202 
9203 #undef PARSE_MEMTUNE_PARAM
9204 
9205     if (nparams == 0) {
9206         /* get the number of memory parameters */
9207         if (virDomainGetMemoryParameters(dom, NULL, &nparams, flags) != 0) {
9208             vshError(ctl, "%s",
9209                      _("Unable to get number of memory parameters"));
9210             goto cleanup;
9211         }
9212 
9213         if (nparams == 0) {
9214             /* nothing to output */
9215             ret = true;
9216             goto cleanup;
9217         }
9218 
9219         /* now go get all the memory parameters */
9220         params = g_new0(virTypedParameter, nparams);
9221         if (virDomainGetMemoryParameters(dom, params, &nparams, flags) != 0) {
9222             vshError(ctl, "%s", _("Unable to get memory parameters"));
9223             goto cleanup;
9224         }
9225 
9226         for (i = 0; i < nparams; i++) {
9227             if (params[i].type == VIR_TYPED_PARAM_ULLONG &&
9228                 params[i].value.ul == VIR_DOMAIN_MEMORY_PARAM_UNLIMITED) {
9229                 vshPrint(ctl, "%-15s: %s\n", params[i].field, _("unlimited"));
9230             } else {
9231                 g_autofree char *str = vshGetTypedParamValue(ctl, &params[i]);
9232                 vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
9233             }
9234         }
9235     } else {
9236         if (virDomainSetMemoryParameters(dom, params, nparams, flags) != 0)
9237             goto error;
9238     }
9239 
9240     ret = true;
9241 
9242  cleanup:
9243     virTypedParamsFree(params, nparams);
9244     return ret;
9245 
9246  save_error:
9247     vshSaveLibvirtError();
9248  error:
9249     vshError(ctl, "%s", _("Unable to change memory parameters"));
9250     goto cleanup;
9251 }
9252 
9253 /*
9254  * "perf" command
9255  */
9256 static const vshCmdInfo info_perf[] = {
9257     {.name = "help",
9258         .data = N_("Get or set perf event")
9259     },
9260     {.name = "desc",
9261         .data = N_("Get or set the current perf events for a guest"
9262                    " domain.\n"
9263                    "    To get the perf events list use following command: \n\n"
9264                    "    virsh # perf <domain>")
9265     },
9266     {.name = NULL}
9267 };
9268 
9269 static const vshCmdOptDef opts_perf[] = {
9270     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
9271     {.name = "enable",
9272      .type = VSH_OT_STRING,
9273      .completer = virshDomainPerfEnableCompleter,
9274      .help = N_("perf events which will be enabled")
9275     },
9276     {.name = "disable",
9277      .type = VSH_OT_STRING,
9278      .completer = virshDomainPerfDisableCompleter,
9279      .help = N_("perf events which will be disabled")
9280     },
9281     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
9282     VIRSH_COMMON_OPT_DOMAIN_LIVE,
9283     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
9284     {.name = NULL}
9285 };
9286 
9287 static int
virshParseEventStr(const char * event,bool state,virTypedParameterPtr * params,int * nparams,int * maxparams)9288 virshParseEventStr(const char *event,
9289                    bool state,
9290                    virTypedParameterPtr *params,
9291                    int *nparams,
9292                    int *maxparams)
9293 {
9294     g_auto(GStrv) tok = NULL;
9295     GStrv next;
9296 
9297     if (!(tok = g_strsplit(event, ",", 0)))
9298         return -1;
9299 
9300     for (next = tok; *next; next++) {
9301         if (*next[0] == '\0')
9302             continue;
9303 
9304         if (virTypedParamsAddBoolean(params, nparams, maxparams, *next, state) < 0)
9305             return -1;
9306     }
9307 
9308     return 0;
9309 }
9310 
9311 static void
virshPrintPerfStatus(vshControl * ctl,virTypedParameterPtr params,int nparams)9312 virshPrintPerfStatus(vshControl *ctl, virTypedParameterPtr params, int nparams)
9313 {
9314     size_t i;
9315 
9316     for (i = 0; i < nparams; i++) {
9317         if (params[i].type == VIR_TYPED_PARAM_BOOLEAN &&
9318             params[i].value.b) {
9319             vshPrintExtra(ctl, "%-15s: %s\n", params[i].field, _("enabled"));
9320         } else {
9321             vshPrintExtra(ctl, "%-15s: %s\n", params[i].field, _("disabled"));
9322         }
9323     }
9324 }
9325 
9326 static bool
cmdPerf(vshControl * ctl,const vshCmd * cmd)9327 cmdPerf(vshControl *ctl, const vshCmd *cmd)
9328 {
9329     g_autoptr(virshDomain) dom = NULL;
9330     int nparams = 0;
9331     int maxparams = 0;
9332     virTypedParameterPtr params = NULL;
9333     bool ret = false;
9334     const char *enable = NULL, *disable = NULL;
9335     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
9336     bool current = vshCommandOptBool(cmd, "current");
9337     bool config = vshCommandOptBool(cmd, "config");
9338     bool live = vshCommandOptBool(cmd, "live");
9339 
9340     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
9341     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
9342 
9343     if (config)
9344         flags |= VIR_DOMAIN_AFFECT_CONFIG;
9345     if (live)
9346         flags |= VIR_DOMAIN_AFFECT_LIVE;
9347 
9348     if (vshCommandOptStringReq(ctl, cmd, "enable", &enable) < 0 ||
9349         vshCommandOptStringReq(ctl, cmd, "disable", &disable) < 0)
9350         return false;
9351 
9352     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
9353         return false;
9354 
9355     if (enable && virshParseEventStr(enable, true, &params,
9356                                      &nparams, &maxparams) < 0)
9357         goto cleanup;
9358 
9359     if (disable && virshParseEventStr(disable, false, &params,
9360                                       &nparams, &maxparams) < 0)
9361         goto cleanup;
9362 
9363     if (nparams == 0) {
9364         if (virDomainGetPerfEvents(dom, &params, &nparams, flags) != 0) {
9365             vshError(ctl, "%s", _("Unable to get perf events"));
9366             goto cleanup;
9367         }
9368         virshPrintPerfStatus(ctl, params, nparams);
9369     } else {
9370         if (virDomainSetPerfEvents(dom, params, nparams, flags) != 0) {
9371             vshError(ctl, "%s", _("Unable to enable/disable perf events"));
9372             goto cleanup;
9373         } else {
9374             virshPrintPerfStatus(ctl, params, nparams);
9375         }
9376     }
9377 
9378     ret = true;
9379  cleanup:
9380     virTypedParamsFree(params, nparams);
9381     return ret;
9382 }
9383 
9384 
9385 /*
9386  * "numatune" command
9387  */
9388 static const vshCmdInfo info_numatune[] = {
9389     {.name = "help",
9390      .data = N_("Get or set numa parameters")
9391     },
9392     {.name = "desc",
9393      .data = N_("Get or set the current numa parameters for a guest"
9394                 " domain.\n"
9395                 "    To get the numa parameters use following command: \n\n"
9396                 "    virsh # numatune <domain>")
9397     },
9398     {.name = NULL}
9399 };
9400 
9401 static const vshCmdOptDef opts_numatune[] = {
9402     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
9403     {.name = "mode",
9404      .type = VSH_OT_STRING,
9405      .help = N_("NUMA mode, one of strict, preferred and interleave \n"
9406                 "or a number from the virDomainNumatuneMemMode enum")
9407     },
9408     {.name = "nodeset",
9409      .type = VSH_OT_STRING,
9410      .help = N_("NUMA node selections to set")
9411     },
9412     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
9413     VIRSH_COMMON_OPT_DOMAIN_LIVE,
9414     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
9415     {.name = NULL}
9416 };
9417 
9418 static bool
cmdNumatune(vshControl * ctl,const vshCmd * cmd)9419 cmdNumatune(vshControl * ctl, const vshCmd * cmd)
9420 {
9421     g_autoptr(virshDomain) dom = NULL;
9422     int nparams = 0;
9423     int maxparams = 0;
9424     size_t i;
9425     virTypedParameterPtr params = NULL;
9426     const char *nodeset = NULL;
9427     bool ret = false;
9428     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
9429     bool current = vshCommandOptBool(cmd, "current");
9430     bool config = vshCommandOptBool(cmd, "config");
9431     bool live = vshCommandOptBool(cmd, "live");
9432     const char *mode = NULL;
9433 
9434     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
9435     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
9436 
9437     if (config)
9438         flags |= VIR_DOMAIN_AFFECT_CONFIG;
9439     if (live)
9440         flags |= VIR_DOMAIN_AFFECT_LIVE;
9441 
9442     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
9443         return false;
9444 
9445     if (vshCommandOptStringReq(ctl, cmd, "nodeset", &nodeset) < 0)
9446         goto cleanup;
9447 
9448     if (nodeset &&
9449         virTypedParamsAddString(&params, &nparams, &maxparams,
9450                                 VIR_DOMAIN_NUMA_NODESET, nodeset) < 0)
9451         goto save_error;
9452 
9453     if (vshCommandOptStringReq(ctl, cmd, "mode", &mode) < 0)
9454         goto cleanup;
9455 
9456     if (mode) {
9457         int m;
9458         /* Accept string or integer, in case server understands newer
9459          * integer than what strings we were compiled with
9460          */
9461         if ((m = virDomainNumatuneMemModeTypeFromString(mode)) < 0 &&
9462             virStrToLong_i(mode, NULL, 0, &m) < 0) {
9463             vshError(ctl, _("Invalid mode: %s"), mode);
9464             goto cleanup;
9465         }
9466 
9467         if (virTypedParamsAddInt(&params, &nparams, &maxparams,
9468                                  VIR_DOMAIN_NUMA_MODE, m) < 0)
9469             goto save_error;
9470     }
9471 
9472     if (nparams == 0) {
9473         /* get the number of numa parameters */
9474         if (virDomainGetNumaParameters(dom, NULL, &nparams, flags) != 0) {
9475             vshError(ctl, "%s",
9476                      _("Unable to get number of memory parameters"));
9477             goto cleanup;
9478         }
9479 
9480         if (nparams == 0) {
9481             /* nothing to output */
9482             ret = true;
9483             goto cleanup;
9484         }
9485 
9486         /* now go get all the numa parameters */
9487         params = g_new0(virTypedParameter, nparams);
9488         if (virDomainGetNumaParameters(dom, params, &nparams, flags) != 0) {
9489             vshError(ctl, "%s", _("Unable to get numa parameters"));
9490             goto cleanup;
9491         }
9492 
9493         for (i = 0; i < nparams; i++) {
9494             if (params[i].type == VIR_TYPED_PARAM_INT &&
9495                 STREQ(params[i].field, VIR_DOMAIN_NUMA_MODE)) {
9496                 vshPrint(ctl, "%-15s: %s\n", params[i].field,
9497                          virDomainNumatuneMemModeTypeToString(params[i].value.i));
9498             } else {
9499                 g_autofree char *str = vshGetTypedParamValue(ctl, &params[i]);
9500                 vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
9501             }
9502         }
9503     } else {
9504         if (virDomainSetNumaParameters(dom, params, nparams, flags) != 0)
9505             goto error;
9506     }
9507 
9508     ret = true;
9509 
9510  cleanup:
9511     virTypedParamsFree(params, nparams);
9512     return ret;
9513 
9514  save_error:
9515     vshSaveLibvirtError();
9516  error:
9517     vshError(ctl, "%s", _("Unable to change numa parameters"));
9518     goto cleanup;
9519 }
9520 
9521 /*
9522  * "qemu-monitor-command" command
9523  */
9524 static const vshCmdInfo info_qemu_monitor_command[] = {
9525     {.name = "help",
9526      .data = N_("QEMU Monitor Command")
9527     },
9528     {.name = "desc",
9529      .data = N_("QEMU Monitor Command")
9530     },
9531     {.name = NULL}
9532 };
9533 
9534 static const vshCmdOptDef opts_qemu_monitor_command[] = {
9535     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
9536     {.name = "hmp",
9537      .type = VSH_OT_BOOL,
9538      .help = N_("command is in human monitor protocol")
9539     },
9540     {.name = "pretty",
9541      .type = VSH_OT_BOOL,
9542      .help = N_("pretty-print any qemu monitor protocol output")
9543     },
9544     {.name = "return-value",
9545      .type = VSH_OT_BOOL,
9546      .help = N_("extract the value of the 'return' key from the returned string")
9547     },
9548     {.name = "cmd",
9549      .type = VSH_OT_ARGV,
9550      .flags = VSH_OFLAG_REQ,
9551      .help = N_("command")
9552     },
9553     {.name = NULL}
9554 };
9555 
9556 
9557 static char *
cmdQemuMonitorCommandConcatCmd(vshControl * ctl,const vshCmd * cmd,const vshCmdOpt * opt)9558 cmdQemuMonitorCommandConcatCmd(vshControl *ctl,
9559                                const vshCmd *cmd,
9560                                const vshCmdOpt *opt)
9561 {
9562     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
9563 
9564     while ((opt = vshCommandOptArgv(ctl, cmd, opt)))
9565         virBufferAsprintf(&buf, "%s ", opt->data);
9566 
9567     virBufferTrim(&buf, " ");
9568 
9569     return virBufferContentAndReset(&buf);
9570 }
9571 
9572 
9573 static char *
cmdQemuMonitorCommandQMPWrap(vshControl * ctl,const vshCmd * cmd)9574 cmdQemuMonitorCommandQMPWrap(vshControl *ctl,
9575                              const vshCmd *cmd)
9576 {
9577     g_autofree char *fullcmd = cmdQemuMonitorCommandConcatCmd(ctl, cmd, NULL);
9578     g_autoptr(virJSONValue) fullcmdjson = virJSONValueFromString(fullcmd);
9579     g_autofree char *fullargs = NULL;
9580     g_autoptr(virJSONValue) fullargsjson = NULL;
9581     const vshCmdOpt *opt = NULL;
9582     const char *commandname = NULL;
9583     g_autoptr(virJSONValue) command = NULL;
9584     g_autoptr(virJSONValue) arguments = NULL;
9585 
9586     /* if we've got a JSON object, pass it through */
9587     if (virJSONValueIsObject(fullcmdjson))
9588         return g_steal_pointer(&fullcmd);
9589 
9590     /* we try to wrap the command and possible arguments into a JSON object, if
9591      * we as fall back we pass through what we've got from the user */
9592 
9593     if ((opt = vshCommandOptArgv(ctl, cmd, opt)))
9594         commandname = opt->data;
9595 
9596     /* now we process arguments similarly to how we've dealt with the full command */
9597     if ((fullargs = cmdQemuMonitorCommandConcatCmd(ctl, cmd, opt)))
9598         fullargsjson = virJSONValueFromString(fullargs);
9599 
9600     /* for empty args or a valid JSON object we just use that */
9601     if (!fullargs || virJSONValueIsObject(fullargsjson)) {
9602         arguments = g_steal_pointer(&fullargsjson);
9603     } else {
9604         /* for a non-object we try to concatenate individual _ARGV bits into a
9605          * JSON object wrapper and try using that */
9606         g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
9607 
9608         virBufferAddLit(&buf, "{");
9609         /* opt points to the _ARGV option bit containing the command so we'll
9610          * iterate through the arguments now */
9611         while ((opt = vshCommandOptArgv(ctl, cmd, opt)))
9612             virBufferAsprintf(&buf, "%s,", opt->data);
9613 
9614         virBufferTrim(&buf, ",");
9615         virBufferAddLit(&buf, "}");
9616 
9617         if (!(arguments = virJSONValueFromString(virBufferCurrentContent(&buf)))) {
9618             vshError(ctl, _("failed to wrap arguments '%s' into a QMP command wrapper"),
9619                      fullargs);
9620             return NULL;
9621         }
9622     }
9623 
9624     if (virJSONValueObjectAdd(&command,
9625                               "s:execute", commandname,
9626                               "A:arguments", &arguments,
9627                               NULL) < 0)
9628         return NULL;
9629 
9630     return virJSONValueToString(command, false);
9631 }
9632 
9633 
9634 static bool
cmdQemuMonitorCommand(vshControl * ctl,const vshCmd * cmd)9635 cmdQemuMonitorCommand(vshControl *ctl, const vshCmd *cmd)
9636 {
9637     g_autoptr(virshDomain) dom = NULL;
9638     g_autofree char *monitor_cmd = NULL;
9639     g_autofree char *result = NULL;
9640     g_autoptr(virJSONValue) resultjson = NULL;
9641     unsigned int flags = 0;
9642     bool pretty = vshCommandOptBool(cmd, "pretty");
9643     bool returnval = vshCommandOptBool(cmd, "return-value");
9644     virJSONValue *formatjson;
9645     g_autofree char *jsonstr = NULL;
9646 
9647     VSH_EXCLUSIVE_OPTIONS("hmp", "pretty");
9648     VSH_EXCLUSIVE_OPTIONS("hmp", "return-value");
9649 
9650     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
9651         return false;
9652 
9653     if (vshCommandOptBool(cmd, "hmp")) {
9654         flags |= VIR_DOMAIN_QEMU_MONITOR_COMMAND_HMP;
9655         monitor_cmd = cmdQemuMonitorCommandConcatCmd(ctl, cmd, NULL);
9656     } else {
9657         monitor_cmd = cmdQemuMonitorCommandQMPWrap(ctl, cmd);
9658     }
9659 
9660     if (!monitor_cmd) {
9661         vshSaveLibvirtError();
9662         return NULL;
9663     }
9664 
9665     if (virDomainQemuMonitorCommand(dom, monitor_cmd, &result, flags) < 0)
9666         return false;
9667 
9668     if (returnval || pretty) {
9669         resultjson = virJSONValueFromString(result);
9670 
9671         if (returnval && !resultjson) {
9672             vshError(ctl, "failed to parse JSON returned by qemu");
9673             return false;
9674         }
9675     }
9676 
9677     /* print raw non-prettified result */
9678     if (!resultjson) {
9679         vshPrint(ctl, "%s\n", result);
9680         return true;
9681     }
9682 
9683     if (returnval) {
9684         if (!(formatjson = virJSONValueObjectGet(resultjson, "return"))) {
9685             vshError(ctl, "'return' member missing");
9686             return false;
9687         }
9688     } else {
9689         formatjson = resultjson;
9690     }
9691 
9692     jsonstr = virJSONValueToString(formatjson, pretty);
9693     virTrimSpaces(jsonstr, NULL);
9694     vshPrint(ctl, "%s", jsonstr);
9695     return true;
9696 }
9697 
9698 /*
9699  * "qemu-monitor-event" command
9700  */
9701 
9702 struct virshQemuEventData {
9703     vshControl *ctl;
9704     bool loop;
9705     bool pretty;
9706     bool timestamp;
9707     int count;
9708 };
9709 typedef struct virshQemuEventData virshQemuEventData;
9710 
9711 static void
virshEventQemuPrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,const char * event,long long seconds,unsigned int micros,const char * details,void * opaque)9712 virshEventQemuPrint(virConnectPtr conn G_GNUC_UNUSED,
9713                     virDomainPtr dom,
9714                     const char *event,
9715                     long long seconds,
9716                     unsigned int micros,
9717                     const char *details,
9718                     void *opaque)
9719 {
9720     virshQemuEventData *data = opaque;
9721     virJSONValue *pretty = NULL;
9722     g_autofree char *str = NULL;
9723 
9724     if (!data->loop && data->count)
9725         return;
9726     if (data->pretty && details) {
9727         pretty = virJSONValueFromString(details);
9728         if (pretty && (str = virJSONValueToString(pretty, true)))
9729             details = str;
9730     }
9731 
9732     if (data->timestamp) {
9733         char timestamp[VIR_TIME_STRING_BUFLEN];
9734 
9735         if (virTimeStringNowRaw(timestamp) < 0)
9736             timestamp[0] = '\0';
9737 
9738         vshPrint(data->ctl, "%s: event %s for domain '%s': %s\n",
9739                  timestamp, event, virDomainGetName(dom), NULLSTR(details));
9740     } else {
9741         vshPrint(data->ctl, "event %s at %lld.%06u for domain '%s': %s\n",
9742                  event, seconds, micros, virDomainGetName(dom), NULLSTR(details));
9743     }
9744 
9745     data->count++;
9746     if (!data->loop)
9747         vshEventDone(data->ctl);
9748 }
9749 
9750 static const vshCmdInfo info_qemu_monitor_event[] = {
9751     {.name = "help",
9752      .data = N_("QEMU Monitor Events")
9753     },
9754     {.name = "desc",
9755      .data = N_("Listen for QEMU Monitor Events")
9756     },
9757     {.name = NULL}
9758 };
9759 
9760 static const vshCmdOptDef opts_qemu_monitor_event[] = {
9761     VIRSH_COMMON_OPT_DOMAIN_OT_STRING(N_("filter by domain name, id or uuid"),
9762                                       0, 0),
9763     {.name = "event",
9764      .type = VSH_OT_STRING,
9765      .help = N_("filter by event name")
9766     },
9767     {.name = "pretty",
9768      .type = VSH_OT_BOOL,
9769      .help = N_("pretty-print any JSON output")
9770     },
9771     {.name = "loop",
9772      .type = VSH_OT_BOOL,
9773      .help = N_("loop until timeout or interrupt, rather than one-shot")
9774     },
9775     {.name = "timeout",
9776      .type = VSH_OT_INT,
9777      .help = N_("timeout seconds")
9778     },
9779     {.name = "regex",
9780      .type = VSH_OT_BOOL,
9781      .help = N_("treat event as a regex rather than literal filter")
9782     },
9783     {.name = "no-case",
9784      .type = VSH_OT_BOOL,
9785      .help = N_("treat event case-insensitively")
9786     },
9787     {.name = "timestamp",
9788      .type = VSH_OT_BOOL,
9789      .help = N_("show timestamp for each printed event")
9790     },
9791     {.name = NULL}
9792 };
9793 
9794 static bool
cmdQemuMonitorEvent(vshControl * ctl,const vshCmd * cmd)9795 cmdQemuMonitorEvent(vshControl *ctl, const vshCmd *cmd)
9796 {
9797     g_autoptr(virshDomain) dom = NULL;
9798     bool ret = false;
9799     unsigned int flags = 0;
9800     int eventId = -1;
9801     int timeout = 0;
9802     const char *event = NULL;
9803     virshQemuEventData data;
9804     virshControl *priv = ctl->privData;
9805 
9806     if (vshCommandOptBool(cmd, "regex"))
9807         flags |= VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_REGEX;
9808     if (vshCommandOptBool(cmd, "no-case"))
9809         flags |= VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_NOCASE;
9810 
9811     data.ctl = ctl;
9812     data.loop = vshCommandOptBool(cmd, "loop");
9813     data.pretty = vshCommandOptBool(cmd, "pretty");
9814     data.timestamp = vshCommandOptBool(cmd, "timestamp");
9815     data.count = 0;
9816     if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
9817         return false;
9818     if (vshCommandOptStringReq(ctl, cmd, "event", &event) < 0)
9819         return false;
9820 
9821     if (vshCommandOptBool(cmd, "domain"))
9822         if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
9823             goto cleanup;
9824 
9825     if (vshEventStart(ctl, timeout) < 0)
9826         goto cleanup;
9827 
9828     if ((eventId = virConnectDomainQemuMonitorEventRegister(priv->conn, dom,
9829                                                             event,
9830                                                             virshEventQemuPrint,
9831                                                             &data, NULL,
9832                                                             flags)) < 0)
9833         goto cleanup;
9834     switch (vshEventWait(ctl)) {
9835     case VSH_EVENT_INTERRUPT:
9836         vshPrint(ctl, _("event loop interrupted\n"));
9837         break;
9838     case VSH_EVENT_TIMEOUT:
9839         vshPrint(ctl, _("event loop timed out\n"));
9840         break;
9841     case VSH_EVENT_DONE:
9842         break;
9843     default:
9844         goto cleanup;
9845     }
9846     vshPrint(ctl, _("events received: %d\n"), data.count);
9847     if (data.count)
9848         ret = true;
9849 
9850  cleanup:
9851     vshEventCleanup(ctl);
9852     if (eventId >= 0 &&
9853         virConnectDomainQemuMonitorEventDeregister(priv->conn, eventId) < 0)
9854         ret = false;
9855 
9856     return ret;
9857 }
9858 
9859 /*
9860  * "qemu-attach" command
9861  */
9862 static const vshCmdInfo info_qemu_attach[] = {
9863     {.name = "help",
9864      .data = N_("QEMU Attach")
9865     },
9866     {.name = "desc",
9867      .data = N_("QEMU Attach")
9868     },
9869     {.name = NULL}
9870 };
9871 
9872 static const vshCmdOptDef opts_qemu_attach[] = {
9873     {.name = "pid",
9874      .type = VSH_OT_DATA,
9875      .flags = VSH_OFLAG_REQ,
9876      .completer = virshCompleteEmpty,
9877      .help = N_("pid")
9878     },
9879     {.name = NULL}
9880 };
9881 
9882 static bool
cmdQemuAttach(vshControl * ctl,const vshCmd * cmd)9883 cmdQemuAttach(vshControl *ctl, const vshCmd *cmd)
9884 {
9885     g_autoptr(virshDomain) dom = NULL;
9886     unsigned int flags = 0;
9887     unsigned int pid_value; /* API uses unsigned int, not pid_t */
9888     virshControl *priv = ctl->privData;
9889 
9890     if (vshCommandOptUInt(ctl, cmd, "pid", &pid_value) <= 0)
9891         return false;
9892 
9893     if (!(dom = virDomainQemuAttach(priv->conn, pid_value, flags))) {
9894         vshError(ctl, _("Failed to attach to pid %u"), pid_value);
9895         return false;
9896     }
9897 
9898     vshPrintExtra(ctl, _("Domain '%s' attached to pid %u\n"),
9899                   virDomainGetName(dom), pid_value);
9900     return true;
9901 }
9902 
9903 /*
9904  * "qemu-agent-command" command
9905  */
9906 static const vshCmdInfo info_qemu_agent_command[] = {
9907     {.name = "help",
9908      .data = N_("QEMU Guest Agent Command")
9909     },
9910     {.name = "desc",
9911      .data = N_("Run an arbitrary qemu guest agent command; use at your own risk")
9912     },
9913     {.name = NULL}
9914 };
9915 
9916 static const vshCmdOptDef opts_qemu_agent_command[] = {
9917     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
9918     {.name = "timeout",
9919      .type = VSH_OT_INT,
9920      .flags = VSH_OFLAG_REQ_OPT,
9921      .help = N_("timeout seconds. must be positive.")
9922     },
9923     {.name = "async",
9924      .type = VSH_OT_BOOL,
9925      .help = N_("execute command without waiting for timeout")
9926     },
9927     {.name = "block",
9928      .type = VSH_OT_BOOL,
9929      .help = N_("execute command without timeout")
9930     },
9931     {.name = "pretty",
9932      .type = VSH_OT_BOOL,
9933      .help = N_("pretty-print the output")
9934     },
9935     {.name = "cmd",
9936      .type = VSH_OT_ARGV,
9937      .flags = VSH_OFLAG_REQ,
9938      .help = N_("command")
9939     },
9940     {.name = NULL}
9941 };
9942 
9943 static bool
cmdQemuAgentCommand(vshControl * ctl,const vshCmd * cmd)9944 cmdQemuAgentCommand(vshControl *ctl, const vshCmd *cmd)
9945 {
9946     g_autoptr(virshDomain) dom = NULL;
9947     bool ret = false;
9948     char *guest_agent_cmd = NULL;
9949     char *result = NULL;
9950     int timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT;
9951     int judge = 0;
9952     unsigned int flags = 0;
9953     const vshCmdOpt *opt = NULL;
9954     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
9955     virJSONValue *pretty = NULL;
9956 
9957     dom = virshCommandOptDomain(ctl, cmd, NULL);
9958     if (dom == NULL)
9959         goto cleanup;
9960 
9961     while ((opt = vshCommandOptArgv(ctl, cmd, opt)))
9962         virBufferAsprintf(&buf, "%s ", opt->data);
9963 
9964     virBufferTrim(&buf, " ");
9965 
9966     guest_agent_cmd = virBufferContentAndReset(&buf);
9967 
9968     judge = vshCommandOptInt(ctl, cmd, "timeout", &timeout);
9969     if (judge < 0)
9970         goto cleanup;
9971     else if (judge > 0)
9972         judge = 1;
9973     if (judge && timeout < 1) {
9974         vshError(ctl, "%s", _("timeout must be positive"));
9975         goto cleanup;
9976     }
9977 
9978     if (vshCommandOptBool(cmd, "async")) {
9979         timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT;
9980         judge++;
9981     }
9982     if (vshCommandOptBool(cmd, "block")) {
9983         timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK;
9984         judge++;
9985     }
9986 
9987     if (judge > 1) {
9988         vshError(ctl, "%s", _("timeout, async and block options are exclusive"));
9989         goto cleanup;
9990     }
9991 
9992     result = virDomainQemuAgentCommand(dom, guest_agent_cmd, timeout, flags);
9993     if (!result)
9994         goto cleanup;
9995 
9996     if (vshCommandOptBool(cmd, "pretty")) {
9997         char *tmp;
9998         pretty = virJSONValueFromString(result);
9999         if (pretty && (tmp = virJSONValueToString(pretty, true))) {
10000             VIR_FREE(result);
10001             result = tmp;
10002         } else {
10003             vshResetLibvirtError();
10004         }
10005     }
10006 
10007     vshPrint(ctl, "%s\n", result);
10008 
10009     ret = true;
10010 
10011  cleanup:
10012     VIR_FREE(result);
10013     VIR_FREE(guest_agent_cmd);
10014 
10015     return ret;
10016 }
10017 
10018 /*
10019  * "lxc-enter-namespace" namespace
10020  */
10021 static const vshCmdInfo info_lxc_enter_namespace[] = {
10022     {.name = "help",
10023      .data = N_("LXC Guest Enter Namespace")
10024     },
10025     {.name = "desc",
10026      .data = N_("Run an arbitrary command in a lxc guest namespace; use at your own risk")
10027     },
10028     {.name = NULL}
10029 };
10030 
10031 static const vshCmdOptDef opts_lxc_enter_namespace[] = {
10032     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
10033     {.name = "noseclabel",
10034      .type = VSH_OT_BOOL,
10035      .help = N_("Do not change process security label")
10036     },
10037     {.name = "cmd",
10038      .type = VSH_OT_ARGV,
10039      .flags = VSH_OFLAG_REQ,
10040      .help = N_("command to run")
10041     },
10042     {.name = NULL}
10043 };
10044 
10045 static bool
cmdLxcEnterNamespace(vshControl * ctl,const vshCmd * cmd)10046 cmdLxcEnterNamespace(vshControl *ctl, const vshCmd *cmd)
10047 {
10048     g_autoptr(virshDomain) dom = NULL;
10049     const vshCmdOpt *opt = NULL;
10050     g_autofree char **cmdargv = NULL;
10051     size_t ncmdargv = 0;
10052     pid_t pid;
10053     int nfdlist;
10054     int *fdlist;
10055     size_t i;
10056     int status;
10057     bool setlabel = true;
10058     g_autofree virSecurityModelPtr secmodel = NULL;
10059     g_autofree virSecurityLabelPtr seclabel = NULL;
10060     virshControl *priv = ctl->privData;
10061 
10062     dom = virshCommandOptDomain(ctl, cmd, NULL);
10063     if (dom == NULL)
10064         return false;
10065 
10066     if (vshCommandOptBool(cmd, "noseclabel"))
10067         setlabel = false;
10068 
10069     while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
10070         VIR_EXPAND_N(cmdargv, ncmdargv, 1);
10071         cmdargv[ncmdargv-1] = opt->data;
10072     }
10073     VIR_EXPAND_N(cmdargv, ncmdargv, 1);
10074     cmdargv[ncmdargv - 1] = NULL;
10075 
10076     if ((nfdlist = virDomainLxcOpenNamespace(dom, &fdlist, 0)) < 0)
10077         return false;
10078 
10079     if (setlabel) {
10080         secmodel = g_new0(virSecurityModel, 1);
10081         seclabel = g_new0(virSecurityLabel, 1);
10082 
10083         if (virNodeGetSecurityModel(priv->conn, secmodel) < 0)
10084             return false;
10085         if (virDomainGetSecurityLabel(dom, seclabel) < 0)
10086             return false;
10087     }
10088 
10089     /* Fork once because we don't want to affect
10090      * virsh's namespace itself, and because user namespace
10091      * can only be changed in single-threaded process
10092      */
10093     if ((pid = virFork()) < 0)
10094         return false;
10095 
10096     if (pid != 0) {
10097         for (i = 0; i < nfdlist; i++)
10098             VIR_FORCE_CLOSE(fdlist[i]);
10099         VIR_FREE(fdlist);
10100         if (virProcessWait(pid, NULL, false) < 0) {
10101             vshReportError(ctl);
10102             return false;
10103         }
10104         return true;
10105     }
10106 
10107     if (setlabel &&
10108         virDomainLxcEnterSecurityLabel(secmodel, seclabel, NULL, 0) < 0)
10109         _exit(EXIT_CANCELED);
10110 
10111     if (virDomainLxcEnterCGroup(dom, 0) < 0)
10112         _exit(EXIT_CANCELED);
10113 
10114     if (virDomainLxcEnterNamespace(dom, nfdlist, fdlist, NULL, NULL, 0) < 0)
10115         _exit(EXIT_CANCELED);
10116 
10117     /* Fork a second time because entering the
10118      * pid namespace only takes effect after fork
10119      */
10120     if ((pid = virFork()) < 0)
10121         _exit(EXIT_CANCELED);
10122 
10123     if (pid == 0) {
10124         execv(cmdargv[0], cmdargv);
10125         _exit(errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
10126     }
10127 
10128     if (virProcessWait(pid, &status, true) < 0)
10129         _exit(EXIT_CANNOT_INVOKE);
10130     virProcessExitWithStatus(status);
10131     return true;
10132 }
10133 
10134 /*
10135  * "dumpxml" command
10136  */
10137 static const vshCmdInfo info_dumpxml[] = {
10138     {.name = "help",
10139      .data = N_("domain information in XML")
10140     },
10141     {.name = "desc",
10142      .data = N_("Output the domain information as an XML dump to stdout.")
10143     },
10144     {.name = NULL}
10145 };
10146 
10147 static const vshCmdOptDef opts_dumpxml[] = {
10148     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
10149     {.name = "inactive",
10150      .type = VSH_OT_BOOL,
10151      .help = N_("show inactive defined XML")
10152     },
10153     {.name = "security-info",
10154      .type = VSH_OT_BOOL,
10155      .help = N_("include security sensitive information in XML dump")
10156     },
10157     {.name = "update-cpu",
10158      .type = VSH_OT_BOOL,
10159      .help = N_("update guest CPU according to host CPU")
10160     },
10161     {.name = "migratable",
10162      .type = VSH_OT_BOOL,
10163      .help = N_("provide XML suitable for migrations")
10164     },
10165     {.name = NULL}
10166 };
10167 
10168 static bool
cmdDumpXML(vshControl * ctl,const vshCmd * cmd)10169 cmdDumpXML(vshControl *ctl, const vshCmd *cmd)
10170 {
10171     g_autoptr(virshDomain) dom = NULL;
10172     g_autofree char *dump = NULL;
10173     unsigned int flags = 0;
10174     bool inactive = vshCommandOptBool(cmd, "inactive");
10175     bool secure = vshCommandOptBool(cmd, "security-info");
10176     bool update = vshCommandOptBool(cmd, "update-cpu");
10177     bool migratable = vshCommandOptBool(cmd, "migratable");
10178 
10179     if (inactive)
10180         flags |= VIR_DOMAIN_XML_INACTIVE;
10181     if (secure)
10182         flags |= VIR_DOMAIN_XML_SECURE;
10183     if (update)
10184         flags |= VIR_DOMAIN_XML_UPDATE_CPU;
10185     if (migratable)
10186         flags |= VIR_DOMAIN_XML_MIGRATABLE;
10187 
10188     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
10189         return false;
10190 
10191     if (!(dump = virDomainGetXMLDesc(dom, flags)))
10192         return false;
10193 
10194     vshPrint(ctl, "%s", dump);
10195     return true;
10196 }
10197 
10198 /*
10199  * "domxml-from-native" command
10200  */
10201 static const vshCmdInfo info_domxmlfromnative[] = {
10202     {.name = "help",
10203      .data = N_("Convert native config to domain XML")
10204     },
10205     {.name = "desc",
10206      .data = N_("Convert native guest configuration format to domain XML format.")
10207     },
10208     {.name = NULL}
10209 };
10210 
10211 static const vshCmdOptDef opts_domxmlfromnative[] = {
10212     {.name = "format",
10213      .type = VSH_OT_DATA,
10214      .flags = VSH_OFLAG_REQ,
10215      .help = N_("source config data format")
10216     },
10217     {.name = "config",
10218      .type = VSH_OT_DATA,
10219      .flags = VSH_OFLAG_REQ,
10220      .completer = virshCompletePathLocalExisting,
10221      .help = N_("config data file to import from")
10222     },
10223     {.name = NULL}
10224 };
10225 
10226 static bool
cmdDomXMLFromNative(vshControl * ctl,const vshCmd * cmd)10227 cmdDomXMLFromNative(vshControl *ctl, const vshCmd *cmd)
10228 {
10229     const char *format = NULL;
10230     const char *configFile = NULL;
10231     g_autofree char *configData = NULL;
10232     g_autofree char *xmlData = NULL;
10233     unsigned int flags = 0;
10234     virshControl *priv = ctl->privData;
10235 
10236     if (vshCommandOptStringReq(ctl, cmd, "format", &format) < 0 ||
10237         vshCommandOptStringReq(ctl, cmd, "config", &configFile) < 0)
10238         return false;
10239 
10240     if (virFileReadAll(configFile, VSH_MAX_XML_FILE, &configData) < 0)
10241         return false;
10242 
10243     xmlData = virConnectDomainXMLFromNative(priv->conn, format, configData, flags);
10244     if (!xmlData)
10245         return false;
10246 
10247     vshPrint(ctl, "%s", xmlData);
10248     return true;
10249 }
10250 
10251 /*
10252  * "domxml-to-native" command
10253  */
10254 static const vshCmdInfo info_domxmltonative[] = {
10255     {.name = "help",
10256      .data = N_("Convert domain XML to native config")
10257     },
10258     {.name = "desc",
10259      .data = N_("Convert domain XML config to a native guest configuration format.")
10260     },
10261     {.name = NULL}
10262 };
10263 
10264 static const vshCmdOptDef opts_domxmltonative[] = {
10265     {.name = "format",
10266      .type = VSH_OT_DATA,
10267      .flags = VSH_OFLAG_REQ,
10268      .help = N_("target config data type format")
10269     },
10270     VIRSH_COMMON_OPT_DOMAIN_OT_STRING_FULL(VSH_OFLAG_REQ_OPT, 0),
10271     {.name = "xml",
10272      .type = VSH_OT_STRING,
10273      .completer = virshCompletePathLocalExisting,
10274      .help = N_("xml data file to export from")
10275     },
10276     {.name = NULL}
10277 };
10278 
10279 static bool
cmdDomXMLToNative(vshControl * ctl,const vshCmd * cmd)10280 cmdDomXMLToNative(vshControl *ctl, const vshCmd *cmd)
10281 {
10282     const char *format = NULL;
10283     const char *xmlFile = NULL;
10284     g_autofree char *configData = NULL;
10285     g_autofree char *xmlData = NULL;
10286     unsigned int flags = 0;
10287     virshControl *priv = ctl->privData;
10288     g_autoptr(virshDomain) dom = NULL;
10289 
10290     if (vshCommandOptStringReq(ctl, cmd, "format", &format) < 0 ||
10291         vshCommandOptStringReq(ctl, cmd, "xml", &xmlFile) < 0)
10292         return false;
10293 
10294     VSH_EXCLUSIVE_OPTIONS("domain", "xml");
10295 
10296     if (vshCommandOptBool(cmd, "domain") &&
10297         (!(dom = virshCommandOptDomain(ctl, cmd, NULL))))
10298             return false;
10299 
10300     if (dom) {
10301         xmlData = virDomainGetXMLDesc(dom, flags);
10302     } else if (xmlFile) {
10303         if (virFileReadAll(xmlFile, VSH_MAX_XML_FILE, &xmlData) < 0)
10304             return false;
10305     } else {
10306         vshError(ctl, "%s", _("need either domain or domain XML"));
10307         return false;
10308     }
10309 
10310     if (!xmlData) {
10311         vshError(ctl, "%s", _("failed to retrieve XML"));
10312         return false;
10313     }
10314 
10315     if (!(configData = virConnectDomainXMLToNative(priv->conn, format, xmlData, flags)))
10316         return false;
10317 
10318     vshPrint(ctl, "%s", configData);
10319 
10320     return true;
10321 }
10322 
10323 /*
10324  * "domname" command
10325  */
10326 static const vshCmdInfo info_domname[] = {
10327     {.name = "help",
10328      .data = N_("convert a domain id or UUID to domain name")
10329     },
10330     {.name = "desc",
10331      .data = ""
10332     },
10333     {.name = NULL}
10334 };
10335 
10336 static const vshCmdOptDef opts_domname[] = {
10337     {.name = "domain",
10338      .type = VSH_OT_DATA,
10339      .flags = VSH_OFLAG_REQ,
10340      .completer = virshDomainUUIDCompleter,
10341      .help = N_("domain id or uuid")
10342     },
10343     {.name = NULL}
10344 };
10345 
10346 static bool
cmdDomname(vshControl * ctl,const vshCmd * cmd)10347 cmdDomname(vshControl *ctl, const vshCmd *cmd)
10348 {
10349     g_autoptr(virshDomain) dom = NULL;
10350 
10351     if (!(dom = virshCommandOptDomainBy(ctl, cmd, NULL,
10352                                         VIRSH_BYID|VIRSH_BYUUID)))
10353         return false;
10354 
10355     vshPrint(ctl, "%s\n", virDomainGetName(dom));
10356     return true;
10357 }
10358 
10359 /*
10360  * "domrename" command
10361  */
10362 static const vshCmdInfo info_domrename[] = {
10363     {.name = "help",
10364      .data = N_("rename a domain")
10365     },
10366     {.name = "desc",
10367      .data = "Rename an inactive domain."
10368     },
10369     {.name = NULL}
10370 };
10371 
10372 static const vshCmdOptDef opts_domrename[] = {
10373     VIRSH_COMMON_OPT_DOMAIN(N_("domain name or uuid"),
10374                             VIR_CONNECT_LIST_DOMAINS_INACTIVE),
10375     {.name = "new-name",
10376      .type = VSH_OT_DATA,
10377      .flags = VSH_OFLAG_REQ,
10378      .completer = virshCompleteEmpty,
10379      .help = N_("new domain name")
10380     },
10381     {.name = NULL}
10382 };
10383 
10384 static bool
cmdDomrename(vshControl * ctl,const vshCmd * cmd)10385 cmdDomrename(vshControl *ctl, const vshCmd *cmd)
10386 {
10387     g_autoptr(virshDomain) dom = NULL;
10388     const char *new_name = NULL;
10389 
10390     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
10391         return false;
10392 
10393     if (vshCommandOptStringReq(ctl, cmd, "new-name", &new_name) < 0)
10394         return false;
10395 
10396     if (virDomainRename(dom, new_name, 0) < 0)
10397         return false;
10398 
10399     vshPrintExtra(ctl, "Domain successfully renamed\n");
10400 
10401     return true;
10402 }
10403 
10404 /*
10405  * "domid" command
10406  */
10407 static const vshCmdInfo info_domid[] = {
10408     {.name = "help",
10409      .data = N_("convert a domain name or UUID to domain id")
10410     },
10411     {.name = "desc",
10412      .data = ""
10413     },
10414     {.name = NULL}
10415 };
10416 
10417 static const vshCmdOptDef opts_domid[] = {
10418     VIRSH_COMMON_OPT_DOMAIN(N_("domain name or uuid"),
10419                             VIR_CONNECT_LIST_DOMAINS_ACTIVE),
10420     {.name = NULL}
10421 };
10422 
10423 static bool
cmdDomid(vshControl * ctl,const vshCmd * cmd)10424 cmdDomid(vshControl *ctl, const vshCmd *cmd)
10425 {
10426     g_autoptr(virshDomain) dom = NULL;
10427     unsigned int id;
10428 
10429     if (!(dom = virshCommandOptDomainBy(ctl, cmd, NULL,
10430                                         VIRSH_BYNAME|VIRSH_BYUUID)))
10431         return false;
10432 
10433     id = virDomainGetID(dom);
10434     if (id == ((unsigned int)-1))
10435         vshPrint(ctl, "%s\n", "-");
10436     else
10437         vshPrint(ctl, "%d\n", id);
10438     return true;
10439 }
10440 
10441 /*
10442  * "domuuid" command
10443  */
10444 static const vshCmdInfo info_domuuid[] = {
10445     {.name = "help",
10446      .data = N_("convert a domain name or id to domain UUID")
10447     },
10448     {.name = "desc",
10449      .data = ""
10450     },
10451     {.name = NULL}
10452 };
10453 
10454 static const vshCmdOptDef opts_domuuid[] = {
10455     VIRSH_COMMON_OPT_DOMAIN(N_("domain id or name"), 0),
10456     {.name = NULL}
10457 };
10458 
10459 static bool
cmdDomuuid(vshControl * ctl,const vshCmd * cmd)10460 cmdDomuuid(vshControl *ctl, const vshCmd *cmd)
10461 {
10462     g_autoptr(virshDomain) dom = NULL;
10463     char uuid[VIR_UUID_STRING_BUFLEN];
10464 
10465     if (!(dom = virshCommandOptDomainBy(ctl, cmd, NULL,
10466                                         VIRSH_BYNAME|VIRSH_BYID)))
10467         return false;
10468 
10469     if (virDomainGetUUIDString(dom, uuid) != -1)
10470         vshPrint(ctl, "%s\n", uuid);
10471     else
10472         vshError(ctl, "%s", _("failed to get domain UUID"));
10473 
10474     return true;
10475 }
10476 
10477 /*
10478  * "migrate" command
10479  */
10480 static const vshCmdInfo info_migrate[] = {
10481     {.name = "help",
10482      .data = N_("migrate domain to another host")
10483     },
10484     {.name = "desc",
10485      .data = N_("Migrate domain to another host.  Add --live for live migration.")
10486     },
10487     {.name = NULL}
10488 };
10489 
10490 static const vshCmdOptDef opts_migrate[] = {
10491     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
10492     {.name = "desturi",
10493      .type = VSH_OT_DATA,
10494      .flags = VSH_OFLAG_REQ,
10495      .completer = virshCompleteEmpty,
10496      .help = N_("connection URI of the destination host as seen from the client(normal migration) or source(p2p migration)")
10497     },
10498     VIRSH_COMMON_OPT_LIVE(N_("live migration")),
10499     {.name = "offline",
10500      .type = VSH_OT_BOOL,
10501      .help = N_("offline migration")
10502     },
10503     {.name = "p2p",
10504      .type = VSH_OT_BOOL,
10505      .help = N_("peer-2-peer migration")
10506     },
10507     {.name = "direct",
10508      .type = VSH_OT_BOOL,
10509      .help = N_("direct migration")
10510     },
10511     {.name = "tunneled",
10512      .type = VSH_OT_ALIAS,
10513      .help = "tunnelled"
10514     },
10515     {.name = "tunnelled",
10516      .type = VSH_OT_BOOL,
10517      .help = N_("tunnelled migration")
10518     },
10519     {.name = "persistent",
10520      .type = VSH_OT_BOOL,
10521      .help = N_("persist VM on destination")
10522     },
10523     {.name = "undefinesource",
10524      .type = VSH_OT_BOOL,
10525      .help = N_("undefine VM on source")
10526     },
10527     {.name = "suspend",
10528      .type = VSH_OT_BOOL,
10529      .help = N_("do not restart the domain on the destination host")
10530     },
10531     {.name = "copy-storage-all",
10532      .type = VSH_OT_BOOL,
10533      .help = N_("migration with non-shared storage with full disk copy")
10534     },
10535     {.name = "copy-storage-inc",
10536      .type = VSH_OT_BOOL,
10537      .help = N_("migration with non-shared storage with incremental copy (same base image shared between source and destination)")
10538     },
10539     {.name = "change-protection",
10540      .type = VSH_OT_BOOL,
10541      .help = N_("prevent any configuration changes to domain until migration ends")
10542     },
10543     {.name = "unsafe",
10544      .type = VSH_OT_BOOL,
10545      .help = N_("force migration even if it may be unsafe")
10546     },
10547     {.name = "verbose",
10548      .type = VSH_OT_BOOL,
10549      .help = N_("display the progress of migration")
10550     },
10551     {.name = "compressed",
10552      .type = VSH_OT_BOOL,
10553      .help = N_("compress repeated pages during live migration")
10554     },
10555     {.name = "auto-converge",
10556      .type = VSH_OT_BOOL,
10557      .help = N_("force convergence during live migration")
10558     },
10559     {.name = "rdma-pin-all",
10560      .type = VSH_OT_BOOL,
10561      .help = N_("pin all memory before starting RDMA live migration")
10562     },
10563     {.name = "abort-on-error",
10564      .type = VSH_OT_BOOL,
10565      .help = N_("abort on soft errors during migration")
10566     },
10567     {.name = "postcopy",
10568      .type = VSH_OT_BOOL,
10569      .help = N_("enable post-copy migration; switch to it using migrate-postcopy command")
10570     },
10571     {.name = "postcopy-after-precopy",
10572      .type = VSH_OT_BOOL,
10573      .help = N_("automatically switch to post-copy migration after one pass of pre-copy")
10574     },
10575     {.name = "migrateuri",
10576      .type = VSH_OT_STRING,
10577      .completer = virshCompleteEmpty,
10578      .help = N_("migration URI, usually can be omitted")
10579     },
10580     {.name = "graphicsuri",
10581      .type = VSH_OT_STRING,
10582      .completer = virshCompleteEmpty,
10583      .help = N_("graphics URI to be used for seamless graphics migration")
10584     },
10585     {.name = "listen-address",
10586      .type = VSH_OT_STRING,
10587      .completer = virshCompleteEmpty,
10588      .help = N_("listen address that destination should bind to for incoming migration")
10589     },
10590     {.name = "dname",
10591      .type = VSH_OT_STRING,
10592      .completer = virshCompleteEmpty,
10593      .help = N_("rename to new name during migration (if supported)")
10594     },
10595     {.name = "timeout",
10596      .type = VSH_OT_INT,
10597      .help = N_("run action specified by --timeout-* option (suspend by "
10598                 "default) if live migration exceeds timeout (in seconds)")
10599     },
10600     {.name = "timeout-suspend",
10601      .type = VSH_OT_BOOL,
10602      .help = N_("suspend the guest after timeout")
10603     },
10604     {.name = "timeout-postcopy",
10605      .type = VSH_OT_BOOL,
10606      .help = N_("switch to post-copy after timeout")
10607     },
10608     {.name = "xml",
10609      .type = VSH_OT_STRING,
10610      .completer = virshCompletePathLocalExisting,
10611      .help = N_("filename containing updated XML for the target")
10612     },
10613     {.name = "migrate-disks",
10614      .type = VSH_OT_STRING,
10615      .completer = virshDomainMigrateDisksCompleter,
10616      .help = N_("comma separated list of disks to be migrated")
10617     },
10618     {.name = "disks-port",
10619      .type = VSH_OT_INT,
10620      .help = N_("port to use by target server for incoming disks migration")
10621     },
10622     {.name = "disks-uri",
10623      .type = VSH_OT_STRING,
10624      .completer = virshCompleteEmpty,
10625      .help = N_("URI to use for disks migration (overrides --disks-port)")
10626     },
10627     {.name = "comp-methods",
10628      .type = VSH_OT_STRING,
10629      .completer = virshDomainMigrateCompMethodsCompleter,
10630      .help = N_("comma separated list of compression methods to be used")
10631     },
10632     {.name = "comp-mt-level",
10633      .type = VSH_OT_INT,
10634      .help = N_("compress level for multithread compression")
10635     },
10636     {.name = "comp-mt-threads",
10637      .type = VSH_OT_INT,
10638      .help = N_("number of compression threads for multithread compression")
10639     },
10640     {.name = "comp-mt-dthreads",
10641      .type = VSH_OT_INT,
10642      .help = N_("number of decompression threads for multithread compression")
10643     },
10644     {.name = "comp-xbzrle-cache",
10645      .type = VSH_OT_INT,
10646      .help = N_("page cache size for xbzrle compression")
10647     },
10648     {.name = "auto-converge-initial",
10649      .type = VSH_OT_INT,
10650      .help = N_("initial CPU throttling rate for auto-convergence")
10651     },
10652     {.name = "auto-converge-increment",
10653      .type = VSH_OT_INT,
10654      .help = N_("CPU throttling rate increment for auto-convergence")
10655     },
10656     {.name = "persistent-xml",
10657      .type = VSH_OT_STRING,
10658      .completer = virshCompletePathLocalExisting,
10659      .help = N_("filename containing updated persistent XML for the target")
10660     },
10661     {.name = "tls",
10662      .type = VSH_OT_BOOL,
10663      .help = N_("use TLS for migration")
10664     },
10665     {.name = "postcopy-bandwidth",
10666      .type = VSH_OT_INT,
10667      .help = N_("post-copy migration bandwidth limit in MiB/s")
10668     },
10669     {.name = "parallel",
10670      .type = VSH_OT_BOOL,
10671      .help = N_("enable parallel migration")
10672     },
10673     {.name = "parallel-connections",
10674      .type = VSH_OT_INT,
10675      .help = N_("number of connections for parallel migration")
10676     },
10677     {.name = "bandwidth",
10678      .type = VSH_OT_INT,
10679      .help = N_("migration bandwidth limit in MiB/s")
10680     },
10681     {.name = "tls-destination",
10682      .type = VSH_OT_STRING,
10683      .completer = virshCompleteEmpty,
10684      .help = N_("override the destination host name used for TLS verification")
10685     },
10686     {.name = NULL}
10687 };
10688 
10689 static void
doMigrate(void * opaque)10690 doMigrate(void *opaque)
10691 {
10692     g_autoptr(virshDomain) dom = NULL;
10693     const char *desturi = NULL;
10694     const char *opt = NULL;
10695     unsigned int flags = 0;
10696     virshCtrlData *data = opaque;
10697     vshControl *ctl = data->ctl;
10698     const vshCmd *cmd = data->cmd;
10699     virTypedParameterPtr params = NULL;
10700     int nparams = 0;
10701     int maxparams = 0;
10702     int intOpt = 0;
10703     unsigned long long ullOpt = 0;
10704     int rv;
10705     virConnectPtr dconn = data->dconn;
10706 #ifndef WIN32
10707     sigset_t sigmask, oldsigmask;
10708 
10709     sigemptyset(&sigmask);
10710     sigaddset(&sigmask, SIGINT);
10711     if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) != 0)
10712         goto out_sig;
10713 #endif /* !WIN32 */
10714 
10715     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
10716         goto out;
10717 
10718     if (vshCommandOptStringReq(ctl, cmd, "desturi", &desturi) < 0)
10719         goto out;
10720 
10721     if (vshCommandOptStringReq(ctl, cmd, "migrateuri", &opt) < 0)
10722         goto out;
10723     if (opt &&
10724         virTypedParamsAddString(&params, &nparams, &maxparams,
10725                                 VIR_MIGRATE_PARAM_URI, opt) < 0)
10726         goto save_error;
10727 
10728     if (vshCommandOptStringReq(ctl, cmd, "graphicsuri", &opt) < 0)
10729         goto out;
10730     if (opt &&
10731         virTypedParamsAddString(&params, &nparams, &maxparams,
10732                                 VIR_MIGRATE_PARAM_GRAPHICS_URI, opt) < 0)
10733         goto save_error;
10734 
10735     if (vshCommandOptStringReq(ctl, cmd, "listen-address", &opt) < 0)
10736         goto out;
10737     if (opt &&
10738         virTypedParamsAddString(&params, &nparams, &maxparams,
10739                                 VIR_MIGRATE_PARAM_LISTEN_ADDRESS, opt) < 0)
10740         goto save_error;
10741 
10742     if (vshCommandOptInt(ctl, cmd, "disks-port", &intOpt) < 0)
10743         goto out;
10744     if (intOpt &&
10745         virTypedParamsAddInt(&params, &nparams, &maxparams,
10746                              VIR_MIGRATE_PARAM_DISKS_PORT, intOpt) < 0)
10747         goto save_error;
10748 
10749     if (vshCommandOptStringReq(ctl, cmd, "disks-uri", &opt) < 0)
10750         goto out;
10751     if (opt &&
10752         virTypedParamsAddString(&params, &nparams, &maxparams,
10753                                 VIR_MIGRATE_PARAM_DISKS_URI,
10754                                 opt) < 0)
10755         goto save_error;
10756 
10757     if (vshCommandOptStringReq(ctl, cmd, "dname", &opt) < 0)
10758         goto out;
10759     if (opt &&
10760         virTypedParamsAddString(&params, &nparams, &maxparams,
10761                                 VIR_MIGRATE_PARAM_DEST_NAME, opt) < 0)
10762         goto save_error;
10763 
10764     if (vshCommandOptStringReq(ctl, cmd, "migrate-disks", &opt) < 0)
10765         goto out;
10766     if (opt) {
10767         char **val = NULL;
10768 
10769         val = g_strsplit(opt, ",", 0);
10770 
10771         if (virTypedParamsAddStringList(&params,
10772                                         &nparams,
10773                                         &maxparams,
10774                                         VIR_MIGRATE_PARAM_MIGRATE_DISKS,
10775                                         (const char **)val) < 0) {
10776             VIR_FREE(val);
10777             goto save_error;
10778         }
10779 
10780         VIR_FREE(val);
10781     }
10782 
10783     if (vshCommandOptStringReq(ctl, cmd, "comp-methods", &opt) < 0)
10784         goto out;
10785     if (opt) {
10786         char **val = g_strsplit(opt, ",", 0);
10787 
10788         if (virTypedParamsAddStringList(&params,
10789                                         &nparams,
10790                                         &maxparams,
10791                                         VIR_MIGRATE_PARAM_COMPRESSION,
10792                                         (const char **)val) < 0) {
10793             VIR_FREE(val);
10794             goto save_error;
10795         }
10796 
10797         VIR_FREE(val);
10798     }
10799 
10800     if ((rv = vshCommandOptInt(ctl, cmd, "comp-mt-level", &intOpt)) < 0) {
10801         goto out;
10802     } else if (rv > 0) {
10803         if (virTypedParamsAddInt(&params, &nparams, &maxparams,
10804                                  VIR_MIGRATE_PARAM_COMPRESSION_MT_LEVEL,
10805                                  intOpt) < 0)
10806             goto save_error;
10807     }
10808 
10809     if ((rv = vshCommandOptInt(ctl, cmd, "comp-mt-threads", &intOpt)) < 0) {
10810         goto out;
10811     } else if (rv > 0) {
10812         if (virTypedParamsAddInt(&params, &nparams, &maxparams,
10813                                  VIR_MIGRATE_PARAM_COMPRESSION_MT_THREADS,
10814                                  intOpt) < 0)
10815             goto save_error;
10816     }
10817 
10818     if ((rv = vshCommandOptInt(ctl, cmd, "comp-mt-dthreads", &intOpt)) < 0) {
10819         goto out;
10820     } else if (rv > 0) {
10821         if (virTypedParamsAddInt(&params, &nparams, &maxparams,
10822                                  VIR_MIGRATE_PARAM_COMPRESSION_MT_DTHREADS,
10823                                  intOpt) < 0)
10824             goto save_error;
10825     }
10826 
10827     if ((rv = vshCommandOptULongLong(ctl, cmd, "comp-xbzrle-cache", &ullOpt)) < 0) {
10828         goto out;
10829     } else if (rv > 0) {
10830         if (virTypedParamsAddULLong(&params, &nparams, &maxparams,
10831                                     VIR_MIGRATE_PARAM_COMPRESSION_XBZRLE_CACHE,
10832                                     ullOpt) < 0)
10833             goto save_error;
10834     }
10835 
10836     if (vshCommandOptStringReq(ctl, cmd, "xml", &opt) < 0)
10837         goto out;
10838     if (opt) {
10839         char *xml;
10840 
10841         if (virFileReadAll(opt, VSH_MAX_XML_FILE, &xml) < 0) {
10842             vshError(ctl, _("cannot read file '%s'"), opt);
10843             goto save_error;
10844         }
10845 
10846         if (virTypedParamsAddString(&params, &nparams, &maxparams,
10847                                     VIR_MIGRATE_PARAM_DEST_XML, xml) < 0) {
10848             VIR_FREE(xml);
10849             goto save_error;
10850         }
10851         VIR_FREE(xml);
10852     }
10853 
10854     if (vshCommandOptStringReq(ctl, cmd, "persistent-xml", &opt) < 0)
10855         goto out;
10856     if (opt) {
10857         char *xml;
10858 
10859         if (virFileReadAll(opt, VSH_MAX_XML_FILE, &xml) < 0) {
10860             vshError(ctl, _("cannot read file '%s'"), opt);
10861             goto save_error;
10862         }
10863 
10864         if (virTypedParamsAddString(&params, &nparams, &maxparams,
10865                                     VIR_MIGRATE_PARAM_PERSIST_XML, xml) < 0) {
10866             VIR_FREE(xml);
10867             goto save_error;
10868         }
10869         VIR_FREE(xml);
10870     }
10871 
10872     if ((rv = vshCommandOptInt(ctl, cmd, "auto-converge-initial", &intOpt)) < 0) {
10873         goto out;
10874     } else if (rv > 0) {
10875         if (virTypedParamsAddInt(&params, &nparams, &maxparams,
10876                                  VIR_MIGRATE_PARAM_AUTO_CONVERGE_INITIAL,
10877                                  intOpt) < 0)
10878             goto save_error;
10879     }
10880 
10881     if ((rv = vshCommandOptInt(ctl, cmd, "auto-converge-increment", &intOpt)) < 0) {
10882         goto out;
10883     } else if (rv > 0) {
10884         if (virTypedParamsAddInt(&params, &nparams, &maxparams,
10885                                  VIR_MIGRATE_PARAM_AUTO_CONVERGE_INCREMENT,
10886                                  intOpt) < 0)
10887             goto save_error;
10888     }
10889 
10890     if ((rv = vshCommandOptULongLong(ctl, cmd, "postcopy-bandwidth", &ullOpt)) < 0) {
10891         goto out;
10892     } else if (rv > 0) {
10893         if (virTypedParamsAddULLong(&params, &nparams, &maxparams,
10894                                     VIR_MIGRATE_PARAM_BANDWIDTH_POSTCOPY,
10895                                     ullOpt) < 0)
10896             goto save_error;
10897     }
10898 
10899     if ((rv = vshCommandOptInt(ctl, cmd, "parallel-connections", &intOpt)) < 0) {
10900         goto out;
10901     } else if (rv > 0) {
10902         if (virTypedParamsAddInt(&params, &nparams, &maxparams,
10903                                  VIR_MIGRATE_PARAM_PARALLEL_CONNECTIONS,
10904                                  intOpt) < 0)
10905             goto save_error;
10906     }
10907 
10908     if ((rv = vshCommandOptULongLong(ctl, cmd, "bandwidth", &ullOpt)) < 0) {
10909         goto out;
10910     } else if (rv > 0) {
10911         if (virTypedParamsAddULLong(&params, &nparams, &maxparams,
10912                                     VIR_MIGRATE_PARAM_BANDWIDTH,
10913                                     ullOpt) < 0)
10914             goto save_error;
10915     }
10916 
10917     if (vshCommandOptStringReq(ctl, cmd, "tls-destination", &opt) < 0)
10918         goto out;
10919     if (opt &&
10920         virTypedParamsAddString(&params, &nparams, &maxparams,
10921                                 VIR_MIGRATE_PARAM_TLS_DESTINATION, opt) < 0)
10922         goto save_error;
10923 
10924     if (vshCommandOptBool(cmd, "live"))
10925         flags |= VIR_MIGRATE_LIVE;
10926     if (vshCommandOptBool(cmd, "p2p"))
10927         flags |= VIR_MIGRATE_PEER2PEER;
10928     if (vshCommandOptBool(cmd, "tunnelled"))
10929         flags |= VIR_MIGRATE_TUNNELLED;
10930 
10931     if (vshCommandOptBool(cmd, "persistent"))
10932         flags |= VIR_MIGRATE_PERSIST_DEST;
10933     if (vshCommandOptBool(cmd, "undefinesource"))
10934         flags |= VIR_MIGRATE_UNDEFINE_SOURCE;
10935 
10936     if (vshCommandOptBool(cmd, "suspend"))
10937         flags |= VIR_MIGRATE_PAUSED;
10938 
10939     if (vshCommandOptBool(cmd, "copy-storage-all"))
10940         flags |= VIR_MIGRATE_NON_SHARED_DISK;
10941 
10942     if (vshCommandOptBool(cmd, "copy-storage-inc"))
10943         flags |= VIR_MIGRATE_NON_SHARED_INC;
10944 
10945     if (vshCommandOptBool(cmd, "change-protection"))
10946         flags |= VIR_MIGRATE_CHANGE_PROTECTION;
10947 
10948     if (vshCommandOptBool(cmd, "unsafe"))
10949         flags |= VIR_MIGRATE_UNSAFE;
10950 
10951     if (vshCommandOptBool(cmd, "compressed"))
10952         flags |= VIR_MIGRATE_COMPRESSED;
10953 
10954     if (vshCommandOptBool(cmd, "auto-converge"))
10955         flags |= VIR_MIGRATE_AUTO_CONVERGE;
10956 
10957     if (vshCommandOptBool(cmd, "rdma-pin-all"))
10958         flags |= VIR_MIGRATE_RDMA_PIN_ALL;
10959 
10960     if (vshCommandOptBool(cmd, "offline"))
10961         flags |= VIR_MIGRATE_OFFLINE;
10962 
10963     if (vshCommandOptBool(cmd, "abort-on-error"))
10964         flags |= VIR_MIGRATE_ABORT_ON_ERROR;
10965 
10966     if (vshCommandOptBool(cmd, "postcopy"))
10967         flags |= VIR_MIGRATE_POSTCOPY;
10968 
10969     if (vshCommandOptBool(cmd, "tls"))
10970         flags |= VIR_MIGRATE_TLS;
10971 
10972     if (vshCommandOptBool(cmd, "parallel"))
10973         flags |= VIR_MIGRATE_PARALLEL;
10974 
10975     if (flags & VIR_MIGRATE_PEER2PEER || vshCommandOptBool(cmd, "direct")) {
10976         if (virDomainMigrateToURI3(dom, desturi, params, nparams, flags) == 0)
10977             data->ret = 0;
10978     } else {
10979         /* For traditional live migration, connect to the destination host directly. */
10980         g_autoptr(virshDomain) ddom = NULL;
10981 
10982         if ((ddom = virDomainMigrate3(dom, dconn, params, nparams, flags))) {
10983             data->ret = 0;
10984         }
10985     }
10986 
10987  out:
10988 #ifndef WIN32
10989     pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
10990  out_sig:
10991 #endif /* !WIN32 */
10992     virTypedParamsFree(params, nparams);
10993     g_main_loop_quit(data->eventLoop);
10994     return;
10995 
10996  save_error:
10997     vshSaveLibvirtError();
10998     goto out;
10999 }
11000 
11001 typedef enum {
11002     VIRSH_MIGRATE_TIMEOUT_DEFAULT,
11003     VIRSH_MIGRATE_TIMEOUT_SUSPEND,
11004     VIRSH_MIGRATE_TIMEOUT_POSTCOPY,
11005 } virshMigrateTimeoutAction;
11006 
11007 static void
virshMigrateTimeout(vshControl * ctl,virDomainPtr dom,void * opaque)11008 virshMigrateTimeout(vshControl *ctl,
11009                     virDomainPtr dom,
11010                     void *opaque)
11011 {
11012     virshMigrateTimeoutAction action = *(virshMigrateTimeoutAction *) opaque;
11013 
11014     switch (action) {
11015     case VIRSH_MIGRATE_TIMEOUT_DEFAULT: /* unreachable */
11016     case VIRSH_MIGRATE_TIMEOUT_SUSPEND:
11017         vshDebug(ctl, VSH_ERR_DEBUG,
11018                  "migration timed out; suspending domain\n");
11019         if (virDomainSuspend(dom) < 0)
11020             vshDebug(ctl, VSH_ERR_INFO, "suspending domain failed\n");
11021         break;
11022 
11023     case VIRSH_MIGRATE_TIMEOUT_POSTCOPY:
11024         vshDebug(ctl, VSH_ERR_DEBUG,
11025                  "migration timed out; switching to post-copy\n");
11026         if (virDomainMigrateStartPostCopy(dom, 0) < 0)
11027             vshDebug(ctl, VSH_ERR_INFO, "switching to post-copy failed\n");
11028         break;
11029     }
11030 }
11031 
11032 static void
virshMigrateIteration(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,int iteration,void * opaque)11033 virshMigrateIteration(virConnectPtr conn G_GNUC_UNUSED,
11034                       virDomainPtr dom,
11035                       int iteration,
11036                       void *opaque)
11037 {
11038     vshControl *ctl = opaque;
11039 
11040     if (iteration == 2) {
11041         vshDebug(ctl, VSH_ERR_DEBUG,
11042                  "iteration %d finished; switching to post-copy\n",
11043                  iteration - 1);
11044         if (virDomainMigrateStartPostCopy(dom, 0) < 0)
11045             vshDebug(ctl, VSH_ERR_INFO, "switching to post-copy failed\n");
11046     }
11047 }
11048 
11049 static bool
cmdMigrate(vshControl * ctl,const vshCmd * cmd)11050 cmdMigrate(vshControl *ctl, const vshCmd *cmd)
11051 {
11052     g_autoptr(virshDomain) dom = NULL;
11053     virThread workerThread;
11054     bool verbose = false;
11055     unsigned int timeout = 0;
11056     virshMigrateTimeoutAction timeoutAction = VIRSH_MIGRATE_TIMEOUT_DEFAULT;
11057     bool live_flag = false;
11058     virshControl *priv = ctl->privData;
11059     int iterEvent = -1;
11060     g_autoptr(GMainContext) eventCtxt = g_main_context_new();
11061     g_autoptr(GMainLoop) eventLoop = g_main_loop_new(eventCtxt, FALSE);
11062     virshCtrlData data = {
11063         .dconn = NULL,
11064         .ctl = ctl,
11065         .cmd = cmd,
11066         .eventLoop = eventLoop,
11067         .ret = -1,
11068     };
11069 
11070     VSH_EXCLUSIVE_OPTIONS("live", "offline");
11071     VSH_EXCLUSIVE_OPTIONS("timeout-suspend", "timeout-postcopy");
11072     VSH_REQUIRE_OPTION("postcopy-after-precopy", "postcopy");
11073     VSH_REQUIRE_OPTION("timeout-postcopy", "postcopy");
11074     VSH_REQUIRE_OPTION("persistent-xml", "persistent");
11075     VSH_REQUIRE_OPTION("tls-destination", "tls");
11076 
11077     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11078         return false;
11079 
11080     if (vshCommandOptBool(cmd, "verbose"))
11081         verbose = true;
11082 
11083     if (vshCommandOptBool(cmd, "live"))
11084         live_flag = true;
11085     if (vshCommandOptUInt(ctl, cmd, "timeout", &timeout) < 0) {
11086         goto cleanup;
11087     } else if (timeout > 0 && !live_flag) {
11088         vshError(ctl, "%s",
11089                  _("migrate: Unexpected timeout for offline migration"));
11090         goto cleanup;
11091     }
11092 
11093     if (vshCommandOptBool(cmd, "timeout-suspend"))
11094         timeoutAction = VIRSH_MIGRATE_TIMEOUT_SUSPEND;
11095     if (vshCommandOptBool(cmd, "timeout-postcopy"))
11096         timeoutAction = VIRSH_MIGRATE_TIMEOUT_POSTCOPY;
11097     if (timeout > 0) {
11098         if (timeoutAction == VIRSH_MIGRATE_TIMEOUT_DEFAULT)
11099             timeoutAction = VIRSH_MIGRATE_TIMEOUT_SUSPEND;
11100     } else if (timeoutAction) {
11101         vshError(ctl, "%s",
11102                  _("migrate: Unexpected --timeout-* option without --timeout"));
11103         goto cleanup;
11104     }
11105 
11106     if (vshCommandOptBool(cmd, "postcopy-after-precopy")) {
11107         iterEvent = virConnectDomainEventRegisterAny(
11108                             priv->conn, dom,
11109                             VIR_DOMAIN_EVENT_ID_MIGRATION_ITERATION,
11110                             VIR_DOMAIN_EVENT_CALLBACK(virshMigrateIteration),
11111                             ctl, NULL);
11112         if (iterEvent < 0)
11113             goto cleanup;
11114     }
11115 
11116     if (vshCommandOptBool(cmd, "p2p") || vshCommandOptBool(cmd, "direct")) {
11117         data.dconn = NULL;
11118     } else {
11119         /* For traditional live migration, connect to the destination host. */
11120         virConnectPtr dconn = NULL;
11121         const char *desturi = NULL;
11122 
11123         if (vshCommandOptStringReq(ctl, cmd, "desturi", &desturi) < 0)
11124             goto cleanup;
11125 
11126         dconn = virshConnect(ctl, desturi, false);
11127         if (!dconn)
11128             goto cleanup;
11129 
11130         data.dconn = dconn;
11131     }
11132 
11133     if (virThreadCreate(&workerThread,
11134                         true,
11135                         doMigrate,
11136                         &data) < 0)
11137         goto cleanup;
11138     virshWatchJob(ctl, dom, verbose, eventLoop,
11139                   &data.ret, timeout,
11140                   virshMigrateTimeout,
11141                   &timeoutAction, _("Migration"));
11142 
11143     virThreadJoin(&workerThread);
11144 
11145  cleanup:
11146     if (data.dconn)
11147         virConnectClose(data.dconn);
11148     if (iterEvent != -1)
11149         virConnectDomainEventDeregisterAny(priv->conn, iterEvent);
11150     return !data.ret;
11151 }
11152 
11153 /*
11154  * "migrate-setmaxdowntime" command
11155  */
11156 static const vshCmdInfo info_migrate_setmaxdowntime[] = {
11157     {.name = "help",
11158      .data = N_("set maximum tolerable downtime")
11159     },
11160     {.name = "desc",
11161      .data = N_("Set maximum tolerable downtime of a domain which is being live-migrated to another host.")
11162     },
11163     {.name = NULL}
11164 };
11165 
11166 static const vshCmdOptDef opts_migrate_setmaxdowntime[] = {
11167     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
11168     {.name = "downtime",
11169      .type = VSH_OT_INT,
11170      .flags = VSH_OFLAG_REQ,
11171      .help = N_("maximum tolerable downtime (in milliseconds) for migration")
11172     },
11173     {.name = NULL}
11174 };
11175 
11176 static bool
cmdMigrateSetMaxDowntime(vshControl * ctl,const vshCmd * cmd)11177 cmdMigrateSetMaxDowntime(vshControl *ctl, const vshCmd *cmd)
11178 {
11179     g_autoptr(virshDomain) dom = NULL;
11180     unsigned long long downtime = 0;
11181 
11182     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11183         return false;
11184 
11185     if (vshCommandOptULongLong(ctl, cmd, "downtime", &downtime) < 0)
11186         return false;
11187 
11188     if (downtime < 1) {
11189         vshError(ctl, "%s", _("migrate: Invalid downtime"));
11190         return false;
11191     }
11192 
11193     return virDomainMigrateSetMaxDowntime(dom, downtime, 0) == 0;
11194 }
11195 
11196 
11197 /*
11198  * "migrate-getmaxdowntime" command
11199  */
11200 static const vshCmdInfo info_migrate_getmaxdowntime[] = {
11201     {.name = "help",
11202      .data = N_("get maximum tolerable downtime")
11203     },
11204     {.name = "desc",
11205      .data = N_("Get maximum tolerable downtime of a domain which is being live-migrated to another host.")
11206     },
11207     {.name = NULL}
11208 };
11209 
11210 static const vshCmdOptDef opts_migrate_getmaxdowntime[] = {
11211     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
11212     {.name = NULL}
11213 };
11214 
11215 static bool
cmdMigrateGetMaxDowntime(vshControl * ctl,const vshCmd * cmd)11216 cmdMigrateGetMaxDowntime(vshControl *ctl, const vshCmd *cmd)
11217 {
11218     g_autoptr(virshDomain) dom = NULL;
11219     unsigned long long downtime;
11220 
11221     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11222         return false;
11223 
11224     if (virDomainMigrateGetMaxDowntime(dom, &downtime, 0) < 0)
11225         return false;
11226 
11227     vshPrint(ctl, "%llu\n", downtime);
11228     return true;
11229 }
11230 
11231 
11232 /*
11233  * "migrate-compcache" command
11234  */
11235 static const vshCmdInfo info_migrate_compcache[] = {
11236     {.name = "help",
11237      .data = N_("get/set compression cache size")
11238     },
11239     {.name = "desc",
11240      .data = N_("Get/set size of the cache (in bytes) used for compressing "
11241                 "repeatedly transferred memory pages during live migration.")
11242     },
11243     {.name = NULL}
11244 };
11245 
11246 static const vshCmdOptDef opts_migrate_compcache[] = {
11247     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
11248     {.name = "size",
11249      .type = VSH_OT_INT,
11250      .flags = VSH_OFLAG_REQ_OPT,
11251      .help = N_("requested size of the cache (in bytes) used for compression")
11252     },
11253     {.name = NULL}
11254 };
11255 
11256 static bool
cmdMigrateCompCache(vshControl * ctl,const vshCmd * cmd)11257 cmdMigrateCompCache(vshControl *ctl, const vshCmd *cmd)
11258 {
11259     g_autoptr(virshDomain) dom = NULL;
11260     unsigned long long size = 0;
11261     const char *unit;
11262     double value;
11263     int rc;
11264 
11265     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11266         return false;
11267 
11268     rc = vshCommandOptULongLong(ctl, cmd, "size", &size);
11269     if (rc < 0)
11270         return false;
11271 
11272     if (rc != 0 &&
11273         (virDomainMigrateSetCompressionCache(dom, size, 0) < 0))
11274         return false;
11275 
11276     if (virDomainMigrateGetCompressionCache(dom, &size, 0) < 0)
11277         return false;
11278 
11279     value = vshPrettyCapacity(size, &unit);
11280     vshPrint(ctl, _("Compression cache: %.3lf %s"), value, unit);
11281 
11282     return true;
11283 }
11284 
11285 /*
11286  * "migrate-setspeed" command
11287  */
11288 static const vshCmdInfo info_migrate_setspeed[] = {
11289     {.name = "help",
11290      .data = N_("Set the maximum migration bandwidth")
11291     },
11292     {.name = "desc",
11293      .data = N_("Set the maximum migration bandwidth (in MiB/s) for a domain "
11294                 "which is being migrated to another host.")
11295     },
11296     {.name = NULL}
11297 };
11298 
11299 static const vshCmdOptDef opts_migrate_setspeed[] = {
11300     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
11301     {.name = "bandwidth",
11302      .type = VSH_OT_INT,
11303      .flags = VSH_OFLAG_REQ,
11304      .help = N_("migration bandwidth limit in MiB/s")
11305     },
11306     {.name = "postcopy",
11307      .type = VSH_OT_BOOL,
11308      .help = N_("set post-copy migration bandwidth")
11309     },
11310     {.name = NULL}
11311 };
11312 
11313 static bool
cmdMigrateSetMaxSpeed(vshControl * ctl,const vshCmd * cmd)11314 cmdMigrateSetMaxSpeed(vshControl *ctl, const vshCmd *cmd)
11315 {
11316     g_autoptr(virshDomain) dom = NULL;
11317     unsigned long bandwidth = 0;
11318     unsigned int flags = 0;
11319 
11320     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11321         return false;
11322 
11323     if (vshCommandOptULWrap(ctl, cmd, "bandwidth", &bandwidth) < 0)
11324         return false;
11325 
11326     if (vshCommandOptBool(cmd, "postcopy"))
11327         flags |= VIR_DOMAIN_MIGRATE_MAX_SPEED_POSTCOPY;
11328 
11329     if (virDomainMigrateSetMaxSpeed(dom, bandwidth, flags) < 0)
11330         return false;
11331 
11332     return true;
11333 }
11334 
11335 /*
11336  * "migrate-getspeed" command
11337  */
11338 static const vshCmdInfo info_migrate_getspeed[] = {
11339     {.name = "help",
11340      .data = N_("Get the maximum migration bandwidth")
11341     },
11342     {.name = "desc",
11343      .data = N_("Get the maximum migration bandwidth (in MiB/s) for a domain.")
11344     },
11345     {.name = NULL}
11346 };
11347 
11348 static const vshCmdOptDef opts_migrate_getspeed[] = {
11349     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
11350     {.name = "postcopy",
11351      .type = VSH_OT_BOOL,
11352      .help = N_("get post-copy migration bandwidth")
11353     },
11354     {.name = NULL}
11355 };
11356 
11357 static bool
cmdMigrateGetMaxSpeed(vshControl * ctl,const vshCmd * cmd)11358 cmdMigrateGetMaxSpeed(vshControl *ctl, const vshCmd *cmd)
11359 {
11360     g_autoptr(virshDomain) dom = NULL;
11361     unsigned long bandwidth;
11362     unsigned int flags = 0;
11363 
11364     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11365         return false;
11366 
11367     if (vshCommandOptBool(cmd, "postcopy"))
11368         flags |= VIR_DOMAIN_MIGRATE_MAX_SPEED_POSTCOPY;
11369 
11370     if (virDomainMigrateGetMaxSpeed(dom, &bandwidth, flags) < 0)
11371         return false;
11372 
11373     vshPrint(ctl, "%lu\n", bandwidth);
11374 
11375     return true;
11376 }
11377 
11378 /*
11379  * "migrate-postcopy" command
11380  */
11381 static const vshCmdInfo info_migrate_postcopy[] = {
11382     {.name = "help",
11383      .data = N_("Switch running migration from pre-copy to post-copy")
11384     },
11385     {.name = "desc",
11386      .data = N_("Switch running migration from pre-copy to post-copy. "
11387                 "The migration must have been started with --postcopy option.")
11388     },
11389     {.name = NULL}
11390 };
11391 
11392 static const vshCmdOptDef opts_migrate_postcopy[] = {
11393     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
11394     {.name = NULL}
11395 };
11396 
11397 static bool
cmdMigratePostCopy(vshControl * ctl,const vshCmd * cmd)11398 cmdMigratePostCopy(vshControl *ctl, const vshCmd *cmd)
11399 {
11400     g_autoptr(virshDomain) dom = NULL;
11401 
11402     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11403         return false;
11404 
11405     if (virDomainMigrateStartPostCopy(dom, 0) < 0)
11406         return false;
11407 
11408     return true;
11409 }
11410 
11411 /*
11412  * "domdisplay" command
11413  */
11414 static const vshCmdInfo info_domdisplay[] = {
11415     {.name = "help",
11416      .data = N_("domain display connection URI")
11417     },
11418     {.name = "desc",
11419      .data = N_("Output the IP address and port number "
11420                 "for the graphical display.")
11421     },
11422     {.name = NULL}
11423 };
11424 
11425 static const vshCmdOptDef opts_domdisplay[] = {
11426     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
11427     {.name = "include-password",
11428      .type = VSH_OT_BOOL,
11429      .help = N_("includes the password into the connection URI if available")
11430     },
11431     {.name = "type",
11432      .type = VSH_OT_STRING,
11433      .help = N_("select particular graphical display "
11434                 "(e.g. \"vnc\", \"spice\", \"rdp\")")
11435     },
11436     {.name = "all",
11437      .type = VSH_OT_BOOL,
11438      .help = N_("show all possible graphical displays")
11439     },
11440     {.name = NULL}
11441 };
11442 
11443 static bool
cmdDomDisplay(vshControl * ctl,const vshCmd * cmd)11444 cmdDomDisplay(vshControl *ctl, const vshCmd *cmd)
11445 {
11446     g_autoptr(xmlDoc) xml = NULL;
11447     g_autoptr(xmlXPathContext) ctxt = NULL;
11448     g_autoptr(virshDomain) dom = NULL;
11449     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
11450     bool ret = false;
11451     char *xpath = NULL;
11452     char *listen_addr = NULL;
11453     int port, tls_port = 0;
11454     char *type_conn = NULL;
11455     char *sockpath = NULL;
11456     char *passwd = NULL;
11457     char *output = NULL;
11458     const char *scheme[] = { "vnc", "spice", "rdp", NULL };
11459     const char *type = NULL;
11460     int iter = 0;
11461     int tmp;
11462     int flags = 0;
11463     bool params = false;
11464     bool all = vshCommandOptBool(cmd, "all");
11465     const char *xpath_fmt = "string(/domain/devices/graphics[@type='%s']/%s)";
11466     virSocketAddr addr;
11467 
11468     VSH_EXCLUSIVE_OPTIONS("all", "type");
11469 
11470     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11471         return false;
11472 
11473     if (!virDomainIsActive(dom)) {
11474         vshError(ctl, _("Domain is not running"));
11475         goto cleanup;
11476     }
11477 
11478     if (vshCommandOptBool(cmd, "include-password"))
11479         flags |= VIR_DOMAIN_XML_SECURE;
11480 
11481     if (vshCommandOptStringReq(ctl, cmd, "type", &type) < 0)
11482         goto cleanup;
11483 
11484     if (virshDomainGetXMLFromDom(ctl, dom, flags, &xml, &ctxt) < 0)
11485         goto cleanup;
11486 
11487     /* Attempt to grab our display info */
11488     for (iter = 0; scheme[iter] != NULL; iter++) {
11489         /* Particular scheme requested */
11490         if (!all && type && STRNEQ(type, scheme[iter]))
11491             continue;
11492 
11493         /* Create our XPATH lookup for the current display's port */
11494         VIR_FREE(xpath);
11495         xpath = g_strdup_printf(xpath_fmt, scheme[iter], "@port");
11496 
11497         /* Attempt to get the port number for the current graphics scheme */
11498         tmp = virXPathInt(xpath, ctxt, &port);
11499         VIR_FREE(xpath);
11500 
11501         /* If there is no port number for this type, then jump to the next
11502          * scheme */
11503         if (tmp)
11504             port = 0;
11505 
11506         /* Create our XPATH lookup for TLS Port (automatically skipped
11507          * for unsupported schemes */
11508         xpath = g_strdup_printf(xpath_fmt, scheme[iter], "@tlsPort");
11509 
11510         /* Attempt to get the TLS port number */
11511         tmp = virXPathInt(xpath, ctxt, &tls_port);
11512         VIR_FREE(xpath);
11513         if (tmp)
11514             tls_port = 0;
11515 
11516         /* Create our XPATH lookup for the current display's address */
11517         xpath = g_strdup_printf(xpath_fmt, scheme[iter], "@listen");
11518 
11519         /* Attempt to get the listening addr if set for the current
11520          * graphics scheme */
11521         VIR_FREE(listen_addr);
11522         listen_addr = virXPathString(xpath, ctxt);
11523         VIR_FREE(xpath);
11524 
11525         /* Create our XPATH lookup for the current spice type. */
11526         xpath = g_strdup_printf(xpath_fmt, scheme[iter], "listen/@type");
11527 
11528         /* Attempt to get the type of spice connection */
11529         VIR_FREE(type_conn);
11530         type_conn = virXPathString(xpath, ctxt);
11531         VIR_FREE(xpath);
11532 
11533         if (STREQ_NULLABLE(type_conn, "socket")) {
11534             if (!sockpath) {
11535                 xpath = g_strdup_printf(xpath_fmt, scheme[iter], "listen/@socket");
11536 
11537                 sockpath = virXPathString(xpath, ctxt);
11538 
11539                 VIR_FREE(xpath);
11540             }
11541         }
11542 
11543         if (!port && !tls_port && !sockpath)
11544             continue;
11545 
11546         if (!listen_addr) {
11547             /* The subelement address - <listen address='xyz'/> -
11548              * *should* have been automatically backfilled into its
11549              * parent <graphics listen='xyz'> (which we just tried to
11550              * retrieve into listen_addr above) but in some cases it
11551              * isn't, so we also do an explicit check for the
11552              * subelement (which, by the way, doesn't exist on libvirt
11553              * < 0.9.4, so we really do need to check both places)
11554              */
11555             xpath = g_strdup_printf(xpath_fmt, scheme[iter], "listen/@address");
11556 
11557             listen_addr = virXPathString(xpath, ctxt);
11558             VIR_FREE(xpath);
11559         } else {
11560             /* If listen_addr is 0.0.0.0 or [::] we should try to parse URI and set
11561              * listen_addr based on current URI. */
11562             if (virSocketAddrParse(&addr, listen_addr, AF_UNSPEC) > 0 &&
11563                 virSocketAddrIsWildcard(&addr)) {
11564 
11565                 virConnectPtr conn = ((virshControl *)(ctl->privData))->conn;
11566                 char *uriStr = virConnectGetURI(conn);
11567                 virURI *uri = NULL;
11568 
11569                 if (uriStr) {
11570                     uri = virURIParse(uriStr);
11571                     VIR_FREE(uriStr);
11572                 }
11573 
11574                 /* It's safe to free the listen_addr even if parsing of URI
11575                  * fails, if there is no listen_addr we will print "localhost". */
11576                 VIR_FREE(listen_addr);
11577 
11578                 if (uri) {
11579                     listen_addr = g_strdup(uri->server);
11580                     virURIFree(uri);
11581                 }
11582             }
11583         }
11584 
11585         /* We can query this info for all the graphics types since we'll
11586          * get nothing for the unsupported ones (just rdp for now).
11587          * Also the parameter '--include-password' was already taken
11588          * care of when getting the XML */
11589 
11590         /* Create our XPATH lookup for the password */
11591         xpath = g_strdup_printf(xpath_fmt, scheme[iter], "@passwd");
11592 
11593         /* Attempt to get the password */
11594         VIR_FREE(passwd);
11595         passwd = virXPathString(xpath, ctxt);
11596         VIR_FREE(xpath);
11597 
11598         /* Build up the full URI, starting with the scheme */
11599         if (sockpath)
11600             virBufferAsprintf(&buf, "%s+unix://", scheme[iter]);
11601         else
11602             virBufferAsprintf(&buf, "%s://", scheme[iter]);
11603 
11604         /* There is no user, so just append password if there's any */
11605         if (STREQ(scheme[iter], "vnc") && passwd)
11606             virBufferAsprintf(&buf, ":%s@", passwd);
11607 
11608         /* Then host name or IP */
11609         if (!listen_addr && !sockpath)
11610             virBufferAddLit(&buf, "localhost");
11611         else if (!sockpath && strchr(listen_addr, ':'))
11612             virBufferAsprintf(&buf, "[%s]", listen_addr);
11613         else if (sockpath)
11614             virBufferAsprintf(&buf, "%s", sockpath);
11615         else
11616             virBufferAsprintf(&buf, "%s", listen_addr);
11617 
11618         /* Free socket to prepare the pointer for the next iteration */
11619         VIR_FREE(sockpath);
11620 
11621         /* Add the port */
11622         if (port) {
11623             if (STREQ(scheme[iter], "vnc")) {
11624                 /* VNC protocol handlers take their port number as
11625                  * 'port' - 5900 */
11626                 port -= 5900;
11627             }
11628 
11629             virBufferAsprintf(&buf, ":%d", port);
11630         }
11631 
11632         /* TLS Port */
11633         if (tls_port) {
11634             virBufferAsprintf(&buf,
11635                               "?tls-port=%d",
11636                               tls_port);
11637             params = true;
11638         }
11639 
11640         if (STREQ(scheme[iter], "spice") && passwd) {
11641             virBufferAsprintf(&buf,
11642                               "%spassword=%s",
11643                               params ? "&" : "?",
11644                               passwd);
11645             params = true;
11646         }
11647 
11648         /* Print out our full URI */
11649         VIR_FREE(output);
11650         output = virBufferContentAndReset(&buf);
11651         vshPrint(ctl, "%s", output);
11652 
11653         /* We got what we came for so return successfully */
11654         ret = true;
11655         if (!all)
11656             break;
11657         vshPrint(ctl, "\n");
11658     }
11659 
11660     if (!ret) {
11661         if (type)
11662             vshError(ctl, _("No graphical display with type '%s' found"), type);
11663         else
11664             vshError(ctl, _("No graphical display found"));
11665     }
11666 
11667  cleanup:
11668     VIR_FREE(xpath);
11669     VIR_FREE(type_conn);
11670     VIR_FREE(sockpath);
11671     VIR_FREE(passwd);
11672     VIR_FREE(listen_addr);
11673     VIR_FREE(output);
11674     return ret;
11675 }
11676 
11677 /*
11678  * "vncdisplay" command
11679  */
11680 static const vshCmdInfo info_vncdisplay[] = {
11681     {.name = "help",
11682      .data = N_("vnc display")
11683     },
11684     {.name = "desc",
11685      .data = N_("Output the IP address and port number for the VNC display.")
11686     },
11687     {.name = NULL}
11688 };
11689 
11690 static const vshCmdOptDef opts_vncdisplay[] = {
11691     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
11692     {.name = NULL}
11693 };
11694 
11695 static bool
cmdVNCDisplay(vshControl * ctl,const vshCmd * cmd)11696 cmdVNCDisplay(vshControl *ctl, const vshCmd *cmd)
11697 {
11698     g_autoptr(xmlDoc) xml = NULL;
11699     g_autoptr(xmlXPathContext) ctxt = NULL;
11700     g_autoptr(virshDomain) dom = NULL;
11701     int port = 0;
11702     g_autofree char *listen_addr = NULL;
11703 
11704     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11705         return false;
11706 
11707     /* Check if the domain is active and don't rely on -1 for this */
11708     if (!virDomainIsActive(dom)) {
11709         vshError(ctl, _("Domain is not running"));
11710         return false;
11711     }
11712 
11713     if (virshDomainGetXMLFromDom(ctl, dom, 0, &xml, &ctxt) < 0)
11714         return false;
11715 
11716     /* Get the VNC port */
11717     if (virXPathInt("string(/domain/devices/graphics[@type='vnc']/@port)",
11718                     ctxt, &port)) {
11719         vshError(ctl, _("Failed to get VNC port. Is this domain using VNC?"));
11720         return false;
11721     }
11722 
11723     listen_addr = virXPathString("string(/domain/devices/graphics"
11724                                  "[@type='vnc']/@listen)", ctxt);
11725     if (!listen_addr) {
11726         /* The subelement address - <listen address='xyz'/> -
11727          * *should* have been automatically backfilled into its
11728          * parent <graphics listen='xyz'> (which we just tried to
11729          * retrieve into listen_addr above) but in some cases it
11730          * isn't, so we also do an explicit check for the
11731          * subelement (which, by the way, doesn't exist on libvirt
11732          * < 0.9.4, so we really do need to check both places)
11733          */
11734         listen_addr = virXPathString("string(/domain/devices/graphics"
11735                                      "[@type='vnc']/listen/@address)", ctxt);
11736     }
11737     if (listen_addr == NULL || STREQ(listen_addr, "0.0.0.0"))
11738         vshPrint(ctl, ":%d\n", port-5900);
11739     else
11740         vshPrint(ctl, "%s:%d\n", listen_addr, port-5900);
11741 
11742     return true;
11743 }
11744 
11745 /*
11746  * "ttyconsole" command
11747  */
11748 static const vshCmdInfo info_ttyconsole[] = {
11749     {.name = "help",
11750      .data = N_("tty console")
11751     },
11752     {.name = "desc",
11753      .data = N_("Output the device for the TTY console.")
11754     },
11755     {.name = NULL}
11756 };
11757 
11758 static const vshCmdOptDef opts_ttyconsole[] = {
11759     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
11760     {.name = NULL}
11761 };
11762 
11763 static bool
cmdTTYConsole(vshControl * ctl,const vshCmd * cmd)11764 cmdTTYConsole(vshControl *ctl, const vshCmd *cmd)
11765 {
11766     g_autoptr(xmlDoc) xml = NULL;
11767     g_autoptr(xmlXPathContext) ctxt = NULL;
11768     g_autofree char *tty = NULL;
11769 
11770     if (virshDomainGetXML(ctl, cmd, 0, &xml, &ctxt) < 0)
11771         return false;
11772 
11773     if (!(tty = virXPathString("string(/domain/devices/console/@tty)", ctxt)))
11774         return false;
11775 
11776     vshPrint(ctl, "%s\n", tty);
11777     return true;
11778 }
11779 
11780 /*
11781  * "domhostname" command
11782  */
11783 static const vshCmdInfo info_domhostname[] = {
11784     {.name = "help",
11785      .data = N_("print the domain's hostname")
11786     },
11787     {.name = "desc",
11788      .data = ""
11789     },
11790     {.name = NULL}
11791 };
11792 
11793 static const vshCmdOptDef opts_domhostname[] = {
11794     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
11795     {.name = "source",
11796      .type = VSH_OT_STRING,
11797      .flags = VSH_OFLAG_NONE,
11798      .completer = virshDomainHostnameSourceCompleter,
11799      .help = N_("address source: 'lease' or 'agent'")},
11800     {.name = NULL}
11801 };
11802 
11803 VIR_ENUM_IMPL(virshDomainHostnameSource,
11804               VIRSH_DOMAIN_HOSTNAME_SOURCE_LAST,
11805               "agent",
11806               "lease");
11807 
11808 static bool
cmdDomHostname(vshControl * ctl,const vshCmd * cmd)11809 cmdDomHostname(vshControl *ctl, const vshCmd *cmd)
11810 {
11811     g_autofree char *hostname = NULL;
11812     g_autoptr(virshDomain) dom = NULL;
11813     const char *sourcestr = NULL;
11814     int flags = 0; /* Use default value. Drivers can have its own default. */
11815 
11816     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11817         return false;
11818 
11819     if (vshCommandOptStringReq(ctl, cmd, "source", &sourcestr) < 0)
11820         return false;
11821 
11822     if (sourcestr) {
11823         int source = virshDomainHostnameSourceTypeFromString(sourcestr);
11824 
11825         if (source < 0) {
11826             vshError(ctl, _("Unknown data source '%s'"), sourcestr);
11827             return false;
11828         }
11829 
11830         switch ((virshDomainHostnameSource) source) {
11831         case VIRSH_DOMAIN_HOSTNAME_SOURCE_AGENT:
11832             flags |= VIR_DOMAIN_GET_HOSTNAME_AGENT;
11833             break;
11834         case VIRSH_DOMAIN_HOSTNAME_SOURCE_LEASE:
11835             flags |= VIR_DOMAIN_GET_HOSTNAME_LEASE;
11836             break;
11837         case VIRSH_DOMAIN_HOSTNAME_SOURCE_LAST:
11838             break;
11839         }
11840     }
11841 
11842     hostname = virDomainGetHostname(dom, flags);
11843     if (hostname == NULL) {
11844         vshError(ctl, "%s", _("failed to get hostname"));
11845         return false;
11846     }
11847 
11848     vshPrint(ctl, "%s\n", hostname);
11849     return true;
11850 }
11851 
11852 /*
11853  * "detach-device" command
11854  */
11855 static const vshCmdInfo info_detach_device[] = {
11856     {.name = "help",
11857      .data = N_("detach device from an XML file")
11858     },
11859     {.name = "desc",
11860      .data = N_("Detach device from an XML <file>")
11861     },
11862     {.name = NULL}
11863 };
11864 
11865 static const vshCmdOptDef opts_detach_device[] = {
11866     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
11867     VIRSH_COMMON_OPT_FILE(N_("XML file")),
11868     VIRSH_COMMON_OPT_DOMAIN_PERSISTENT,
11869     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
11870     VIRSH_COMMON_OPT_DOMAIN_LIVE,
11871     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
11872     {.name = NULL}
11873 };
11874 
11875 static bool
cmdDetachDevice(vshControl * ctl,const vshCmd * cmd)11876 cmdDetachDevice(vshControl *ctl, const vshCmd *cmd)
11877 {
11878     g_autoptr(virshDomain) dom = NULL;
11879     const char *from = NULL;
11880     g_autofree char *buffer = NULL;
11881     int ret;
11882     bool current = vshCommandOptBool(cmd, "current");
11883     bool config = vshCommandOptBool(cmd, "config");
11884     bool live = vshCommandOptBool(cmd, "live");
11885     bool persistent = vshCommandOptBool(cmd, "persistent");
11886     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
11887 
11888     VSH_EXCLUSIVE_OPTIONS_VAR(persistent, current);
11889 
11890     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
11891     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
11892 
11893     if (config || persistent)
11894         flags |= VIR_DOMAIN_AFFECT_CONFIG;
11895     if (live)
11896         flags |= VIR_DOMAIN_AFFECT_LIVE;
11897 
11898     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11899         return false;
11900 
11901     if (persistent &&
11902         virDomainIsActive(dom) == 1)
11903         flags |= VIR_DOMAIN_AFFECT_LIVE;
11904 
11905     if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
11906         return false;
11907 
11908     if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) {
11909         vshReportError(ctl);
11910         return false;
11911     }
11912 
11913     if (flags != 0 || current)
11914         ret = virDomainDetachDeviceFlags(dom, buffer, flags);
11915     else
11916         ret = virDomainDetachDevice(dom, buffer);
11917 
11918     if (ret < 0) {
11919         vshError(ctl, _("Failed to detach device from %s"), from);
11920         return false;
11921     }
11922 
11923     vshPrintExtra(ctl, "%s", _("Device detached successfully\n"));
11924     return true;
11925 }
11926 
11927 
11928 /*
11929  * "detach-device-alias" command
11930  */
11931 static const vshCmdInfo info_detach_device_alias[] = {
11932     {.name = "help",
11933      .data = N_("detach device from an alias")
11934     },
11935     {.name = "desc",
11936      .data = N_("Detach device identified by the given alias from a domain")
11937     },
11938     {.name = NULL}
11939 };
11940 
11941 static const vshCmdOptDef opts_detach_device_alias[] = {
11942     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
11943     {.name = "alias",
11944      .type = VSH_OT_DATA,
11945      .flags = VSH_OFLAG_REQ,
11946      .completer = virshDomainDeviceAliasCompleter,
11947      .help = N_("device alias")
11948     },
11949     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
11950     VIRSH_COMMON_OPT_DOMAIN_LIVE,
11951     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
11952     {.name = NULL}
11953 };
11954 
11955 static bool
cmdDetachDeviceAlias(vshControl * ctl,const vshCmd * cmd)11956 cmdDetachDeviceAlias(vshControl *ctl, const vshCmd *cmd)
11957 {
11958     g_autoptr(virshDomain) dom = NULL;
11959     const char *alias = NULL;
11960     bool current = vshCommandOptBool(cmd, "current");
11961     bool config = vshCommandOptBool(cmd, "config");
11962     bool live = vshCommandOptBool(cmd, "live");
11963     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
11964 
11965     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
11966     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
11967 
11968     if (config)
11969         flags |= VIR_DOMAIN_AFFECT_CONFIG;
11970     if (live)
11971         flags |= VIR_DOMAIN_AFFECT_LIVE;
11972 
11973     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
11974         return false;
11975 
11976     if (vshCommandOptStringReq(ctl, cmd, "alias", &alias) < 0)
11977         return false;
11978 
11979     if (virDomainDetachDeviceAlias(dom, alias, flags) < 0) {
11980         vshError(ctl, _("Failed to detach device with alias %s"), alias);
11981         return false;
11982     }
11983 
11984     vshPrintExtra(ctl, "%s", _("Device detach request sent successfully\n"));
11985     return true;
11986 }
11987 
11988 
11989 /*
11990  * "update-device" command
11991  */
11992 static const vshCmdInfo info_update_device[] = {
11993     {.name = "help",
11994      .data = N_("update device from an XML file")
11995     },
11996     {.name = "desc",
11997      .data = N_("Update device from an XML <file>.")
11998     },
11999     {.name = NULL}
12000 };
12001 
12002 static const vshCmdOptDef opts_update_device[] = {
12003     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
12004     VIRSH_COMMON_OPT_FILE(N_("XML file")),
12005     VIRSH_COMMON_OPT_DOMAIN_PERSISTENT,
12006     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
12007     VIRSH_COMMON_OPT_DOMAIN_LIVE,
12008     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
12009     {.name = "force",
12010      .type = VSH_OT_BOOL,
12011      .help = N_("force device update")
12012     },
12013     {.name = NULL}
12014 };
12015 
12016 static bool
cmdUpdateDevice(vshControl * ctl,const vshCmd * cmd)12017 cmdUpdateDevice(vshControl *ctl, const vshCmd *cmd)
12018 {
12019     g_autoptr(virshDomain) dom = NULL;
12020     const char *from = NULL;
12021     g_autofree char *buffer = NULL;
12022     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
12023     bool current = vshCommandOptBool(cmd, "current");
12024     bool config = vshCommandOptBool(cmd, "config");
12025     bool live = vshCommandOptBool(cmd, "live");
12026     bool persistent = vshCommandOptBool(cmd, "persistent");
12027 
12028     VSH_EXCLUSIVE_OPTIONS_VAR(persistent, current);
12029 
12030     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
12031     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
12032 
12033     if (config || persistent)
12034         flags |= VIR_DOMAIN_AFFECT_CONFIG;
12035     if (live)
12036         flags |= VIR_DOMAIN_AFFECT_LIVE;
12037 
12038     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
12039         return false;
12040 
12041     if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
12042         return false;
12043 
12044     if (persistent &&
12045         virDomainIsActive(dom) == 1)
12046         flags |= VIR_DOMAIN_AFFECT_LIVE;
12047 
12048     if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) {
12049         vshReportError(ctl);
12050         return false;
12051     }
12052 
12053     if (vshCommandOptBool(cmd, "force"))
12054         flags |= VIR_DOMAIN_DEVICE_MODIFY_FORCE;
12055 
12056     if (virDomainUpdateDeviceFlags(dom, buffer, flags) < 0) {
12057         vshError(ctl, _("Failed to update device from %s"), from);
12058         return false;
12059     }
12060 
12061     vshPrintExtra(ctl, "%s", _("Device updated successfully\n"));
12062 
12063     return true;
12064 }
12065 
12066 /*
12067  * "detach-interface" command
12068  */
12069 static const vshCmdInfo info_detach_interface[] = {
12070     {.name = "help",
12071      .data = N_("detach network interface")
12072     },
12073     {.name = "desc",
12074      .data = N_("Detach network interface.")
12075     },
12076     {.name = NULL}
12077 };
12078 
12079 static const vshCmdOptDef opts_detach_interface[] = {
12080     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
12081     {.name = "type",
12082      .type = VSH_OT_DATA,
12083      .flags = VSH_OFLAG_REQ,
12084      .help = N_("network interface type")
12085     },
12086     {.name = "mac",
12087      .type = VSH_OT_STRING,
12088      .completer = virshDomainInterfaceCompleter,
12089      .completer_flags = VIRSH_DOMAIN_INTERFACE_COMPLETER_MAC,
12090      .help = N_("MAC address")
12091     },
12092     VIRSH_COMMON_OPT_DOMAIN_PERSISTENT,
12093     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
12094     VIRSH_COMMON_OPT_DOMAIN_LIVE,
12095     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
12096     {.name = NULL}
12097 };
12098 
12099 static bool
virshDomainDetachInterface(char * doc,unsigned int flags,virDomainPtr dom,vshControl * ctl,bool current,const char * type,const char * mac)12100 virshDomainDetachInterface(char *doc,
12101                            unsigned int flags,
12102                            virDomainPtr dom,
12103                            vshControl *ctl,
12104                            bool current,
12105                            const char *type,
12106                            const char *mac)
12107 {
12108     g_autoptr(xmlDoc) xml = NULL;
12109     g_autoptr(xmlXPathObject) obj = NULL;
12110     g_autoptr(xmlXPathContext) ctxt = NULL;
12111     xmlNodePtr cur = NULL, matchNode = NULL;
12112     g_autofree char *detach_xml = NULL;
12113     char buf[64];
12114     int diff_mac = -1;
12115     size_t i;
12116 
12117     if (!(xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt))) {
12118         vshError(ctl, "%s", _("Failed to get interface information"));
12119         return false;
12120     }
12121 
12122     g_snprintf(buf, sizeof(buf), "/domain/devices/interface[@type='%s']", type);
12123     obj = xmlXPathEval(BAD_CAST buf, ctxt);
12124     if (obj == NULL || obj->type != XPATH_NODESET ||
12125         obj->nodesetval == NULL || obj->nodesetval->nodeNr == 0) {
12126         vshError(ctl, _("No interface found whose type is %s"), type);
12127         return false;
12128     }
12129 
12130     if (!mac && obj->nodesetval->nodeNr > 1) {
12131         vshError(ctl, _("Domain has %d interfaces. Please specify which one "
12132                         "to detach using --mac"), obj->nodesetval->nodeNr);
12133         return false;
12134     }
12135 
12136     if (!mac) {
12137         matchNode = obj->nodesetval->nodeTab[0];
12138         goto hit;
12139     }
12140 
12141     /* multiple possibilities, so search for matching mac */
12142     for (i = 0; i < obj->nodesetval->nodeNr; i++) {
12143         cur = obj->nodesetval->nodeTab[i]->children;
12144         while (cur != NULL) {
12145             if (cur->type == XML_ELEMENT_NODE &&
12146                 virXMLNodeNameEqual(cur, "mac")) {
12147                 g_autofree char *tmp_mac = virXMLPropString(cur, "address");
12148                 diff_mac = virMacAddrCompare(tmp_mac, mac);
12149                 if (!diff_mac) {
12150                     if (matchNode) {
12151                         /* this is the 2nd match, so it's ambiguous */
12152                         vshError(ctl, _("Domain has multiple interfaces matching "
12153                                         "MAC address %s. You must use detach-device and "
12154                                         "specify the device pci address to remove it."),
12155                                  mac);
12156                         return false;
12157                     }
12158                     matchNode = obj->nodesetval->nodeTab[i];
12159                 }
12160             }
12161             cur = cur->next;
12162         }
12163     }
12164     if (!matchNode) {
12165         vshError(ctl, _("No interface with MAC address %s was found"), mac);
12166         return false;
12167     }
12168 
12169  hit:
12170     if (!(detach_xml = virXMLNodeToString(xml, matchNode))) {
12171         vshSaveLibvirtError();
12172         return false;
12173     }
12174 
12175     if (flags != 0 || current)
12176         return virDomainDetachDeviceFlags(dom, detach_xml, flags) == 0;
12177     return virDomainDetachDevice(dom, detach_xml) == 0;
12178 }
12179 
12180 
12181 static bool
cmdDetachInterface(vshControl * ctl,const vshCmd * cmd)12182 cmdDetachInterface(vshControl *ctl, const vshCmd *cmd)
12183 {
12184     g_autoptr(virshDomain) dom = NULL;
12185     g_autofree char *doc_live = NULL;
12186     g_autofree char *doc_config = NULL;
12187     const char *mac = NULL, *type = NULL;
12188     int flags = 0;
12189     bool ret = false, affect_config, affect_live;
12190     bool current = vshCommandOptBool(cmd, "current");
12191     bool config = vshCommandOptBool(cmd, "config");
12192     bool live = vshCommandOptBool(cmd, "live");
12193     bool persistent = vshCommandOptBool(cmd, "persistent");
12194 
12195     VSH_EXCLUSIVE_OPTIONS_VAR(persistent, current);
12196 
12197     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
12198     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
12199 
12200     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
12201         return false;
12202 
12203     if (vshCommandOptStringReq(ctl, cmd, "type", &type) < 0)
12204         goto cleanup;
12205 
12206     if (vshCommandOptStringReq(ctl, cmd, "mac", &mac) < 0)
12207         goto cleanup;
12208 
12209     affect_config = (config || persistent);
12210 
12211     if (affect_config) {
12212         if (!(doc_config = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE)))
12213             goto cleanup;
12214         if (!(ret = virshDomainDetachInterface(doc_config,
12215                                                flags | VIR_DOMAIN_AFFECT_CONFIG,
12216                                                dom, ctl, current, type, mac)))
12217             goto cleanup;
12218     }
12219 
12220     affect_live = (live || (persistent && virDomainIsActive(dom) == 1));
12221 
12222     if (affect_live || !affect_config) {
12223         flags = 0;
12224 
12225         if (affect_live)
12226             flags |= VIR_DOMAIN_AFFECT_LIVE;
12227 
12228         if (!(doc_live = virDomainGetXMLDesc(dom, 0)))
12229             goto cleanup;
12230 
12231         ret = virshDomainDetachInterface(doc_live, flags,
12232                                          dom, ctl, current, type, mac);
12233     }
12234 
12235  cleanup:
12236     if (!ret) {
12237         vshError(ctl, "%s", _("Failed to detach interface"));
12238     } else {
12239         vshPrintExtra(ctl, "%s", _("Interface detached successfully\n"));
12240     }
12241     return ret;
12242 }
12243 
12244 
12245 static void
virshDiskDropBackingStore(xmlNodePtr disk_node)12246 virshDiskDropBackingStore(xmlNodePtr disk_node)
12247 {
12248     xmlNodePtr tmp;
12249 
12250     for (tmp = disk_node->children; tmp; tmp = tmp->next) {
12251         if (tmp->type != XML_ELEMENT_NODE)
12252             continue;
12253 
12254         if (virXMLNodeNameEqual(tmp, "backingStore")) {
12255             xmlUnlinkNode(tmp);
12256             xmlFreeNode(tmp);
12257 
12258             return;
12259         }
12260     }
12261 }
12262 
12263 
12264 typedef enum {
12265     VIRSH_FIND_DISK_NORMAL,
12266     VIRSH_FIND_DISK_CHANGEABLE,
12267 } virshFindDiskType;
12268 
12269 /* Helper function to find disk device in XML doc.  Returns the disk
12270  * node on success, or NULL on failure. Caller must free the result
12271  * @path: Fully-qualified path or target of disk device.
12272  * @type: Either VIRSH_FIND_DISK_NORMAL or VIRSH_FIND_DISK_CHANGEABLE.
12273  */
12274 static xmlNodePtr
virshFindDisk(const char * doc,const char * path,int type)12275 virshFindDisk(const char *doc,
12276               const char *path,
12277               int type)
12278 {
12279     g_autoptr(xmlDoc) xml = NULL;
12280     g_autoptr(xmlXPathObject) obj = NULL;
12281     g_autoptr(xmlXPathContext) ctxt = NULL;
12282     xmlNodePtr cur = NULL;
12283     size_t i;
12284 
12285     xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
12286     if (!xml) {
12287         vshError(NULL, "%s", _("Failed to get disk information"));
12288         return NULL;
12289     }
12290 
12291     obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt);
12292     if (obj == NULL ||
12293         obj->type != XPATH_NODESET ||
12294         obj->nodesetval == NULL ||
12295         obj->nodesetval->nodeNr == 0) {
12296         vshError(NULL, "%s", _("Failed to get disk information"));
12297         return NULL;
12298     }
12299 
12300     /* search disk using @path */
12301     for (i = 0; i < obj->nodesetval->nodeNr; i++) {
12302         bool is_supported = true;
12303 
12304         if (type == VIRSH_FIND_DISK_CHANGEABLE) {
12305             xmlNodePtr n = obj->nodesetval->nodeTab[i];
12306             is_supported = false;
12307 
12308             /* Check if the disk is CDROM or floppy disk */
12309             if (virXMLNodeNameEqual(n, "disk")) {
12310                 g_autofree char *device_value = virXMLPropString(n, "device");
12311 
12312                 if (STREQ(device_value, "cdrom") ||
12313                     STREQ(device_value, "floppy"))
12314                     is_supported = true;
12315             }
12316 
12317             if (!is_supported)
12318                 continue;
12319         }
12320 
12321         cur = obj->nodesetval->nodeTab[i]->children;
12322         while (cur != NULL) {
12323             if (cur->type == XML_ELEMENT_NODE) {
12324                 g_autofree char *tmp = NULL;
12325 
12326                 if (virXMLNodeNameEqual(cur, "source")) {
12327                     if ((tmp = virXMLPropString(cur, "file")) ||
12328                         (tmp = virXMLPropString(cur, "dev")) ||
12329                         (tmp = virXMLPropString(cur, "dir")) ||
12330                         (tmp = virXMLPropString(cur, "name"))) {
12331                     }
12332                 } else if (virXMLNodeNameEqual(cur, "target")) {
12333                     tmp = virXMLPropString(cur, "dev");
12334                 }
12335 
12336                 if (STREQ_NULLABLE(tmp, path)) {
12337                     xmlNodePtr ret = xmlCopyNode(obj->nodesetval->nodeTab[i], 1);
12338                     /* drop backing store since they are not needed here */
12339                     virshDiskDropBackingStore(ret);
12340                     return ret;
12341                 }
12342             }
12343             cur = cur->next;
12344         }
12345     }
12346 
12347     vshError(NULL, _("No disk found whose source path or target is %s"), path);
12348 
12349     return NULL;
12350 }
12351 
12352 typedef enum {
12353     VIRSH_UPDATE_DISK_XML_EJECT,
12354     VIRSH_UPDATE_DISK_XML_INSERT,
12355     VIRSH_UPDATE_DISK_XML_UPDATE,
12356 } virshUpdateDiskXMLType;
12357 
12358 /* Helper function to prepare disk XML. Could be used for disk
12359  * detaching, media changing(ejecting, inserting, updating)
12360  * for changeable disk. Returns the processed XML as string on
12361  * success, or NULL on failure. Caller must free the result.
12362  */
12363 static char *
virshUpdateDiskXML(xmlNodePtr disk_node,const char * new_source,bool source_block,const char * target,virshUpdateDiskXMLType type)12364 virshUpdateDiskXML(xmlNodePtr disk_node,
12365                    const char *new_source,
12366                    bool source_block,
12367                    const char *target,
12368                    virshUpdateDiskXMLType type)
12369 {
12370     xmlNodePtr tmp = NULL;
12371     xmlNodePtr source = NULL;
12372     xmlNodePtr target_node = NULL;
12373     xmlNodePtr text_node = NULL;
12374     g_autofree char *device_type = NULL;
12375     char *ret = NULL;
12376     g_autofree char *startupPolicy = NULL;
12377     g_autofree char *source_path = NULL;
12378 
12379     if (!disk_node)
12380         return NULL;
12381 
12382     device_type = virXMLPropString(disk_node, "device");
12383 
12384     if (!(STREQ_NULLABLE(device_type, "cdrom") ||
12385           STREQ_NULLABLE(device_type, "floppy"))) {
12386         vshError(NULL, _("The disk device '%s' is not removable"), target);
12387         return NULL;
12388     }
12389 
12390     /* find the current source subelement */
12391     for (tmp = disk_node->children; tmp; tmp = tmp->next) {
12392         /*
12393          * Save the last text node before the <target/>.  The
12394          * reasoning behind this is that the target node will be
12395          * present in this case and also has a proper indentation.
12396          */
12397         if (!target_node && tmp->type == XML_TEXT_NODE)
12398             text_node = tmp;
12399 
12400         /*
12401          * We need only element nodes from now on.
12402          */
12403         if (tmp->type != XML_ELEMENT_NODE)
12404             continue;
12405 
12406         if (!source && virXMLNodeNameEqual(tmp, "source"))
12407             source = tmp;
12408         else if (!target_node && virXMLNodeNameEqual(tmp, "target"))
12409             target_node = tmp;
12410 
12411         /*
12412          * We've found all we needed.
12413          */
12414         if (source && target_node)
12415             break;
12416     }
12417 
12418     if (type == VIRSH_UPDATE_DISK_XML_EJECT) {
12419         if (!source) {
12420             vshError(NULL, _("The disk device '%s' doesn't have media"), target);
12421             return NULL;
12422         }
12423 
12424         /* forcibly switch to empty file cdrom */
12425         source_block = false;
12426         new_source = NULL;
12427     } else if (!new_source) {
12428         vshError(NULL, _("New disk media source was not specified"));
12429         return NULL;
12430     }
12431 
12432     if (source) {
12433         if (!(source_path = virXMLPropString(source, "file")) &&
12434             !(source_path = virXMLPropString(source, "dev")) &&
12435             !(source_path = virXMLPropString(source, "dir")) &&
12436             !(source_path = virXMLPropString(source, "pool")))
12437             source_path = virXMLPropString(source, "name");
12438 
12439         if (source_path && type == VIRSH_UPDATE_DISK_XML_INSERT) {
12440             vshError(NULL, _("The disk device '%s' already has media"), target);
12441             return NULL;
12442         }
12443 
12444         startupPolicy = virXMLPropString(source, "startupPolicy");
12445 
12446         /* remove current source */
12447         xmlUnlinkNode(source);
12448         xmlFreeNode(source);
12449         source = NULL;
12450     }
12451 
12452     /* set the correct disk type */
12453     if (source_block)
12454         xmlSetProp(disk_node, BAD_CAST "type", BAD_CAST "block");
12455     else
12456         xmlSetProp(disk_node, BAD_CAST "type", BAD_CAST "file");
12457 
12458     if (new_source) {
12459         /* create new source subelement */
12460         source = virXMLNewNode(NULL, "source");
12461 
12462         if (source_block)
12463             xmlNewProp(source, BAD_CAST "dev", BAD_CAST new_source);
12464         else
12465             xmlNewProp(source, BAD_CAST "file", BAD_CAST new_source);
12466 
12467         if (startupPolicy)
12468             xmlNewProp(source, BAD_CAST "startupPolicy", BAD_CAST startupPolicy);
12469 
12470         /*
12471          * So that the output XML looks nice in case anyone calls
12472          * 'change-media' with '--print-xml', let's attach the source
12473          * before target...
12474          */
12475         xmlAddPrevSibling(target_node, source);
12476 
12477         /*
12478          * ... and duplicate the text node doing the indentation just
12479          * so it's more easily readable.  And don't make it fatal.
12480          */
12481         if ((tmp = xmlCopyNode(text_node, 0))) {
12482             if (!xmlAddPrevSibling(target_node, tmp))
12483                 xmlFreeNode(tmp);
12484         }
12485     }
12486 
12487     if (!(ret = virXMLNodeToString(NULL, disk_node))) {
12488         vshSaveLibvirtError();
12489         return NULL;
12490     }
12491 
12492     return ret;
12493 }
12494 
12495 
12496 /*
12497  * "detach-disk" command
12498  */
12499 static const vshCmdInfo info_detach_disk[] = {
12500     {.name = "help",
12501      .data = N_("detach disk device")
12502     },
12503     {.name = "desc",
12504      .data = N_("Detach disk device.")
12505     },
12506     {.name = NULL}
12507 };
12508 
12509 static const vshCmdOptDef opts_detach_disk[] = {
12510     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
12511     {.name = "target",
12512      .type = VSH_OT_DATA,
12513      .flags = VSH_OFLAG_REQ,
12514      .completer = virshDomainDiskTargetCompleter,
12515      .help = N_("target of disk device")
12516     },
12517     VIRSH_COMMON_OPT_DOMAIN_PERSISTENT,
12518     VIRSH_COMMON_OPT_DOMAIN_CONFIG,
12519     VIRSH_COMMON_OPT_DOMAIN_LIVE,
12520     VIRSH_COMMON_OPT_DOMAIN_CURRENT,
12521     {.name = "print-xml",
12522      .type = VSH_OT_BOOL,
12523      .help = N_("print XML document rather than detach the disk")
12524     },
12525     {.name = NULL}
12526 };
12527 
12528 static bool
cmdDetachDisk(vshControl * ctl,const vshCmd * cmd)12529 cmdDetachDisk(vshControl *ctl, const vshCmd *cmd)
12530 {
12531     g_autofree char *disk_xml = NULL;
12532     g_autoptr(virshDomain) dom = NULL;
12533     const char *target = NULL;
12534     g_autofree char *doc = NULL;
12535     int ret;
12536     bool functionReturn = false;
12537     xmlNodePtr disk_node = NULL;
12538     bool current = vshCommandOptBool(cmd, "current");
12539     bool config = vshCommandOptBool(cmd, "config");
12540     bool live = vshCommandOptBool(cmd, "live");
12541     bool persistent = vshCommandOptBool(cmd, "persistent");
12542     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
12543 
12544     VSH_EXCLUSIVE_OPTIONS_VAR(persistent, current);
12545 
12546     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
12547     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
12548 
12549     if (config || persistent)
12550         flags |= VIR_DOMAIN_AFFECT_CONFIG;
12551     if (live)
12552         flags |= VIR_DOMAIN_AFFECT_LIVE;
12553 
12554     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
12555         return false;
12556 
12557     if (vshCommandOptStringReq(ctl, cmd, "target", &target) < 0)
12558         goto cleanup;
12559 
12560     if (flags == VIR_DOMAIN_AFFECT_CONFIG)
12561         doc = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
12562     else
12563         doc = virDomainGetXMLDesc(dom, 0);
12564 
12565     if (!doc)
12566         goto cleanup;
12567 
12568     if (persistent &&
12569         virDomainIsActive(dom) == 1)
12570         flags |= VIR_DOMAIN_AFFECT_LIVE;
12571 
12572     if (!(disk_node = virshFindDisk(doc, target, VIRSH_FIND_DISK_NORMAL)))
12573         goto cleanup;
12574 
12575     if (!(disk_xml = virXMLNodeToString(NULL, disk_node))) {
12576         vshSaveLibvirtError();
12577         goto cleanup;
12578     }
12579 
12580     if (vshCommandOptBool(cmd, "print-xml")) {
12581         vshPrint(ctl, "%s", disk_xml);
12582         functionReturn = true;
12583         goto cleanup;
12584     }
12585 
12586     if (flags != 0 || current)
12587         ret = virDomainDetachDeviceFlags(dom, disk_xml, flags);
12588     else
12589         ret = virDomainDetachDevice(dom, disk_xml);
12590 
12591     if (ret != 0) {
12592         vshError(ctl, "%s", _("Failed to detach disk"));
12593         goto cleanup;
12594     }
12595 
12596     vshPrintExtra(ctl, "%s", _("Disk detached successfully\n"));
12597     functionReturn = true;
12598 
12599  cleanup:
12600     xmlFreeNode(disk_node);
12601     return functionReturn;
12602 }
12603 
12604 /*
12605  * "edit" command
12606  */
12607 static const vshCmdInfo info_edit[] = {
12608     {.name = "help",
12609      .data = N_("edit XML configuration for a domain")
12610     },
12611     {.name = "desc",
12612      .data = N_("Edit the XML configuration for a domain.")
12613     },
12614     {.name = NULL}
12615 };
12616 
12617 static const vshCmdOptDef opts_edit[] = {
12618     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
12619     {.name = "skip-validate",
12620      .type = VSH_OT_BOOL,
12621      .help = N_("skip validation of the XML against the schema")
12622     },
12623     {.name = NULL}
12624 };
12625 
12626 static bool
cmdEdit(vshControl * ctl,const vshCmd * cmd)12627 cmdEdit(vshControl *ctl, const vshCmd *cmd)
12628 {
12629     bool ret = false;
12630     g_autoptr(virshDomain) dom = NULL;
12631     g_autoptr(virshDomain) dom_edited = NULL;
12632     unsigned int query_flags = VIR_DOMAIN_XML_SECURE | VIR_DOMAIN_XML_INACTIVE;
12633     unsigned int define_flags = VIR_DOMAIN_DEFINE_VALIDATE;
12634     virshControl *priv = ctl->privData;
12635 
12636     dom = virshCommandOptDomain(ctl, cmd, NULL);
12637     if (dom == NULL)
12638         goto cleanup;
12639 
12640     if (vshCommandOptBool(cmd, "skip-validate"))
12641         define_flags &= ~VIR_DOMAIN_DEFINE_VALIDATE;
12642 
12643 #define EDIT_GET_XML virDomainGetXMLDesc(dom, query_flags)
12644 #define EDIT_NOT_CHANGED \
12645     do { \
12646         vshPrintExtra(ctl, _("Domain '%s' XML configuration not changed.\n"), \
12647                       virDomainGetName(dom)); \
12648         ret = true; \
12649         goto edit_cleanup; \
12650     } while (0)
12651 #define EDIT_DEFINE \
12652     (dom_edited = virshDomainDefine(priv->conn, doc_edited, define_flags))
12653 #define EDIT_RELAX \
12654     do { \
12655         define_flags &= ~VIR_DOMAIN_DEFINE_VALIDATE; \
12656     } while (0);
12657 
12658 #include "virsh-edit.c"
12659 #undef EDIT_RELAX
12660 
12661     vshPrintExtra(ctl, _("Domain '%s' XML configuration edited.\n"),
12662                   virDomainGetName(dom_edited));
12663 
12664     ret = true;
12665 
12666  cleanup:
12667 
12668     return ret;
12669 }
12670 
12671 
12672 /*
12673  * "event" command
12674  */
12675 VIR_ENUM_DECL(virshDomainEvent);
12676 VIR_ENUM_IMPL(virshDomainEvent,
12677               VIR_DOMAIN_EVENT_LAST,
12678               N_("Defined"),
12679               N_("Undefined"),
12680               N_("Started"),
12681               N_("Suspended"),
12682               N_("Resumed"),
12683               N_("Stopped"),
12684               N_("Shutdown"),
12685               N_("PMSuspended"),
12686               N_("Crashed"));
12687 
12688 static const char *
virshDomainEventToString(int event)12689 virshDomainEventToString(int event)
12690 {
12691     const char *str = virshDomainEventTypeToString(event);
12692     return str ? _(str) : _("unknown");
12693 }
12694 
12695 VIR_ENUM_DECL(virshDomainEventDefined);
12696 VIR_ENUM_IMPL(virshDomainEventDefined,
12697               VIR_DOMAIN_EVENT_DEFINED_LAST,
12698               N_("Added"),
12699               N_("Updated"),
12700               N_("Renamed"),
12701               N_("Snapshot"));
12702 
12703 VIR_ENUM_DECL(virshDomainEventUndefined);
12704 VIR_ENUM_IMPL(virshDomainEventUndefined,
12705               VIR_DOMAIN_EVENT_UNDEFINED_LAST,
12706               N_("Removed"),
12707               N_("Renamed"));
12708 
12709 VIR_ENUM_DECL(virshDomainEventStarted);
12710 VIR_ENUM_IMPL(virshDomainEventStarted,
12711               VIR_DOMAIN_EVENT_STARTED_LAST,
12712               N_("Booted"),
12713               N_("Migrated"),
12714               N_("Restored"),
12715               N_("Snapshot"),
12716               N_("Event wakeup"));
12717 
12718 VIR_ENUM_DECL(virshDomainEventSuspended);
12719 VIR_ENUM_IMPL(virshDomainEventSuspended,
12720               VIR_DOMAIN_EVENT_SUSPENDED_LAST,
12721               N_("Paused"),
12722               N_("Migrated"),
12723               N_("I/O Error"),
12724               N_("Watchdog"),
12725               N_("Restored"),
12726               N_("Snapshot"),
12727               N_("API error"),
12728               N_("Post-copy"),
12729               N_("Post-copy Error"));
12730 
12731 VIR_ENUM_DECL(virshDomainEventResumed);
12732 VIR_ENUM_IMPL(virshDomainEventResumed,
12733               VIR_DOMAIN_EVENT_RESUMED_LAST,
12734               N_("Unpaused"),
12735               N_("Migrated"),
12736               N_("Snapshot"),
12737               N_("Post-copy"));
12738 
12739 VIR_ENUM_DECL(virshDomainEventStopped);
12740 VIR_ENUM_IMPL(virshDomainEventStopped,
12741               VIR_DOMAIN_EVENT_STOPPED_LAST,
12742               N_("Shutdown"),
12743               N_("Destroyed"),
12744               N_("Crashed"),
12745               N_("Migrated"),
12746               N_("Saved"),
12747               N_("Failed"),
12748               N_("Snapshot"));
12749 
12750 VIR_ENUM_DECL(virshDomainEventShutdown);
12751 VIR_ENUM_IMPL(virshDomainEventShutdown,
12752               VIR_DOMAIN_EVENT_SHUTDOWN_LAST,
12753               N_("Finished"),
12754               N_("Finished after guest request"),
12755               N_("Finished after host request"));
12756 
12757 VIR_ENUM_DECL(virshDomainEventPMSuspended);
12758 VIR_ENUM_IMPL(virshDomainEventPMSuspended,
12759               VIR_DOMAIN_EVENT_PMSUSPENDED_LAST,
12760               N_("Memory"),
12761               N_("Disk"));
12762 
12763 VIR_ENUM_DECL(virshDomainEventCrashed);
12764 VIR_ENUM_IMPL(virshDomainEventCrashed,
12765               VIR_DOMAIN_EVENT_CRASHED_LAST,
12766               N_("Panicked"),
12767               N_("Crashloaded"));
12768 
12769 static const char *
virshDomainEventDetailToString(int event,int detail)12770 virshDomainEventDetailToString(int event, int detail)
12771 {
12772     const char *str = NULL;
12773     switch ((virDomainEventType) event) {
12774     case VIR_DOMAIN_EVENT_DEFINED:
12775         str = virshDomainEventDefinedTypeToString(detail);
12776         break;
12777     case VIR_DOMAIN_EVENT_UNDEFINED:
12778         str = virshDomainEventUndefinedTypeToString(detail);
12779         break;
12780     case VIR_DOMAIN_EVENT_STARTED:
12781         str = virshDomainEventStartedTypeToString(detail);
12782         break;
12783     case VIR_DOMAIN_EVENT_SUSPENDED:
12784         str = virshDomainEventSuspendedTypeToString(detail);
12785         break;
12786     case VIR_DOMAIN_EVENT_RESUMED:
12787         str = virshDomainEventResumedTypeToString(detail);
12788         break;
12789     case VIR_DOMAIN_EVENT_STOPPED:
12790         str = virshDomainEventStoppedTypeToString(detail);
12791         break;
12792     case VIR_DOMAIN_EVENT_SHUTDOWN:
12793         str = virshDomainEventShutdownTypeToString(detail);
12794         break;
12795     case VIR_DOMAIN_EVENT_PMSUSPENDED:
12796         str = virshDomainEventPMSuspendedTypeToString(detail);
12797         break;
12798     case VIR_DOMAIN_EVENT_CRASHED:
12799         str = virshDomainEventCrashedTypeToString(detail);
12800         break;
12801     case VIR_DOMAIN_EVENT_LAST:
12802         break;
12803     }
12804     return str ? _(str) : _("unknown");
12805 }
12806 
12807 VIR_ENUM_DECL(virshDomainEventWatchdog);
12808 VIR_ENUM_IMPL(virshDomainEventWatchdog,
12809               VIR_DOMAIN_EVENT_WATCHDOG_LAST,
12810               N_("none"),
12811               N_("pause"),
12812               N_("reset"),
12813               N_("poweroff"),
12814               N_("shutdown"),
12815               N_("debug"),
12816               N_("inject-nmi"));
12817 
12818 static const char *
virshDomainEventWatchdogToString(int action)12819 virshDomainEventWatchdogToString(int action)
12820 {
12821     const char *str = virshDomainEventWatchdogTypeToString(action);
12822     return str ? _(str) : _("unknown");
12823 }
12824 
12825 VIR_ENUM_DECL(virshDomainEventIOError);
12826 VIR_ENUM_IMPL(virshDomainEventIOError,
12827               VIR_DOMAIN_EVENT_IO_ERROR_LAST,
12828               N_("none"),
12829               N_("pause"),
12830               N_("report"));
12831 
12832 static const char *
virshDomainEventIOErrorToString(int action)12833 virshDomainEventIOErrorToString(int action)
12834 {
12835     const char *str = virshDomainEventIOErrorTypeToString(action);
12836     return str ? _(str) : _("unknown");
12837 }
12838 
12839 VIR_ENUM_DECL(virshGraphicsPhase);
12840 VIR_ENUM_IMPL(virshGraphicsPhase,
12841               VIR_DOMAIN_EVENT_GRAPHICS_LAST,
12842               N_("connect"),
12843               N_("initialize"),
12844               N_("disconnect"));
12845 
12846 static const char *
virshGraphicsPhaseToString(int phase)12847 virshGraphicsPhaseToString(int phase)
12848 {
12849     const char *str = virshGraphicsPhaseTypeToString(phase);
12850     return str ? _(str) : _("unknown");
12851 }
12852 
12853 VIR_ENUM_DECL(virshGraphicsAddress);
12854 VIR_ENUM_IMPL(virshGraphicsAddress,
12855               VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_LAST,
12856               N_("IPv4"),
12857               N_("IPv6"),
12858               N_("unix"));
12859 
12860 static const char *
virshGraphicsAddressToString(int family)12861 virshGraphicsAddressToString(int family)
12862 {
12863     const char *str = virshGraphicsAddressTypeToString(family);
12864     return str ? _(str) : _("unknown");
12865 }
12866 
12867 VIR_ENUM_DECL(virshDomainBlockJobStatus);
12868 VIR_ENUM_IMPL(virshDomainBlockJobStatus,
12869               VIR_DOMAIN_BLOCK_JOB_LAST,
12870               N_("completed"),
12871               N_("failed"),
12872               N_("canceled"),
12873               N_("ready"));
12874 
12875 static const char *
virshDomainBlockJobStatusToString(int status)12876 virshDomainBlockJobStatusToString(int status)
12877 {
12878     const char *str = virshDomainBlockJobStatusTypeToString(status);
12879     return str ? _(str) : _("unknown");
12880 }
12881 
12882 VIR_ENUM_DECL(virshDomainEventDiskChange);
12883 VIR_ENUM_IMPL(virshDomainEventDiskChange,
12884               VIR_DOMAIN_EVENT_DISK_CHANGE_LAST,
12885               N_("changed"),
12886               N_("dropped"));
12887 
12888 static const char *
virshDomainEventDiskChangeToString(int reason)12889 virshDomainEventDiskChangeToString(int reason)
12890 {
12891     const char *str = virshDomainEventDiskChangeTypeToString(reason);
12892     return str ? _(str) : _("unknown");
12893 }
12894 
12895 VIR_ENUM_DECL(virshDomainEventTrayChange);
12896 VIR_ENUM_IMPL(virshDomainEventTrayChange,
12897               VIR_DOMAIN_EVENT_TRAY_CHANGE_LAST,
12898               N_("opened"),
12899               N_("closed"));
12900 
12901 static const char *
virshDomainEventTrayChangeToString(int reason)12902 virshDomainEventTrayChangeToString(int reason)
12903 {
12904     const char *str = virshDomainEventTrayChangeTypeToString(reason);
12905     return str ? _(str) : _("unknown");
12906 }
12907 
12908 struct virshDomEventData {
12909     vshControl *ctl;
12910     bool loop;
12911     int *count;
12912     bool timestamp;
12913     virshDomainEventCallback *cb;
12914     int id;
12915 };
12916 typedef struct virshDomEventData virshDomEventData;
12917 
12918 /**
12919  * virshEventPrint:
12920  *
12921  * @data: opaque data passed to all event callbacks
12922  * @buf: string buffer describing the event
12923  *
12924  * Print the event description found in @buf and update virshDomEventData.
12925  *
12926  * This function resets @buf and frees all memory consumed by its content.
12927  */
12928 static void
virshEventPrint(virshDomEventData * data,virBuffer * buf)12929 virshEventPrint(virshDomEventData *data,
12930                 virBuffer *buf)
12931 {
12932     char *msg;
12933 
12934     if (!(msg = virBufferContentAndReset(buf)))
12935         return;
12936 
12937     if (!data->loop && *data->count)
12938         goto cleanup;
12939 
12940     if (data->timestamp) {
12941         char timestamp[VIR_TIME_STRING_BUFLEN];
12942 
12943         if (virTimeStringNowRaw(timestamp) < 0)
12944             timestamp[0] = '\0';
12945 
12946         vshPrint(data->ctl, "%s: %s", timestamp, msg);
12947     } else {
12948         vshPrint(data->ctl, "%s", msg);
12949     }
12950 
12951     (*data->count)++;
12952     if (!data->loop)
12953         vshEventDone(data->ctl);
12954 
12955  cleanup:
12956     VIR_FREE(msg);
12957 }
12958 
12959 static void
virshEventGenericPrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,void * opaque)12960 virshEventGenericPrint(virConnectPtr conn G_GNUC_UNUSED,
12961                        virDomainPtr dom,
12962                        void *opaque)
12963 {
12964     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
12965 
12966     virBufferAsprintf(&buf, _("event '%s' for domain '%s'\n"),
12967                       ((virshDomEventData *) opaque)->cb->name,
12968                       virDomainGetName(dom));
12969     virshEventPrint(opaque, &buf);
12970 }
12971 
12972 static void
virshEventLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,int event,int detail,void * opaque)12973 virshEventLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED,
12974                          virDomainPtr dom,
12975                          int event,
12976                          int detail,
12977                          void *opaque)
12978 {
12979     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
12980 
12981     virBufferAsprintf(&buf, _("event 'lifecycle' for domain '%s': %s %s\n"),
12982                       virDomainGetName(dom),
12983                       virshDomainEventToString(event),
12984                       virshDomainEventDetailToString(event, detail));
12985     virshEventPrint(opaque, &buf);
12986 }
12987 
12988 static void
virshEventRTCChangePrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,long long utcoffset,void * opaque)12989 virshEventRTCChangePrint(virConnectPtr conn G_GNUC_UNUSED,
12990                          virDomainPtr dom,
12991                          long long utcoffset,
12992                          void *opaque)
12993 {
12994     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
12995 
12996     virBufferAsprintf(&buf, _("event 'rtc-change' for domain '%s': %lld\n"),
12997                       virDomainGetName(dom),
12998                       utcoffset);
12999     virshEventPrint(opaque, &buf);
13000 }
13001 
13002 static void
virshEventWatchdogPrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,int action,void * opaque)13003 virshEventWatchdogPrint(virConnectPtr conn G_GNUC_UNUSED,
13004                         virDomainPtr dom,
13005                         int action,
13006                         void *opaque)
13007 {
13008     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
13009 
13010     virBufferAsprintf(&buf, _("event 'watchdog' for domain '%s': %s\n"),
13011                       virDomainGetName(dom),
13012                       virshDomainEventWatchdogToString(action));
13013     virshEventPrint(opaque, &buf);
13014 }
13015 
13016 static void
virshEventIOErrorPrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,const char * srcPath,const char * devAlias,int action,void * opaque)13017 virshEventIOErrorPrint(virConnectPtr conn G_GNUC_UNUSED,
13018                        virDomainPtr dom,
13019                        const char *srcPath,
13020                        const char *devAlias,
13021                        int action,
13022                        void *opaque)
13023 {
13024     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
13025 
13026     virBufferAsprintf(&buf, _("event 'io-error' for domain '%s': %s (%s) %s\n"),
13027                       virDomainGetName(dom),
13028                       srcPath,
13029                       devAlias,
13030                       virshDomainEventIOErrorToString(action));
13031     virshEventPrint(opaque, &buf);
13032 }
13033 
13034 static void
virshEventGraphicsPrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,int phase,const virDomainEventGraphicsAddress * local,const virDomainEventGraphicsAddress * remote,const char * authScheme,const virDomainEventGraphicsSubject * subject,void * opaque)13035 virshEventGraphicsPrint(virConnectPtr conn G_GNUC_UNUSED,
13036                         virDomainPtr dom,
13037                         int phase,
13038                         const virDomainEventGraphicsAddress *local,
13039                         const virDomainEventGraphicsAddress *remote,
13040                         const char *authScheme,
13041                         const virDomainEventGraphicsSubject *subject,
13042                         void *opaque)
13043 {
13044     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
13045     size_t i;
13046 
13047     virBufferAsprintf(&buf, _("event 'graphics' for domain '%s': "
13048                               "%s local[%s %s %s] remote[%s %s %s] %s\n"),
13049                       virDomainGetName(dom),
13050                       virshGraphicsPhaseToString(phase),
13051                       virshGraphicsAddressToString(local->family),
13052                       local->node,
13053                       local->service,
13054                       virshGraphicsAddressToString(remote->family),
13055                       remote->node,
13056                       remote->service,
13057                       authScheme);
13058     for (i = 0; i < subject->nidentity; i++) {
13059         virBufferAsprintf(&buf, "\t%s=%s\n",
13060                           subject->identities[i].type,
13061                           subject->identities[i].name);
13062     }
13063     virshEventPrint(opaque, &buf);
13064 }
13065 
13066 static void
virshEventIOErrorReasonPrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,const char * srcPath,const char * devAlias,int action,const char * reason,void * opaque)13067 virshEventIOErrorReasonPrint(virConnectPtr conn G_GNUC_UNUSED,
13068                              virDomainPtr dom,
13069                              const char *srcPath,
13070                              const char *devAlias,
13071                              int action,
13072                              const char *reason,
13073                              void *opaque)
13074 {
13075     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
13076 
13077     virBufferAsprintf(&buf, _("event 'io-error-reason' for domain '%s': "
13078                               "%s (%s) %s due to %s\n"),
13079                       virDomainGetName(dom),
13080                       srcPath,
13081                       devAlias,
13082                       virshDomainEventIOErrorToString(action),
13083                       reason);
13084     virshEventPrint(opaque, &buf);
13085 }
13086 
13087 static void
virshEventBlockJobPrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,const char * disk,int type,int status,void * opaque)13088 virshEventBlockJobPrint(virConnectPtr conn G_GNUC_UNUSED,
13089                         virDomainPtr dom,
13090                         const char *disk,
13091                         int type,
13092                         int status,
13093                         void *opaque)
13094 {
13095     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
13096 
13097     virBufferAsprintf(&buf, _("event '%s' for domain '%s': %s for %s %s\n"),
13098                       ((virshDomEventData *) opaque)->cb->name,
13099                       virDomainGetName(dom),
13100                       virshDomainBlockJobToString(type),
13101                       disk,
13102                       virshDomainBlockJobStatusToString(status));
13103     virshEventPrint(opaque, &buf);
13104 }
13105 
13106 static void
virshEventDiskChangePrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,const char * oldSrc,const char * newSrc,const char * alias,int reason,void * opaque)13107 virshEventDiskChangePrint(virConnectPtr conn G_GNUC_UNUSED,
13108                           virDomainPtr dom,
13109                           const char *oldSrc,
13110                           const char *newSrc,
13111                           const char *alias,
13112                           int reason,
13113                           void *opaque)
13114 {
13115     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
13116 
13117     virBufferAsprintf(&buf, _("event 'disk-change' for domain '%s' disk %s: "
13118                               "%s -> %s: %s\n"),
13119                       virDomainGetName(dom),
13120                       alias,
13121                       NULLSTR(oldSrc),
13122                       NULLSTR(newSrc),
13123                       virshDomainEventDiskChangeToString(reason));
13124     virshEventPrint(opaque, &buf);
13125 }
13126 
13127 static void
virshEventTrayChangePrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,const char * alias,int reason,void * opaque)13128 virshEventTrayChangePrint(virConnectPtr conn G_GNUC_UNUSED,
13129                           virDomainPtr dom,
13130                           const char *alias,
13131                           int reason,
13132                           void *opaque)
13133 {
13134     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
13135 
13136     virBufferAsprintf(&buf, _("event 'tray-change' for domain '%s' disk %s: %s\n"),
13137                       virDomainGetName(dom),
13138                       alias,
13139                       virshDomainEventTrayChangeToString(reason));
13140     virshEventPrint(opaque, &buf);
13141 }
13142 
13143 static void
virshEventPMChangePrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,int reason G_GNUC_UNUSED,void * opaque)13144 virshEventPMChangePrint(virConnectPtr conn G_GNUC_UNUSED,
13145                         virDomainPtr dom,
13146                         int reason G_GNUC_UNUSED,
13147                         void *opaque)
13148 {
13149     /* As long as libvirt.h doesn't define any reasons, we might as
13150      * well treat all PM state changes as generic events.  */
13151     virshEventGenericPrint(conn, dom, opaque);
13152 }
13153 
13154 static void
virshEventBalloonChangePrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,unsigned long long actual,void * opaque)13155 virshEventBalloonChangePrint(virConnectPtr conn G_GNUC_UNUSED,
13156                              virDomainPtr dom,
13157                              unsigned long long actual,
13158                              void *opaque)
13159 {
13160     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
13161 
13162     virBufferAsprintf(&buf, _("event 'balloon-change' for domain '%s': %lluKiB\n"),
13163                       virDomainGetName(dom),
13164                       actual);
13165     virshEventPrint(opaque, &buf);
13166 }
13167 
13168 static void
virshEventDeviceRemovedPrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,const char * alias,void * opaque)13169 virshEventDeviceRemovedPrint(virConnectPtr conn G_GNUC_UNUSED,
13170                              virDomainPtr dom,
13171                              const char *alias,
13172                              void *opaque)
13173 {
13174     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
13175 
13176     virBufferAsprintf(&buf, _("event 'device-removed' for domain '%s': %s\n"),
13177                       virDomainGetName(dom),
13178                       alias);
13179     virshEventPrint(opaque, &buf);
13180 }
13181 
13182 static void
virshEventDeviceAddedPrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,const char * alias,void * opaque)13183 virshEventDeviceAddedPrint(virConnectPtr conn G_GNUC_UNUSED,
13184                            virDomainPtr dom,
13185                            const char *alias,
13186                            void *opaque)
13187 {
13188     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
13189 
13190     virBufferAsprintf(&buf, _("event 'device-added' for domain '%s': %s\n"),
13191                       virDomainGetName(dom),
13192                       alias);
13193     virshEventPrint(opaque, &buf);
13194 }
13195 
13196 static void
virshEventTunablePrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,virTypedParameterPtr params,int nparams,void * opaque)13197 virshEventTunablePrint(virConnectPtr conn G_GNUC_UNUSED,
13198                        virDomainPtr dom,
13199                        virTypedParameterPtr params,
13200                        int nparams,
13201                        void *opaque)
13202 {
13203     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
13204     size_t i;
13205     char *value;
13206 
13207     virBufferAsprintf(&buf, _("event 'tunable' for domain '%s':\n"),
13208                       virDomainGetName(dom));
13209     for (i = 0; i < nparams; i++) {
13210         value = virTypedParameterToString(&params[i]);
13211         if (value) {
13212             virBufferAsprintf(&buf, "\t%s: %s\n", params[i].field, value);
13213             VIR_FREE(value);
13214         }
13215     }
13216     virshEventPrint(opaque, &buf);
13217 }
13218 
13219 VIR_ENUM_DECL(virshEventAgentLifecycleState);
13220 VIR_ENUM_IMPL(virshEventAgentLifecycleState,
13221               VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_LAST,
13222               N_("unknown"),
13223               N_("connected"),
13224               N_("disconnected"));
13225 
13226 VIR_ENUM_DECL(virshEventAgentLifecycleReason);
13227 VIR_ENUM_IMPL(virshEventAgentLifecycleReason,
13228               VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_LAST,
13229               N_("unknown"),
13230               N_("domain started"),
13231               N_("channel event"));
13232 
13233 #define UNKNOWNSTR(str) (str ? str : N_("unsupported value"))
13234 static void
virshEventAgentLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,int state,int reason,void * opaque)13235 virshEventAgentLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED,
13236                               virDomainPtr dom,
13237                               int state,
13238                               int reason,
13239                               void *opaque)
13240 {
13241     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
13242 
13243     virBufferAsprintf(&buf, _("event 'agent-lifecycle' for domain '%s': state: "
13244                               "'%s' reason: '%s'\n"),
13245                       virDomainGetName(dom),
13246                       UNKNOWNSTR(virshEventAgentLifecycleStateTypeToString(state)),
13247                       UNKNOWNSTR(virshEventAgentLifecycleReasonTypeToString(reason)));
13248     virshEventPrint(opaque, &buf);
13249 }
13250 
13251 static void
virshEventMigrationIterationPrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,int iteration,void * opaque)13252 virshEventMigrationIterationPrint(virConnectPtr conn G_GNUC_UNUSED,
13253                                   virDomainPtr dom,
13254                                   int iteration,
13255                                   void *opaque)
13256 {
13257     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
13258 
13259     virBufferAsprintf(&buf, _("event 'migration-iteration' for domain '%s': "
13260                               "iteration: '%d'\n"),
13261                       virDomainGetName(dom),
13262                       iteration);
13263 
13264     virshEventPrint(opaque, &buf);
13265 }
13266 
13267 static void
virshEventJobCompletedPrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,virTypedParameterPtr params,int nparams,void * opaque)13268 virshEventJobCompletedPrint(virConnectPtr conn G_GNUC_UNUSED,
13269                             virDomainPtr dom,
13270                             virTypedParameterPtr params,
13271                             int nparams,
13272                             void *opaque)
13273 {
13274     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
13275     size_t i;
13276 
13277     virBufferAsprintf(&buf, _("event 'job-completed' for domain '%s':\n"),
13278                       virDomainGetName(dom));
13279     for (i = 0; i < nparams; i++) {
13280         g_autofree char *value = virTypedParameterToString(&params[i]);
13281         if (value)
13282             virBufferAsprintf(&buf, "\t%s: %s\n", params[i].field, value);
13283     }
13284     virshEventPrint(opaque, &buf);
13285 }
13286 
13287 
13288 static void
virshEventDeviceRemovalFailedPrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,const char * alias,void * opaque)13289 virshEventDeviceRemovalFailedPrint(virConnectPtr conn G_GNUC_UNUSED,
13290                                    virDomainPtr dom,
13291                                    const char *alias,
13292                                    void *opaque)
13293 {
13294     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
13295 
13296     virBufferAsprintf(&buf, _("event 'device-removal-failed' for domain '%s': %s\n"),
13297                       virDomainGetName(dom),
13298                       alias);
13299     virshEventPrint(opaque, &buf);
13300 }
13301 
13302 VIR_ENUM_DECL(virshEventMetadataChangeType);
13303 VIR_ENUM_IMPL(virshEventMetadataChangeType,
13304               VIR_DOMAIN_METADATA_LAST,
13305               N_("description"),
13306               N_("title"),
13307               N_("element"));
13308 
13309 static void
virshEventMetadataChangePrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,int type,const char * nsuri,void * opaque)13310 virshEventMetadataChangePrint(virConnectPtr conn G_GNUC_UNUSED,
13311                               virDomainPtr dom,
13312                               int type,
13313                               const char *nsuri,
13314                               void *opaque)
13315 {
13316     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
13317 
13318     virBufferAsprintf(&buf, _("event 'metadata-change' for domain '%s': type %s, uri %s\n"),
13319                       virDomainGetName(dom),
13320                       UNKNOWNSTR(virshEventMetadataChangeTypeTypeToString(type)),
13321                       NULLSTR(nsuri));
13322     virshEventPrint(opaque, &buf);
13323 }
13324 
13325 
13326 static void
virshEventBlockThresholdPrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,const char * dev,const char * path,unsigned long long threshold,unsigned long long excess,void * opaque)13327 virshEventBlockThresholdPrint(virConnectPtr conn G_GNUC_UNUSED,
13328                               virDomainPtr dom,
13329                               const char *dev,
13330                               const char *path,
13331                               unsigned long long threshold,
13332                               unsigned long long excess,
13333                               void *opaque)
13334 {
13335     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
13336 
13337     virBufferAsprintf(&buf, _("event 'block-threshold' for domain '%s': "
13338                               "dev: %s(%s) %llu %llu\n"),
13339                       virDomainGetName(dom),
13340                       dev, NULLSTR(path), threshold, excess);
13341     virshEventPrint(opaque, &buf);
13342 }
13343 
13344 
13345 VIR_ENUM_DECL(virshEventMemoryFailureRecipientType);
13346 VIR_ENUM_IMPL(virshEventMemoryFailureRecipientType,
13347               VIR_DOMAIN_EVENT_MEMORY_FAILURE_RECIPIENT_LAST,
13348               N_("hypervisor"),
13349               N_("guest"));
13350 
13351 VIR_ENUM_DECL(virshEventMemoryFailureActionType);
13352 VIR_ENUM_IMPL(virshEventMemoryFailureActionType,
13353               VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_LAST,
13354               N_("ignore"),
13355               N_("inject"),
13356               N_("fatal"),
13357               N_("reset"));
13358 
13359 static void
virshEventMemoryFailurePrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,int recipient,int action,unsigned int flags,void * opaque)13360 virshEventMemoryFailurePrint(virConnectPtr conn G_GNUC_UNUSED,
13361                              virDomainPtr dom,
13362                              int recipient,
13363                              int action,
13364                              unsigned int flags,
13365                              void *opaque)
13366 {
13367     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
13368 
13369     virBufferAsprintf(&buf, _("event 'memory-failure' for domain '%s':\n"
13370                               "recipient: %s\naction: %s\n"),
13371                       virDomainGetName(dom),
13372                       UNKNOWNSTR(virshEventMemoryFailureRecipientTypeTypeToString(recipient)),
13373                       UNKNOWNSTR(virshEventMemoryFailureActionTypeTypeToString(action)));
13374     virBufferAsprintf(&buf, _("flags:\n"
13375                               "\taction required: %d\n\trecursive: %d\n"),
13376                       !!(flags & VIR_DOMAIN_MEMORY_FAILURE_ACTION_REQUIRED),
13377                       !!(flags & VIR_DOMAIN_MEMORY_FAILURE_RECURSIVE));
13378 
13379     virshEventPrint(opaque, &buf);
13380 }
13381 
13382 
13383 static void
virshEventMemoryDeviceSizeChangePrint(virConnectPtr conn G_GNUC_UNUSED,virDomainPtr dom,const char * alias,unsigned long long size,void * opaque)13384 virshEventMemoryDeviceSizeChangePrint(virConnectPtr conn G_GNUC_UNUSED,
13385                                       virDomainPtr dom,
13386                                       const char *alias,
13387                                       unsigned long long size,
13388                                       void *opaque)
13389 {
13390     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
13391 
13392     virBufferAsprintf(&buf,
13393                       _("event 'memory-device-size-change' for domain '%s':\n"
13394                         "alias: %s\nsize: %llu\n"),
13395                       virDomainGetName(dom), alias, size);
13396 
13397     virshEventPrint(opaque, &buf);
13398 }
13399 
13400 
13401 virshDomainEventCallback virshDomainEventCallbacks[] = {
13402     { "lifecycle",
13403       VIR_DOMAIN_EVENT_CALLBACK(virshEventLifecyclePrint), },
13404     { "reboot", virshEventGenericPrint, },
13405     { "rtc-change",
13406       VIR_DOMAIN_EVENT_CALLBACK(virshEventRTCChangePrint), },
13407     { "watchdog",
13408       VIR_DOMAIN_EVENT_CALLBACK(virshEventWatchdogPrint), },
13409     { "io-error",
13410       VIR_DOMAIN_EVENT_CALLBACK(virshEventIOErrorPrint), },
13411     { "graphics",
13412       VIR_DOMAIN_EVENT_CALLBACK(virshEventGraphicsPrint), },
13413     { "io-error-reason",
13414       VIR_DOMAIN_EVENT_CALLBACK(virshEventIOErrorReasonPrint), },
13415     { "control-error", virshEventGenericPrint, },
13416     { "block-job",
13417       VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockJobPrint), },
13418     { "disk-change",
13419       VIR_DOMAIN_EVENT_CALLBACK(virshEventDiskChangePrint), },
13420     { "tray-change",
13421       VIR_DOMAIN_EVENT_CALLBACK(virshEventTrayChangePrint), },
13422     { "pm-wakeup",
13423       VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), },
13424     { "pm-suspend",
13425       VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), },
13426     { "balloon-change",
13427       VIR_DOMAIN_EVENT_CALLBACK(virshEventBalloonChangePrint), },
13428     { "pm-suspend-disk",
13429       VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), },
13430     { "device-removed",
13431       VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovedPrint), },
13432     { "block-job-2",
13433       VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockJobPrint), },
13434     { "tunable",
13435       VIR_DOMAIN_EVENT_CALLBACK(virshEventTunablePrint), },
13436     { "agent-lifecycle",
13437       VIR_DOMAIN_EVENT_CALLBACK(virshEventAgentLifecyclePrint), },
13438     { "device-added",
13439       VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceAddedPrint), },
13440     { "migration-iteration",
13441       VIR_DOMAIN_EVENT_CALLBACK(virshEventMigrationIterationPrint), },
13442     { "job-completed",
13443       VIR_DOMAIN_EVENT_CALLBACK(virshEventJobCompletedPrint), },
13444     { "device-removal-failed",
13445       VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovalFailedPrint), },
13446     { "metadata-change",
13447       VIR_DOMAIN_EVENT_CALLBACK(virshEventMetadataChangePrint), },
13448     { "block-threshold",
13449       VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockThresholdPrint), },
13450     { "memory-failure",
13451       VIR_DOMAIN_EVENT_CALLBACK(virshEventMemoryFailurePrint), },
13452     { "memory-device-size-change",
13453       VIR_DOMAIN_EVENT_CALLBACK(virshEventMemoryDeviceSizeChangePrint), },
13454 };
13455 G_STATIC_ASSERT(VIR_DOMAIN_EVENT_ID_LAST == G_N_ELEMENTS(virshDomainEventCallbacks));
13456 
13457 static const vshCmdInfo info_event[] = {
13458     {.name = "help",
13459      .data = N_("Domain Events")
13460     },
13461     {.name = "desc",
13462      .data = N_("List event types, or wait for domain events to occur")
13463     },
13464     {.name = NULL}
13465 };
13466 
13467 static const vshCmdOptDef opts_event[] = {
13468     VIRSH_COMMON_OPT_DOMAIN_OT_STRING(N_("filter by domain name, id or uuid"),
13469                                       0, 0),
13470     {.name = "event",
13471      .type = VSH_OT_STRING,
13472      .completer = virshDomainEventNameCompleter,
13473      .help = N_("which event type to wait for")
13474     },
13475     {.name = "all",
13476      .type = VSH_OT_BOOL,
13477      .help = N_("wait for all events instead of just one type")
13478     },
13479     {.name = "loop",
13480      .type = VSH_OT_BOOL,
13481      .help = N_("loop until timeout or interrupt, rather than one-shot")
13482     },
13483     {.name = "timeout",
13484      .type = VSH_OT_INT,
13485      .help = N_("timeout seconds")
13486     },
13487     {.name = "list",
13488      .type = VSH_OT_BOOL,
13489      .help = N_("list valid event types")
13490     },
13491     {.name = "timestamp",
13492      .type = VSH_OT_BOOL,
13493      .help = N_("show timestamp for each printed event")
13494     },
13495     {.name = NULL}
13496 };
13497 
13498 static bool
cmdEvent(vshControl * ctl,const vshCmd * cmd)13499 cmdEvent(vshControl *ctl, const vshCmd *cmd)
13500 {
13501     g_autoptr(virshDomain) dom = NULL;
13502     bool ret = false;
13503     int timeout = 0;
13504     virshDomEventData *data = NULL;
13505     size_t i;
13506     const char *eventName = NULL;
13507     int event = -1;
13508     bool all = vshCommandOptBool(cmd, "all");
13509     bool loop = vshCommandOptBool(cmd, "loop");
13510     bool timestamp = vshCommandOptBool(cmd, "timestamp");
13511     int count = 0;
13512     virshControl *priv = ctl->privData;
13513 
13514     VSH_EXCLUSIVE_OPTIONS("all", "event");
13515     VSH_EXCLUSIVE_OPTIONS("list", "all");
13516     VSH_EXCLUSIVE_OPTIONS("list", "event");
13517 
13518     if (vshCommandOptBool(cmd, "list")) {
13519         for (event = 0; event < VIR_DOMAIN_EVENT_ID_LAST; event++)
13520             vshPrint(ctl, "%s\n", virshDomainEventCallbacks[event].name);
13521         return true;
13522     }
13523 
13524     if (vshCommandOptStringReq(ctl, cmd, "event", &eventName) < 0)
13525         return false;
13526     if (eventName) {
13527         for (event = 0; event < VIR_DOMAIN_EVENT_ID_LAST; event++)
13528             if (STREQ(eventName, virshDomainEventCallbacks[event].name))
13529                 break;
13530         if (event == VIR_DOMAIN_EVENT_ID_LAST) {
13531             vshError(ctl, _("unknown event type %s"), eventName);
13532             return false;
13533         }
13534     } else if (!all) {
13535         vshError(ctl, "%s",
13536                  _("one of --list, --all, or --event <type> is required"));
13537         return false;
13538     }
13539 
13540     if (all) {
13541         data = g_new0(virshDomEventData, VIR_DOMAIN_EVENT_ID_LAST);
13542         for (i = 0; i < VIR_DOMAIN_EVENT_ID_LAST; i++) {
13543             data[i].ctl = ctl;
13544             data[i].loop = loop;
13545             data[i].count = &count;
13546             data[i].timestamp = timestamp;
13547             data[i].cb = &virshDomainEventCallbacks[i];
13548             data[i].id = -1;
13549         }
13550     } else {
13551         data = g_new0(virshDomEventData, 1);
13552         data[0].ctl = ctl;
13553         data[0].loop = vshCommandOptBool(cmd, "loop");
13554         data[0].count = &count;
13555         data[0].timestamp = timestamp;
13556         data[0].cb = &virshDomainEventCallbacks[event];
13557         data[0].id = -1;
13558     }
13559     if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
13560         goto cleanup;
13561 
13562     if (vshCommandOptBool(cmd, "domain"))
13563         if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
13564             goto cleanup;
13565 
13566     if (vshEventStart(ctl, timeout) < 0)
13567         goto cleanup;
13568 
13569     for (i = 0; i < (all ? VIR_DOMAIN_EVENT_ID_LAST : 1); i++) {
13570         if ((data[i].id = virConnectDomainEventRegisterAny(priv->conn, dom,
13571                                                            all ? i : event,
13572                                                            data[i].cb->cb,
13573                                                            &data[i],
13574                                                            NULL)) < 0) {
13575             /* When registering for all events: if the first
13576              * registration succeeds, silently ignore failures on all
13577              * later registrations on the assumption that the server
13578              * is older and didn't know quite as many events.  */
13579             if (i)
13580                 vshResetLibvirtError();
13581             else
13582                 goto cleanup;
13583         }
13584     }
13585     switch (vshEventWait(ctl)) {
13586     case VSH_EVENT_INTERRUPT:
13587         vshPrint(ctl, "%s", _("event loop interrupted\n"));
13588         break;
13589     case VSH_EVENT_TIMEOUT:
13590         vshPrint(ctl, "%s", _("event loop timed out\n"));
13591         break;
13592     case VSH_EVENT_DONE:
13593         break;
13594     default:
13595         goto cleanup;
13596     }
13597     vshPrint(ctl, _("events received: %d\n"), count);
13598     if (count)
13599         ret = true;
13600 
13601  cleanup:
13602     vshEventCleanup(ctl);
13603     if (data) {
13604         for (i = 0; i < (all ? VIR_DOMAIN_EVENT_ID_LAST : 1); i++) {
13605             if (data[i].id >= 0 &&
13606                 virConnectDomainEventDeregisterAny(priv->conn, data[i].id) < 0)
13607                 ret = false;
13608         }
13609         VIR_FREE(data);
13610     }
13611     return ret;
13612 }
13613 
13614 
13615 /*
13616  * "change-media" command
13617  */
13618 static const vshCmdInfo info_change_media[] = {
13619     {.name = "help",
13620      .data = N_("Change media of CD or floppy drive")
13621     },
13622     {.name = "desc",
13623      .data = N_("Change media of CD or floppy drive.")
13624     },
13625     {.name = NULL}
13626 };
13627 
13628 static const vshCmdOptDef opts_change_media[] = {
13629     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
13630     {.name = "path",
13631      .type = VSH_OT_DATA,
13632      .flags = VSH_OFLAG_REQ,
13633      .completer = virshDomainDiskTargetCompleter,
13634      .help = N_("Fully-qualified path or target of disk device")
13635     },
13636     {.name = "source",
13637      .type = VSH_OT_STRING,
13638      .help = N_("source of the media")
13639     },
13640     {.name = "eject",
13641      .type = VSH_OT_BOOL,
13642      .help = N_("Eject the media")
13643     },
13644     {.name = "insert",
13645      .type = VSH_OT_BOOL,
13646      .help = N_("Insert the media")
13647     },
13648     {.name = "update",
13649      .type = VSH_OT_BOOL,
13650      .help = N_("Update the media")
13651     },
13652     VIRSH_COMMON_OPT_CURRENT(N_("can be either or both of --live and "
13653                                 "--config, depends on implementation "
13654                                 "hypervisor driver")),
13655     VIRSH_COMMON_OPT_LIVE(N_("alter live configuration of running domain")),
13656     VIRSH_COMMON_OPT_CONFIG(N_("alter persistent configuration, effect "
13657                                "observed on next boot")),
13658     {.name = "force",
13659      .type = VSH_OT_BOOL,
13660      .help = N_("force media changing")
13661     },
13662     {.name = "print-xml",
13663      .type = VSH_OT_BOOL,
13664      .help = N_("print XML document rather than change media")
13665     },
13666     {.name = "block",
13667      .type = VSH_OT_BOOL,
13668      .help = N_("source media is a block device")
13669     },
13670     {.name = NULL}
13671 };
13672 
13673 static bool
cmdChangeMedia(vshControl * ctl,const vshCmd * cmd)13674 cmdChangeMedia(vshControl *ctl, const vshCmd *cmd)
13675 {
13676     g_autoptr(virshDomain) dom = NULL;
13677     const char *source = NULL;
13678     const char *path = NULL;
13679     g_autofree char *doc = NULL;
13680     xmlNodePtr disk_node = NULL;
13681     g_autofree char *disk_xml = NULL;
13682     bool ret = false;
13683     virshUpdateDiskXMLType update_type;
13684     const char *action = NULL;
13685     const char *success_msg = NULL;
13686     bool config = vshCommandOptBool(cmd, "config");
13687     bool live = vshCommandOptBool(cmd, "live");
13688     bool current = vshCommandOptBool(cmd, "current");
13689     bool force = vshCommandOptBool(cmd, "force");
13690     bool eject = vshCommandOptBool(cmd, "eject");
13691     bool insert = vshCommandOptBool(cmd, "insert");
13692     bool update = vshCommandOptBool(cmd, "update");
13693     bool block = vshCommandOptBool(cmd, "block");
13694     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
13695 
13696     VSH_EXCLUSIVE_OPTIONS_VAR(eject, insert);
13697     VSH_EXCLUSIVE_OPTIONS_VAR(eject, update);
13698     VSH_EXCLUSIVE_OPTIONS_VAR(insert, update);
13699 
13700     VSH_EXCLUSIVE_OPTIONS_VAR(eject, block);
13701 
13702     if (vshCommandOptStringReq(ctl, cmd, "source", &source) < 0)
13703         return false;
13704 
13705     /* Docs state that update without source is eject */
13706     if (update && !source) {
13707         update = false;
13708         eject = true;
13709     }
13710 
13711     if (eject) {
13712         update_type = VIRSH_UPDATE_DISK_XML_EJECT;
13713         action = "eject";
13714         success_msg = _("Successfully ejected media.");
13715     }
13716 
13717     if (insert) {
13718         update_type = VIRSH_UPDATE_DISK_XML_INSERT;
13719         action = "insert";
13720         success_msg = _("Successfully inserted media.");
13721     }
13722 
13723     if (update || (!eject && !insert)) {
13724         update_type = VIRSH_UPDATE_DISK_XML_UPDATE;
13725         action = "update";
13726         success_msg = _("Successfully updated media.");
13727     }
13728 
13729     VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
13730     VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
13731 
13732     if (config)
13733         flags |= VIR_DOMAIN_AFFECT_CONFIG;
13734     if (live)
13735         flags |= VIR_DOMAIN_AFFECT_LIVE;
13736     if (force)
13737         flags |= VIR_DOMAIN_DEVICE_MODIFY_FORCE;
13738 
13739     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
13740         return false;
13741 
13742     if (vshCommandOptStringReq(ctl, cmd, "path", &path) < 0)
13743         goto cleanup;
13744 
13745     if (flags & VIR_DOMAIN_AFFECT_CONFIG)
13746         doc = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
13747     else
13748         doc = virDomainGetXMLDesc(dom, 0);
13749     if (!doc)
13750         goto cleanup;
13751 
13752     if (!(disk_node = virshFindDisk(doc, path, VIRSH_FIND_DISK_CHANGEABLE)))
13753         goto cleanup;
13754 
13755     if (!(disk_xml = virshUpdateDiskXML(disk_node, source, block, path,
13756                                         update_type)))
13757         goto cleanup;
13758 
13759     if (vshCommandOptBool(cmd, "print-xml")) {
13760         vshPrint(ctl, "%s", disk_xml);
13761     } else {
13762         if (virDomainUpdateDeviceFlags(dom, disk_xml, flags) != 0) {
13763             vshError(ctl, _("Failed to complete action %s on media"), action);
13764             goto cleanup;
13765         }
13766 
13767         vshPrint(ctl, "%s", success_msg);
13768     }
13769 
13770     ret = true;
13771 
13772  cleanup:
13773     xmlFreeNode(disk_node);
13774     return ret;
13775 }
13776 
13777 static const vshCmdInfo info_domfstrim[] = {
13778     {.name = "help",
13779      .data = N_("Invoke fstrim on domain's mounted filesystems.")
13780     },
13781     {.name = "desc",
13782      .data = N_("Invoke fstrim on domain's mounted filesystems.")
13783     },
13784     {.name = NULL}
13785 };
13786 
13787 static const vshCmdOptDef opts_domfstrim[] = {
13788     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
13789     {.name = "minimum",
13790      .type = VSH_OT_INT,
13791      .help = N_("Just a hint to ignore contiguous "
13792                 "free ranges smaller than this (Bytes)")
13793     },
13794     {.name = "mountpoint",
13795      .type = VSH_OT_STRING,
13796      .completer = virshDomainFSMountpointsCompleter,
13797      .help = N_("which mount point to trim")
13798     },
13799     {.name = NULL}
13800 };
13801 static bool
cmdDomFSTrim(vshControl * ctl,const vshCmd * cmd)13802 cmdDomFSTrim(vshControl *ctl, const vshCmd *cmd)
13803 {
13804     g_autoptr(virshDomain) dom = NULL;
13805     unsigned long long minimum = 0;
13806     const char *mountPoint = NULL;
13807     unsigned int flags = 0;
13808 
13809     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
13810         return false;
13811 
13812     if (vshCommandOptULongLong(ctl, cmd, "minimum", &minimum) < 0)
13813         return false;
13814 
13815     if (vshCommandOptStringReq(ctl, cmd, "mountpoint", &mountPoint) < 0)
13816         return false;
13817 
13818     if (virDomainFSTrim(dom, mountPoint, minimum, flags) < 0) {
13819         vshError(ctl, _("Unable to invoke fstrim"));
13820         return false;
13821     }
13822 
13823     return true;
13824 }
13825 
13826 static const vshCmdInfo info_domfsfreeze[] = {
13827     {.name = "help",
13828      .data = N_("Freeze domain's mounted filesystems.")
13829     },
13830     {.name = "desc",
13831      .data = N_("Freeze domain's mounted filesystems.")
13832     },
13833     {.name = NULL}
13834 };
13835 
13836 static const vshCmdOptDef opts_domfsfreeze[] = {
13837     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
13838     {.name = "mountpoint",
13839      .type = VSH_OT_ARGV,
13840      .completer = virshDomainFSMountpointsCompleter,
13841      .help = N_("mountpoint path to be frozen")
13842     },
13843     {.name = NULL}
13844 };
13845 static bool
cmdDomFSFreeze(vshControl * ctl,const vshCmd * cmd)13846 cmdDomFSFreeze(vshControl *ctl, const vshCmd *cmd)
13847 {
13848     g_autoptr(virshDomain) dom = NULL;
13849     const vshCmdOpt *opt = NULL;
13850     g_autofree const char **mountpoints = NULL;
13851     size_t nmountpoints = 0;
13852     int count = 0;
13853 
13854     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
13855         return false;
13856 
13857     while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
13858         VIR_EXPAND_N(mountpoints, nmountpoints, 1);
13859         mountpoints[nmountpoints-1] = opt->data;
13860     }
13861 
13862     if ((count = virDomainFSFreeze(dom, mountpoints, nmountpoints, 0)) < 0) {
13863         vshError(ctl, _("Unable to freeze filesystems"));
13864         return false;
13865     }
13866 
13867     vshPrintExtra(ctl, _("Froze %d filesystem(s)\n"), count);
13868     return true;
13869 }
13870 
13871 static const vshCmdInfo info_domfsthaw[] = {
13872     {.name = "help",
13873      .data = N_("Thaw domain's mounted filesystems.")
13874     },
13875     {.name = "desc",
13876      .data = N_("Thaw domain's mounted filesystems.")
13877     },
13878     {.name = NULL}
13879 };
13880 
13881 static const vshCmdOptDef opts_domfsthaw[] = {
13882     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
13883     {.name = "mountpoint",
13884      .type = VSH_OT_ARGV,
13885      .completer = virshDomainFSMountpointsCompleter,
13886      .help = N_("mountpoint path to be thawed")
13887     },
13888     {.name = NULL}
13889 };
13890 static bool
cmdDomFSThaw(vshControl * ctl,const vshCmd * cmd)13891 cmdDomFSThaw(vshControl *ctl, const vshCmd *cmd)
13892 {
13893     g_autoptr(virshDomain) dom = NULL;
13894     const vshCmdOpt *opt = NULL;
13895     g_autofree const char **mountpoints = NULL;
13896     size_t nmountpoints = 0;
13897     int count = 0;
13898 
13899     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
13900         return false;
13901 
13902     while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
13903         VIR_EXPAND_N(mountpoints, nmountpoints, 1);
13904         mountpoints[nmountpoints-1] = opt->data;
13905     }
13906 
13907     if ((count = virDomainFSThaw(dom, mountpoints, nmountpoints, 0)) < 0) {
13908         vshError(ctl, _("Unable to thaw filesystems"));
13909         return false;
13910     }
13911 
13912     vshPrintExtra(ctl, _("Thawed %d filesystem(s)\n"), count);
13913     return true;
13914 }
13915 
13916 static const vshCmdInfo info_domfsinfo[] = {
13917     {.name = "help",
13918      .data = N_("Get information of domain's mounted filesystems.")
13919     },
13920     {.name = "desc",
13921      .data = N_("Get information of domain's mounted filesystems.")
13922     },
13923     {.name = NULL}
13924 };
13925 
13926 static const vshCmdOptDef opts_domfsinfo[] = {
13927     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
13928     {.name = NULL}
13929 };
13930 
13931 static bool
cmdDomFSInfo(vshControl * ctl,const vshCmd * cmd)13932 cmdDomFSInfo(vshControl *ctl, const vshCmd *cmd)
13933 {
13934     g_autoptr(virshDomain) dom = NULL;
13935     int rc = -1;
13936     size_t i, j;
13937     virDomainFSInfoPtr *info = NULL;
13938     g_autoptr(vshTable) table = NULL;
13939     size_t ninfos = 0;
13940     bool ret = false;
13941 
13942     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
13943         return false;
13944 
13945     rc = virDomainGetFSInfo(dom, &info, 0);
13946     if (rc < 0) {
13947         vshError(ctl, _("Unable to get filesystem information"));
13948         goto cleanup;
13949     }
13950     ninfos = rc;
13951 
13952     if (ninfos == 0) {
13953         ret = true;
13954         vshPrintExtra(ctl, _("No filesystems are mounted in the domain"));
13955         goto cleanup;
13956     }
13957 
13958     if (info) {
13959         table = vshTableNew(_("Mountpoint"), _("Name"), _("Type"), _("Target"), NULL);
13960         if (!table)
13961             goto cleanup;
13962 
13963         for (i = 0; i < ninfos; i++) {
13964             g_auto(virBuffer) targetsBuff = VIR_BUFFER_INITIALIZER;
13965             g_autofree char *targets = NULL;
13966 
13967             for (j = 0; j < info[i]->ndevAlias; j++)
13968                 virBufferAsprintf(&targetsBuff, "%s,", info[i]->devAlias[j]);
13969             virBufferTrim(&targetsBuff, ",");
13970 
13971             targets = virBufferContentAndReset(&targetsBuff);
13972 
13973             if (vshTableRowAppend(table,
13974                                   info[i]->mountpoint,
13975                                   info[i]->name,
13976                                   info[i]->fstype,
13977                                   targets ? targets : "",
13978                                   NULL) < 0)
13979                 goto cleanup;
13980         }
13981 
13982         vshTablePrintToStdout(table, ctl);
13983     }
13984 
13985     ret = true;
13986 
13987  cleanup:
13988     if (info) {
13989         for (i = 0; i < ninfos; i++)
13990             virDomainFSInfoFree(info[i]);
13991         VIR_FREE(info);
13992     }
13993     return ret;
13994 }
13995 
13996 /*
13997  * "guest-agent-timeout" command
13998  */
13999 static const vshCmdInfo info_guest_agent_timeout[] = {
14000     {.name = "help",
14001      .data = N_("Set the guest agent timeout")
14002     },
14003     {.name = "desc",
14004      .data = N_("Set the number of seconds to wait for a response from the guest agent.")
14005     },
14006     {.name = NULL}
14007 };
14008 
14009 static const vshCmdOptDef opts_guest_agent_timeout[] = {
14010     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
14011     {.name = "timeout",
14012      .type = VSH_OT_INT,
14013      .flags = VSH_OFLAG_REQ_OPT,
14014      .help = N_("timeout seconds.")
14015     },
14016     {.name = NULL}
14017 };
14018 
14019 static bool
cmdGuestAgentTimeout(vshControl * ctl,const vshCmd * cmd)14020 cmdGuestAgentTimeout(vshControl *ctl, const vshCmd *cmd)
14021 {
14022     g_autoptr(virshDomain) dom = NULL;
14023     int timeout = VIR_DOMAIN_AGENT_RESPONSE_TIMEOUT_BLOCK;
14024     const unsigned int flags = 0;
14025 
14026     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
14027         return false;
14028 
14029     if (vshCommandOptInt(ctl, cmd, "timeout", &timeout) < 0)
14030         return false;
14031 
14032     if (virDomainAgentSetResponseTimeout(dom, timeout, flags) < 0)
14033         return false;
14034 
14035     return true;
14036 }
14037 
14038 /*
14039  * "guestinfo" command
14040  */
14041 static const vshCmdInfo info_guestinfo[] = {
14042     {.name = "help",
14043      .data = N_("query information about the guest (via agent)")
14044     },
14045     {.name = "desc",
14046      .data = N_("Use the guest agent to query various information from guest's "
14047                 "point of view")
14048     },
14049     {.name = NULL}
14050 };
14051 
14052 static const vshCmdOptDef opts_guestinfo[] = {
14053     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
14054     {.name = "user",
14055      .type = VSH_OT_BOOL,
14056      .help = N_("report active users"),
14057     },
14058     {.name = "os",
14059      .type = VSH_OT_BOOL,
14060      .help = N_("report operating system information"),
14061     },
14062     {.name = "timezone",
14063      .type = VSH_OT_BOOL,
14064      .help = N_("report timezone information"),
14065     },
14066     {.name = "hostname",
14067      .type = VSH_OT_BOOL,
14068      .help = N_("report hostname"),
14069     },
14070     {.name = "filesystem",
14071      .type = VSH_OT_BOOL,
14072      .help = N_("report filesystem information"),
14073     },
14074     {.name = "disk",
14075      .type = VSH_OT_BOOL,
14076      .help = N_("report disk information"),
14077     },
14078     {.name = "interface",
14079      .type = VSH_OT_BOOL,
14080      .help = N_("report interface information"),
14081     },
14082     {.name = NULL}
14083 };
14084 
14085 static bool
cmdGuestInfo(vshControl * ctl,const vshCmd * cmd)14086 cmdGuestInfo(vshControl *ctl, const vshCmd *cmd)
14087 {
14088     g_autoptr(virshDomain) dom = NULL;
14089     bool ret = false;
14090     virTypedParameterPtr params = NULL;
14091     int nparams = 0;
14092     size_t i;
14093     unsigned int types = 0;
14094 
14095     if (vshCommandOptBool(cmd, "user"))
14096         types |= VIR_DOMAIN_GUEST_INFO_USERS;
14097     if (vshCommandOptBool(cmd, "os"))
14098         types |= VIR_DOMAIN_GUEST_INFO_OS;
14099     if (vshCommandOptBool(cmd, "timezone"))
14100         types |= VIR_DOMAIN_GUEST_INFO_TIMEZONE;
14101     if (vshCommandOptBool(cmd, "hostname"))
14102         types |= VIR_DOMAIN_GUEST_INFO_HOSTNAME;
14103     if (vshCommandOptBool(cmd, "filesystem"))
14104         types |= VIR_DOMAIN_GUEST_INFO_FILESYSTEM;
14105     if (vshCommandOptBool(cmd, "disk"))
14106         types |= VIR_DOMAIN_GUEST_INFO_DISKS;
14107     if (vshCommandOptBool(cmd, "interface"))
14108         types |= VIR_DOMAIN_GUEST_INFO_INTERFACES;
14109 
14110     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
14111         return false;
14112 
14113     if (virDomainGetGuestInfo(dom, types, &params, &nparams, 0) < 0)
14114         goto cleanup;
14115 
14116     for (i = 0; i < nparams; i++) {
14117         g_autofree char *str = vshGetTypedParamValue(ctl, &params[i]);
14118         vshPrint(ctl, "%-20s: %s\n", params[i].field, str);
14119     }
14120 
14121     ret = true;
14122 
14123  cleanup:
14124     virTypedParamsFree(params, nparams);
14125     return ret;
14126 }
14127 
14128 /*
14129  * "get-user-sshkeys" command
14130  */
14131 static const vshCmdInfo info_get_user_sshkeys[] = {
14132     {.name = "help",
14133      .data = N_("list authorized SSH keys for given user (via agent)")
14134     },
14135     {.name = "desc",
14136      .data = N_("Use the guest agent to query authorized SSH keys for given "
14137                 "user")
14138     },
14139     {.name = NULL}
14140 };
14141 
14142 static const vshCmdOptDef opts_get_user_sshkeys[] = {
14143     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
14144     {.name = "user",
14145      .type = VSH_OT_DATA,
14146      .flags = VSH_OFLAG_REQ,
14147      .help = N_("user to list authorized keys for"),
14148     },
14149     {.name = NULL}
14150 };
14151 
14152 static bool
cmdGetUserSSHKeys(vshControl * ctl,const vshCmd * cmd)14153 cmdGetUserSSHKeys(vshControl *ctl, const vshCmd *cmd)
14154 {
14155     g_autoptr(virshDomain) dom = NULL;
14156     const char *user;
14157     g_auto(GStrv) keys = NULL;
14158     int nkeys = 0;
14159     size_t i;
14160     const unsigned int flags = 0;
14161 
14162     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
14163         return false;
14164 
14165     if (vshCommandOptStringReq(ctl, cmd, "user", &user) < 0)
14166         return false;
14167 
14168     nkeys = virDomainAuthorizedSSHKeysGet(dom, user, &keys, flags);
14169     if (nkeys < 0)
14170         return false;
14171 
14172     for (i = 0; i < nkeys; i++) {
14173         vshPrint(ctl, "%s", keys[i]);
14174     }
14175 
14176     return true;
14177 }
14178 
14179 
14180 /*
14181  * "set-user-sshkeys" command
14182  */
14183 static const vshCmdInfo info_set_user_sshkeys[] = {
14184     {.name = "help",
14185      .data = N_("manipulate authorized SSH keys file for given user (via agent)")
14186     },
14187     {.name = "desc",
14188      .data = N_("Append, reset or remove specified key from the authorized "
14189                 "keys file for given user")
14190     },
14191     {.name = NULL}
14192 };
14193 
14194 static const vshCmdOptDef opts_set_user_sshkeys[] = {
14195     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
14196     {.name = "user",
14197      .type = VSH_OT_DATA,
14198      .flags = VSH_OFLAG_REQ,
14199      .help = N_("user to set authorized keys for"),
14200     },
14201     {.name = "file",
14202      .type = VSH_OT_STRING,
14203      .completer = virshCompletePathLocalExisting,
14204      .help = N_("optional file to read keys from"),
14205     },
14206     {.name = "reset",
14207      .type = VSH_OT_BOOL,
14208      .help = N_("clear out authorized keys file before adding new keys"),
14209     },
14210     {.name = "remove",
14211      .type = VSH_OT_BOOL,
14212      .help = N_("remove keys from the authorized keys file"),
14213     },
14214     {.name = NULL}
14215 };
14216 
14217 static bool
cmdSetUserSSHKeys(vshControl * ctl,const vshCmd * cmd)14218 cmdSetUserSSHKeys(vshControl *ctl, const vshCmd *cmd)
14219 {
14220     g_autoptr(virshDomain) dom = NULL;
14221     const char *user;
14222     const char *from;
14223     g_autofree char *buffer = NULL;
14224     g_auto(GStrv) keys = NULL;
14225     int nkeys = 0;
14226     unsigned int flags = 0;
14227 
14228     VSH_REQUIRE_OPTION("remove", "file");
14229     VSH_EXCLUSIVE_OPTIONS("reset", "remove");
14230 
14231     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
14232         return false;
14233 
14234     if (vshCommandOptStringReq(ctl, cmd, "user", &user) < 0)
14235         return false;
14236 
14237     if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
14238         return false;
14239 
14240     if (vshCommandOptBool(cmd, "remove")) {
14241         flags |= VIR_DOMAIN_AUTHORIZED_SSH_KEYS_SET_REMOVE;
14242     } else {
14243         if (!vshCommandOptBool(cmd, "reset")) {
14244             flags |= VIR_DOMAIN_AUTHORIZED_SSH_KEYS_SET_APPEND;
14245 
14246             if (!from) {
14247                 vshError(ctl, _("Option --file is required"));
14248                 return false;
14249             }
14250         }
14251     }
14252 
14253     if (from) {
14254         if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) {
14255             vshSaveLibvirtError();
14256             return false;
14257         }
14258 
14259         if (!(keys = g_strsplit(buffer, "\n", -1)))
14260             return false;
14261 
14262         nkeys = g_strv_length(keys);
14263         if (nkeys == 0) {
14264             vshError(ctl, _("File %s contains no keys"), from);
14265             return false;
14266         }
14267     }
14268 
14269     if (virDomainAuthorizedSSHKeysSet(dom, user,
14270                                       (const char **) keys, nkeys, flags) < 0) {
14271         return false;
14272     }
14273 
14274     return true;
14275 }
14276 
14277 
14278 /*
14279  * "domdirtyrate" command
14280  */
14281 static const vshCmdInfo info_domdirtyrate_calc[] = {
14282     {.name = "help",
14283      .data = N_("Calculate a vm's memory dirty rate")
14284     },
14285     {.name = "desc",
14286      .data = N_("Calculate memory dirty rate of a domain in order to "
14287                 "decide whether it's proper to be migrated out or not.\n"
14288                 "The calculated dirty rate information is available by "
14289                 "calling 'domstats --dirtyrate'.")
14290     },
14291     {.name = NULL}
14292 };
14293 
14294 static const vshCmdOptDef opts_domdirtyrate_calc[] = {
14295     VIRSH_COMMON_OPT_DOMAIN_FULL(0),
14296     {.name = "seconds",
14297      .type = VSH_OT_INT,
14298      .help = N_("calculate memory dirty rate within specified seconds, "
14299                 "the supported value range from 1 to 60, default to 1.")
14300     },
14301     {.name = NULL}
14302 };
14303 
14304 static bool
cmdDomDirtyRateCalc(vshControl * ctl,const vshCmd * cmd)14305 cmdDomDirtyRateCalc(vshControl *ctl, const vshCmd *cmd)
14306 {
14307     g_autoptr(virshDomain) dom = NULL;
14308     int seconds = 1; /* the default value is 1 */
14309 
14310     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
14311         return false;
14312 
14313     if (vshCommandOptInt(ctl, cmd, "seconds", &seconds) < 0)
14314         return false;
14315 
14316     if (virDomainStartDirtyRateCalc(dom, seconds, 0) < 0)
14317         return false;
14318 
14319     vshPrintExtra(ctl, _("Start to calculate domain's memory "
14320                          "dirty rate successfully.\n"));
14321 
14322     return true;
14323 }
14324 
14325 
14326 const vshCmdDef domManagementCmds[] = {
14327     {.name = "attach-device",
14328      .handler = cmdAttachDevice,
14329      .opts = opts_attach_device,
14330      .info = info_attach_device,
14331      .flags = 0
14332     },
14333     {.name = "attach-disk",
14334      .handler = cmdAttachDisk,
14335      .opts = opts_attach_disk,
14336      .info = info_attach_disk,
14337      .flags = 0
14338     },
14339     {.name = "attach-interface",
14340      .handler = cmdAttachInterface,
14341      .opts = opts_attach_interface,
14342      .info = info_attach_interface,
14343      .flags = 0
14344     },
14345     {.name = "autostart",
14346      .handler = cmdAutostart,
14347      .opts = opts_autostart,
14348      .info = info_autostart,
14349      .flags = 0
14350     },
14351     {.name = "blkdeviotune",
14352      .handler = cmdBlkdeviotune,
14353      .opts = opts_blkdeviotune,
14354      .info = info_blkdeviotune,
14355      .flags = 0
14356     },
14357     {.name = "blkiotune",
14358      .handler = cmdBlkiotune,
14359      .opts = opts_blkiotune,
14360      .info = info_blkiotune,
14361      .flags = 0
14362     },
14363     {.name = "blockcommit",
14364      .handler = cmdBlockcommit,
14365      .opts = opts_blockcommit,
14366      .info = info_blockcommit,
14367      .flags = 0
14368     },
14369     {.name = "blockcopy",
14370      .handler = cmdBlockcopy,
14371      .opts = opts_blockcopy,
14372      .info = info_blockcopy,
14373      .flags = 0
14374     },
14375     {.name = "blockjob",
14376      .handler = cmdBlockjob,
14377      .opts = opts_blockjob,
14378      .info = info_blockjob,
14379      .flags = 0
14380     },
14381     {.name = "blockpull",
14382      .handler = cmdBlockpull,
14383      .opts = opts_blockpull,
14384      .info = info_blockpull,
14385      .flags = 0
14386     },
14387     {.name = "blockresize",
14388      .handler = cmdBlockresize,
14389      .opts = opts_blockresize,
14390      .info = info_blockresize,
14391      .flags = 0
14392     },
14393     {.name = "change-media",
14394      .handler = cmdChangeMedia,
14395      .opts = opts_change_media,
14396      .info = info_change_media,
14397      .flags = 0
14398     },
14399 #ifndef WIN32
14400     {.name = "console",
14401      .handler = cmdConsole,
14402      .opts = opts_console,
14403      .info = info_console,
14404      .flags = 0
14405     },
14406 #endif
14407     {.name = "cpu-stats",
14408      .handler = cmdCPUStats,
14409      .opts = opts_cpu_stats,
14410      .info = info_cpu_stats,
14411      .flags = 0
14412     },
14413     {.name = "create",
14414      .handler = cmdCreate,
14415      .opts = opts_create,
14416      .info = info_create,
14417      .flags = 0
14418     },
14419     {.name = "define",
14420      .handler = cmdDefine,
14421      .opts = opts_define,
14422      .info = info_define,
14423      .flags = 0
14424     },
14425     {.name = "desc",
14426      .handler = cmdDesc,
14427      .opts = opts_desc,
14428      .info = info_desc,
14429      .flags = 0
14430     },
14431     {.name = "destroy",
14432      .handler = cmdDestroy,
14433      .opts = opts_destroy,
14434      .info = info_destroy,
14435      .flags = 0
14436     },
14437     {.name = "detach-device",
14438      .handler = cmdDetachDevice,
14439      .opts = opts_detach_device,
14440      .info = info_detach_device,
14441      .flags = 0
14442     },
14443     {.name = "detach-device-alias",
14444      .handler = cmdDetachDeviceAlias,
14445      .opts = opts_detach_device_alias,
14446      .info = info_detach_device_alias,
14447      .flags = 0
14448     },
14449     {.name = "detach-disk",
14450      .handler = cmdDetachDisk,
14451      .opts = opts_detach_disk,
14452      .info = info_detach_disk,
14453      .flags = 0
14454     },
14455     {.name = "detach-interface",
14456      .handler = cmdDetachInterface,
14457      .opts = opts_detach_interface,
14458      .info = info_detach_interface,
14459      .flags = 0
14460     },
14461     {.name = "domdisplay",
14462      .handler = cmdDomDisplay,
14463      .opts = opts_domdisplay,
14464      .info = info_domdisplay,
14465      .flags = 0
14466     },
14467     {.name = "domfsfreeze",
14468      .handler = cmdDomFSFreeze,
14469      .opts = opts_domfsfreeze,
14470      .info = info_domfsfreeze,
14471      .flags = 0
14472     },
14473     {.name = "domfsthaw",
14474      .handler = cmdDomFSThaw,
14475      .opts = opts_domfsthaw,
14476      .info = info_domfsthaw,
14477      .flags = 0
14478     },
14479     {.name = "domfsinfo",
14480      .handler = cmdDomFSInfo,
14481      .opts = opts_domfsinfo,
14482      .info = info_domfsinfo,
14483      .flags = 0
14484     },
14485     {.name = "domfstrim",
14486      .handler = cmdDomFSTrim,
14487      .opts = opts_domfstrim,
14488      .info = info_domfstrim,
14489      .flags = 0
14490     },
14491     {.name = "domhostname",
14492      .handler = cmdDomHostname,
14493      .opts = opts_domhostname,
14494      .info = info_domhostname,
14495      .flags = 0
14496     },
14497     {.name = "domid",
14498      .handler = cmdDomid,
14499      .opts = opts_domid,
14500      .info = info_domid,
14501      .flags = 0
14502     },
14503     {.name = "domif-setlink",
14504      .handler = cmdDomIfSetLink,
14505      .opts = opts_domif_setlink,
14506      .info = info_domif_setlink,
14507      .flags = 0
14508     },
14509     {.name = "domiftune",
14510      .handler = cmdDomIftune,
14511      .opts = opts_domiftune,
14512      .info = info_domiftune,
14513      .flags = 0
14514     },
14515     {.name = "domjobabort",
14516      .handler = cmdDomjobabort,
14517      .opts = opts_domjobabort,
14518      .info = info_domjobabort,
14519      .flags = 0
14520     },
14521     {.name = "domjobinfo",
14522      .handler = cmdDomjobinfo,
14523      .opts = opts_domjobinfo,
14524      .info = info_domjobinfo,
14525      .flags = 0
14526     },
14527     {.name = "domname",
14528      .handler = cmdDomname,
14529      .opts = opts_domname,
14530      .info = info_domname,
14531      .flags = 0
14532     },
14533     {.name = "domrename",
14534      .handler = cmdDomrename,
14535      .opts = opts_domrename,
14536      .info = info_domrename,
14537      .flags = 0
14538     },
14539     {.name = "dompmsuspend",
14540      .handler = cmdDomPMSuspend,
14541      .opts = opts_dom_pm_suspend,
14542      .info = info_dom_pm_suspend,
14543      .flags = 0
14544     },
14545     {.name = "dompmwakeup",
14546      .handler = cmdDomPMWakeup,
14547      .opts = opts_dom_pm_wakeup,
14548      .info = info_dom_pm_wakeup,
14549      .flags = 0
14550     },
14551     {.name = "domuuid",
14552      .handler = cmdDomuuid,
14553      .opts = opts_domuuid,
14554      .info = info_domuuid,
14555      .flags = 0
14556     },
14557     {.name = "domxml-from-native",
14558      .handler = cmdDomXMLFromNative,
14559      .opts = opts_domxmlfromnative,
14560      .info = info_domxmlfromnative,
14561      .flags = 0
14562     },
14563     {.name = "domxml-to-native",
14564      .handler = cmdDomXMLToNative,
14565      .opts = opts_domxmltonative,
14566      .info = info_domxmltonative,
14567      .flags = 0
14568     },
14569     {.name = "dump",
14570      .handler = cmdDump,
14571      .opts = opts_dump,
14572      .info = info_dump,
14573      .flags = 0
14574     },
14575     {.name = "dumpxml",
14576      .handler = cmdDumpXML,
14577      .opts = opts_dumpxml,
14578      .info = info_dumpxml,
14579      .flags = 0
14580     },
14581     {.name = "edit",
14582      .handler = cmdEdit,
14583      .opts = opts_edit,
14584      .info = info_edit,
14585      .flags = 0
14586     },
14587     {.name = "event",
14588      .handler = cmdEvent,
14589      .opts = opts_event,
14590      .info = info_event,
14591      .flags = 0
14592     },
14593     {.name = "get-user-sshkeys",
14594      .handler = cmdGetUserSSHKeys,
14595      .opts = opts_get_user_sshkeys,
14596      .info = info_get_user_sshkeys,
14597      .flags = 0
14598     },
14599     {.name = "inject-nmi",
14600      .handler = cmdInjectNMI,
14601      .opts = opts_inject_nmi,
14602      .info = info_inject_nmi,
14603      .flags = 0
14604     },
14605     {.name = "iothreadinfo",
14606      .handler = cmdIOThreadInfo,
14607      .opts = opts_iothreadinfo,
14608      .info = info_iothreadinfo,
14609      .flags = 0
14610     },
14611     {.name = "iothreadpin",
14612      .handler = cmdIOThreadPin,
14613      .opts = opts_iothreadpin,
14614      .info = info_iothreadpin,
14615      .flags = 0
14616     },
14617     {.name = "iothreadadd",
14618      .handler = cmdIOThreadAdd,
14619      .opts = opts_iothreadadd,
14620      .info = info_iothreadadd,
14621      .flags = 0
14622     },
14623     {.name = "iothreadset",
14624      .handler = cmdIOThreadSet,
14625      .opts = opts_iothreadset,
14626      .info = info_iothreadset,
14627      .flags = 0
14628     },
14629     {.name = "iothreaddel",
14630      .handler = cmdIOThreadDel,
14631      .opts = opts_iothreaddel,
14632      .info = info_iothreaddel,
14633      .flags = 0
14634     },
14635     {.name = "send-key",
14636      .handler = cmdSendKey,
14637      .opts = opts_send_key,
14638      .info = info_send_key,
14639      .flags = 0
14640     },
14641     {.name = "send-process-signal",
14642      .handler = cmdSendProcessSignal,
14643      .opts = opts_send_process_signal,
14644      .info = info_send_process_signal,
14645      .flags = 0
14646     },
14647     {.name = "lxc-enter-namespace",
14648      .handler = cmdLxcEnterNamespace,
14649      .opts = opts_lxc_enter_namespace,
14650      .info = info_lxc_enter_namespace,
14651      .flags = 0
14652     },
14653     {.name = "managedsave",
14654      .handler = cmdManagedSave,
14655      .opts = opts_managedsave,
14656      .info = info_managedsave,
14657      .flags = 0
14658     },
14659     {.name = "managedsave-remove",
14660      .handler = cmdManagedSaveRemove,
14661      .opts = opts_managedsaveremove,
14662      .info = info_managedsaveremove,
14663      .flags = 0
14664     },
14665     {.name = "managedsave-edit",
14666      .handler = cmdManagedSaveEdit,
14667      .opts = opts_managed_save_edit,
14668      .info = info_managed_save_edit,
14669      .flags = 0
14670     },
14671     {.name = "managedsave-dumpxml",
14672      .handler = cmdManagedSaveDumpxml,
14673      .opts = opts_managed_save_dumpxml,
14674      .info = info_managed_save_dumpxml,
14675      .flags = 0
14676     },
14677     {.name = "managedsave-define",
14678      .handler = cmdManagedSaveDefine,
14679      .opts = opts_managed_save_define,
14680      .info = info_managed_save_define,
14681      .flags = 0
14682     },
14683     {.name = "memtune",
14684      .handler = cmdMemtune,
14685      .opts = opts_memtune,
14686      .info = info_memtune,
14687      .flags = 0
14688     },
14689     {.name = "perf",
14690      .handler = cmdPerf,
14691      .opts = opts_perf,
14692      .info = info_perf,
14693      .flags = 0
14694     },
14695     {.name = "metadata",
14696      .handler = cmdMetadata,
14697      .opts = opts_metadata,
14698      .info = info_metadata,
14699      .flags = 0
14700     },
14701     {.name = "migrate",
14702      .handler = cmdMigrate,
14703      .opts = opts_migrate,
14704      .info = info_migrate,
14705      .flags = 0
14706     },
14707     {.name = "migrate-setmaxdowntime",
14708      .handler = cmdMigrateSetMaxDowntime,
14709      .opts = opts_migrate_setmaxdowntime,
14710      .info = info_migrate_setmaxdowntime,
14711      .flags = 0
14712     },
14713     {.name = "migrate-getmaxdowntime",
14714      .handler = cmdMigrateGetMaxDowntime,
14715      .opts = opts_migrate_getmaxdowntime,
14716      .info = info_migrate_getmaxdowntime,
14717      .flags = 0
14718     },
14719     {.name = "migrate-compcache",
14720      .handler = cmdMigrateCompCache,
14721      .opts = opts_migrate_compcache,
14722      .info = info_migrate_compcache,
14723      .flags = 0
14724     },
14725     {.name = "migrate-setspeed",
14726      .handler = cmdMigrateSetMaxSpeed,
14727      .opts = opts_migrate_setspeed,
14728      .info = info_migrate_setspeed,
14729      .flags = 0
14730     },
14731     {.name = "migrate-getspeed",
14732      .handler = cmdMigrateGetMaxSpeed,
14733      .opts = opts_migrate_getspeed,
14734      .info = info_migrate_getspeed,
14735      .flags = 0
14736     },
14737     {.name = "migrate-postcopy",
14738      .handler = cmdMigratePostCopy,
14739      .opts = opts_migrate_postcopy,
14740      .info = info_migrate_postcopy,
14741      .flags = 0
14742     },
14743     {.name = "numatune",
14744      .handler = cmdNumatune,
14745      .opts = opts_numatune,
14746      .info = info_numatune,
14747      .flags = 0
14748     },
14749     {.name = "qemu-attach",
14750      .handler = cmdQemuAttach,
14751      .opts = opts_qemu_attach,
14752      .info = info_qemu_attach,
14753      .flags = 0
14754     },
14755     {.name = "qemu-monitor-command",
14756      .handler = cmdQemuMonitorCommand,
14757      .opts = opts_qemu_monitor_command,
14758      .info = info_qemu_monitor_command,
14759      .flags = 0
14760     },
14761     {.name = "qemu-monitor-event",
14762      .handler = cmdQemuMonitorEvent,
14763      .opts = opts_qemu_monitor_event,
14764      .info = info_qemu_monitor_event,
14765      .flags = 0
14766     },
14767     {.name = "qemu-agent-command",
14768      .handler = cmdQemuAgentCommand,
14769      .opts = opts_qemu_agent_command,
14770      .info = info_qemu_agent_command,
14771      .flags = 0
14772     },
14773     {.name = "guest-agent-timeout",
14774      .handler = cmdGuestAgentTimeout,
14775      .opts = opts_guest_agent_timeout,
14776      .info = info_guest_agent_timeout,
14777      .flags = 0
14778     },
14779     {.name = "reboot",
14780      .handler = cmdReboot,
14781      .opts = opts_reboot,
14782      .info = info_reboot,
14783      .flags = 0
14784     },
14785     {.name = "reset",
14786      .handler = cmdReset,
14787      .opts = opts_reset,
14788      .info = info_reset,
14789      .flags = 0
14790     },
14791     {.name = "restore",
14792      .handler = cmdRestore,
14793      .opts = opts_restore,
14794      .info = info_restore,
14795      .flags = 0
14796     },
14797     {.name = "resume",
14798      .handler = cmdResume,
14799      .opts = opts_resume,
14800      .info = info_resume,
14801      .flags = 0
14802     },
14803     {.name = "save",
14804      .handler = cmdSave,
14805      .opts = opts_save,
14806      .info = info_save,
14807      .flags = 0
14808     },
14809     {.name = "save-image-define",
14810      .handler = cmdSaveImageDefine,
14811      .opts = opts_save_image_define,
14812      .info = info_save_image_define,
14813      .flags = 0
14814     },
14815     {.name = "save-image-dumpxml",
14816      .handler = cmdSaveImageDumpxml,
14817      .opts = opts_save_image_dumpxml,
14818      .info = info_save_image_dumpxml,
14819      .flags = 0
14820     },
14821     {.name = "save-image-edit",
14822      .handler = cmdSaveImageEdit,
14823      .opts = opts_save_image_edit,
14824      .info = info_save_image_edit,
14825      .flags = 0
14826     },
14827     {.name = "schedinfo",
14828      .handler = cmdSchedinfo,
14829      .opts = opts_schedinfo,
14830      .info = info_schedinfo,
14831      .flags = 0
14832     },
14833     {.name = "screenshot",
14834      .handler = cmdScreenshot,
14835      .opts = opts_screenshot,
14836      .info = info_screenshot,
14837      .flags = 0
14838     },
14839     {.name = "set-lifecycle-action",
14840      .handler = cmdSetLifecycleAction,
14841      .opts = opts_setLifecycleAction,
14842      .info = info_setLifecycleAction,
14843      .flags = 0
14844     },
14845     {.name = "set-user-sshkeys",
14846      .handler = cmdSetUserSSHKeys,
14847      .opts = opts_set_user_sshkeys,
14848      .info = info_set_user_sshkeys,
14849      .flags = 0
14850     },
14851     {.name = "set-user-password",
14852      .handler = cmdSetUserPassword,
14853      .opts = opts_set_user_password,
14854      .info = info_set_user_password,
14855      .flags = 0
14856     },
14857     {.name = "setmaxmem",
14858      .handler = cmdSetmaxmem,
14859      .opts = opts_setmaxmem,
14860      .info = info_setmaxmem,
14861      .flags = 0
14862     },
14863     {.name = "setmem",
14864      .handler = cmdSetmem,
14865      .opts = opts_setmem,
14866      .info = info_setmem,
14867      .flags = 0
14868     },
14869     {.name = "setvcpus",
14870      .handler = cmdSetvcpus,
14871      .opts = opts_setvcpus,
14872      .info = info_setvcpus,
14873      .flags = 0
14874     },
14875     {.name = "shutdown",
14876      .handler = cmdShutdown,
14877      .opts = opts_shutdown,
14878      .info = info_shutdown,
14879      .flags = 0
14880     },
14881     {.name = "start",
14882      .handler = cmdStart,
14883      .opts = opts_start,
14884      .info = info_start,
14885      .flags = 0
14886     },
14887     {.name = "suspend",
14888      .handler = cmdSuspend,
14889      .opts = opts_suspend,
14890      .info = info_suspend,
14891      .flags = 0
14892     },
14893     {.name = "ttyconsole",
14894      .handler = cmdTTYConsole,
14895      .opts = opts_ttyconsole,
14896      .info = info_ttyconsole,
14897      .flags = 0
14898     },
14899     {.name = "undefine",
14900      .handler = cmdUndefine,
14901      .opts = opts_undefine,
14902      .info = info_undefine,
14903      .flags = 0
14904     },
14905     {.name = "update-device",
14906      .handler = cmdUpdateDevice,
14907      .opts = opts_update_device,
14908      .info = info_update_device,
14909      .flags = 0
14910     },
14911     {.name = "update-memory-device",
14912      .handler = cmdUpdateMemoryDevice,
14913      .opts = opts_update_memory_device,
14914      .info = info_update_memory_device,
14915      .flags = 0
14916     },
14917     {.name = "vcpucount",
14918      .handler = cmdVcpucount,
14919      .opts = opts_vcpucount,
14920      .info = info_vcpucount,
14921      .flags = 0
14922     },
14923     {.name = "vcpuinfo",
14924      .handler = cmdVcpuinfo,
14925      .opts = opts_vcpuinfo,
14926      .info = info_vcpuinfo,
14927      .flags = 0
14928     },
14929     {.name = "vcpupin",
14930      .handler = cmdVcpuPin,
14931      .opts = opts_vcpupin,
14932      .info = info_vcpupin,
14933      .flags = 0
14934     },
14935     {.name = "emulatorpin",
14936      .handler = cmdEmulatorPin,
14937      .opts = opts_emulatorpin,
14938      .info = info_emulatorpin,
14939      .flags = 0
14940     },
14941     {.name = "vncdisplay",
14942      .handler = cmdVNCDisplay,
14943      .opts = opts_vncdisplay,
14944      .info = info_vncdisplay,
14945      .flags = 0
14946     },
14947     {.name = "guestvcpus",
14948      .handler = cmdGuestvcpus,
14949      .opts = opts_guestvcpus,
14950      .info = info_guestvcpus,
14951      .flags = 0
14952     },
14953     {.name = "setvcpu",
14954      .handler = cmdSetvcpu,
14955      .opts = opts_setvcpu,
14956      .info = info_setvcpu,
14957      .flags = 0
14958     },
14959     {.name = "domblkthreshold",
14960      .handler = cmdDomblkthreshold,
14961      .opts = opts_domblkthreshold,
14962      .info = info_domblkthreshold,
14963      .flags = 0
14964     },
14965     {.name = "guestinfo",
14966      .handler = cmdGuestInfo,
14967      .opts = opts_guestinfo,
14968      .info = info_guestinfo,
14969      .flags = 0
14970     },
14971     {.name = "domdirtyrate-calc",
14972      .handler = cmdDomDirtyRateCalc,
14973      .opts = opts_domdirtyrate_calc,
14974      .info = info_domdirtyrate_calc,
14975      .flags = 0
14976     },
14977     {.name = NULL}
14978 };
14979