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, &params, &nparams, 0) < 0)
774             goto cleanup;
775 
776         onlineVcpuStr = vshGetTypedParamValue(ctl, &params[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, &params[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, &parallels);
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