1 /*
2 * virsh-completer-domain.c: virsh completer callbacks related to domains
3 *
4 * Copyright (C) 2019 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
23 #include "virsh-completer-domain.h"
24 #include "viralloc.h"
25 #include "virmacaddr.h"
26 #include "virsh-domain.h"
27 #include "virsh-domain-monitor.h"
28 #include "virsh-util.h"
29 #include "virsh.h"
30 #include "virstring.h"
31 #include "virxml.h"
32 #include "virperf.h"
33 #include "virbitmap.h"
34 #include "virkeycode.h"
35 #include "virglibutil.h"
36 #include "virkeynametable_linux.h"
37 #include "virkeynametable_osx.h"
38 #include "virkeynametable_win32.h"
39 #include "conf/storage_conf.h"
40
41 char **
virshDomainNameCompleter(vshControl * ctl,const vshCmd * cmd G_GNUC_UNUSED,unsigned int flags)42 virshDomainNameCompleter(vshControl *ctl,
43 const vshCmd *cmd G_GNUC_UNUSED,
44 unsigned int flags)
45 {
46 virshControl *priv = ctl->privData;
47 virDomainPtr *domains = NULL;
48 int ndomains = 0;
49 size_t i = 0;
50 char **ret = NULL;
51 g_auto(GStrv) tmp = NULL;
52
53 virCheckFlags(VIR_CONNECT_LIST_DOMAINS_ACTIVE |
54 VIR_CONNECT_LIST_DOMAINS_INACTIVE |
55 VIR_CONNECT_LIST_DOMAINS_OTHER |
56 VIR_CONNECT_LIST_DOMAINS_PAUSED |
57 VIR_CONNECT_LIST_DOMAINS_PERSISTENT |
58 VIR_CONNECT_LIST_DOMAINS_RUNNING |
59 VIR_CONNECT_LIST_DOMAINS_SHUTOFF |
60 VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE |
61 VIR_CONNECT_LIST_DOMAINS_HAS_SNAPSHOT |
62 VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT,
63 NULL);
64
65 if (!priv->conn || virConnectIsAlive(priv->conn) <= 0)
66 return NULL;
67
68 if ((ndomains = virConnectListAllDomains(priv->conn, &domains, flags)) < 0)
69 return NULL;
70
71 tmp = g_new0(char *, ndomains + 1);
72
73 for (i = 0; i < ndomains; i++) {
74 const char *name = virDomainGetName(domains[i]);
75
76 tmp[i] = g_strdup(name);
77 }
78
79 ret = g_steal_pointer(&tmp);
80
81 for (i = 0; i < ndomains; i++)
82 virshDomainFree(domains[i]);
83 g_free(domains);
84 return ret;
85 }
86
87
88 char **
virshDomainUUIDCompleter(vshControl * ctl,const vshCmd * cmd G_GNUC_UNUSED,unsigned int flags)89 virshDomainUUIDCompleter(vshControl *ctl,
90 const vshCmd *cmd G_GNUC_UNUSED,
91 unsigned int flags)
92 {
93 virshControl *priv = ctl->privData;
94 virDomainPtr *domains = NULL;
95 int ndomains = 0;
96 size_t i = 0;
97 char **ret = NULL;
98 g_auto(GStrv) tmp = NULL;
99
100 virCheckFlags(VIR_CONNECT_LIST_DOMAINS_ACTIVE |
101 VIR_CONNECT_LIST_DOMAINS_INACTIVE |
102 VIR_CONNECT_LIST_DOMAINS_OTHER |
103 VIR_CONNECT_LIST_DOMAINS_PAUSED |
104 VIR_CONNECT_LIST_DOMAINS_PERSISTENT |
105 VIR_CONNECT_LIST_DOMAINS_RUNNING |
106 VIR_CONNECT_LIST_DOMAINS_SHUTOFF |
107 VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE |
108 VIR_CONNECT_LIST_DOMAINS_HAS_SNAPSHOT |
109 VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT,
110 NULL);
111
112 if (!priv->conn || virConnectIsAlive(priv->conn) <= 0)
113 return NULL;
114
115 if ((ndomains = virConnectListAllDomains(priv->conn, &domains, flags)) < 0)
116 return NULL;
117
118 tmp = g_new0(char *, ndomains + 1);
119
120 for (i = 0; i < ndomains; i++) {
121 char uuid[VIR_UUID_STRING_BUFLEN];
122
123 if (virDomainGetUUIDString(domains[i], uuid) < 0)
124 goto cleanup;
125
126 tmp[i] = g_strdup(uuid);
127 }
128
129 ret = g_steal_pointer(&tmp);
130
131 cleanup:
132 for (i = 0; i < ndomains; i++)
133 virshDomainFree(domains[i]);
134 g_free(domains);
135 return ret;
136 }
137
138
139 char **
virshDomainInterfaceCompleter(vshControl * ctl,const vshCmd * cmd,unsigned int flags)140 virshDomainInterfaceCompleter(vshControl *ctl,
141 const vshCmd *cmd,
142 unsigned int flags)
143 {
144 virshControl *priv = ctl->privData;
145 g_autoptr(xmlDoc) xmldoc = NULL;
146 g_autoptr(xmlXPathContext) ctxt = NULL;
147 int ninterfaces;
148 g_autofree xmlNodePtr *interfaces = NULL;
149 size_t i;
150 unsigned int domainXMLFlags = 0;
151 g_auto(GStrv) tmp = NULL;
152
153 virCheckFlags(VIRSH_DOMAIN_INTERFACE_COMPLETER_MAC, NULL);
154
155 if (!priv->conn || virConnectIsAlive(priv->conn) <= 0)
156 return NULL;
157
158 if (vshCommandOptBool(cmd, "config"))
159 domainXMLFlags = VIR_DOMAIN_XML_INACTIVE;
160
161 if (virshDomainGetXML(ctl, cmd, domainXMLFlags, &xmldoc, &ctxt) < 0)
162 return NULL;
163
164 ninterfaces = virXPathNodeSet("./devices/interface", ctxt, &interfaces);
165 if (ninterfaces < 0)
166 return NULL;
167
168 tmp = g_new0(char *, ninterfaces + 1);
169
170 for (i = 0; i < ninterfaces; i++) {
171 ctxt->node = interfaces[i];
172
173 if (!(flags & VIRSH_DOMAIN_INTERFACE_COMPLETER_MAC) &&
174 (tmp[i] = virXPathString("string(./target/@dev)", ctxt)))
175 continue;
176
177 /* In case we are dealing with inactive domain XML there's no
178 * <target dev=''/>. Offer MAC addresses then. */
179 if (!(tmp[i] = virXPathString("string(./mac/@address)", ctxt)))
180 return NULL;
181 }
182
183 return g_steal_pointer(&tmp);
184 }
185
186
187 char **
virshDomainDiskTargetCompleter(vshControl * ctl,const vshCmd * cmd,unsigned int flags)188 virshDomainDiskTargetCompleter(vshControl *ctl,
189 const vshCmd *cmd,
190 unsigned int flags)
191 {
192 virshControl *priv = ctl->privData;
193 g_autoptr(xmlDoc) xmldoc = NULL;
194 g_autoptr(xmlXPathContext) ctxt = NULL;
195 g_autofree xmlNodePtr *disks = NULL;
196 int ndisks;
197 size_t i;
198 g_auto(GStrv) tmp = NULL;
199
200 virCheckFlags(0, NULL);
201
202 if (!priv->conn || virConnectIsAlive(priv->conn) <= 0)
203 return NULL;
204
205 if (virshDomainGetXML(ctl, cmd, 0, &xmldoc, &ctxt) < 0)
206 return NULL;
207
208 ndisks = virXPathNodeSet("./devices/disk", ctxt, &disks);
209 if (ndisks < 0)
210 return NULL;
211
212 tmp = g_new0(char *, ndisks + 1);
213
214 for (i = 0; i < ndisks; i++) {
215 ctxt->node = disks[i];
216 if (!(tmp[i] = virXPathString("string(./target/@dev)", ctxt)))
217 return NULL;
218 }
219
220 return g_steal_pointer(&tmp);
221 }
222
223
224 static char **
virshDomainDiskTargetListCompleter(vshControl * ctl,const vshCmd * cmd,const char * argname)225 virshDomainDiskTargetListCompleter(vshControl *ctl,
226 const vshCmd *cmd,
227 const char *argname)
228 {
229 const char *curval = NULL;
230 g_auto(GStrv) targets = virshDomainDiskTargetCompleter(ctl, cmd, 0);
231
232 if (vshCommandOptStringQuiet(ctl, cmd, argname, &curval) < 0)
233 return NULL;
234
235 if (!targets)
236 return NULL;
237
238 return virshCommaStringListComplete(curval, (const char **) targets);
239 }
240
241
242 char **
virshDomainMigrateDisksCompleter(vshControl * ctl,const vshCmd * cmd,unsigned int completeflags G_GNUC_UNUSED)243 virshDomainMigrateDisksCompleter(vshControl *ctl,
244 const vshCmd *cmd,
245 unsigned int completeflags G_GNUC_UNUSED)
246 {
247 return virshDomainDiskTargetListCompleter(ctl, cmd, "migrate-disks");
248 }
249
250
251 char **
virshDomainUndefineStorageDisksCompleter(vshControl * ctl,const vshCmd * cmd,unsigned int completeflags G_GNUC_UNUSED)252 virshDomainUndefineStorageDisksCompleter(vshControl *ctl,
253 const vshCmd *cmd,
254 unsigned int completeflags G_GNUC_UNUSED)
255 {
256 return virshDomainDiskTargetListCompleter(ctl, cmd, "storage");
257 }
258
259
260 static GSList *
virshDomainBlockjobBaseTopCompleteDisk(const char * target,xmlXPathContext * ctxt)261 virshDomainBlockjobBaseTopCompleteDisk(const char *target,
262 xmlXPathContext *ctxt)
263 {
264 g_autofree xmlNodePtr *indexlist = NULL;
265 int nindexlist = 0;
266 size_t i;
267 GSList *ret = NULL;
268
269 if ((nindexlist = virXPathNodeSet("./source|./backingStore",
270 ctxt, &indexlist)) < 0)
271 return NULL;
272
273 ret = g_slist_prepend(ret, g_strdup(target));
274
275 for (i = 0; i < nindexlist; i++) {
276 g_autofree char *idx = virXMLPropString(indexlist[i], "index");
277
278 if (!idx)
279 continue;
280
281 ret = g_slist_prepend(ret, g_strdup_printf("%s[%s]", target, idx));
282 }
283
284 return ret;
285 }
286
287
288 char **
virshDomainBlockjobBaseTopCompleter(vshControl * ctl,const vshCmd * cmd,unsigned int flags)289 virshDomainBlockjobBaseTopCompleter(vshControl *ctl,
290 const vshCmd *cmd,
291 unsigned int flags)
292 {
293 virshControl *priv = ctl->privData;
294 g_autoptr(xmlDoc) xmldoc = NULL;
295 g_autoptr(xmlXPathContext) ctxt = NULL;
296 g_autofree xmlNodePtr *disks = NULL;
297 int ndisks;
298 size_t i;
299 const char *path = NULL;
300 g_autoptr(virGSListString) list = NULL;
301 GSList *n;
302 GStrv ret = NULL;
303 size_t nelems;
304
305 virCheckFlags(0, NULL);
306
307 if (!priv->conn || virConnectIsAlive(priv->conn) <= 0)
308 return NULL;
309
310 if (virshDomainGetXML(ctl, cmd, 0, &xmldoc, &ctxt) < 0)
311 return NULL;
312
313 ignore_value(vshCommandOptStringQuiet(ctl, cmd, "path", &path));
314
315 if ((ndisks = virXPathNodeSet("./devices/disk", ctxt, &disks)) <= 0)
316 return NULL;
317
318 for (i = 0; i < ndisks; i++) {
319 g_autofree char *disktarget = NULL;
320
321 ctxt->node = disks[i];
322 disktarget = virXPathString("string(./target/@dev)", ctxt);
323
324 if (STREQ_NULLABLE(path, disktarget))
325 break;
326 }
327
328 if (i == ndisks)
329 path = NULL;
330
331 for (i = 0; i < ndisks; i++) {
332 g_autofree char *disktarget = NULL;
333 GSList *tmplist;
334
335 ctxt->node = disks[i];
336
337 if (!(disktarget = virXPathString("string(./target/@dev)", ctxt)))
338 return NULL;
339
340 if (path && STRNEQ(path, disktarget))
341 continue;
342
343 /* note that ctxt->node moved */
344 if ((tmplist = virshDomainBlockjobBaseTopCompleteDisk(disktarget, ctxt)))
345 list = g_slist_concat(tmplist, list);
346 }
347
348 list = g_slist_reverse(list);
349 nelems = g_slist_length(list);
350 ret = g_new0(char *, nelems + 1);
351 i = 0;
352
353 for (n = list; n; n = n->next)
354 ret[i++] = g_strdup(n->data);
355
356 return ret;
357 }
358
359 char **
virshDomainEventNameCompleter(vshControl * ctl G_GNUC_UNUSED,const vshCmd * cmd G_GNUC_UNUSED,unsigned int flags)360 virshDomainEventNameCompleter(vshControl *ctl G_GNUC_UNUSED,
361 const vshCmd *cmd G_GNUC_UNUSED,
362 unsigned int flags)
363 {
364 size_t i = 0;
365 g_auto(GStrv) tmp = NULL;
366
367 virCheckFlags(0, NULL);
368
369 tmp = g_new0(char *, VIR_DOMAIN_EVENT_ID_LAST + 1);
370
371 for (i = 0; i < VIR_DOMAIN_EVENT_ID_LAST; i++)
372 tmp[i] = g_strdup(virshDomainEventCallbacks[i].name);
373
374 return g_steal_pointer(&tmp);
375 }
376
377
378 char **
virshDomainInterfaceStateCompleter(vshControl * ctl,const vshCmd * cmd,unsigned int flags)379 virshDomainInterfaceStateCompleter(vshControl *ctl,
380 const vshCmd *cmd,
381 unsigned int flags)
382 {
383 virshControl *priv = ctl->privData;
384 const char *iface = NULL;
385 g_autoptr(xmlDoc) xml = NULL;
386 g_autoptr(xmlXPathContext) ctxt = NULL;
387 virMacAddr macaddr;
388 char macstr[VIR_MAC_STRING_BUFLEN] = "";
389 int ninterfaces;
390 g_autofree xmlNodePtr *interfaces = NULL;
391 g_autofree char *xpath = NULL;
392 g_autofree char *state = NULL;
393 g_auto(GStrv) tmp = NULL;
394
395 virCheckFlags(0, NULL);
396
397 if (!priv->conn || virConnectIsAlive(priv->conn) <= 0)
398 return NULL;
399
400 if (virshDomainGetXML(ctl, cmd, flags, &xml, &ctxt) < 0)
401 return NULL;
402
403 if (vshCommandOptStringReq(ctl, cmd, "interface", &iface) < 0)
404 return NULL;
405
406 /* normalize the mac addr */
407 if (virMacAddrParse(iface, &macaddr) == 0)
408 virMacAddrFormat(&macaddr, macstr);
409
410 xpath = g_strdup_printf("/domain/devices/interface[(mac/@address = '%s') or "
411 " (target/@dev = '%s')]", macstr,
412 iface);
413
414 if ((ninterfaces = virXPathNodeSet(xpath, ctxt, &interfaces)) < 0)
415 return NULL;
416
417 if (ninterfaces != 1)
418 return NULL;
419
420 ctxt->node = interfaces[0];
421
422 tmp = g_new0(char *, 2);
423
424 if ((state = virXPathString("string(./link/@state)", ctxt)) &&
425 STREQ(state, "down")) {
426 tmp[0] = g_strdup("up");
427 } else {
428 tmp[0] = g_strdup("down");
429 }
430
431 return g_steal_pointer(&tmp);
432 }
433
434
435 char **
virshDomainDeviceAliasCompleter(vshControl * ctl,const vshCmd * cmd,unsigned int flags)436 virshDomainDeviceAliasCompleter(vshControl *ctl,
437 const vshCmd *cmd,
438 unsigned int flags)
439 {
440 virshControl *priv = ctl->privData;
441 g_autoptr(xmlDoc) xmldoc = NULL;
442 g_autoptr(xmlXPathContext) ctxt = NULL;
443 int naliases;
444 g_autofree xmlNodePtr *aliases = NULL;
445 size_t i;
446 unsigned int domainXMLFlags = 0;
447 g_auto(GStrv) tmp = NULL;
448
449 virCheckFlags(0, NULL);
450
451 if (!priv->conn || virConnectIsAlive(priv->conn) <= 0)
452 return NULL;
453
454 if (vshCommandOptBool(cmd, "config"))
455 domainXMLFlags = VIR_DOMAIN_XML_INACTIVE;
456
457 if (virshDomainGetXML(ctl, cmd, domainXMLFlags, &xmldoc, &ctxt) < 0)
458 return NULL;
459
460 naliases = virXPathNodeSet("/domain/devices//alias[@name]", ctxt, &aliases);
461 if (naliases < 0)
462 return NULL;
463
464 tmp = g_new0(char *, naliases + 1);
465
466 for (i = 0; i < naliases; i++) {
467 if (!(tmp[i] = virXMLPropString(aliases[i], "name")))
468 return NULL;
469 }
470
471 return g_steal_pointer(&tmp);
472 }
473
474
475 char **
virshDomainShutdownModeCompleter(vshControl * ctl,const vshCmd * cmd,unsigned int flags)476 virshDomainShutdownModeCompleter(vshControl *ctl,
477 const vshCmd *cmd,
478 unsigned int flags)
479 {
480 const char *modes[] = {"acpi", "agent", "initctl", "signal", "paravirt", NULL};
481 const char *mode = NULL;
482
483 virCheckFlags(0, NULL);
484
485 if (vshCommandOptStringQuiet(ctl, cmd, "mode", &mode) < 0)
486 return NULL;
487
488 return virshCommaStringListComplete(mode, modes);
489 }
490
491
492 char **
virshDomainInterfaceAddrSourceCompleter(vshControl * ctl G_GNUC_UNUSED,const vshCmd * cmd G_GNUC_UNUSED,unsigned int flags)493 virshDomainInterfaceAddrSourceCompleter(vshControl *ctl G_GNUC_UNUSED,
494 const vshCmd *cmd G_GNUC_UNUSED,
495 unsigned int flags)
496 {
497 char **ret = NULL;
498 size_t i;
499
500 virCheckFlags(0, NULL);
501
502 ret = g_new0(char *, VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LAST + 1);
503
504 for (i = 0; i < VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LAST; i++)
505 ret[i] = g_strdup(virshDomainInterfaceAddressesSourceTypeToString(i));
506
507 return ret;
508 }
509
510
511 char **
virshDomainInterfaceSourceModeCompleter(vshControl * ctl G_GNUC_UNUSED,const vshCmd * cmd G_GNUC_UNUSED,unsigned int flags)512 virshDomainInterfaceSourceModeCompleter(vshControl *ctl G_GNUC_UNUSED,
513 const vshCmd *cmd G_GNUC_UNUSED,
514 unsigned int flags)
515 {
516 char **ret = NULL;
517 size_t i;
518
519 virCheckFlags(0, NULL);
520
521 ret = g_new0(char *, VIRSH_DOMAIN_INTERFACE_SOURCE_MODE_LAST);
522
523 for (i = 0; i < VIRSH_DOMAIN_INTERFACE_SOURCE_MODE_LAST; i++)
524 ret[i] = g_strdup(virshDomainInterfaceSourceModeTypeToString(i));
525
526 return ret;
527 }
528
529
530 char **
virshDomainHostnameSourceCompleter(vshControl * ctl G_GNUC_UNUSED,const vshCmd * cmd G_GNUC_UNUSED,unsigned int flags)531 virshDomainHostnameSourceCompleter(vshControl *ctl G_GNUC_UNUSED,
532 const vshCmd *cmd G_GNUC_UNUSED,
533 unsigned int flags)
534 {
535 char **ret = NULL;
536 size_t i;
537
538 virCheckFlags(0, NULL);
539
540 ret = g_new0(char *, VIRSH_DOMAIN_HOSTNAME_SOURCE_LAST + 1);
541
542 for (i = 0; i < VIRSH_DOMAIN_HOSTNAME_SOURCE_LAST; i++)
543 ret[i] = g_strdup(virshDomainHostnameSourceTypeToString(i));
544
545 return ret;
546 }
547
548
549 char **
virshDomainPerfEnableCompleter(vshControl * ctl,const vshCmd * cmd,unsigned int flags)550 virshDomainPerfEnableCompleter(vshControl *ctl,
551 const vshCmd *cmd,
552 unsigned int flags)
553 {
554 size_t i = 0;
555 g_auto(GStrv) events = NULL;
556 const char *event = NULL;
557
558 virCheckFlags(0, NULL);
559
560 events = g_new0(char *, VIR_PERF_EVENT_LAST + 1);
561
562 for (i = 0; i < VIR_PERF_EVENT_LAST; i++)
563 events[i] = g_strdup(virPerfEventTypeToString(i));
564
565 if (vshCommandOptStringQuiet(ctl, cmd, "enable", &event) < 0)
566 return NULL;
567
568 return virshCommaStringListComplete(event, (const char **)events);
569 }
570
571
572 char **
virshDomainPerfDisableCompleter(vshControl * ctl,const vshCmd * cmd,unsigned int flags)573 virshDomainPerfDisableCompleter(vshControl *ctl,
574 const vshCmd *cmd,
575 unsigned int flags)
576 {
577 size_t i = 0;
578 g_auto(GStrv) events = NULL;
579 const char *event = NULL;
580
581 virCheckFlags(0, NULL);
582
583 events = g_new0(char *, VIR_PERF_EVENT_LAST + 1);
584
585 for (i = 0; i < VIR_PERF_EVENT_LAST; i++)
586 events[i] = g_strdup(virPerfEventTypeToString(i));
587
588 if (vshCommandOptStringQuiet(ctl, cmd, "disable", &event) < 0)
589 return NULL;
590
591 return virshCommaStringListComplete(event, (const char **)events);
592 }
593
594
595 char **
virshDomainIOThreadIdCompleter(vshControl * ctl,const vshCmd * cmd,unsigned int flags)596 virshDomainIOThreadIdCompleter(vshControl *ctl,
597 const vshCmd *cmd,
598 unsigned int flags)
599 {
600 g_autoptr(virshDomain) dom = NULL;
601 size_t niothreads = 0;
602 g_autofree virDomainIOThreadInfoPtr *info = NULL;
603 size_t i;
604 int rc;
605 g_auto(GStrv) tmp = NULL;
606
607 virCheckFlags(0, NULL);
608
609 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
610 return NULL;
611
612 if ((rc = virDomainGetIOThreadInfo(dom, &info, flags)) < 0)
613 return NULL;
614
615 niothreads = rc;
616
617 tmp = g_new0(char *, niothreads + 1);
618
619 for (i = 0; i < niothreads; i++)
620 tmp[i] = g_strdup_printf("%u", info[i]->iothread_id);
621
622 return g_steal_pointer(&tmp);
623 }
624
625
626 char **
virshDomainVcpuCompleter(vshControl * ctl,const vshCmd * cmd,unsigned int flags)627 virshDomainVcpuCompleter(vshControl *ctl,
628 const vshCmd *cmd,
629 unsigned int flags)
630 {
631 g_autoptr(virshDomain) dom = NULL;
632 g_autoptr(xmlDoc) xml = NULL;
633 g_autoptr(xmlXPathContext) ctxt = NULL;
634 int nvcpus = 0;
635 unsigned int id;
636 g_auto(GStrv) tmp = NULL;
637
638 virCheckFlags(0, NULL);
639
640 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
641 return NULL;
642
643 if (virshDomainGetXMLFromDom(ctl, dom, VIR_DOMAIN_XML_INACTIVE,
644 &xml, &ctxt) < 0)
645 return NULL;
646
647 /* Query the max rather than the current vcpu count */
648 if (virXPathInt("string(/domain/vcpu)", ctxt, &nvcpus) < 0)
649 return NULL;
650
651 tmp = g_new0(char *, nvcpus + 1);
652
653 for (id = 0; id < nvcpus; id++)
654 tmp[id] = g_strdup_printf("%u", id);
655
656 return g_steal_pointer(&tmp);
657 }
658
659
660 char **
virshDomainVcpulistCompleter(vshControl * ctl,const vshCmd * cmd,unsigned int flags)661 virshDomainVcpulistCompleter(vshControl *ctl,
662 const vshCmd *cmd,
663 unsigned int flags)
664 {
665 g_autoptr(virshDomain) dom = NULL;
666 g_autoptr(xmlDoc) xml = NULL;
667 g_autoptr(xmlXPathContext) ctxt = NULL;
668 int nvcpus = 0;
669 unsigned int id;
670 g_auto(GStrv) vcpulist = NULL;
671 const char *vcpuid = NULL;
672
673 virCheckFlags(0, NULL);
674
675 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
676 return NULL;
677
678 if (vshCommandOptStringQuiet(ctl, cmd, "vcpulist", &vcpuid) < 0)
679 return NULL;
680
681 if (virshDomainGetXMLFromDom(ctl, dom, VIR_DOMAIN_XML_INACTIVE,
682 &xml, &ctxt) < 0)
683 return NULL;
684
685 /* Query the max rather than the current vcpu count */
686 if (virXPathInt("string(/domain/vcpu)", ctxt, &nvcpus) < 0)
687 return NULL;
688
689 vcpulist = g_new0(char *, nvcpus + 1);
690
691 for (id = 0; id < nvcpus; id++)
692 vcpulist[id] = g_strdup_printf("%u", id);
693
694 return virshCommaStringListComplete(vcpuid, (const char **)vcpulist);
695 }
696
697
698 char **
virshDomainCpulistCompleter(vshControl * ctl,const vshCmd * cmd,unsigned int flags)699 virshDomainCpulistCompleter(vshControl *ctl,
700 const vshCmd *cmd,
701 unsigned int flags)
702 {
703 virshControl *priv = ctl->privData;
704 size_t i;
705 int cpunum;
706 g_autofree unsigned char *cpumap = NULL;
707 unsigned int online;
708 g_auto(GStrv) cpulist = NULL;
709 const char *cpuid = NULL;
710
711 virCheckFlags(0, NULL);
712
713 if (vshCommandOptStringQuiet(ctl, cmd, "cpulist", &cpuid) < 0)
714 return NULL;
715
716 if ((cpunum = virNodeGetCPUMap(priv->conn, &cpumap, &online, 0)) < 0)
717 return NULL;
718
719 cpulist = g_new0(char *, cpunum + 1);
720
721 for (i = 0; i < cpunum; i++)
722 cpulist[i] = g_strdup_printf("%zu", i);
723
724 return virshCommaStringListComplete(cpuid, (const char **)cpulist);
725 }
726
727
728 char **
virshDomainVcpulistViaAgentCompleter(vshControl * ctl,const vshCmd * cmd,unsigned int flags)729 virshDomainVcpulistViaAgentCompleter(vshControl *ctl,
730 const vshCmd *cmd,
731 unsigned int flags)
732 {
733 g_autoptr(virshDomain) dom = NULL;
734 bool enable = vshCommandOptBool(cmd, "enable");
735 bool disable = vshCommandOptBool(cmd, "disable");
736 virTypedParameterPtr params = NULL;
737 unsigned int nparams = 0;
738 size_t i;
739 int nvcpus;
740 g_auto(GStrv) cpulist = NULL;
741 const char *vcpuid = NULL;
742 char **ret = NULL;
743
744 virCheckFlags(0, NULL);
745
746 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
747 return NULL;
748
749 if (vshCommandOptStringQuiet(ctl, cmd, "cpulist", &vcpuid) < 0)
750 goto cleanup;
751
752 /* retrieve vcpu count from the guest instead of the hypervisor */
753 if ((nvcpus = virDomainGetVcpusFlags(dom,
754 VIR_DOMAIN_VCPU_GUEST |
755 VIR_DOMAIN_VCPU_MAXIMUM)) < 0)
756 goto cleanup;
757
758 if (!enable && !disable) {
759 cpulist = g_new0(char *, nvcpus + 1);
760 for (i = 0; i < nvcpus; i++)
761 cpulist[i] = g_strdup_printf("%zu", i);
762 } else {
763 g_autofree char *onlineVcpuStr = NULL;
764 g_autofree char *offlinableVcpuStr = NULL;
765 g_autofree unsigned char *onlineVcpumap = NULL;
766 g_autofree unsigned char *offlinableVcpumap = NULL;
767 g_autoptr(virBitmap) onlineVcpus = NULL;
768 g_autoptr(virBitmap) offlinableVcpus = NULL;
769 size_t j = 0;
770 int lastcpu;
771 int dummy;
772
773 if (virDomainGetGuestVcpus(dom, ¶ms, &nparams, 0) < 0)
774 goto cleanup;
775
776 onlineVcpuStr = vshGetTypedParamValue(ctl, ¶ms[1]);
777 if (!(onlineVcpus = virBitmapParseUnlimited(onlineVcpuStr)))
778 goto cleanup;
779
780 if (virBitmapToData(onlineVcpus, &onlineVcpumap, &dummy) < 0)
781 goto cleanup;
782
783 if (enable) {
784 offlinableVcpuStr = vshGetTypedParamValue(ctl, ¶ms[2]);
785
786 if (!(offlinableVcpus = virBitmapParseUnlimited(offlinableVcpuStr)))
787 goto cleanup;
788
789 if (virBitmapToData(offlinableVcpus, &offlinableVcpumap, &dummy) < 0)
790 goto cleanup;
791
792 lastcpu = virBitmapLastSetBit(offlinableVcpus);
793 cpulist = g_new0(char *, nvcpus - virBitmapCountBits(onlineVcpus) + 1);
794 for (i = 0; i < nvcpus - virBitmapCountBits(onlineVcpus); i++) {
795 while (j <= lastcpu) {
796 if (VIR_CPU_USED(onlineVcpumap, j) != 0 ||
797 VIR_CPU_USED(offlinableVcpumap, j) == 0) {
798 j += 1;
799 continue;
800 } else {
801 break;
802 }
803 }
804
805 cpulist[i] = g_strdup_printf("%zu", j++);
806 }
807 } else if (disable) {
808 lastcpu = virBitmapLastSetBit(onlineVcpus);
809 cpulist = g_new0(char *, virBitmapCountBits(onlineVcpus) + 1);
810 for (i = 0; i < virBitmapCountBits(onlineVcpus); i++) {
811 while (j <= lastcpu) {
812 if (VIR_CPU_USED(onlineVcpumap, j) == 0) {
813 j += 1;
814 continue;
815 } else {
816 break;
817 }
818 }
819
820 cpulist[i] = g_strdup_printf("%zu", j++);
821 }
822 }
823 }
824
825 ret = virshCommaStringListComplete(vcpuid, (const char **)cpulist);
826
827 cleanup:
828 virTypedParamsFree(params, nparams);
829 return ret;
830 }
831
832
833 char **
virshDomainConsoleCompleter(vshControl * ctl,const vshCmd * cmd,unsigned int flags)834 virshDomainConsoleCompleter(vshControl *ctl,
835 const vshCmd *cmd,
836 unsigned int flags)
837 {
838 virshControl *priv = ctl->privData;
839 g_autoptr(xmlDoc) xmldoc = NULL;
840 g_autoptr(xmlXPathContext) ctxt = NULL;
841 int nserials;
842 int nparallels;
843 g_autofree xmlNodePtr *serials = NULL;
844 g_autofree xmlNodePtr *parallels = NULL;
845 size_t i;
846 size_t offset = 0;
847 g_auto(GStrv) tmp = NULL;
848
849 virCheckFlags(0, NULL);
850
851 if (!priv->conn || virConnectIsAlive(priv->conn) <= 0)
852 return NULL;
853
854 if (virshDomainGetXML(ctl, cmd, 0, &xmldoc, &ctxt) < 0)
855 return NULL;
856
857 nserials = virXPathNodeSet("./devices/serial", ctxt, &serials);
858 if (nserials < 0)
859 return NULL;
860
861 nparallels = virXPathNodeSet("./devices/parallel", ctxt, ¶llels);
862 if (nparallels < 0)
863 return NULL;
864
865 tmp = g_new0(char *, nserials + nparallels + 1);
866
867 for (i = 0; i < nserials + nparallels; i++) {
868 g_autofree char *type = NULL;
869
870
871 if (i < nserials)
872 ctxt->node = serials[i];
873 else
874 ctxt->node = parallels[i - nserials];
875
876 type = virXPathString("string(./@type)", ctxt);
877 if (STRNEQ(type, "pty"))
878 continue;
879
880 tmp[offset++] = virXPathString("string(./alias/@name)", ctxt);
881 }
882
883 return g_steal_pointer(&tmp);
884 }
885
886
887 char **
virshDomainSignalCompleter(vshControl * ctl G_GNUC_UNUSED,const vshCmd * cmd G_GNUC_UNUSED,unsigned int flags)888 virshDomainSignalCompleter(vshControl *ctl G_GNUC_UNUSED,
889 const vshCmd *cmd G_GNUC_UNUSED,
890 unsigned int flags)
891 {
892 g_auto(GStrv) tmp = NULL;
893 size_t i = 0;
894
895 virCheckFlags(0, NULL);
896
897 tmp = g_new0(char *, VIR_DOMAIN_PROCESS_SIGNAL_LAST + 1);
898
899 for (i = 0; i < VIR_DOMAIN_PROCESS_SIGNAL_LAST; i++) {
900 const char *name = virshDomainProcessSignalTypeToString(i);
901 tmp[i] = g_strdup(name);
902 }
903
904 return g_steal_pointer(&tmp);
905 }
906
907
908 char **
virshDomainLifecycleCompleter(vshControl * ctl G_GNUC_UNUSED,const vshCmd * cmd G_GNUC_UNUSED,unsigned int flags)909 virshDomainLifecycleCompleter(vshControl *ctl G_GNUC_UNUSED,
910 const vshCmd *cmd G_GNUC_UNUSED,
911 unsigned int flags)
912 {
913 g_auto(GStrv) tmp = NULL;
914 size_t i = 0;
915
916 virCheckFlags(0, NULL);
917
918 tmp = g_new0(char *, VIR_DOMAIN_LIFECYCLE_LAST + 1);
919
920 for (i = 0; i < VIR_DOMAIN_LIFECYCLE_LAST; i++) {
921 const char *name = virshDomainLifecycleTypeToString(i);
922 tmp[i] = g_strdup(name);
923 }
924
925 return g_steal_pointer(&tmp);
926 }
927
928
929 char **
virshDomainLifecycleActionCompleter(vshControl * ctl G_GNUC_UNUSED,const vshCmd * cmd G_GNUC_UNUSED,unsigned int flags)930 virshDomainLifecycleActionCompleter(vshControl *ctl G_GNUC_UNUSED,
931 const vshCmd *cmd G_GNUC_UNUSED,
932 unsigned int flags)
933 {
934 g_auto(GStrv) tmp = NULL;
935 size_t i = 0;
936
937 virCheckFlags(0, NULL);
938
939 tmp = g_new0(char *, VIR_DOMAIN_LIFECYCLE_ACTION_LAST + 1);
940
941 for (i = 0; i < VIR_DOMAIN_LIFECYCLE_ACTION_LAST; i++) {
942 const char *action = virshDomainLifecycleActionTypeToString(i);
943 tmp[i] = g_strdup(action);
944 }
945
946 return g_steal_pointer(&tmp);
947 }
948
949
950 char **
virshCodesetNameCompleter(vshControl * ctl G_GNUC_UNUSED,const vshCmd * cmd G_GNUC_UNUSED,unsigned int flags)951 virshCodesetNameCompleter(vshControl *ctl G_GNUC_UNUSED,
952 const vshCmd *cmd G_GNUC_UNUSED,
953 unsigned int flags)
954 {
955 g_auto(GStrv) tmp = NULL;
956 size_t i = 0;
957
958 virCheckFlags(0, NULL);
959
960 tmp = g_new0(char *, VIR_KEYCODE_SET_LAST + 1);
961
962 for (i = 0; i < VIR_KEYCODE_SET_LAST; i++) {
963 const char *name = virKeycodeSetTypeToString(i);
964 tmp[i] = g_strdup(name);
965 }
966
967 return g_steal_pointer(&tmp);
968 }
969
970
971 char **
virshKeycodeNameCompleter(vshControl * ctl,const vshCmd * cmd,unsigned int flags)972 virshKeycodeNameCompleter(vshControl *ctl,
973 const vshCmd *cmd,
974 unsigned int flags)
975 {
976 g_auto(GStrv) tmp = NULL;
977 size_t i = 0;
978 size_t j = 0;
979 const char *codeset_option;
980 int codeset;
981 const char **names = NULL;
982 size_t len;
983
984 virCheckFlags(0, NULL);
985
986 if (vshCommandOptStringQuiet(ctl, cmd, "codeset", &codeset_option) <= 0)
987 codeset_option = "linux";
988
989 if (STREQ(codeset_option, "rfb"))
990 codeset_option = "qnum";
991
992 codeset = virKeycodeSetTypeFromString(codeset_option);
993
994 if (codeset < 0)
995 return NULL;
996
997 switch ((virKeycodeSet) codeset) {
998 case VIR_KEYCODE_SET_LINUX:
999 names = virKeyNameTable_linux;
1000 len = virKeyNameTable_linux_len;
1001 break;
1002 case VIR_KEYCODE_SET_OSX:
1003 names = virKeyNameTable_osx;
1004 len = virKeyNameTable_osx_len;
1005 break;
1006 case VIR_KEYCODE_SET_WIN32:
1007 names = virKeyNameTable_win32;
1008 len = virKeyNameTable_win32_len;
1009 break;
1010 case VIR_KEYCODE_SET_XT:
1011 case VIR_KEYCODE_SET_ATSET1:
1012 case VIR_KEYCODE_SET_ATSET2:
1013 case VIR_KEYCODE_SET_ATSET3:
1014 case VIR_KEYCODE_SET_XT_KBD:
1015 case VIR_KEYCODE_SET_USB:
1016 case VIR_KEYCODE_SET_QNUM:
1017 case VIR_KEYCODE_SET_LAST:
1018 break;
1019 }
1020
1021 if (!names)
1022 return NULL;
1023
1024 tmp = g_new0(char *, len + 1);
1025
1026 for (i = 0; i < len; i++) {
1027 if (!names[i])
1028 continue;
1029
1030 tmp[j] = g_strdup(names[i]);
1031 j++;
1032 }
1033
1034 tmp = g_renew(char *, tmp, j + 1);
1035
1036 return g_steal_pointer(&tmp);
1037 }
1038
1039
1040 char **
virshDomainFSMountpointsCompleter(vshControl * ctl,const vshCmd * cmd,unsigned int flags)1041 virshDomainFSMountpointsCompleter(vshControl *ctl,
1042 const vshCmd *cmd,
1043 unsigned int flags)
1044 {
1045 g_auto(GStrv) tmp = NULL;
1046 g_autoptr(virshDomain) dom = NULL;
1047 int rc = -1;
1048 size_t i;
1049 virDomainFSInfoPtr *info = NULL;
1050 size_t ninfos = 0;
1051 char **ret = NULL;
1052
1053 virCheckFlags(0, NULL);
1054
1055 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
1056 return NULL;
1057
1058 rc = virDomainGetFSInfo(dom, &info, 0);
1059 if (rc <= 0) {
1060 goto cleanup;
1061 }
1062 ninfos = rc;
1063
1064 tmp = g_new0(char *, ninfos + 1);
1065 for (i = 0; i < ninfos; i++) {
1066 tmp[i] = g_strdup(info[i]->mountpoint);
1067 }
1068 ret = g_steal_pointer(&tmp);
1069
1070 cleanup:
1071 if (info) {
1072 for (i = 0; i < ninfos; i++)
1073 virDomainFSInfoFree(info[i]);
1074 VIR_FREE(info);
1075 }
1076 return ret;
1077 }
1078
1079
1080 char **
virshDomainCoreDumpFormatCompleter(vshControl * ctl G_GNUC_UNUSED,const vshCmd * cmd G_GNUC_UNUSED,unsigned int flags)1081 virshDomainCoreDumpFormatCompleter(vshControl *ctl G_GNUC_UNUSED,
1082 const vshCmd *cmd G_GNUC_UNUSED,
1083 unsigned int flags)
1084 {
1085 char **ret = NULL;
1086 size_t i;
1087
1088 virCheckFlags(0, NULL);
1089
1090 ret = g_new0(char *, VIR_DOMAIN_CORE_DUMP_FORMAT_LAST + 1);
1091
1092 for (i = 0; i < VIR_DOMAIN_CORE_DUMP_FORMAT_LAST; i++)
1093 ret[i] = g_strdup(virshDomainCoreDumpFormatTypeToString(i));
1094
1095 return ret;
1096 }
1097
1098
1099 char **
virshDomainMigrateCompMethodsCompleter(vshControl * ctl,const vshCmd * cmd,unsigned int flags)1100 virshDomainMigrateCompMethodsCompleter(vshControl *ctl,
1101 const vshCmd *cmd,
1102 unsigned int flags)
1103 {
1104 const char *methods[] = {"xbzrle", "mt", NULL};
1105 const char *method = NULL;
1106
1107 virCheckFlags(0, NULL);
1108
1109 if (vshCommandOptStringQuiet(ctl, cmd, "comp-methods", &method) < 0)
1110 return NULL;
1111
1112 return virshCommaStringListComplete(method, methods);
1113 }
1114
1115
1116 char **
virshDomainStorageFileFormatCompleter(vshControl * ctl G_GNUC_UNUSED,const vshCmd * cmd G_GNUC_UNUSED,unsigned int flags)1117 virshDomainStorageFileFormatCompleter(vshControl *ctl G_GNUC_UNUSED,
1118 const vshCmd *cmd G_GNUC_UNUSED,
1119 unsigned int flags)
1120 {
1121 char **ret = NULL;
1122 size_t i;
1123
1124 virCheckFlags(0, NULL);
1125
1126 ret = g_new0(char *, VIR_STORAGE_FILE_LAST + 1);
1127
1128 for (i = 0; i < VIR_STORAGE_FILE_LAST; i++)
1129 ret[i] = g_strdup(virStorageFileFormatTypeToString(i));
1130
1131 return ret;
1132 }
1133