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(¶ms, &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(¶ms, &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(¶ms, &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, ¶ms[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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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, ¶ms[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(¶ms[nparams++],
2406 VIR_DOMAIN_BLOCK_COPY_BANDWIDTH,
2407 VIR_TYPED_PARAM_ULLONG,
2408 bandwidth) < 0)
2409 goto cleanup;
2410 }
2411 if (granularity &&
2412 virTypedParameterAssign(¶ms[nparams++],
2413 VIR_DOMAIN_BLOCK_COPY_GRANULARITY,
2414 VIR_TYPED_PARAM_UINT,
2415 granularity) < 0)
2416 goto cleanup;
2417 if (buf_size &&
2418 virTypedParameterAssign(¶ms[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(¶ms, &nparams, &maxparams,
3295 VIR_DOMAIN_BANDWIDTH_IN_AVERAGE,
3296 inbound.average) < 0)
3297 goto save_error;
3298
3299 if (inbound.peak &&
3300 virTypedParamsAddUInt(¶ms, &nparams, &maxparams,
3301 VIR_DOMAIN_BANDWIDTH_IN_PEAK,
3302 inbound.peak) < 0)
3303 goto save_error;
3304
3305 if (inbound.burst &&
3306 virTypedParamsAddUInt(¶ms, &nparams, &maxparams,
3307 VIR_DOMAIN_BANDWIDTH_IN_BURST,
3308 inbound.burst) < 0)
3309 goto save_error;
3310
3311 if (inbound.floor &&
3312 virTypedParamsAddUInt(¶ms, &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(¶ms, &nparams, &maxparams,
3338 VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE,
3339 outbound.average) < 0)
3340 goto save_error;
3341
3342 if (outbound.peak &&
3343 virTypedParamsAddUInt(¶ms, &nparams, &maxparams,
3344 VIR_DOMAIN_BANDWIDTH_OUT_PEAK,
3345 outbound.peak) < 0)
3346 goto save_error;
3347
3348 if (outbound.burst &&
3349 virTypedParamsAddUInt(¶ms, &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, ¶ms[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 ¶ms, &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 ¶ms, &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 ¶ms, &nparams, &maxparams,
5120 "weight", val) < 0))
5121 goto cleanup;
5122
5123 ret = nparams;
5124 *update_params = g_steal_pointer(¶ms);
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, ¶ms[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, ¶ms, &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(¶ms[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, ¶ms, &nparams, 0) < 0)
7335 goto cleanup;
7336
7337 for (i = 0; i < nparams; i++) {
7338 char *str = vshGetTypedParamValue(ctl, ¶ms[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(¶ms, &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(¶ms, &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(¶ms, &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, ¶ms[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, ¶ms,
9356 &nparams, &maxparams) < 0)
9357 goto cleanup;
9358
9359 if (disable && virshParseEventStr(disable, false, ¶ms,
9360 &nparams, &maxparams) < 0)
9361 goto cleanup;
9362
9363 if (nparams == 0) {
9364 if (virDomainGetPerfEvents(dom, ¶ms, &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(¶ms, &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(¶ms, &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, ¶ms[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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms,
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(¶ms,
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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms[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(¶ms[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, ¶ms, &nparams, 0) < 0)
14114 goto cleanup;
14115
14116 for (i = 0; i < nparams; i++) {
14117 g_autofree char *str = vshGetTypedParamValue(ctl, ¶ms[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