1 /*
2  * virsh-network.c: Commands to manage network
3  *
4  * Copyright (C) 2005, 2007-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 #include "virsh-network.h"
23 #include "virsh-util.h"
24 
25 #include "internal.h"
26 #include "viralloc.h"
27 #include "virfile.h"
28 #include "virtime.h"
29 #include "conf/network_conf.h"
30 #include "vsh-table.h"
31 #include "virenum.h"
32 
33 #define VIRSH_COMMON_OPT_NETWORK(_helpstr, cflags) \
34     {.name = "network", \
35      .type = VSH_OT_DATA, \
36      .flags = VSH_OFLAG_REQ, \
37      .help = _helpstr, \
38      .completer = virshNetworkNameCompleter, \
39      .completer_flags = cflags, \
40     }
41 
42 #define VIRSH_COMMON_OPT_NETWORK_FULL(cflags) \
43     VIRSH_COMMON_OPT_NETWORK(N_("network name or uuid"), cflags)
44 
45 #define VIRSH_COMMON_OPT_NETWORK_OT_STRING(_helpstr, cflags) \
46     {.name = "network", \
47      .type = VSH_OT_STRING, \
48      .help = _helpstr, \
49      .completer = virshNetworkNameCompleter, \
50      .completer_flags = cflags, \
51     }
52 
53 #define VIRSH_COMMON_OPT_NETWORK_OT_STRING_FULL(cflags) \
54     VIRSH_COMMON_OPT_NETWORK_OT_STRING(N_("network name or uuid"), cflags)
55 
56 #define VIRSH_COMMON_OPT_NETWORK_PORT(cflags) \
57     {.name = "port", \
58      .type = VSH_OT_DATA, \
59      .flags = VSH_OFLAG_REQ, \
60      .help = N_("port UUID"), \
61      .completer = virshNetworkPortUUIDCompleter, \
62      .completer_flags = cflags, \
63     }
64 
65 
66 virNetworkPtr
virshCommandOptNetworkBy(vshControl * ctl,const vshCmd * cmd,const char ** name,unsigned int flags)67 virshCommandOptNetworkBy(vshControl *ctl, const vshCmd *cmd,
68                          const char **name, unsigned int flags)
69 {
70     virNetworkPtr network = NULL;
71     const char *n = NULL;
72     const char *optname = "network";
73     virshControl *priv = ctl->privData;
74 
75     virCheckFlags(VIRSH_BYUUID | VIRSH_BYNAME, NULL);
76 
77     if (vshCommandOptStringReq(ctl, cmd, optname, &n) < 0)
78         return NULL;
79 
80     vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
81              cmd->def->name, optname, n);
82 
83     if (name)
84         *name = n;
85 
86     /* try it by UUID */
87     if ((flags & VIRSH_BYUUID) && strlen(n) == VIR_UUID_STRING_BUFLEN-1) {
88         vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as network UUID\n",
89                  cmd->def->name, optname);
90         network = virNetworkLookupByUUIDString(priv->conn, n);
91     }
92     /* try it by NAME */
93     if (!network && (flags & VIRSH_BYNAME)) {
94         vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as network NAME\n",
95                  cmd->def->name, optname);
96         network = virNetworkLookupByName(priv->conn, n);
97     }
98 
99     if (!network)
100         vshError(ctl, _("failed to get network '%s'"), n);
101 
102     return network;
103 }
104 
105 
106 virNetworkPortPtr
virshCommandOptNetworkPort(vshControl * ctl,const vshCmd * cmd,virNetworkPtr net,const char ** name)107 virshCommandOptNetworkPort(vshControl *ctl, const vshCmd *cmd,
108                            virNetworkPtr net,
109                            const char **name)
110 {
111     virNetworkPortPtr port = NULL;
112     const char *n = NULL;
113     const char *optname = "port";
114 
115     if (vshCommandOptStringReq(ctl, cmd, optname, &n) < 0)
116         return NULL;
117 
118     vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
119              cmd->def->name, optname, n);
120 
121     if (name)
122         *name = n;
123 
124     vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as network UUID\n",
125              cmd->def->name, optname);
126     port = virNetworkPortLookupByUUIDString(net, n);
127 
128     if (!port)
129         vshError(ctl, _("failed to get network port '%s'"), n);
130 
131     return port;
132 }
133 
134 /*
135  * "net-autostart" command
136  */
137 static const vshCmdInfo info_network_autostart[] = {
138     {.name = "help",
139      .data = N_("autostart a network")
140     },
141     {.name = "desc",
142      .data = N_("Configure a network to be automatically started at boot.")
143     },
144     {.name = NULL}
145 };
146 
147 static const vshCmdOptDef opts_network_autostart[] = {
148     VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_PERSISTENT),
149     {.name = "disable",
150      .type = VSH_OT_BOOL,
151      .help = N_("disable autostarting")
152     },
153     {.name = NULL}
154 };
155 
156 static bool
cmdNetworkAutostart(vshControl * ctl,const vshCmd * cmd)157 cmdNetworkAutostart(vshControl *ctl, const vshCmd *cmd)
158 {
159     g_autoptr(virshNetwork) network = NULL;
160     const char *name;
161     int autostart;
162 
163     if (!(network = virshCommandOptNetwork(ctl, cmd, &name)))
164         return false;
165 
166     autostart = !vshCommandOptBool(cmd, "disable");
167 
168     if (virNetworkSetAutostart(network, autostart) < 0) {
169         if (autostart)
170             vshError(ctl, _("failed to mark network %s as autostarted"), name);
171         else
172             vshError(ctl, _("failed to unmark network %s as autostarted"), name);
173         return false;
174     }
175 
176     if (autostart)
177         vshPrintExtra(ctl, _("Network %s marked as autostarted\n"), name);
178     else
179         vshPrintExtra(ctl, _("Network %s unmarked as autostarted\n"), name);
180 
181     return true;
182 }
183 
184 /*
185  * "net-create" command
186  */
187 static const vshCmdInfo info_network_create[] = {
188     {.name = "help",
189      .data = N_("create a network from an XML file")
190     },
191     {.name = "desc",
192      .data = N_("Create a network.")
193     },
194     {.name = NULL}
195 };
196 
197 static const vshCmdOptDef opts_network_create[] = {
198     VIRSH_COMMON_OPT_FILE(N_("file containing an XML network description")),
199     {.name = "validate",
200      .type = VSH_OT_BOOL,
201      .help = N_("validate the XML against the schema")
202     },
203     {.name = NULL}
204 };
205 
206 static bool
cmdNetworkCreate(vshControl * ctl,const vshCmd * cmd)207 cmdNetworkCreate(vshControl *ctl, const vshCmd *cmd)
208 {
209     g_autoptr(virshNetwork) network = NULL;
210     const char *from = NULL;
211     g_autofree char *buffer = NULL;
212     unsigned int flags = 0;
213     virshControl *priv = ctl->privData;
214 
215     if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
216         return false;
217 
218     if (vshCommandOptBool(cmd, "validate"))
219         flags |= VIR_NETWORK_CREATE_VALIDATE;
220 
221     if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0)
222         return false;
223 
224     if (flags)
225         network = virNetworkCreateXMLFlags(priv->conn, buffer, flags);
226     else
227         network = virNetworkCreateXML(priv->conn, buffer);
228 
229     if (!network) {
230         vshError(ctl, _("Failed to create network from %s"), from);
231         return false;
232     }
233 
234     vshPrintExtra(ctl, _("Network %s created from %s\n"),
235                   virNetworkGetName(network), from);
236     return true;
237 }
238 
239 /*
240  * "net-define" command
241  */
242 static const vshCmdInfo info_network_define[] = {
243     {.name = "help",
244      .data = N_("define an inactive persistent virtual network or modify "
245                 "an existing persistent one from an XML file")
246     },
247     {.name = "desc",
248      .data = N_("Define or modify a persistent virtual network.")
249     },
250     {.name = NULL}
251 };
252 
253 static const vshCmdOptDef opts_network_define[] = {
254     VIRSH_COMMON_OPT_FILE(N_("file containing an XML network description")),
255     {.name = "validate",
256      .type = VSH_OT_BOOL,
257      .help = N_("validate the XML against the schema")
258     },
259     {.name = NULL}
260 };
261 
262 static bool
cmdNetworkDefine(vshControl * ctl,const vshCmd * cmd)263 cmdNetworkDefine(vshControl *ctl, const vshCmd *cmd)
264 {
265     g_autoptr(virshNetwork) network = NULL;
266     const char *from = NULL;
267     g_autofree char *buffer = NULL;
268     unsigned int flags = 0;
269     virshControl *priv = ctl->privData;
270 
271     if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
272         return false;
273 
274     if (vshCommandOptBool(cmd, "validate"))
275         flags |= VIR_NETWORK_DEFINE_VALIDATE;
276 
277     if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0)
278         return false;
279 
280     if (flags)
281         network = virNetworkDefineXMLFlags(priv->conn, buffer, flags);
282     else
283         network = virNetworkDefineXML(priv->conn, buffer);
284 
285     if (!network) {
286         vshError(ctl, _("Failed to define network from %s"), from);
287         return false;
288     }
289 
290     vshPrintExtra(ctl, _("Network %s defined from %s\n"),
291                   virNetworkGetName(network), from);
292     return true;
293 }
294 
295 /*
296  * "net-destroy" command
297  */
298 static const vshCmdInfo info_network_destroy[] = {
299     {.name = "help",
300      .data = N_("destroy (stop) a network")
301     },
302     {.name = "desc",
303      .data = N_("Forcefully stop a given network.")
304     },
305     {.name = NULL}
306 };
307 
308 static const vshCmdOptDef opts_network_destroy[] = {
309     VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_ACTIVE),
310     {.name = NULL}
311 };
312 
313 static bool
cmdNetworkDestroy(vshControl * ctl,const vshCmd * cmd)314 cmdNetworkDestroy(vshControl *ctl, const vshCmd *cmd)
315 {
316     g_autoptr(virshNetwork) network = NULL;
317     bool ret = true;
318     const char *name;
319 
320     if (!(network = virshCommandOptNetwork(ctl, cmd, &name)))
321         return false;
322 
323     if (virNetworkDestroy(network) == 0) {
324         vshPrintExtra(ctl, _("Network %s destroyed\n"), name);
325     } else {
326         vshError(ctl, _("Failed to destroy network %s"), name);
327         ret = false;
328     }
329 
330     return ret;
331 }
332 
333 /*
334  * "net-dumpxml" command
335  */
336 static const vshCmdInfo info_network_dumpxml[] = {
337     {.name = "help",
338      .data = N_("network information in XML")
339     },
340     {.name = "desc",
341      .data = N_("Output the network information as an XML dump to stdout.")
342     },
343     {.name = NULL}
344 };
345 
346 static const vshCmdOptDef opts_network_dumpxml[] = {
347     VIRSH_COMMON_OPT_NETWORK_FULL(0),
348     {.name = "inactive",
349      .type = VSH_OT_BOOL,
350      .help = N_("show inactive defined XML")
351     },
352     {.name = NULL}
353 };
354 
355 static bool
cmdNetworkDumpXML(vshControl * ctl,const vshCmd * cmd)356 cmdNetworkDumpXML(vshControl *ctl, const vshCmd *cmd)
357 {
358     g_autoptr(virshNetwork) network = NULL;
359     g_autofree char *dump = NULL;
360     unsigned int flags = 0;
361 
362     if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
363         return false;
364 
365     if (vshCommandOptBool(cmd, "inactive"))
366         flags |= VIR_NETWORK_XML_INACTIVE;
367 
368     if (!(dump = virNetworkGetXMLDesc(network, flags))) {
369         return false;
370     }
371 
372     vshPrint(ctl, "%s", dump);
373     return true;
374 }
375 
376 /*
377  * "net-info" command
378  */
379 static const vshCmdInfo info_network_info[] = {
380     {.name = "help",
381      .data = N_("network information")
382     },
383     {.name = "desc",
384      .data = N_("Returns basic information about the network")
385     },
386     {.name = NULL}
387 };
388 
389 static const vshCmdOptDef opts_network_info[] = {
390     VIRSH_COMMON_OPT_NETWORK_FULL(0),
391     {.name = NULL}
392 };
393 
394 static bool
cmdNetworkInfo(vshControl * ctl,const vshCmd * cmd)395 cmdNetworkInfo(vshControl *ctl, const vshCmd *cmd)
396 {
397     g_autoptr(virshNetwork) network = NULL;
398     char uuid[VIR_UUID_STRING_BUFLEN];
399     int autostart;
400     int persistent = -1;
401     int active = -1;
402     char *bridge = NULL;
403 
404     if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
405         return false;
406 
407     vshPrint(ctl, "%-15s %s\n", _("Name:"), virNetworkGetName(network));
408 
409     if (virNetworkGetUUIDString(network, uuid) == 0)
410         vshPrint(ctl, "%-15s %s\n", _("UUID:"), uuid);
411 
412     active = virNetworkIsActive(network);
413     if (active >= 0)
414         vshPrint(ctl, "%-15s %s\n", _("Active:"), active? _("yes") : _("no"));
415 
416     persistent = virNetworkIsPersistent(network);
417     if (persistent < 0)
418         vshPrint(ctl, "%-15s %s\n", _("Persistent:"), _("unknown"));
419     else
420         vshPrint(ctl, "%-15s %s\n", _("Persistent:"), persistent ? _("yes") : _("no"));
421 
422     if (virNetworkGetAutostart(network, &autostart) < 0)
423         vshPrint(ctl, "%-15s %s\n", _("Autostart:"), _("no autostart"));
424     else
425         vshPrint(ctl, "%-15s %s\n", _("Autostart:"), autostart ? _("yes") : _("no"));
426 
427     bridge = virNetworkGetBridgeName(network);
428     if (bridge)
429         vshPrint(ctl, "%-15s %s\n", _("Bridge:"), bridge);
430 
431     VIR_FREE(bridge);
432     return true;
433 }
434 
435 static int
virshNetworkSorter(const void * a,const void * b)436 virshNetworkSorter(const void *a, const void *b)
437 {
438     virNetworkPtr *na = (virNetworkPtr *) a;
439     virNetworkPtr *nb = (virNetworkPtr *) b;
440 
441     if (*na && !*nb)
442         return -1;
443 
444     if (!*na)
445         return *nb != NULL;
446 
447     return vshStrcasecmp(virNetworkGetName(*na),
448                       virNetworkGetName(*nb));
449 }
450 
451 struct virshNetworkList {
452     virNetworkPtr *nets;
453     size_t nnets;
454 };
455 
456 static void
virshNetworkListFree(struct virshNetworkList * list)457 virshNetworkListFree(struct virshNetworkList *list)
458 {
459     size_t i;
460 
461     if (list && list->nets) {
462         for (i = 0; i < list->nnets; i++) {
463             virshNetworkFree(list->nets[i]);
464         }
465         g_free(list->nets);
466     }
467     g_free(list);
468 }
469 
470 static struct virshNetworkList *
virshNetworkListCollect(vshControl * ctl,unsigned int flags)471 virshNetworkListCollect(vshControl *ctl,
472                         unsigned int flags)
473 {
474     struct virshNetworkList *list = g_new0(struct virshNetworkList, 1);
475     size_t i;
476     int ret;
477     char **names = NULL;
478     virNetworkPtr net;
479     bool success = false;
480     size_t deleted = 0;
481     int persistent;
482     int autostart;
483     int nActiveNets = 0;
484     int nInactiveNets = 0;
485     int nAllNets = 0;
486     virshControl *priv = ctl->privData;
487 
488     /* try the list with flags support (0.10.2 and later) */
489     if ((ret = virConnectListAllNetworks(priv->conn,
490                                          &list->nets,
491                                          flags)) >= 0) {
492         list->nnets = ret;
493         goto finished;
494     }
495 
496     /* check if the command is actually supported */
497     if (last_error && last_error->code == VIR_ERR_NO_SUPPORT)
498         goto fallback;
499 
500     if (last_error && last_error->code ==  VIR_ERR_INVALID_ARG) {
501         /* try the new API again but mask non-guaranteed flags */
502         unsigned int newflags = flags & (VIR_CONNECT_LIST_NETWORKS_ACTIVE |
503                                          VIR_CONNECT_LIST_NETWORKS_INACTIVE);
504 
505         vshResetLibvirtError();
506         if ((ret = virConnectListAllNetworks(priv->conn, &list->nets,
507                                              newflags)) >= 0) {
508             list->nnets = ret;
509             goto filter;
510         }
511     }
512 
513     /* there was an error during the first or second call */
514     vshError(ctl, "%s", _("Failed to list networks"));
515     goto cleanup;
516 
517 
518  fallback:
519     /* fall back to old method (0.10.1 and older) */
520     vshResetLibvirtError();
521 
522     /* Get the number of active networks */
523     if (!VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) ||
524         VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE)) {
525         if ((nActiveNets = virConnectNumOfNetworks(priv->conn)) < 0) {
526             vshError(ctl, "%s", _("Failed to get the number of active networks"));
527             goto cleanup;
528         }
529     }
530 
531     /* Get the number of inactive networks */
532     if (!VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) ||
533         VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_INACTIVE)) {
534         if ((nInactiveNets = virConnectNumOfDefinedNetworks(priv->conn)) < 0) {
535             vshError(ctl, "%s", _("Failed to get the number of inactive networks"));
536             goto cleanup;
537         }
538     }
539 
540     nAllNets = nActiveNets + nInactiveNets;
541 
542     if (nAllNets == 0)
543          return list;
544 
545     names = g_new0(char *, nAllNets);
546 
547     /* Retrieve a list of active network names */
548     if (!VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) ||
549         VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE)) {
550         if (virConnectListNetworks(priv->conn,
551                                    names, nActiveNets) < 0) {
552             vshError(ctl, "%s", _("Failed to list active networks"));
553             goto cleanup;
554         }
555     }
556 
557     /* Add the inactive networks to the end of the name list */
558     if (!VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) ||
559         VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE)) {
560         if (virConnectListDefinedNetworks(priv->conn,
561                                           &names[nActiveNets],
562                                           nInactiveNets) < 0) {
563             vshError(ctl, "%s", _("Failed to list inactive networks"));
564             goto cleanup;
565         }
566     }
567 
568     list->nets = g_new0(virNetworkPtr, nAllNets);
569     list->nnets = 0;
570 
571     /* get active networks */
572     for (i = 0; i < nActiveNets; i++) {
573         if (!(net = virNetworkLookupByName(priv->conn, names[i])))
574             continue;
575         list->nets[list->nnets++] = net;
576     }
577 
578     /* get inactive networks */
579     for (i = 0; i < nInactiveNets; i++) {
580         if (!(net = virNetworkLookupByName(priv->conn, names[i])))
581             continue;
582         list->nets[list->nnets++] = net;
583     }
584 
585     /* truncate networks that weren't found */
586     deleted = nAllNets - list->nnets;
587 
588  filter:
589     /* filter list the list if the list was acquired by fallback means */
590     for (i = 0; i < list->nnets; i++) {
591         net = list->nets[i];
592 
593         /* persistence filter */
594         if (VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_PERSISTENT)) {
595             if ((persistent = virNetworkIsPersistent(net)) < 0) {
596                 vshError(ctl, "%s", _("Failed to get network persistence info"));
597                 goto cleanup;
598             }
599 
600             if (!((VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_PERSISTENT) && persistent) ||
601                   (VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_TRANSIENT) && !persistent)))
602                 goto remove_entry;
603         }
604 
605         /* autostart filter */
606         if (VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_AUTOSTART)) {
607             if (virNetworkGetAutostart(net, &autostart) < 0) {
608                 vshError(ctl, "%s", _("Failed to get network autostart state"));
609                 goto cleanup;
610             }
611 
612             if (!((VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_AUTOSTART) && autostart) ||
613                   (VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_NO_AUTOSTART) && !autostart)))
614                 goto remove_entry;
615         }
616         /* the pool matched all filters, it may stay */
617         continue;
618 
619  remove_entry:
620         /* the pool has to be removed as it failed one of the filters */
621         g_clear_pointer(&list->nets[i], virshNetworkFree);
622         deleted++;
623     }
624 
625  finished:
626     /* sort the list */
627     if (list->nets && list->nnets)
628         qsort(list->nets, list->nnets,
629               sizeof(*list->nets), virshNetworkSorter);
630 
631     /* truncate the list if filter simulation deleted entries */
632     if (deleted)
633         VIR_SHRINK_N(list->nets, list->nnets, deleted);
634 
635     success = true;
636 
637  cleanup:
638     for (i = 0; i < nAllNets; i++)
639         VIR_FREE(names[i]);
640     VIR_FREE(names);
641 
642     if (!success) {
643         virshNetworkListFree(list);
644         list = NULL;
645     }
646 
647     return list;
648 }
649 
650 /*
651  * "net-list" command
652  */
653 static const vshCmdInfo info_network_list[] = {
654     {.name = "help",
655      .data = N_("list networks")
656     },
657     {.name = "desc",
658      .data = N_("Returns list of networks.")
659     },
660     {.name = NULL}
661 };
662 
663 static const vshCmdOptDef opts_network_list[] = {
664     {.name = "inactive",
665      .type = VSH_OT_BOOL,
666      .help = N_("list inactive networks")
667     },
668     {.name = "all",
669      .type = VSH_OT_BOOL,
670      .help = N_("list inactive & active networks")
671     },
672     {.name = "persistent",
673      .type = VSH_OT_BOOL,
674      .help = N_("list persistent networks")
675     },
676     {.name = "transient",
677      .type = VSH_OT_BOOL,
678      .help = N_("list transient networks")
679     },
680     {.name = "autostart",
681      .type = VSH_OT_BOOL,
682      .help = N_("list networks with autostart enabled")
683     },
684     {.name = "no-autostart",
685      .type = VSH_OT_BOOL,
686      .help = N_("list networks with autostart disabled")
687     },
688     {.name = "uuid",
689      .type = VSH_OT_BOOL,
690      .help = N_("list uuid's only")
691     },
692     {.name = "name",
693      .type = VSH_OT_BOOL,
694      .help = N_("list network names only")
695     },
696     {.name = "table",
697      .type = VSH_OT_BOOL,
698      .help = N_("list table (default)")
699     },
700     {.name = NULL}
701 };
702 
703 #define FILTER(NAME, FLAG) \
704     if (vshCommandOptBool(cmd, NAME)) \
705         flags |= (FLAG)
706 static bool
cmdNetworkList(vshControl * ctl,const vshCmd * cmd G_GNUC_UNUSED)707 cmdNetworkList(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED)
708 {
709     struct virshNetworkList *list = NULL;
710     size_t i;
711     bool ret = false;
712     bool optName = vshCommandOptBool(cmd, "name");
713     bool optTable = vshCommandOptBool(cmd, "table");
714     bool optUUID = vshCommandOptBool(cmd, "uuid");
715     char uuid[VIR_UUID_STRING_BUFLEN];
716     unsigned int flags = VIR_CONNECT_LIST_NETWORKS_ACTIVE;
717     g_autoptr(vshTable) table = NULL;
718 
719     if (vshCommandOptBool(cmd, "inactive"))
720         flags = VIR_CONNECT_LIST_NETWORKS_INACTIVE;
721 
722     if (vshCommandOptBool(cmd, "all"))
723         flags = VIR_CONNECT_LIST_NETWORKS_ACTIVE |
724                 VIR_CONNECT_LIST_NETWORKS_INACTIVE;
725 
726     FILTER("persistent", VIR_CONNECT_LIST_NETWORKS_PERSISTENT);
727     FILTER("transient", VIR_CONNECT_LIST_NETWORKS_TRANSIENT);
728 
729     FILTER("autostart", VIR_CONNECT_LIST_NETWORKS_AUTOSTART);
730     FILTER("no-autostart", VIR_CONNECT_LIST_NETWORKS_NO_AUTOSTART);
731 
732     if (optTable + optName + optUUID > 1) {
733         vshError(ctl, "%s",
734                  _("Only one argument from --table, --name and --uuid "
735                    "may be specified."));
736         return false;
737     }
738 
739     if (!optUUID && !optName)
740         optTable = true;
741 
742     if (!(list = virshNetworkListCollect(ctl, flags)))
743         return false;
744 
745     if (optTable) {
746         table = vshTableNew(_("Name"), _("State"), _("Autostart"),
747                             _("Persistent"), NULL);
748         if (!table)
749             goto cleanup;
750     }
751 
752     for (i = 0; i < list->nnets; i++) {
753         virNetworkPtr network = list->nets[i];
754         const char *autostartStr;
755         int is_autostart = 0;
756 
757         if (optTable) {
758             if (virNetworkGetAutostart(network, &is_autostart) < 0)
759                 autostartStr = _("no autostart");
760             else
761                 autostartStr = is_autostart ? _("yes") : _("no");
762 
763             if (vshTableRowAppend(table,
764                                   virNetworkGetName(network),
765                                   virNetworkIsActive(network) ?
766                                   _("active") : _("inactive"),
767                                   autostartStr,
768                                   virNetworkIsPersistent(network) ?
769                                   _("yes") : _("no"),
770                                   NULL) < 0)
771                 goto cleanup;
772         } else if (optUUID) {
773             if (virNetworkGetUUIDString(network, uuid) < 0) {
774                 vshError(ctl, "%s", _("Failed to get network's UUID"));
775                 goto cleanup;
776             }
777             vshPrint(ctl, "%s\n", uuid);
778         } else if (optName) {
779             vshPrint(ctl, "%s\n", virNetworkGetName(network));
780         }
781     }
782 
783     if (optTable)
784         vshTablePrintToStdout(table, ctl);
785 
786     ret = true;
787  cleanup:
788     virshNetworkListFree(list);
789     return ret;
790 }
791 #undef FILTER
792 
793 /*
794  * "net-name" command
795  */
796 static const vshCmdInfo info_network_name[] = {
797     {.name = "help",
798      .data = N_("convert a network UUID to network name")
799     },
800     {.name = "desc",
801      .data = ""
802     },
803     {.name = NULL}
804 };
805 
806 static const vshCmdOptDef opts_network_name[] = {
807     {.name = "network",
808      .type = VSH_OT_DATA,
809      .flags = VSH_OFLAG_REQ,
810      .completer = virshNetworkUUIDCompleter,
811      .help = N_("network uuid")
812     },
813     {.name = NULL}
814 };
815 
816 static bool
cmdNetworkName(vshControl * ctl,const vshCmd * cmd)817 cmdNetworkName(vshControl *ctl, const vshCmd *cmd)
818 {
819     g_autoptr(virshNetwork) network = NULL;
820 
821     if (!(network = virshCommandOptNetworkBy(ctl, cmd, NULL,
822                                              VIRSH_BYUUID)))
823         return false;
824 
825     vshPrint(ctl, "%s\n", virNetworkGetName(network));
826     return true;
827 }
828 
829 /*
830  * "net-start" command
831  */
832 static const vshCmdInfo info_network_start[] = {
833     {.name = "help",
834      .data = N_("start a (previously defined) inactive network")
835     },
836     {.name = "desc",
837      .data = N_("Start a network.")
838     },
839     {.name = NULL}
840 };
841 
842 static const vshCmdOptDef opts_network_start[] = {
843     VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_INACTIVE),
844     {.name = NULL}
845 };
846 
847 static bool
cmdNetworkStart(vshControl * ctl,const vshCmd * cmd)848 cmdNetworkStart(vshControl *ctl, const vshCmd *cmd)
849 {
850     g_autoptr(virshNetwork) network = NULL;
851     bool ret = true;
852     const char *name = NULL;
853 
854     if (!(network = virshCommandOptNetwork(ctl, cmd, &name)))
855          return false;
856 
857     if (virNetworkCreate(network) == 0) {
858         vshPrintExtra(ctl, _("Network %s started\n"), name);
859     } else {
860         vshError(ctl, _("Failed to start network %s"), name);
861         ret = false;
862     }
863     return ret;
864 }
865 
866 /*
867  * "net-undefine" command
868  */
869 static const vshCmdInfo info_network_undefine[] = {
870     {.name = "help",
871      .data = N_("undefine a persistent network")
872     },
873     {.name = "desc",
874      .data = N_("Undefine the configuration for a persistent network.")
875     },
876     {.name = NULL}
877 };
878 
879 static const vshCmdOptDef opts_network_undefine[] = {
880     VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_PERSISTENT),
881     {.name = NULL}
882 };
883 
884 static bool
cmdNetworkUndefine(vshControl * ctl,const vshCmd * cmd)885 cmdNetworkUndefine(vshControl *ctl, const vshCmd *cmd)
886 {
887     g_autoptr(virshNetwork) network = NULL;
888     bool ret = true;
889     const char *name;
890 
891     if (!(network = virshCommandOptNetwork(ctl, cmd, &name)))
892         return false;
893 
894     if (virNetworkUndefine(network) == 0) {
895         vshPrintExtra(ctl, _("Network %s has been undefined\n"), name);
896     } else {
897         vshError(ctl, _("Failed to undefine network %s"), name);
898         ret = false;
899     }
900 
901     return ret;
902 }
903 
904 /*
905  * "net-update" command
906  */
907 static const vshCmdInfo info_network_update[] = {
908     {.name = "help",
909      .data = N_("update parts of an existing network's configuration")
910     },
911     {.name = "desc",
912      .data = ""
913     },
914     {.name = NULL}
915 };
916 
917 static const vshCmdOptDef opts_network_update[] = {
918     VIRSH_COMMON_OPT_NETWORK_FULL(0),
919     {.name = "command",
920      .type = VSH_OT_DATA,
921      .flags = VSH_OFLAG_REQ,
922      .help = N_("type of update (add-first, add-last (add), delete, or modify)")
923     },
924     {.name = "section",
925      .type = VSH_OT_DATA,
926      .flags = VSH_OFLAG_REQ,
927      .help = N_("which section of network configuration to update")
928     },
929     {.name = "xml",
930      .type = VSH_OT_DATA,
931      .flags = VSH_OFLAG_REQ,
932      .completer = virshCompletePathLocalExisting,
933      .help = N_("name of file containing xml (or, if it starts with '<', the complete "
934                 "xml element itself) to add/modify, or to be matched for search")
935     },
936     {.name = "parent-index",
937      .type = VSH_OT_INT,
938      .help = N_("which parent object to search through")
939     },
940     VIRSH_COMMON_OPT_CONFIG(N_("affect next network startup")),
941     VIRSH_COMMON_OPT_LIVE(N_("affect running network")),
942     VIRSH_COMMON_OPT_CURRENT(N_("affect current state of network")),
943     {.name = NULL}
944 };
945 
946 VIR_ENUM_DECL(virshNetworkUpdateCommand);
947 VIR_ENUM_IMPL(virshNetworkUpdateCommand,
948               VIR_NETWORK_UPDATE_COMMAND_LAST,
949               "none", "modify", "delete", "add-last", "add-first");
950 
951 VIR_ENUM_DECL(virshNetworkSection);
952 VIR_ENUM_IMPL(virshNetworkSection,
953               VIR_NETWORK_SECTION_LAST,
954               "none", "bridge", "domain", "ip", "ip-dhcp-host",
955               "ip-dhcp-range", "forward", "forward-interface",
956               "forward-pf", "portgroup", "dns-host", "dns-txt",
957               "dns-srv");
958 
959 static bool
cmdNetworkUpdate(vshControl * ctl,const vshCmd * cmd)960 cmdNetworkUpdate(vshControl *ctl, const vshCmd *cmd)
961 {
962     bool ret = false;
963     g_autoptr(virshNetwork) network = NULL;
964     const char *commandStr = NULL;
965     const char *sectionStr = NULL;
966     int command, section, parentIndex = -1;
967     const char *xml = NULL;
968     g_autofree char *xmlFromFile = NULL;
969     bool config = vshCommandOptBool(cmd, "config");
970     bool live = vshCommandOptBool(cmd, "live");
971     unsigned int flags = VIR_NETWORK_UPDATE_AFFECT_CURRENT;
972 
973     VSH_EXCLUSIVE_OPTIONS("current", "live");
974     VSH_EXCLUSIVE_OPTIONS("current", "config");
975 
976     if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
977         return false;
978 
979     if (vshCommandOptStringReq(ctl, cmd, "command", &commandStr) < 0)
980         goto cleanup;
981 
982     if (STREQ(commandStr, "add")) {
983         /* "add" is a synonym for "add-last" */
984         command = VIR_NETWORK_UPDATE_COMMAND_ADD_LAST;
985     } else {
986         command = virshNetworkUpdateCommandTypeFromString(commandStr);
987         if (command <= 0 || command >= VIR_NETWORK_UPDATE_COMMAND_LAST) {
988             vshError(ctl, _("unrecognized command name '%s'"), commandStr);
989             goto cleanup;
990         }
991     }
992 
993     if (vshCommandOptStringReq(ctl, cmd, "section", &sectionStr) < 0)
994         goto cleanup;
995 
996     section = virshNetworkSectionTypeFromString(sectionStr);
997     if (section <= 0 || section >= VIR_NETWORK_SECTION_LAST) {
998         vshError(ctl, _("unrecognized section name '%s'"), sectionStr);
999         goto cleanup;
1000     }
1001 
1002     if (vshCommandOptInt(ctl, cmd, "parent-index", &parentIndex) < 0)
1003         goto cleanup;
1004 
1005     /* The goal is to have a full xml element in the "xml"
1006      * string. This is provided in the --xml option, either directly
1007      * (detected by the first character being "<"), or indirectly by
1008      * supplying a filename (first character isn't "<") that contains
1009      * the desired xml.
1010      */
1011 
1012     if (vshCommandOptStringReq(ctl, cmd, "xml", &xml) < 0)
1013         goto cleanup;
1014 
1015     if (*xml != '<') {
1016         /* contents of xmldata is actually the name of a file that
1017          * contains the xml.
1018          */
1019         if (virFileReadAll(xml, VSH_MAX_XML_FILE, &xmlFromFile) < 0)
1020             goto cleanup;
1021         /* NB: the original xml is just a const char * that points
1022          * to a string owned by the vshCmd object, and will be freed
1023          * by vshCommandFree, so it's safe to lose its pointer here.
1024          */
1025         xml = xmlFromFile;
1026     }
1027 
1028     if (config)
1029         flags |= VIR_NETWORK_UPDATE_AFFECT_CONFIG;
1030     if (live)
1031         flags |= VIR_NETWORK_UPDATE_AFFECT_LIVE;
1032 
1033     if (virNetworkUpdate(network, command,
1034                          section, parentIndex, xml, flags) < 0) {
1035         vshError(ctl, _("Failed to update network %s"),
1036                  virNetworkGetName(network));
1037         goto cleanup;
1038     }
1039 
1040     if (config) {
1041         if (live)
1042             vshPrintExtra(ctl, _("Updated network %s persistent config and "
1043                                  "live state"),
1044                           virNetworkGetName(network));
1045         else
1046             vshPrintExtra(ctl, _("Updated network %s persistent config"),
1047                           virNetworkGetName(network));
1048     } else if (live) {
1049         vshPrintExtra(ctl, _("Updated network %s live state"),
1050                       virNetworkGetName(network));
1051     } else if (virNetworkIsActive(network)) {
1052         vshPrintExtra(ctl, _("Updated network %s live state"),
1053                       virNetworkGetName(network));
1054     } else {
1055         vshPrintExtra(ctl, _("Updated network %s persistent config"),
1056                       virNetworkGetName(network));
1057     }
1058 
1059     ret = true;
1060  cleanup:
1061     vshReportError(ctl);
1062     return ret;
1063 }
1064 
1065 /*
1066  * "net-uuid" command
1067  */
1068 static const vshCmdInfo info_network_uuid[] = {
1069     {.name = "help",
1070      .data = N_("convert a network name to network UUID")
1071     },
1072     {.name = "desc",
1073      .data = ""
1074     },
1075     {.name = NULL}
1076 };
1077 
1078 static const vshCmdOptDef opts_network_uuid[] = {
1079     VIRSH_COMMON_OPT_NETWORK(N_("network name"), 0),
1080     {.name = NULL}
1081 };
1082 
1083 static bool
cmdNetworkUuid(vshControl * ctl,const vshCmd * cmd)1084 cmdNetworkUuid(vshControl *ctl, const vshCmd *cmd)
1085 {
1086     g_autoptr(virshNetwork) network = NULL;
1087     char uuid[VIR_UUID_STRING_BUFLEN];
1088 
1089     if (!(network = virshCommandOptNetworkBy(ctl, cmd, NULL,
1090                                              VIRSH_BYNAME)))
1091         return false;
1092 
1093     if (virNetworkGetUUIDString(network, uuid) != -1)
1094         vshPrint(ctl, "%s\n", uuid);
1095     else
1096         vshError(ctl, "%s", _("failed to get network UUID"));
1097 
1098     return true;
1099 }
1100 
1101 /*
1102  * "net-edit" command
1103  */
1104 static const vshCmdInfo info_network_edit[] = {
1105     {.name = "help",
1106      .data = N_("edit XML configuration for a network")
1107     },
1108     {.name = "desc",
1109      .data = N_("Edit the XML configuration for a network.")
1110     },
1111     {.name = NULL}
1112 };
1113 
1114 static const vshCmdOptDef opts_network_edit[] = {
1115     VIRSH_COMMON_OPT_NETWORK_FULL(0),
1116     {.name = NULL}
1117 };
1118 
virshNetworkGetXMLDesc(virNetworkPtr network)1119 static char *virshNetworkGetXMLDesc(virNetworkPtr network)
1120 {
1121     unsigned int flags = VIR_NETWORK_XML_INACTIVE;
1122     char *doc = virNetworkGetXMLDesc(network, flags);
1123 
1124     if (!doc && last_error->code == VIR_ERR_INVALID_ARG) {
1125         /* The server side libvirt doesn't support
1126          * VIR_NETWORK_XML_INACTIVE, so retry without it.
1127          */
1128         vshResetLibvirtError();
1129         flags &= ~VIR_NETWORK_XML_INACTIVE;
1130         doc = virNetworkGetXMLDesc(network, flags);
1131     }
1132     return doc;
1133 }
1134 
1135 static bool
cmdNetworkEdit(vshControl * ctl,const vshCmd * cmd)1136 cmdNetworkEdit(vshControl *ctl, const vshCmd *cmd)
1137 {
1138     bool ret = false;
1139     g_autoptr(virshNetwork) network = NULL;
1140     g_autoptr(virshNetwork) network_edited = NULL;
1141     virshControl *priv = ctl->privData;
1142 
1143     network = virshCommandOptNetwork(ctl, cmd, NULL);
1144     if (network == NULL)
1145         goto cleanup;
1146 
1147 #define EDIT_GET_XML virshNetworkGetXMLDesc(network)
1148 #define EDIT_NOT_CHANGED \
1149     do { \
1150         vshPrintExtra(ctl, _("Network %s XML configuration not changed.\n"), \
1151                       virNetworkGetName(network)); \
1152         ret = true; \
1153         goto edit_cleanup; \
1154     } while (0)
1155 #define EDIT_DEFINE \
1156     (network_edited = virNetworkDefineXML(priv->conn, doc_edited))
1157 #include "virsh-edit.c"
1158 
1159     vshPrintExtra(ctl, _("Network %s XML configuration edited.\n"),
1160                   virNetworkGetName(network_edited));
1161 
1162     ret = true;
1163 
1164  cleanup:
1165     return ret;
1166 }
1167 
1168 
1169 /*
1170  * "net-event" command
1171  */
1172 VIR_ENUM_DECL(virshNetworkEvent);
1173 VIR_ENUM_IMPL(virshNetworkEvent,
1174               VIR_NETWORK_EVENT_LAST,
1175               N_("Defined"),
1176               N_("Undefined"),
1177               N_("Started"),
1178               N_("Stopped"));
1179 
1180 static const char *
virshNetworkEventToString(int event)1181 virshNetworkEventToString(int event)
1182 {
1183     const char *str = virshNetworkEventTypeToString(event);
1184     return str ? _(str) : _("unknown");
1185 }
1186 
1187 struct virshNetEventData {
1188     vshControl *ctl;
1189     bool loop;
1190     bool timestamp;
1191     int count;
1192     virshNetworkEventCallback *cb;
1193 };
1194 typedef struct virshNetEventData virshNetEventData;
1195 
1196 VIR_ENUM_DECL(virshNetworkEventId);
1197 VIR_ENUM_IMPL(virshNetworkEventId,
1198               VIR_NETWORK_EVENT_ID_LAST,
1199               "lifecycle");
1200 
1201 static void
vshEventLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED,virNetworkPtr net,int event,int detail G_GNUC_UNUSED,void * opaque)1202 vshEventLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED,
1203                        virNetworkPtr net,
1204                        int event,
1205                        int detail G_GNUC_UNUSED,
1206                        void *opaque)
1207 {
1208     virshNetEventData *data = opaque;
1209 
1210     if (!data->loop && data->count)
1211         return;
1212 
1213     if (data->timestamp) {
1214         char timestamp[VIR_TIME_STRING_BUFLEN];
1215 
1216         if (virTimeStringNowRaw(timestamp) < 0)
1217             timestamp[0] = '\0';
1218 
1219         vshPrint(data->ctl, _("%s: event 'lifecycle' for network %s: %s\n"),
1220                  timestamp,
1221                  virNetworkGetName(net), virshNetworkEventToString(event));
1222     } else {
1223         vshPrint(data->ctl, _("event 'lifecycle' for network %s: %s\n"),
1224                  virNetworkGetName(net), virshNetworkEventToString(event));
1225     }
1226 
1227     data->count++;
1228     if (!data->loop)
1229         vshEventDone(data->ctl);
1230 }
1231 
1232 virshNetworkEventCallback virshNetworkEventCallbacks[] = {
1233     { "lifecycle",
1234       VIR_NETWORK_EVENT_CALLBACK(vshEventLifecyclePrint), },
1235 };
1236 G_STATIC_ASSERT(VIR_NETWORK_EVENT_ID_LAST == G_N_ELEMENTS(virshNetworkEventCallbacks));
1237 
1238 static const vshCmdInfo info_network_event[] = {
1239     {.name = "help",
1240      .data = N_("Network Events")
1241     },
1242     {.name = "desc",
1243      .data = N_("List event types, or wait for network events to occur")
1244     },
1245     {.name = NULL}
1246 };
1247 
1248 static const vshCmdOptDef opts_network_event[] = {
1249     VIRSH_COMMON_OPT_NETWORK_OT_STRING(N_("filter by network name or uuid"), 0),
1250     {.name = "event",
1251      .type = VSH_OT_STRING,
1252      .completer = virshNetworkEventNameCompleter,
1253      .help = N_("which event type to wait for")
1254     },
1255     {.name = "loop",
1256      .type = VSH_OT_BOOL,
1257      .help = N_("loop until timeout or interrupt, rather than one-shot")
1258     },
1259     {.name = "timeout",
1260      .type = VSH_OT_INT,
1261      .help = N_("timeout seconds")
1262     },
1263     {.name = "list",
1264      .type = VSH_OT_BOOL,
1265      .help = N_("list valid event types")
1266     },
1267     {.name = "timestamp",
1268      .type = VSH_OT_BOOL,
1269      .help = N_("show timestamp for each printed event")
1270     },
1271     {.name = NULL}
1272 };
1273 
1274 static bool
cmdNetworkEvent(vshControl * ctl,const vshCmd * cmd)1275 cmdNetworkEvent(vshControl *ctl, const vshCmd *cmd)
1276 {
1277     g_autoptr(virshNetwork) net = NULL;
1278     bool ret = false;
1279     int eventId = -1;
1280     int timeout = 0;
1281     virshNetEventData data;
1282     const char *eventName = NULL;
1283     int event;
1284     virshControl *priv = ctl->privData;
1285 
1286     if (vshCommandOptBool(cmd, "list")) {
1287         size_t i;
1288 
1289         for (i = 0; i < VIR_NETWORK_EVENT_ID_LAST; i++)
1290             vshPrint(ctl, "%s\n", virshNetworkEventCallbacks[i].name);
1291         return true;
1292     }
1293 
1294     if (vshCommandOptStringReq(ctl, cmd, "event", &eventName) < 0)
1295         return false;
1296     if (!eventName) {
1297         vshError(ctl, "%s", _("either --list or --event <type> is required"));
1298         return false;
1299     }
1300     for (event = 0; event < VIR_NETWORK_EVENT_ID_LAST; event++)
1301         if (STREQ(eventName, virshNetworkEventCallbacks[event].name))
1302             break;
1303     if (event == VIR_NETWORK_EVENT_ID_LAST) {
1304         vshError(ctl, _("unknown event type %s"), eventName);
1305         return false;
1306     }
1307 
1308     data.ctl = ctl;
1309     data.loop = vshCommandOptBool(cmd, "loop");
1310     data.timestamp = vshCommandOptBool(cmd, "timestamp");
1311     data.count = 0;
1312     data.cb = &virshNetworkEventCallbacks[event];
1313     if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
1314         return false;
1315 
1316     if (vshCommandOptBool(cmd, "network"))
1317         net = virshCommandOptNetwork(ctl, cmd, NULL);
1318     if (vshEventStart(ctl, timeout) < 0)
1319         goto cleanup;
1320 
1321     if ((eventId = virConnectNetworkEventRegisterAny(priv->conn, net, event,
1322                                                      data.cb->cb,
1323                                                      &data, NULL)) < 0)
1324         goto cleanup;
1325     switch (vshEventWait(ctl)) {
1326     case VSH_EVENT_INTERRUPT:
1327         vshPrint(ctl, "%s", _("event loop interrupted\n"));
1328         break;
1329     case VSH_EVENT_TIMEOUT:
1330         vshPrint(ctl, "%s", _("event loop timed out\n"));
1331         break;
1332     case VSH_EVENT_DONE:
1333         break;
1334     default:
1335         goto cleanup;
1336     }
1337     vshPrint(ctl, _("events received: %d\n"), data.count);
1338     if (data.count)
1339         ret = true;
1340 
1341  cleanup:
1342     vshEventCleanup(ctl);
1343     if (eventId >= 0 &&
1344         virConnectNetworkEventDeregisterAny(priv->conn, eventId) < 0)
1345         ret = false;
1346     return ret;
1347 }
1348 
1349 
1350 /*
1351  * "net-dhcp-leases" command
1352  */
1353 static const vshCmdInfo info_network_dhcp_leases[] = {
1354     {.name = "help",
1355      .data = N_("print lease info for a given network")
1356     },
1357     {.name = "desc",
1358      .data = N_("Print lease info for a given network")
1359     },
1360     {.name = NULL}
1361 };
1362 
1363 static const vshCmdOptDef opts_network_dhcp_leases[] = {
1364     VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_ACTIVE),
1365     {.name = "mac",
1366      .type = VSH_OT_STRING,
1367      .flags = VSH_OFLAG_NONE,
1368      .help = N_("MAC address"),
1369      .completer = virshNetworkDhcpMacCompleter,
1370     },
1371     {.name = NULL}
1372 };
1373 
1374 static int
virshNetworkDHCPLeaseSorter(const void * a,const void * b)1375 virshNetworkDHCPLeaseSorter(const void *a, const void *b)
1376 {
1377     virNetworkDHCPLeasePtr *lease1 = (virNetworkDHCPLeasePtr *) a;
1378     virNetworkDHCPLeasePtr *lease2 = (virNetworkDHCPLeasePtr *) b;
1379 
1380     if (*lease1 && !*lease2)
1381         return -1;
1382 
1383     if (!*lease1)
1384         return *lease2 != NULL;
1385 
1386     return vshStrcasecmp((*lease1)->mac, (*lease2)->mac);
1387 }
1388 
1389 static bool
cmdNetworkDHCPLeases(vshControl * ctl,const vshCmd * cmd)1390 cmdNetworkDHCPLeases(vshControl *ctl, const vshCmd *cmd)
1391 {
1392     const char *name = NULL;
1393     const char *mac = NULL;
1394     virNetworkDHCPLeasePtr *leases = NULL;
1395     int nleases = 0;
1396     bool ret = false;
1397     size_t i;
1398     unsigned int flags = 0;
1399     g_autoptr(virshNetwork) network = NULL;
1400     g_autoptr(vshTable) table = NULL;
1401 
1402     if (vshCommandOptStringReq(ctl, cmd, "mac", &mac) < 0)
1403         return false;
1404 
1405     if (!(network = virshCommandOptNetwork(ctl, cmd, &name)))
1406         return false;
1407 
1408     if ((nleases = virNetworkGetDHCPLeases(network, mac, &leases, flags)) < 0) {
1409         vshError(ctl, _("Failed to get leases info for %s"), name);
1410         goto cleanup;
1411     }
1412 
1413     /* Sort the list according to MAC Address/IAID */
1414     qsort(leases, nleases, sizeof(*leases), virshNetworkDHCPLeaseSorter);
1415 
1416     table = vshTableNew(_("Expiry Time"), _("MAC address"), _("Protocol"),
1417                         _("IP address"), _("Hostname"), _("Client ID or DUID"),
1418                         NULL);
1419     if (!table)
1420         goto cleanup;
1421 
1422     for (i = 0; i < nleases; i++) {
1423         const char *typestr = NULL;
1424         g_autofree char *cidr_format = NULL;
1425         virNetworkDHCPLeasePtr lease = leases[i];
1426         g_autoptr(GDateTime) then = g_date_time_new_from_unix_local(lease->expirytime);
1427         g_autofree char *thenstr = NULL;
1428 
1429         thenstr = g_date_time_format(then, "%Y-%m-%d %H:%M:%S");
1430 
1431         if (lease->type == VIR_IP_ADDR_TYPE_IPV4)
1432             typestr = "ipv4";
1433         else if (lease->type == VIR_IP_ADDR_TYPE_IPV6)
1434             typestr = "ipv6";
1435 
1436         cidr_format = g_strdup_printf("%s/%d", lease->ipaddr, lease->prefix);
1437 
1438         if (vshTableRowAppend(table,
1439                               thenstr,
1440                               NULLSTR_MINUS(lease->mac),
1441                               NULLSTR_MINUS(typestr),
1442                               NULLSTR_MINUS(cidr_format),
1443                               NULLSTR_MINUS(lease->hostname),
1444                               NULLSTR_MINUS(lease->clientid),
1445                               NULL) < 0)
1446             goto cleanup;
1447     }
1448 
1449     vshTablePrintToStdout(table, ctl);
1450 
1451     ret = true;
1452 
1453  cleanup:
1454     if (leases) {
1455         for (i = 0; i < nleases; i++)
1456             virNetworkDHCPLeaseFree(leases[i]);
1457         VIR_FREE(leases);
1458     }
1459     return ret;
1460 }
1461 
1462 /*
1463  * "net-port-create" command
1464  */
1465 static const vshCmdInfo info_network_port_create[] = {
1466     {.name = "help",
1467      .data = N_("create a network port from an XML file")
1468     },
1469     {.name = "desc",
1470      .data = N_("Create a network port.")
1471     },
1472     {.name = NULL}
1473 };
1474 
1475 static const vshCmdOptDef opts_network_port_create[] = {
1476     VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_ACTIVE),
1477     VIRSH_COMMON_OPT_FILE(N_("file containing an XML network port description")),
1478     {.name = "validate",
1479      .type = VSH_OT_BOOL,
1480      .help = N_("validate the XML against the schema")
1481     },
1482     {.name = NULL}
1483 };
1484 
1485 static bool
cmdNetworkPortCreate(vshControl * ctl,const vshCmd * cmd)1486 cmdNetworkPortCreate(vshControl *ctl, const vshCmd *cmd)
1487 {
1488     virNetworkPortPtr port = NULL;
1489     const char *from = NULL;
1490     bool ret = false;
1491     char *buffer = NULL;
1492     g_autoptr(virshNetwork) network = NULL;
1493     unsigned int flags = 0;
1494 
1495     network = virshCommandOptNetwork(ctl, cmd, NULL);
1496     if (network == NULL)
1497         goto cleanup;
1498 
1499     if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
1500         goto cleanup;
1501 
1502     if (vshCommandOptBool(cmd, "validate"))
1503         flags |= VIR_NETWORK_PORT_CREATE_VALIDATE;
1504 
1505     if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) {
1506         vshSaveLibvirtError();
1507         goto cleanup;
1508     }
1509 
1510     port = virNetworkPortCreateXML(network, buffer, flags);
1511 
1512     if (port != NULL) {
1513         char uuidstr[VIR_UUID_STRING_BUFLEN];
1514         virNetworkPortGetUUIDString(port, uuidstr);
1515         vshPrintExtra(ctl, _("Network port %s created from %s\n"),
1516                       uuidstr, from);
1517     } else {
1518         vshError(ctl, _("Failed to create network from %s"), from);
1519         goto cleanup;
1520     }
1521 
1522     ret = true;
1523  cleanup:
1524     VIR_FREE(buffer);
1525     if (port)
1526         virNetworkPortFree(port);
1527     return ret;
1528 }
1529 
1530 /*
1531  * "net-port-dumpxml" command
1532  */
1533 static const vshCmdInfo info_network_port_dumpxml[] = {
1534     {.name = "help",
1535      .data = N_("network port information in XML")
1536     },
1537     {.name = "desc",
1538      .data = N_("Output the network port information as an XML dump to stdout.")
1539     },
1540     {.name = NULL}
1541 };
1542 
1543 static const vshCmdOptDef opts_network_port_dumpxml[] = {
1544     VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_ACTIVE),
1545     VIRSH_COMMON_OPT_NETWORK_PORT(0),
1546     {.name = NULL}
1547 };
1548 
1549 static bool
cmdNetworkPortDumpXML(vshControl * ctl,const vshCmd * cmd)1550 cmdNetworkPortDumpXML(vshControl *ctl, const vshCmd *cmd)
1551 {
1552     g_autoptr(virshNetwork) network = NULL;
1553     virNetworkPortPtr port = NULL;
1554     bool ret = true;
1555     g_autofree char *dump = NULL;
1556     unsigned int flags = 0;
1557 
1558     if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
1559         goto cleanup;
1560 
1561     if (!(port = virshCommandOptNetworkPort(ctl, cmd, network, NULL)))
1562         goto cleanup;
1563 
1564     dump = virNetworkPortGetXMLDesc(port, flags);
1565 
1566     if (dump != NULL) {
1567         vshPrint(ctl, "%s", dump);
1568     } else {
1569         ret = false;
1570     }
1571 
1572  cleanup:
1573     if (port)
1574         virNetworkPortFree(port);
1575     return ret;
1576 }
1577 
1578 
1579 /*
1580  * "net-port-delete" command
1581  */
1582 static const vshCmdInfo info_network_port_delete[] = {
1583     {.name = "help",
1584      .data = N_("delete the specified network port")
1585     },
1586     {.name = "desc",
1587      .data = N_("Delete the specified network port.")
1588     },
1589     {.name = NULL}
1590 };
1591 
1592 static const vshCmdOptDef opts_network_port_delete[] = {
1593     VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_ACTIVE),
1594     VIRSH_COMMON_OPT_NETWORK_PORT(0),
1595     {.name = NULL}
1596 };
1597 
1598 static bool
cmdNetworkPortDelete(vshControl * ctl,const vshCmd * cmd)1599 cmdNetworkPortDelete(vshControl *ctl, const vshCmd *cmd)
1600 {
1601     g_autoptr(virshNetwork) network = NULL;
1602     virNetworkPortPtr port = NULL;
1603     bool ret = true;
1604     char uuidstr[VIR_UUID_STRING_BUFLEN];
1605 
1606     if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
1607         goto cleanup;
1608 
1609     if (!(port = virshCommandOptNetworkPort(ctl, cmd, network, NULL)))
1610         goto cleanup;
1611 
1612     if (virNetworkPortGetUUIDString(port, uuidstr) < 0)
1613         goto cleanup;
1614 
1615     if (virNetworkPortDelete(port, 0) < 0) {
1616         vshError(ctl, _("Failed to delete network port %s"), uuidstr);
1617         goto cleanup;
1618     } else {
1619         vshPrintExtra(ctl, _("Network port %s deleted\n"), uuidstr);
1620     }
1621 
1622     ret = true;
1623  cleanup:
1624     if (port)
1625         virNetworkPortFree(port);
1626     return ret;
1627 }
1628 
1629 
1630 static int
virshNetworkPortSorter(const void * a,const void * b)1631 virshNetworkPortSorter(const void *a, const void *b)
1632 {
1633     virNetworkPortPtr *na = (virNetworkPortPtr *) a;
1634     virNetworkPortPtr *nb = (virNetworkPortPtr *) b;
1635     unsigned char uuida[VIR_UUID_BUFLEN];
1636     unsigned char uuidb[VIR_UUID_BUFLEN];
1637 
1638     if (*na && !*nb)
1639         return -1;
1640 
1641     if (!*na)
1642         return *nb != NULL;
1643 
1644     if (virNetworkPortGetUUID(*na, uuida) < 0 ||
1645         virNetworkPortGetUUID(*nb, uuidb) < 0)
1646         return -1;
1647 
1648     return memcmp(uuida, uuidb, VIR_UUID_BUFLEN);
1649 }
1650 
1651 struct virshNetworkPortList {
1652     virNetworkPortPtr *ports;
1653     size_t nports;
1654 };
1655 
1656 static void
virshNetworkPortListFree(struct virshNetworkPortList * list)1657 virshNetworkPortListFree(struct virshNetworkPortList *list)
1658 {
1659     size_t i;
1660 
1661     if (list && list->ports) {
1662         for (i = 0; i < list->nports; i++) {
1663             if (list->ports[i])
1664                 virNetworkPortFree(list->ports[i]);
1665         }
1666         g_free(list->ports);
1667     }
1668     g_free(list);
1669 }
1670 
1671 static struct virshNetworkPortList *
virshNetworkPortListCollect(vshControl * ctl,const vshCmd * cmd,unsigned int flags)1672 virshNetworkPortListCollect(vshControl *ctl,
1673                             const vshCmd *cmd,
1674                             unsigned int flags)
1675 {
1676     struct virshNetworkPortList *list = g_new0(struct virshNetworkPortList, 1);
1677     int ret;
1678     g_autoptr(virshNetwork) network = NULL;
1679     bool success = false;
1680 
1681     if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
1682         goto cleanup;
1683 
1684     if ((ret = virNetworkListAllPorts(network,
1685                                       &list->ports,
1686                                       flags)) < 0)
1687         goto cleanup;
1688 
1689     list->nports = ret;
1690 
1691     /* sort the list */
1692     if (list->ports && list->nports)
1693         qsort(list->ports, list->nports,
1694               sizeof(*list->ports), virshNetworkPortSorter);
1695 
1696     success = true;
1697 
1698  cleanup:
1699     if (!success) {
1700         virshNetworkPortListFree(list);
1701         list = NULL;
1702     }
1703 
1704     return list;
1705 }
1706 
1707 /*
1708  * "net-list" command
1709  */
1710 static const vshCmdInfo info_network_port_list[] = {
1711     {.name = "help",
1712      .data = N_("list network ports")
1713     },
1714     {.name = "desc",
1715      .data = N_("Returns list of network ports.")
1716     },
1717     {.name = NULL}
1718 };
1719 
1720 static const vshCmdOptDef opts_network_port_list[] = {
1721     VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_ACTIVE),
1722     {.name = "uuid",
1723      .type = VSH_OT_BOOL,
1724      .help = N_("list uuid's only")
1725     },
1726     {.name = "table",
1727      .type = VSH_OT_BOOL,
1728      .help = N_("list table (default)")
1729     },
1730     {.name = NULL}
1731 };
1732 
1733 static bool
cmdNetworkPortList(vshControl * ctl,const vshCmd * cmd)1734 cmdNetworkPortList(vshControl *ctl, const vshCmd *cmd)
1735 {
1736     struct virshNetworkPortList *list = NULL;
1737     size_t i;
1738     bool ret = false;
1739     bool optTable = vshCommandOptBool(cmd, "table");
1740     bool optUUID = vshCommandOptBool(cmd, "uuid");
1741     char uuid[VIR_UUID_STRING_BUFLEN];
1742     unsigned int flags = 0;
1743     g_autoptr(vshTable) table = NULL;
1744 
1745     if (optTable + optUUID > 1) {
1746         vshError(ctl, "%s",
1747                  _("Only one argument from --table and --uuid "
1748                    "may be specified."));
1749         return false;
1750     }
1751 
1752     if (!optUUID)
1753         optTable = true;
1754 
1755     if (!(list = virshNetworkPortListCollect(ctl, cmd, flags)))
1756         return false;
1757 
1758     if (optTable) {
1759         table = vshTableNew(_("UUID"), NULL);
1760         if (!table)
1761             goto cleanup;
1762     }
1763 
1764     for (i = 0; i < list->nports; i++) {
1765         virNetworkPortPtr port = list->ports[i];
1766 
1767         if (virNetworkPortGetUUIDString(port, uuid) < 0) {
1768             vshError(ctl, "%s", _("Failed to get network's UUID"));
1769             goto cleanup;
1770         }
1771         if (optTable) {
1772             if (vshTableRowAppend(table, uuid, NULL) < 0)
1773                 goto cleanup;
1774         } else if (optUUID) {
1775             vshPrint(ctl, "%s\n", uuid);
1776         }
1777     }
1778 
1779     if (optTable)
1780         vshTablePrintToStdout(table, ctl);
1781 
1782     ret = true;
1783  cleanup:
1784     virshNetworkPortListFree(list);
1785     return ret;
1786 }
1787 
1788 
1789 const vshCmdDef networkCmds[] = {
1790     {.name = "net-autostart",
1791      .handler = cmdNetworkAutostart,
1792      .opts = opts_network_autostart,
1793      .info = info_network_autostart,
1794      .flags = 0
1795     },
1796     {.name = "net-create",
1797      .handler = cmdNetworkCreate,
1798      .opts = opts_network_create,
1799      .info = info_network_create,
1800      .flags = 0
1801     },
1802     {.name = "net-define",
1803      .handler = cmdNetworkDefine,
1804      .opts = opts_network_define,
1805      .info = info_network_define,
1806      .flags = 0
1807     },
1808     {.name = "net-destroy",
1809      .handler = cmdNetworkDestroy,
1810      .opts = opts_network_destroy,
1811      .info = info_network_destroy,
1812      .flags = 0
1813     },
1814     {.name = "net-dhcp-leases",
1815      .handler = cmdNetworkDHCPLeases,
1816      .opts = opts_network_dhcp_leases,
1817      .info = info_network_dhcp_leases,
1818      .flags = 0,
1819     },
1820     {.name = "net-dumpxml",
1821      .handler = cmdNetworkDumpXML,
1822      .opts = opts_network_dumpxml,
1823      .info = info_network_dumpxml,
1824      .flags = 0
1825     },
1826     {.name = "net-edit",
1827      .handler = cmdNetworkEdit,
1828      .opts = opts_network_edit,
1829      .info = info_network_edit,
1830      .flags = 0
1831     },
1832     {.name = "net-event",
1833      .handler = cmdNetworkEvent,
1834      .opts = opts_network_event,
1835      .info = info_network_event,
1836      .flags = 0
1837     },
1838     {.name = "net-info",
1839      .handler = cmdNetworkInfo,
1840      .opts = opts_network_info,
1841      .info = info_network_info,
1842      .flags = 0
1843     },
1844     {.name = "net-list",
1845      .handler = cmdNetworkList,
1846      .opts = opts_network_list,
1847      .info = info_network_list,
1848      .flags = 0
1849     },
1850     {.name = "net-name",
1851      .handler = cmdNetworkName,
1852      .opts = opts_network_name,
1853      .info = info_network_name,
1854      .flags = 0
1855     },
1856     {.name = "net-start",
1857      .handler = cmdNetworkStart,
1858      .opts = opts_network_start,
1859      .info = info_network_start,
1860      .flags = 0
1861     },
1862     {.name = "net-undefine",
1863      .handler = cmdNetworkUndefine,
1864      .opts = opts_network_undefine,
1865      .info = info_network_undefine,
1866      .flags = 0
1867     },
1868     {.name = "net-update",
1869      .handler = cmdNetworkUpdate,
1870      .opts = opts_network_update,
1871      .info = info_network_update,
1872      .flags = 0
1873     },
1874     {.name = "net-uuid",
1875      .handler = cmdNetworkUuid,
1876      .opts = opts_network_uuid,
1877      .info = info_network_uuid,
1878      .flags = 0
1879     },
1880     {.name = "net-port-list",
1881      .handler = cmdNetworkPortList,
1882      .opts = opts_network_port_list,
1883      .info = info_network_port_list,
1884      .flags = 0
1885     },
1886     {.name = "net-port-create",
1887      .handler = cmdNetworkPortCreate,
1888      .opts = opts_network_port_create,
1889      .info = info_network_port_create,
1890      .flags = 0
1891     },
1892     {.name = "net-port-dumpxml",
1893      .handler = cmdNetworkPortDumpXML,
1894      .opts = opts_network_port_dumpxml,
1895      .info = info_network_port_dumpxml,
1896      .flags = 0
1897     },
1898     {.name = "net-port-delete",
1899      .handler = cmdNetworkPortDelete,
1900      .opts = opts_network_port_delete,
1901      .info = info_network_port_delete,
1902      .flags = 0
1903     },
1904     {.name = NULL}
1905 };
1906