Re: [RFCv3 00/25] RFC: Generate parsexml/formatbuf functions based on directives

2021-06-27 Thread Shi Lei
Polite ping.

On 2021-04-22 at 15:25, Shi Lei wrote:
>V2 here: 
>[https://listman.redhat.com/archives/libvir-list/2020-September/msg00204.html]
>
>Differ from V2:
>
>  * Add tests for xmlgen to illustrate all the different features we can use
>    and make sure its proper functions in the future.
>
>  * Add docs/xmlgen.rst to explain the usage of all directives and the tool
>    itself.
>
>  * Now xmlgen can check whether the first item of enum ends with _NONE, 
>_DEFAULT
>    or _ABSENT and generate proper code. So we no longer need to add extra
>    'default' item for enum.
>
>  * Now xmlgen can provide extra [tips] when we execute its command-line to 
>show
>    generated code for preview.
>
>  * Enable/disable hooks by macros rather than by special directives.
>
>  * Add virStrToBoolYesNo/virStrToBoolTrueFalse/virStrToBoolOnOff
>    and explicitly check both the true and false values.
>
>  * Stronger check for python3-clang and libclang.so to make sure it can work.
>
>  * Add python3-clang to the libvirt.spec.in and the mingw-libvirt.spec.in.
>
>  Thanks!
>
>
>Shi Lei (25):
>  scripts: Add a tool to generate xml parse/format functions
>  maint: Check python3-clang and libclang
>  maint: Call xmlgen automatically when c-head-files change
>  docs: Add xmlgen.rst to explain how to use it
>  build-aux: Only check *.[ch] for sc_prohibit_useless_translation
>  tests: Add tests for xmlgen
>  util: Add some xml-helper-functions to cooperate with xmlgen
>  util: Add helper aliases and functions for 'bool' and 'time_t' to cooperate 
>with xmlgen
>  util: Add parsexml/formatbuf helper functions for virSocketAddr
>  util: Add virUUID type and parse/format functions
>  conf: Extract error-checking code from virNetworkDNSTxtDefParseXML
>  conf: Replace virNetworkDNSTxtDefParseXML(hardcoded) with namesake(generated)
>  conf: Generate virNetworkDNSTxtDefFormatBuf
>  conf: Extract error-checking code from virNetworkDNSSrvDefParseXML
>  conf: Replace virNetworkDNSSrvDefParseXML(hardcoded) with namesake(generated)
>  conf: Generate virNetworkDNSSrvDefFormatBuf
>  conf: Extract error-checking code from virNetworkDNSHostDefParseXML
>  conf: Replace virNetworkDNSHostDefParseXML(hardcoded) with 
>namesake(generated)
>  conf: Generate virNetworkDNSHostDefFormatBuf
>  conf: Extract virNetworkDNSForwarderParseXML from virNetworkDNSParseXML
>  conf: Replace virNetworkDNSForwarderParseXML(hardcoded) with 
>namesake(generated)
>  conf: Generate virNetworkDNSForwarderFormatBuf
>  conf: Extract error-checking code from virNetworkDNSDefParseXML
>  conf: Replace virNetworkDNSDefParseXML(hardcoded) with namesake(generated)
>  conf: Generate virNetworkDNSDefFormatBuf
>
> build-aux/syntax-check.mk    |    2 +-
> docs/meson.build |    1 +
> docs/xmlgen.rst  |  684 +
> libvirt.spec.in  |    1 +
> meson.build  |   10 +
> mingw-libvirt.spec.in    |    1 +
> po/POTFILES.in   |    2 +
> scripts/meson.build  |    8 +
> scripts/xmlgen/directive.py  | 1192 ++
> scripts/xmlgen/go    |   29 +
> scripts/xmlgen/main.py   |  534 ++
> scripts/xmlgen/utils.py  |  121 +++
> src/conf/meson.build |   37 +
> src/conf/network_conf.c  |  463 ++---
> src/conf/network_conf.h  |   59 +-
> src/internal.h   |    8 +
> src/libvirt_private.syms |   13 +
> src/meson.build  |    6 +
> src/util/meson.build |   36 +
> src/util/virbuffer.c |   44 +
> src/util/virbuffer.h |    8 +-
> src/util/virsocketaddr.c |   42 +
> src/util/virsocketaddr.h |   23 +-
> src/util/virstring.c |  102 ++
> src/util/virstring.h |   15 +
> src/util/viruuid.c   |   31 +
> src/util/viruuid.h   |   18 +
> src/util/virxml.c    |  120 +++
> src/util/virxml.h    |    6 +
> tests/meson.build    |    3 +
> tests/xmlgenin/conf/array.h  |   17 +
> tests/xmlgenin/conf/empty.h  |    7 +
> tests/xmlgenin/conf/enum-first-item.h    |   12 +
> tests/xmlgenin/conf/external.h   |    9 +
> tests/xmlgenin/conf/genformat-separate.h |   11 +
> tests/xmlgenin/conf/genformat.h  |   11 +
> tests/xmlgenin/conf/genparse.h   |   11 +
&

[RFCv3 14/25] conf: Extract error-checking code from virNetworkDNSSrvDefParseXML

2021-04-22 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/conf/network_conf.c | 107 +++-
 1 file changed, 83 insertions(+), 24 deletions(-)

diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index bb976a78..20128af0 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -768,23 +768,26 @@ virNetworkDNSHostDefParseXML(const char *networkName,
 "_-+/*"
 
 static int
-virNetworkDNSSrvDefParseXML(const char *networkName,
-xmlNodePtr node,
-xmlXPathContextPtr ctxt,
-virNetworkDNSSrvDef *def,
-bool partialOkay)
+virNetworkDNSSrvDefParseHook(xmlNodePtr node G_GNUC_UNUSED,
+ virNetworkDNSSrvDef *def,
+ const char *networkName,
+ void *parent G_GNUC_UNUSED,
+ void *opaque,
+ const char *portStr,
+ const char *priorityStr,
+ const char *weightStr)
 {
-int ret;
-VIR_XPATH_NODE_AUTORESTORE(ctxt)
-
-ctxt->node = node;
+bool partialOkay = false;
+if (opaque)
+partialOkay = *((bool *) opaque);
 
-if (!(def->service = virXMLPropString(node, "service")) && !partialOkay) {
+if (!def->service && !partialOkay) {
 virReportError(VIR_ERR_XML_DETAIL,
_("missing required service attribute in DNS SRV record 
"
  "of network '%s'"), networkName);
 goto error;
 }
+
 if (def->service) {
 if (strlen(def->service) > DNS_RECORD_LENGTH_SRV) {
 virReportError(VIR_ERR_XML_DETAIL,
@@ -802,13 +805,14 @@ virNetworkDNSSrvDefParseXML(const char *networkName,
 }
 }
 
-if (!(def->protocol = virXMLPropString(node, "protocol")) && !partialOkay) 
{
+if (!def->protocol && !partialOkay) {
 virReportError(VIR_ERR_XML_DETAIL,
_("missing required protocol attribute "
  "in DNS SRV record '%s' of network '%s'"),
def->service, networkName);
 goto error;
 }
+
 if (def->protocol &&
 strspn(def->protocol, PROTOCOL_CHARS) < strlen(def->protocol)) {
 virReportError(VIR_ERR_XML_DETAIL,
@@ -818,19 +822,14 @@ virNetworkDNSSrvDefParseXML(const char *networkName,
 goto error;
 }
 
-/* Following attributes are optional */
-def->domain = virXMLPropString(node, "domain");
-def->target = virXMLPropString(node, "target");
-
-ret = virXPathUInt("string(./@port)", ctxt, &def->port);
-if (ret >= 0 && !def->target) {
+if (portStr && !def->target) {
 virReportError(VIR_ERR_XML_DETAIL,
_("DNS SRV port attribute not permitted without "
  "target for service '%s' in network '%s'"),
def->service, networkName);
 goto error;
 }
-if (ret == -2 || (ret >= 0 && (def->port < 1 || def->port > 65535))) {
+if (portStr && (def->port < 1 || def->port > 65535)) {
 virReportError(VIR_ERR_XML_DETAIL,
_("invalid DNS SRV port attribute "
  "for service '%s' in network '%s'"),
@@ -838,15 +837,14 @@ virNetworkDNSSrvDefParseXML(const char *networkName,
 goto error;
 }
 
-ret = virXPathUInt("string(./@priority)", ctxt, &def->priority);
-if (ret >= 0 && !def->target) {
+if (priorityStr && !def->target) {
 virReportError(VIR_ERR_XML_DETAIL,
_("DNS SRV priority attribute not permitted without "
  "target for service '%s' in network '%s'"),
def->service, networkName);
 goto error;
 }
-if (ret == -2 || (ret >= 0 && def->priority > 65535)) {
+if (priorityStr && def->priority > 65535) {
 virReportError(VIR_ERR_XML_DETAIL,
_("Invalid DNS SRV priority attribute "
  "for service '%s' in network '%s'"),
@@ -854,15 +852,14 @@ virNetworkDNSSrvDefParseXML(const char *networkName,
 goto error;
 }
 
-ret = virXPathUInt("string(./@weight)", ctxt, &def->weight);
-if (ret >= 0 && !def->target) {
+if (weightStr && !def->target) {
 virReportError(VIR_ERR

[RFCv3 15/25] conf: Replace virNetworkDNSSrvDefParseXML(hardcoded) with namesake(generated)

2021-04-22 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/conf/network_conf.c | 83 +++--
 src/conf/network_conf.h | 17 +
 2 files changed, 15 insertions(+), 85 deletions(-)

diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index 20128af0..cb2f3163 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -183,16 +183,6 @@ virNetworkDNSHostDefClear(virNetworkDNSHostDef *def)
 }
 
 
-static void
-virNetworkDNSSrvDefClear(virNetworkDNSSrvDef *def)
-{
-VIR_FREE(def->domain);
-VIR_FREE(def->service);
-VIR_FREE(def->protocol);
-VIR_FREE(def->target);
-}
-
-
 static void
 virNetworkDNSForwarderClear(virNetworkDNSForwarder *def)
 {
@@ -767,7 +757,7 @@ virNetworkDNSHostDefParseXML(const char *networkName,
 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" \
 "_-+/*"
 
-static int
+int
 virNetworkDNSSrvDefParseHook(xmlNodePtr node G_GNUC_UNUSED,
  virNetworkDNSSrvDef *def,
  const char *networkName,
@@ -874,69 +864,6 @@ virNetworkDNSSrvDefParseHook(xmlNodePtr node G_GNUC_UNUSED,
 }
 
 
-static int
-virNetworkDNSSrvDefParseXML(const char *networkName,
-xmlNodePtr node,
-xmlXPathContextPtr ctxt G_GNUC_UNUSED,
-virNetworkDNSSrvDef *def,
-bool partialOkay)
-{
-g_autofree char *portStr = NULL;
-g_autofree char *priorityStr = NULL;
-g_autofree char *weightStr = NULL;
-
-def->service = virXMLPropString(node, "service");
-def->protocol = virXMLPropString(node, "protocol");
-
-/* Following attributes are optional */
-def->domain = virXMLPropString(node, "domain");
-def->target = virXMLPropString(node, "target");
-
-portStr = virXMLPropString(node, "port");
-if (portStr) {
-if (virStrToLong_uip(portStr, NULL, 0, &def->port) < 0) {
-virReportError(VIR_ERR_XML_DETAIL,
-   _("invalid DNS SRV port attribute "
- "for service '%s' in network '%s'"),
-   def->service, networkName);
-goto error;
-}
-}
-
-priorityStr = virXMLPropString(node, "priority");
-if (priorityStr) {
-if (virStrToLong_uip(priorityStr, NULL, 0, &def->priority) < 0) {
-virReportError(VIR_ERR_XML_DETAIL,
-   _("Invalid DNS SRV priority attribute "
- "for service '%s' in network '%s'"),
-   def->service, networkName);
-goto error;
-}
-}
-
-weightStr = virXMLPropString(node, "weight");
-if (weightStr) {
-if (virStrToLong_uip(weightStr, NULL, 0, &def->weight) < 0) {
-virReportError(VIR_ERR_XML_DETAIL,
-   _("invalid DNS SRV weight attribute "
- "for service '%s' in network '%s'"),
-   def->service, networkName);
-goto error;
-}
-}
-
-if (virNetworkDNSSrvDefParseHook(node, def, networkName, def, &partialOkay,
- portStr, priorityStr, weightStr) < 0)
-goto error;
-
-return 0;
-
- error:
-virNetworkDNSSrvDefClear(def);
-return -1;
-}
-
-
 int
 virNetworkDNSTxtDefParseHook(xmlNodePtr node G_GNUC_UNUSED,
  virNetworkDNSTxtDef *def,
@@ -1082,8 +1009,8 @@ virNetworkDNSDefParseXML(const char *networkName,
 def->srvs = g_new0(virNetworkDNSSrvDef, nsrvs);
 
 for (i = 0; i < nsrvs; i++) {
-if (virNetworkDNSSrvDefParseXML(networkName, srvNodes[i], ctxt,
-&def->srvs[def->nsrvs], false) < 
0) {
+if (virNetworkDNSSrvDefParseXML(srvNodes[i], 
&def->srvs[def->nsrvs],
+networkName, def, NULL) < 0) {
 return -1;
 }
 def->nsrvs++;
@@ -3553,6 +3480,7 @@ virNetworkDefUpdateDNSSrv(virNetworkDef *def,
 bool isAdd = (command == VIR_NETWORK_UPDATE_COMMAND_ADD_FIRST ||
   command == VIR_NETWORK_UPDATE_COMMAND_ADD_LAST);
 int foundCt = 0;
+bool notAdd;
 
 memset(&srv, 0, sizeof(srv));
 
@@ -3566,7 +3494,8 @@ virNetworkDefUpdateDNSSrv(virNetworkDef *def,
 if (virNetworkDefUpdateCheckElementName(def, ctxt->node, "srv") < 0)
 goto cleanup;
 
-if (virNetworkDNSSrvDefParseXML(def->name, ctxt->node, ctxt, &srv, !isAdd) 
< 0)
+notAdd = !isAdd;
+if (virNetworkDNSSrvDefParseXML(ctxt->node, &

[RFCv3 17/25] conf: Extract error-checking code from virNetworkDNSHostDefParseXML

2021-04-22 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/conf/network_conf.c | 63 +
 1 file changed, 45 insertions(+), 18 deletions(-)

diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index 146c4977..b326ef5f 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -676,21 +676,57 @@ virNetworkDHCPDefParseXML(const char *networkName,
 
 
 static int
-virNetworkDNSHostDefParseXML(const char *networkName,
- xmlNodePtr node,
- virNetworkDNSHostDef *def,
- bool partialOkay)
+virNetworkDNSHostDefParseHook(xmlNodePtr node G_GNUC_UNUSED,
+  virNetworkDNSHostDef *def,
+  const char *networkName,
+  void *parent G_GNUC_UNUSED,
+  void *opaque,
+  const char *ip,
+  int nHostnameNodes G_GNUC_UNUSED)
 {
-xmlNodePtr cur;
-g_autofree char *ip = NULL;
+bool partialOkay = false;
 
-if (!(ip = virXMLPropString(node, "ip")) && !partialOkay) {
+if (opaque)
+partialOkay = *((bool *) opaque);
+
+if (!ip && !partialOkay) {
 virReportError(VIR_ERR_XML_DETAIL,
_("Missing IP address in network '%s' DNS HOST record"),
networkName);
 goto error;
 }
 
+if (def->nnames == 0 && !partialOkay) {
+virReportError(VIR_ERR_XML_DETAIL,
+   _("Missing hostname in network '%s' DNS HOST record"),
+   networkName);
+goto error;
+}
+
+if (!VIR_SOCKET_ADDR_VALID(&def->ip) && def->nnames == 0) {
+virReportError(VIR_ERR_XML_DETAIL,
+   _("Missing ip and hostname in network '%s' DNS HOST 
record"),
+   networkName);
+goto error;
+}
+
+return 0;
+
+ error:
+return -1;
+}
+
+
+static int
+virNetworkDNSHostDefParseXML(const char *networkName,
+ xmlNodePtr node,
+ virNetworkDNSHostDef *def,
+ bool partialOkay)
+{
+xmlNodePtr cur;
+g_autofree char *ip = NULL;
+
+ip = virXMLPropString(node, "ip");
 if (ip && (virSocketAddrParse(&def->ip, ip, AF_UNSPEC) < 0)) {
 virReportError(VIR_ERR_XML_DETAIL,
_("Invalid IP address in network '%s' DNS HOST record"),
@@ -720,19 +756,10 @@ virNetworkDNSHostDefParseXML(const char *networkName,
 }
 cur = cur->next;
 }
-if (def->nnames == 0 && !partialOkay) {
-virReportError(VIR_ERR_XML_DETAIL,
-   _("Missing hostname in network '%s' DNS HOST record"),
-   networkName);
-goto error;
-}
 
-if (!VIR_SOCKET_ADDR_VALID(&def->ip) && def->nnames == 0) {
-virReportError(VIR_ERR_XML_DETAIL,
-   _("Missing ip and hostname in network '%s' DNS HOST 
record"),
-   networkName);
+if (virNetworkDNSHostDefParseHook(node, def, networkName, def, 
&partialOkay,
+  ip, def->nnames) < 0)
 goto error;
-}
 
 return 0;
 
-- 
2.25.1




[RFCv3 11/25] conf: Extract error-checking code from virNetworkDNSTxtDefParseXML

2021-04-22 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/conf/network_conf.c | 49 -
 1 file changed, 38 insertions(+), 11 deletions(-)

diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index d6eafa3f..87157591 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -887,26 +887,26 @@ virNetworkDNSSrvDefParseXML(const char *networkName,
 
 
 static int
-virNetworkDNSTxtDefParseXML(const char *networkName,
-xmlNodePtr node,
-virNetworkDNSTxtDef *def,
-bool partialOkay)
+virNetworkDNSTxtDefParseHook(xmlNodePtr node G_GNUC_UNUSED,
+ virNetworkDNSTxtDef *def,
+ const char *networkName,
+ void *parent G_GNUC_UNUSED,
+ void *opaque)
 {
 const char *bad = " ,";
+bool partialOkay = false;
+
+if (opaque)
+partialOkay = *((bool *) opaque);
 
-if (!(def->name = virXMLPropString(node, "name"))) {
-virReportError(VIR_ERR_XML_DETAIL,
-   _("missing required name attribute in DNS TXT record "
- "of network %s"), networkName);
-goto error;
-}
 if (strcspn(def->name, bad) != strlen(def->name)) {
 virReportError(VIR_ERR_XML_DETAIL,
_("prohibited character in DNS TXT record "
  "name '%s' of network %s"), def->name, networkName);
 goto error;
 }
-if (!(def->value = virXMLPropString(node, "value")) && !partialOkay) {
+
+if (!def->value && !partialOkay) {
 virReportError(VIR_ERR_XML_DETAIL,
_("missing required value attribute in DNS TXT record "
  "named '%s' of network %s"), def->name, networkName);
@@ -919,6 +919,33 @@ virNetworkDNSTxtDefParseXML(const char *networkName,
  "in DNS TXT record of network %s"), networkName);
 goto error;
 }
+
+return 0;
+
+ error:
+return -1;
+}
+
+
+static int
+virNetworkDNSTxtDefParseXML(const char *networkName,
+xmlNodePtr node,
+virNetworkDNSTxtDef *def,
+bool partialOkay)
+{
+if (!(def->name = virXMLPropString(node, "name"))) {
+virReportError(VIR_ERR_XML_DETAIL,
+   _("missing required name attribute in DNS TXT record "
+ "of network %s"), networkName);
+goto error;
+}
+
+def->value = virXMLPropString(node, "value");
+
+if (virNetworkDNSTxtDefParseHook(node, def, networkName,
+ NULL, &partialOkay) < 0)
+goto error;
+
 return 0;
 
  error:
-- 
2.25.1




[RFCv3 07/25] util: Add some xml-helper-functions to cooperate with xmlgen

2021-04-22 Thread Shi Lei
1) virXMLChildNode and virXMLChildNodeSet
   Parse xml based on 'node' rather than 'ctxt'.

2) virXMLChildPropString
   Support to parse child node's property value by path,
   which is as "child_node_name/prop_name".

3) virXMLChildNodeContent
   Support to parse child node's content.

4) virBufferIgnore
   Mark a buffer with the flag 'ignored'.

5) virBufferTouched
   Check whether the buffer has been touched, which means
   this buffer has been used or ignored.

6) virBufferInheritIndent
   Inherit the indentation from another buffer.

Signed-off-by: Shi Lei 
---
 src/libvirt_private.syms |   6 ++
 src/util/virbuffer.c |  44 ++
 src/util/virbuffer.h |   8 ++-
 src/util/virxml.c| 120 +++
 src/util/virxml.h|   6 ++
 5 files changed, 182 insertions(+), 2 deletions(-)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index e9bb2391..fad0ff71 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1864,9 +1864,12 @@ virBufferEscapeString;
 virBufferFreeAndReset;
 virBufferGetEffectiveIndent;
 virBufferGetIndent;
+virBufferIgnore;
+virBufferInheritIndent;
 virBufferSetIndent;
 virBufferStrcat;
 virBufferStrcatVArgs;
+virBufferTouched;
 virBufferTrim;
 virBufferTrimChars;
 virBufferTrimLen;
@@ -3539,6 +3542,9 @@ virVsockSetGuestCid;
 virParseScaledValue;
 virXMLBufferCreate;
 virXMLCheckIllegalChars;
+virXMLChildNode;
+virXMLChildNodeSet;
+virXMLChildPropString;
 virXMLExtractNamespaceXML;
 virXMLFormatElement;
 virXMLNewNode;
diff --git a/src/util/virbuffer.c b/src/util/virbuffer.c
index 8f9cd57e..b6f936c8 100644
--- a/src/util/virbuffer.c
+++ b/src/util/virbuffer.c
@@ -80,6 +80,24 @@ virBufferSetIndent(virBuffer *buf, int indent)
 }
 
 
+/**
+ * virBufferInheritIndent:
+ * @buf: the buffer
+ * @frombuf: new indentation size from it.
+ *
+ * Just like virBufferSetIndent, but the indentation comes from
+ * another buffer.
+ */
+void
+virBufferInheritIndent(virBuffer *buf, virBuffer *frombuf)
+{
+if (!buf || !frombuf)
+return;
+
+buf->indent = virBufferGetEffectiveIndent(frombuf);
+}
+
+
 /**
  * virBufferGetIndent:
  * @buf: the buffer
@@ -291,6 +309,32 @@ virBufferUse(const virBuffer *buf)
 return buf->str->len;
 }
 
+/**
+ * virBufferTouched:
+ * @buf: the buffer to be checked
+ *
+ * Return true when the buffer has content or
+ * it has been ignored(by calling virBufferIgnore);
+ * otherwise, return false.
+ */
+bool
+virBufferTouched(virBuffer *buf)
+{
+return buf->ignored || virBufferUse(buf);
+}
+
+/**
+ * virBufferIgnore:
+ * @buf: the buffer to be ignored
+ *
+ * Ignore the buffer explicitly.
+ */
+void
+virBufferIgnore(virBuffer *buf)
+{
+buf->ignored = true;
+}
+
 /**
  * virBufferAsprintf:
  * @buf: the buffer to append to
diff --git a/src/util/virbuffer.h b/src/util/virbuffer.h
index 0e72d078..ddb29cb5 100644
--- a/src/util/virbuffer.h
+++ b/src/util/virbuffer.h
@@ -32,7 +32,7 @@
  */
 typedef struct _virBuffer virBuffer;
 
-#define VIR_BUFFER_INITIALIZER { NULL, 0 }
+#define VIR_BUFFER_INITIALIZER { NULL, 0, false }
 
 /**
  * VIR_BUFFER_INIT_CHILD:
@@ -41,11 +41,12 @@ typedef struct _virBuffer virBuffer;
  * Initialize a virBuffer structure and set up the indentation level for
  * formatting XML subelements of @parentbuf.
  */
-#define VIR_BUFFER_INIT_CHILD(parentbuf) { NULL, (parentbuf)->indent + 2 }
+#define VIR_BUFFER_INIT_CHILD(parentbuf) { NULL, (parentbuf)->indent + 2, 
false }
 
 struct _virBuffer {
 GString *str;
 int indent;
+bool ignored;
 };
 
 const char *virBufferCurrentContent(virBuffer *buf);
@@ -86,6 +87,7 @@ void virBufferURIEncodeString(virBuffer *buf, const char 
*str);
 
 void virBufferAdjustIndent(virBuffer *buf, int indent);
 void virBufferSetIndent(virBuffer *, int indent);
+void virBufferInheritIndent(virBuffer *buf, virBuffer *frombuf);
 
 size_t virBufferGetIndent(const virBuffer *buf);
 size_t virBufferGetEffectiveIndent(const virBuffer *buf);
@@ -94,3 +96,5 @@ void virBufferTrim(virBuffer *buf, const char *trim);
 void virBufferTrimChars(virBuffer *buf, const char *trim);
 void virBufferTrimLen(virBuffer *buf, int len);
 void virBufferAddStr(virBuffer *buf, const char *str);
+bool virBufferTouched(virBuffer *buf);
+void virBufferIgnore(virBuffer *buf);
diff --git a/src/util/virxml.c b/src/util/virxml.c
index 5ceef738..a6e50459 100644
--- a/src/util/virxml.c
+++ b/src/util/virxml.c
@@ -1746,3 +1746,123 @@ virXMLNewNode(xmlNsPtr ns,
 
 return ret;
 }
+
+
+/**
+ * virXMLChildNode:
+ * @node: Parent XML dom node pointer
+ * @name: Name of the child element
+ *
+ * Convenience function to return the child element of a XML node.
+ *
+ * Returns the pointer of child element node or NULL in case of failure.
+ * If there are many nodes match condition, it only returns the first node.
+ */
+xmlNodePtr
+vi

[RFCv3 21/25] conf: Replace virNetworkDNSForwarderParseXML(hardcoded) with namesake(generated)

2021-04-22 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/conf/network_conf.c | 36 +---
 src/conf/network_conf.h |  7 ---
 2 files changed, 5 insertions(+), 38 deletions(-)

diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index cf9e77d3..ef28bb4d 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -174,13 +174,6 @@ virNetworkIPDefClear(virNetworkIPDef *def)
 }
 
 
-static void
-virNetworkDNSForwarderClear(virNetworkDNSForwarder *def)
-{
-VIR_FREE(def->domain);
-}
-
-
 static void
 virNetworkDNSDefClear(virNetworkDNSDef *def)
 {
@@ -871,7 +864,7 @@ virNetworkDNSTxtDefParseHook(xmlNodePtr node G_GNUC_UNUSED,
 }
 
 
-static int
+int
 virNetworkDNSForwarderParseHook(xmlNodePtr node G_GNUC_UNUSED,
 virNetworkDNSForwarder *def,
 const char *instname G_GNUC_UNUSED,
@@ -890,33 +883,6 @@ virNetworkDNSForwarderParseHook(xmlNodePtr node 
G_GNUC_UNUSED,
 }
 
 
-static int
-virNetworkDNSForwarderParseXML(xmlNodePtr node,
-   virNetworkDNSForwarder *def,
-   const char *networkName,
-   void *parent G_GNUC_UNUSED,
-   void *opaque)
-{
-g_autofree char *addr = virXMLPropString(node, "addr");
-
-if (addr && virSocketAddrParse(&def->addr, addr, AF_UNSPEC) < 0) {
-virReportError(VIR_ERR_XML_ERROR,
-   _("Invalid forwarder IP address '%s' "
- "in network '%s'"),
-   addr, networkName);
-return -1;
-}
-
-def->domain = virXMLPropString(node, "domain");
-
-if (virNetworkDNSForwarderParseHook(node, def, networkName, def, opaque,
-addr) < 0)
-return -1;
-
-return 0;
-}
-
-
 static int
 virNetworkDNSDefParseXML(const char *networkName,
  xmlNodePtr node,
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index 836d088d..17f6c309 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -150,9 +150,9 @@ struct _virNetworkDNSHostDef {  /* genparse, genformat */
 
 
 typedef struct _virNetworkDNSForwarder virNetworkDNSForwarder;
-struct _virNetworkDNSForwarder {
-virSocketAddr addr;
-char *domain;
+struct _virNetworkDNSForwarder {/* genparse */
+virSocketAddr addr; /* xmlattr */
+char *domain;   /* xmlattr */
 };
 
 typedef struct _virNetworkDNSDef virNetworkDNSDef;
@@ -429,6 +429,7 @@ virNetworkDefUpdateSection(virNetworkDef *def,
 
 VIR_ENUM_DECL(virNetworkTaint);
 
+#define ENABLE_VIR_NETWORK_DNSFORWARDER_PARSE_HOOK
 #define ENABLE_VIR_NETWORK_DNSHOST_DEF_PARSE_HOOK
 #define ENABLE_VIR_NETWORK_DNSSRV_DEF_PARSE_HOOK
 #define ENABLE_VIR_NETWORK_DNSTXT_DEF_PARSE_HOOK
-- 
2.25.1




[RFCv3 00/25] RFC: Generate parsexml/formatbuf functions based on directives

2021-04-22 Thread Shi Lei
V2 here: 
[https://listman.redhat.com/archives/libvir-list/2020-September/msg00204.html]

Differ from V2:

  * Add tests for xmlgen to illustrate all the different features we can use
and make sure its proper functions in the future.

  * Add docs/xmlgen.rst to explain the usage of all directives and the tool
itself.

  * Now xmlgen can check whether the first item of enum ends with _NONE, 
_DEFAULT
or _ABSENT and generate proper code. So we no longer need to add extra
'default' item for enum.

  * Now xmlgen can provide extra [tips] when we execute its command-line to show
generated code for preview.

  * Enable/disable hooks by macros rather than by special directives.

  * Add virStrToBoolYesNo/virStrToBoolTrueFalse/virStrToBoolOnOff
and explicitly check both the true and false values.

  * Stronger check for python3-clang and libclang.so to make sure it can work.

  * Add python3-clang to the libvirt.spec.in and the mingw-libvirt.spec.in.

  Thanks!


Shi Lei (25):
  scripts: Add a tool to generate xml parse/format functions
  maint: Check python3-clang and libclang
  maint: Call xmlgen automatically when c-head-files change
  docs: Add xmlgen.rst to explain how to use it
  build-aux: Only check *.[ch] for sc_prohibit_useless_translation
  tests: Add tests for xmlgen
  util: Add some xml-helper-functions to cooperate with xmlgen
  util: Add helper aliases and functions for 'bool' and 'time_t' to cooperate 
with xmlgen
  util: Add parsexml/formatbuf helper functions for virSocketAddr
  util: Add virUUID type and parse/format functions
  conf: Extract error-checking code from virNetworkDNSTxtDefParseXML
  conf: Replace virNetworkDNSTxtDefParseXML(hardcoded) with namesake(generated)
  conf: Generate virNetworkDNSTxtDefFormatBuf
  conf: Extract error-checking code from virNetworkDNSSrvDefParseXML
  conf: Replace virNetworkDNSSrvDefParseXML(hardcoded) with namesake(generated)
  conf: Generate virNetworkDNSSrvDefFormatBuf
  conf: Extract error-checking code from virNetworkDNSHostDefParseXML
  conf: Replace virNetworkDNSHostDefParseXML(hardcoded) with namesake(generated)
  conf: Generate virNetworkDNSHostDefFormatBuf
  conf: Extract virNetworkDNSForwarderParseXML from virNetworkDNSParseXML
  conf: Replace virNetworkDNSForwarderParseXML(hardcoded) with 
namesake(generated)
  conf: Generate virNetworkDNSForwarderFormatBuf
  conf: Extract error-checking code from virNetworkDNSDefParseXML
  conf: Replace virNetworkDNSDefParseXML(hardcoded) with namesake(generated)
  conf: Generate virNetworkDNSDefFormatBuf

 build-aux/syntax-check.mk|2 +-
 docs/meson.build |1 +
 docs/xmlgen.rst  |  684 +
 libvirt.spec.in  |1 +
 meson.build  |   10 +
 mingw-libvirt.spec.in|1 +
 po/POTFILES.in   |2 +
 scripts/meson.build  |8 +
 scripts/xmlgen/directive.py  | 1192 ++
 scripts/xmlgen/go|   29 +
 scripts/xmlgen/main.py   |  534 ++
 scripts/xmlgen/utils.py  |  121 +++
 src/conf/meson.build |   37 +
 src/conf/network_conf.c  |  463 ++---
 src/conf/network_conf.h  |   59 +-
 src/internal.h   |8 +
 src/libvirt_private.syms |   13 +
 src/meson.build  |6 +
 src/util/meson.build |   36 +
 src/util/virbuffer.c |   44 +
 src/util/virbuffer.h |8 +-
 src/util/virsocketaddr.c |   42 +
 src/util/virsocketaddr.h |   23 +-
 src/util/virstring.c |  102 ++
 src/util/virstring.h |   15 +
 src/util/viruuid.c   |   31 +
 src/util/viruuid.h   |   18 +
 src/util/virxml.c|  120 +++
 src/util/virxml.h|6 +
 tests/meson.build|3 +
 tests/xmlgenin/conf/array.h  |   17 +
 tests/xmlgenin/conf/empty.h  |7 +
 tests/xmlgenin/conf/enum-first-item.h|   12 +
 tests/xmlgenin/conf/external.h   |9 +
 tests/xmlgenin/conf/genformat-separate.h |   11 +
 tests/xmlgenin/conf/genformat.h  |   11 +
 tests/xmlgenin/conf/genparse.h   |   11 +
 tests/xmlgenin/conf/namespace.h  |   12 +
 tests/xmlgenin/conf/required.h   |9 +
 tests/xmlgenin/conf/skipparse.h  |   10 +
 tests/xmlgenin/conf/specify.h|   13 +
 tests/xmlgenin/conf/xmlattr.h|   10 +
 tests/xmlgenin/conf/xmlelem.h|   10 +
 tests/xmlgenin/conf/xmlgroup.h   |8 +
 tests/xmlgenin/conf/xmlswitch.h  |   17 +
 tests/xmlgenin/util/enums.h  

[RFCv3 22/25] conf: Generate virNetworkDNSForwarderFormatBuf

2021-04-22 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/conf/network_conf.c | 18 +++---
 src/conf/network_conf.h |  4 ++--
 2 files changed, 5 insertions(+), 17 deletions(-)

diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index ef28bb4d..be639500 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -2135,21 +2135,9 @@ virNetworkDNSDefFormat(virBuffer *buf,
 virBufferAdjustIndent(buf, 2);
 
 for (i = 0; i < def->nfwds; i++) {
-
-virBufferAddLit(buf, "forwarders[i].domain) {
-virBufferEscapeString(buf, " domain='%s'",
-  def->forwarders[i].domain);
-}
-if (VIR_SOCKET_ADDR_VALID(&def->forwarders[i].addr)) {
-g_autofree char *addr = 
virSocketAddrFormat(&def->forwarders[i].addr);
-
-if (!addr)
-return -1;
-
-virBufferAsprintf(buf, " addr='%s'", addr);
-}
-virBufferAddLit(buf, "/>\n");
+if (virNetworkDNSForwarderFormatBuf(buf, "forwarder",
+&def->forwarders[i], def, NULL) < 
0)
+return -1;
 }
 
 for (i = 0; i < def->ntxts; i++) {
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index 17f6c309..577c1568 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -150,9 +150,9 @@ struct _virNetworkDNSHostDef {  /* genparse, genformat */
 
 
 typedef struct _virNetworkDNSForwarder virNetworkDNSForwarder;
-struct _virNetworkDNSForwarder {/* genparse */
-virSocketAddr addr; /* xmlattr */
+struct _virNetworkDNSForwarder {/* genparse, genformat */
 char *domain;   /* xmlattr */
+virSocketAddr addr; /* xmlattr */
 };
 
 typedef struct _virNetworkDNSDef virNetworkDNSDef;
-- 
2.25.1




[RFCv3 10/25] util: Add virUUID type and parse/format functions

2021-04-22 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/util/viruuid.c | 31 +++
 src/util/viruuid.h | 18 ++
 2 files changed, 49 insertions(+)

diff --git a/src/util/viruuid.c b/src/util/viruuid.c
index 558fbb9c..c6e6272f 100644
--- a/src/util/viruuid.c
+++ b/src/util/viruuid.c
@@ -134,6 +134,18 @@ virUUIDParse(const char *uuidstr, unsigned char *uuid)
 return 0;
 }
 
+
+int
+virUUIDParseXML(const char *uuidstr,
+virUUID *puuid,
+const char *instname G_GNUC_UNUSED,
+void *parent G_GNUC_UNUSED,
+void *opaque G_GNUC_UNUSED)
+{
+return virUUIDParse(uuidstr, *puuid);
+}
+
+
 /**
  * virUUIDFormat:
  * @uuid: array of VIR_UUID_BUFLEN bytes to store the raw UUID
@@ -159,6 +171,20 @@ virUUIDFormat(const unsigned char *uuid, char *uuidstr)
 }
 
 
+int
+virUUIDFormatBuf(virBuffer *buf,
+ const char *fmt,
+ const virUUID *puuid,
+ const void *parent G_GNUC_UNUSED,
+ void *opaque G_GNUC_UNUSED)
+{
+char uuidstr[VIR_UUID_STRING_BUFLEN];
+virUUIDFormat(*puuid, uuidstr);
+virBufferAsprintf(buf, fmt, uuidstr);
+
+return 0;
+}
+
 
 /**
  * virUUIDIsValid
@@ -263,3 +289,8 @@ int virGetHostUUID(unsigned char *uuid)
 
 return ret;
 }
+
+void virUUIDClear(virUUID *puuid G_GNUC_UNUSED)
+{
+memset(*puuid, 0, VIR_UUID_BUFLEN);
+}
diff --git a/src/util/viruuid.h b/src/util/viruuid.h
index b403b190..36766356 100644
--- a/src/util/viruuid.h
+++ b/src/util/viruuid.h
@@ -21,6 +21,7 @@
 #pragma once
 
 #include "internal.h"
+#include "virbuffer.h"
 
 
 /**
@@ -39,6 +40,7 @@
 } \
 } while (0)
 
+typedef unsigned char virUUID[VIR_UUID_BUFLEN];
 
 int virSetHostUUIDStr(const char *host_uuid);
 int virGetHostUUID(unsigned char *host_uuid) ATTRIBUTE_NONNULL(1) 
G_GNUC_NO_INLINE;
@@ -51,5 +53,21 @@ int virUUIDParse(const char *uuidstr,
  unsigned char *uuid)
 ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
 
+int
+virUUIDParseXML(const char *uuidstr,
+virUUID *puuid,
+const char *instname,
+void *parent,
+void *opaque);
+
 const char *virUUIDFormat(const unsigned char *uuid,
   char *uuidstr) ATTRIBUTE_NONNULL(1) 
ATTRIBUTE_NONNULL(2);
+
+int
+virUUIDFormatBuf(virBuffer *buf,
+ const char *fmt,
+ const virUUID *puuid,
+ const void *parent,
+ void *opaque);
+
+void virUUIDClear(virUUID *puuid);
-- 
2.25.1




[RFCv3 09/25] util: Add parsexml/formatbuf helper functions for virSocketAddr

2021-04-22 Thread Shi Lei
Implement the parsexml/formatbuf functions for virSocketAddr.

Signed-off-by: Shi Lei 
---
 src/libvirt_private.syms |  2 ++
 src/util/virsocketaddr.c | 42 
 src/util/virsocketaddr.h | 23 --
 3 files changed, 65 insertions(+), 2 deletions(-)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index e78491dc..055396d0 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -3213,6 +3213,7 @@ virSocketAddrBroadcastByPrefix;
 virSocketAddrCheckNetmask;
 virSocketAddrEqual;
 virSocketAddrFormat;
+virSocketAddrFormatBuf;
 virSocketAddrFormatFull;
 virSocketAddrGetIPPrefix;
 virSocketAddrGetNumNetmaskBits;
@@ -3230,6 +3231,7 @@ virSocketAddrParse;
 virSocketAddrParseAny;
 virSocketAddrParseIPv4;
 virSocketAddrParseIPv6;
+virSocketAddrParseXML;
 virSocketAddrPrefixToNetmask;
 virSocketAddrPTRDomain;
 virSocketAddrResolveService;
diff --git a/src/util/virsocketaddr.c b/src/util/virsocketaddr.c
index 94cbfc62..fcad7f8a 100644
--- a/src/util/virsocketaddr.c
+++ b/src/util/virsocketaddr.c
@@ -154,6 +154,15 @@ int virSocketAddrParse(virSocketAddr *addr, const char 
*val, int family)
 return len;
 }
 
+int virSocketAddrParseXML(const char *val,
+  virSocketAddr *addr,
+  const char *instname G_GNUC_UNUSED,
+  void *parent G_GNUC_UNUSED,
+  void *opaque G_GNUC_UNUSED)
+{
+return virSocketAddrParse(addr, val, AF_UNSPEC);
+}
+
 /**
  * virSocketAddrParseAny:
  * @addr: where to store the return value, optional.
@@ -1306,3 +1315,36 @@ virSocketAddrFree(virSocketAddr *addr)
 {
 g_free(addr);
 }
+
+void
+virSocketAddrClear(virSocketAddr *addr)
+{
+memset(addr, 0, sizeof(virSocketAddr));
+}
+
+int
+virSocketAddrFormatBuf(virBuffer *buf,
+   const char *fmt,
+   const virSocketAddr *addr,
+   const void *parent G_GNUC_UNUSED,
+   void *opaque G_GNUC_UNUSED)
+{
+g_autofree char *str = NULL;
+if (!VIR_SOCKET_ADDR_VALID(addr))
+return 0;
+
+str = virSocketAddrFormatFull(addr, false, NULL);
+if (!str)
+return -1;
+
+virBufferAsprintf(buf, fmt, str);
+return 0;
+}
+
+bool
+virSocketAddrCheck(const virSocketAddr *addr,
+   const void *parent G_GNUC_UNUSED,
+   void *opaque G_GNUC_UNUSED)
+{
+return VIR_SOCKET_ADDR_VALID(addr);
+}
diff --git a/src/util/virsocketaddr.h b/src/util/virsocketaddr.h
index f76e2297..cba87390 100644
--- a/src/util/virsocketaddr.h
+++ b/src/util/virsocketaddr.h
@@ -18,11 +18,13 @@
 
 #pragma once
 
+#include "virbuffer.h"
 #include "virsocket.h"
 
 #define VIR_LOOPBACK_IPV4_ADDR "127.0.0.1"
 
-typedef struct {
+typedef struct _virSocketAddr virSocketAddr;
+struct _virSocketAddr {
 union {
 struct sockaddr sa;
 struct sockaddr_storage stor;
@@ -33,7 +35,7 @@ typedef struct {
 #endif
 } data;
 socklen_t len;
-} virSocketAddr;
+};
 
 #define VIR_SOCKET_ADDR_VALID(s) \
 ((s)->data.sa.sa_family != AF_UNSPEC)
@@ -66,6 +68,12 @@ int virSocketAddrParse(virSocketAddr *addr,
const char *val,
int family);
 
+int virSocketAddrParseXML(const char *val,
+  virSocketAddr *addr,
+  const char *instname,
+  void *parent,
+  void *opaque);
+
 int virSocketAddrParseAny(virSocketAddr *addr,
   const char *val,
   int family,
@@ -89,6 +97,12 @@ char *virSocketAddrFormatFull(const virSocketAddr *addr,
   bool withService,
   const char *separator);
 
+int virSocketAddrFormatBuf(virBuffer *buf,
+   const char *fmt,
+   const virSocketAddr *addr,
+   const void *parent,
+   void *opaque);
+
 char *virSocketAddrGetPath(virSocketAddr *addr);
 
 int virSocketAddrSetPort(virSocketAddr *addr, int port);
@@ -141,5 +155,10 @@ int virSocketAddrPTRDomain(const virSocketAddr *addr,
 ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3);
 
 void virSocketAddrFree(virSocketAddr *addr);
+void virSocketAddrClear(virSocketAddr *addr);
 
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(virSocketAddr, virSocketAddrFree);
+
+bool virSocketAddrCheck(const virSocketAddr *addr,
+const void *parent,
+void *opaque);
-- 
2.25.1




[RFCv3 06/25] tests: Add tests for xmlgen

2021-04-22 Thread Shi Lei
Add some tests to make sure xmlgen's validity.

Signed-off-by: Shi Lei 
---
 tests/meson.build|   1 +
 tests/xmlgenin/conf/array.h  |  17 +
 tests/xmlgenin/conf/empty.h  |   7 +
 tests/xmlgenin/conf/enum-first-item.h|  12 +
 tests/xmlgenin/conf/external.h   |   9 +
 tests/xmlgenin/conf/genformat-separate.h |  11 +
 tests/xmlgenin/conf/genformat.h  |  11 +
 tests/xmlgenin/conf/genparse.h   |  11 +
 tests/xmlgenin/conf/namespace.h  |  12 +
 tests/xmlgenin/conf/required.h   |   9 +
 tests/xmlgenin/conf/skipparse.h  |  10 +
 tests/xmlgenin/conf/specify.h|  13 +
 tests/xmlgenin/conf/xmlattr.h|  10 +
 tests/xmlgenin/conf/xmlelem.h|  10 +
 tests/xmlgenin/conf/xmlgroup.h   |   8 +
 tests/xmlgenin/conf/xmlswitch.h  |  17 +
 tests/xmlgenin/util/enums.h  |  58 +++
 tests/xmlgenin/util/structs.h|  67 
 tests/xmlgenout/array.txt| 364 ++
 tests/xmlgenout/empty.txt| 181 +
 tests/xmlgenout/enum-first-item.txt  | 297 ++
 tests/xmlgenout/external.txt | 205 ++
 tests/xmlgenout/genformat-separate.txt   | 190 +
 tests/xmlgenout/genformat.txt| 142 +++
 tests/xmlgenout/genparse.txt | 154 
 tests/xmlgenout/namespace.txt| 222 +++
 tests/xmlgenout/required.txt | 236 
 tests/xmlgenout/skipparse.txt| 223 +++
 tests/xmlgenout/specify.txt  | 291 ++
 tests/xmlgenout/xmlattr.txt  | 252 
 tests/xmlgenout/xmlelem.txt  | 243 
 tests/xmlgenout/xmlgroup.txt | 204 ++
 tests/xmlgenout/xmlswitch.txt| 470 +++
 tests/xmlgentest.c   | 107 ++
 34 files changed, 4074 insertions(+)
 create mode 100644 tests/xmlgenin/conf/array.h
 create mode 100644 tests/xmlgenin/conf/empty.h
 create mode 100644 tests/xmlgenin/conf/enum-first-item.h
 create mode 100644 tests/xmlgenin/conf/external.h
 create mode 100644 tests/xmlgenin/conf/genformat-separate.h
 create mode 100644 tests/xmlgenin/conf/genformat.h
 create mode 100644 tests/xmlgenin/conf/genparse.h
 create mode 100644 tests/xmlgenin/conf/namespace.h
 create mode 100644 tests/xmlgenin/conf/required.h
 create mode 100644 tests/xmlgenin/conf/skipparse.h
 create mode 100644 tests/xmlgenin/conf/specify.h
 create mode 100644 tests/xmlgenin/conf/xmlattr.h
 create mode 100644 tests/xmlgenin/conf/xmlelem.h
 create mode 100644 tests/xmlgenin/conf/xmlgroup.h
 create mode 100644 tests/xmlgenin/conf/xmlswitch.h
 create mode 100644 tests/xmlgenin/util/enums.h
 create mode 100644 tests/xmlgenin/util/structs.h
 create mode 100644 tests/xmlgenout/array.txt
 create mode 100644 tests/xmlgenout/empty.txt
 create mode 100644 tests/xmlgenout/enum-first-item.txt
 create mode 100644 tests/xmlgenout/external.txt
 create mode 100644 tests/xmlgenout/genformat-separate.txt
 create mode 100644 tests/xmlgenout/genformat.txt
 create mode 100644 tests/xmlgenout/genparse.txt
 create mode 100644 tests/xmlgenout/namespace.txt
 create mode 100644 tests/xmlgenout/required.txt
 create mode 100644 tests/xmlgenout/skipparse.txt
 create mode 100644 tests/xmlgenout/specify.txt
 create mode 100644 tests/xmlgenout/xmlattr.txt
 create mode 100644 tests/xmlgenout/xmlelem.txt
 create mode 100644 tests/xmlgenout/xmlgroup.txt
 create mode 100644 tests/xmlgenout/xmlswitch.txt
 create mode 100644 tests/xmlgentest.c

diff --git a/tests/meson.build b/tests/meson.build
index 14ace476..166416cc 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -340,6 +340,7 @@ tests += [
   { 'name': 'viruritest' },
   { 'name': 'vshtabletest', 'link_with': [ libvirt_shell_lib ] },
   { 'name': 'virmigtest' },
+  { 'name': 'xmlgentest' },
 ]
 
 if host_machine.system() == 'linux'
diff --git a/tests/xmlgenin/conf/array.h b/tests/xmlgenin/conf/array.h
new file mode 100644
index ..8f003be4
--- /dev/null
+++ b/tests/xmlgenin/conf/array.h
@@ -0,0 +1,17 @@
+/* Test array features */
+
+#pragma once
+
+typedef struct _virArrayDef virArrayDef;
+struct _virArrayDef {   /* genparse, genformat */
+size_t nnames;
+char **names;   /* xmlelem:hostname, array */
+size_t nfwds;
+virNetworkDNSForwarder *forwarders; /* xmlelem, array:nfwds */
+size_t ntxts;
+virNetworkDNSTxtDef *txts;  /* xmlelem, array */
+
+size_t nroutes;
+/* ptr to array of static routes on this interface */
+virNetDevIPRoute **routes;  /* xmlelem, array */
+};
diff --git a/tests/xmlgenin/conf/empty.h b/tests/xmlgenin/conf/empty.h
new file mode 100644
index ..c8292c8e
--- /dev/null
+++

[RFCv3 20/25] conf: Extract virNetworkDNSForwarderParseXML from virNetworkDNSParseXML

2021-04-22 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/conf/network_conf.c | 68 +++--
 1 file changed, 52 insertions(+), 16 deletions(-)

diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index ba67eab1..cf9e77d3 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -871,6 +871,52 @@ virNetworkDNSTxtDefParseHook(xmlNodePtr node G_GNUC_UNUSED,
 }
 
 
+static int
+virNetworkDNSForwarderParseHook(xmlNodePtr node G_GNUC_UNUSED,
+virNetworkDNSForwarder *def,
+const char *instname G_GNUC_UNUSED,
+void *parent G_GNUC_UNUSED,
+void *opaque G_GNUC_UNUSED,
+const char *addr)
+{
+if (!(addr || def->domain)) {
+virReportError(VIR_ERR_XML_ERROR, "%s",
+   _("Invalid forwarder element, must contain "
+ "at least one of addr or domain"));
+return -1;
+}
+
+return 0;
+}
+
+
+static int
+virNetworkDNSForwarderParseXML(xmlNodePtr node,
+   virNetworkDNSForwarder *def,
+   const char *networkName,
+   void *parent G_GNUC_UNUSED,
+   void *opaque)
+{
+g_autofree char *addr = virXMLPropString(node, "addr");
+
+if (addr && virSocketAddrParse(&def->addr, addr, AF_UNSPEC) < 0) {
+virReportError(VIR_ERR_XML_ERROR,
+   _("Invalid forwarder IP address '%s' "
+ "in network '%s'"),
+   addr, networkName);
+return -1;
+}
+
+def->domain = virXMLPropString(node, "domain");
+
+if (virNetworkDNSForwarderParseHook(node, def, networkName, def, opaque,
+addr) < 0)
+return -1;
+
+return 0;
+}
+
+
 static int
 virNetworkDNSDefParseXML(const char *networkName,
  xmlNodePtr node,
@@ -924,23 +970,13 @@ virNetworkDNSDefParseXML(const char *networkName,
 def->forwarders = g_new0(virNetworkDNSForwarder, nfwds);
 
 for (i = 0; i < nfwds; i++) {
-g_autofree char *addr = virXMLPropString(fwdNodes[i], "addr");
-
-if (addr && virSocketAddrParse(&def->forwarders[i].addr,
-   addr, AF_UNSPEC) < 0) {
-virReportError(VIR_ERR_XML_ERROR,
-   _("Invalid forwarder IP address '%s' "
- "in network '%s'"),
-   addr, networkName);
+if (virNetworkDNSForwarderParseXML(fwdNodes[i],
+   &def->forwarders[i],
+   networkName,
+   def,
+   NULL) < 0)
 return -1;
-}
-def->forwarders[i].domain = virXMLPropString(fwdNodes[i], 
"domain");
-if (!(addr || def->forwarders[i].domain)) {
-virReportError(VIR_ERR_XML_ERROR, "%s",
-   _("Invalid forwarder element, must contain "
- "at least one of addr or domain"));
-return -1;
-}
+
 def->nfwds++;
 }
 }
-- 
2.25.1




[RFCv3 25/25] conf: Generate virNetworkDNSDefFormatBuf

2021-04-22 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/conf/network_conf.c | 73 +
 src/conf/network_conf.h |  2 +-
 2 files changed, 2 insertions(+), 73 deletions(-)

diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index 364c10e2..a652110c 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -1959,77 +1959,6 @@ virNetworkDefParseNode(xmlDocPtr xml,
 }
 
 
-static int
-virNetworkDNSDefFormat(virBuffer *buf,
-   const virNetworkDNSDef *def)
-{
-size_t i;
-
-if (!(def->enable || def->forwardPlainNames || def->nfwds || def->nhosts ||
-  def->nsrvs || def->ntxts))
-return 0;
-
-virBufferAddLit(buf, "enable) {
-const char *fwd = virTristateBoolTypeToString(def->enable);
-
-if (!fwd) {
-virReportError(VIR_ERR_INTERNAL_ERROR,
-   _("Unknown enable type %d in network"),
-   def->enable);
-return -1;
-}
-virBufferAsprintf(buf, " enable='%s'", fwd);
-}
-if (def->forwardPlainNames) {
-const char *fwd = virTristateBoolTypeToString(def->forwardPlainNames);
-
-if (!fwd) {
-virReportError(VIR_ERR_INTERNAL_ERROR,
-   _("Unknown forwardPlainNames type %d in network"),
-   def->forwardPlainNames);
-return -1;
-}
-virBufferAsprintf(buf, " forwardPlainNames='%s'", fwd);
-}
-if (!(def->nfwds || def->nhosts || def->nsrvs || def->ntxts)) {
-virBufferAddLit(buf, "/>\n");
-return 0;
-}
-
-virBufferAddLit(buf, ">\n");
-virBufferAdjustIndent(buf, 2);
-
-for (i = 0; i < def->nfwds; i++) {
-if (virNetworkDNSForwarderFormatBuf(buf, "forwarder",
-&def->forwarders[i], def, NULL) < 
0)
-return -1;
-}
-
-for (i = 0; i < def->ntxts; i++) {
-if (virNetworkDNSTxtDefFormatBuf(buf, "txt", &def->txts[i], def, NULL) 
< 0)
-return -1;
-}
-
-for (i = 0; i < def->nsrvs; i++) {
-if (def->srvs[i].service && def->srvs[i].protocol) {
-if (virNetworkDNSSrvDefFormatBuf(buf, "srv", &def->srvs[i], def, 
NULL) < 0)
-return -1;
-}
-}
-
-if (def->nhosts) {
-for (i = 0; i < def->nhosts; i++) {
-if (virNetworkDNSHostDefFormatBuf(buf, "host", &def->hosts[i], 
def, NULL) < 0)
-return -1;
-}
-}
-virBufferAdjustIndent(buf, -2);
-virBufferAddLit(buf, "\n");
-return 0;
-}
-
-
 static int
 virNetworkIPDefFormat(virBuffer *buf,
   const virNetworkIPDef *def)
@@ -2436,7 +2365,7 @@ virNetworkDefFormatBuf(virBuffer *buf,
 virBufferAddLit(buf, "/>\n");
 }
 
-if (virNetworkDNSDefFormat(buf, &def->dns) < 0)
+if (virNetworkDNSDefFormatBuf(buf, "dns", &def->dns, def, NULL) < 0)
 return -1;
 
 if (virNetDevVlanFormat(&def->vlan, buf) < 0)
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index 9968d962..b0675175 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -156,7 +156,7 @@ struct _virNetworkDNSForwarder {/* genparse, genformat 
*/
 };
 
 typedef struct _virNetworkDNSDef virNetworkDNSDef;
-struct _virNetworkDNSDef {  /* genparse */
+struct _virNetworkDNSDef {  /* genparse, genformat */
 virTristateBool enable; /* xmlattr */
 virTristateBool forwardPlainNames;  /* xmlattr */
 size_t nfwds;
-- 
2.25.1




[RFCv3 05/25] build-aux: Only check *.[ch] for sc_prohibit_useless_translation

2021-04-22 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 build-aux/syntax-check.mk | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build-aux/syntax-check.mk b/build-aux/syntax-check.mk
index 552d6391..614f21bc 100644
--- a/build-aux/syntax-check.mk
+++ b/build-aux/syntax-check.mk
@@ -735,7 +735,7 @@ sc_prohibit_useless_translation:
halt='found useless translation' \
  $(_sc_search_regexp)
@prohibit='\

[RFCv3 23/25] conf: Extract error-checking code from virNetworkDNSDefParseXML

2021-04-22 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/conf/network_conf.c | 34 --
 1 file changed, 28 insertions(+), 6 deletions(-)

diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index be639500..19408987 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -883,6 +883,31 @@ virNetworkDNSForwarderParseHook(xmlNodePtr node 
G_GNUC_UNUSED,
 }
 
 
+static int
+virNetworkDNSDefParseHook(xmlNodePtr node G_GNUC_UNUSED,
+  virNetworkDNSDef *def,
+  const char *networkName,
+  void *parent G_GNUC_UNUSED,
+  void *opaque G_GNUC_UNUSED,
+  const char *enable G_GNUC_UNUSED,
+  const char *forwardPlainNames G_GNUC_UNUSED,
+  int nfwds,
+  int ntxts,
+  int nsrvs,
+  int nhosts)
+{
+if (def->enable == VIR_TRISTATE_BOOL_NO &&
+(nfwds || nhosts || nsrvs || ntxts)) {
+virReportError(VIR_ERR_XML_ERROR,
+   _("Extra data in disabled network '%s'"),
+   networkName);
+return -1;
+}
+
+return 0;
+}
+
+
 static int
 virNetworkDNSDefParseXML(const char *networkName,
  xmlNodePtr node,
@@ -1004,13 +1029,10 @@ virNetworkDNSDefParseXML(const char *networkName,
 }
 }
 
-if (def->enable == VIR_TRISTATE_BOOL_NO &&
-(nfwds || nhosts || nsrvs || ntxts)) {
-virReportError(VIR_ERR_XML_ERROR,
-   _("Extra data in disabled network '%s'"),
-   networkName);
+if (virNetworkDNSDefParseHook(node, def, networkName, def, NULL,
+  enable, forwardPlainNames,
+  nfwds, ntxts, nsrvs, nhosts) < 0)
 return -1;
-}
 
 return 0;
 }
-- 
2.25.1




[RFCv3 12/25] conf: Replace virNetworkDNSTxtDefParseXML(hardcoded) with namesake(generated)

2021-04-22 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 po/POTFILES.in  |  1 +
 src/conf/meson.build|  1 +
 src/conf/network_conf.c | 45 ++---
 src/conf/network_conf.h |  9 ++---
 4 files changed, 14 insertions(+), 42 deletions(-)

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9740bb2b..fe20b9d7 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -3,6 +3,7 @@
 @BUILDDIR@src/access/viraccessapicheckqemu.c
 @BUILDDIR@src/admin/admin_client.h
 @BUILDDIR@src/admin/admin_server_dispatch_stubs.h
+@BUILDDIR@src/conf/network_conf.generated.c
 @BUILDDIR@src/remote/remote_client_bodies.h
 @BUILDDIR@src/remote/remote_daemon_dispatch_stubs.h
 @SRCDIR@scripts/xmlgen/directive.py
diff --git a/src/conf/meson.build b/src/conf/meson.build
index 1439c31d..f1cfe35c 100644
--- a/src/conf/meson.build
+++ b/src/conf/meson.build
@@ -1,4 +1,5 @@
 conf_xmlgen_input = [
+  'network_conf.h',
 ]
 
 conf_xmlgen_output = []
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index 87157591..a6c2f11a 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -174,14 +174,6 @@ virNetworkIPDefClear(virNetworkIPDef *def)
 }
 
 
-static void
-virNetworkDNSTxtDefClear(virNetworkDNSTxtDef *def)
-{
-VIR_FREE(def->name);
-VIR_FREE(def->value);
-}
-
-
 static void
 virNetworkDNSHostDefClear(virNetworkDNSHostDef *def)
 {
@@ -886,7 +878,7 @@ virNetworkDNSSrvDefParseXML(const char *networkName,
 }
 
 
-static int
+int
 virNetworkDNSTxtDefParseHook(xmlNodePtr node G_GNUC_UNUSED,
  virNetworkDNSTxtDef *def,
  const char *networkName,
@@ -927,33 +919,6 @@ virNetworkDNSTxtDefParseHook(xmlNodePtr node G_GNUC_UNUSED,
 }
 
 
-static int
-virNetworkDNSTxtDefParseXML(const char *networkName,
-xmlNodePtr node,
-virNetworkDNSTxtDef *def,
-bool partialOkay)
-{
-if (!(def->name = virXMLPropString(node, "name"))) {
-virReportError(VIR_ERR_XML_DETAIL,
-   _("missing required name attribute in DNS TXT record "
- "of network %s"), networkName);
-goto error;
-}
-
-def->value = virXMLPropString(node, "value");
-
-if (virNetworkDNSTxtDefParseHook(node, def, networkName,
- NULL, &partialOkay) < 0)
-goto error;
-
-return 0;
-
- error:
-virNetworkDNSTxtDefClear(def);
-return -1;
-}
-
-
 static int
 virNetworkDNSDefParseXML(const char *networkName,
  xmlNodePtr node,
@@ -1077,8 +1042,8 @@ virNetworkDNSDefParseXML(const char *networkName,
 def->txts = g_new0(virNetworkDNSTxtDef, ntxts);
 
 for (i = 0; i < ntxts; i++) {
-if (virNetworkDNSTxtDefParseXML(networkName, txtNodes[i],
-&def->txts[def->ntxts], false) < 
0) {
+if (virNetworkDNSTxtDefParseXML(txtNodes[i], 
&def->txts[def->ntxts],
+networkName, def, NULL) < 0) {
 return -1;
 }
 def->ntxts++;
@@ -3614,6 +3579,7 @@ virNetworkDefUpdateDNSTxt(virNetworkDef *def,
 virNetworkDNSTxtDef txt;
 bool isAdd = (command == VIR_NETWORK_UPDATE_COMMAND_ADD_FIRST ||
   command == VIR_NETWORK_UPDATE_COMMAND_ADD_LAST);
+bool notAdd;
 
 memset(&txt, 0, sizeof(txt));
 
@@ -3627,7 +3593,8 @@ virNetworkDefUpdateDNSTxt(virNetworkDef *def,
 if (virNetworkDefUpdateCheckElementName(def, ctxt->node, "txt") < 0)
 goto cleanup;
 
-if (virNetworkDNSTxtDefParseXML(def->name, ctxt->node, &txt, !isAdd) < 0)
+notAdd = !isAdd;
+if (virNetworkDNSTxtDefParseXML(ctxt->node, &txt, def->name, def, ¬Add) 
< 0)
 goto cleanup;
 
 for (foundIdx = 0; foundIdx < dns->ntxts; foundIdx++) {
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index a7e6b7a2..f5720e5e 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -125,9 +125,9 @@ struct _virNetworkDHCPHostDef {
 };
 
 typedef struct _virNetworkDNSTxtDef virNetworkDNSTxtDef;
-struct _virNetworkDNSTxtDef {
-char *name;
-char *value;
+struct _virNetworkDNSTxtDef {   /* genparse */
+char *name; /* xmlattr, required */
+char *value;/* xmlattr */
 };
 
 typedef struct _virNetworkDNSSrvDef virNetworkDNSSrvDef;
@@ -428,3 +428,6 @@ virNetworkDefUpdateSection(virNetworkDef *def,
unsigned int flags);  /* virNetworkUpdateFlags */
 
 VIR_ENUM_DECL(virNetworkTaint);
+
+#define ENABLE_VIR_NETWORK_DNSTXT_DEF_PARSE_HOOK
+#include "network_conf.generated.h"
-- 
2.25.1




[RFCv3 16/25] conf: Generate virNetworkDNSSrvDefFormatBuf

2021-04-22 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/conf/network_conf.c | 18 ++
 src/conf/network_conf.h |  2 +-
 2 files changed, 3 insertions(+), 17 deletions(-)

diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index cb2f3163..146c4977 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -2191,22 +2191,8 @@ virNetworkDNSDefFormat(virBuffer *buf,
 
 for (i = 0; i < def->nsrvs; i++) {
 if (def->srvs[i].service && def->srvs[i].protocol) {
-virBufferEscapeString(buf, "srvs[i].service);
-virBufferEscapeString(buf, "protocol='%s'", def->srvs[i].protocol);
-
-if (def->srvs[i].domain)
-virBufferEscapeString(buf, " domain='%s'", 
def->srvs[i].domain);
-if (def->srvs[i].target)
-virBufferEscapeString(buf, " target='%s'", 
def->srvs[i].target);
-if (def->srvs[i].port)
-virBufferAsprintf(buf, " port='%d'", def->srvs[i].port);
-if (def->srvs[i].priority)
-virBufferAsprintf(buf, " priority='%d'", 
def->srvs[i].priority);
-if (def->srvs[i].weight)
-virBufferAsprintf(buf, " weight='%d'", def->srvs[i].weight);
-
-virBufferAddLit(buf, "/>\n");
+if (virNetworkDNSSrvDefFormatBuf(buf, "srv", &def->srvs[i], def, 
NULL) < 0)
+return -1;
 }
 }
 
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index a58d8953..052ccb58 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -131,7 +131,7 @@ struct _virNetworkDNSTxtDef {   /* genparse, genformat */
 };
 
 typedef struct _virNetworkDNSSrvDef virNetworkDNSSrvDef;
-struct _virNetworkDNSSrvDef {   /* genparse */
+struct _virNetworkDNSSrvDef {   /* genparse, genformat */
 char *service;  /* xmlattr */
 char *protocol; /* xmlattr */
 char *domain;   /* xmlattr */
-- 
2.25.1




[RFCv3 03/25] maint: Call xmlgen automatically when c-head-files change

2021-04-22 Thread Shi Lei
Monitor changes of header-files in src/util and src/conf.
Whenever that happens, the tool xmlgen will generate
parse/format functions based on these files automatically.

Signed-off-by: Shi Lei 
---
 scripts/meson.build  |  8 
 src/conf/meson.build | 36 
 src/meson.build  |  6 ++
 src/util/meson.build | 36 
 tests/meson.build|  2 ++
 tools/meson.build|  3 +++
 6 files changed, 91 insertions(+)

diff --git a/scripts/meson.build b/scripts/meson.build
index 421e3d2a..5399868c 100644
--- a/scripts/meson.build
+++ b/scripts/meson.build
@@ -35,3 +35,11 @@ foreach name : scripts
   sname = name.split('.')[0].underscorify()
   set_variable('@0@_prog'.format(sname), find_program(name))
 endforeach
+
+xmlgen_self = files(
+  'xmlgen/main.py',
+  'xmlgen/directive.py',
+  'xmlgen/utils.py'
+)
+
+set_variable('virxmlgen_prog', find_program('xmlgen/main.py'))
diff --git a/src/conf/meson.build b/src/conf/meson.build
index bd35d87e..1439c31d 100644
--- a/src/conf/meson.build
+++ b/src/conf/meson.build
@@ -1,3 +1,38 @@
+conf_xmlgen_input = [
+]
+
+conf_xmlgen_output = []
+foreach name : conf_xmlgen_input
+  conf_xmlgen_output += '@0@.generated.c'.format(name.split('.')[0])
+  conf_xmlgen_output += '@0@.generated.h'.format(name.split('.')[0])
+endforeach
+
+conf_xmlgen_headers = []
+if conf_xmlgen_output.length() > 0
+  conf_xmlgen_objects = custom_target(
+'virxmlgen',
+input: xmlgen_self + conf_xmlgen_input,
+output: conf_xmlgen_output,
+command: [
+  meson_python_prog, python3_prog.path(), '-B', virxmlgen_prog.path(),
+  '-s', meson.source_root() / 'src', '-b', meson.build_root() / 'src',
+  '-d', 'conf', 'generate',
+],
+  )
+
+  index = 0
+  foreach header : conf_xmlgen_objects.to_list()
+if index % 2 == 1
+  conf_xmlgen_headers += header
+endif
+index += 1
+  endforeach
+else
+  conf_xmlgen_objects = []
+endif
+
+conf_xmlgen_dep = declare_dependency(sources: conf_xmlgen_headers)
+
 netdev_conf_sources = [
   'netdev_bandwidth_conf.c',
   'netdev_vlan_conf.c',
@@ -90,6 +125,7 @@ device_conf_sources = [
 virt_conf_lib = static_library(
   'virt_conf',
   [
+conf_xmlgen_objects,
 chrdev_conf_sources,
 cpu_conf_sources,
 device_conf_sources,
diff --git a/src/meson.build b/src/meson.build
index c7ff9e97..f8ae47b4 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -245,6 +245,12 @@ src_dep = declare_dependency(
 )
 
 subdir('conf')
+
+src_dep = declare_dependency(
+  dependencies: [ src_dep, util_xmlgen_dep, conf_xmlgen_dep ],
+  include_directories: [ conf_inc_dir ],
+)
+
 subdir('rpc')
 subdir('access')
 subdir('cpu')
diff --git a/src/util/meson.build b/src/util/meson.build
index 05934f68..0d41de92 100644
--- a/src/util/meson.build
+++ b/src/util/meson.build
@@ -1,3 +1,38 @@
+util_xmlgen_input = [
+]
+
+util_xmlgen_output = []
+foreach name : util_xmlgen_input
+  util_xmlgen_output += '@0@.generated.c'.format(name.split('.')[0])
+  util_xmlgen_output += '@0@.generated.h'.format(name.split('.')[0])
+endforeach
+
+util_xmlgen_headers = []
+if util_xmlgen_output.length() > 0
+  util_xmlgen_objects = custom_target(
+'virxmlgen',
+input: xmlgen_self + util_xmlgen_input,
+output: util_xmlgen_output,
+command: [
+  meson_python_prog, python3_prog.path(), '-B', virxmlgen_prog.path(),
+  '-s', meson.source_root() / 'src', '-b', meson.build_root() / 'src',
+  '-d', 'util', 'generate',
+],
+  )
+
+  index = 0
+  foreach header : util_xmlgen_objects.to_list()
+if index % 2 == 1
+  util_xmlgen_headers += header
+endif
+index += 1
+  endforeach
+else
+  util_xmlgen_objects = []
+endif
+
+util_xmlgen_dep = declare_dependency(sources: util_xmlgen_headers)
+
 util_sources = [
   'glibcompat.c',
   'viralloc.c',
@@ -179,6 +214,7 @@ io_helper_sources = [
 virt_util_lib = static_library(
   'virt_util',
   [
+util_xmlgen_objects,
 util_sources,
 util_public_sources,
 keycode_gen_sources,
diff --git a/tests/meson.build b/tests/meson.build
index 05c3e901..14ace476 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -17,6 +17,8 @@ tests_dep = declare_dependency(
 selinux_dep,
 xdr_dep,
 yajl_dep,
+util_xmlgen_dep,
+conf_xmlgen_dep,
   ],
   include_directories: [
 conf_inc_dir,
diff --git a/tools/meson.build b/tools/meson.build
index 2acf7b0a..162db0e8 100644
--- a/tools/meson.build
+++ b/tools/meson.build
@@ -5,11 +5,14 @@ tools_dep = declare_dependency(
   dependencies: [
 libxml_dep,
 glib_dep,
+util_xmlgen_dep,
+conf_xmlgen_dep,
   ],
   include_directories: [
 libvirt_inc,
 src_inc_dir,
 util_inc_dir,
+conf_inc_dir,
 top_inc_dir,
   ],
   link_args: (
-- 
2.25.1




[RFCv3 01/25] scripts: Add a tool to generate xml parse/format functions

2021-04-22 Thread Shi Lei
This tool is used to generate parsexml/formatbuf/clear functions.
It is based on libclang and its python-binding.
Some directives (such as genparse, xmlattr, etc.) need to be added on
the declarations of structs to direct the tool.

Signed-off-by: Shi Lei 
---
 po/POTFILES.in  |1 +
 scripts/xmlgen/directive.py | 1192 +++
 scripts/xmlgen/go   |   29 +
 scripts/xmlgen/main.py  |  534 
 scripts/xmlgen/utils.py |  121 
 5 files changed, 1877 insertions(+)
 create mode 100644 scripts/xmlgen/directive.py
 create mode 100755 scripts/xmlgen/go
 create mode 100755 scripts/xmlgen/main.py
 create mode 100644 scripts/xmlgen/utils.py

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 413783ee..9740bb2b 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -5,6 +5,7 @@
 @BUILDDIR@src/admin/admin_server_dispatch_stubs.h
 @BUILDDIR@src/remote/remote_client_bodies.h
 @BUILDDIR@src/remote/remote_daemon_dispatch_stubs.h
+@SRCDIR@scripts/xmlgen/directive.py
 @SRCDIR@src/access/viraccessdriverpolkit.c
 @SRCDIR@src/access/viraccessmanager.c
 @SRCDIR@src/admin/admin_server.c
diff --git a/scripts/xmlgen/directive.py b/scripts/xmlgen/directive.py
new file mode 100644
index ..cc8fb5aa
--- /dev/null
+++ b/scripts/xmlgen/directive.py
@@ -0,0 +1,1192 @@
+#
+# Copyright (C) 2021 Shandong Massclouds Co.,Ltd.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library.  If not, see
+# <http://www.gnu.org/licenses/>.
+#
+
+import json
+from collections import OrderedDict
+from utils import singleton, dedup, Block, Terms, render
+
+BUILTIN_TYPES = {
+'String': {},
+'Bool': {
+'conv': 'virStrToBoolYesNo(${mdvar}, &def->${name})'
+},
+'BoolYesNo': {
+'conv': 'virStrToBoolYesNo(${mdvar}, &def->${name})'
+},
+'BoolOnOff': {
+'conv': 'virStrToBoolOnOff(${mdvar}, &def->${name})'
+},
+'BoolTrueFalse': {
+'conv': 'virStrToBoolTrueFalse(${mdvar}, &def->${name})'
+},
+'Chars': {
+'conv': 'virStrcpyStatic(def->${name}, ${name}Str)'
+},
+'UChars': {
+'conv': 'virStrcpyStatic((char *)def->${name}, ${mdvar})'
+},
+'Int': {
+'fmt': '%d',
+'conv': 'virStrToLong_i(${mdvar}, NULL, 0, &def->${name})'
+},
+'UInt': {
+'fmt': '%u',
+'conv': 'virStrToLong_uip(${mdvar}, NULL, 0, &def->${name})'
+},
+'ULong': {
+'fmt': '%lu',
+'conv': 'virStrToLong_ulp(${mdvar}, NULL, 0, &def->${name})'
+},
+'ULongLong': {
+'fmt': '%llu',
+'conv': 'virStrToLong_ullp(${mdvar}, NULL, 0, &def->${name})'
+},
+'U8': {
+'fmt': '%u',
+'conv': 'virStrToLong_u8p(${mdvar}, NULL, 0, &def->${name})'
+},
+'U32': {
+'fmt': '%u',
+'conv': 'virStrToLong_uip(${mdvar}, NULL, 0, &def->${name})'
+},
+'Time': {
+'conv': 'virStrToTime(${mdvar}, &def->${name})'
+},
+}
+
+
+@singleton
+class TypeTable(OrderedDict):
+def __init__(self):
+OrderedDict.__init__(self)
+for name, kvs in BUILTIN_TYPES.items():
+kvs['name'] = name
+kvs['meta'] = 'Builtin'
+self[name] = kvs
+
+def register(self, kvs):
+name = kvs['name']
+if name not in self:
+self[name] = kvs
+return name
+
+def get(self, name):
+if name in self:
+return self[name]
+return {'meta': 'Struct', 'name': name, 'external': True}
+
+def check(self, name):
+return name in self
+
+
+T_NAMESPACE_PARSE = [
+'if (xmlopt)',
+'def->ns = xmlopt->ns;',
+'if (def->ns.parse) {',
+'

[RFCv3 04/25] docs: Add xmlgen.rst to explain how to use it

2021-04-22 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 docs/meson.build |   1 +
 docs/xmlgen.rst  | 684 +++
 2 files changed, 685 insertions(+)
 create mode 100644 docs/xmlgen.rst

diff --git a/docs/meson.build b/docs/meson.build
index f550629d..a8a58815 100644
--- a/docs/meson.build
+++ b/docs/meson.build
@@ -124,6 +124,7 @@ docs_rst_files = [
   'programming-languages',
   'styleguide',
   'submitting-patches',
+  'xmlgen',
 ]
 
 # list of web targets to build for docs/web rule
diff --git a/docs/xmlgen.rst b/docs/xmlgen.rst
new file mode 100644
index ..caea1f99
--- /dev/null
+++ b/docs/xmlgen.rst
@@ -0,0 +1,684 @@
+==
+xmlgen
+==
+
+In libvirt, developers usually have to implement and maintain parse/format 
functions. This tool xmlgen aims to generate most of these functions 
automatically and help relieve developers' burden.
+
+A Quick Start
+=
+
+Take virNetworkDNSDef for example, which is in 'src/conf/network_conf.h'.
+
+In the past, we have to manually implement virNetworkDNSDefParseXML and 
virNetworkDNSFormatBuf.
+And now, we can just take several steps to have xmlgen generate these 
functions and apply them into libvirt project.
+
+Step1. Add directives on the struct's declaration.
+--
+
+Directives for xmlgen are used to help direct the generating process. As below:
+
+ ::
+
+  typedef struct _virNetworkDNSDef virNetworkDNSDef;
+  struct _virNetworkDNSDef {  /* genparse, genformat */
+  virTristateBool enable; /* xmlattr */
+  virTristateBool forwardPlainNames;  /* xmlattr */
+  size_t nfwds;
+  virNetworkDNSForwarder *forwarders; /* xmlelem, array:nfwds */
+  size_t ntxts;
+  virNetworkDNSTxtDef *txts;  /* xmlelem, array */
+  ... ...
+  };
+
+On the line of struct's declaration, we set two directives **genparse** and 
**genformat**, which direct xmlgen to generate parse/format functions for this 
struct respectively. In this example, these functions include 
virNetworkDNSDefParseXML, virNetworkDNSDefFormatBuf and some auxilliary 
functions. Other directives are for members. They direct xmlgen to generate 
code blocks in the parse/format functions. Directive **xmlattr** indicates that 
the member matches an xml attribute, and **xmlelem** is for an xml element. 
Additional directive **array** indicates that the member matches an array of 
xml node.
+
+Step2. Preview generated functions.
+---
+
+By the below command line:
+
+ ::
+
+  # ./scripts/xmlgen/go list
+
+Got a list of structs detected by xmlgen, including *virNetworkDNSDef*.
+Then we execute the command line as below:
+
+ ::
+
+  # ./scripts/xmlgen/go show virNetworkDNSDef
+
+All the generated functions related to virNetworkDNSDef are displayed, then we 
ought to preview them before really use them into libvirt project.
+There is a special part **[Tips]** except the generated functions and the 
declaration of hooks.
+
+[**Tips**] provides instructions about how to apply these generated functions 
into project and how to enable hooks. [**Tips**] will be used in step3.
+
+Also, we got the declaration of hooks. In step4, we will implement a hook 
according to it.
+
+Step3. Enable hooks and include generated functions.
+
+
+According to [**Tips**] that we got in step2:
+
+ ::
+
+  [Tips]
+
+  /* Put these lines at the bottom of "conf/network_conf.h" */
+  /* Makesure "network_conf.h" to be appended into conf_xmlgen_input in 
src/conf/meson.build */
+
+  /* Define macro to enable hook or redefine check when necessary */
+  /* #define ENABLE_VIR_NETWORK_DNSDEF_PARSE_HOOK */
+  /* #define ENABLE_VIR_NETWORK_DNSDEF_PARSE_HOOK_SET_ARGS */
+  /* #define ENABLE_VIR_NETWORK_DNSDEF_FORMAT_HOOK */
+
+  /* #define RESET_VIR_NETWORK_DNSDEF_CHECK */
+
+  /* Makesure below is the bottom line! */
+  #include "network_conf.generated.h"
+
+Uncomment macros and enable hooks when necessary. In this example, we only 
need to define ENABLE_VIR_NETWORK_DNSDEF_PARSE_HOOK to enable the post-hook for 
parse-function.
+
+Include the header-file to apply the generated functions.
+In this example, we just include "network_conf.generated.h" into 
"conf/network_conf.h" and modify "src/conf/meson.build" as instructed.
+
+Step4. Implement hooks when necessary.
+--
+
+In original implementation of virNetworkDNSDefParseXML, there's a piece of 
error-checking code as below:
+
+ ::
+
+  if (def->enable == VIR_TRISTATE_BOOL_NO && (nfwds || nhosts || nsrvs || 
ntxts)) {
+  virReportError(VIR_ERR_XML_ERROR,
+  _("Extra data in disabled network '%s'"),
+  networkName);
+  }

[RFCv3 13/25] conf: Generate virNetworkDNSTxtDefFormatBuf

2021-04-22 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/conf/network_conf.c | 4 ++--
 src/conf/network_conf.h | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index a6c2f11a..bb976a78 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -2199,8 +2199,8 @@ virNetworkDNSDefFormat(virBuffer *buf,
 }
 
 for (i = 0; i < def->ntxts; i++) {
-virBufferEscapeString(buf, "txts[i].name);
-virBufferEscapeString(buf, "value='%s'/>\n", def->txts[i].value);
+if (virNetworkDNSTxtDefFormatBuf(buf, "txt", &def->txts[i], def, NULL) 
< 0)
+return -1;
 }
 
 for (i = 0; i < def->nsrvs; i++) {
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index f5720e5e..a4c83b46 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -125,7 +125,7 @@ struct _virNetworkDHCPHostDef {
 };
 
 typedef struct _virNetworkDNSTxtDef virNetworkDNSTxtDef;
-struct _virNetworkDNSTxtDef {   /* genparse */
+struct _virNetworkDNSTxtDef {   /* genparse, genformat */
 char *name; /* xmlattr, required */
 char *value;/* xmlattr */
 };
-- 
2.25.1




[RFCv3 02/25] maint: Check python3-clang and libclang

2021-04-22 Thread Shi Lei
Make sure 'python3-clang' and 'libclang' have been installed
and can work. Also, add 'python3-clang' into libvirt.spec.in
and mingw-libvirt.spec.in.

Signed-off-by: Shi Lei 
---
 libvirt.spec.in   |  1 +
 meson.build   | 10 ++
 mingw-libvirt.spec.in |  1 +
 3 files changed, 12 insertions(+)

diff --git a/libvirt.spec.in b/libvirt.spec.in
index be74964b..4ebd67ce 100644
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -277,6 +277,7 @@ BuildRequires: perl-interpreter
 BuildRequires: perl
 %endif
 BuildRequires: python3
+BuildRequires: python3-clang
 BuildRequires: systemd-units
 %if %{with_libxl}
 BuildRequires: xen-devel
diff --git a/meson.build b/meson.build
index 837955de..a99be250 100644
--- a/meson.build
+++ b/meson.build
@@ -2406,3 +2406,13 @@ if conf.has('WITH_QEMU')
   }
   summary(priv_summary, section: 'Privileges')
 endif
+
+py3_clang = run_command('python3', '-c', 'import clang.cindex;print("ok")')
+if py3_clang.returncode() != 0
+  error('python3-clang is required.')
+endif
+
+py3_clang_working = run_command('python3', '-c', 'import 
clang.cindex;clang.cindex.Index.create()')
+if py3_clang_working.returncode() != 0
+  error('python3-clang is present, but not working. Perhaps libclang is 
missing?')
+endif
diff --git a/mingw-libvirt.spec.in b/mingw-libvirt.spec.in
index 288f533d..00b54d4a 100644
--- a/mingw-libvirt.spec.in
+++ b/mingw-libvirt.spec.in
@@ -52,6 +52,7 @@ BuildRequires:  pkgconfig
 BuildRequires:  gettext
 BuildRequires:  libxslt
 BuildRequires:  python3
+BuildRequires:  python3-clang
 BuildRequires:  perl-interpreter
 BuildRequires:  perl(Getopt::Long)
 BuildRequires:  meson
-- 
2.25.1




[RFCv3 18/25] conf: Replace virNetworkDNSHostDefParseXML(hardcoded) with namesake(generated)

2021-04-22 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/conf/network_conf.c | 71 -
 src/conf/network_conf.h |  7 ++--
 2 files changed, 10 insertions(+), 68 deletions(-)

diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index b326ef5f..90b1e0ee 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -174,15 +174,6 @@ virNetworkIPDefClear(virNetworkIPDef *def)
 }
 
 
-static void
-virNetworkDNSHostDefClear(virNetworkDNSHostDef *def)
-{
-while (def->nnames)
-VIR_FREE(def->names[--def->nnames]);
-VIR_FREE(def->names);
-}
-
-
 static void
 virNetworkDNSForwarderClear(virNetworkDNSForwarder *def)
 {
@@ -675,7 +666,7 @@ virNetworkDHCPDefParseXML(const char *networkName,
 }
 
 
-static int
+int
 virNetworkDNSHostDefParseHook(xmlNodePtr node G_GNUC_UNUSED,
   virNetworkDNSHostDef *def,
   const char *networkName,
@@ -717,58 +708,6 @@ virNetworkDNSHostDefParseHook(xmlNodePtr node 
G_GNUC_UNUSED,
 }
 
 
-static int
-virNetworkDNSHostDefParseXML(const char *networkName,
- xmlNodePtr node,
- virNetworkDNSHostDef *def,
- bool partialOkay)
-{
-xmlNodePtr cur;
-g_autofree char *ip = NULL;
-
-ip = virXMLPropString(node, "ip");
-if (ip && (virSocketAddrParse(&def->ip, ip, AF_UNSPEC) < 0)) {
-virReportError(VIR_ERR_XML_DETAIL,
-   _("Invalid IP address in network '%s' DNS HOST record"),
-   networkName);
-goto error;
-}
-
-cur = node->children;
-while (cur != NULL) {
-if (cur->type == XML_ELEMENT_NODE &&
-virXMLNodeNameEqual(cur, "hostname")) {
-  if (cur->children != NULL) {
-  g_autofree char *name = virXMLNodeContentString(cur);
-
-  if (!name)
-  goto error;
-
-  if (!name[0]) {
-  virReportError(VIR_ERR_XML_DETAIL,
- _("Missing hostname in network '%s' DNS 
HOST record"),
- networkName);
-  goto error;
-  }
-  if (VIR_APPEND_ELEMENT(def->names, def->nnames, name) < 0)
-  goto error;
-  }
-}
-cur = cur->next;
-}
-
-if (virNetworkDNSHostDefParseHook(node, def, networkName, def, 
&partialOkay,
-  ip, def->nnames) < 0)
-goto error;
-
-return 0;
-
- error:
-virNetworkDNSHostDefClear(def);
-return -1;
-}
-
-
 /* This includes all characters used in the names of current
  * /etc/services and /etc/protocols files (on Fedora 20), except ".",
  * which we can't allow because it would conflict with the use of "."
@@ -1017,8 +956,8 @@ virNetworkDNSDefParseXML(const char *networkName,
 def->hosts = g_new0(virNetworkDNSHostDef, nhosts);
 
 for (i = 0; i < nhosts; i++) {
-if (virNetworkDNSHostDefParseXML(networkName, hostNodes[i],
- &def->hosts[def->nhosts], false) 
< 0) {
+if (virNetworkDNSHostDefParseXML(hostNodes[i], 
&def->hosts[def->nhosts],
+ networkName, def, NULL) < 0) {
 return -1;
 }
 def->nhosts++;
@@ -3398,6 +3337,7 @@ virNetworkDefUpdateDNSHost(virNetworkDef *def,
 bool isAdd = (command == VIR_NETWORK_UPDATE_COMMAND_ADD_FIRST ||
   command == VIR_NETWORK_UPDATE_COMMAND_ADD_LAST);
 int foundCt = 0;
+bool notAdd;
 
 memset(&host, 0, sizeof(host));
 
@@ -3411,7 +3351,8 @@ virNetworkDefUpdateDNSHost(virNetworkDef *def,
 if (virNetworkDefUpdateCheckElementName(def, ctxt->node, "host") < 0)
 goto cleanup;
 
-if (virNetworkDNSHostDefParseXML(def->name, ctxt->node, &host, !isAdd) < 0)
+notAdd = !isAdd;
+if (virNetworkDNSHostDefParseXML(ctxt->node, &host, def->name, def, 
¬Add) < 0)
 goto cleanup;
 
 for (i = 0; i < dns->nhosts; i++) {
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index 052ccb58..998411be 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -142,10 +142,10 @@ struct _virNetworkDNSSrvDef {   /* genparse, genformat */
 };
 
 typedef struct _virNetworkDNSHostDef virNetworkDNSHostDef;
-struct _virNetworkDNSHostDef {
-virSocketAddr ip;
+struct _virNetworkDNSHostDef {  /* genparse */
+virSocketAddr ip;   /* xmlattr */
 size_t nnames;
-char **names;
+char **names;   /* xmlelem:hostname, array */
 };
 
 
@@ -429,6 +429,7 @@ vir

[RFCv3 24/25] conf: Replace virNetworkDNSDefParseXML(hardcoded) with namesake(generated)

2021-04-22 Thread Shi Lei
Reorder the members of virNetworkDNSDef according to their orders in
format function.

Signed-off-by: Shi Lei 
---
 src/conf/network_conf.c | 160 +---
 src/conf/network_conf.h |  19 ++---
 2 files changed, 12 insertions(+), 167 deletions(-)

diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index 19408987..364c10e2 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -174,32 +174,6 @@ virNetworkIPDefClear(virNetworkIPDef *def)
 }
 
 
-static void
-virNetworkDNSDefClear(virNetworkDNSDef *def)
-{
-if (def->forwarders) {
-while (def->nfwds)
-virNetworkDNSForwarderClear(&def->forwarders[--def->nfwds]);
-VIR_FREE(def->forwarders);
-}
-if (def->txts) {
-while (def->ntxts)
-virNetworkDNSTxtDefClear(&def->txts[--def->ntxts]);
-VIR_FREE(def->txts);
-}
-if (def->hosts) {
-while (def->nhosts)
-virNetworkDNSHostDefClear(&def->hosts[--def->nhosts]);
-VIR_FREE(def->hosts);
-}
-if (def->srvs) {
-while (def->nsrvs)
-virNetworkDNSSrvDefClear(&def->srvs[--def->nsrvs]);
-VIR_FREE(def->srvs);
-}
-}
-
-
 static void
 virNetworkForwardDefClear(virNetworkForwardDef *def)
 {
@@ -883,7 +857,7 @@ virNetworkDNSForwarderParseHook(xmlNodePtr node 
G_GNUC_UNUSED,
 }
 
 
-static int
+int
 virNetworkDNSDefParseHook(xmlNodePtr node G_GNUC_UNUSED,
   virNetworkDNSDef *def,
   const char *networkName,
@@ -908,136 +882,6 @@ virNetworkDNSDefParseHook(xmlNodePtr node G_GNUC_UNUSED,
 }
 
 
-static int
-virNetworkDNSDefParseXML(const char *networkName,
- xmlNodePtr node,
- xmlXPathContextPtr ctxt,
- virNetworkDNSDef *def)
-{
-g_autofree xmlNodePtr *hostNodes = NULL;
-g_autofree xmlNodePtr *srvNodes = NULL;
-g_autofree xmlNodePtr *txtNodes = NULL;
-g_autofree xmlNodePtr *fwdNodes = NULL;
-g_autofree char *forwardPlainNames = NULL;
-g_autofree char *enable = NULL;
-int nhosts, nsrvs, ntxts, nfwds;
-size_t i;
-VIR_XPATH_NODE_AUTORESTORE(ctxt)
-
-ctxt->node = node;
-
-enable = virXPathString("string(./@enable)", ctxt);
-if (enable) {
-def->enable = virTristateBoolTypeFromString(enable);
-if (def->enable <= 0) {
-virReportError(VIR_ERR_XML_ERROR,
-   _("Invalid dns enable setting '%s' "
- "in network '%s'"),
-   enable, networkName);
-return -1;
-}
-}
-
-forwardPlainNames = virXPathString("string(./@forwardPlainNames)", ctxt);
-if (forwardPlainNames) {
-def->forwardPlainNames = 
virTristateBoolTypeFromString(forwardPlainNames);
-if (def->forwardPlainNames <= 0) {
-virReportError(VIR_ERR_XML_ERROR,
-   _("Invalid dns forwardPlainNames setting '%s' "
- "in network '%s'"),
-   forwardPlainNames, networkName);
-return -1;
-}
-}
-
-nfwds = virXPathNodeSet("./forwarder", ctxt, &fwdNodes);
-if (nfwds < 0) {
-virReportError(VIR_ERR_XML_ERROR,
-   _("invalid  element found in  of 
network %s"),
-   networkName);
-return -1;
-}
-if (nfwds > 0) {
-def->forwarders = g_new0(virNetworkDNSForwarder, nfwds);
-
-for (i = 0; i < nfwds; i++) {
-if (virNetworkDNSForwarderParseXML(fwdNodes[i],
-   &def->forwarders[i],
-   networkName,
-   def,
-   NULL) < 0)
-return -1;
-
-def->nfwds++;
-}
-}
-
-nhosts = virXPathNodeSet("./host", ctxt, &hostNodes);
-if (nhosts < 0) {
-virReportError(VIR_ERR_XML_ERROR,
-   _("invalid  element found in  of network 
%s"),
-   networkName);
-return -1;
-}
-if (nhosts > 0) {
-def->hosts = g_new0(virNetworkDNSHostDef, nhosts);
-
-for (i = 0; i < nhosts; i++) {
-if (virNetworkDNSHostDefParseXML(hostNodes[i], 
&def->hosts[def->nhosts],
- networkName, def, NULL) < 0) {
-return -1;
-}
-def->nhosts++;
-}
-}
-
-nsrvs = virXPathNodeSet("./srv", ctxt, &srvNodes);
-if (nsrvs < 0) {

[RFCv3 19/25] conf: Generate virNetworkDNSHostDefFormatBuf

2021-04-22 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/conf/network_conf.c | 14 +++---
 src/conf/network_conf.h |  2 +-
 2 files changed, 4 insertions(+), 12 deletions(-)

diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index 90b1e0ee..ba67eab1 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -2095,7 +2095,7 @@ static int
 virNetworkDNSDefFormat(virBuffer *buf,
const virNetworkDNSDef *def)
 {
-size_t i, j;
+size_t i;
 
 if (!(def->enable || def->forwardPlainNames || def->nfwds || def->nhosts ||
   def->nsrvs || def->ntxts))
@@ -2164,16 +2164,8 @@ virNetworkDNSDefFormat(virBuffer *buf,
 
 if (def->nhosts) {
 for (i = 0; i < def->nhosts; i++) {
-g_autofree char *ip = virSocketAddrFormat(&def->hosts[i].ip);
-
-virBufferAsprintf(buf, "\n", ip);
-virBufferAdjustIndent(buf, 2);
-for (j = 0; j < def->hosts[i].nnames; j++)
-virBufferEscapeString(buf, "%s\n",
-  def->hosts[i].names[j]);
-
-virBufferAdjustIndent(buf, -2);
-virBufferAddLit(buf, "\n");
+if (virNetworkDNSHostDefFormatBuf(buf, "host", &def->hosts[i], 
def, NULL) < 0)
+return -1;
 }
 }
 virBufferAdjustIndent(buf, -2);
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index 998411be..836d088d 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -142,7 +142,7 @@ struct _virNetworkDNSSrvDef {   /* genparse, genformat */
 };
 
 typedef struct _virNetworkDNSHostDef virNetworkDNSHostDef;
-struct _virNetworkDNSHostDef {  /* genparse */
+struct _virNetworkDNSHostDef {  /* genparse, genformat */
 virSocketAddr ip;   /* xmlattr */
 size_t nnames;
 char **names;   /* xmlelem:hostname, array */
-- 
2.25.1




[RFCv3 08/25] util: Add helper aliases and functions for 'bool' and 'time_t' to cooperate with xmlgen

2021-04-22 Thread Shi Lei
Add three aliases virBoolYesNo, virBoolOnOff and virBoolTrueFalse for
type 'bool'. The tool xmlgen depends on them to determine their ture
values in XML. Also, add corresponding functions to parse them.

Add virStrToTime and virTimeFormatBuf to convert 'time_t' and 'string'.

Add virStrToLong_u8p to convert 'string' to 'uint_8'.

Signed-off-by: Shi Lei 
---
 src/internal.h   |   8 +++
 src/libvirt_private.syms |   5 ++
 src/util/virstring.c | 102 +++
 src/util/virstring.h |  15 ++
 4 files changed, 130 insertions(+)

diff --git a/src/internal.h b/src/internal.h
index 0a03dfc4..3342934f 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -529,3 +529,11 @@ enum {
 # define fprintf(fh, ...) g_fprintf(fh, __VA_ARGS__)
 
 #endif /* VIR_NO_GLIB_STDIO */
+
+/* These are aliases for bool.
+ * The xmlgen depends on them to determine their true values
+ * in XML.
+ */
+typedef bool virBoolYesNo;  /* True values: 'yes', 'no' */
+typedef bool virBoolOnOff;  /* True values: 'on', 'off' */
+typedef bool virBoolTrueFalse;  /* True values: 'true', 'false' */
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index fad0ff71..e78491dc 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -3275,6 +3275,9 @@ virStringStripIPv6Brackets;
 virStringStripSuffix;
 virStringToUpper;
 virStringTrimOptionalNewline;
+virStrToBoolOnOff;
+virStrToBoolTrueFalse;
+virStrToBoolYesNo;
 virStrToDouble;
 virStrToLong_i;
 virStrToLong_l;
@@ -3285,6 +3288,8 @@ virStrToLong_ul;
 virStrToLong_ull;
 virStrToLong_ullp;
 virStrToLong_ulp;
+virStrToTime;
+virTimeFormatBuf;
 virTrimSpaces;
 
 
diff --git a/src/util/virstring.c b/src/util/virstring.c
index a2c07e5c..ac6c0aae 100644
--- a/src/util/virstring.c
+++ b/src/util/virstring.c
@@ -233,6 +233,21 @@ virStrToLong_ulp(char const *s, char **end_ptr, int base,
 return 0;
 }
 
+/* Just like virStrToLong_uip, above, but produce an "uint8_t" value.
+ * This version rejects any negative signs.  */
+int
+virStrToLong_u8p(char const *s, char **end_ptr, int base, uint8_t *result)
+{
+unsigned int val;
+int ret;
+ret = virStrToLong_uip(s, end_ptr, base, &val);
+if (ret < 0 || val > 0xff)
+return -1;
+
+*result = (uint8_t) val;
+return 0;
+}
+
 /* Just like virStrToLong_i, above, but produce a "long long" value.  */
 int
 virStrToLong_ll(char const *s, char **end_ptr, int base, long long *result)
@@ -1077,3 +1092,90 @@ int virStringParseYesNo(const char *str, bool *result)
 
 return 0;
 }
+
+
+int
+virStrToBoolYesNo(const char *str, bool *result)
+{
+if (STREQ(str, "yes"))
+*result = true;
+else if (STREQ(str, "no"))
+*result = false;
+else
+return -1;
+
+return 0;
+}
+
+
+int
+virStrToBoolOnOff(const char *str, bool *result)
+{
+if (STREQ(str, "on"))
+*result = true;
+else if (STREQ(str, "off"))
+*result = false;
+else
+return -1;
+
+return 0;
+}
+
+
+int
+virStrToBoolTrueFalse(const char *str, bool *result)
+{
+if (STREQ(str, "true"))
+*result = true;
+else if (STREQ(str, "false"))
+*result = false;
+else
+return -1;
+
+return 0;
+}
+
+
+int virStrToTime(const char *str, time_t *result)
+{
+g_autoptr(GDateTime) then = NULL;
+g_autoptr(GTimeZone) tz = g_time_zone_new_utc();
+char *tmp;
+int year, mon, mday, hour, min, sec;
+
+/* Expect: -MM-DDTHH:MM:SS (%d-%d-%dT%d:%d:%d)  eg 2010-11-28T14:29:01 
*/
+if (/* year */
+virStrToLong_i(str, &tmp, 10, &year) < 0 || *tmp != '-' ||
+/* month */
+virStrToLong_i(tmp+1, &tmp, 10, &mon) < 0 || *tmp != '-' ||
+/* day */
+virStrToLong_i(tmp+1, &tmp, 10, &mday) < 0 || *tmp != 'T' ||
+/* hour */
+virStrToLong_i(tmp+1, &tmp, 10, &hour) < 0 || *tmp != ':' ||
+/* minute */
+virStrToLong_i(tmp+1, &tmp, 10, &min) < 0 || *tmp != ':' ||
+/* second */
+virStrToLong_i(tmp+1, &tmp, 10, &sec) < 0 || *tmp != '\0') {
+virReportError(VIR_ERR_INTERNAL_ERROR,
+   _("invalid time '%s', expect -MM-DDTHH:MM:SS"),
+   str);
+return -1;
+}
+
+then = g_date_time_new(tz, year, mon, mday, hour, min, sec);
+*result = (time_t)g_date_time_to_unix(then);
+return 0;
+}
+
+
+int
+virTimeFormatBuf(virBuffer *buf, const char *layout, const time_t time)
+{
+g_autoptr(GDateTime) then = NULL;
+g_autofree char *thenstr = NULL;
+
+then = g_date_time_new_from_unix_utc(time);
+thenstr = g_date_time_format(then

Re: Re: [PATCHv3 4/4] netlink: Introduce a helper function to simplify netlink functions

2021-01-11 Thread Shi Lei
On 2021-01-11 at 19:26, Michal Privoznik wrote:
>On 1/11/21 3:23 AM, Shi Lei wrote:
>> Extract common code as helper function virNetlinkTalk, then simplify
>> the functions virNetlink[DumpLink|NewLink|DelLink|GetNeighbor].
>>
>> Signed-off-by: Shi Lei 
>> ---
>>   src/util/virnetlink.c | 232 ++
>>   src/util/virnetlink.h |   4 +-
>>   2 files changed, 101 insertions(+), 135 deletions(-)
>>
>> diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
>> index fdcb0dc0..650acff7 100644
>> --- a/src/util/virnetlink.c
>> +++ b/src/util/virnetlink.c
>> @@ -353,6 +353,54 @@ int virNetlinkCommand(struct nl_msg *nl_msg,
>>   return 0;
>>   }
>>  
>> +
>
>I guess we should document that NLMSG_ERROR and err->error == 0 is a
>valid case and no error. How about:
>
>/**
>  * virNetlinkTalk:
>  * @ifname: name of the link
>  * @nl_msg: pointer to netlink message
>  * @src_pid: pid used for nl_pid of the local end of the netlink message
>  *   (0 == "use getpid()")
>  * @dst_pid: pid of destination nl_pid if the kernel
>  *   is not the target of the netlink message but it is to be
>  *   sent to another process (0 if sending to the kernel)
>  * @resp: pointer to pointer where response buffer will be allocated
>  * @resp_len: pointer to integer holding the size of the response buffer
>  *    on return of the function
>  * @error: pointer to store netlink error (-errno)
>  * @fallback: pointer to an alternate function that will be called in case
>  *    netlink fails with EOPNOTSUPP (any other error will simply be
>  *    treated as an error)
>  *
>  * Simple wrapper around virNetlinkCommand(). The returned netlink message
>  * is allocated at @resp. Please note that according to netlink(7) man
>page,
>  * reply with type of NLMSG_ERROR and @error == 0 is an acknowledgment and
>  * thus not an error.
>  *
>  * Returns: 0 on success,
>  * -1 otherwise (error reported if @error == NULL)
>  */
>
>
>> +static int
>> +virNetlinkTalk(const char *ifname,
>> +   virNetlinkMsg *nl_msg,
>> +   uint32_t src_pid,
>> +   uint32_t dst_pid,
>> +   struct nlmsghdr **resp,
>> +   unsigned int *resp_len,
>> +   int *error,
>> +   virNetlinkTalkFallback fallback)
>> +{
>> +    if (virNetlinkCommand(nl_msg, resp, resp_len,
>> +  src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
>> +    return -1;
>> +
>> +    if (*resp_len < NLMSG_LENGTH(0) || *resp == NULL)
>> +    goto malformed_resp;
>> +
>> +    if ((*resp)->nlmsg_type == NLMSG_ERROR) {
>> +    struct nlmsgerr *err;
>> +
>> +    err = (struct nlmsgerr *) NLMSG_DATA(*resp);
>> +
>> +    if ((*resp)->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
>> +    goto malformed_resp;
>> +
>> +    if (-err->error == EOPNOTSUPP && fallback)
>> +    return fallback(ifname);
>> +
>> +    if (err->error < 0) {
>> +    if (error)
>> +    *error = err->error;
>
>So this sets @error to a negative number. [1]
>
>> +    else
>> +    virReportSystemError(-err->error, "%s", _("netlink error"));
>> +
>> +    return -1;
>> +    }
>
>And here I'd put:
>
> /* According to netlink(7) man page NLMSG_ERROR packet with error
>  * field set to 0 is an acknowledgment packet and thus not an
>error. */
>
>
>> +    }
>> +
>> +    return 0;
>> +
>> + malformed_resp:
>> +    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>> +   _("malformed netlink response message"));
>> +    return -1;
>> +}
>> +
>> +
>>   int
>>   virNetlinkDumpCommand(struct nl_msg *nl_msg,
>> virNetlinkDumpCallback callback,
>> @@ -396,6 +444,7 @@ virNetlinkDumpCommand(struct nl_msg *nl_msg,
>>   return 0;
>>   }
>>  
>> +
>>   /**
>>    * virNetlinkDumpLink:
>>    *
>> @@ -420,15 +469,14 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
>>  void **nlData, struct nlattr **tb,
>>  uint32_t src_pid, uint32_t dst_pid)
>>   {
>> -    int rc = -1;
>> -    struct nlmsgerr *err;
>>   struct ifinfomsg ifinfo = {
>>

[PATCHv3 0/4] netlink: Extract common code to simplify netlink functions

2021-01-10 Thread Shi Lei
V2 here: https://www.redhat.com/archives/libvir-list/2021-January/msg00174.html

Since V2:

- Fix the post process of virNetlinkTalk().
  If virNetlinkTalk() succeeds and we got an NLMSG_ERROR packet with
  the zero-value error, it should be regarded as a success.

- Several fixes for the pointer of resp in virNetlinkTalk().

- Minor fixes according to coding style.

Shi Lei (4):
  netlink: Remove invalid flags(NLM_F_CREATE and NLM_F_EXCL) for RTM_DELLINK
  netlink: Minor changes for macros NETLINK_MSG_[NEST_START|NEST_END|PUT]
  netlink: Introduce macro NETLINK_MSG_APPEND to wrap nlmsg_append
  netlink: Introduce a helper function to simplify netlink functions

 src/util/virnetlink.c | 321 +++---
 src/util/virnetlink.h |  27 +---
 2 files changed, 149 insertions(+), 199 deletions(-)

-- 
2.25.1




[PATCHv3 4/4] netlink: Introduce a helper function to simplify netlink functions

2021-01-10 Thread Shi Lei
Extract common code as helper function virNetlinkTalk, then simplify
the functions virNetlink[DumpLink|NewLink|DelLink|GetNeighbor].

Signed-off-by: Shi Lei 
---
 src/util/virnetlink.c | 232 ++
 src/util/virnetlink.h |   4 +-
 2 files changed, 101 insertions(+), 135 deletions(-)

diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
index fdcb0dc0..650acff7 100644
--- a/src/util/virnetlink.c
+++ b/src/util/virnetlink.c
@@ -353,6 +353,54 @@ int virNetlinkCommand(struct nl_msg *nl_msg,
 return 0;
 }
 
+
+static int
+virNetlinkTalk(const char *ifname,
+   virNetlinkMsg *nl_msg,
+   uint32_t src_pid,
+   uint32_t dst_pid,
+   struct nlmsghdr **resp,
+   unsigned int *resp_len,
+   int *error,
+   virNetlinkTalkFallback fallback)
+{
+if (virNetlinkCommand(nl_msg, resp, resp_len,
+  src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
+return -1;
+
+if (*resp_len < NLMSG_LENGTH(0) || *resp == NULL)
+goto malformed_resp;
+
+if ((*resp)->nlmsg_type == NLMSG_ERROR) {
+struct nlmsgerr *err;
+
+err = (struct nlmsgerr *) NLMSG_DATA(*resp);
+
+if ((*resp)->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+goto malformed_resp;
+
+if (-err->error == EOPNOTSUPP && fallback)
+return fallback(ifname);
+
+if (err->error < 0) {
+if (error)
+*error = err->error;
+else
+virReportSystemError(-err->error, "%s", _("netlink error"));
+
+return -1;
+}
+}
+
+return 0;
+
+ malformed_resp:
+virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+   _("malformed netlink response message"));
+return -1;
+}
+
+
 int
 virNetlinkDumpCommand(struct nl_msg *nl_msg,
   virNetlinkDumpCallback callback,
@@ -396,6 +444,7 @@ virNetlinkDumpCommand(struct nl_msg *nl_msg,
 return 0;
 }
 
+
 /**
  * virNetlinkDumpLink:
  *
@@ -420,15 +469,14 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
void **nlData, struct nlattr **tb,
uint32_t src_pid, uint32_t dst_pid)
 {
-int rc = -1;
-struct nlmsgerr *err;
 struct ifinfomsg ifinfo = {
 .ifi_family = AF_UNSPEC,
 .ifi_index  = ifindex
 };
-unsigned int recvbuflen;
 g_autoptr(virNetlinkMsg) nl_msg = NULL;
 g_autofree struct nlmsghdr *resp = NULL;
+unsigned int resp_len = 0;
+int error = 0;
 
 if (ifname && ifindex <= 0 && virNetDevGetIndex(ifname, &ifindex) < 0)
 return -1;
@@ -459,46 +507,25 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
 }
 # endif
 
-if (virNetlinkCommand(nl_msg, &resp, &recvbuflen,
-  src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
+if (virNetlinkTalk(ifname, nl_msg, src_pid, dst_pid,
+   &resp, &resp_len, &error, NULL) < 0) {
+virReportSystemError(error,
+ _("error dumping %s (%d) interface"),
+ ifname, ifindex);
 return -1;
+}
 
-if (recvbuflen < NLMSG_LENGTH(0) || resp == NULL)
-goto malformed_resp;
-
-switch (resp->nlmsg_type) {
-case NLMSG_ERROR:
-err = (struct nlmsgerr *)NLMSG_DATA(resp);
-if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
-goto malformed_resp;
-
-if (err->error) {
-virReportSystemError(-err->error,
- _("error dumping %s (%d) interface"),
- ifname, ifindex);
-return -1;
-}
-break;
-
-case GENL_ID_CTRL:
-case NLMSG_DONE:
-rc = nlmsg_parse(resp, sizeof(struct ifinfomsg),
- tb, IFLA_MAX, NULL);
-if (rc < 0)
-goto malformed_resp;
-break;
-
-default:
-goto malformed_resp;
+if ((resp->nlmsg_type != NLMSG_ERROR &&
+ resp->nlmsg_type != GENL_ID_CTRL &&
+ resp->nlmsg_type != NLMSG_DONE) ||
+nlmsg_parse(resp, sizeof(struct ifinfomsg), tb, IFLA_MAX, NULL) < 0) {
+virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+   _("malformed netlink response message"));
+return -1;
 }
 
 *nlData = g_steal_pointer(&resp);
 return 0;
-
- malformed_resp:
-virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-   _("malformed netlink response message"));
-return rc;
 }
 
 
@@ -523,13 +550,12 @@ virNetlinkNewLink(const char *ifname,
   virNetlinkNewLinkDataPtr extra_args,
   int *error)
 {
-struct nlmsgerr *err;
 s

[PATCHv3 3/4] netlink: Introduce macro NETLINK_MSG_APPEND to wrap nlmsg_append

2021-01-10 Thread Shi Lei
Introduce a macro NETLINK_MSG_APPEND to wrap nlmsg_append and
simplify code. Remove those labels 'buffer_too_small', since they
are now useless.

Signed-off-by: Shi Lei 
---
 src/util/virnetlink.c | 42 +-
 1 file changed, 13 insertions(+), 29 deletions(-)

diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
index 0b55b124..fdcb0dc0 100644
--- a/src/util/virnetlink.c
+++ b/src/util/virnetlink.c
@@ -72,6 +72,15 @@ do { \
 } \
 } while (0)
 
+# define NETLINK_MSG_APPEND(msg, datalen, dataptr) \
+do { \
+if (nlmsg_append(msg, dataptr, datalen, NLMSG_ALIGNTO) < 0) { \
+virReportError(VIR_ERR_INTERNAL_ERROR, "%s", \
+   _("allocated netlink buffer is too small")); \
+return -1; \
+} \
+} while (0)
+
 
 /* State for a single netlink event handle */
 struct virNetlinkEventHandle {
@@ -432,8 +441,7 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
 return -1;
 }
 
-if (nlmsg_append(nl_msg,  &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
-goto buffer_too_small;
+NETLINK_MSG_APPEND(nl_msg, sizeof(ifinfo), &ifinfo);
 
 if (ifname)
 NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME, (strlen(ifname) + 1), ifname);
@@ -491,11 +499,6 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed netlink response message"));
 return rc;
-
- buffer_too_small:
-virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-   _("allocated netlink buffer is too small"));
-return rc;
 }
 
 
@@ -545,8 +548,7 @@ virNetlinkNewLink(const char *ifname,
 return -1;
 }
 
-if (nlmsg_append(nl_msg,  &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
-goto buffer_too_small;
+NETLINK_MSG_APPEND(nl_msg, sizeof(ifinfo), &ifinfo);
 
 NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME, (strlen(ifname) + 1), ifname);
 
@@ -620,11 +622,6 @@ virNetlinkNewLink(const char *ifname,
 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed netlink response message"));
 return -1;
-
- buffer_too_small:
-virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-   _("allocated netlink buffer is too small"));
-return -1;
 }
 
 
@@ -658,8 +655,7 @@ virNetlinkDelLink(const char *ifname, 
virNetlinkDelLinkFallback fallback)
 return -1;
 }
 
-if (nlmsg_append(nl_msg,  &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
-goto buffer_too_small;
+NETLINK_MSG_APPEND(nl_msg, sizeof(ifinfo), &ifinfo);
 
 NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME, (strlen(ifname) + 1), ifname);
 
@@ -701,11 +697,6 @@ virNetlinkDelLink(const char *ifname, 
virNetlinkDelLinkFallback fallback)
 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed netlink response message"));
 return -1;
-
- buffer_too_small:
-virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-   _("allocated netlink buffer is too small"));
-return -1;
 }
 
 /**
@@ -740,9 +731,7 @@ virNetlinkGetNeighbor(void **nlData, uint32_t src_pid, 
uint32_t dst_pid)
 return -1;
 }
 
-if (nlmsg_append(nl_msg, &ndinfo, sizeof(ndinfo), NLMSG_ALIGNTO) < 0)
-goto buffer_too_small;
-
+NETLINK_MSG_APPEND(nl_msg, sizeof(ndinfo), &ndinfo);
 
 if (virNetlinkCommand(nl_msg, &resp, &recvbuflen,
   src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
@@ -778,11 +767,6 @@ virNetlinkGetNeighbor(void **nlData, uint32_t src_pid, 
uint32_t dst_pid)
 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed netlink response message"));
 return -1;
-
- buffer_too_small:
-virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-   _("allocated netlink buffer is too small"));
-return -1;
 }
 
 int
-- 
2.25.1




[PATCHv3 1/4] netlink: Remove invalid flags(NLM_F_CREATE and NLM_F_EXCL) for RTM_DELLINK

2021-01-10 Thread Shi Lei
NLM_F_CREATE and NLM_F_EXCL are invalid for RTM_DELLINK,
so remove them.

Signed-off-by: Shi Lei 
---
 src/util/virnetlink.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
index ca735bb8..17e6eeb9 100644
--- a/src/util/virnetlink.c
+++ b/src/util/virnetlink.c
@@ -627,8 +627,7 @@ virNetlinkDelLink(const char *ifname, 
virNetlinkDelLinkFallback fallback)
 g_autoptr(virNetlinkMsg) nl_msg = NULL;
 g_autofree struct nlmsghdr *resp = NULL;
 
-nl_msg = nlmsg_alloc_simple(RTM_DELLINK,
-NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL);
+nl_msg = nlmsg_alloc_simple(RTM_DELLINK, NLM_F_REQUEST);
 if (!nl_msg) {
 virReportOOMError();
 return -1;
-- 
2.25.1




[PATCHv3 2/4] netlink: Minor changes for macros NETLINK_MSG_[NEST_START|NEST_END|PUT]

2021-01-10 Thread Shi Lei
Move macros NETLINK_MSG_[NEST_START|NEST_END|PUT] from .h into .c;
within these macros, replace 'goto' with reporting error and returning;
simplify virNetlinkDumpLink and virNetlinkDelLink by using NETLINK_MSG_PUT.

Signed-off-by: Shi Lei 
---
 src/util/virnetlink.c | 44 +--
 src/util/virnetlink.h | 23 --
 2 files changed, 34 insertions(+), 33 deletions(-)

diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
index 17e6eeb9..0b55b124 100644
--- a/src/util/virnetlink.c
+++ b/src/util/virnetlink.c
@@ -44,6 +44,35 @@ VIR_LOG_INIT("util.netlink");
 
 # include 
 
+# define NETLINK_MSG_NEST_START(msg, container, attrtype) \
+do { \
+container = nla_nest_start(msg, attrtype); \
+if (!container) { \
+virReportError(VIR_ERR_INTERNAL_ERROR, "%s", \
+   _("allocated netlink buffer is too small")); \
+return -1; \
+} \
+} while (0)
+
+# define NETLINK_MSG_NEST_END(msg, container) \
+do { nla_nest_end(msg, container); } while (0)
+
+/*
+ * we need to use an intermediary pointer to @data as compilers may sometimes
+ * complain about @data not being a pointer type:
+ * error: the address of 'foo' will always evaluate as 'true' [-Werror=address]
+ */
+# define NETLINK_MSG_PUT(msg, attrtype, datalen, data) \
+do { \
+const void *dataptr = data; \
+if (dataptr && nla_put(msg, attrtype, datalen, dataptr) < 0) { \
+virReportError(VIR_ERR_INTERNAL_ERROR, "%s", \
+   _("allocated netlink buffer is too small")); \
+return -1; \
+} \
+} while (0)
+
+
 /* State for a single netlink event handle */
 struct virNetlinkEventHandle {
 int watch;
@@ -406,10 +435,8 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
 if (nlmsg_append(nl_msg,  &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
 goto buffer_too_small;
 
-if (ifname) {
-if (nla_put(nl_msg, IFLA_IFNAME, strlen(ifname)+1, ifname) < 0)
-goto buffer_too_small;
-}
+if (ifname)
+NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME, (strlen(ifname) + 1), ifname);
 
 # ifdef RTEXT_FILTER_VF
 /* if this filter exists in the kernel's netlink implementation,
@@ -419,10 +446,8 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
 {
 uint32_t ifla_ext_mask = RTEXT_FILTER_VF;
 
-if (nla_put(nl_msg, IFLA_EXT_MASK,
-sizeof(ifla_ext_mask), &ifla_ext_mask) < 0) {
-goto buffer_too_small;
-}
+NETLINK_MSG_PUT(nl_msg, IFLA_EXT_MASK,
+sizeof(ifla_ext_mask), &ifla_ext_mask);
 }
 # endif
 
@@ -636,8 +661,7 @@ virNetlinkDelLink(const char *ifname, 
virNetlinkDelLinkFallback fallback)
 if (nlmsg_append(nl_msg,  &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
 goto buffer_too_small;
 
-if (nla_put(nl_msg, IFLA_IFNAME, strlen(ifname)+1, ifname) < 0)
-goto buffer_too_small;
+NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME, (strlen(ifname) + 1), ifname);
 
 if (virNetlinkCommand(nl_msg, &resp, &recvbuflen, 0, 0,
   NETLINK_ROUTE, 0) < 0) {
diff --git a/src/util/virnetlink.h b/src/util/virnetlink.h
index 7c4ed202..966d6db3 100644
--- a/src/util/virnetlink.h
+++ b/src/util/virnetlink.h
@@ -38,29 +38,6 @@ struct nlmsghdr;
 
 #endif /* WITH_LIBNL */
 
-#define NETLINK_MSG_NEST_START(msg, container, attrtype) \
-do { \
-container = nla_nest_start(msg, attrtype); \
-if (!container) \
-goto buffer_too_small; \
-} while(0)
-
-#define NETLINK_MSG_NEST_END(msg, container) \
-do { nla_nest_end(msg, container); } while(0)
-
-/*
- * we need to use an intermediary pointer to @data as compilers may sometimes
- * complain about @data not being a pointer type:
- * error: the address of 'foo' will always evaluate as 'true' [-Werror=address]
- */
-#define NETLINK_MSG_PUT(msg, attrtype, datalen, data) \
-do { \
-const void *dataptr = data; \
-if (dataptr && nla_put(msg, attrtype, datalen, dataptr) < 0) \
-goto buffer_too_small; \
-} while(0)
-
-
 int virNetlinkStartup(void);
 void virNetlinkShutdown(void);
 
-- 
2.25.1




Re: Re: [PATCHv2 4/4] netlink: Introduce a helper function to simplify netlink functions

2021-01-06 Thread Shi Lei
On 2021-01-07 at 10:52, Shi Lei wrote:
>On 2021-01-06 at 18:21, Michal Privoznik wrote:
>>On 1/6/21 3:40 AM, Shi Lei wrote:
>>> Extract common code as helper function virNetlinkTalk, then simplify
>>> the functions virNetlink[DumpLink|NewLink|DelLink|GetNeighbor].
>>>
>>> -    default:
>>> -    goto malformed_resp;
>>> +    if (resp->nlmsg_type != NLMSG_DONE) {
>>> +    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>>> +   _("malformed netlink response message"));
>>> +    return -1;
>>>   }
>>
>>This is not correct IMO. The way that control flows through this
>>function before your patch is (I'm trying to create virbr0 using 'virsh
>>net-start default'):
>>
>>1) virNetlinkCommand() suceeds,
>>2) resp->nlmsg_type == NLMSG_ERROR even though the bridge was created,
>>3) err->error is 0 hence we hit 'break' and fall out of the switch and ..
>>
>>>  
>>>   return 0;
>
>Yes.
>So the result ((resp->nlmsg_type == NLMSG_ERROR) && (err->error == 0)) should
>be regarded as a success.
>
>I feel like that this piece of code can be like:
>
>    if (((resp->nlmsg_type != NLMSG_ERROR) || (*error != 0)) &&
>        (resp->nlmsg_type != NLMSG_DONE) {
>        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>                                _("malformed netlink response message"));
>        return -1;
>    }
>
>    return 0;


This piece of code can be simplified as :

    if (resp->nlmsg_type != NLMSG_ERROR &&
        resp->nlmsg_type != NLMSG_DONE) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                                    _("malformed netlink response message"));
            return -1;
    }

When we reach here, virNetlinkTalk have returned 0.
And if (resp->nlmsg_type == NLMSG_ERROR), then (*error) must be 0.

Shi Lei




Re: Re: [PATCHv2 4/4] netlink: Introduce a helper function to simplify netlink functions

2021-01-06 Thread Shi Lei
On 2021-01-06 at 18:21, Michal Privoznik wrote:
>On 1/6/21 3:40 AM, Shi Lei wrote:
>> Extract common code as helper function virNetlinkTalk, then simplify
>> the functions virNetlink[DumpLink|NewLink|DelLink|GetNeighbor].
>>
>> Signed-off-by: Shi Lei 
>> ---
>>   src/util/virnetlink.c | 225 +-
>>   src/util/virnetlink.h |   4 +-
>>   2 files changed, 94 insertions(+), 135 deletions(-)
>>
>> diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
>> index fdcb0dc0..2936a3ef 100644
>> --- a/src/util/virnetlink.c
>> +++ b/src/util/virnetlink.c
>> @@ -353,6 +353,52 @@ int virNetlinkCommand(struct nl_msg *nl_msg,
>>   return 0;
>>   }
>>  
>> +
>> +static int
>> +virNetlinkTalk(const char *ifname,
>> +   virNetlinkMsg *nl_msg,
>> +   uint32_t src_pid,
>> +   uint32_t dst_pid,
>> +   struct nlmsghdr **resp,
>> +   unsigned int *resp_len,
>> +   int *error,
>> +   virNetlinkTalkFallback fallback)
>> +{
>> +    if (virNetlinkCommand(nl_msg, resp, resp_len,
>> +  src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
>> +    return -1;
>> +
>> +    if (*resp_len < NLMSG_LENGTH(0) || resp == NULL)
>
>This needs to be *resp == NULL, because now we're passing a pointer to a
>pointer. 

Uh. It's my bad. I'll fix it.

>
>> +    goto malformed_resp;
>> +
>> +    if ((*resp)->nlmsg_type == NLMSG_ERROR) {
>> +    struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA(resp);
>
>And this needs to be NLMSG_DATA(*resp); 

Yes!

>Also, might be worth putting an empty line here to create two blocks:
>one with variable declaration and the other with the code. 

Okay.

>
>
>> +    if ((*resp)->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
>> +    goto malformed_resp;
>> +
>> +    if (-err->error == EOPNOTSUPP && fallback)
>> +    return fallback(ifname);
>> +
>> +    if (err->error < 0) {
>> +    if (error)
>> +    *error = err->error;
>> +    else
>> +    virReportSystemError(-err->error,
>> + "%s", _("netlink error"));
>
>Since this is a two line body, it should be wrapped in curly braces
>(according to our coding style) which means that both bodies should have
>them (we don't really like one body having them while the other not). 

I feel like it can be :
    if (error)
        *error = err->error;
    else
        virReportSystemError(-err->error, "%s", _("netlink error"));

Since ^this line isn't more than 80 columns.

>> +
>> +    return -1;
>> +    }
>> +    }
>> +
>> +    return 0;
>> +
>> + malformed_resp:
>> +    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>> +   _("malformed netlink response message"));
>> +    return -1;
>> +}
>> +
>> +
>>   int
>>   virNetlinkDumpCommand(struct nl_msg *nl_msg,
>> virNetlinkDumpCallback callback,
>> @@ -396,6 +442,7 @@ virNetlinkDumpCommand(struct nl_msg *nl_msg,
>>   return 0;
>>   }
>>  
>> +
>>   /**
>>    * virNetlinkDumpLink:
>>    *
>> @@ -420,15 +467,14 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
>>  void **nlData, struct nlattr **tb,
>>  uint32_t src_pid, uint32_t dst_pid)
>>   {
>> -    int rc = -1;
>> -    struct nlmsgerr *err;
>>   struct ifinfomsg ifinfo = {
>>   .ifi_family = AF_UNSPEC,
>>   .ifi_index  = ifindex
>>   };
>> -    unsigned int recvbuflen;
>>   g_autoptr(virNetlinkMsg) nl_msg = NULL;
>>   g_autofree struct nlmsghdr *resp = NULL;
>> +    unsigned int resp_len = 0;
>> +    int error = 0;
>>  
>>   if (ifname && ifindex <= 0 && virNetDevGetIndex(ifname, &ifindex) < 0)
>>   return -1;
>> @@ -459,46 +505,23 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
>>   }
>>   # endif
>>  
>> -    if (virNetlinkCommand(nl_msg, &resp, &recvbuflen,
>> -  src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
>> +    if (virNetlinkTalk(ifname, nl_msg, src_pid, dst_pid,
>> +   &resp, &resp_len, &error, NULL) < 0) {
>> +   

[PATCHv2 3/4] netlink: Introduce macro NETLINK_MSG_APPEND to wrap nlmsg_append

2021-01-05 Thread Shi Lei
Introduce a macro NETLINK_MSG_APPEND to wrap nlmsg_append and
simplify code. Remove those labels 'buffer_too_small', since they
are now useless.

Signed-off-by: Shi Lei 
---
 src/util/virnetlink.c | 42 +-
 1 file changed, 13 insertions(+), 29 deletions(-)

diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
index 0b55b124..fdcb0dc0 100644
--- a/src/util/virnetlink.c
+++ b/src/util/virnetlink.c
@@ -72,6 +72,15 @@ do { \
 } \
 } while (0)
 
+# define NETLINK_MSG_APPEND(msg, datalen, dataptr) \
+do { \
+if (nlmsg_append(msg, dataptr, datalen, NLMSG_ALIGNTO) < 0) { \
+virReportError(VIR_ERR_INTERNAL_ERROR, "%s", \
+   _("allocated netlink buffer is too small")); \
+return -1; \
+} \
+} while (0)
+
 
 /* State for a single netlink event handle */
 struct virNetlinkEventHandle {
@@ -432,8 +441,7 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
 return -1;
 }
 
-if (nlmsg_append(nl_msg,  &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
-goto buffer_too_small;
+NETLINK_MSG_APPEND(nl_msg, sizeof(ifinfo), &ifinfo);
 
 if (ifname)
 NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME, (strlen(ifname) + 1), ifname);
@@ -491,11 +499,6 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed netlink response message"));
 return rc;
-
- buffer_too_small:
-virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-   _("allocated netlink buffer is too small"));
-return rc;
 }
 
 
@@ -545,8 +548,7 @@ virNetlinkNewLink(const char *ifname,
 return -1;
 }
 
-if (nlmsg_append(nl_msg,  &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
-goto buffer_too_small;
+NETLINK_MSG_APPEND(nl_msg, sizeof(ifinfo), &ifinfo);
 
 NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME, (strlen(ifname) + 1), ifname);
 
@@ -620,11 +622,6 @@ virNetlinkNewLink(const char *ifname,
 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed netlink response message"));
 return -1;
-
- buffer_too_small:
-virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-   _("allocated netlink buffer is too small"));
-return -1;
 }
 
 
@@ -658,8 +655,7 @@ virNetlinkDelLink(const char *ifname, 
virNetlinkDelLinkFallback fallback)
 return -1;
 }
 
-if (nlmsg_append(nl_msg,  &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
-goto buffer_too_small;
+NETLINK_MSG_APPEND(nl_msg, sizeof(ifinfo), &ifinfo);
 
 NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME, (strlen(ifname) + 1), ifname);
 
@@ -701,11 +697,6 @@ virNetlinkDelLink(const char *ifname, 
virNetlinkDelLinkFallback fallback)
 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed netlink response message"));
 return -1;
-
- buffer_too_small:
-virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-   _("allocated netlink buffer is too small"));
-return -1;
 }
 
 /**
@@ -740,9 +731,7 @@ virNetlinkGetNeighbor(void **nlData, uint32_t src_pid, 
uint32_t dst_pid)
 return -1;
 }
 
-if (nlmsg_append(nl_msg, &ndinfo, sizeof(ndinfo), NLMSG_ALIGNTO) < 0)
-goto buffer_too_small;
-
+NETLINK_MSG_APPEND(nl_msg, sizeof(ndinfo), &ndinfo);
 
 if (virNetlinkCommand(nl_msg, &resp, &recvbuflen,
   src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
@@ -778,11 +767,6 @@ virNetlinkGetNeighbor(void **nlData, uint32_t src_pid, 
uint32_t dst_pid)
 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed netlink response message"));
 return -1;
-
- buffer_too_small:
-virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-   _("allocated netlink buffer is too small"));
-return -1;
 }
 
 int
-- 
2.25.1




[PATCHv2 4/4] netlink: Introduce a helper function to simplify netlink functions

2021-01-05 Thread Shi Lei
Extract common code as helper function virNetlinkTalk, then simplify
the functions virNetlink[DumpLink|NewLink|DelLink|GetNeighbor].

Signed-off-by: Shi Lei 
---
 src/util/virnetlink.c | 225 +-
 src/util/virnetlink.h |   4 +-
 2 files changed, 94 insertions(+), 135 deletions(-)

diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
index fdcb0dc0..2936a3ef 100644
--- a/src/util/virnetlink.c
+++ b/src/util/virnetlink.c
@@ -353,6 +353,52 @@ int virNetlinkCommand(struct nl_msg *nl_msg,
 return 0;
 }
 
+
+static int
+virNetlinkTalk(const char *ifname,
+   virNetlinkMsg *nl_msg,
+   uint32_t src_pid,
+   uint32_t dst_pid,
+   struct nlmsghdr **resp,
+   unsigned int *resp_len,
+   int *error,
+   virNetlinkTalkFallback fallback)
+{
+if (virNetlinkCommand(nl_msg, resp, resp_len,
+  src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
+return -1;
+
+if (*resp_len < NLMSG_LENGTH(0) || resp == NULL)
+goto malformed_resp;
+
+if ((*resp)->nlmsg_type == NLMSG_ERROR) {
+struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA(resp);
+if ((*resp)->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+goto malformed_resp;
+
+if (-err->error == EOPNOTSUPP && fallback)
+return fallback(ifname);
+
+if (err->error < 0) {
+if (error)
+*error = err->error;
+else
+virReportSystemError(-err->error,
+ "%s", _("netlink error"));
+
+return -1;
+}
+}
+
+return 0;
+
+ malformed_resp:
+virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+   _("malformed netlink response message"));
+return -1;
+}
+
+
 int
 virNetlinkDumpCommand(struct nl_msg *nl_msg,
   virNetlinkDumpCallback callback,
@@ -396,6 +442,7 @@ virNetlinkDumpCommand(struct nl_msg *nl_msg,
 return 0;
 }
 
+
 /**
  * virNetlinkDumpLink:
  *
@@ -420,15 +467,14 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
void **nlData, struct nlattr **tb,
uint32_t src_pid, uint32_t dst_pid)
 {
-int rc = -1;
-struct nlmsgerr *err;
 struct ifinfomsg ifinfo = {
 .ifi_family = AF_UNSPEC,
 .ifi_index  = ifindex
 };
-unsigned int recvbuflen;
 g_autoptr(virNetlinkMsg) nl_msg = NULL;
 g_autofree struct nlmsghdr *resp = NULL;
+unsigned int resp_len = 0;
+int error = 0;
 
 if (ifname && ifindex <= 0 && virNetDevGetIndex(ifname, &ifindex) < 0)
 return -1;
@@ -459,46 +505,23 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
 }
 # endif
 
-if (virNetlinkCommand(nl_msg, &resp, &recvbuflen,
-  src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
+if (virNetlinkTalk(ifname, nl_msg, src_pid, dst_pid,
+   &resp, &resp_len, &error, NULL) < 0) {
+virReportSystemError(error,
+ _("error dumping %s (%d) interface"),
+ ifname, ifindex);
 return -1;
+}
 
-if (recvbuflen < NLMSG_LENGTH(0) || resp == NULL)
-goto malformed_resp;
-
-switch (resp->nlmsg_type) {
-case NLMSG_ERROR:
-err = (struct nlmsgerr *)NLMSG_DATA(resp);
-if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
-goto malformed_resp;
-
-if (err->error) {
-virReportSystemError(-err->error,
- _("error dumping %s (%d) interface"),
- ifname, ifindex);
-return -1;
-}
-break;
-
-case GENL_ID_CTRL:
-case NLMSG_DONE:
-rc = nlmsg_parse(resp, sizeof(struct ifinfomsg),
- tb, IFLA_MAX, NULL);
-if (rc < 0)
-goto malformed_resp;
-break;
-
-default:
-goto malformed_resp;
+if ((resp->nlmsg_type != GENL_ID_CTRL && resp->nlmsg_type != NLMSG_DONE) ||
+nlmsg_parse(resp, sizeof(struct ifinfomsg), tb, IFLA_MAX, NULL) < 0) {
+virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+   _("malformed netlink response message"));
+return -1;
 }
 
 *nlData = g_steal_pointer(&resp);
 return 0;
-
- malformed_resp:
-virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-   _("malformed netlink response message"));
-return rc;
 }
 
 
@@ -523,13 +546,12 @@ virNetlinkNewLink(const char *ifname,
   virNetlinkNewLinkDataPtr extra_args,
   int *error)
 {
-struct nlmsgerr *err;
 struct nlattr *linkinfo = NULL;
 struct 

[PATCHv2 0/4] netlink: Extract common code to simplify netlink functions

2021-01-05 Thread Shi Lei
V1 here: https://www.redhat.com/archives/libvir-list/2020-December/msg00836.html

Since V1:
- Minor fixes for reporting system error in Patch 4.

Shi Lei (4):
  netlink: Remove invalid flags(NLM_F_CREATE and NLM_F_EXCL) for RTM_DELLINK
  netlink: Minor changes for macros NETLINK_MSG_[NEST_START|NEST_END|PUT]
  netlink: Introduce macro NETLINK_MSG_APPEND to wrap nlmsg_append
  netlink: Introduce a helper function to simplify netlink functions

 src/util/virnetlink.c | 314 +++---
 src/util/virnetlink.h |  27 +---
 2 files changed, 142 insertions(+), 199 deletions(-)

-- 
2.25.1




[PATCHv2 1/4] netlink: Remove invalid flags(NLM_F_CREATE and NLM_F_EXCL) for RTM_DELLINK

2021-01-05 Thread Shi Lei
NLM_F_CREATE and NLM_F_EXCL are invalid for RTM_DELLINK,
so remove them.

Signed-off-by: Shi Lei 
---
 src/util/virnetlink.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
index ca735bb8..17e6eeb9 100644
--- a/src/util/virnetlink.c
+++ b/src/util/virnetlink.c
@@ -627,8 +627,7 @@ virNetlinkDelLink(const char *ifname, 
virNetlinkDelLinkFallback fallback)
 g_autoptr(virNetlinkMsg) nl_msg = NULL;
 g_autofree struct nlmsghdr *resp = NULL;
 
-nl_msg = nlmsg_alloc_simple(RTM_DELLINK,
-NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL);
+nl_msg = nlmsg_alloc_simple(RTM_DELLINK, NLM_F_REQUEST);
 if (!nl_msg) {
 virReportOOMError();
 return -1;
-- 
2.25.1




[PATCHv2 2/4] netlink: Minor changes for macros NETLINK_MSG_[NEST_START|NEST_END|PUT]

2021-01-05 Thread Shi Lei
Move macros NETLINK_MSG_[NEST_START|NEST_END|PUT] from .h into .c;
within these macros, replace 'goto' with reporting error and returning;
simplify virNetlinkDumpLink and virNetlinkDelLink by using NETLINK_MSG_PUT.

Signed-off-by: Shi Lei 
---
 src/util/virnetlink.c | 44 +--
 src/util/virnetlink.h | 23 --
 2 files changed, 34 insertions(+), 33 deletions(-)

diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
index 17e6eeb9..0b55b124 100644
--- a/src/util/virnetlink.c
+++ b/src/util/virnetlink.c
@@ -44,6 +44,35 @@ VIR_LOG_INIT("util.netlink");
 
 # include 
 
+# define NETLINK_MSG_NEST_START(msg, container, attrtype) \
+do { \
+container = nla_nest_start(msg, attrtype); \
+if (!container) { \
+virReportError(VIR_ERR_INTERNAL_ERROR, "%s", \
+   _("allocated netlink buffer is too small")); \
+return -1; \
+} \
+} while (0)
+
+# define NETLINK_MSG_NEST_END(msg, container) \
+do { nla_nest_end(msg, container); } while (0)
+
+/*
+ * we need to use an intermediary pointer to @data as compilers may sometimes
+ * complain about @data not being a pointer type:
+ * error: the address of 'foo' will always evaluate as 'true' [-Werror=address]
+ */
+# define NETLINK_MSG_PUT(msg, attrtype, datalen, data) \
+do { \
+const void *dataptr = data; \
+if (dataptr && nla_put(msg, attrtype, datalen, dataptr) < 0) { \
+virReportError(VIR_ERR_INTERNAL_ERROR, "%s", \
+   _("allocated netlink buffer is too small")); \
+return -1; \
+} \
+} while (0)
+
+
 /* State for a single netlink event handle */
 struct virNetlinkEventHandle {
 int watch;
@@ -406,10 +435,8 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
 if (nlmsg_append(nl_msg,  &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
 goto buffer_too_small;
 
-if (ifname) {
-if (nla_put(nl_msg, IFLA_IFNAME, strlen(ifname)+1, ifname) < 0)
-goto buffer_too_small;
-}
+if (ifname)
+NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME, (strlen(ifname) + 1), ifname);
 
 # ifdef RTEXT_FILTER_VF
 /* if this filter exists in the kernel's netlink implementation,
@@ -419,10 +446,8 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
 {
 uint32_t ifla_ext_mask = RTEXT_FILTER_VF;
 
-if (nla_put(nl_msg, IFLA_EXT_MASK,
-sizeof(ifla_ext_mask), &ifla_ext_mask) < 0) {
-goto buffer_too_small;
-}
+NETLINK_MSG_PUT(nl_msg, IFLA_EXT_MASK,
+sizeof(ifla_ext_mask), &ifla_ext_mask);
 }
 # endif
 
@@ -636,8 +661,7 @@ virNetlinkDelLink(const char *ifname, 
virNetlinkDelLinkFallback fallback)
 if (nlmsg_append(nl_msg,  &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
 goto buffer_too_small;
 
-if (nla_put(nl_msg, IFLA_IFNAME, strlen(ifname)+1, ifname) < 0)
-goto buffer_too_small;
+NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME, (strlen(ifname) + 1), ifname);
 
 if (virNetlinkCommand(nl_msg, &resp, &recvbuflen, 0, 0,
   NETLINK_ROUTE, 0) < 0) {
diff --git a/src/util/virnetlink.h b/src/util/virnetlink.h
index 7c4ed202..966d6db3 100644
--- a/src/util/virnetlink.h
+++ b/src/util/virnetlink.h
@@ -38,29 +38,6 @@ struct nlmsghdr;
 
 #endif /* WITH_LIBNL */
 
-#define NETLINK_MSG_NEST_START(msg, container, attrtype) \
-do { \
-container = nla_nest_start(msg, attrtype); \
-if (!container) \
-goto buffer_too_small; \
-} while(0)
-
-#define NETLINK_MSG_NEST_END(msg, container) \
-do { nla_nest_end(msg, container); } while(0)
-
-/*
- * we need to use an intermediary pointer to @data as compilers may sometimes
- * complain about @data not being a pointer type:
- * error: the address of 'foo' will always evaluate as 'true' [-Werror=address]
- */
-#define NETLINK_MSG_PUT(msg, attrtype, datalen, data) \
-do { \
-const void *dataptr = data; \
-if (dataptr && nla_put(msg, attrtype, datalen, dataptr) < 0) \
-goto buffer_too_small; \
-} while(0)
-
-
 int virNetlinkStartup(void);
 void virNetlinkShutdown(void);
 
-- 
2.25.1




Re: Re: [PATCH 4/4] netlink: Introduce a helper function to simplify netlink functions

2021-01-05 Thread Shi Lei
On 2021-01-06 at 00:00, Michal Privoznik wrote:
>On 12/21/20 4:23 AM, Shi Lei wrote:
>> Extract common code as helper function virNetlinkTalk, then simplify
>> the functions virNetlink[DumpLink|NewLink|DelLink|GetNeighbor].
>>
>> Signed-off-by: Shi Lei 
>> ---
>>   src/util/virnetlink.c | 210 +++---
>>   src/util/virnetlink.h |   4 +-
>>   2 files changed, 78 insertions(+), 136 deletions(-)
>
>Nice cleanup. Patches 1-3 look good. 

Okay.

>
>>
>> diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
>> index fdcb0dc0..7bea38c0 100644
>> --- a/src/util/virnetlink.c
>> +++ b/src/util/virnetlink.c
>> @@ -353,6 +353,48 @@ int virNetlinkCommand(struct nl_msg *nl_msg,
>>   return 0;
>>   }
>>  
>> +
>> +static int
>> +virNetlinkTalk(const char *ifname,
>> +   virNetlinkMsg *nl_msg,
>> +   uint32_t src_pid,
>> +   uint32_t dst_pid,
>> +   struct nlmsghdr **resp,
>> +   unsigned int *resp_len,
>> +   int *error,
>> +   virNetlinkTalkFallback fallback)
>> +{
>> +    if (virNetlinkCommand(nl_msg, resp, resp_len,
>> +  src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
>> +    return -1;
>> +
>> +    if (*resp_len < NLMSG_LENGTH(0) || resp == NULL)
>> +    goto malformed_resp;
>> +
>> +    if ((*resp)->nlmsg_type == NLMSG_ERROR) {
>> +    struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA(resp);
>> +    if ((*resp)->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
>> +    goto malformed_resp;
>> +
>> +    if (-err->error == EOPNOTSUPP && fallback)
>> +    return fallback(ifname);
>> +
>> +    if (err->error < 0) {
>> +    if (error)
>> +    *error = err->error;
>
>I wonder whether we should report an error here. I mean, if err->error
>is set (and not EOPNOTSUPP) then it's stored into *error, good. But -1
>is returned ...
>
>> +    return -1;
>
>.. here and it's indistinguishable to the caller from -1 returned in
>'malformed_resp'. Looking at the usage in next hunks, how about:
>
>   if (error)
> *error = err->error;
>   else
> virReportSystemError(-err->error, ...);
>
>   return -1;
> 

Okay.

>> +    }
>> +    }
>> +
>> +    return 0;
>> +
>> + malformed_resp:
>> +    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>> +   _("malformed netlink response message"));
>> +    return -1;
>> +}
>> +
>> +
>>   int
>>   virNetlinkDumpCommand(struct nl_msg *nl_msg,
>> virNetlinkDumpCallback callback,
>> @@ -396,6 +438,7 @@ virNetlinkDumpCommand(struct nl_msg *nl_msg,
>>   return 0;
>>   }
>>  
>> +
>>   /**
>>    * virNetlinkDumpLink:
>>    *
>> @@ -420,15 +463,13 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
>>  void **nlData, struct nlattr **tb,
>>  uint32_t src_pid, uint32_t dst_pid)
>>   {
>> -    int rc = -1;
>> -    struct nlmsgerr *err;
>>   struct ifinfomsg ifinfo = {
>>   .ifi_family = AF_UNSPEC,
>>   .ifi_index  = ifindex
>>   };
>> -    unsigned int recvbuflen;
>>   g_autoptr(virNetlinkMsg) nl_msg = NULL;
>>   g_autofree struct nlmsghdr *resp = NULL;
>> +    unsigned int resp_len = 0;
>>  
>>   if (ifname && ifindex <= 0 && virNetDevGetIndex(ifname, &ifindex) < 0)
>>   return -1;
>> @@ -459,46 +500,19 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
>>   }
>>   # endif
>>  
>> -    if (virNetlinkCommand(nl_msg, &resp, &recvbuflen,
>> -  src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
>> +    if (virNetlinkTalk(ifname, nl_msg, src_pid, dst_pid,
>> +   &resp, &resp_len, NULL, NULL) < 0)
>>   return -1;
>>  
>> -    if (recvbuflen < NLMSG_LENGTH(0) || resp == NULL)
>> -    goto malformed_resp;
>> -
>> -    switch (resp->nlmsg_type) {
>> -    case NLMSG_ERROR:
>> -    err = (struct nlmsgerr *)NLMSG_DATA(resp);
>> -    if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
>> -    goto malformed_resp;
>> -
>> -    if (err->error) {
>> -    virReportSy

[PATCH 4/4] netlink: Introduce a helper function to simplify netlink functions

2020-12-20 Thread Shi Lei
Extract common code as helper function virNetlinkTalk, then simplify
the functions virNetlink[DumpLink|NewLink|DelLink|GetNeighbor].

Signed-off-by: Shi Lei 
---
 src/util/virnetlink.c | 210 +++---
 src/util/virnetlink.h |   4 +-
 2 files changed, 78 insertions(+), 136 deletions(-)

diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
index fdcb0dc0..7bea38c0 100644
--- a/src/util/virnetlink.c
+++ b/src/util/virnetlink.c
@@ -353,6 +353,48 @@ int virNetlinkCommand(struct nl_msg *nl_msg,
 return 0;
 }
 
+
+static int
+virNetlinkTalk(const char *ifname,
+   virNetlinkMsg *nl_msg,
+   uint32_t src_pid,
+   uint32_t dst_pid,
+   struct nlmsghdr **resp,
+   unsigned int *resp_len,
+   int *error,
+   virNetlinkTalkFallback fallback)
+{
+if (virNetlinkCommand(nl_msg, resp, resp_len,
+  src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
+return -1;
+
+if (*resp_len < NLMSG_LENGTH(0) || resp == NULL)
+goto malformed_resp;
+
+if ((*resp)->nlmsg_type == NLMSG_ERROR) {
+struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA(resp);
+if ((*resp)->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+goto malformed_resp;
+
+if (-err->error == EOPNOTSUPP && fallback)
+return fallback(ifname);
+
+if (err->error < 0) {
+if (error)
+*error = err->error;
+return -1;
+}
+}
+
+return 0;
+
+ malformed_resp:
+virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+   _("malformed netlink response message"));
+return -1;
+}
+
+
 int
 virNetlinkDumpCommand(struct nl_msg *nl_msg,
   virNetlinkDumpCallback callback,
@@ -396,6 +438,7 @@ virNetlinkDumpCommand(struct nl_msg *nl_msg,
 return 0;
 }
 
+
 /**
  * virNetlinkDumpLink:
  *
@@ -420,15 +463,13 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
void **nlData, struct nlattr **tb,
uint32_t src_pid, uint32_t dst_pid)
 {
-int rc = -1;
-struct nlmsgerr *err;
 struct ifinfomsg ifinfo = {
 .ifi_family = AF_UNSPEC,
 .ifi_index  = ifindex
 };
-unsigned int recvbuflen;
 g_autoptr(virNetlinkMsg) nl_msg = NULL;
 g_autofree struct nlmsghdr *resp = NULL;
+unsigned int resp_len = 0;
 
 if (ifname && ifindex <= 0 && virNetDevGetIndex(ifname, &ifindex) < 0)
 return -1;
@@ -459,46 +500,19 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
 }
 # endif
 
-if (virNetlinkCommand(nl_msg, &resp, &recvbuflen,
-  src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
+if (virNetlinkTalk(ifname, nl_msg, src_pid, dst_pid,
+   &resp, &resp_len, NULL, NULL) < 0)
 return -1;
 
-if (recvbuflen < NLMSG_LENGTH(0) || resp == NULL)
-goto malformed_resp;
-
-switch (resp->nlmsg_type) {
-case NLMSG_ERROR:
-err = (struct nlmsgerr *)NLMSG_DATA(resp);
-if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
-goto malformed_resp;
-
-if (err->error) {
-virReportSystemError(-err->error,
- _("error dumping %s (%d) interface"),
- ifname, ifindex);
-return -1;
-}
-break;
-
-case GENL_ID_CTRL:
-case NLMSG_DONE:
-rc = nlmsg_parse(resp, sizeof(struct ifinfomsg),
- tb, IFLA_MAX, NULL);
-if (rc < 0)
-goto malformed_resp;
-break;
-
-default:
-goto malformed_resp;
+if ((resp->nlmsg_type != GENL_ID_CTRL && resp->nlmsg_type != NLMSG_DONE) ||
+nlmsg_parse(resp, sizeof(struct ifinfomsg), tb, IFLA_MAX, NULL) < 0) {
+virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+   _("malformed netlink response message"));
+return -1;
 }
 
 *nlData = g_steal_pointer(&resp);
 return 0;
-
- malformed_resp:
-virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-   _("malformed netlink response message"));
-return rc;
 }
 
 
@@ -523,13 +537,12 @@ virNetlinkNewLink(const char *ifname,
   virNetlinkNewLinkDataPtr extra_args,
   int *error)
 {
-struct nlmsgerr *err;
 struct nlattr *linkinfo = NULL;
 struct nlattr *infodata = NULL;
-unsigned int buflen;
 struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC };
 g_autoptr(virNetlinkMsg) nl_msg = NULL;
 g_autofree struct nlmsghdr *resp = NULL;
+unsigned int resp_len = 0;
 
 *error = 0;
 
@@ -591,37 +604,17 @@ virNetlinkNewLink(const char *ifname,
 }
 }
 
-if (virNetlinkCommand(n

[PATCH 2/4] netlink: Minor changes for macros NETLINK_MSG_[NEST_START|NEST_END|PUT]

2020-12-20 Thread Shi Lei
Move macros NETLINK_MSG_[NEST_START|NEST_END|PUT] from .h into .c;
within these macros, replace 'goto' with reporting error and returning;
simplify virNetlinkDumpLink and virNetlinkDelLink by using NETLINK_MSG_PUT.

Signed-off-by: Shi Lei 
---
 src/util/virnetlink.c | 44 +--
 src/util/virnetlink.h | 23 --
 2 files changed, 34 insertions(+), 33 deletions(-)

diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
index 17e6eeb9..0b55b124 100644
--- a/src/util/virnetlink.c
+++ b/src/util/virnetlink.c
@@ -44,6 +44,35 @@ VIR_LOG_INIT("util.netlink");
 
 # include 
 
+# define NETLINK_MSG_NEST_START(msg, container, attrtype) \
+do { \
+container = nla_nest_start(msg, attrtype); \
+if (!container) { \
+virReportError(VIR_ERR_INTERNAL_ERROR, "%s", \
+   _("allocated netlink buffer is too small")); \
+return -1; \
+} \
+} while (0)
+
+# define NETLINK_MSG_NEST_END(msg, container) \
+do { nla_nest_end(msg, container); } while (0)
+
+/*
+ * we need to use an intermediary pointer to @data as compilers may sometimes
+ * complain about @data not being a pointer type:
+ * error: the address of 'foo' will always evaluate as 'true' [-Werror=address]
+ */
+# define NETLINK_MSG_PUT(msg, attrtype, datalen, data) \
+do { \
+const void *dataptr = data; \
+if (dataptr && nla_put(msg, attrtype, datalen, dataptr) < 0) { \
+virReportError(VIR_ERR_INTERNAL_ERROR, "%s", \
+   _("allocated netlink buffer is too small")); \
+return -1; \
+} \
+} while (0)
+
+
 /* State for a single netlink event handle */
 struct virNetlinkEventHandle {
 int watch;
@@ -406,10 +435,8 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
 if (nlmsg_append(nl_msg,  &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
 goto buffer_too_small;
 
-if (ifname) {
-if (nla_put(nl_msg, IFLA_IFNAME, strlen(ifname)+1, ifname) < 0)
-goto buffer_too_small;
-}
+if (ifname)
+NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME, (strlen(ifname) + 1), ifname);
 
 # ifdef RTEXT_FILTER_VF
 /* if this filter exists in the kernel's netlink implementation,
@@ -419,10 +446,8 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
 {
 uint32_t ifla_ext_mask = RTEXT_FILTER_VF;
 
-if (nla_put(nl_msg, IFLA_EXT_MASK,
-sizeof(ifla_ext_mask), &ifla_ext_mask) < 0) {
-goto buffer_too_small;
-}
+NETLINK_MSG_PUT(nl_msg, IFLA_EXT_MASK,
+sizeof(ifla_ext_mask), &ifla_ext_mask);
 }
 # endif
 
@@ -636,8 +661,7 @@ virNetlinkDelLink(const char *ifname, 
virNetlinkDelLinkFallback fallback)
 if (nlmsg_append(nl_msg,  &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
 goto buffer_too_small;
 
-if (nla_put(nl_msg, IFLA_IFNAME, strlen(ifname)+1, ifname) < 0)
-goto buffer_too_small;
+NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME, (strlen(ifname) + 1), ifname);
 
 if (virNetlinkCommand(nl_msg, &resp, &recvbuflen, 0, 0,
   NETLINK_ROUTE, 0) < 0) {
diff --git a/src/util/virnetlink.h b/src/util/virnetlink.h
index 7c4ed202..966d6db3 100644
--- a/src/util/virnetlink.h
+++ b/src/util/virnetlink.h
@@ -38,29 +38,6 @@ struct nlmsghdr;
 
 #endif /* WITH_LIBNL */
 
-#define NETLINK_MSG_NEST_START(msg, container, attrtype) \
-do { \
-container = nla_nest_start(msg, attrtype); \
-if (!container) \
-goto buffer_too_small; \
-} while(0)
-
-#define NETLINK_MSG_NEST_END(msg, container) \
-do { nla_nest_end(msg, container); } while(0)
-
-/*
- * we need to use an intermediary pointer to @data as compilers may sometimes
- * complain about @data not being a pointer type:
- * error: the address of 'foo' will always evaluate as 'true' [-Werror=address]
- */
-#define NETLINK_MSG_PUT(msg, attrtype, datalen, data) \
-do { \
-const void *dataptr = data; \
-if (dataptr && nla_put(msg, attrtype, datalen, dataptr) < 0) \
-goto buffer_too_small; \
-} while(0)
-
-
 int virNetlinkStartup(void);
 void virNetlinkShutdown(void);
 
-- 
2.25.1




[PATCH 3/4] netlink: Introduce macro NETLINK_MSG_APPEND to wrap nlmsg_append

2020-12-20 Thread Shi Lei
Introduce a macro NETLINK_MSG_APPEND to wrap nlmsg_append and
simplify code.
Remove those labels 'buffer_too_small', since they are now useless.

Signed-off-by: Shi Lei 
---
 src/util/virnetlink.c | 42 +-
 1 file changed, 13 insertions(+), 29 deletions(-)

diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
index 0b55b124..fdcb0dc0 100644
--- a/src/util/virnetlink.c
+++ b/src/util/virnetlink.c
@@ -72,6 +72,15 @@ do { \
 } \
 } while (0)
 
+# define NETLINK_MSG_APPEND(msg, datalen, dataptr) \
+do { \
+if (nlmsg_append(msg, dataptr, datalen, NLMSG_ALIGNTO) < 0) { \
+virReportError(VIR_ERR_INTERNAL_ERROR, "%s", \
+   _("allocated netlink buffer is too small")); \
+return -1; \
+} \
+} while (0)
+
 
 /* State for a single netlink event handle */
 struct virNetlinkEventHandle {
@@ -432,8 +441,7 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
 return -1;
 }
 
-if (nlmsg_append(nl_msg,  &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
-goto buffer_too_small;
+NETLINK_MSG_APPEND(nl_msg, sizeof(ifinfo), &ifinfo);
 
 if (ifname)
 NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME, (strlen(ifname) + 1), ifname);
@@ -491,11 +499,6 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed netlink response message"));
 return rc;
-
- buffer_too_small:
-virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-   _("allocated netlink buffer is too small"));
-return rc;
 }
 
 
@@ -545,8 +548,7 @@ virNetlinkNewLink(const char *ifname,
 return -1;
 }
 
-if (nlmsg_append(nl_msg,  &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
-goto buffer_too_small;
+NETLINK_MSG_APPEND(nl_msg, sizeof(ifinfo), &ifinfo);
 
 NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME, (strlen(ifname) + 1), ifname);
 
@@ -620,11 +622,6 @@ virNetlinkNewLink(const char *ifname,
 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed netlink response message"));
 return -1;
-
- buffer_too_small:
-virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-   _("allocated netlink buffer is too small"));
-return -1;
 }
 
 
@@ -658,8 +655,7 @@ virNetlinkDelLink(const char *ifname, 
virNetlinkDelLinkFallback fallback)
 return -1;
 }
 
-if (nlmsg_append(nl_msg,  &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
-goto buffer_too_small;
+NETLINK_MSG_APPEND(nl_msg, sizeof(ifinfo), &ifinfo);
 
 NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME, (strlen(ifname) + 1), ifname);
 
@@ -701,11 +697,6 @@ virNetlinkDelLink(const char *ifname, 
virNetlinkDelLinkFallback fallback)
 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed netlink response message"));
 return -1;
-
- buffer_too_small:
-virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-   _("allocated netlink buffer is too small"));
-return -1;
 }
 
 /**
@@ -740,9 +731,7 @@ virNetlinkGetNeighbor(void **nlData, uint32_t src_pid, 
uint32_t dst_pid)
 return -1;
 }
 
-if (nlmsg_append(nl_msg, &ndinfo, sizeof(ndinfo), NLMSG_ALIGNTO) < 0)
-goto buffer_too_small;
-
+NETLINK_MSG_APPEND(nl_msg, sizeof(ndinfo), &ndinfo);
 
 if (virNetlinkCommand(nl_msg, &resp, &recvbuflen,
   src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
@@ -778,11 +767,6 @@ virNetlinkGetNeighbor(void **nlData, uint32_t src_pid, 
uint32_t dst_pid)
 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed netlink response message"));
 return -1;
-
- buffer_too_small:
-virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-   _("allocated netlink buffer is too small"));
-return -1;
 }
 
 int
-- 
2.25.1




[PATCH 1/4] netlink: Remove invalid flags(NLM_F_CREATE and NLM_F_EXCL) for RTM_DELLINK

2020-12-20 Thread Shi Lei
NLM_F_CREATE and NLM_F_EXCL are invalid for RTM_DELLINK, so remove them.

Signed-off-by: Shi Lei 
---
 src/util/virnetlink.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
index ca735bb8..17e6eeb9 100644
--- a/src/util/virnetlink.c
+++ b/src/util/virnetlink.c
@@ -627,8 +627,7 @@ virNetlinkDelLink(const char *ifname, 
virNetlinkDelLinkFallback fallback)
 g_autoptr(virNetlinkMsg) nl_msg = NULL;
 g_autofree struct nlmsghdr *resp = NULL;
 
-nl_msg = nlmsg_alloc_simple(RTM_DELLINK,
-NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL);
+nl_msg = nlmsg_alloc_simple(RTM_DELLINK, NLM_F_REQUEST);
 if (!nl_msg) {
 virReportOOMError();
 return -1;
-- 
2.25.1




[PATCH 0/4] netlink: Extract common code to simplify netlink functions

2020-12-20 Thread Shi Lei
This series makes some minor changes for macros NETLINK_MSG_* and extract
common code to simplify those netlink functions.

Shi Lei (4):
  netlink: Remove invalid flags(NLM_F_CREATE and NLM_F_EXCL) for RTM_DELLINK
  netlink: Minor changes for macros NETLINK_MSG_[NEST_START|NEST_END|PUT]
  netlink: Introduce macro NETLINK_MSG_APPEND to wrap nlmsg_append
  netlink: Introduce a helper function to simplify netlink functions

 src/util/virnetlink.c | 299 ++
 src/util/virnetlink.h |  27 +---
 2 files changed, 126 insertions(+), 200 deletions(-)

-- 
2.25.1




Re: Re: [PATCHv3 3/3] lxc: fix a memory leak

2020-12-18 Thread Shi Lei
On 2020-12-18 at 22:01, John Ferlan wrote:

>

>Coverity reminds us of the ancient software engineering proverb related

>to being stuck with ownership because you touched the code last :-) - I

>know this patch didn't cause the problem, but because the code was

>touched Coverity decided to look harder and found another leak.

>

>On 12/16/20 1:01 AM, Shi Lei wrote:

>> In virLXCProcessSetupInterfaceTap, containerVeth needs to be freed on

>> failure.

>> 

>> Signed-off-by: Shi Lei 

>> ---

>>  src/lxc/lxc_process.c | 4 ++--

>>  1 file changed, 2 insertions(+), 2 deletions(-)

>> 
>> diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c

>> index 85d0287a..0f7c9295 100644

>> --- a/src/lxc/lxc_process.c

>> +++ b/src/lxc/lxc_process.c

>> @@ -303,7 +303,7 @@ virLXCProcessSetupInterfaceTap(virDomainDefPtr vm,

>> const char *brname)

>>  {

>>  char *parentVeth;

>

>Coverity complains that @parentVeth is leaked too - although it's far

>more opaque and ornery.

>

>Let's assume on input that net->ifname != NULL - that means that in

>virNetDevVethCreate the call to virNetDevGenerateName and the memory in

>@**ifname (e.g. @parentVeth's copy of net->ifname) will be g_free()'d

>when !virNetDevExists(try) succeeds and @*ifname gets the memory from

>@try. In this case @try is a g_strdup_printf of @*ifname. So this code

>essentially free's memory pointed to by net->ifname, but since

>@parentVeth is used in the call net->ifname is *NOT* updated which is a

>second problem that Coverity did not note.

>

>Then let's say the call to virNetDevGenerateName fails for @veth2...

>Since on input @orig1 != NULL (e.g. @parentVeth != NULL), we will not

>VIR_FREE(*veth1); - that's fine given the original assumption, but when

>we return to virLXCProcessSetupInterfaceTap that means net->ifname will

>point at memory that was g_free'd and @parentVeth will not be free'd,

>thus Coverity squawks loud and proud about the resource leak - although

>it doesn't complain about the fact that net->ifname now points to memory

>that was free'd.

>

>As an aside, I see no way on input @*veth2 could not be NULL so in the

>cleanup path the check for @orig2 would seem to be dead code. Although,

>sure future changes could alter that reality.

>

>As a test if I replace all @parentVeth refs w/ net->ifname - then

>Coverity is happy again. I will leave it up to Laine or Shi Lei to

>generate the "real fix" and/or validate that my reading of the logic is

>right or not ;-).

>

>John

Thanks! :-)

As far as I can see, if the original net->ifname != NULL, it should be a 
user-provided
name and NOT a template (prefix+'%d'). When @parentVeth (as the copy of 
net->ifname)
is passed into virNetDevGenerateName, this function will leave it unchanged 
because it is
not a template.

So for now these problems will not happen. But for future, I think it's 
necessary to fix
virLXCProcessSetupInterfaceTap.

I agree that it's a right way to replace all *parentVeth* with net->ifname in
virLXCProcessSetupInterfaceTap. And the *parentVeth* should be removed.

Shi Lei






[PATCHv3 1/3] util:netlink: Enable virNetlinkNewLink to support veth

2020-12-15 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/util/virnetlink.c | 14 ++
 src/util/virnetlink.h |  1 +
 2 files changed, 15 insertions(+)

diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
index fdd3a6a4..8625b896 100644
--- a/src/util/virnetlink.c
+++ b/src/util/virnetlink.c
@@ -24,6 +24,7 @@
 #include 
 
 #include 
+#include 
 
 #include "virnetlink.h"
 #include "virnetdev.h"
@@ -535,6 +536,19 @@ virNetlinkNewLink(const char *ifname,
 NETLINK_MSG_NEST_END(nl_msg, infodata);
 }
 
+if (STREQ(type, "veth") && extra_args && extra_args->veth_peer) {
+struct nlattr *infoveth = NULL;
+
+NETLINK_MSG_NEST_START(nl_msg, infodata, IFLA_INFO_DATA);
+NETLINK_MSG_NEST_START(nl_msg, infoveth, VETH_INFO_PEER);
+nlmsg_reserve(nl_msg, sizeof(struct ifinfomsg), 0);
+NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME,
+(strlen(extra_args->veth_peer) + 1),
+extra_args->veth_peer);
+NETLINK_MSG_NEST_END(nl_msg, infoveth);
+NETLINK_MSG_NEST_END(nl_msg, infodata);
+}
+
 NETLINK_MSG_NEST_END(nl_msg, linkinfo);
 
 if (extra_args) {
diff --git a/src/util/virnetlink.h b/src/util/virnetlink.h
index 7121eac4..7c4ed202 100644
--- a/src/util/virnetlink.h
+++ b/src/util/virnetlink.h
@@ -84,6 +84,7 @@ struct _virNetlinkNewLinkData {
 const int *ifindex; /* The index for the 'link' device */
 const virMacAddr *mac;  /* The MAC address of the device */
 const uint32_t *macvlan_mode;   /* The mode of macvlan */
+const char *veth_peer;  /* The peer name for veth */
 };
 
 int virNetlinkNewLink(const char *ifname,
-- 
2.25.1




[PATCHv3 0/3] Use netlink to create veth device pair when netlink is supported

2020-12-15 Thread Shi Lei
V2 here: https://www.redhat.com/archives/libvir-list/2020-November/msg01239.html
Since V2:
* Remove the argument 'status' of virNetDevVethCreateInternal
* fix a memory leak in virLXCProcessSetupInterfaceTap

V1 here: https://www.redhat.com/archives/libvir-list/2020-November/msg00898.html
Since V1:
* Include  rather than introducing enum VETH_INFO_*
* Have complete functions within an #ifdefs rather than
  putting #ifdefs in function

Shi Lei (3):
  util:netlink: Enable virNetlinkNewLink to support veth
  util:veth: Create veth device pair by netlink
  lxc: fix a memory leak

 src/lxc/lxc_process.c|   4 +-
 src/util/virnetdevveth.c | 126 ++-
 src/util/virnetlink.c|  14 +
 src/util/virnetlink.h|   1 +
 4 files changed, 89 insertions(+), 56 deletions(-)

-- 
2.25.1




[PATCHv3 2/3] util:veth: Create veth device pair by netlink

2020-12-15 Thread Shi Lei
When netlink is supported, use netlink to create veth device pair
rather than 'ip link' command.

Signed-off-by: Shi Lei 
---
 src/util/virnetdevveth.c | 126 ++-
 1 file changed, 72 insertions(+), 54 deletions(-)

diff --git a/src/util/virnetdevveth.c b/src/util/virnetdevveth.c
index 194f595a..7133af44 100644
--- a/src/util/virnetdevveth.c
+++ b/src/util/virnetdevveth.c
@@ -27,21 +27,69 @@
 #include "virfile.h"
 #include "virstring.h"
 #include "virnetdev.h"
+#include "virnetlink.h"
 
 #define VIR_FROM_THIS VIR_FROM_NONE
 
 VIR_LOG_INIT("util.netdevveth");
 
 
+#if defined(WITH_LIBNL)
+static int
+virNetDevVethCreateInternal(const char *veth1, const char *veth2)
+{
+int status; /* Just ignore it */
+virNetlinkNewLinkData data = { .veth_peer = veth2 };
+
+return virNetlinkNewLink(veth1, "veth", &data, &status);
+}
+
+static int
+virNetDevVethDeleteInternal(const char *veth)
+{
+return virNetlinkDelLink(veth, NULL);
+}
+#else
+static int
+virNetDevVethCreateInternal(const char *veth1, const char *veth2)
+{
+g_autoptr(virCommand) cmd = virCommandNew("ip");
+virCommandAddArgList(cmd, "link", "add", veth1, "type", "veth",
+ "peer", "name", veth2, NULL);
+
+return virCommandRun(cmd, NULL);
+}
+
+static int
+virNetDevVethDeleteInternal(const char *veth)
+{
+int status;
+g_autoptr(virCommand) cmd = virCommandNewArgList("ip", "link",
+ "del", veth, NULL);
+
+if (virCommandRun(cmd, &status) < 0)
+return -1;
+
+if (status != 0) {
+if (!virNetDevExists(veth)) {
+VIR_DEBUG("Device %s already deleted (by kernel namespace 
cleanup)", veth);
+return 0;
+}
+virReportError(VIR_ERR_INTERNAL_ERROR,
+   _("Failed to delete veth device %s"), veth);
+return -1;
+}
+
+return 0;
+}
+#endif /* WITH_LIBNL */
+
 /**
  * virNetDevVethCreate:
- * @veth1: pointer to name for parent end of veth pair
- * @veth2: pointer to return name for container end of veth pair
+ * @veth1: pointer to name for one end of veth pair
+ * @veth2: pointer to name for another end of veth pair
  *
- * Creates a veth device pair using the ip command:
- * ip link add veth1 type veth peer name veth2
- * If veth1 points to NULL on entry, it will be a valid interface on
- * return.  veth2 should point to NULL on entry.
+ * Creates a veth device pair.
  *
  * NOTE: If veth1 and veth2 names are not specified, ip will auto assign
  *   names.  There seems to be two problems here -
@@ -58,44 +106,31 @@ VIR_LOG_INIT("util.netdevveth");
  *
  * Returns 0 on success or -1 in case of error
  */
-int virNetDevVethCreate(char** veth1, char** veth2)
+int virNetDevVethCreate(char **veth1, char **veth2)
 {
-int status;
-g_autofree char *veth1auto = NULL;
-g_autofree char *veth2auto = NULL;
-g_autoptr(virCommand) cmd = NULL;
+const char *orig1 = *veth1;
+const char *orig2 = *veth2;
 
-if (virNetDevGenerateName(&veth1auto, VIR_NET_DEV_GEN_NAME_VNET) < 0)
-return -1;
+if (virNetDevGenerateName(veth1, VIR_NET_DEV_GEN_NAME_VNET) < 0)
+goto cleanup;
 
-if (virNetDevGenerateName(&veth2auto, VIR_NET_DEV_GEN_NAME_VNET) < 0)
-return -1;
+if (virNetDevGenerateName(veth2, VIR_NET_DEV_GEN_NAME_VNET) < 0)
+goto cleanup;
 
-cmd = virCommandNew("ip");
-virCommandAddArgList(cmd, "link", "add",
- *veth1 ? *veth1 : veth1auto,
- "type", "veth", "peer", "name",
- *veth2 ? *veth2 : veth2auto,
- NULL);
+if (virNetDevVethCreateInternal(*veth1, *veth2) < 0)
+goto cleanup;
 
-if (virCommandRun(cmd, &status) < 0 || status) {
-virReportError(VIR_ERR_INTERNAL_ERROR,
-   "%s", _("Failed to allocate free veth pair"));
-return -1;
-}
+VIR_DEBUG("Create Host: %s guest: %s", *veth1, *veth2);
+return 0;
 
-VIR_DEBUG("create veth host: %s guest: %s: %d",
-  *veth1 ? *veth1 : veth1auto,
-  *veth2 ? *veth2 : veth2auto,
-  status);
+ cleanup:
+if (orig1 == NULL)
+VIR_FREE(*veth1);
 
-if (veth1auto)
-*veth1 = g_steal_pointer(&veth1auto);
-if (veth2auto)
-*veth2 = g_steal_pointer(&veth2auto);
+if (orig2 == NULL)
+VIR_FREE(*veth2);
 
-VIR_DEBUG("Create Host: %s guest: %s", *veth1, *veth2);
-return 0;
+return -1;
 }
 
 /**
@@ -111,22 +146,5 @@ int virNetDevVe

[PATCHv3 3/3] lxc: fix a memory leak

2020-12-15 Thread Shi Lei
In virLXCProcessSetupInterfaceTap, containerVeth needs to be freed on
failure.

Signed-off-by: Shi Lei 
---
 src/lxc/lxc_process.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c
index 85d0287a..0f7c9295 100644
--- a/src/lxc/lxc_process.c
+++ b/src/lxc/lxc_process.c
@@ -303,7 +303,7 @@ virLXCProcessSetupInterfaceTap(virDomainDefPtr vm,
const char *brname)
 {
 char *parentVeth;
-char *containerVeth = NULL;
+g_autofree char *containerVeth = NULL;
 const virNetDevVPortProfile *vport = 
virDomainNetGetActualVirtPortProfile(net);
 
 VIR_DEBUG("calling vethCreate()");
@@ -357,7 +357,7 @@ virLXCProcessSetupInterfaceTap(virDomainDefPtr vm,
 virDomainConfNWFilterInstantiate(vm->name, vm->uuid, net, false) < 0)
 return NULL;
 
-return containerVeth;
+return g_steal_pointer(&containerVeth);
 }
 
 
-- 
2.25.1




Re: Re: [PATCHv3 4/5] netdevveth: Simplify virNetDevVethCreate by using virNetDevGenerateName

2020-12-14 Thread Shi Lei
On 2020-12-15 at 11:09, Laine Stump wrote:
>On 12/13/20 8:50 PM, Shi Lei wrote:
>> Simplify virNetDevVethCreate by using common GenerateName/ReserveName
>> functions.
>>
>> Signed-off-by: Shi Lei 
>> ---
>>   src/lxc/lxc_process.c    |   3 +
>>   src/util/virnetdevveth.c | 140 +--
>>   2 files changed, 36 insertions(+), 107 deletions(-)
>>
>> diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c
>> index eb29431e..2e8ae706 100644
>> --- a/src/lxc/lxc_process.c
>> +++ b/src/lxc/lxc_process.c
>> @@ -307,6 +307,9 @@ virLXCProcessSetupInterfaceTap(virDomainDefPtr vm,
>>  
>>   VIR_DEBUG("calling vethCreate()");
>>   parentVeth = net->ifname;
>> +    if (parentVeth)
>> +    virNetDevReserveName(parentVeth);
>> +
>
>I think this is unnecessary (since a user-provided name shouldn't be
>using the auto-generate pattern, and would have been deleted by the
>parser anyway (see src/conf/domain_conf.c:12038, and comments in my
>reply to patch 5/5). So the only possible string here would be a string
>that would pass through virNetDevReserveName() with no action taken anyway. 

Okay.

>
>
>>   if (virNetDevVethCreate(&parentVeth, &containerVeth) < 0)
>>   return NULL;
>>   VIR_DEBUG("parentVeth: %s, containerVeth: %s", parentVeth, 
>>containerVeth);
>> diff --git a/src/util/virnetdevveth.c b/src/util/virnetdevveth.c
>> index b3eee1af..d6932a2e 100644
>> --- a/src/util/virnetdevveth.c
>> +++ b/src/util/virnetdevveth.c
>> @@ -32,48 +32,6 @@
>>  
>>   VIR_LOG_INIT("util.netdevveth");
>>  
>> -/* Functions */
>> -
>> -virMutex virNetDevVethCreateMutex = VIR_MUTEX_INITIALIZER;
>> -
>> -static int virNetDevVethExists(int devNum)
>> -{
>> -    int ret;
>> -    g_autofree char *path = NULL;
>> -
>> -    path = g_strdup_printf(SYSFS_NET_DIR "vnet%d/", devNum);
>> -    ret = virFileExists(path) ? 1 : 0;
>> -    VIR_DEBUG("Checked dev vnet%d usage: %d", devNum, ret);
>> -    return ret;
>> -}
>> -
>> -/**
>> - * virNetDevVethGetFreeNum:
>> - * @startDev: device number to start at (x in vethx)
>> - *
>> - * Looks in /sys/class/net/ to find the first available veth device
>> - * name.
>> - *
>> - * Returns non-negative device number on success or -1 in case of error
>> - */
>> -static int virNetDevVethGetFreeNum(int startDev)
>> -{
>> -    int devNum;
>> -
>> -#define MAX_DEV_NUM 65536
>> -
>> -    for (devNum = startDev; devNum < MAX_DEV_NUM; devNum++) {
>> -    int ret = virNetDevVethExists(devNum);
>> -    if (ret < 0)
>> -    return -1;
>> -    if (ret == 0)
>> -    return devNum;
>> -    }
>> -
>> -    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>> -   _("No free veth devices available"));
>> -    return -1;
>> -}
>>  
>>   /**
>>    * virNetDevVethCreate:
>> @@ -102,77 +60,45 @@ static int virNetDevVethGetFreeNum(int startDev)
>>    */
>>   int virNetDevVethCreate(char** veth1, char** veth2)
>>   {
>> -    int ret = -1;
>> -    int vethNum = 0;
>> -    size_t i;
>> -
>> -    /*
>> - * We might race with other containers, but this is reasonably
>> - * unlikely, so don't do too many retries for device creation
>> - */
>> -    virMutexLock(&virNetDevVethCreateMutex);
>> -#define MAX_VETH_RETRIES 10
>> -
>> -    for (i = 0; i < MAX_VETH_RETRIES; i++) {
>> -    g_autofree char *veth1auto = NULL;
>> -    g_autofree char *veth2auto = NULL;
>> -    g_autoptr(virCommand) cmd = NULL;
>> -
>> -    int status;
>> -    if (!*veth1) {
>> -    int veth1num;
>> -    if ((veth1num = virNetDevVethGetFreeNum(vethNum)) < 0)
>> -    goto cleanup;
>> -
>> -    veth1auto = g_strdup_printf("vnet%d", veth1num);
>> -    vethNum = veth1num + 1;
>> -    }
>> -    if (!*veth2) {
>> -    int veth2num;
>> -    if ((veth2num = virNetDevVethGetFreeNum(vethNum)) < 0)
>> -    goto cleanup;
>> +    int status;
>> +    g_autofree char *veth1auto = NULL;
>> +    g_autofree char *veth2auto = NULL;
>> +    g_autoptr(virCommand) cmd = NULL;
>>  
>> -    veth2auto = g_strdup_printf("vnet%d", veth2num

Re: Re: [PATCHv2 1/5] netdev: Introduce several helper functions for generating unique netdev name

2020-12-13 Thread Shi Lei
On 2020-12-14 at 10:10, Laine Stump wrote:
>On 12/9/20 10:00 PM, Shi Lei wrote:
>> Extract ReserveName/GenerateName from netdevtap and netdevmacvlan as
>> common helper functions.
>>
>> Signed-off-by: Shi Lei 
>> ---
>>   src/bhyve/bhyve_command.c  |   4 +-
>>   src/conf/domain_conf.c |   4 +-
>>   src/interface/interface_backend_udev.c |   2 +-
>>   src/libvirt_private.syms   |   2 +
>>   src/qemu/qemu_interface.c  |   8 +-
>>   src/util/virnetdev.c   | 116 +
>>   src/util/virnetdev.h   |  27 +-
>>   src/util/virnetdevtap.c    |  10 +--
>>   8 files changed, 158 insertions(+), 15 deletions(-)
>>
>> diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c
>> index acf3a5a4..4cf98c0e 100644
>> --- a/src/bhyve/bhyve_command.c
>> +++ b/src/bhyve/bhyve_command.c
>> @@ -80,10 +80,10 @@ bhyveBuildNetArgStr(const virDomainDef *def,
>>   }
>>  
>>   if (!net->ifname ||
>> -    STRPREFIX(net->ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
>> +    STRPREFIX(net->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
>>   strchr(net->ifname, '%')) {
>>   VIR_FREE(net->ifname);
>> -    net->ifname = g_strdup(VIR_NET_GENERATED_TAP_PREFIX "%d");
>> +    net->ifname = g_strdup(VIR_NET_GENERATED_VNET_PREFIX "%d");
>>   }
>>  
>>   if (!dryRun) {
>> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
>> index 23415b32..403ecab8 100644
>> --- a/src/conf/domain_conf.c
>> +++ b/src/conf/domain_conf.c
>> @@ -12037,7 +12037,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
>>  
>>   if (def->managed_tap != VIR_TRISTATE_BOOL_NO && ifname &&
>>   (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) &&
>> -    (STRPREFIX(ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
>> +    (STRPREFIX(ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
>>    STRPREFIX(ifname, VIR_NET_GENERATED_MACVTAP_PREFIX) ||
>>    STRPREFIX(ifname, VIR_NET_GENERATED_MACVLAN_PREFIX) ||
>>    (prefix && STRPREFIX(ifname, prefix {
>> @@ -26460,7 +26460,7 @@ virDomainNetDefFormat(virBufferPtr buf,
>>   if (def->ifname &&
>>   (def->managed_tap == VIR_TRISTATE_BOOL_NO ||
>>    !((flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE) &&
>> -   (STRPREFIX(def->ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
>> +   (STRPREFIX(def->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
>>   STRPREFIX(def->ifname, VIR_NET_GENERATED_MACVTAP_PREFIX) ||
>>   STRPREFIX(def->ifname, VIR_NET_GENERATED_MACVLAN_PREFIX) ||
>>   (prefix && STRPREFIX(def->ifname, prefix)) {
>> diff --git a/src/interface/interface_backend_udev.c 
>> b/src/interface/interface_backend_udev.c
>> index 173c4fc3..6a94a450 100644
>> --- a/src/interface/interface_backend_udev.c
>> +++ b/src/interface/interface_backend_udev.c
>> @@ -544,7 +544,7 @@ udevBridgeScanDirFilter(const struct dirent *entry)
>>    * vnet%d. Improvements to this check are welcome.
>>    */
>>   if (strlen(entry->d_name) >= 5) {
>> -    if (STRPREFIX(entry->d_name, VIR_NET_GENERATED_TAP_PREFIX) &&
>> +    if (STRPREFIX(entry->d_name, VIR_NET_GENERATED_VNET_PREFIX) &&
>>   g_ascii_isdigit(entry->d_name[4]))
>>   return 0;
>>   }
>> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
>> index 992488f7..c0f50856 100644
>> --- a/src/libvirt_private.syms
>> +++ b/src/libvirt_private.syms
>> @@ -2551,6 +2551,7 @@ virNetDevDelMulti;
>>   virNetDevExists;
>>   virNetDevFeatureTypeFromString;
>>   virNetDevFeatureTypeToString;
>> +virNetDevGenerateName;
>>   virNetDevGetFeatures;
>>   virNetDevGetIndex;
>>   virNetDevGetLinkInfo;
>> @@ -2574,6 +2575,7 @@ virNetDevIfStateTypeToString;
>>   virNetDevIsVirtualFunction;
>>   virNetDevPFGetVF;
>>   virNetDevReadNetConfig;
>> +virNetDevReserveName;
>>   virNetDevRunEthernetScript;
>>   virNetDevRxFilterFree;
>>   virNetDevRxFilterModeTypeFromString;
>> diff --git a/src/qemu/qemu_interface.c b/src/qemu/qemu_interface.c
>> index 32b397d2..197c0aa2 100644
>> --- a/src/qemu/qemu_interface.c
>> +++ b/src/qemu/qemu_interface.c
>> @@ -456,10 +456,10 @@ qemuInterfaceEthernetConnect(virDomai

[PATCHv3 1/5] netdev: Introduce several helper functions for generating unique netdev name

2020-12-13 Thread Shi Lei
Extract ReserveName/GenerateName from netdevtap and netdevmacvlan as
common helper functions.

Signed-off-by: Shi Lei 
---
 src/bhyve/bhyve_command.c  |   4 +-
 src/conf/domain_conf.c |   4 +-
 src/interface/interface_backend_udev.c |   2 +-
 src/libvirt_private.syms   |   2 +
 src/qemu/qemu_interface.c  |   8 +-
 src/util/virnetdev.c   | 116 +
 src/util/virnetdev.h   |  27 +-
 src/util/virnetdevtap.c|  10 +--
 8 files changed, 158 insertions(+), 15 deletions(-)

diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c
index acf3a5a4..4cf98c0e 100644
--- a/src/bhyve/bhyve_command.c
+++ b/src/bhyve/bhyve_command.c
@@ -80,10 +80,10 @@ bhyveBuildNetArgStr(const virDomainDef *def,
 }
 
 if (!net->ifname ||
-STRPREFIX(net->ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
+STRPREFIX(net->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
 strchr(net->ifname, '%')) {
 VIR_FREE(net->ifname);
-net->ifname = g_strdup(VIR_NET_GENERATED_TAP_PREFIX "%d");
+net->ifname = g_strdup(VIR_NET_GENERATED_VNET_PREFIX "%d");
 }
 
 if (!dryRun) {
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 23415b32..403ecab8 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -12037,7 +12037,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
 
 if (def->managed_tap != VIR_TRISTATE_BOOL_NO && ifname &&
 (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) &&
-(STRPREFIX(ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
+(STRPREFIX(ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
  STRPREFIX(ifname, VIR_NET_GENERATED_MACVTAP_PREFIX) ||
  STRPREFIX(ifname, VIR_NET_GENERATED_MACVLAN_PREFIX) ||
  (prefix && STRPREFIX(ifname, prefix {
@@ -26460,7 +26460,7 @@ virDomainNetDefFormat(virBufferPtr buf,
 if (def->ifname &&
 (def->managed_tap == VIR_TRISTATE_BOOL_NO ||
  !((flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE) &&
-   (STRPREFIX(def->ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
+   (STRPREFIX(def->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
 STRPREFIX(def->ifname, VIR_NET_GENERATED_MACVTAP_PREFIX) ||
 STRPREFIX(def->ifname, VIR_NET_GENERATED_MACVLAN_PREFIX) ||
 (prefix && STRPREFIX(def->ifname, prefix)) {
diff --git a/src/interface/interface_backend_udev.c 
b/src/interface/interface_backend_udev.c
index 173c4fc3..6a94a450 100644
--- a/src/interface/interface_backend_udev.c
+++ b/src/interface/interface_backend_udev.c
@@ -544,7 +544,7 @@ udevBridgeScanDirFilter(const struct dirent *entry)
  * vnet%d. Improvements to this check are welcome.
  */
 if (strlen(entry->d_name) >= 5) {
-if (STRPREFIX(entry->d_name, VIR_NET_GENERATED_TAP_PREFIX) &&
+if (STRPREFIX(entry->d_name, VIR_NET_GENERATED_VNET_PREFIX) &&
 g_ascii_isdigit(entry->d_name[4]))
 return 0;
 }
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 992488f7..c0f50856 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2551,6 +2551,7 @@ virNetDevDelMulti;
 virNetDevExists;
 virNetDevFeatureTypeFromString;
 virNetDevFeatureTypeToString;
+virNetDevGenerateName;
 virNetDevGetFeatures;
 virNetDevGetIndex;
 virNetDevGetLinkInfo;
@@ -2574,6 +2575,7 @@ virNetDevIfStateTypeToString;
 virNetDevIsVirtualFunction;
 virNetDevPFGetVF;
 virNetDevReadNetConfig;
+virNetDevReserveName;
 virNetDevRunEthernetScript;
 virNetDevRxFilterFree;
 virNetDevRxFilterModeTypeFromString;
diff --git a/src/qemu/qemu_interface.c b/src/qemu/qemu_interface.c
index 32b397d2..197c0aa2 100644
--- a/src/qemu/qemu_interface.c
+++ b/src/qemu/qemu_interface.c
@@ -456,10 +456,10 @@ qemuInterfaceEthernetConnect(virDomainDefPtr def,
 }
 } else {
 if (!net->ifname ||
-STRPREFIX(net->ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
+STRPREFIX(net->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
 strchr(net->ifname, '%')) {
 VIR_FREE(net->ifname);
-net->ifname = g_strdup(VIR_NET_GENERATED_TAP_PREFIX "%d");
+net->ifname = g_strdup(VIR_NET_GENERATED_VNET_PREFIX "%d");
 /* avoid exposing vnet%d in getXMLDesc or error outputs */
 template_ifname = true;
 }
@@ -560,10 +560,10 @@ qemuInterfaceBridgeConnect(virDomainDefPtr def,
 }
 
 if (!net->ifname ||
-STRPREFIX(net->ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
+STRPREFIX(net->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
 strchr(net->ifname, '%')) {
 VIR_FREE(net->ifname);
-net->ifname = g_strdup

[PATCHv3 4/5] netdevveth: Simplify virNetDevVethCreate by using virNetDevGenerateName

2020-12-13 Thread Shi Lei
Simplify virNetDevVethCreate by using common GenerateName/ReserveName
functions.

Signed-off-by: Shi Lei 
---
 src/lxc/lxc_process.c|   3 +
 src/util/virnetdevveth.c | 140 +--
 2 files changed, 36 insertions(+), 107 deletions(-)

diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c
index eb29431e..2e8ae706 100644
--- a/src/lxc/lxc_process.c
+++ b/src/lxc/lxc_process.c
@@ -307,6 +307,9 @@ virLXCProcessSetupInterfaceTap(virDomainDefPtr vm,
 
 VIR_DEBUG("calling vethCreate()");
 parentVeth = net->ifname;
+if (parentVeth)
+virNetDevReserveName(parentVeth);
+
 if (virNetDevVethCreate(&parentVeth, &containerVeth) < 0)
 return NULL;
 VIR_DEBUG("parentVeth: %s, containerVeth: %s", parentVeth, containerVeth);
diff --git a/src/util/virnetdevveth.c b/src/util/virnetdevveth.c
index b3eee1af..d6932a2e 100644
--- a/src/util/virnetdevveth.c
+++ b/src/util/virnetdevveth.c
@@ -32,48 +32,6 @@
 
 VIR_LOG_INIT("util.netdevveth");
 
-/* Functions */
-
-virMutex virNetDevVethCreateMutex = VIR_MUTEX_INITIALIZER;
-
-static int virNetDevVethExists(int devNum)
-{
-int ret;
-g_autofree char *path = NULL;
-
-path = g_strdup_printf(SYSFS_NET_DIR "vnet%d/", devNum);
-ret = virFileExists(path) ? 1 : 0;
-VIR_DEBUG("Checked dev vnet%d usage: %d", devNum, ret);
-return ret;
-}
-
-/**
- * virNetDevVethGetFreeNum:
- * @startDev: device number to start at (x in vethx)
- *
- * Looks in /sys/class/net/ to find the first available veth device
- * name.
- *
- * Returns non-negative device number on success or -1 in case of error
- */
-static int virNetDevVethGetFreeNum(int startDev)
-{
-int devNum;
-
-#define MAX_DEV_NUM 65536
-
-for (devNum = startDev; devNum < MAX_DEV_NUM; devNum++) {
-int ret = virNetDevVethExists(devNum);
-if (ret < 0)
-return -1;
-if (ret == 0)
-return devNum;
-}
-
-virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-   _("No free veth devices available"));
-return -1;
-}
 
 /**
  * virNetDevVethCreate:
@@ -102,77 +60,45 @@ static int virNetDevVethGetFreeNum(int startDev)
  */
 int virNetDevVethCreate(char** veth1, char** veth2)
 {
-int ret = -1;
-int vethNum = 0;
-size_t i;
-
-/*
- * We might race with other containers, but this is reasonably
- * unlikely, so don't do too many retries for device creation
- */
-virMutexLock(&virNetDevVethCreateMutex);
-#define MAX_VETH_RETRIES 10
-
-for (i = 0; i < MAX_VETH_RETRIES; i++) {
-g_autofree char *veth1auto = NULL;
-g_autofree char *veth2auto = NULL;
-g_autoptr(virCommand) cmd = NULL;
-
-int status;
-if (!*veth1) {
-int veth1num;
-if ((veth1num = virNetDevVethGetFreeNum(vethNum)) < 0)
-goto cleanup;
-
-veth1auto = g_strdup_printf("vnet%d", veth1num);
-vethNum = veth1num + 1;
-}
-if (!*veth2) {
-int veth2num;
-if ((veth2num = virNetDevVethGetFreeNum(vethNum)) < 0)
-goto cleanup;
+int status;
+g_autofree char *veth1auto = NULL;
+g_autofree char *veth2auto = NULL;
+g_autoptr(virCommand) cmd = NULL;
 
-veth2auto = g_strdup_printf("vnet%d", veth2num);
-vethNum = veth2num + 1;
-}
+if (!*veth1) {
+if (virNetDevGenerateName(&veth1auto, VIR_NET_DEV_GEN_NAME_VNET) < 0)
+return -1;
+}
+if (!*veth2) {
+if (virNetDevGenerateName(&veth2auto, VIR_NET_DEV_GEN_NAME_VNET) < 0)
+return -1;
+}
 
-cmd = virCommandNew("ip");
-virCommandAddArgList(cmd, "link", "add",
- *veth1 ? *veth1 : veth1auto,
- "type", "veth", "peer", "name",
- *veth2 ? *veth2 : veth2auto,
- NULL);
-
-if (virCommandRun(cmd, &status) < 0)
-goto cleanup;
-
-if (status == 0) {
-if (veth1auto) {
-*veth1 = veth1auto;
-veth1auto = NULL;
-}
-if (veth2auto) {
-*veth2 = veth2auto;
-veth2auto = NULL;
-}
-VIR_DEBUG("Create Host: %s guest: %s", *veth1, *veth2);
-ret = 0;
-goto cleanup;
-}
+cmd = virCommandNew("ip");
+virCommandAddArgList(cmd, "link", "add",
+ *veth1 ? *veth1 : veth1auto,
+ "type", "veth", "peer", "name",
+ *veth2 ? *veth2 : veth2auto,
+ N

[PATCHv3 0/5] netdev: Extract GenerateName/ReserveName as common functions

2020-12-13 Thread Shi Lei
V2 here: https://www.redhat.com/archives/libvir-list/2020-December/msg00563.html

Since V2:
 *  Fix libxl driver for missing changing virNetDevMacVLanReserveName

V1 here: https://www.redhat.com/archives/libvir-list/2020-December/msg00308.html

Since V1:
(1) Remove virNetDev[Lock|Unlock]GenName.
Only *lastID* needs to be protected. Now we lock *lastID*
inside virNetDevReserveName and virNetDevGenerateName, then lock and
unlock functions are no longer needed.

(2) Shorten the locking range for generating names for tap and macvlan.
Since virNetDevReserveName and virNetDevGenerateName are now with lock,
we can call them directly rather than adding lock outside.

(3) Rename *_GEN_NAME_TAP to *_GEN_NAME_VNET.

(4) Now veth and tap share the same prefix "vnet".

(5) Use name rather than type as the argument of virNetDevReserveName.
Just follow the style of virNetDev[Tap|MacVlan]ReserveName.

(6) Remove those in-between functions for tap and macvlan.

(7) Remove useless ENUM_[DECL|IMPL] for enum virNetDevGenNameType.

(8) Remove the *_NONE item for enum virNetDevGenNameType.

(9) Remove useless  in virnetdevtap.

(10) When @ifname of virNetDevGenerateName is NOT a template or NULL,
just leave it unchanged.

(11) Take advantage of the "g_strdup_printf(*ifname, id)" in
virNetDevGenerateName, prevent the functions that call 
virNetDevTapCreate()
from adding in "vnet%d" when ifname is empty.

(12) Use VIR_NETDEV_MACVLAN_CREATE_WITH_TAP to distinguish macvtap and
macvlan and remove those useless macros.


Shi Lei (5):
  netdev: Introduce several helper functions for generating unique netdev name
  netdevtap: Use common helper function to create unique tap name
  netdevmacvlan: Use helper function to create unique macvlan/macvtap name
  netdevveth: Simplify virNetDevVethCreate by using virNetDevGenerateName
  netdev: Prevent functions that call virNetDevTapCreate from adding 'vnet%d' 
into ifname

 src/bhyve/bhyve_command.c  |   3 +-
 src/conf/domain_conf.c |   4 +-
 src/interface/interface_backend_udev.c |   2 +-
 src/libvirt_private.syms   |   4 +-
 src/libxl/libxl_driver.c   |   2 +-
 src/lxc/lxc_process.c  |   5 +-
 src/qemu/qemu_interface.c  |  16 +--
 src/qemu/qemu_process.c|   4 +-
 src/util/virnetdev.c   | 116 
 src/util/virnetdev.h   |  27 +++-
 src/util/virnetdevmacvlan.c| 177 +++--
 src/util/virnetdevmacvlan.h|  14 +-
 src/util/virnetdevtap.c| 100 +-
 src/util/virnetdevtap.h|   4 -
 src/util/virnetdevveth.c   | 140 +--
 15 files changed, 218 insertions(+), 400 deletions(-)

-- 
2.25.1




[PATCHv3 3/5] netdevmacvlan: Use helper function to create unique macvlan/macvtap name

2020-12-13 Thread Shi Lei
Simplify ReserveName/GenerateName for macvlan and macvtap by using
common functions.

Signed-off-by: Shi Lei 
---
 src/libvirt_private.syms|   1 -
 src/libxl/libxl_driver.c|   2 +-
 src/lxc/lxc_process.c   |   2 +-
 src/qemu/qemu_process.c |   2 +-
 src/util/virnetdevmacvlan.c | 177 +---
 src/util/virnetdevmacvlan.h |  14 +--
 6 files changed, 27 insertions(+), 171 deletions(-)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 64ef01e1..4d6ae84b 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2655,7 +2655,6 @@ virNetDevMacVLanDelete;
 virNetDevMacVLanDeleteWithVPortProfile;
 virNetDevMacVLanIsMacvtap;
 virNetDevMacVLanModeTypeFromString;
-virNetDevMacVLanReserveName;
 virNetDevMacVLanRestartWithVPortProfile;
 virNetDevMacVLanTapOpen;
 virNetDevMacVLanTapSetup;
diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c
index 6af274cb..3488d7ec 100644
--- a/src/libxl/libxl_driver.c
+++ b/src/libxl/libxl_driver.c
@@ -364,7 +364,7 @@ libxlReconnectNotifyNets(virDomainDefPtr def)
  * impolite.
  */
 if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_DIRECT)
-virNetDevMacVLanReserveName(net->ifname);
+virNetDevReserveName(net->ifname);
 
 if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
 if (!conn && !(conn = virGetConnectNetwork()))
diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c
index 0f818e2e..eb29431e 100644
--- a/src/lxc/lxc_process.c
+++ b/src/lxc/lxc_process.c
@@ -1641,7 +1641,7 @@ virLXCProcessReconnectNotifyNets(virDomainDefPtr def)
  * impolite.
  */
 if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_DIRECT)
-virNetDevMacVLanReserveName(net->ifname);
+virNetDevReserveName(net->ifname);
 
 if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
 if (!conn && !(conn = virGetConnectNetwork()))
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 1d54f201..6244ade4 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -3390,7 +3390,7 @@ qemuProcessNotifyNets(virDomainDefPtr def)
  */
 switch (virDomainNetGetActualType(net)) {
 case VIR_DOMAIN_NET_TYPE_DIRECT:
-virNetDevMacVLanReserveName(net->ifname);
+virNetDevReserveName(net->ifname);
 break;
 case VIR_DOMAIN_NET_TYPE_BRIDGE:
 case VIR_DOMAIN_NET_TYPE_NETWORK:
diff --git a/src/util/virnetdevmacvlan.c b/src/util/virnetdevmacvlan.c
index 72f0d670..36b13133 100644
--- a/src/util/virnetdevmacvlan.c
+++ b/src/util/virnetdevmacvlan.c
@@ -45,7 +45,6 @@ VIR_ENUM_IMPL(virNetDevMacVLanMode,
 
 # include 
 # include 
-# include 
 
 # include "viralloc.h"
 # include "virlog.h"
@@ -59,129 +58,6 @@ VIR_ENUM_IMPL(virNetDevMacVLanMode,
 
 VIR_LOG_INIT("util.netdevmacvlan");
 
-# define VIR_NET_GENERATED_MACVTAP_PATTERN VIR_NET_GENERATED_MACVTAP_PREFIX 
"%d"
-# define VIR_NET_GENERATED_MACVLAN_PATTERN VIR_NET_GENERATED_MACVLAN_PREFIX 
"%d"
-# define VIR_NET_GENERATED_PREFIX \
-((flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? \
- VIR_NET_GENERATED_MACVTAP_PREFIX : VIR_NET_GENERATED_MACVLAN_PREFIX)
-
-
-virMutex virNetDevMacVLanCreateMutex = VIR_MUTEX_INITIALIZER;
-static int virNetDevMacVTapLastID = -1;
-static int virNetDevMacVLanLastID = -1;
-
-
-static void
-virNetDevMacVLanReserveNameInternal(const char *name)
-{
-unsigned int id;
-const char *idstr = NULL;
-int *lastID = NULL;
-int len;
-
-if (STRPREFIX(name, VIR_NET_GENERATED_MACVTAP_PREFIX)) {
-lastID = &virNetDevMacVTapLastID;
-len = strlen(VIR_NET_GENERATED_MACVTAP_PREFIX);
-} else if (STRPREFIX(name, VIR_NET_GENERATED_MACVLAN_PREFIX)) {
-lastID = &virNetDevMacVTapLastID;
-len = strlen(VIR_NET_GENERATED_MACVLAN_PREFIX);
-} else {
-return;
-}
-
-VIR_INFO("marking device in use: '%s'", name);
-
-idstr = name + len;
-
-if (virStrToLong_ui(idstr, NULL, 10, &id) >= 0) {
-if (*lastID < (int)id)
-*lastID = id;
-}
-}
-
-
-/**
- * virNetDevMacVLanReserveName:
- * @name: name of an existing macvtap/macvlan device
- *
- * Set the value of virNetDevMacV(Lan|Tap)LastID to assure that any
- * new device created with an autogenerated name will use a number
- * higher than the number in the given device name.
- *
- * Returns nothing.
- */
-void
-virNetDevMacVLanReserveName(const char *name)
-{
-virMutexLock(&virNetDevMacVLanCreateMutex);
-virNetDevMacVLanReserveNameInternal(name);
-virMutexUnlock(&virNetDevMacVLanCreateMutex);
-}
-
-
-/**
- * virNetDevMacVLanGenerateName:
- * @ifname: pointer to pointer to string containing template
- * @lastID: counter to add to the template to form the name
- *
- * generate 

[PATCHv3 2/5] netdevtap: Use common helper function to create unique tap name

2020-12-13 Thread Shi Lei
Simplify GenerateName/ReserveName for netdevtap by using common
functions.

Signed-off-by: Shi Lei 
---
 src/libvirt_private.syms |   1 -
 src/qemu/qemu_process.c  |   2 +-
 src/util/virnetdevtap.c  | 100 ++-
 src/util/virnetdevtap.h  |   4 --
 4 files changed, 5 insertions(+), 102 deletions(-)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index c0f50856..64ef01e1 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2689,7 +2689,6 @@ virNetDevTapGetName;
 virNetDevTapGetRealDeviceName;
 virNetDevTapInterfaceStats;
 virNetDevTapReattachBridge;
-virNetDevTapReserveName;
 
 
 # util/virnetdevveth.h
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 3b64caa6..1d54f201 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -3395,7 +3395,7 @@ qemuProcessNotifyNets(virDomainDefPtr def)
 case VIR_DOMAIN_NET_TYPE_BRIDGE:
 case VIR_DOMAIN_NET_TYPE_NETWORK:
 case VIR_DOMAIN_NET_TYPE_ETHERNET:
-virNetDevTapReserveName(net->ifname);
+virNetDevReserveName(net->ifname);
 break;
 case VIR_DOMAIN_NET_TYPE_USER:
 case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
diff --git a/src/util/virnetdevtap.c b/src/util/virnetdevtap.c
index 9354cc10..88ad3216 100644
--- a/src/util/virnetdevtap.c
+++ b/src/util/virnetdevtap.c
@@ -49,51 +49,11 @@
 #if defined(WITH_GETIFADDRS) && defined(AF_LINK)
 # include 
 #endif
-#include 
 
 #define VIR_FROM_THIS VIR_FROM_NONE
 
 VIR_LOG_INIT("util.netdevtap");
 
-virMutex virNetDevTapCreateMutex = VIR_MUTEX_INITIALIZER;
-static int virNetDevTapLastID = -1; /* not "unsigned" because callers use %d */
-
-
-/**
- * virNetDevTapReserveName:
- * @name: name of an existing tap device
- *
- * Set the value of virNetDevTapLastID to assure that any new tap
- * device created with an autogenerated name will use a number higher
- * than the number in the given tap device name.
- *
- * Returns nothing.
- */
-void
-virNetDevTapReserveName(const char *name)
-{
-unsigned int id;
-const char *idstr = NULL;
-
-
-if (STRPREFIX(name, VIR_NET_GENERATED_VNET_PREFIX)) {
-
-VIR_INFO("marking device in use: '%s'", name);
-
-idstr = name + strlen(VIR_NET_GENERATED_VNET_PREFIX);
-
-if (virStrToLong_ui(idstr, NULL, 10, &id) >= 0) {
-virMutexLock(&virNetDevTapCreateMutex);
-
-if (virNetDevTapLastID < (int)id)
-virNetDevTapLastID = id;
-
-virMutexUnlock(&virNetDevTapCreateMutex);
-}
-}
-}
-
-
 /**
  * virNetDevTapGetName:
  * @tapfd: a tun/tap file descriptor
@@ -183,55 +143,6 @@ virNetDevTapGetRealDeviceName(char *ifname G_GNUC_UNUSED)
 
 
 #ifdef TUNSETIFF
-/**
- * virNetDevTapGenerateName:
- * @ifname: pointer to pointer to string containing template
- *
- * generate a new (currently unused) name for a new tap device based
- * on the templace string in @ifname - replace %d with
- * ++virNetDevTapLastID, and keep trying new values until one is found
- * that doesn't already exist, or we've tried 1 different
- * names. Once a usable name is found, replace the template with the
- * actual name.
- *
- * Returns 0 on success, -1 on failure.
- */
-static int
-virNetDevTapGenerateName(char **ifname)
-{
-int id;
-double maxIDd = pow(10, IFNAMSIZ - 1 - 
strlen(VIR_NET_GENERATED_VNET_PREFIX));
-int maxID = INT_MAX;
-int attempts = 0;
-
-if (maxIDd <= (double)INT_MAX)
-maxID = (int)maxIDd;
-
-do {
-g_autofree char *try = NULL;
-
-id = ++virNetDevTapLastID;
-
-/* reset before overflow */
-if (virNetDevTapLastID >= maxID)
-virNetDevTapLastID = -1;
-
-try = g_strdup_printf(*ifname, id);
-
-if (!virNetDevExists(try)) {
-g_free(*ifname);
-*ifname = g_steal_pointer(&try);
-return 0;
-}
-} while (++attempts < 1);
-
-virReportError(VIR_ERR_INTERNAL_ERROR,
-   _("no unused %s names available"),
-   VIR_NET_GENERATED_VNET_PREFIX);
-return -1;
-}
-
-
 /**
  * virNetDevTapCreate:
  * @ifname: the interface name
@@ -263,16 +174,14 @@ int virNetDevTapCreate(char **ifname,
 int ret = -1;
 int fd = -1;
 
-virMutexLock(&virNetDevTapCreateMutex);
-
 /* if ifname is "vnet%d", then auto-generate a name for the new
  * device (the kernel could do this for us, but has a bad habit of
  * immediately re-using names that have just been released, which
  * can lead to race conditions).
- */
-if (STREQ(*ifname, VIR_NET_GENERATED_VNET_PREFIX "%d") &&
-virNetDevTapGenerateName(ifname) < 0) {
-goto cleanup;
+ * if ifname is just a user-provided name, virNetDevGenerateName
+ * leaves it unchanged. */
+   

[PATCHv3 5/5] netdev: Prevent functions that call virNetDevTapCreate from adding 'vnet%d' into ifname

2020-12-13 Thread Shi Lei
Those functions that call virNetDevTapCreate don't need to adding
'vnet%d' into ifname when it is empty, since virNetDevGenerateName
which is in virNetDevTapCreate can deal with it.

Signed-off-by: Shi Lei 
---
 src/bhyve/bhyve_command.c |  1 -
 src/qemu/qemu_interface.c | 12 
 2 files changed, 13 deletions(-)

diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c
index 4cf98c0e..92b31a6e 100644
--- a/src/bhyve/bhyve_command.c
+++ b/src/bhyve/bhyve_command.c
@@ -83,7 +83,6 @@ bhyveBuildNetArgStr(const virDomainDef *def,
 STRPREFIX(net->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
 strchr(net->ifname, '%')) {
 VIR_FREE(net->ifname);
-net->ifname = g_strdup(VIR_NET_GENERATED_VNET_PREFIX "%d");
 }
 
 if (!dryRun) {
diff --git a/src/qemu/qemu_interface.c b/src/qemu/qemu_interface.c
index 197c0aa2..87cfb8fc 100644
--- a/src/qemu/qemu_interface.c
+++ b/src/qemu/qemu_interface.c
@@ -413,7 +413,6 @@ qemuInterfaceEthernetConnect(virDomainDefPtr def,
 virMacAddr tapmac;
 int ret = -1;
 unsigned int tap_create_flags = VIR_NETDEV_TAP_CREATE_IFUP;
-bool template_ifname = false;
 g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
 const char *tunpath = "/dev/net/tun";
 const char *auditdev = tunpath;
@@ -459,9 +458,6 @@ qemuInterfaceEthernetConnect(virDomainDefPtr def,
 STRPREFIX(net->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
 strchr(net->ifname, '%')) {
 VIR_FREE(net->ifname);
-net->ifname = g_strdup(VIR_NET_GENERATED_VNET_PREFIX "%d");
-/* avoid exposing vnet%d in getXMLDesc or error outputs */
-template_ifname = true;
 }
 if (virNetDevTapCreate(&net->ifname, tunpath, tapfd, tapfdSize,
tap_create_flags) < 0) {
@@ -512,8 +508,6 @@ qemuInterfaceEthernetConnect(virDomainDefPtr def,
 virDomainAuditNetDevice(def, net, auditdev, false);
 for (i = 0; i < tapfdSize && tapfd[i] >= 0; i++)
 VIR_FORCE_CLOSE(tapfd[i]);
-if (template_ifname)
-VIR_FREE(net->ifname);
 }
 
 return ret;
@@ -541,7 +535,6 @@ qemuInterfaceBridgeConnect(virDomainDefPtr def,
 const char *brname;
 int ret = -1;
 unsigned int tap_create_flags = VIR_NETDEV_TAP_CREATE_IFUP;
-bool template_ifname = false;
 g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
 const char *tunpath = "/dev/net/tun";
 
@@ -563,9 +556,6 @@ qemuInterfaceBridgeConnect(virDomainDefPtr def,
 STRPREFIX(net->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
 strchr(net->ifname, '%')) {
 VIR_FREE(net->ifname);
-net->ifname = g_strdup(VIR_NET_GENERATED_VNET_PREFIX "%d");
-/* avoid exposing vnet%d in getXMLDesc or error outputs */
-template_ifname = true;
 }
 
 if (qemuInterfaceIsVnetCompatModel(net))
@@ -630,8 +620,6 @@ qemuInterfaceBridgeConnect(virDomainDefPtr def,
 size_t i;
 for (i = 0; i < *tapfdSize && tapfd[i] >= 0; i++)
 VIR_FORCE_CLOSE(tapfd[i]);
-if (template_ifname)
-VIR_FREE(net->ifname);
 }
 
 return ret;
-- 
2.25.1




Re: Re: [PATCHv2 3/5] netdevmacvlan: Use helper function to create unique macvlan/macvtap name

2020-12-13 Thread Shi Lei
On 2020-12-14 at 08:58, Laine Stump wrote:
>On 12/9/20 10:00 PM, Shi Lei wrote:
>> Simplify ReserveName/GenerateName for macvlan and macvtap by using
>> common functions.
>>
>> Signed-off-by: Shi Lei 
>> ---
>>   src/libvirt_private.syms    |   1 -
>>   src/lxc/lxc_process.c   |   2 +-
>>   src/qemu/qemu_process.c |   2 +-
>>   src/util/virnetdevmacvlan.c | 177 +---
>>   src/util/virnetdevmacvlan.h |  14 +--
>>   5 files changed, 26 insertions(+), 170 deletions(-)
>
>You probably don't have the libxl driver enabled in your builds, so you
>missed this change (which I've squashed in):
>
>diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c
>index 6af274cb1b..3488d7ec08 100644
>--- a/src/libxl/libxl_driver.c
>+++ b/src/libxl/libxl_driver.c
>@@ -364,7 +364,7 @@ libxlReconnectNotifyNets(virDomainDefPtr def)
>   * impolite.
>   */
>  if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_DIRECT)
>-    virNetDevMacVLanReserveName(net->ifname);
>+    virNetDevReserveName(net->ifname);
> 

Sorry for that. I fix it and send V3.

Shi Lei

>>
>> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
>> index 64ef01e1..4d6ae84b 100644
>> --- a/src/libvirt_private.syms
>> +++ b/src/libvirt_private.syms
>> @@ -2655,7 +2655,6 @@ virNetDevMacVLanDelete;
>>   virNetDevMacVLanDeleteWithVPortProfile;
>>   virNetDevMacVLanIsMacvtap;
>>   virNetDevMacVLanModeTypeFromString;
>> -virNetDevMacVLanReserveName;
>>   virNetDevMacVLanRestartWithVPortProfile;
>>   virNetDevMacVLanTapOpen;
>>   virNetDevMacVLanTapSetup;
>> diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c
>> index 0f818e2e..eb29431e 100644
>> --- a/src/lxc/lxc_process.c
>> +++ b/src/lxc/lxc_process.c
>> @@ -1641,7 +1641,7 @@ virLXCProcessReconnectNotifyNets(virDomainDefPtr def)
>>    * impolite.
>>    */
>>   if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_DIRECT)
>> -    virNetDevMacVLanReserveName(net->ifname);
>> +    virNetDevReserveName(net->ifname);
>>  
>>   if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
>>   if (!conn && !(conn = virGetConnectNetwork()))
>> diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
>> index 1d54f201..6244ade4 100644
>> --- a/src/qemu/qemu_process.c
>> +++ b/src/qemu/qemu_process.c
>> @@ -3390,7 +3390,7 @@ qemuProcessNotifyNets(virDomainDefPtr def)
>>    */
>>   switch (virDomainNetGetActualType(net)) {
>>   case VIR_DOMAIN_NET_TYPE_DIRECT:
>> -    virNetDevMacVLanReserveName(net->ifname);
>> +    virNetDevReserveName(net->ifname);
>>   break;
>>   case VIR_DOMAIN_NET_TYPE_BRIDGE:
>>   case VIR_DOMAIN_NET_TYPE_NETWORK:
>> diff --git a/src/util/virnetdevmacvlan.c b/src/util/virnetdevmacvlan.c
>> index 72f0d670..36b13133 100644
>> --- a/src/util/virnetdevmacvlan.c
>> +++ b/src/util/virnetdevmacvlan.c
>> @@ -45,7 +45,6 @@ VIR_ENUM_IMPL(virNetDevMacVLanMode,
>>  
>>   # include 
>>   # include 
>> -# include 
>>  
>>   # include "viralloc.h"
>>   # include "virlog.h"
>> @@ -59,129 +58,6 @@ VIR_ENUM_IMPL(virNetDevMacVLanMode,
>>  
>>   VIR_LOG_INIT("util.netdevmacvlan");
>>  
>> -# define VIR_NET_GENERATED_MACVTAP_PATTERN VIR_NET_GENERATED_MACVTAP_PREFIX 
>> "%d"
>> -# define VIR_NET_GENERATED_MACVLAN_PATTERN VIR_NET_GENERATED_MACVLAN_PREFIX 
>> "%d"
>> -# define VIR_NET_GENERATED_PREFIX \
>> -    ((flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? \
>> - VIR_NET_GENERATED_MACVTAP_PREFIX : VIR_NET_GENERATED_MACVLAN_PREFIX)
>> -
>> -
>> -virMutex virNetDevMacVLanCreateMutex = VIR_MUTEX_INITIALIZER;
>> -static int virNetDevMacVTapLastID = -1;
>> -static int virNetDevMacVLanLastID = -1;
>> -
>> -
>> -static void
>> -virNetDevMacVLanReserveNameInternal(const char *name)
>> -{
>> -    unsigned int id;
>> -    const char *idstr = NULL;
>> -    int *lastID = NULL;
>> -    int len;
>> -
>> -    if (STRPREFIX(name, VIR_NET_GENERATED_MACVTAP_PREFIX)) {
>> -    lastID = &virNetDevMacVTapLastID;
>> -    len = strlen(VIR_NET_GENERATED_MACVTAP_PREFIX);
>> -    } else if (STRPREFIX(name, VIR_NET_GENERATED_MACVLAN_PREFIX)) {
>> -    lastID = &virNetDevMacVTapLastID;
&

[PATCHv2 3/5] netdevmacvlan: Use helper function to create unique macvlan/macvtap name

2020-12-09 Thread Shi Lei
Simplify ReserveName/GenerateName for macvlan and macvtap by using
common functions.

Signed-off-by: Shi Lei 
---
 src/libvirt_private.syms|   1 -
 src/lxc/lxc_process.c   |   2 +-
 src/qemu/qemu_process.c |   2 +-
 src/util/virnetdevmacvlan.c | 177 +---
 src/util/virnetdevmacvlan.h |  14 +--
 5 files changed, 26 insertions(+), 170 deletions(-)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 64ef01e1..4d6ae84b 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2655,7 +2655,6 @@ virNetDevMacVLanDelete;
 virNetDevMacVLanDeleteWithVPortProfile;
 virNetDevMacVLanIsMacvtap;
 virNetDevMacVLanModeTypeFromString;
-virNetDevMacVLanReserveName;
 virNetDevMacVLanRestartWithVPortProfile;
 virNetDevMacVLanTapOpen;
 virNetDevMacVLanTapSetup;
diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c
index 0f818e2e..eb29431e 100644
--- a/src/lxc/lxc_process.c
+++ b/src/lxc/lxc_process.c
@@ -1641,7 +1641,7 @@ virLXCProcessReconnectNotifyNets(virDomainDefPtr def)
  * impolite.
  */
 if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_DIRECT)
-virNetDevMacVLanReserveName(net->ifname);
+virNetDevReserveName(net->ifname);
 
 if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
 if (!conn && !(conn = virGetConnectNetwork()))
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 1d54f201..6244ade4 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -3390,7 +3390,7 @@ qemuProcessNotifyNets(virDomainDefPtr def)
  */
 switch (virDomainNetGetActualType(net)) {
 case VIR_DOMAIN_NET_TYPE_DIRECT:
-virNetDevMacVLanReserveName(net->ifname);
+virNetDevReserveName(net->ifname);
 break;
 case VIR_DOMAIN_NET_TYPE_BRIDGE:
 case VIR_DOMAIN_NET_TYPE_NETWORK:
diff --git a/src/util/virnetdevmacvlan.c b/src/util/virnetdevmacvlan.c
index 72f0d670..36b13133 100644
--- a/src/util/virnetdevmacvlan.c
+++ b/src/util/virnetdevmacvlan.c
@@ -45,7 +45,6 @@ VIR_ENUM_IMPL(virNetDevMacVLanMode,
 
 # include 
 # include 
-# include 
 
 # include "viralloc.h"
 # include "virlog.h"
@@ -59,129 +58,6 @@ VIR_ENUM_IMPL(virNetDevMacVLanMode,
 
 VIR_LOG_INIT("util.netdevmacvlan");
 
-# define VIR_NET_GENERATED_MACVTAP_PATTERN VIR_NET_GENERATED_MACVTAP_PREFIX 
"%d"
-# define VIR_NET_GENERATED_MACVLAN_PATTERN VIR_NET_GENERATED_MACVLAN_PREFIX 
"%d"
-# define VIR_NET_GENERATED_PREFIX \
-((flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? \
- VIR_NET_GENERATED_MACVTAP_PREFIX : VIR_NET_GENERATED_MACVLAN_PREFIX)
-
-
-virMutex virNetDevMacVLanCreateMutex = VIR_MUTEX_INITIALIZER;
-static int virNetDevMacVTapLastID = -1;
-static int virNetDevMacVLanLastID = -1;
-
-
-static void
-virNetDevMacVLanReserveNameInternal(const char *name)
-{
-unsigned int id;
-const char *idstr = NULL;
-int *lastID = NULL;
-int len;
-
-if (STRPREFIX(name, VIR_NET_GENERATED_MACVTAP_PREFIX)) {
-lastID = &virNetDevMacVTapLastID;
-len = strlen(VIR_NET_GENERATED_MACVTAP_PREFIX);
-} else if (STRPREFIX(name, VIR_NET_GENERATED_MACVLAN_PREFIX)) {
-lastID = &virNetDevMacVTapLastID;
-len = strlen(VIR_NET_GENERATED_MACVLAN_PREFIX);
-} else {
-return;
-}
-
-VIR_INFO("marking device in use: '%s'", name);
-
-idstr = name + len;
-
-if (virStrToLong_ui(idstr, NULL, 10, &id) >= 0) {
-if (*lastID < (int)id)
-*lastID = id;
-}
-}
-
-
-/**
- * virNetDevMacVLanReserveName:
- * @name: name of an existing macvtap/macvlan device
- *
- * Set the value of virNetDevMacV(Lan|Tap)LastID to assure that any
- * new device created with an autogenerated name will use a number
- * higher than the number in the given device name.
- *
- * Returns nothing.
- */
-void
-virNetDevMacVLanReserveName(const char *name)
-{
-virMutexLock(&virNetDevMacVLanCreateMutex);
-virNetDevMacVLanReserveNameInternal(name);
-virMutexUnlock(&virNetDevMacVLanCreateMutex);
-}
-
-
-/**
- * virNetDevMacVLanGenerateName:
- * @ifname: pointer to pointer to string containing template
- * @lastID: counter to add to the template to form the name
- *
- * generate a new (currently unused) name for a new macvtap/macvlan
- * device based on the template string in @ifname - replace %d with
- * ++(*counter), and keep trying new values until one is found
- * that doesn't already exist, or we've tried 1 different
- * names. Once a usable name is found, replace the template with the
- * actual name.
- *
- * Returns 0 on success, -1 on failure.
- */
-static int
-virNetDevMacVLanGenerateName(char **ifname, unsigned int flags)
-{
-const char *prefix;
-const char *iftemplate;
-int *lastID;
-int id;
-double maxIDd;
-int maxID = INT_MA

[PATCHv2 5/5] netdev: Prevent functions that call virNetDevTapCreate from adding 'vnet%d' into ifname

2020-12-09 Thread Shi Lei
Those functions that call virNetDevTapCreate don't need to adding
'vnet%d' into ifname when it is empty, since virNetDevGenerateName
which is in virNetDevTapCreate can deal with it.

Signed-off-by: Shi Lei 
---
 src/bhyve/bhyve_command.c |  1 -
 src/qemu/qemu_interface.c | 12 
 2 files changed, 13 deletions(-)

diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c
index 4cf98c0e..92b31a6e 100644
--- a/src/bhyve/bhyve_command.c
+++ b/src/bhyve/bhyve_command.c
@@ -83,7 +83,6 @@ bhyveBuildNetArgStr(const virDomainDef *def,
 STRPREFIX(net->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
 strchr(net->ifname, '%')) {
 VIR_FREE(net->ifname);
-net->ifname = g_strdup(VIR_NET_GENERATED_VNET_PREFIX "%d");
 }
 
 if (!dryRun) {
diff --git a/src/qemu/qemu_interface.c b/src/qemu/qemu_interface.c
index 197c0aa2..87cfb8fc 100644
--- a/src/qemu/qemu_interface.c
+++ b/src/qemu/qemu_interface.c
@@ -413,7 +413,6 @@ qemuInterfaceEthernetConnect(virDomainDefPtr def,
 virMacAddr tapmac;
 int ret = -1;
 unsigned int tap_create_flags = VIR_NETDEV_TAP_CREATE_IFUP;
-bool template_ifname = false;
 g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
 const char *tunpath = "/dev/net/tun";
 const char *auditdev = tunpath;
@@ -459,9 +458,6 @@ qemuInterfaceEthernetConnect(virDomainDefPtr def,
 STRPREFIX(net->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
 strchr(net->ifname, '%')) {
 VIR_FREE(net->ifname);
-net->ifname = g_strdup(VIR_NET_GENERATED_VNET_PREFIX "%d");
-/* avoid exposing vnet%d in getXMLDesc or error outputs */
-template_ifname = true;
 }
 if (virNetDevTapCreate(&net->ifname, tunpath, tapfd, tapfdSize,
tap_create_flags) < 0) {
@@ -512,8 +508,6 @@ qemuInterfaceEthernetConnect(virDomainDefPtr def,
 virDomainAuditNetDevice(def, net, auditdev, false);
 for (i = 0; i < tapfdSize && tapfd[i] >= 0; i++)
 VIR_FORCE_CLOSE(tapfd[i]);
-if (template_ifname)
-VIR_FREE(net->ifname);
 }
 
 return ret;
@@ -541,7 +535,6 @@ qemuInterfaceBridgeConnect(virDomainDefPtr def,
 const char *brname;
 int ret = -1;
 unsigned int tap_create_flags = VIR_NETDEV_TAP_CREATE_IFUP;
-bool template_ifname = false;
 g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
 const char *tunpath = "/dev/net/tun";
 
@@ -563,9 +556,6 @@ qemuInterfaceBridgeConnect(virDomainDefPtr def,
 STRPREFIX(net->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
 strchr(net->ifname, '%')) {
 VIR_FREE(net->ifname);
-net->ifname = g_strdup(VIR_NET_GENERATED_VNET_PREFIX "%d");
-/* avoid exposing vnet%d in getXMLDesc or error outputs */
-template_ifname = true;
 }
 
 if (qemuInterfaceIsVnetCompatModel(net))
@@ -630,8 +620,6 @@ qemuInterfaceBridgeConnect(virDomainDefPtr def,
 size_t i;
 for (i = 0; i < *tapfdSize && tapfd[i] >= 0; i++)
 VIR_FORCE_CLOSE(tapfd[i]);
-if (template_ifname)
-VIR_FREE(net->ifname);
 }
 
 return ret;
-- 
2.25.1




[PATCHv2 1/5] netdev: Introduce several helper functions for generating unique netdev name

2020-12-09 Thread Shi Lei
Extract ReserveName/GenerateName from netdevtap and netdevmacvlan as
common helper functions.

Signed-off-by: Shi Lei 
---
 src/bhyve/bhyve_command.c  |   4 +-
 src/conf/domain_conf.c |   4 +-
 src/interface/interface_backend_udev.c |   2 +-
 src/libvirt_private.syms   |   2 +
 src/qemu/qemu_interface.c  |   8 +-
 src/util/virnetdev.c   | 116 +
 src/util/virnetdev.h   |  27 +-
 src/util/virnetdevtap.c|  10 +--
 8 files changed, 158 insertions(+), 15 deletions(-)

diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c
index acf3a5a4..4cf98c0e 100644
--- a/src/bhyve/bhyve_command.c
+++ b/src/bhyve/bhyve_command.c
@@ -80,10 +80,10 @@ bhyveBuildNetArgStr(const virDomainDef *def,
 }
 
 if (!net->ifname ||
-STRPREFIX(net->ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
+STRPREFIX(net->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
 strchr(net->ifname, '%')) {
 VIR_FREE(net->ifname);
-net->ifname = g_strdup(VIR_NET_GENERATED_TAP_PREFIX "%d");
+net->ifname = g_strdup(VIR_NET_GENERATED_VNET_PREFIX "%d");
 }
 
 if (!dryRun) {
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 23415b32..403ecab8 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -12037,7 +12037,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
 
 if (def->managed_tap != VIR_TRISTATE_BOOL_NO && ifname &&
 (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) &&
-(STRPREFIX(ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
+(STRPREFIX(ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
  STRPREFIX(ifname, VIR_NET_GENERATED_MACVTAP_PREFIX) ||
  STRPREFIX(ifname, VIR_NET_GENERATED_MACVLAN_PREFIX) ||
  (prefix && STRPREFIX(ifname, prefix {
@@ -26460,7 +26460,7 @@ virDomainNetDefFormat(virBufferPtr buf,
 if (def->ifname &&
 (def->managed_tap == VIR_TRISTATE_BOOL_NO ||
  !((flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE) &&
-   (STRPREFIX(def->ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
+   (STRPREFIX(def->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
 STRPREFIX(def->ifname, VIR_NET_GENERATED_MACVTAP_PREFIX) ||
 STRPREFIX(def->ifname, VIR_NET_GENERATED_MACVLAN_PREFIX) ||
 (prefix && STRPREFIX(def->ifname, prefix)) {
diff --git a/src/interface/interface_backend_udev.c 
b/src/interface/interface_backend_udev.c
index 173c4fc3..6a94a450 100644
--- a/src/interface/interface_backend_udev.c
+++ b/src/interface/interface_backend_udev.c
@@ -544,7 +544,7 @@ udevBridgeScanDirFilter(const struct dirent *entry)
  * vnet%d. Improvements to this check are welcome.
  */
 if (strlen(entry->d_name) >= 5) {
-if (STRPREFIX(entry->d_name, VIR_NET_GENERATED_TAP_PREFIX) &&
+if (STRPREFIX(entry->d_name, VIR_NET_GENERATED_VNET_PREFIX) &&
 g_ascii_isdigit(entry->d_name[4]))
 return 0;
 }
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 992488f7..c0f50856 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2551,6 +2551,7 @@ virNetDevDelMulti;
 virNetDevExists;
 virNetDevFeatureTypeFromString;
 virNetDevFeatureTypeToString;
+virNetDevGenerateName;
 virNetDevGetFeatures;
 virNetDevGetIndex;
 virNetDevGetLinkInfo;
@@ -2574,6 +2575,7 @@ virNetDevIfStateTypeToString;
 virNetDevIsVirtualFunction;
 virNetDevPFGetVF;
 virNetDevReadNetConfig;
+virNetDevReserveName;
 virNetDevRunEthernetScript;
 virNetDevRxFilterFree;
 virNetDevRxFilterModeTypeFromString;
diff --git a/src/qemu/qemu_interface.c b/src/qemu/qemu_interface.c
index 32b397d2..197c0aa2 100644
--- a/src/qemu/qemu_interface.c
+++ b/src/qemu/qemu_interface.c
@@ -456,10 +456,10 @@ qemuInterfaceEthernetConnect(virDomainDefPtr def,
 }
 } else {
 if (!net->ifname ||
-STRPREFIX(net->ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
+STRPREFIX(net->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
 strchr(net->ifname, '%')) {
 VIR_FREE(net->ifname);
-net->ifname = g_strdup(VIR_NET_GENERATED_TAP_PREFIX "%d");
+net->ifname = g_strdup(VIR_NET_GENERATED_VNET_PREFIX "%d");
 /* avoid exposing vnet%d in getXMLDesc or error outputs */
 template_ifname = true;
 }
@@ -560,10 +560,10 @@ qemuInterfaceBridgeConnect(virDomainDefPtr def,
 }
 
 if (!net->ifname ||
-STRPREFIX(net->ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
+STRPREFIX(net->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
 strchr(net->ifname, '%')) {
 VIR_FREE(net->ifname);
-net->ifname = g_strdup

[PATCHv2 2/5] netdevtap: Use common helper function to create unique tap name

2020-12-09 Thread Shi Lei
Simplify GenerateName/ReserveName for netdevtap by using common
functions.

Signed-off-by: Shi Lei 
---
 src/libvirt_private.syms |   1 -
 src/qemu/qemu_process.c  |   2 +-
 src/util/virnetdevtap.c  | 100 ++-
 src/util/virnetdevtap.h  |   4 --
 4 files changed, 5 insertions(+), 102 deletions(-)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index c0f50856..64ef01e1 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2689,7 +2689,6 @@ virNetDevTapGetName;
 virNetDevTapGetRealDeviceName;
 virNetDevTapInterfaceStats;
 virNetDevTapReattachBridge;
-virNetDevTapReserveName;
 
 
 # util/virnetdevveth.h
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 3b64caa6..1d54f201 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -3395,7 +3395,7 @@ qemuProcessNotifyNets(virDomainDefPtr def)
 case VIR_DOMAIN_NET_TYPE_BRIDGE:
 case VIR_DOMAIN_NET_TYPE_NETWORK:
 case VIR_DOMAIN_NET_TYPE_ETHERNET:
-virNetDevTapReserveName(net->ifname);
+virNetDevReserveName(net->ifname);
 break;
 case VIR_DOMAIN_NET_TYPE_USER:
 case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
diff --git a/src/util/virnetdevtap.c b/src/util/virnetdevtap.c
index 9354cc10..88ad3216 100644
--- a/src/util/virnetdevtap.c
+++ b/src/util/virnetdevtap.c
@@ -49,51 +49,11 @@
 #if defined(WITH_GETIFADDRS) && defined(AF_LINK)
 # include 
 #endif
-#include 
 
 #define VIR_FROM_THIS VIR_FROM_NONE
 
 VIR_LOG_INIT("util.netdevtap");
 
-virMutex virNetDevTapCreateMutex = VIR_MUTEX_INITIALIZER;
-static int virNetDevTapLastID = -1; /* not "unsigned" because callers use %d */
-
-
-/**
- * virNetDevTapReserveName:
- * @name: name of an existing tap device
- *
- * Set the value of virNetDevTapLastID to assure that any new tap
- * device created with an autogenerated name will use a number higher
- * than the number in the given tap device name.
- *
- * Returns nothing.
- */
-void
-virNetDevTapReserveName(const char *name)
-{
-unsigned int id;
-const char *idstr = NULL;
-
-
-if (STRPREFIX(name, VIR_NET_GENERATED_VNET_PREFIX)) {
-
-VIR_INFO("marking device in use: '%s'", name);
-
-idstr = name + strlen(VIR_NET_GENERATED_VNET_PREFIX);
-
-if (virStrToLong_ui(idstr, NULL, 10, &id) >= 0) {
-virMutexLock(&virNetDevTapCreateMutex);
-
-if (virNetDevTapLastID < (int)id)
-virNetDevTapLastID = id;
-
-virMutexUnlock(&virNetDevTapCreateMutex);
-}
-}
-}
-
-
 /**
  * virNetDevTapGetName:
  * @tapfd: a tun/tap file descriptor
@@ -183,55 +143,6 @@ virNetDevTapGetRealDeviceName(char *ifname G_GNUC_UNUSED)
 
 
 #ifdef TUNSETIFF
-/**
- * virNetDevTapGenerateName:
- * @ifname: pointer to pointer to string containing template
- *
- * generate a new (currently unused) name for a new tap device based
- * on the templace string in @ifname - replace %d with
- * ++virNetDevTapLastID, and keep trying new values until one is found
- * that doesn't already exist, or we've tried 1 different
- * names. Once a usable name is found, replace the template with the
- * actual name.
- *
- * Returns 0 on success, -1 on failure.
- */
-static int
-virNetDevTapGenerateName(char **ifname)
-{
-int id;
-double maxIDd = pow(10, IFNAMSIZ - 1 - 
strlen(VIR_NET_GENERATED_VNET_PREFIX));
-int maxID = INT_MAX;
-int attempts = 0;
-
-if (maxIDd <= (double)INT_MAX)
-maxID = (int)maxIDd;
-
-do {
-g_autofree char *try = NULL;
-
-id = ++virNetDevTapLastID;
-
-/* reset before overflow */
-if (virNetDevTapLastID >= maxID)
-virNetDevTapLastID = -1;
-
-try = g_strdup_printf(*ifname, id);
-
-if (!virNetDevExists(try)) {
-g_free(*ifname);
-*ifname = g_steal_pointer(&try);
-return 0;
-}
-} while (++attempts < 1);
-
-virReportError(VIR_ERR_INTERNAL_ERROR,
-   _("no unused %s names available"),
-   VIR_NET_GENERATED_VNET_PREFIX);
-return -1;
-}
-
-
 /**
  * virNetDevTapCreate:
  * @ifname: the interface name
@@ -263,16 +174,14 @@ int virNetDevTapCreate(char **ifname,
 int ret = -1;
 int fd = -1;
 
-virMutexLock(&virNetDevTapCreateMutex);
-
 /* if ifname is "vnet%d", then auto-generate a name for the new
  * device (the kernel could do this for us, but has a bad habit of
  * immediately re-using names that have just been released, which
  * can lead to race conditions).
- */
-if (STREQ(*ifname, VIR_NET_GENERATED_VNET_PREFIX "%d") &&
-virNetDevTapGenerateName(ifname) < 0) {
-goto cleanup;
+ * if ifname is just a user-provided name, virNetDevGenerateName
+ * leaves it unchanged. */
+   

[PATCHv2 0/5] netdev: Extract GenerateName/ReserveName as common functions

2020-12-09 Thread Shi Lei
V1 here: https://www.redhat.com/archives/libvir-list/2020-December/msg00308.html

Since V1:
(1) Remove virNetDev[Lock|Unlock]GenName.
Only *lastID* needs to be protected. Now we lock *lastID*
inside virNetDevReserveName and virNetDevGenerateName, then lock and
unlock functions are no longer needed.

(2) Shorten the locking range for generating names for tap and macvlan.
Since virNetDevReserveName and virNetDevGenerateName are now with lock,
we can call them directly rather than adding lock outside.

(3) Rename *_GEN_NAME_TAP to *_GEN_NAME_VNET.

(4) Now veth and tap share the same prefix "vnet".

(5) Use name rather than type as the argument of virNetDevReserveName.
Just follow the style of virNetDev[Tap|MacVlan]ReserveName.

(6) Remove those in-between functions for tap and macvlan.

(7) Remove useless ENUM_[DECL|IMPL] for enum virNetDevGenNameType.

(8) Remove the *_NONE item for enum virNetDevGenNameType.

(9) Remove useless  in virnetdevtap.

(10) When @ifname of virNetDevGenerateName is NOT a template or NULL,
just leave it unchanged.

(11) Take advantage of the "g_strdup_printf(*ifname, id)" in
virNetDevGenerateName, prevent the functions that call 
virNetDevTapCreate()
from adding in "vnet%d" when ifname is empty.

(12) Use VIR_NETDEV_MACVLAN_CREATE_WITH_TAP to distinguish macvtap and
macvlan and remove those useless macros.

Shi Lei (5):
  netdev: Introduce several helper functions for generating unique netdev name
  netdevtap: Use common helper function to create unique tap name
  netdevmacvlan: Use helper function to create unique macvlan/macvtap name
  netdevveth: Simplify virNetDevVethCreate by using virNetDevGenerateName
  netdev: Prevent functions that call virNetDevTapCreate from adding 'vnet%d' 
into ifname

 src/bhyve/bhyve_command.c  |   3 +-
 src/conf/domain_conf.c |   4 +-
 src/interface/interface_backend_udev.c |   2 +-
 src/libvirt_private.syms   |   4 +-
 src/lxc/lxc_process.c  |   5 +-
 src/qemu/qemu_interface.c  |  16 +--
 src/qemu/qemu_process.c|   4 +-
 src/util/virnetdev.c   | 116 
 src/util/virnetdev.h   |  27 +++-
 src/util/virnetdevmacvlan.c| 177 +++--
 src/util/virnetdevmacvlan.h|  14 +-
 src/util/virnetdevtap.c| 100 +-
 src/util/virnetdevtap.h|   4 -
 src/util/virnetdevveth.c   | 140 +--
 14 files changed, 217 insertions(+), 399 deletions(-)

-- 
2.25.1




[PATCHv2 4/5] netdevveth: Simplify virNetDevVethCreate by using virNetDevGenerateName

2020-12-09 Thread Shi Lei
Simplify virNetDevVethCreate by using common GenerateName/ReserveName
functions.

Signed-off-by: Shi Lei 
---
 src/lxc/lxc_process.c|   3 +
 src/util/virnetdevveth.c | 140 +--
 2 files changed, 36 insertions(+), 107 deletions(-)

diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c
index eb29431e..2e8ae706 100644
--- a/src/lxc/lxc_process.c
+++ b/src/lxc/lxc_process.c
@@ -307,6 +307,9 @@ virLXCProcessSetupInterfaceTap(virDomainDefPtr vm,
 
 VIR_DEBUG("calling vethCreate()");
 parentVeth = net->ifname;
+if (parentVeth)
+virNetDevReserveName(parentVeth);
+
 if (virNetDevVethCreate(&parentVeth, &containerVeth) < 0)
 return NULL;
 VIR_DEBUG("parentVeth: %s, containerVeth: %s", parentVeth, containerVeth);
diff --git a/src/util/virnetdevveth.c b/src/util/virnetdevveth.c
index b3eee1af..d6932a2e 100644
--- a/src/util/virnetdevveth.c
+++ b/src/util/virnetdevveth.c
@@ -32,48 +32,6 @@
 
 VIR_LOG_INIT("util.netdevveth");
 
-/* Functions */
-
-virMutex virNetDevVethCreateMutex = VIR_MUTEX_INITIALIZER;
-
-static int virNetDevVethExists(int devNum)
-{
-int ret;
-g_autofree char *path = NULL;
-
-path = g_strdup_printf(SYSFS_NET_DIR "vnet%d/", devNum);
-ret = virFileExists(path) ? 1 : 0;
-VIR_DEBUG("Checked dev vnet%d usage: %d", devNum, ret);
-return ret;
-}
-
-/**
- * virNetDevVethGetFreeNum:
- * @startDev: device number to start at (x in vethx)
- *
- * Looks in /sys/class/net/ to find the first available veth device
- * name.
- *
- * Returns non-negative device number on success or -1 in case of error
- */
-static int virNetDevVethGetFreeNum(int startDev)
-{
-int devNum;
-
-#define MAX_DEV_NUM 65536
-
-for (devNum = startDev; devNum < MAX_DEV_NUM; devNum++) {
-int ret = virNetDevVethExists(devNum);
-if (ret < 0)
-return -1;
-if (ret == 0)
-return devNum;
-}
-
-virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-   _("No free veth devices available"));
-return -1;
-}
 
 /**
  * virNetDevVethCreate:
@@ -102,77 +60,45 @@ static int virNetDevVethGetFreeNum(int startDev)
  */
 int virNetDevVethCreate(char** veth1, char** veth2)
 {
-int ret = -1;
-int vethNum = 0;
-size_t i;
-
-/*
- * We might race with other containers, but this is reasonably
- * unlikely, so don't do too many retries for device creation
- */
-virMutexLock(&virNetDevVethCreateMutex);
-#define MAX_VETH_RETRIES 10
-
-for (i = 0; i < MAX_VETH_RETRIES; i++) {
-g_autofree char *veth1auto = NULL;
-g_autofree char *veth2auto = NULL;
-g_autoptr(virCommand) cmd = NULL;
-
-int status;
-if (!*veth1) {
-int veth1num;
-if ((veth1num = virNetDevVethGetFreeNum(vethNum)) < 0)
-goto cleanup;
-
-veth1auto = g_strdup_printf("vnet%d", veth1num);
-vethNum = veth1num + 1;
-}
-if (!*veth2) {
-int veth2num;
-if ((veth2num = virNetDevVethGetFreeNum(vethNum)) < 0)
-goto cleanup;
+int status;
+g_autofree char *veth1auto = NULL;
+g_autofree char *veth2auto = NULL;
+g_autoptr(virCommand) cmd = NULL;
 
-veth2auto = g_strdup_printf("vnet%d", veth2num);
-vethNum = veth2num + 1;
-}
+if (!*veth1) {
+if (virNetDevGenerateName(&veth1auto, VIR_NET_DEV_GEN_NAME_VNET) < 0)
+return -1;
+}
+if (!*veth2) {
+if (virNetDevGenerateName(&veth2auto, VIR_NET_DEV_GEN_NAME_VNET) < 0)
+return -1;
+}
 
-cmd = virCommandNew("ip");
-virCommandAddArgList(cmd, "link", "add",
- *veth1 ? *veth1 : veth1auto,
- "type", "veth", "peer", "name",
- *veth2 ? *veth2 : veth2auto,
- NULL);
-
-if (virCommandRun(cmd, &status) < 0)
-goto cleanup;
-
-if (status == 0) {
-if (veth1auto) {
-*veth1 = veth1auto;
-veth1auto = NULL;
-}
-if (veth2auto) {
-*veth2 = veth2auto;
-veth2auto = NULL;
-}
-VIR_DEBUG("Create Host: %s guest: %s", *veth1, *veth2);
-ret = 0;
-goto cleanup;
-}
+cmd = virCommandNew("ip");
+virCommandAddArgList(cmd, "link", "add",
+ *veth1 ? *veth1 : veth1auto,
+ "type", "veth", "peer", "name",
+ *veth2 ? *veth2 : veth2auto,
+ N

Re: Re: [PATCHv2 2/2] util:veth: Create veth device pair by netlink

2020-12-07 Thread Shi Lei
Okay. According to your comment, I will deal with another series first.
Then I get rid of that 'status' in this patch and post V3 of it.
Thanks!

Shi Lei

On 2020-12-08 at 06:54, Laine Stump wrote:
>On 11/22/20 10:28 PM, Shi Lei wrote:
>> When netlink is supported, use netlink to create veth device pair
>> rather than 'ip link' command.
>>
>> Signed-off-by: Shi Lei 
>> ---
>>   src/util/virnetdevveth.c | 85 ++--
>>   1 file changed, 56 insertions(+), 29 deletions(-)
>>
>> diff --git a/src/util/virnetdevveth.c b/src/util/virnetdevveth.c
>> index b3eee1af..b4074371 100644
>> --- a/src/util/virnetdevveth.c
>> +++ b/src/util/virnetdevveth.c
>> @@ -27,6 +27,7 @@
>>   #include "virfile.h"
>>   #include "virstring.h"
>>   #include "virnetdev.h"
>> +#include "virnetlink.h"
>>  
>>   #define VIR_FROM_THIS VIR_FROM_NONE
>>  
>> @@ -75,6 +76,55 @@ static int virNetDevVethGetFreeNum(int startDev)
>>   return -1;
>>   }
>>  
>> +#if defined(WITH_LIBNL)
>> +static int
>> +virNetDevVethCreateInternal(const char *veth1, const char *veth2, int 
>> *status)
>> +{
>> +    virNetlinkNewLinkData data = { .veth_peer = veth2 };
>> +
>> +    return virNetlinkNewLink(veth1, "veth", &data, status);
>> +}
>
>The only thing that makes me uncomfortable in this patch is that the two
>versions of virNetDevVethCreateInternal() each return something
>different for "status". In this first case, it is returning 0 on
>success, and -errno on failure...
>
>
>> [...]
>
>> +#else
>> +static int
>> +virNetDevVethCreateInternal(const char *veth1, const char *veth2, int 
>> *status)
>> +{
>> +    g_autoptr(virCommand) cmd = virCommandNew("ip");
>> +    virCommandAddArgList(cmd, "link", "add", veth1, "type", "veth",
>> + "peer", "name", veth2, NULL);
>> +
>> +    return virCommandRun(cmd, status);
>> +}
>
>
>But in this case it is returning the exit code of the "ip link add" command.
>
>
>It turns out that the value of status is only checked for 0 / not-0, so
>in practice it doesn't matter, but it could lead to confusion in the future.
>
>
>If we want these patches to be applied before your "netdevveth: Simplify
>virNetDevVethCreate by using virNetDevGenerateName" patch (which I'll
>get to as soon as I send this mail), then we do still need a way to
>differentiate between "The requested device already exists" and
>"Permanent Failure", and in that case I would suggest that "status" be
>replaced with a variable called "retry" which would be set to true if
>retrying with a different device name might lead to success (it would be
>set based on an interpretation of status made by each of the
>vir*Internal() functions)
>
>
>However, if we decide to apply the *other* patchset first, then we will
>never retry anyway (because we've already checked if the device exists
>before we try to create it, and there is therefore no loop) and so we
>could just eliminate the final argument completely, and keep each
>vir*Internal() function's status internal to itself. (As a matter of
>fact, status in the virCommandRun() version could simply be replaced
>with NULL in the arglist to virCommandRun(), and not defined at all;
>status in the case of virNetlinkNewLink() would still need to be defined
>and passed into the function, but its value would just be ignored).
>
>
>I think it may be cleaner to do the latter, and it looks like your other
>series was written to be applied without this series anyway; let me look
>at those patches and I'll reply a 2nd time to this based on the results
>of reviewing the other series...
>
>
>BTW, I appreciate your patience - I had looked at these patches nearly
>two weeks ago (soon after you sent them), but then a holiday got in the
>way, and I forgot to post my reply after I returned to work :-/
>
>
>> [...]
>> -
>> -    if (virCommandRun(cmd, &status) < 0)
>> +    if (virNetDevVethCreateInternal(*veth1 ? *veth1 : veth1auto,
>> +    *veth2 ? *veth2 : veth2auto,
>> +    &status) < 0)
>>   goto cleanup;
>>  
>>   if (status == 0) {
>
>
>This spot here ^ is the only place that status is examined, and I
>actually think it's not going to work as you'd like in the case of
>netlink - status will always be 0 if virNetDevVethCreateInternal()
>returns 0, and when the return is < 0, status may or may not be set to
>-errno of the attempted operation - if status is 0, that means there was
>a serious error before even trying to create the device (e.g. OOM), and
>if it is non-0, then it might be because a device with the desired name
>already exists, or it might be because we don't have enough privilege to
>create a new device.
>
>
>Anyway, let me look at the other patchset...
>



Re: Re: [PATCH 5/5] netdevveth: Simplify virNetDevVethCreate by using virNetDevGenerateName

2020-12-07 Thread Shi Lei
On 2020-12-08 at 09:42, Laine Stump wrote:
>On 12/4/20 2:01 AM, Shi Lei wrote:
>> Simplify virNetDevVethCreate by using common GenerateName/ReserveName
>> functions.
>>
>> Signed-off-by: Shi Lei 
>> ---
>>   src/lxc/lxc_process.c    |   3 +
>>   src/util/virnetdevveth.c | 146 +++
>>   2 files changed, 43 insertions(+), 106 deletions(-)
>>
>> diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c
>> index 7e07f49f..5f277a22 100644
>> --- a/src/lxc/lxc_process.c
>> +++ b/src/lxc/lxc_process.c
>> @@ -287,6 +287,9 @@ virLXCProcessSetupInterfaceTap(virDomainDefPtr vm,
>>  
>>   VIR_DEBUG("calling vethCreate()");
>>   parentVeth = net->ifname;
>> +    if (parentVeth)
>> +    virNetDevReserveName(parentVeth, VIR_NET_DEV_GEN_NAME_VETH, true);
>> +
>>   if (virNetDevVethCreate(&parentVeth, &containerVeth) < 0)
>>   return NULL;
>>   VIR_DEBUG("parentVeth: %s, containerVeth: %s", parentVeth, 
>>containerVeth);
>> diff --git a/src/util/virnetdevveth.c b/src/util/virnetdevveth.c
>> index b3eee1af..2e3d0c82 100644
>> --- a/src/util/virnetdevveth.c
>> +++ b/src/util/virnetdevveth.c
>> @@ -32,48 +32,6 @@
>>  
>>   VIR_LOG_INIT("util.netdevveth");
>>  
>> -/* Functions */
>> -
>> -virMutex virNetDevVethCreateMutex = VIR_MUTEX_INITIALIZER;
>> -
>> -static int virNetDevVethExists(int devNum)
>> -{
>> -    int ret;
>> -    g_autofree char *path = NULL;
>> -
>> -    path = g_strdup_printf(SYSFS_NET_DIR "vnet%d/", devNum);
>> -    ret = virFileExists(path) ? 1 : 0;
>> -    VIR_DEBUG("Checked dev vnet%d usage: %d", devNum, ret);
>> -    return ret;
>> -}
>> -
>> -/**
>> - * virNetDevVethGetFreeNum:
>> - * @startDev: device number to start at (x in vethx)
>> - *
>> - * Looks in /sys/class/net/ to find the first available veth device
>> - * name.
>> - *
>> - * Returns non-negative device number on success or -1 in case of error
>> - */
>> -static int virNetDevVethGetFreeNum(int startDev)
>> -{
>> -    int devNum;
>> -
>> -#define MAX_DEV_NUM 65536
>> -
>> -    for (devNum = startDev; devNum < MAX_DEV_NUM; devNum++) {
>> -    int ret = virNetDevVethExists(devNum);
>> -    if (ret < 0)
>> -    return -1;
>> -    if (ret == 0)
>> -    return devNum;
>> -    }
>> -
>> -    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>> -   _("No free veth devices available"));
>> -    return -1;
>> -}
>>  
>>   /**
>>    * virNetDevVethCreate:
>> @@ -102,77 +60,53 @@ static int virNetDevVethGetFreeNum(int startDev)
>>    */
>>   int virNetDevVethCreate(char** veth1, char** veth2)
>>   {
>> -    int ret = -1;
>> -    int vethNum = 0;
>> -    size_t i;
>> -
>> -    /*
>> - * We might race with other containers, but this is reasonably
>> - * unlikely, so don't do too many retries for device creation
>> - */
>> -    virMutexLock(&virNetDevVethCreateMutex);
>> -#define MAX_VETH_RETRIES 10
>> -
>> -    for (i = 0; i < MAX_VETH_RETRIES; i++) {
>> -    g_autofree char *veth1auto = NULL;
>> -    g_autofree char *veth2auto = NULL;
>> -    g_autoptr(virCommand) cmd = NULL;
>> -
>> -    int status;
>> -    if (!*veth1) {
>> -    int veth1num;
>> -    if ((veth1num = virNetDevVethGetFreeNum(vethNum)) < 0)
>> -    goto cleanup;
>> -
>> -    veth1auto = g_strdup_printf("vnet%d", veth1num);
>> -    vethNum = veth1num + 1;
>> -    }
>> -    if (!*veth2) {
>> -    int veth2num;
>> -    if ((veth2num = virNetDevVethGetFreeNum(vethNum)) < 0)
>> -    goto cleanup;
>> +    int status;
>> +    g_autofree char *veth1auto = NULL;
>> +    g_autofree char *veth2auto = NULL;
>> +    g_autoptr(virCommand) cmd = NULL;
>>  
>> -    veth2auto = g_strdup_printf("vnet%d", veth2num);
>> -    vethNum = veth2num + 1;
>> -    }
>> +    virNetDevLockGenName(VIR_NET_DEV_GEN_NAME_VETH);
>>  
>> -    cmd = virCommandNew("ip");
>> -    virCommandAddArgList(cmd, "link", "add",
>> - *veth1 ? *veth1 : veth1auto,
>> -   

Re: Re: [PATCH 3/5] netdevmacvlan: Use helper function to create unique macvlan/macvtap name

2020-12-07 Thread Shi Lei
On 2020-12-08 at 09:23, Laine Stump wrote:
>On 12/4/20 2:01 AM, Shi Lei wrote:
>> Simplify ReserveName/GenerateName for macvlan and macvtap by using
>> common functions.
>>
>> Signed-off-by: Shi Lei 
>> ---
>>   src/util/virnetdevmacvlan.c | 107 
>>   src/util/virnetdevmacvlan.h |   6 --
>>   2 files changed, 22 insertions(+), 91 deletions(-)
>>
>> diff --git a/src/util/virnetdevmacvlan.c b/src/util/virnetdevmacvlan.c
>> index 72f0d670..7f58d7ca 100644
>> --- a/src/util/virnetdevmacvlan.c
>> +++ b/src/util/virnetdevmacvlan.c
>> @@ -45,7 +45,6 @@ VIR_ENUM_IMPL(virNetDevMacVLanMode,
>>  
>>   # include 
>>   # include 
>> -# include 
>>  
>>   # include "viralloc.h"
>>   # include "virlog.h"
>> @@ -64,39 +63,20 @@ VIR_LOG_INIT("util.netdevmacvlan");
>>   # define VIR_NET_GENERATED_PREFIX \
>>   ((flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? \
>>    VIR_NET_GENERATED_MACVTAP_PREFIX : VIR_NET_GENERATED_MACVLAN_PREFIX)
>
>^^ can't this (and the *_PATTERN #defines above it) be removed? (I
>haven't applied the patches and checked yet, but hopefully anything that
>needs those can be encapsulated in the new functions). If it's still
>lingering around, don't worry about it too much - it can be cleaned up
>after the fact, but if it can be trivially removed, then now would be a
>good time to do it.
>
>(looking into the non-patched file, it looks like
>virNetDevMacVLanCreateWithVPortProfile() is called with a flag set
>(VIR_NETDEV_MACVLAN_CREATE_WITH_TAP), and we then set char *type =
>VIR_NET_GENERATED_PREFIX, and that is just sent down unchanged to
>virNetDevMacVLanCreate(). We could instead modified
>virNetDevMacVLanCreate to accept flags and set the string inside that
>function according to the flags. Especially since the string in
>virNetDevMacVLanCreate() isn't used as a "PREFIX" for the device name -
>it is sent to netlink as the completely-unrelated-to-device-name device
>*type*, which just coincidentally is the same as our chosen name prefix
>- I think we could easily eliminate the *_PREFIX macros in this file. 

Okay. I'll cleanup those stuff.

>
>
>> +# define VIR_NET_CREATE_TYPE \
>> +    ((flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? \
>> + VIR_NET_DEV_GEN_NAME_MACVTAP : VIR_NET_DEV_GEN_NAME_MACVLAN)
>>  
>>  
>> -virMutex virNetDevMacVLanCreateMutex = VIR_MUTEX_INITIALIZER;
>> -static int virNetDevMacVTapLastID = -1;
>> -static int virNetDevMacVLanLastID = -1;
>> -
>> -
>> -static void
>> -virNetDevMacVLanReserveNameInternal(const char *name)
>> +static virNetDevGenNameType
>> +virNetDevMacVLanGetTypeByName(const char *name)
>
>
>I *think* once we do what I suggested above, this new function will no
>longer be needed. 

Okay.

>
>
>
>>   {
>> -    unsigned int id;
>> -    const char *idstr = NULL;
>> -    int *lastID = NULL;
>> -    int len;
>> -
>> -    if (STRPREFIX(name, VIR_NET_GENERATED_MACVTAP_PREFIX)) {
>> -    lastID = &virNetDevMacVTapLastID;
>> -    len = strlen(VIR_NET_GENERATED_MACVTAP_PREFIX);
>> -    } else if (STRPREFIX(name, VIR_NET_GENERATED_MACVLAN_PREFIX)) {
>> -    lastID = &virNetDevMacVTapLastID;
>> -    len = strlen(VIR_NET_GENERATED_MACVLAN_PREFIX);
>> -    } else {
>> -    return;
>> -    }
>> -
>> -    VIR_INFO("marking device in use: '%s'", name);
>> -
>> -    idstr = name + len;
>> -
>> -    if (virStrToLong_ui(idstr, NULL, 10, &id) >= 0) {
>> -    if (*lastID < (int)id)
>> -    *lastID = id;
>> -    }
>> +    if (STRPREFIX(name, VIR_NET_GENERATED_MACVTAP_PREFIX))
>> +    return VIR_NET_DEV_GEN_NAME_MACVTAP;
>> +    else if (STRPREFIX(name, VIR_NET_GENERATED_MACVLAN_PREFIX))
>> +    return VIR_NET_DEV_GEN_NAME_MACVLAN;
>> +    else
>> +    return VIR_NET_DEV_GEN_NAME_NONE;
>>   }
>>  
>>  
>> @@ -113,9 +93,7 @@ virNetDevMacVLanReserveNameInternal(const char *name)
>>   void
>>   virNetDevMacVLanReserveName(const char *name)
>>   {
>> -    virMutexLock(&virNetDevMacVLanCreateMutex);
>> -    virNetDevMacVLanReserveNameInternal(name);
>> -    virMutexUnlock(&virNetDevMacVLanCreateMutex);
>> +    virNetDevReserveName(name, virNetDevMacVLanGetTypeByName(name), true);
>
>
>As with virnetdevtap.c - I think we should call the new common function
>directly, rather than keeping this i

Re: Re: [PATCH 4/5] netdev: Enable virNetDevGenerateName to support veth

2020-12-07 Thread Shi Lei
On 2020-12-08 at 09:29, Laine Stump wrote:
>On 12/4/20 2:01 AM, Shi Lei wrote:
>> Signed-off-by: Shi Lei 
>> ---
>>   src/util/virnetdev.c | 2 ++
>>   src/util/virnetdev.h | 3 +++
>>   2 files changed, 5 insertions(+)
>>
>> diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
>> index 5ff8e35f..ff1b1fa0 100644
>> --- a/src/util/virnetdev.c
>> +++ b/src/util/virnetdev.c
>> @@ -102,6 +102,7 @@ VIR_ENUM_IMPL(virNetDevGenNameType,
>> "tap",
>> "macvtap",
>> "macvlan",
>> +  "veth",
>
>
>As discussed in patch 1, this ENUM_IMPL is unnecessary. 

Okay.

>
>
>>   );
>>  
>>   static virNetDevGenName
>> @@ -110,6 +111,7 @@ virNetDevGenNames[VIR_NET_DEV_GEN_NAME_LAST] = {
>>   {-1, VIR_NET_GENERATED_TAP_PREFIX, VIR_MUTEX_INITIALIZER},
>>   {-1, VIR_NET_GENERATED_MACVTAP_PREFIX, VIR_MUTEX_INITIALIZER},
>>   {-1, VIR_NET_GENERATED_MACVLAN_PREFIX, VIR_MUTEX_INITIALIZER},
>> +    {-1, VIR_NET_GENERATED_VETH_PREFIX, VIR_MUTEX_INITIALIZER},
>>   };
>>  
>>   typedef enum {
>> diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h
>> index 19f37b61..097d0f8e 100644
>> --- a/src/util/virnetdev.h
>> +++ b/src/util/virnetdev.h
>> @@ -40,6 +40,8 @@ typedef void virIfreq;
>>    */
>>   #define VIR_NET_GENERATED_TAP_PREFIX "vnet"
>>  
>> +#define VIR_NET_GENERATED_VETH_PREFIX "veth"
>
>
>Up until now, libvirt has named the veth devices as "vnetN", not
>"vethN". I don't know that it would cause any problem to change to using
>"vethN" (since already-running domains would have their "vnetN" device
>name available in the domain status - only newly started domains would
>use  "vethN"). However I don't see any concrete reason for making that
>change.
>
>
>That being the case, I think you can just drop this patch, and use the
>TAP prefix in the next patch (actually that's a good indication that it
>shouldn't be called VIR_NET_DEV_GEN_NAME_TAP, but should instead be
>called VIR_NET_DEV_GEN_NAME_VNET). 

Okay.

>
>
>> +
>>   /* libvirt will start macvtap/macvlan interface names with one of
>>    * these prefixes when it auto-generates the name
>>    */
>> @@ -156,6 +158,7 @@ typedef enum {
>>   VIR_NET_DEV_GEN_NAME_TAP,
>>   VIR_NET_DEV_GEN_NAME_MACVTAP,
>>   VIR_NET_DEV_GEN_NAME_MACVLAN,
>> +    VIR_NET_DEV_GEN_NAME_VETH,
>>   VIR_NET_DEV_GEN_NAME_LAST
>>   } virNetDevGenNameType;
>>  
>
>



Re: Re: [PATCH 2/5] netdevtap: Use common helper function to create unique tap name

2020-12-07 Thread Shi Lei
On 2020-12-08 at 07:42, Laine Stump wrote:
>On 12/4/20 2:01 AM, Shi Lei wrote:
>> Simplify GenerateName/ReserveName for netdevtap by using common
>> functions.
>>
>> Signed-off-by: Shi Lei 
>> ---
>>   src/util/virnetdevtap.c | 58 +++--
>>   1 file changed, 4 insertions(+), 54 deletions(-)
>>
>> diff --git a/src/util/virnetdevtap.c b/src/util/virnetdevtap.c
>> index 198607b5..38b2f171 100644
>> --- a/src/util/virnetdevtap.c
>> +++ b/src/util/virnetdevtap.c
>> @@ -55,9 +55,6 @@
>>  
>>   VIR_LOG_INIT("util.netdevtap");
>>  
>> -virMutex virNetDevTapCreateMutex = VIR_MUTEX_INITIALIZER;
>> -static int virNetDevTapLastID = -1; /* not "unsigned" because callers use 
>> %d */
>> -
>>  
>>   /**
>>    * virNetDevTapReserveName:
>> @@ -72,25 +69,7 @@ static int virNetDevTapLastID = -1; /* not "unsigned" 
>> because callers use %d */
>>   void
>>   virNetDevTapReserveName(const char *name)
>>   {
>> -    unsigned int id;
>> -    const char *idstr = NULL;
>> -
>> -
>> -    if (STRPREFIX(name, VIR_NET_GENERATED_TAP_PREFIX)) {
>> -
>> -    VIR_INFO("marking device in use: '%s'", name);
>> -
>> -    idstr = name + strlen(VIR_NET_GENERATED_TAP_PREFIX);
>> -
>> -    if (virStrToLong_ui(idstr, NULL, 10, &id) >= 0) {
>> -    virMutexLock(&virNetDevTapCreateMutex);
>> -
>> -    if (virNetDevTapLastID < (int)id)
>> -    virNetDevTapLastID = id;
>> -
>> -    virMutexUnlock(&virNetDevTapCreateMutex);
>> -    }
>> -    }
>> +    virNetDevReserveName(name, VIR_NET_DEV_GEN_NAME_TAP, true);
>
>
>I think we can just call virNetDevReserveName() directly, rather than
>keeping virNetDevTapReserveName() as a go-between...
> 

Okay.

>
>
>>   }
>>  
>>  
>> @@ -199,36 +178,7 @@ virNetDevTapGetRealDeviceName(char *ifname 
>> G_GNUC_UNUSED)
>>   static int
>>   virNetDevTapGenerateName(char **ifname)
>>   {
>> -    int id;
>> -    double maxIDd = pow(10, IFNAMSIZ - 1 - 
>> strlen(VIR_NET_GENERATED_TAP_PREFIX));
>> -    int maxID = INT_MAX;
>> -    int attempts = 0;
>> -
>> -    if (maxIDd <= (double)INT_MAX)
>> -    maxID = (int)maxIDd;
>> -
>> -    do {
>> -    g_autofree char *try = NULL;
>> -
>> -    id = ++virNetDevTapLastID;
>> -
>> -    /* reset before overflow */
>> -    if (virNetDevTapLastID >= maxID)
>> -    virNetDevTapLastID = -1;
>> -
>> -    try = g_strdup_printf(*ifname, id);
>> -
>> -    if (!virNetDevExists(try)) {
>> -    g_free(*ifname);
>> -    *ifname = g_steal_pointer(&try);
>> -    return 0;
>> -    }
>> -    } while (++attempts < 1);
>> -
>> -    virReportError(VIR_ERR_INTERNAL_ERROR,
>> -   _("no unused %s names available"),
>> -   VIR_NET_GENERATED_TAP_PREFIX);
>> -    return -1;
>> +    return virNetDevGenerateName(ifname, VIR_NET_DEV_GEN_NAME_TAP);
>
>
>Same here. 

Okay.

>
>
>>   }
>>  
>>  
>> @@ -263,7 +213,7 @@ int virNetDevTapCreate(char **ifname,
>>   int ret = -1;
>>   int fd = -1;
>>  
>> -    virMutexLock(&virNetDevTapCreateMutex);
>> +    virNetDevLockGenName(VIR_NET_DEV_GEN_NAME_TAP);
>>  
>>   /* if ifname is "vnet%d", then auto-generate a name for the new
>>    * device (the kernel could do this for us, but has a bad habit of
>
>
>The code around here is going to need to be updated to take advantage of
>the "g_strdup_printf(*ifname, id)" in your new Generate function. (as a
>matter of fact, the functions that call virNetDevTapCreate() should
>probably also be updated to stop them from adding in "vnet%d" when
>ifname is empty - we can just let it remain empty until the call to
>virNetDevGenerateName(). 

Okay. I'll find out those functions and prevent them from doing such things.

>
>
>> @@ -333,7 +283,7 @@ int virNetDevTapCreate(char **ifname,
>>   ret = 0;
>>  
>>    cleanup:
>> -    virMutexUnlock(&virNetDevTapCreateMutex);
>> +    virNetDevUnlockGenName(VIR_NET_DEV_GEN_NAME_TAP);
>>   if (ret < 0) {
>>   VIR_FORCE_CLOSE(fd);
>>   while (i--)
>
>



Re: Re: [PATCH 2/5] netdevtap: Use common helper function to create unique tap name

2020-12-07 Thread Shi Lei
On 2020-12-08 at 07:44, Laine Stump wrote:
>On 12/4/20 2:01 AM, Shi Lei wrote:
>> Simplify GenerateName/ReserveName for netdevtap by using common
>> functions.
>>
>> Signed-off-by: Shi Lei 
>> ---
>>   src/util/virnetdevtap.c | 58 +++--
>>   1 file changed, 4 insertions(+), 54 deletions(-)
>>
>> diff --git a/src/util/virnetdevtap.c b/src/util/virnetdevtap.c
>> index 198607b5..38b2f171 100644
>> --- a/src/util/virnetdevtap.c
>> +++ b/src/util/virnetdevtap.c
>> @@ -55,9 +55,6 @@
>
>
>Also, I noticed when looking at patch 3 that you removed #include
> due to pow() no longer being needed - I think you need to do
>that in this file too. 

Oh. Thanks for reminding me.

>
>
>>  
>>   VIR_LOG_INIT("util.netdevtap");
>>  
>



Re: Re: [PATCH 1/5] netdev: Introduce several helper functions for generating unique netdev name

2020-12-07 Thread Shi Lei
On 2020-12-08 at 07:37, Laine Stump wrote:
>First - thanks for taking this on, and doing it the right way! (i.e.
>refactoring my two changes down into a common set of functions to be
>reused, rather than duplicating the code). I can't really say why I
>didn't go the extra bit when I made the changes to virnetdevtap.c and
>virnetdevmacvlan.c - I really should have cleaned it up as you've done
>in patches 1 & 2 here.
>
>On 12/4/20 2:01 AM, Shi Lei wrote:
>> Extract ReserveName/GenerateName from netdevtap and netdevmacvlan as
>> common helper functions. Along with them, export Lock/Unlock functions
>> to protect these processes.
>>
>> Signed-off-by: Shi Lei 
>> ---
>>   src/libvirt_private.syms |   4 ++
>>   src/util/virnetdev.c | 140 +++
>>   src/util/virnetdev.h |  33 +
>>   3 files changed, 177 insertions(+)
>>
>> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
>> index 2f640ef1..be3148c9 100644
>> --- a/src/libvirt_private.syms
>> +++ b/src/libvirt_private.syms
>> @@ -2551,6 +2551,7 @@ virNetDevDelMulti;
>>   virNetDevExists;
>>   virNetDevFeatureTypeFromString;
>>   virNetDevFeatureTypeToString;
>> +virNetDevGenerateName;
>>   virNetDevGetFeatures;
>>   virNetDevGetIndex;
>>   virNetDevGetLinkInfo;
>> @@ -2572,8 +2573,10 @@ virNetDevGetVLanID;
>>   virNetDevIfStateTypeFromString;
>>   virNetDevIfStateTypeToString;
>>   virNetDevIsVirtualFunction;
>> +virNetDevLockGenName;
>>   virNetDevPFGetVF;
>>   virNetDevReadNetConfig;
>> +virNetDevReserveName;
>>   virNetDevRunEthernetScript;
>>   virNetDevRxFilterFree;
>>   virNetDevRxFilterModeTypeFromString;
>> @@ -2594,6 +2597,7 @@ virNetDevSetRcvMulti;
>>   virNetDevSetRootQDisc;
>>   virNetDevSetupControl;
>>   virNetDevSysfsFile;
>> +virNetDevUnlockGenName;
>>   virNetDevValidateConfig;
>>   virNetDevVFInterfaceStats;
>>  
>> diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
>> index 5104bbe7..5ff8e35f 100644
>> --- a/src/util/virnetdev.c
>> +++ b/src/util/virnetdev.c
>> @@ -17,6 +17,7 @@
>>    */
>>  
>>   #include 
>> +#include 
>>  
>>   #include "virnetdev.h"
>>   #include "viralloc.h"
>> @@ -95,6 +96,22 @@ VIR_LOG_INIT("util.netdev");
>>   (FEATURE_WORD(blocks, index, field) & FEATURE_FIELD_FLAG(index))
>>   #endif
>>  
>> +VIR_ENUM_IMPL(virNetDevGenNameType,
>> +  VIR_NET_DEV_GEN_NAME_LAST,
>> +  "none",
>> +  "tap",
>> +  "macvtap",
>> +  "macvlan",
>> +);
>
>
>You've added VIR_ENUM_IMPL and VIR_ENUM_DECL, but haven't actually used
>the corresponding virNetDevGenNameTypeToString() and
>virNetDevGenNameTypeFromString() functions anywhere. Unless it will be
>used, I'd say it would be just as well to leave it out. (and if you do
>define it, probably use "vnet" for the tap device entry, since it's not
>really important that the device is a tap device, just that it's a
>network device whose name starts with "vnet").
> 

Okay.

>
>
>> +static virNetDevGenName
>> +virNetDevGenNames[VIR_NET_DEV_GEN_NAME_LAST] = {
>> +    {0, NULL, VIR_MUTEX_INITIALIZER},
>
>
>So you have this extra entry in the table at [0] because the first enum
>value is VIR_NET_DEV_GEN_NAME_NONE. Since that value would never be
>possible, I think you should just make the first value in the enum be
>VIR_NET_DEV_GEN_NAME_VNET (instead of ..._TAP), and then you don't need
>the unused entry in the table.
> 

Okay.

>
>> +    {-1, VIR_NET_GENERATED_TAP_PREFIX, VIR_MUTEX_INITIALIZER},
>> +    {-1, VIR_NET_GENERATED_MACVTAP_PREFIX, VIR_MUTEX_INITIALIZER},
>> +    {-1, VIR_NET_GENERATED_MACVLAN_PREFIX, VIR_MUTEX_INITIALIZER},
>> +};
>> +
>>   typedef enum {
>>   VIR_MCAST_TYPE_INDEX_TOKEN,
>>   VIR_MCAST_TYPE_NAME_TOKEN,
>> @@ -3516,3 +3533,126 @@ virNetDevSetRootQDisc(const char *ifname,
>>  
>>   return 0;
>>   }
>> +
>> +
>> +/**
>> + * virNetDevReserveName:
>> + * @name: name of an existing network device
>> + * @type: type of the network device
>> + * @locked: whether this process is locked by the internal mutex
>> + *
>> + * Reserve a network device name, so that any new network device
>> + * created with an autogenerated name will use a number higher
>> + * tha

Re: Re: [RFCv2 37/46] conf: Replace virDomainGraphicsDefParseXMLSpice(hardcoded) with virDomainGraphicsSpiceDefParseXML(generated)

2020-12-07 Thread Shi Lei
On 2020-12-07 at 17:38, DanielP. Berrangé wrote:
>On Mon, Dec 07, 2020 at 03:47:39PM +0800, Shi Lei wrote:
>> On 2020-12-05 at 02:17, DanielP. Berrangé wrote:
>> >On Fri, Sep 04, 2020 at 11:35:29AM +0800, Shi Lei wrote:
>> >> Signed-off-by: Shi Lei 
>> >> ---
>> >>  src/conf/domain_conf.c | 272 ++---
>> >>  src/conf/domain_conf.h |  37 +++---
>> >>  2 files changed, 26 insertions(+), 283 deletions(-)
>> >>
>> >> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
>> >> index b3ec111..20d731b 100644
>> >> --- a/src/conf/domain_conf.c
>> >> +++ b/src/conf/domain_conf.c
>> >> @@ -877,6 +877,7 @@ VIR_ENUM_IMPL(virDomainGraphicsVNCSharePolicy,
>> >> 
>> >>  VIR_ENUM_IMPL(virDomainGraphicsSpiceChannelName,
>> >>    VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST,
>> >> +  "none",
>> >>    "main",
>> >>    "display",
>> >>    "inputs",
>> >> @@ -14431,13 +14432,14 @@ virDomainGraphicsRDPDefParseXMLHook(xmlNodePtr 
>> >> node
>> >> diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
>> >> index df84763..f27f429 100644
>> >> --- a/src/conf/domain_conf.h
>> >> +++ b/src/conf/domain_conf.h
>> >> @@ -1584,6 +1584,7 @@ struct _virDomainGraphicsAuthDef {  /* genparse, 
>> >> genformat:separate */
>> >>  };
>> >> 
>> >>  typedef enum {
>> >> +    VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_NONE = 0,
>> >
>> >I'm not sure why this extra enum field needs to be added ?
>> >
>> >IMHO we don't really want to have such extra values except in
>> >a few special cases where we need to track some "default"
>> >explicitly.
>>
>> There're two forms of enum in the code.
>>
>> (1) Start with a 'default' or 'none' item, e.g., virDomainDiskIo
>>
>>     The virDomainDiskIo has VIR_DOMAIN_DISK_IO_DEFAULT which is ZERO-indexed.
>>
>>     When parsing it, the code is like:
>>
>>     if ((tmp = virXMLPropString(cur, "io")) &&
>>         (def->iomode = virDomainDiskIoTypeFromString(tmp)) <= 0) {
>>         virReportError(...);
>>         return -1;
>>     }
>>
>>     It checks XXXTypeFromString() ** <= ** 0, because the form <... 
>> io='default'>
>>     is illegal.
>>
>> (2) Without a 'default' or 'none' value, e.g., virDomainDiskDevice
>>
>>     When parsing it, the code is like:
>>
>>      if ((tmp = virXMLPropString(node, "device")) &&
>>          (def->device = virDomainDiskDeviceTypeFromString(tmp)) < 0) {
>>          virReportError(...);
>>         return -1;
>>     }
>>
>>     It just checks XXXTypeFromString() ** < ** 0, because the enum's 
>> ZERO-Value item
>>     is valid.
>>
>>
>> To handle these two situations, I add extra "default" for case #2 and unify 
>> the form of enums,
>> so that it's easier to implement the generator.
>>
>> Another solution is to add an extra directive to indicate whether a enum has 
>> a 'default' item,
>> so that the generator can decide between '<=' and '<'. Perhaps it is more 
>> safe and reliable.
>
>Looks like there are about 200 enums,  43 use _NONE and 56 use _DEFAULT,
>and the remaining 100 use neither.
>
>Maybe we can do without the need to add a directive, if we change all cases of
>NONE to DEFAULT, and just make the generator look at whether _DEFAULT is 
>present
>or not ? 

Hmm. I try it.

Shi Lei

>
>
>Regards,
>Daniel
>--
>|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
>|: https://libvirt.org -o- https://fstop138.berrange.com :|
>|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
>



Re: Re: [RFCv2 00/46] RFC: Generate parsexml/formatbuf functions based on directives

2020-12-07 Thread Shi Lei
On 2020-12-05 at 02:55, DanielP. Berrangé wrote:
>On Fri, Sep 04, 2020 at 11:34:52AM +0800, Shi Lei wrote:
>> V1 here: 
>> [https://www.redhat.com/archives/libvir-list/2020-June/msg00357.html]
>>
>> Differ from V1:
>>
>>   * Move the generator into scripts/xmlgen and rename it 'xmlgen'.
>>
>>   * Declare virXMLChildNode and virXMLChildNodeSet in libvirt_private.syms.
>>
>>   * Replace VIR_FREE with g_free and VIR_ALLOC[_N] with g_new0.
>>
>>   * Adjust virReportError to avoid unnecessary translation.
>>
>>   * Remove the macro VIR_USED and use G_GNUC_UNUSED to declare arguments.
>>
>>   * When parsing string member, assign value to it directly instead of
>> using middle variable.
>>
>>   * Don't set libclang_path. Just use python-clang's default setting.
>>
>>   * Use virEscapeString for escaping xml characters.
>>
>>   * Enable directive 'genformat' with a parameter to support separation mode.
>>
>>   * Add directive 'xmlswitch' and 'xmlgroup' to support discriminated unions.
>>
>>   * Allow directive 'array' and 'specified' to carry with a parameter,
>> which specifies its counterpart explicitly.
>>
>>   * Enable directive 'xmlattr' with path.
>>
>>   * Add directive 'formatflag' and 'formathook'.
>
>I'm very sorry for the extremely long delay in reviewing this new
>series.
>
>Overall I like this series and would like to see how we can focus on
>getting at least part of it merged.  It is interesting to see how it
>is forcing a separation of the parsing and validation of data. I
>also like that it is making the code more consistent in style.
>
>The main blocker I think is that we need to temporarily commit the
>generated files to git, and not run the generator during normal
>builds, until we ditch RHEL-7 support in April 2021.  This should
>be quite easy to deal with.
>
>The next thing is that wierd need to insert a "_NONE" field in to
>all the enums. I'm not understanding what reqiures this, but I think
>we need to come up with a different solution to whatever the problem
>is.  It looks like this only affects the DomainGraphics conversion
>in your series. So we could start by only merging the Network conversion
>patches, until we figure out a solution to avoid adding _NONE fields.
>
>That's it for the main blockers to merge from my POV.
>
>After that I think a priority is to get some test coverage of the
>generator script, and to add the docs about the how the generator
>annotations work. This can be done after the initial merge.
>
>Then there's a large ongoing work to actually convert everything,
>but there's no rush to finish that, as you've shown that we can
>do it incrementally.
>
>So if you want to re-post an update of the series with the few
>blocking items addressed, then personally I'd look at trying to
>merge that unless other people have objections they want to
>raise that can't be solved after merging the initial support.
>
>Regards,
>Daniel
>--

Thanks for reviewing this series.

For the first blocker, I think we can wait until April 2021, since we have 
spent a lot time on it.

According to your comment, I think I can make some preparations for the initial 
merge during
these 4~5 months.

(1) Implement some testcases for the generator to make sure its proper 
functions in the future.

(2) Add some docs to explain and demonstrate this generator. My English is not 
well, so I will
try my best to do it and I may need a bit long time.

(3) For the 'default' item of enums, I think we can add an extra directive to 
direct the generator
how to generate code for enum, as I mentioned in the last email. Or we can 
continue to talk about
other solutions.

(4) Next series will only include conversion about network, so that we can 
focus on the docs,
testcases and this generator itself.

I plan to finish these jobs and post the next series in the next month, which 
will be still a RFC.
So you and other people can have sufficent time to review and comment it and I 
will have
sufficent time to improve it :-)


Regards,
Shi Lei



Re: Re: [RFCv2 37/46] conf: Replace virDomainGraphicsDefParseXMLSpice(hardcoded) with virDomainGraphicsSpiceDefParseXML(generated)

2020-12-07 Thread Shi Lei
On 2020-12-05 at 02:17, DanielP. Berrangé wrote:
>On Fri, Sep 04, 2020 at 11:35:29AM +0800, Shi Lei wrote:
>> Signed-off-by: Shi Lei 
>> ---
>>  src/conf/domain_conf.c | 272 ++---
>>  src/conf/domain_conf.h |  37 +++---
>>  2 files changed, 26 insertions(+), 283 deletions(-)
>>
>> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
>> index b3ec111..20d731b 100644
>> --- a/src/conf/domain_conf.c
>> +++ b/src/conf/domain_conf.c
>> @@ -877,6 +877,7 @@ VIR_ENUM_IMPL(virDomainGraphicsVNCSharePolicy,
>> 
>>  VIR_ENUM_IMPL(virDomainGraphicsSpiceChannelName,
>>    VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST,
>> +  "none",
>>    "main",
>>    "display",
>>    "inputs",
>> @@ -14431,13 +14432,14 @@ virDomainGraphicsRDPDefParseXMLHook(xmlNodePtr node
>> diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
>> index df84763..f27f429 100644
>> --- a/src/conf/domain_conf.h
>> +++ b/src/conf/domain_conf.h
>> @@ -1584,6 +1584,7 @@ struct _virDomainGraphicsAuthDef {  /* genparse, 
>> genformat:separate */
>>  };
>> 
>>  typedef enum {
>> +    VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_NONE = 0,
>
>I'm not sure why this extra enum field needs to be added ?
>
>IMHO we don't really want to have such extra values except in
>a few special cases where we need to track some "default"
>explicitly. 

There're two forms of enum in the code.

(1) Start with a 'default' or 'none' item, e.g., virDomainDiskIo

    The virDomainDiskIo has VIR_DOMAIN_DISK_IO_DEFAULT which is ZERO-indexed.

    When parsing it, the code is like:

    if ((tmp = virXMLPropString(cur, "io")) &&
        (def->iomode = virDomainDiskIoTypeFromString(tmp)) <= 0) {
        virReportError(...);
        return -1;
    }

    It checks XXXTypeFromString() ** <= ** 0, because the form <... 
io='default'>
    is illegal.

(2) Without a 'default' or 'none' value, e.g., virDomainDiskDevice

    When parsing it, the code is like:

     if ((tmp = virXMLPropString(node, "device")) &&
         (def->device = virDomainDiskDeviceTypeFromString(tmp)) < 0) {
         virReportError(...);
        return -1;
    }

    It just checks XXXTypeFromString() ** < ** 0, because the enum's ZERO-Value 
item
    is valid.


To handle these two situations, I add extra "default" for case #2 and unify the 
form of enums,
so that it's easier to implement the generator.

Another solution is to add an extra directive to indicate whether a enum has a 
'default' item,
so that the generator can decide between '<=' and '<'. Perhaps it is more safe 
and reliable.


Regards,
Shi Lei

>
>>  VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MAIN,
>>  VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_DISPLAY,
>>  VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_INPUT,
>
>
>Regards,
>Daniel
>--
>|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
>|: https://libvirt.org -o- https://fstop138.berrange.com :|
>|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
>



Re: Re: [RFCv2 05/46] util: Add helper functions for 'bool' and 'time_t' and cooperate with xmlgen

2020-12-06 Thread Shi Lei
On 2020-12-05 at 02:02, DanielP. Berrangé wrote:
>On Fri, Sep 04, 2020 at 11:34:57AM +0800, Shi Lei wrote:
>> Signed-off-by: Shi Lei 
>> ---
>>  src/libvirt_private.syms |  3 +++
>>  src/util/virstring.c | 57 
>>  src/util/virstring.h |  9 +++
>>  3 files changed, 69 insertions(+)
>>
>> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
>> index 191eab0..4ad9d1e 100644
>> --- a/src/libvirt_private.syms
>> +++ b/src/libvirt_private.syms
>> @@ -3242,6 +3242,7 @@ virStringStripSuffix;
>>  virStringToUpper;
>>  virStringTrimOptionalNewline;
>>  virStrncpy;
>> +virStrToBool;
>>  virStrToDouble;
>>  virStrToLong_i;
>>  virStrToLong_l;
>> @@ -3252,6 +3253,8 @@ virStrToLong_ul;
>>  virStrToLong_ull;
>>  virStrToLong_ullp;
>>  virStrToLong_ulp;
>> +virStrToTime;
>> +virTimeFormatBuf;
>>  virTrimSpaces;
>> 
>> 
>> diff --git a/src/util/virstring.c b/src/util/virstring.c
>> index de2ef96..23ade40 100644
>> --- a/src/util/virstring.c
>> +++ b/src/util/virstring.c
>> @@ -1349,3 +1349,60 @@ int virStringParseYesNo(const char *str, bool *result)
>> 
>>  return 0;
>>  }
>> +
>> +
>> +int
>> +virStrToBool(const char *str, const char *truevalue, bool *result)
>> +{
>> +    if (STREQ(str, truevalue))
>> +    *result = true;
>> +    else
>> +    *result = false;
>> +
>> +    return 0;
>> +}
>
>I'd suggest we should explicitly check both the true and false
>values, and raise an error for any other value.
>
>I think we probably ought to have wrappers for the pairs of words
>we consider sane.  eg  virStrToBoolYesNo, virStrToBoolTrueFalse
>and virStrToBoolOnOff I think are probably the only ones we want
>to permit 

Okay.

Shi Lei

>
>> +
>> +
>
>Regards,
>Daniel
>--
>|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
>|: https://libvirt.org -o- https://fstop138.berrange.com :|
>|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
>



Re: Re: [RFCv2 01/46] scripts: Add a tool to generate xml parse/format functions

2020-12-06 Thread Shi Lei
On 2020-12-05 at 01:22, DanielP. Berrangé wrote:
>On Fri, Sep 04, 2020 at 11:34:53AM +0800, Shi Lei wrote:
>> This tool is used to generate parsexml/formatbuf functions.
>> It is based on libclang and its python-binding.
>> Some directives (such as genparse, xmlattr, etc.) need to be added on
>> the declarations of structs to direct the tool.
>
>In testing this I hit one problem with use of libclang python binding.
>
>In RHEL-7, we have clang 3.4.1 via EPEL repos, and that only has a
>python 2 binding available, no python 3 binding.
>
>IOW, if we take te xml generator, we loose support for building on
>RHEL-7 as we consume it today.
>
>I see a few options
>
> 1 Wait until April 2021 to merge this series, because our
>   platform matrix lets us drop RHEL-7 support in April.
>
> https://libvirt.org/platforms.html
>
> 2 Enable use of software collections in our RHEL-7 CI
>   container images and pull clang 7 from there
>
> https://www.softwarecollections.org/en/scls/rhscl/llvm-toolset-7.0/
>
> 3 Merge this feature now, commit the generated files to git and
>   only run the generator manually when we need changes. Once
>   we drop RHEL7 we can delete the generated files from git.
>
> 4 Drop RHEL-7 immediately in violation of our platform support
>   matrix. I know of people, however, who still rely on being
>   able to build new libvirt on RHEL-7 this would harm.
>
>
>My feeling is to go for option (3) and temporarily commit
>the generated files to git, as it will only need to be a short
>term hack for 4-5 months.
> 

I would like to select option(1), since it's just a short waiting time.
During this time, we can refine the document, testcase and the generator itself.

Regards,
Shi Lei

>
>
>Regards,
>Daniel
>--
>|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
>|: https://libvirt.org -o- https://fstop138.berrange.com :|
>|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
>



Re: Re: [RFCv2 00/46] RFC: Generate parsexml/formatbuf functions based on directives

2020-12-06 Thread Shi Lei
On 2020-12-05 at 01:41, DanielP. Berrangé wrote:
>On Fri, Sep 04, 2020 at 11:34:52AM +0800, Shi Lei wrote:
>> V1 here: 
>> [https://www.redhat.com/archives/libvir-list/2020-June/msg00357.html]
>
>
>> For those new and changed directives, illustrate them by an example:
>>
>> struct _virDomainGraphicsAuthDef {  /* genparse, genformat:separate */
>> char *passwd;   /* xmlattr, 
>>formatflag:VIR_DOMAIN_DEF_FORMAT_SECURE */
>> bool expires;
>> time_t validTo; /* xmlattr:passwdValidTo, 
>>specified:expires */
>> virDomainGraphicsAuthConnectedType connected;   /* xmlattr */
>> };
>>
>> struct _virDomainGraphicsListenDef {    /* genparse:withhook, genformat */
>> virDomainGraphicsListenType type;   /* xmlattr */
>> char *address;  /* xmlattr, formathook */
>> char *network;  /* xmlattr, 
>>formatflag:VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK */
>> char *socket;   /* xmlattr, formathook */
>> int fromConfig; /* xmlattr, 
>>formatflag:%VIR_DOMAIN_DEF_FORMAT_STATUS */
>> bool autoGenerated; /* xmlattr, 
>>formatflag:%VIR_DOMAIN_DEF_FORMAT_STATUS */
>> };
>>
>> struct _virDomainGraphicsSDLDef {   /* genparse, genformat:separate */
>> char *display;  /* xmlattr */
>> char *xauth;    /* xmlattr */
>> bool fullscreen;    /* xmlattr */
>> virTristateBool gl; /* xmlattr:gl/enable */
>> };
>>
>> struct _virDomainGraphicsDef {  /* genparse:concisehook, genformat */
>> virObjectPtr privateData;
>> virDomainGraphicsType type; /* xmlattr */
>>
>> size_t nListens;
>> virDomainGraphicsListenDefPtr listens;  /* xmlelem, array:nListens */
>>
>> union {
>> virDomainGraphicsSDLDef sdl;    /* xmlgroup */
>> virDomainGraphicsVNCDef vnc;    /* xmlgroup */
>> virDomainGraphicsRDPDef rdp;    /* xmlgroup */
>> virDomainGraphicsDesktopDef desktop;    /* xmlgroup */
>> virDomainGraphicsSpiceDef spice;    /* xmlgroup */
>> virDomainGraphicsEGLHeadlessDef egl_headless;   /* xmlgroup */
>> } data; /* xmlswitch:type */
>> };
>>
>>
>> Explanation for these directives:
>>
>> - genformat[:separate|onlyattrs|onlyelems]
>>
>>   Only work on a struct.
>>   Generate formatbuf function for this struct only if 'genformat' is 
>>specified.
>>   The function name is based on struct-name and suffixed with 
>>'FormatBuf'.
>>
>>   When 'genformat:separate' is specified, generate two formatbuf 
>>functions
>>   rather than a single full-mode formatbuf function.
>>   One for formatting attributes and another for formatting elements.
>>   These function names are based on struct-name and suffixed with 
>>'FormatAttr'
>>   and 'FormatElem' respectively.
>>
>>   The 'onlyattrs' and 'onlyelems' are just like 'separate', but only
>>   generate one of those two functions according to its denotation.
>>
>> - xmlattr[:[parentname/]thename]
>>
>>   Parse/Format the field as an XML attribute or
>>   attribute wrapped by an XML element.
>>   If only 'thename' is specified, use it as the XML attribute name;
>>   or use the filed name.
>>   The 'parentname' is the name of the attribute's parent element.
>>   If 'parentname/thename' is specified, the corresponding form is
>>   .
>>
>> - xmlgroup
>>
>>   The field is a struct, but its corresponding form in XML is a group
>>   rather than an element.
>>
>> - xmlswitch:thename
>>
>>   Only for discriminated union. 'thename' is the name of its relative 
>>enum.
>>   The name of each union member should match a shortname of the enum.
>>
>> - array[:countername]
>>
>>   Parse/Format the field as an array.
>>   Each array field must have an related counter field, which name is
>>   specified by 'countername'.
>>   If 'countername' is omitted, follow the pattern:
>>   n + 'field_name'.
>>
>> - spe

Re: Re: [RFCv2 02/46] maint: Check python3-clang

2020-12-06 Thread Shi Lei
On 2020-12-04 at 21:01, DanielP. Berrangé wrote:
>On Fri, Sep 04, 2020 at 11:34:54AM +0800, Shi Lei wrote:
>> Make sure python3-clang has been installed.
>>
>> Signed-off-by: Shi Lei 
>> ---
>>  meson.build | 5 +
>>  1 file changed, 5 insertions(+)
>>
>> diff --git a/meson.build b/meson.build
>> index 1eadea3..f97a03b 100644
>> --- a/meson.build
>> +++ b/meson.build
>> @@ -2504,3 +2504,8 @@ if conf.has('WITH_QEMU')
>>    }
>>    summary(priv_summary, section: 'Privileges')
>>  endif
>> +
>> +py3_clang = run_command('python3', '-c', 'import clang.cindex;print("ok")')
>> +if py3_clang.returncode() != 0
>> +  error('python3-clang is required.')
>> +endif
>
>This probably needs to be a bit of a stronger check, as I found it is
>possible "pip install clang", without having libclang.so actually
>installed, and this "import clang.cindex" will still succeeed.
>
>I think we need to add something like
>
> py3_clang_working = run_command('python3', '-c', 'import 
> clang.cindex;cindex.Index()')
> if py3_clang_working.returncode() != 0
>   error('python3-clang is present, but not working. Perhaps libclang is 
>missing?')
> endif 

Okay.
We can use 'clang.cindex.Config().get_cindex_library' to check libclang.so. And 
also, the version
of the libclang needs to be printed in error message when it is missing.

>
>
>Also, we need to add  python3-clang to the  libvirt.spec.in and the
>mingw-libvirt.spec.in, and also the containers in ci/containers/ 

Okay.

Regards,
Shi Lei

>
>Regards,
>Daniel
>--
>|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
>|: https://libvirt.org -o- https://fstop138.berrange.com :|
>|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
>



[PATCH 2/5] netdevtap: Use common helper function to create unique tap name

2020-12-03 Thread Shi Lei
Simplify GenerateName/ReserveName for netdevtap by using common
functions.

Signed-off-by: Shi Lei 
---
 src/util/virnetdevtap.c | 58 +++--
 1 file changed, 4 insertions(+), 54 deletions(-)

diff --git a/src/util/virnetdevtap.c b/src/util/virnetdevtap.c
index 198607b5..38b2f171 100644
--- a/src/util/virnetdevtap.c
+++ b/src/util/virnetdevtap.c
@@ -55,9 +55,6 @@
 
 VIR_LOG_INIT("util.netdevtap");
 
-virMutex virNetDevTapCreateMutex = VIR_MUTEX_INITIALIZER;
-static int virNetDevTapLastID = -1; /* not "unsigned" because callers use %d */
-
 
 /**
  * virNetDevTapReserveName:
@@ -72,25 +69,7 @@ static int virNetDevTapLastID = -1; /* not "unsigned" 
because callers use %d */
 void
 virNetDevTapReserveName(const char *name)
 {
-unsigned int id;
-const char *idstr = NULL;
-
-
-if (STRPREFIX(name, VIR_NET_GENERATED_TAP_PREFIX)) {
-
-VIR_INFO("marking device in use: '%s'", name);
-
-idstr = name + strlen(VIR_NET_GENERATED_TAP_PREFIX);
-
-if (virStrToLong_ui(idstr, NULL, 10, &id) >= 0) {
-virMutexLock(&virNetDevTapCreateMutex);
-
-if (virNetDevTapLastID < (int)id)
-virNetDevTapLastID = id;
-
-virMutexUnlock(&virNetDevTapCreateMutex);
-}
-}
+virNetDevReserveName(name, VIR_NET_DEV_GEN_NAME_TAP, true);
 }
 
 
@@ -199,36 +178,7 @@ virNetDevTapGetRealDeviceName(char *ifname G_GNUC_UNUSED)
 static int
 virNetDevTapGenerateName(char **ifname)
 {
-int id;
-double maxIDd = pow(10, IFNAMSIZ - 1 - 
strlen(VIR_NET_GENERATED_TAP_PREFIX));
-int maxID = INT_MAX;
-int attempts = 0;
-
-if (maxIDd <= (double)INT_MAX)
-maxID = (int)maxIDd;
-
-do {
-g_autofree char *try = NULL;
-
-id = ++virNetDevTapLastID;
-
-/* reset before overflow */
-if (virNetDevTapLastID >= maxID)
-virNetDevTapLastID = -1;
-
-try = g_strdup_printf(*ifname, id);
-
-if (!virNetDevExists(try)) {
-g_free(*ifname);
-*ifname = g_steal_pointer(&try);
-return 0;
-}
-} while (++attempts < 1);
-
-virReportError(VIR_ERR_INTERNAL_ERROR,
-   _("no unused %s names available"),
-   VIR_NET_GENERATED_TAP_PREFIX);
-return -1;
+return virNetDevGenerateName(ifname, VIR_NET_DEV_GEN_NAME_TAP);
 }
 
 
@@ -263,7 +213,7 @@ int virNetDevTapCreate(char **ifname,
 int ret = -1;
 int fd = -1;
 
-virMutexLock(&virNetDevTapCreateMutex);
+virNetDevLockGenName(VIR_NET_DEV_GEN_NAME_TAP);
 
 /* if ifname is "vnet%d", then auto-generate a name for the new
  * device (the kernel could do this for us, but has a bad habit of
@@ -333,7 +283,7 @@ int virNetDevTapCreate(char **ifname,
 ret = 0;
 
  cleanup:
-virMutexUnlock(&virNetDevTapCreateMutex);
+virNetDevUnlockGenName(VIR_NET_DEV_GEN_NAME_TAP);
 if (ret < 0) {
 VIR_FORCE_CLOSE(fd);
 while (i--)
-- 
2.25.1




[PATCH 0/5] netdev: Extract GenerateName/ReserveName as common functions

2020-12-03 Thread Shi Lei
Extract ReserveName/GenerateName from netdevtap and netdevmacvlan as
common functions. Along with them, export Lock/Unlock functions
to protect these processes.

So tap, macvlan and veth can reuse them to simplify their
implementations.

Shi Lei (5):
  netdev: Introduce several helper functions for generating unique netdev name
  netdevtap: Use common helper function to create unique tap name
  netdevmacvlan: Use helper function to create unique macvlan/macvtap name
  netdev: Enable virNetDevGenerateName to support veth
  netdevveth: Simplify virNetDevVethCreate by using virNetDevGenerateName

 src/libvirt_private.syms|   4 +
 src/lxc/lxc_process.c   |   3 +
 src/util/virnetdev.c| 142 +++
 src/util/virnetdev.h|  36 +
 src/util/virnetdevmacvlan.c | 107 ++
 src/util/virnetdevmacvlan.h |   6 --
 src/util/virnetdevtap.c |  58 +-
 src/util/virnetdevveth.c| 146 ++--
 8 files changed, 251 insertions(+), 251 deletions(-)

-- 
2.25.1




[PATCH 4/5] netdev: Enable virNetDevGenerateName to support veth

2020-12-03 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/util/virnetdev.c | 2 ++
 src/util/virnetdev.h | 3 +++
 2 files changed, 5 insertions(+)

diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
index 5ff8e35f..ff1b1fa0 100644
--- a/src/util/virnetdev.c
+++ b/src/util/virnetdev.c
@@ -102,6 +102,7 @@ VIR_ENUM_IMPL(virNetDevGenNameType,
   "tap",
   "macvtap",
   "macvlan",
+  "veth",
 );
 
 static virNetDevGenName
@@ -110,6 +111,7 @@ virNetDevGenNames[VIR_NET_DEV_GEN_NAME_LAST] = {
 {-1, VIR_NET_GENERATED_TAP_PREFIX, VIR_MUTEX_INITIALIZER},
 {-1, VIR_NET_GENERATED_MACVTAP_PREFIX, VIR_MUTEX_INITIALIZER},
 {-1, VIR_NET_GENERATED_MACVLAN_PREFIX, VIR_MUTEX_INITIALIZER},
+{-1, VIR_NET_GENERATED_VETH_PREFIX, VIR_MUTEX_INITIALIZER},
 };
 
 typedef enum {
diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h
index 19f37b61..097d0f8e 100644
--- a/src/util/virnetdev.h
+++ b/src/util/virnetdev.h
@@ -40,6 +40,8 @@ typedef void virIfreq;
  */
 #define VIR_NET_GENERATED_TAP_PREFIX "vnet"
 
+#define VIR_NET_GENERATED_VETH_PREFIX "veth"
+
 /* libvirt will start macvtap/macvlan interface names with one of
  * these prefixes when it auto-generates the name
  */
@@ -156,6 +158,7 @@ typedef enum {
 VIR_NET_DEV_GEN_NAME_TAP,
 VIR_NET_DEV_GEN_NAME_MACVTAP,
 VIR_NET_DEV_GEN_NAME_MACVLAN,
+VIR_NET_DEV_GEN_NAME_VETH,
 VIR_NET_DEV_GEN_NAME_LAST
 } virNetDevGenNameType;
 
-- 
2.25.1




[PATCH 3/5] netdevmacvlan: Use helper function to create unique macvlan/macvtap name

2020-12-03 Thread Shi Lei
Simplify ReserveName/GenerateName for macvlan and macvtap by using
common functions.

Signed-off-by: Shi Lei 
---
 src/util/virnetdevmacvlan.c | 107 
 src/util/virnetdevmacvlan.h |   6 --
 2 files changed, 22 insertions(+), 91 deletions(-)

diff --git a/src/util/virnetdevmacvlan.c b/src/util/virnetdevmacvlan.c
index 72f0d670..7f58d7ca 100644
--- a/src/util/virnetdevmacvlan.c
+++ b/src/util/virnetdevmacvlan.c
@@ -45,7 +45,6 @@ VIR_ENUM_IMPL(virNetDevMacVLanMode,
 
 # include 
 # include 
-# include 
 
 # include "viralloc.h"
 # include "virlog.h"
@@ -64,39 +63,20 @@ VIR_LOG_INIT("util.netdevmacvlan");
 # define VIR_NET_GENERATED_PREFIX \
 ((flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? \
  VIR_NET_GENERATED_MACVTAP_PREFIX : VIR_NET_GENERATED_MACVLAN_PREFIX)
+# define VIR_NET_CREATE_TYPE \
+((flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ? \
+ VIR_NET_DEV_GEN_NAME_MACVTAP : VIR_NET_DEV_GEN_NAME_MACVLAN)
 
 
-virMutex virNetDevMacVLanCreateMutex = VIR_MUTEX_INITIALIZER;
-static int virNetDevMacVTapLastID = -1;
-static int virNetDevMacVLanLastID = -1;
-
-
-static void
-virNetDevMacVLanReserveNameInternal(const char *name)
+static virNetDevGenNameType
+virNetDevMacVLanGetTypeByName(const char *name)
 {
-unsigned int id;
-const char *idstr = NULL;
-int *lastID = NULL;
-int len;
-
-if (STRPREFIX(name, VIR_NET_GENERATED_MACVTAP_PREFIX)) {
-lastID = &virNetDevMacVTapLastID;
-len = strlen(VIR_NET_GENERATED_MACVTAP_PREFIX);
-} else if (STRPREFIX(name, VIR_NET_GENERATED_MACVLAN_PREFIX)) {
-lastID = &virNetDevMacVTapLastID;
-len = strlen(VIR_NET_GENERATED_MACVLAN_PREFIX);
-} else {
-return;
-}
-
-VIR_INFO("marking device in use: '%s'", name);
-
-idstr = name + len;
-
-if (virStrToLong_ui(idstr, NULL, 10, &id) >= 0) {
-if (*lastID < (int)id)
-*lastID = id;
-}
+if (STRPREFIX(name, VIR_NET_GENERATED_MACVTAP_PREFIX))
+return VIR_NET_DEV_GEN_NAME_MACVTAP;
+else if (STRPREFIX(name, VIR_NET_GENERATED_MACVLAN_PREFIX))
+return VIR_NET_DEV_GEN_NAME_MACVLAN;
+else
+return VIR_NET_DEV_GEN_NAME_NONE;
 }
 
 
@@ -113,9 +93,7 @@ virNetDevMacVLanReserveNameInternal(const char *name)
 void
 virNetDevMacVLanReserveName(const char *name)
 {
-virMutexLock(&virNetDevMacVLanCreateMutex);
-virNetDevMacVLanReserveNameInternal(name);
-virMutexUnlock(&virNetDevMacVLanCreateMutex);
+virNetDevReserveName(name, virNetDevMacVLanGetTypeByName(name), true);
 }
 
 
@@ -136,50 +114,7 @@ virNetDevMacVLanReserveName(const char *name)
 static int
 virNetDevMacVLanGenerateName(char **ifname, unsigned int flags)
 {
-const char *prefix;
-const char *iftemplate;
-int *lastID;
-int id;
-double maxIDd;
-int maxID = INT_MAX;
-int attempts = 0;
-
-if (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) {
-prefix = VIR_NET_GENERATED_MACVTAP_PREFIX;
-iftemplate = VIR_NET_GENERATED_MACVTAP_PREFIX "%d";
-lastID = &virNetDevMacVTapLastID;
-} else {
-prefix = VIR_NET_GENERATED_MACVLAN_PREFIX;
-iftemplate = VIR_NET_GENERATED_MACVLAN_PREFIX "%d";
-lastID = &virNetDevMacVLanLastID;
-}
-
-maxIDd = pow(10, IFNAMSIZ - 1 - strlen(prefix));
-if (maxIDd <= (double)INT_MAX)
-maxID = (int)maxIDd;
-
-do {
-g_autofree char *try = NULL;
-
-id = ++(*lastID);
-
-/* reset before overflow */
-if (*lastID == maxID)
-*lastID = -1;
-
-try = g_strdup_printf(iftemplate, id);
-
-if (!virNetDevExists(try)) {
-g_free(*ifname);
-*ifname = g_steal_pointer(&try);
-return 0;
-}
-} while (++attempts < 1);
-
-virReportError(VIR_ERR_INTERNAL_ERROR,
-   _("no unused %s names available"),
-   *ifname);
-return -1;
+return virNetDevGenerateName(ifname, VIR_NET_CREATE_TYPE);
 }
 
 
@@ -832,7 +767,7 @@ virNetDevMacVLanCreateWithVPortProfile(const char 
*ifnameRequested,
return -1;
 }
 
-virMutexLock(&virNetDevMacVLanCreateMutex);
+virNetDevLockGenName(VIR_NET_CREATE_TYPE);
 
 if (ifnameRequested) {
 int rc;
@@ -843,7 +778,7 @@ virNetDevMacVLanCreateWithVPortProfile(const char 
*ifnameRequested,
 VIR_INFO("Requested macvtap device name: %s", ifnameRequested);
 
 if ((rc = virNetDevExists(ifnameRequested)) < 0) {
-virMutexUnlock(&virNetDevMacVLanCreateMutex);
+virNetDevUnlockGenName(VIR_NET_CREATE_TYPE);
 return -1;
 }
 
@@ -854,14 +789,16 @@ virNetDevMacVLanCreateWithVPortProfile(const char 
*ifnameRequested,
 virReportSystemError(EEXIST,

[PATCH 1/5] netdev: Introduce several helper functions for generating unique netdev name

2020-12-03 Thread Shi Lei
Extract ReserveName/GenerateName from netdevtap and netdevmacvlan as
common helper functions. Along with them, export Lock/Unlock functions
to protect these processes.

Signed-off-by: Shi Lei 
---
 src/libvirt_private.syms |   4 ++
 src/util/virnetdev.c | 140 +++
 src/util/virnetdev.h |  33 +
 3 files changed, 177 insertions(+)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 2f640ef1..be3148c9 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2551,6 +2551,7 @@ virNetDevDelMulti;
 virNetDevExists;
 virNetDevFeatureTypeFromString;
 virNetDevFeatureTypeToString;
+virNetDevGenerateName;
 virNetDevGetFeatures;
 virNetDevGetIndex;
 virNetDevGetLinkInfo;
@@ -2572,8 +2573,10 @@ virNetDevGetVLanID;
 virNetDevIfStateTypeFromString;
 virNetDevIfStateTypeToString;
 virNetDevIsVirtualFunction;
+virNetDevLockGenName;
 virNetDevPFGetVF;
 virNetDevReadNetConfig;
+virNetDevReserveName;
 virNetDevRunEthernetScript;
 virNetDevRxFilterFree;
 virNetDevRxFilterModeTypeFromString;
@@ -2594,6 +2597,7 @@ virNetDevSetRcvMulti;
 virNetDevSetRootQDisc;
 virNetDevSetupControl;
 virNetDevSysfsFile;
+virNetDevUnlockGenName;
 virNetDevValidateConfig;
 virNetDevVFInterfaceStats;
 
diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
index 5104bbe7..5ff8e35f 100644
--- a/src/util/virnetdev.c
+++ b/src/util/virnetdev.c
@@ -17,6 +17,7 @@
  */
 
 #include 
+#include 
 
 #include "virnetdev.h"
 #include "viralloc.h"
@@ -95,6 +96,22 @@ VIR_LOG_INIT("util.netdev");
 (FEATURE_WORD(blocks, index, field) & FEATURE_FIELD_FLAG(index))
 #endif
 
+VIR_ENUM_IMPL(virNetDevGenNameType,
+  VIR_NET_DEV_GEN_NAME_LAST,
+  "none",
+  "tap",
+  "macvtap",
+  "macvlan",
+);
+
+static virNetDevGenName
+virNetDevGenNames[VIR_NET_DEV_GEN_NAME_LAST] = {
+{0, NULL, VIR_MUTEX_INITIALIZER},
+{-1, VIR_NET_GENERATED_TAP_PREFIX, VIR_MUTEX_INITIALIZER},
+{-1, VIR_NET_GENERATED_MACVTAP_PREFIX, VIR_MUTEX_INITIALIZER},
+{-1, VIR_NET_GENERATED_MACVLAN_PREFIX, VIR_MUTEX_INITIALIZER},
+};
+
 typedef enum {
 VIR_MCAST_TYPE_INDEX_TOKEN,
 VIR_MCAST_TYPE_NAME_TOKEN,
@@ -3516,3 +3533,126 @@ virNetDevSetRootQDisc(const char *ifname,
 
 return 0;
 }
+
+
+/**
+ * virNetDevReserveName:
+ * @name: name of an existing network device
+ * @type: type of the network device
+ * @locked: whether this process is locked by the internal mutex
+ *
+ * Reserve a network device name, so that any new network device
+ * created with an autogenerated name will use a number higher
+ * than the number in the given device name.
+ *
+ * Returns nothing.
+ */
+void
+virNetDevReserveName(const char *name,
+ virNetDevGenNameType type,
+ bool locked)
+{
+unsigned int id;
+const char *idstr = NULL;
+
+if (type && STRPREFIX(name, virNetDevGenNames[type].prefix)) {
+
+VIR_INFO("marking device in use: '%s'", name);
+
+idstr = name + strlen(virNetDevGenNames[type].prefix);
+
+if (virStrToLong_ui(idstr, NULL, 10, &id) >= 0) {
+if (locked)
+virMutexLock(&virNetDevGenNames[type].mutex);
+
+if (virNetDevGenNames[type].lastID < (int)id)
+virNetDevGenNames[type].lastID = id;
+
+if (locked)
+virMutexUnlock(&virNetDevGenNames[type].mutex);
+}
+}
+}
+
+
+/**
+ * virNetDevGenerateName:
+ * @ifname: pointer to pointer to string containing template
+ *
+ * generate a new (currently unused) name for a new network device based
+ * on the templace string in @ifname - replace %d with the reserved id,
+ * and keep trying new values until one is found
+ * that doesn't already exist, or we've tried 1 different
+ * names. Once a usable name is found, replace the template with the
+ * actual name.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int
+virNetDevGenerateName(char **ifname, virNetDevGenNameType type)
+{
+int id;
+const char *prefix = virNetDevGenNames[type].prefix;
+double maxIDd = pow(10, IFNAMSIZ - 1 - strlen(prefix));
+int maxID = INT_MAX;
+int attempts = 0;
+
+if (maxIDd <= (double)INT_MAX)
+maxID = (int)maxIDd;
+
+do {
+g_autofree char *try = NULL;
+
+id = ++virNetDevGenNames[type].lastID;
+
+/* reset before overflow */
+if (virNetDevGenNames[type].lastID >= maxID)
+virNetDevGenNames[type].lastID = -1;
+
+if (*ifname)
+try = g_strdup_printf(*ifname, id);
+else
+try = g_strdup_printf("%s%d", prefix, id);
+
+if (!virNetDevExists(try)) {
+g_free(*ifname);
+*ifname = g_steal_pointer(&try);
+return 0;
+}
+} while (++attempts <

[PATCH 5/5] netdevveth: Simplify virNetDevVethCreate by using virNetDevGenerateName

2020-12-03 Thread Shi Lei
Simplify virNetDevVethCreate by using common GenerateName/ReserveName
functions.

Signed-off-by: Shi Lei 
---
 src/lxc/lxc_process.c|   3 +
 src/util/virnetdevveth.c | 146 +++
 2 files changed, 43 insertions(+), 106 deletions(-)

diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c
index 7e07f49f..5f277a22 100644
--- a/src/lxc/lxc_process.c
+++ b/src/lxc/lxc_process.c
@@ -287,6 +287,9 @@ virLXCProcessSetupInterfaceTap(virDomainDefPtr vm,
 
 VIR_DEBUG("calling vethCreate()");
 parentVeth = net->ifname;
+if (parentVeth)
+virNetDevReserveName(parentVeth, VIR_NET_DEV_GEN_NAME_VETH, true);
+
 if (virNetDevVethCreate(&parentVeth, &containerVeth) < 0)
 return NULL;
 VIR_DEBUG("parentVeth: %s, containerVeth: %s", parentVeth, containerVeth);
diff --git a/src/util/virnetdevveth.c b/src/util/virnetdevveth.c
index b3eee1af..2e3d0c82 100644
--- a/src/util/virnetdevveth.c
+++ b/src/util/virnetdevveth.c
@@ -32,48 +32,6 @@
 
 VIR_LOG_INIT("util.netdevveth");
 
-/* Functions */
-
-virMutex virNetDevVethCreateMutex = VIR_MUTEX_INITIALIZER;
-
-static int virNetDevVethExists(int devNum)
-{
-int ret;
-g_autofree char *path = NULL;
-
-path = g_strdup_printf(SYSFS_NET_DIR "vnet%d/", devNum);
-ret = virFileExists(path) ? 1 : 0;
-VIR_DEBUG("Checked dev vnet%d usage: %d", devNum, ret);
-return ret;
-}
-
-/**
- * virNetDevVethGetFreeNum:
- * @startDev: device number to start at (x in vethx)
- *
- * Looks in /sys/class/net/ to find the first available veth device
- * name.
- *
- * Returns non-negative device number on success or -1 in case of error
- */
-static int virNetDevVethGetFreeNum(int startDev)
-{
-int devNum;
-
-#define MAX_DEV_NUM 65536
-
-for (devNum = startDev; devNum < MAX_DEV_NUM; devNum++) {
-int ret = virNetDevVethExists(devNum);
-if (ret < 0)
-return -1;
-if (ret == 0)
-return devNum;
-}
-
-virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-   _("No free veth devices available"));
-return -1;
-}
 
 /**
  * virNetDevVethCreate:
@@ -102,77 +60,53 @@ static int virNetDevVethGetFreeNum(int startDev)
  */
 int virNetDevVethCreate(char** veth1, char** veth2)
 {
-int ret = -1;
-int vethNum = 0;
-size_t i;
-
-/*
- * We might race with other containers, but this is reasonably
- * unlikely, so don't do too many retries for device creation
- */
-virMutexLock(&virNetDevVethCreateMutex);
-#define MAX_VETH_RETRIES 10
-
-for (i = 0; i < MAX_VETH_RETRIES; i++) {
-g_autofree char *veth1auto = NULL;
-g_autofree char *veth2auto = NULL;
-g_autoptr(virCommand) cmd = NULL;
-
-int status;
-if (!*veth1) {
-int veth1num;
-if ((veth1num = virNetDevVethGetFreeNum(vethNum)) < 0)
-goto cleanup;
-
-veth1auto = g_strdup_printf("vnet%d", veth1num);
-vethNum = veth1num + 1;
-}
-if (!*veth2) {
-int veth2num;
-if ((veth2num = virNetDevVethGetFreeNum(vethNum)) < 0)
-goto cleanup;
+int status;
+g_autofree char *veth1auto = NULL;
+g_autofree char *veth2auto = NULL;
+g_autoptr(virCommand) cmd = NULL;
 
-veth2auto = g_strdup_printf("vnet%d", veth2num);
-vethNum = veth2num + 1;
-}
+virNetDevLockGenName(VIR_NET_DEV_GEN_NAME_VETH);
 
-cmd = virCommandNew("ip");
-virCommandAddArgList(cmd, "link", "add",
- *veth1 ? *veth1 : veth1auto,
- "type", "veth", "peer", "name",
- *veth2 ? *veth2 : veth2auto,
- NULL);
-
-if (virCommandRun(cmd, &status) < 0)
-goto cleanup;
-
-if (status == 0) {
-if (veth1auto) {
-*veth1 = veth1auto;
-veth1auto = NULL;
-}
-if (veth2auto) {
-*veth2 = veth2auto;
-veth2auto = NULL;
-}
-VIR_DEBUG("Create Host: %s guest: %s", *veth1, *veth2);
-ret = 0;
-goto cleanup;
+if (!*veth1) {
+if (virNetDevGenerateName(&veth1auto, VIR_NET_DEV_GEN_NAME_VETH) < 0) {
+virNetDevUnlockGenName(VIR_NET_DEV_GEN_NAME_VETH);
+return -1;
+}
+}
+if (!*veth2) {
+if (virNetDevGenerateName(&veth2auto, VIR_NET_DEV_GEN_NAME_VETH) < 0) {
+virNetDevUnlockGenName(VIR_NET_DEV_GEN_NAME_VETH);
+return -1;
 }
+}
+
+virNetDevUnlockGenName(VIR_NET_DEV_GEN_NAME_VETH);
 
-VIR_DEBUG("Failed to cr

[PATCHv2 2/2] util:veth: Create veth device pair by netlink

2020-11-22 Thread Shi Lei
When netlink is supported, use netlink to create veth device pair
rather than 'ip link' command.

Signed-off-by: Shi Lei 
---
 src/util/virnetdevveth.c | 85 ++--
 1 file changed, 56 insertions(+), 29 deletions(-)

diff --git a/src/util/virnetdevveth.c b/src/util/virnetdevveth.c
index b3eee1af..b4074371 100644
--- a/src/util/virnetdevveth.c
+++ b/src/util/virnetdevveth.c
@@ -27,6 +27,7 @@
 #include "virfile.h"
 #include "virstring.h"
 #include "virnetdev.h"
+#include "virnetlink.h"
 
 #define VIR_FROM_THIS VIR_FROM_NONE
 
@@ -75,6 +76,55 @@ static int virNetDevVethGetFreeNum(int startDev)
 return -1;
 }
 
+#if defined(WITH_LIBNL)
+static int
+virNetDevVethCreateInternal(const char *veth1, const char *veth2, int *status)
+{
+virNetlinkNewLinkData data = { .veth_peer = veth2 };
+
+return virNetlinkNewLink(veth1, "veth", &data, status);
+}
+
+static int
+virNetDevVethDeleteInternal(const char *veth)
+{
+return virNetlinkDelLink(veth, NULL);
+}
+#else
+static int
+virNetDevVethCreateInternal(const char *veth1, const char *veth2, int *status)
+{
+g_autoptr(virCommand) cmd = virCommandNew("ip");
+virCommandAddArgList(cmd, "link", "add", veth1, "type", "veth",
+ "peer", "name", veth2, NULL);
+
+return virCommandRun(cmd, status);
+}
+
+static int
+virNetDevVethDeleteInternal(const char *veth)
+{
+int status;
+g_autoptr(virCommand) cmd = virCommandNewArgList("ip", "link",
+ "del", veth, NULL);
+
+if (virCommandRun(cmd, &status) < 0)
+return -1;
+
+if (status != 0) {
+if (!virNetDevExists(veth)) {
+VIR_DEBUG("Device %s already deleted (by kernel namespace 
cleanup)", veth);
+return 0;
+}
+virReportError(VIR_ERR_INTERNAL_ERROR,
+   _("Failed to delete veth device %s"), veth);
+return -1;
+}
+
+return 0;
+}
+#endif /* WITH_LIBNL */
+
 /**
  * virNetDevVethCreate:
  * @veth1: pointer to name for parent end of veth pair
@@ -100,7 +150,7 @@ static int virNetDevVethGetFreeNum(int startDev)
  *
  * Returns 0 on success or -1 in case of error
  */
-int virNetDevVethCreate(char** veth1, char** veth2)
+int virNetDevVethCreate(char **veth1, char **veth2)
 {
 int ret = -1;
 int vethNum = 0;
@@ -116,9 +166,8 @@ int virNetDevVethCreate(char** veth1, char** veth2)
 for (i = 0; i < MAX_VETH_RETRIES; i++) {
 g_autofree char *veth1auto = NULL;
 g_autofree char *veth2auto = NULL;
-g_autoptr(virCommand) cmd = NULL;
 
-int status;
+int status = 0;
 if (!*veth1) {
 int veth1num;
 if ((veth1num = virNetDevVethGetFreeNum(vethNum)) < 0)
@@ -136,14 +185,9 @@ int virNetDevVethCreate(char** veth1, char** veth2)
 vethNum = veth2num + 1;
 }
 
-cmd = virCommandNew("ip");
-virCommandAddArgList(cmd, "link", "add",
- *veth1 ? *veth1 : veth1auto,
- "type", "veth", "peer", "name",
- *veth2 ? *veth2 : veth2auto,
- NULL);
-
-if (virCommandRun(cmd, &status) < 0)
+if (virNetDevVethCreateInternal(*veth1 ? *veth1 : veth1auto,
+*veth2 ? *veth2 : veth2auto,
+&status) < 0)
 goto cleanup;
 
 if (status == 0) {
@@ -188,22 +232,5 @@ int virNetDevVethCreate(char** veth1, char** veth2)
  */
 int virNetDevVethDelete(const char *veth)
 {
-int status;
-g_autoptr(virCommand) cmd = virCommandNewArgList("ip", "link",
-   "del", veth, NULL);
-
-if (virCommandRun(cmd, &status) < 0)
-return -1;
-
-if (status != 0) {
-if (!virNetDevExists(veth)) {
-VIR_DEBUG("Device %s already deleted (by kernel namespace 
cleanup)", veth);
-return 0;
-}
-virReportError(VIR_ERR_INTERNAL_ERROR,
-   _("Failed to delete veth device %s"), veth);
-return -1;
-}
-
-return 0;
+return virNetDevVethDeleteInternal(veth);
 }
-- 
2.25.1




[PATCHv2 0/2] Use netlink to create veth device pair when netlink is supported

2020-11-22 Thread Shi Lei
V1 here: https://www.redhat.com/archives/libvir-list/2020-November/msg00898.html

Since V1:
* Include  rather than introducing enum VETH_INFO_*
* Have complete functions within an #ifdefs rather than
  putting #ifdefs in function

Shi Lei (2):
  util:netlink: Enable virNetlinkNewLink to support veth
  util:veth: Create veth device pair by netlink

 src/util/virnetdevveth.c | 85 ++--
 src/util/virnetlink.c| 14 +++
 src/util/virnetlink.h|  1 +
 3 files changed, 71 insertions(+), 29 deletions(-)

-- 
2.25.1




[PATCHv2 1/2] util:netlink: Enable virNetlinkNewLink to support veth

2020-11-22 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/util/virnetlink.c | 14 ++
 src/util/virnetlink.h |  1 +
 2 files changed, 15 insertions(+)

diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
index fdd3a6a4..8625b896 100644
--- a/src/util/virnetlink.c
+++ b/src/util/virnetlink.c
@@ -24,6 +24,7 @@
 #include 
 
 #include 
+#include 
 
 #include "virnetlink.h"
 #include "virnetdev.h"
@@ -535,6 +536,19 @@ virNetlinkNewLink(const char *ifname,
 NETLINK_MSG_NEST_END(nl_msg, infodata);
 }
 
+if (STREQ(type, "veth") && extra_args && extra_args->veth_peer) {
+struct nlattr *infoveth = NULL;
+
+NETLINK_MSG_NEST_START(nl_msg, infodata, IFLA_INFO_DATA);
+NETLINK_MSG_NEST_START(nl_msg, infoveth, VETH_INFO_PEER);
+nlmsg_reserve(nl_msg, sizeof(struct ifinfomsg), 0);
+NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME,
+(strlen(extra_args->veth_peer) + 1),
+extra_args->veth_peer);
+NETLINK_MSG_NEST_END(nl_msg, infoveth);
+NETLINK_MSG_NEST_END(nl_msg, infodata);
+}
+
 NETLINK_MSG_NEST_END(nl_msg, linkinfo);
 
 if (extra_args) {
diff --git a/src/util/virnetlink.h b/src/util/virnetlink.h
index 7121eac4..7c4ed202 100644
--- a/src/util/virnetlink.h
+++ b/src/util/virnetlink.h
@@ -84,6 +84,7 @@ struct _virNetlinkNewLinkData {
 const int *ifindex; /* The index for the 'link' device */
 const virMacAddr *mac;  /* The MAC address of the device */
 const uint32_t *macvlan_mode;   /* The mode of macvlan */
+const char *veth_peer;  /* The peer name for veth */
 };
 
 int virNetlinkNewLink(const char *ifname,
-- 
2.25.1




Re: Re: [PATCH 2/2] util:veth: Create veth device pair by netlink

2020-11-22 Thread Shi Lei


>On 11/17/20 1:48 AM, Shi Lei wrote:
>> When netlink is supported, use netlink to create veth device pair
>> rather than 'ip link' command.
>>
>> Signed-off-by: Shi Lei 
>> ---
>>   src/util/virnetdevveth.c | 39 ++-
>>   1 file changed, 30 insertions(+), 9 deletions(-)
>>
>> diff --git a/src/util/virnetdevveth.c b/src/util/virnetdevveth.c
>> index b3eee1af..996bf5dd 100644
>> --- a/src/util/virnetdevveth.c
>> +++ b/src/util/virnetdevveth.c
>> @@ -27,6 +27,7 @@
>>   #include "virfile.h"
>>   #include "virstring.h"
>>   #include "virnetdev.h"
>> +#include "virnetlink.h"
>>  
>>   #define VIR_FROM_THIS VIR_FROM_NONE
>>  
>> @@ -116,7 +117,6 @@ int virNetDevVethCreate(char** veth1, char** veth2)
>>   for (i = 0; i < MAX_VETH_RETRIES; i++) {
>>   g_autofree char *veth1auto = NULL;
>>   g_autofree char *veth2auto = NULL;
>> -    g_autoptr(virCommand) cmd = NULL;
>>  
>>   int status;
>>   if (!*veth1) {
>> @@ -136,15 +136,32 @@ int virNetDevVethCreate(char** veth1, char** veth2)
>>   vethNum = veth2num + 1;
>>   }
>>  
>> -    cmd = virCommandNew("ip");
>> -    virCommandAddArgList(cmd, "link", "add",
>> - *veth1 ? *veth1 : veth1auto,
>> - "type", "veth", "peer", "name",
>> - *veth2 ? *veth2 : veth2auto,
>> - NULL);
>> +#if defined(WITH_LIBNL)
>> +    {
>> +    int error = 0;
>> +    virNetlinkNewLinkData data = {
>> +    .veth_peer = *veth2 ? *veth2 : veth2auto,
>> +    };
>>  
>> -    if (virCommandRun(cmd, &status) < 0)
>> -    goto cleanup;
>> +    status = virNetlinkNewLink(*veth1 ? *veth1 : veth1auto,
>> +   "veth", &data, &error);
>> +    if (status < 0)
>> +    goto cleanup;
>> +    }
>> +#else
>> +    {
>> +    g_autoptr(virCommand) cmd = NULL;
>> +    cmd = virCommandNew("ip");
>> +    virCommandAddArgList(cmd, "link", "add",
>> + *veth1 ? *veth1 : veth1auto,
>> + "type", "veth", "peer", "name",
>> + *veth2 ? *veth2 : veth2auto,
>> + NULL);
>> +
>> +    if (virCommandRun(cmd, &status) < 0)
>> +    goto cleanup;
>> +    }
>> +#endif /* WITH_LIBNL */
>
>
>Although it isn't a hard/fast rule, we generally prefer to have complete
>functions within an #ifdefs rather then putting #ifdefs in the middle of
>a function. Could you put these bits into smaller functions, patterned
>like below, and then call the vir*Internal() functions from the existing
>functions?
>
>(other than that this looks fine, although I haven't actually tried
>*running* it yet :-)) 

Okay. It looks fine.

>
>
>#if defined(WITH_LIBNL)
>
>int
>
>virNetDevVethCreateInternal(char *veth1, char *veth2)
>
>{
>
>     int error;
>
>     virNetlinkNewLinkData data = { .veth_peer = veth2 };
>
>
>     return virNetlinkNewLink(veth1, "veth", &data, &error);
>
>}
>
>
>
>int
>
>virNetDevVethDeleteInternal(char *veth)
>
>{
>
>     return virNetlinkDelLink(veth, NULL);
>
>}
>
>
>
>#else
>
>
>
>int
>
>virNetDevVethCreateInternal(char *veth1, char *veth2)
>
>{
>
>     g_autoptr(virCommand) cmd = virCommandNew("ip");
>
>
>     virCommandAddArgList(cmd, "link", "add", veth1, veth2);
>
>     return virCommandRun(cmd, NULL);
>
>}
>
>
>
>int
>
>virNetDevVethDeleteInternal(char *veth)
>
>{
>
>     int status;
>     g_autoptr(virCommand) cmd = virCommandNewArgList("ip", "link",
>"del", veth, NULL);
>
>     if (virCommandRun(cmd, &status) < 0)
>     return -1;
>
>     if (status != 0) {
>     if (!virNetDevExists(veth)) {
>     VIR_DEBUG("Device %s already deleted (by kernel namespace
>cleanup)", veth);
>     return 0;
>     }
>     virReportError(VIR_ERR_IN

Re: Re: [PATCH 1/2] util:netlink: Enable virNetlinkNewLink to support veth

2020-11-22 Thread Shi Lei


>On 11/17/20 1:48 AM, Shi Lei wrote:
>> Signed-off-by: Shi Lei 
>> ---
>>   src/util/virnetlink.c | 25 +
>>   src/util/virnetlink.h |  1 +
>>   2 files changed, 26 insertions(+)
>>
>> diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
>> index fdd3a6a4..e191f63b 100644
>> --- a/src/util/virnetlink.c
>> +++ b/src/util/virnetlink.c
>> @@ -41,6 +41,18 @@ VIR_LOG_INIT("util.netlink");
>>   #define NETLINK_ACK_TIMEOUT_S  (2*1000)
>>  
>>   #if defined(WITH_LIBNL)
>> +
>> +/*
>> + * VETH_INFO_PEER is defined in libnl, but it isn't exposed.
>> + * We include it just like what iproute2 has done.
>
>This is actually defined as a part of the basic netlink .h files, and is
>available in /usr/include/linux/veth.h. The file in iproute2 is just a
>copy of that file (in include/uapi/linux/veth.h). I'm not sure why they
>did that, possibly so they could build an ip binary containing the "new"
>(at the time) veth support on a machine that was still lacking support
>in the kernel header files?.
>
>
>Anyway, I replaced this open-coded definition of VETH_INFO_* with
>
>#include  and everything built with no problem. And I've
>checked and that file is present in the kernel headers at least as far
>back as kernel-3.18 (and we don't support anything older than that).
>
>
>So, when you send V2 with the change I outlined in my review of Patch
>2/2, could you replace this enum with the proper #include?
>
>
>Thanks! 

Okay. I also don't like introducing that enum.
Thanks for letting me know  :-)

Shi Lei

>
>> + */
>> +enum {
>> +    VETH_INFO_UNSPEC,
>> +    VETH_INFO_PEER,
>> +
>> +    __VETH_INFO_MAX
>> +};
>> +
>>   /* State for a single netlink event handle */
>>   struct virNetlinkEventHandle {
>>   int watch;
>> @@ -535,6 +547,19 @@ virNetlinkNewLink(const char *ifname,
>>   NETLINK_MSG_NEST_END(nl_msg, infodata);
>>   }
>>  
>> +    if (STREQ(type, "veth") && extra_args && extra_args->veth_peer) {
>> +    struct nlattr *infoveth = NULL;
>> +
>> +    NETLINK_MSG_NEST_START(nl_msg, infodata, IFLA_INFO_DATA);
>> +    NETLINK_MSG_NEST_START(nl_msg, infoveth, VETH_INFO_PEER);
>> +    nlmsg_reserve(nl_msg, sizeof(struct ifinfomsg), 0);
>> +    NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME,
>> +    (strlen(extra_args->veth_peer) + 1),
>> +    extra_args->veth_peer);
>> +    NETLINK_MSG_NEST_END(nl_msg, infoveth);
>> +    NETLINK_MSG_NEST_END(nl_msg, infodata);
>> +    }
>> +
>>   NETLINK_MSG_NEST_END(nl_msg, linkinfo);
>>  
>>   if (extra_args) {
>> diff --git a/src/util/virnetlink.h b/src/util/virnetlink.h
>> index 7121eac4..7c4ed202 100644
>> --- a/src/util/virnetlink.h
>> +++ b/src/util/virnetlink.h
>> @@ -84,6 +84,7 @@ struct _virNetlinkNewLinkData {
>>   const int *ifindex; /* The index for the 'link' device */
>>   const virMacAddr *mac;  /* The MAC address of the device */
>>   const uint32_t *macvlan_mode;   /* The mode of macvlan */
>> +    const char *veth_peer;  /* The peer name for veth */
>>   };
>>  
>>   int virNetlinkNewLink(const char *ifname,
>
>



[PATCH 2/2] util:veth: Create veth device pair by netlink

2020-11-16 Thread Shi Lei
When netlink is supported, use netlink to create veth device pair
rather than 'ip link' command.

Signed-off-by: Shi Lei 
---
 src/util/virnetdevveth.c | 39 ++-
 1 file changed, 30 insertions(+), 9 deletions(-)

diff --git a/src/util/virnetdevveth.c b/src/util/virnetdevveth.c
index b3eee1af..996bf5dd 100644
--- a/src/util/virnetdevveth.c
+++ b/src/util/virnetdevveth.c
@@ -27,6 +27,7 @@
 #include "virfile.h"
 #include "virstring.h"
 #include "virnetdev.h"
+#include "virnetlink.h"
 
 #define VIR_FROM_THIS VIR_FROM_NONE
 
@@ -116,7 +117,6 @@ int virNetDevVethCreate(char** veth1, char** veth2)
 for (i = 0; i < MAX_VETH_RETRIES; i++) {
 g_autofree char *veth1auto = NULL;
 g_autofree char *veth2auto = NULL;
-g_autoptr(virCommand) cmd = NULL;
 
 int status;
 if (!*veth1) {
@@ -136,15 +136,32 @@ int virNetDevVethCreate(char** veth1, char** veth2)
 vethNum = veth2num + 1;
 }
 
-cmd = virCommandNew("ip");
-virCommandAddArgList(cmd, "link", "add",
- *veth1 ? *veth1 : veth1auto,
- "type", "veth", "peer", "name",
- *veth2 ? *veth2 : veth2auto,
- NULL);
+#if defined(WITH_LIBNL)
+{
+int error = 0;
+virNetlinkNewLinkData data = {
+.veth_peer = *veth2 ? *veth2 : veth2auto,
+};
 
-if (virCommandRun(cmd, &status) < 0)
-goto cleanup;
+status = virNetlinkNewLink(*veth1 ? *veth1 : veth1auto,
+   "veth", &data, &error);
+if (status < 0)
+goto cleanup;
+}
+#else
+{
+g_autoptr(virCommand) cmd = NULL;
+cmd = virCommandNew("ip");
+virCommandAddArgList(cmd, "link", "add",
+ *veth1 ? *veth1 : veth1auto,
+ "type", "veth", "peer", "name",
+ *veth2 ? *veth2 : veth2auto,
+ NULL);
+
+if (virCommandRun(cmd, &status) < 0)
+goto cleanup;
+}
+#endif /* WITH_LIBNL */
 
 if (status == 0) {
 if (veth1auto) {
@@ -188,6 +205,9 @@ int virNetDevVethCreate(char** veth1, char** veth2)
  */
 int virNetDevVethDelete(const char *veth)
 {
+#if defined(WITH_LIBNL)
+return virNetlinkDelLink(veth, NULL);
+#else
 int status;
 g_autoptr(virCommand) cmd = virCommandNewArgList("ip", "link",
"del", veth, NULL);
@@ -206,4 +226,5 @@ int virNetDevVethDelete(const char *veth)
 }
 
 return 0;
+#endif /* WITH_LIBNL */
 }
-- 
2.25.1




[PATCH 1/2] util:netlink: Enable virNetlinkNewLink to support veth

2020-11-16 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/util/virnetlink.c | 25 +
 src/util/virnetlink.h |  1 +
 2 files changed, 26 insertions(+)

diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
index fdd3a6a4..e191f63b 100644
--- a/src/util/virnetlink.c
+++ b/src/util/virnetlink.c
@@ -41,6 +41,18 @@ VIR_LOG_INIT("util.netlink");
 #define NETLINK_ACK_TIMEOUT_S  (2*1000)
 
 #if defined(WITH_LIBNL)
+
+/*
+ * VETH_INFO_PEER is defined in libnl, but it isn't exposed.
+ * We include it just like what iproute2 has done.
+ */
+enum {
+VETH_INFO_UNSPEC,
+VETH_INFO_PEER,
+
+__VETH_INFO_MAX
+};
+
 /* State for a single netlink event handle */
 struct virNetlinkEventHandle {
 int watch;
@@ -535,6 +547,19 @@ virNetlinkNewLink(const char *ifname,
 NETLINK_MSG_NEST_END(nl_msg, infodata);
 }
 
+if (STREQ(type, "veth") && extra_args && extra_args->veth_peer) {
+struct nlattr *infoveth = NULL;
+
+NETLINK_MSG_NEST_START(nl_msg, infodata, IFLA_INFO_DATA);
+NETLINK_MSG_NEST_START(nl_msg, infoveth, VETH_INFO_PEER);
+nlmsg_reserve(nl_msg, sizeof(struct ifinfomsg), 0);
+NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME,
+(strlen(extra_args->veth_peer) + 1),
+extra_args->veth_peer);
+NETLINK_MSG_NEST_END(nl_msg, infoveth);
+NETLINK_MSG_NEST_END(nl_msg, infodata);
+}
+
 NETLINK_MSG_NEST_END(nl_msg, linkinfo);
 
 if (extra_args) {
diff --git a/src/util/virnetlink.h b/src/util/virnetlink.h
index 7121eac4..7c4ed202 100644
--- a/src/util/virnetlink.h
+++ b/src/util/virnetlink.h
@@ -84,6 +84,7 @@ struct _virNetlinkNewLinkData {
 const int *ifindex; /* The index for the 'link' device */
 const virMacAddr *mac;  /* The MAC address of the device */
 const uint32_t *macvlan_mode;   /* The mode of macvlan */
+const char *veth_peer;  /* The peer name for veth */
 };
 
 int virNetlinkNewLink(const char *ifname,
-- 
2.25.1




[PATCH 0/2] Use netlink to create veth device pair when netlink is

2020-11-16 Thread Shi Lei
When netlink and libnl are supported, use it to create veth device pair.

Shi Lei (2):
  util:netlink: Enable virNetlinkNewLink to support veth
  util:veth: Create veth device pair by netlink

 src/util/virnetdevveth.c | 39 ++-
 src/util/virnetlink.c| 25 +
 src/util/virnetlink.h|  1 +
 3 files changed, 56 insertions(+), 9 deletions(-)

-- 
2.25.1




[RFCv2 36/46] conf: Extract error-checking code from virDomainGraphicsDefParseXMLSpice

2020-09-03 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/conf/domain_conf.c | 46 +-
 1 file changed, 32 insertions(+), 14 deletions(-)

diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index bc42068..b3ec111 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -14431,10 +14431,35 @@ virDomainGraphicsRDPDefParseXMLHook(xmlNodePtr node 
G_GNUC_UNUSED,
 }
 
 
+static int
+virDomainGraphicsSpiceDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED,
+  virDomainGraphicsSpiceDefPtr def,
+  const char *instname G_GNUC_UNUSED,
+  void *parent G_GNUC_UNUSED,
+  void *opaque)
+{
+unsigned int flags = 0;
+if (opaque)
+flags = *((unsigned int *) opaque);
+
+if (def->port == -1 && def->tlsPort == -1) {
+/* Legacy compat syntax, used -1 for auto-port */
+def->autoport = true;
+}
+
+if (def->autoport && (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) {
+def->port = 0;
+def->tlsPort = 0;
+}
+
+return 0;
+}
+
+
 static int
 virDomainGraphicsDefParseXMLSpice(virDomainGraphicsDefPtr def,
   xmlNodePtr node,
-  xmlXPathContextPtr ctxt,
+  xmlXPathContextPtr ctxt G_GNUC_UNUSED,
   unsigned int flags)
 {
 xmlNodePtr cur;
@@ -1,9 +14469,6 @@ 
virDomainGraphicsDefParseXMLSpice(virDomainGraphicsDefPtr def,
 g_autofree char *autoport = virXMLPropString(node, "autoport");
 g_autofree char *defaultMode = virXMLPropString(node, "defaultMode");
 
-if (virDomainGraphicsListensParseXML(def, node, ctxt, flags) < 0)
-return -1;
-
 if (port) {
 if (virStrToLong_i(port, NULL, 10, &def->data.spice.port) < 0) {
 virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -14482,16 +14504,6 @@ 
virDomainGraphicsDefParseXMLSpice(virDomainGraphicsDefPtr def,
 def->data.spice.defaultMode = defaultModeVal;
 }
 
-if (def->data.spice.port == -1 && def->data.spice.tlsPort == -1) {
-/* Legacy compat syntax, used -1 for auto-port */
-def->data.spice.autoport = true;
-}
-
-if (def->data.spice.autoport && (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) {
-def->data.spice.port = 0;
-def->data.spice.tlsPort = 0;
-}
-
 def->data.spice.keymap = virXMLPropString(node, "keymap");
 
 if (virDomainGraphicsAuthDefParseXML(node, &def->data.spice.auth,
@@ -14701,6 +14713,10 @@ 
virDomainGraphicsDefParseXMLSpice(virDomainGraphicsDefPtr def,
 cur = cur->next;
 }
 
+if (virDomainGraphicsSpiceDefParseXMLHook(node, &def->data.spice, NULL,
+  def, &flags) < 0)
+return -1;
+
 return 0;
 }
 
@@ -14808,6 +14824,8 @@ virDomainGraphicsDefParseXML(virDomainXMLOptionPtr 
xmlopt,
 goto error;
 break;
 case VIR_DOMAIN_GRAPHICS_TYPE_SPICE:
+if (virDomainGraphicsListensParseXML(def, node, ctxt, flags) < 0)
+goto error;
 if (virDomainGraphicsDefParseXMLSpice(def, node, ctxt, flags) < 0)
 goto error;
 break;
-- 
2.25.1




[RFCv2 34/46] conf: Generate virDomainGraphicsDesktopDefFormatAttr

2020-09-03 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/conf/domain_conf.c | 8 ++--
 src/conf/domain_conf.h | 2 +-
 2 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index ad129e9..bc42068 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -28177,12 +28177,8 @@ virDomainGraphicsDefFormat(virBufferPtr buf,
 break;
 
 case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP:
-if (def->data.desktop.display)
-virBufferEscapeString(buf, " display='%s'",
-  def->data.desktop.display);
-
-if (def->data.desktop.fullscreen)
-virBufferAddLit(buf, " fullscreen='yes'");
+if (virDomainGraphicsDesktopDefFormatAttr(buf, &def->data.desktop, 
def, NULL) < 0)
+return -1;
 
 break;
 
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index ec08bdd..08af9e1 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1703,7 +1703,7 @@ struct _virDomainGraphicsRDPDef {   /* genparse:withhook, 
genformat:separate */
 char *_listen;  /* xmlattr:listen, formathook */
 };
 
-struct _virDomainGraphicsDesktopDef {   /* genparse */
+struct _virDomainGraphicsDesktopDef {   /* genparse, genformat:separate */
 char *display;  /* xmlattr */
 bool fullscreen;/* xmlattr */
 };
-- 
2.25.1




[RFCv2 24/46] conf: Generate format functions for virDomainGraphicsSDLDef

2020-09-03 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/conf/domain_conf.c | 20 +---
 src/conf/domain_conf.h |  2 +-
 2 files changed, 6 insertions(+), 16 deletions(-)

diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index c1f8847..544b984 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -28266,27 +28266,17 @@ virDomainGraphicsDefFormat(virBufferPtr buf,
 break;
 
 case VIR_DOMAIN_GRAPHICS_TYPE_SDL:
-if (def->data.sdl.display)
-virBufferEscapeString(buf, " display='%s'",
-  def->data.sdl.display);
-
-if (def->data.sdl.xauth)
-virBufferEscapeString(buf, " xauth='%s'",
-  def->data.sdl.xauth);
-if (def->data.sdl.fullscreen)
-virBufferAddLit(buf, " fullscreen='yes'");
+if (virDomainGraphicsSDLDefFormatAttr(buf, &def->data.sdl, def, NULL) 
< 0)
+return -1;
 
-if (!children && def->data.sdl.gl != VIR_TRISTATE_BOOL_ABSENT) {
+if (!children && virDomainGraphicsSDLDefCheckElem(&def->data.sdl, def, 
NULL)) {
 virBufferAddLit(buf, ">\n");
 virBufferAdjustIndent(buf, 2);
 children = true;
 }
 
-if (def->data.sdl.gl != VIR_TRISTATE_BOOL_ABSENT) {
-virBufferAsprintf(buf, "data.sdl.gl));
-virBufferAddLit(buf, "/>\n");
-}
+if (virDomainGraphicsSDLDefFormatElem(buf, &def->data.sdl, def, NULL) 
< 0)
+return -1;
 
 break;
 
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 13d2d4f..e64a284 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1672,7 +1672,7 @@ struct _virDomainGraphicsListenDef {
 bool autoGenerated;
 };
 
-struct _virDomainGraphicsSDLDef {   /* genparse */
+struct _virDomainGraphicsSDLDef {   /* genparse, genformat:separate */
 char *display;  /* xmlattr */
 char *xauth;/* xmlattr */
 bool fullscreen;/* xmlattr */
-- 
2.25.1




[RFCv2 18/46] conf: Generate virNetworkDNSForwarderFormatBuf

2020-09-03 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/conf/network_conf.c | 18 +++---
 src/conf/network_conf.h |  4 ++--
 2 files changed, 5 insertions(+), 17 deletions(-)

diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index 608843d..437c2c6 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -2155,21 +2155,9 @@ virNetworkDNSDefFormat(virBufferPtr buf,
 virBufferAdjustIndent(buf, 2);
 
 for (i = 0; i < def->nfwds; i++) {
-
-virBufferAddLit(buf, "forwarders[i].domain) {
-virBufferEscapeString(buf, " domain='%s'",
-  def->forwarders[i].domain);
-}
-if (VIR_SOCKET_ADDR_VALID(&def->forwarders[i].addr)) {
-g_autofree char *addr = 
virSocketAddrFormat(&def->forwarders[i].addr);
-
-if (!addr)
-return -1;
-
-virBufferAsprintf(buf, " addr='%s'", addr);
-}
-virBufferAddLit(buf, "/>\n");
+if (virNetworkDNSForwarderFormatBuf(buf, "forwarder",
+&def->forwarders[i], def, NULL) < 
0)
+return -1;
 }
 
 for (i = 0; i < def->ntxts; i++) {
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index 881a9f2..84b636e 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -158,9 +158,9 @@ struct _virNetworkDNSHostDef {  /* genparse:withhook, 
genformat */
 
 typedef struct _virNetworkDNSForwarder virNetworkDNSForwarder;
 typedef virNetworkDNSForwarder *virNetworkDNSForwarderPtr;
-struct _virNetworkDNSForwarder {/* genparse:withhook */
-virSocketAddr addr; /* xmlattr */
+struct _virNetworkDNSForwarder {/* genparse:withhook, genformat */
 char *domain;   /* xmlattr */
+virSocketAddr addr; /* xmlattr */
 };
 
 typedef struct _virNetworkDNSDef virNetworkDNSDef;
-- 
2.25.1




[RFCv2 40/46] conf: Generate virDomainGraphicsEGLHeadlessDefFormatElem

2020-09-03 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/conf/domain_conf.c | 7 +++
 src/conf/domain_conf.h | 2 +-
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index f3b59ed..0802c45 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -27928,10 +27928,9 @@ virDomainGraphicsDefFormat(virBufferPtr buf,
 children = true;
 }
 
-virBufferAddLit(buf, "data.egl_headless.rendernode);
-virBufferAddLit(buf, "/>\n");
+if (virDomainGraphicsEGLHeadlessDefFormatElem(buf, 
&def->data.egl_headless, def, NULL) < 0)
+return -1;
+
 break;
 case VIR_DOMAIN_GRAPHICS_TYPE_LAST:
 break;
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index baa9166..6273c40 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1744,7 +1744,7 @@ struct _virDomainGraphicsSpiceDef { /* 
genparse:concisehook, genformat:separ
 char *rendernode;   /* xmlattr:gl/rendernode */
 };
 
-struct _virDomainGraphicsEGLHeadlessDef {   /* genparse */
+struct _virDomainGraphicsEGLHeadlessDef {   /* genparse, genformat:separate */
 char *rendernode;   /* xmlattr:gl/rendernode */
 };
 
-- 
2.25.1




[RFCv2 43/46] conf: Generate virDomainGraphicsListenDefFormatBuf

2020-09-03 Thread Shi Lei
Signed-off-by: Shi Lei 
---
 src/conf/domain_conf.c | 114 +++--
 src/conf/domain_conf.h |  12 ++---
 2 files changed, 70 insertions(+), 56 deletions(-)

diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 507679b..007ee44 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -27614,19 +27614,64 @@ virDomainTimerDefFormat(virBufferPtr buf,
 }
 
 
-static void
-virDomainGraphicsListenDefFormat(virBufferPtr buf,
- virDomainGraphicsListenDefPtr def,
- unsigned int flags)
+static bool
+virDomainGraphicsListenDefValid(const virDomainGraphicsListenDef *def,
+const virDomainGraphicsDef *graphics,
+unsigned int flags)
 {
-/* If generating migratable XML, skip listen address
- * dragged in from config file */
-if ((flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE) && def->fromConfig)
-return;
+if (flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE) {
+/* If the listen is based on config options from qemu.conf we need
+ * to skip it.  It's up to user to properly configure both hosts for
+ * migration. */
+if (def->fromConfig)
+return false;
+
+/* If the socket is provided by user in the XML we need to skip this
+ * listen type to support migration back to old libvirt since old
+ * libvirt supports specifying socket path inside graphics element
+ * as 'socket' attribute.  Auto-generated socket is a new feature
+ * thus we can generate it in the migrateble XML. */
+if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
+def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET &&
+def->socket && !def->autoGenerated)
+return false;
+
+/* The new listen type none is in the migratable XML represented as
+ * port=0 and autoport=no because old libvirt support this
+ * configuration for spice. */
+if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE &&
+def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE)
+return false;
+}
+
+return true;
+}
+
+
+bool
+virDomainGraphicsListenDefCheckHook(const virDomainGraphicsListenDef *def,
+const void *parent,
+void *opaque,
+bool value G_GNUC_UNUSED)
+{
+virDomainGraphicsDefPtr graphics = (virDomainGraphicsDefPtr) parent;
+unsigned int flags = 0;
+if (opaque)
+flags = *((unsigned int *) opaque);
+return virDomainGraphicsListenDefValid(def, graphics, flags);
+}
 
-virBufferAddLit(buf, "type));
+
+int
+virDomainGraphicsListenDefFormatHook(const virDomainGraphicsListenDef *def,
+ const void *parent G_GNUC_UNUSED,
+ const void *opaque,
+ virBufferPtr addressBuf,
+ virBufferPtr socketBuf)
+{
+unsigned int flags = 0;
+if (opaque)
+flags = *((unsigned int *) opaque);
 
 if (def->address &&
 (def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS ||
@@ -27634,28 +27679,17 @@ virDomainGraphicsListenDefFormat(virBufferPtr buf,
   !(flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE {
 /* address may also be set to show current status when type='network',
  * but we don't want to print that if INACTIVE data is requested. */
-virBufferAsprintf(buf, " address='%s'", def->address);
-}
-
-if (def->network &&
-(def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK)) {
-virBufferEscapeString(buf, " network='%s'", def->network);
+virBufferAsprintf(addressBuf, " address='%s'", def->address);
 }
 
 if (def->socket &&
 def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET &&
 !(def->autoGenerated &&
   (flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE))) {
-virBufferEscapeString(buf, " socket='%s'", def->socket);
-}
-
-if (flags & VIR_DOMAIN_DEF_FORMAT_STATUS) {
-virBufferAsprintf(buf, " fromConfig='%d'", def->fromConfig);
-virBufferAsprintf(buf, " autoGenerated='%s'",
-  def->autoGenerated ? "yes" : "no");
+virBufferEscapeString(socketBuf, " socket='%s'", def->socket);
 }
 
-virBufferAddLit(buf, "/>\n");
+return 0;
 }
 
 
@@ -27883,37 +27917,17 @@ virDomainGraphicsDefFormat(virBufferPtr buf,
 }
 
 for (i

  1   2   3   4   >