]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
scripts/apibuild: Extract and format API ACLs
authorPeter Krempa <pkrempa@redhat.com>
Tue, 21 Feb 2023 13:20:09 +0000 (14:20 +0100)
committerPeter Krempa <pkrempa@redhat.com>
Mon, 6 Mar 2023 12:09:16 +0000 (13:09 +0100)
As an additional step before processing the API parse the protocol file
and extract all ACL definitions. This way we can distribute them for any
user of the libvirt API XML files. We will be also able to avoid another
call to gendispatch, which generates all this data into a standalone
XML.

The remote procedure to API name is inspired by what rpcgen does.

Signed-off-by: Peter Krempa <pkrempa@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
docs/meson.build
scripts/apibuild.py

index 89ac93a9588413deadcf87e2c48487f1e26f27e9..864abf0ba5241c7a66cf755fb3556737ce501633 100644 (file)
@@ -160,6 +160,9 @@ docs_api_generated = custom_target(
     libvirt_lxc_sources,
     admin_sources,
     util_public_sources,
+    meson.project_source_root() / 'src' / 'remote' / 'remote_protocol.x',
+    meson.project_source_root() / 'src' / 'remote' / 'qemu_protocol.x',
+    meson.project_source_root() / 'src' / 'remote' / 'lxc_protocol.x',
   ],
 )
 
index cced9a555148f7ff2e7f8abc2e3100e751b55215..f532dbe8340e39ab0906ddf469edec922a18c8c3 100755 (executable)
@@ -2588,6 +2588,125 @@ class docBuilder:
             sys.exit(3)
 
 
+def remoteProcToAPI(remotename: str) -> (str):
+    components = remotename.split('_')
+    fixednames = []
+
+    if components[1] != "PROC":
+        raise Exception("Malformed remote function name '%s'" % remotename)
+
+    if components[0] == 'REMOTE':
+        driver = ''
+    elif components[0] == 'QEMU':
+        driver = 'Qemu'
+    elif components[0] == 'LXC':
+        driver = 'Lxc'
+    else:
+        raise Exception("Unknown remote protocol '%s'" % components[0])
+
+    for comp in components[2:]:
+        if comp == '':
+            raise Exception("Invalid empty component in remote procedure name '%s'" % remotename)
+
+        fixedname = comp[0].upper() + comp[1:].lower()
+
+        fixedname = re.sub('Nwfilter', 'NWFilter', fixedname)
+        fixedname = re.sub('Xml$', 'XML', fixedname)
+        fixedname = re.sub('Xml2$', 'XML2', fixedname)
+        fixedname = re.sub('Uri$', 'URI', fixedname)
+        fixedname = re.sub('Uuid$', 'UUID', fixedname)
+        fixedname = re.sub('Id$', 'ID', fixedname)
+        fixedname = re.sub('Mac$', 'MAC', fixedname)
+        fixedname = re.sub('Cpu$', 'CPU', fixedname)
+        fixedname = re.sub('Os$', 'OS', fixedname)
+        fixedname = re.sub('Nmi$', 'NMI', fixedname)
+        fixedname = re.sub('Pm', 'PM', fixedname)
+        fixedname = re.sub('Fstrim$', 'FSTrim', fixedname)
+        fixedname = re.sub('Fsfreeze$', 'FSFreeze', fixedname)
+        fixedname = re.sub('Fsthaw$', 'FSThaw', fixedname)
+        fixedname = re.sub('Fsinfo$', 'FSInfo', fixedname)
+        fixedname = re.sub('Iothread$', 'IOThread', fixedname)
+        fixedname = re.sub('Scsi', 'SCSI', fixedname)
+        fixedname = re.sub('Wwn$', 'WWN', fixedname)
+        fixedname = re.sub('Dhcp$', 'DHCP', fixedname)
+
+        fixednames.append(fixedname)
+
+    apiname = "vir" + fixednames[0]
+
+    # In case of remote procedures for qemu/lxc private APIs we need to add
+    # the name of the driver in the middle of the string after the object name.
+    # For a special case of event callbacks the 'object' name is actually two
+    # words: virConenctDomainQemuEvent ...
+    if fixednames[1] == 'Domain':
+        apiname += 'Domain'
+        fixednames.pop(1)
+
+    apiname += driver
+
+    for name in fixednames[1:]:
+        apiname = apiname + name
+
+    return apiname
+
+
+def remoteProtocolGetAcls(protocolfilename: str) -> {}:
+    apiacls = {}
+
+    with open(protocolfilename) as proto:
+        in_procedures = False
+        acls = []
+        aclfilters = []
+
+        while True:
+            line = proto.readline()
+            if not line:
+                break
+
+            if not in_procedures:
+                if re.match('^enum [a-z]+_procedure {$', line):
+                    in_procedures = True
+
+                continue
+
+            if line == '};\n':
+                break
+
+            acl_match = re.search(r"\* @acl: ([^\s]+)", line)
+
+            if acl_match:
+                acls.append(acl_match.group(1))
+                continue
+
+            aclfilter_match = re.search(r"\* @aclfilter: ([^\s]+)", line)
+
+            if aclfilter_match:
+                aclfilters.append(aclfilter_match.group(1))
+                continue
+
+            remote_proc_match = re.search(r"^\s+([A-Z_0-9]+) ", line)
+
+            if remote_proc_match:
+                proc = remote_proc_match.group(1)
+                apiname = remoteProcToAPI(proc)
+
+                if len(acls) == 0:
+                    raise Exception("No ACLs for procedure %s(%s)" % proc, apiname)
+
+                if 'none' in acls:
+                    if len(acls) > 1:
+                        raise Exception("Procedure %s(%s) has 'none' ACL followed by other ACLs" % proc, apiname)
+
+                    acls = []
+
+                apiacls[apiname] = (acls, aclfilters)
+                acls = []
+                aclfilters = []
+                continue
+
+    return apiacls
+
+
 class app:
     def warning(self, msg):
         global warnings
@@ -2595,16 +2714,27 @@ class app:
         print(msg)
 
     def rebuild(self, name, srcdir, builddir):
+        apiacl = None
+
         syms = {
             "libvirt": srcdir + "/../src/libvirt_public.syms",
             "libvirt-qemu": srcdir + "/../src/libvirt_qemu.syms",
             "libvirt-lxc": srcdir + "/../src/libvirt_lxc.syms",
             "libvirt-admin": srcdir + "/../src/admin/libvirt_admin_public.syms",
         }
-        if name not in syms:
+        protocols = {
+            "libvirt": srcdir + "/../src/remote/remote_protocol.x",
+            "libvirt-qemu": srcdir + "/../src/remote/qemu_protocol.x",
+            "libvirt-lxc": srcdir + "/../src/remote/lxc_protocol.x",
+            "libvirt-admin": None,
+        }
+        if name not in syms or name not in protocols:
             self.warning("rebuild() failed, unknown module %s" % name)
             return None
 
+        if protocols[name]:
+            apiacl = remoteProtocolGetAcls(protocols[name])
+
         builder = None
         if glob.glob(srcdir + "/../src/libvirt.c") != []:
             if not quiet:
@@ -2614,7 +2744,7 @@ class app:
                     srcdir + "/../src/util",
                     srcdir + "/../include/libvirt",
                     builddir + "/../include/libvirt"]
-            builder = docBuilder(name, syms[name], builddir, dirs, [])
+            builder = docBuilder(name, syms[name], builddir, dirs, [], apiacl)
         else:
             self.warning("rebuild() failed, unable to guess the module")
             return None