From 3be8bb571d13795c2824dd6d2089035a1be6cf57 Mon Sep 17 00:00:00 2001
From: jiang-dawei15 <jiangdawei15@huawei.com>
Date: Wed, 26 Jan 2022 15:18:10 +0800
Subject: [PATCH] hotpatch: virsh support autoload mode

---
 include/libvirt/libvirt-domain.h |   1 +
 src/qemu/qemu_conf.c             |   9 +++
 src/qemu/qemu_conf.h             |   1 +
 src/qemu/qemu_driver.c           |   5 ++
 src/qemu/qemu_hotpatch.c         | 122 +++++++++++++++++++++++++++++++
 src/qemu/qemu_hotpatch.h         |   3 +
 src/qemu/qemu_process.c          |   6 ++
 tools/virsh-domain.c             |   5 +-
 8 files changed, 150 insertions(+), 2 deletions(-)

diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h
index 2d6432cab2..4ab0c9c0b2 100644
--- a/include/libvirt/libvirt-domain.h
+++ b/include/libvirt/libvirt-domain.h
@@ -4997,6 +4997,7 @@ typedef enum {
     VIR_DOMAIN_HOTPATCH_APPLY,    /* Apply hotpatch */
     VIR_DOMAIN_HOTPATCH_UNAPPLY,  /* Unapply hotpatch */
     VIR_DOMAIN_HOTPATCH_QUERY,    /* Query hotpatch */
+    VIR_DOMAIN_HOTPATCH_AUTOLOAD, /* Autoload hotpatch */
 
 # ifdef VIR_ENUM_SENTINELS
     VIR_DOMAIN_HOTPATCH_LAST
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index 809e8fe526..bd96ccb78e 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -1006,6 +1006,12 @@ virQEMUDriverConfigLoadCapsFiltersEntry(virQEMUDriverConfigPtr cfg,
     return 0;
 }
 
+static int
+virQEMUDriverConfigLoadHotpatchPathEntry(virQEMUDriverConfigPtr cfg,
+                                        virConfPtr conf)
+{
+    return virConfGetValueString(conf, "hotpatch_path", &cfg->hotpatchPath);
+}
 
 int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg,
                                 const char *filename,
@@ -1078,6 +1084,9 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg,
     if (virQEMUDriverConfigLoadCapsFiltersEntry(cfg, conf) < 0)
         return -1;
 
+    if (virQEMUDriverConfigLoadHotpatchPathEntry(cfg, conf) < 0)
+        return -1;
+
     return 0;
 }
 
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
index 14f9b9e81e..f0124a0fe2 100644
--- a/src/qemu/qemu_conf.h
+++ b/src/qemu/qemu_conf.h
@@ -220,6 +220,7 @@ struct _virQEMUDriverConfig {
     gid_t swtpm_group;
 
     char **capabilityfilters;
+    char *hotpatchPath;
 };
 
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(virQEMUDriverConfig, virObjectUnref);
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 2b24881f75..37b2c4a2da 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -23186,6 +23186,7 @@ qemuDomainHotpatchManage(virDomainPtr domain,
     virQEMUDriverPtr driver = domain->conn->privateData;
     char *ret = NULL;
     size_t len;
+    g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
 
     virCheckFlags(0, NULL);
 
@@ -23214,6 +23215,10 @@ qemuDomainHotpatchManage(virDomainPtr domain,
         ret = qemuDomainHotpatchQuery(vm);
         break;
 
+    case VIR_DOMAIN_HOTPATCH_AUTOLOAD:
+        ret = qemuDomainHotpatchAutoload(vm, cfg->hotpatchPath);
+        break;
+
     default:
         virReportError(VIR_ERR_OPERATION_INVALID, "%s",
                        _("Unknow hotpatch action"));
diff --git a/src/qemu/qemu_hotpatch.c b/src/qemu/qemu_hotpatch.c
index 03e63c1341..02f511cc38 100644
--- a/src/qemu/qemu_hotpatch.c
+++ b/src/qemu/qemu_hotpatch.c
@@ -25,6 +25,8 @@
 #include "virerror.h"
 #include "virfile.h"
 #include "virlog.h"
+#include "virbuffer.h"
+#include "virstring.h"
 #include "vircommand.h"
 #include "qemu/qemu_domain.h"
 #include "qemu_hotpatch.h"
@@ -32,6 +34,7 @@
 #define LIBCARE_CTL "libcare-ctl"
 #define LIBCARE_ERROR_NUMBER 255
 #define MAX_PATCHID_LEN 8
+#define MAX_FILE_SIZE (1024*1024)
 
 #define VIR_FROM_THIS VIR_FROM_QEMU
 
@@ -204,3 +207,122 @@ qemuDomainHotpatchUnapply(virDomainObjPtr vm,
     VIR_FREE(output);
     return NULL;
 }
+
+char *
+qemuDomainHotpatchAutoload(virDomainObjPtr vm, char *hotpatch_path)
+{
+    VIR_AUTOSTRINGLIST applied_patches = NULL;
+    VIR_AUTOSTRINGLIST lines = NULL;
+    g_autofree char *applied_patch = NULL;
+    g_autofree char *libvirtd_conf = NULL;
+    g_autofree char *patch_conf = NULL;
+    g_autofree char *buf = NULL;
+    char *ret = NULL;
+    int i, j, len;
+
+    if (hotpatch_path == NULL) {
+        virReportError(VIR_ERR_INVALID_ARG, "%s",
+                        _("Invalid hotpatch path."));
+        return NULL;
+    }
+
+    /* get hotpatch info from Patch.conf */
+    patch_conf = g_strdup_printf("%s/Patch.conf", hotpatch_path);
+    if ((len = virFileReadAll(patch_conf, MAX_FILE_SIZE, &buf)) < 0) {
+        virReportError(VIR_ERR_INVALID_ARG, "%s",
+                        _("Failed to read Patch.conf file."));
+        return NULL;
+    }
+    if (len > 0)
+        buf[len-1] = '\0';
+
+    lines = virStringSplit(buf, "\n", 0);
+    if (!lines)
+        return NULL;
+
+    /* get domain hotpatch infomation */
+    applied_patch = qemuDomainHotpatchQuery(vm);
+    if (!applied_patch)
+        return NULL;
+
+    applied_patches = virStringSplit(applied_patch, "\n", 0);
+    if (!applied_patches)
+        return NULL;
+
+    /* load all hotpatch which are listed in Patch.conf one by one */
+    for (i = 0; lines[i] != NULL; i++) {
+        VIR_AUTOSTRINGLIST patch_info = NULL;
+        g_autofree char *kpatch_dir = NULL;
+        g_autofree char *file_path = NULL;
+        struct dirent *de;
+        DIR *dh;
+        int direrr;
+
+        if (!strstr(lines[i], "QEMU-"))
+            continue;
+
+        patch_info = virStringSplit(lines[i], " ", 0);
+        if (!patch_info)
+            continue;
+
+        /* skip already applied patch */
+        if (strstr(applied_patch, patch_info[2]))
+            continue;
+
+        /* get the kpatch file name */
+        kpatch_dir = g_strdup_printf("%s/%s", hotpatch_path, patch_info[1]);
+        if (!kpatch_dir || !virFileExists(kpatch_dir))
+            return NULL;
+
+        if (virDirOpen(&dh, kpatch_dir) < 0)
+            return NULL;
+        if ((direrr = virDirRead(dh, &de, kpatch_dir)) > 0) {
+            GStatBuf sb;
+
+            file_path = g_strdup_printf("%s/%s", kpatch_dir, de->d_name);
+            if (g_lstat(file_path, &sb) < 0) {
+                virReportSystemError(errno, _("Cannot access '%s'"),
+                                    file_path);
+                VIR_DIR_CLOSE(dh);
+                return NULL;
+            }
+        }
+        VIR_DIR_CLOSE(dh);
+
+        if (qemuDomainHotpatchApply(vm, file_path) == NULL) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                            _("failed to apply the hotpatch."));
+            return NULL;
+        }
+    }
+
+    /* unload the hotpatch which are not listed in Patch.conf */
+    for (i = 0; applied_patches[i] != NULL; i++) {
+        const char *patch_id = NULL;
+        bool is_need_unload = true;
+
+        if (!strstr(applied_patches[i], "Patch id"))
+            continue;
+
+        patch_id = strstr(applied_patches[i], ":") + 1;
+        virSkipSpaces(&patch_id);
+
+        for (j = 0; lines[j] != NULL; j++) {
+            if (!strstr(lines[j], "QEMU-"))
+                continue;
+            if (strstr(lines[j], patch_id)) {
+                is_need_unload = false;
+                break;
+            }
+        }
+        if (is_need_unload == true)
+            if (qemuDomainHotpatchUnapply(vm, patch_id) == NULL) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                _("failed to unapply the hotpatch."));
+                return NULL;
+        }
+    }
+
+    ret = g_strdup_printf("Hotpatch autoload successfully.\n");
+    return ret;
+}
diff --git a/src/qemu/qemu_hotpatch.h b/src/qemu/qemu_hotpatch.h
index 4c84a57950..8e0bfe348a 100644
--- a/src/qemu/qemu_hotpatch.h
+++ b/src/qemu/qemu_hotpatch.h
@@ -34,3 +34,6 @@ qemuDomainHotpatchApply(virDomainObjPtr vm,
 char *
 qemuDomainHotpatchUnapply(virDomainObjPtr vm,
                           const char *id);
+
+char *
+qemuDomainHotpatchAutoload(virDomainObjPtr vm, char *path_config);
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 818a72d8f9..24dd9f052c 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -59,6 +59,7 @@
 #include "qemu_firmware.h"
 #include "qemu_backup.h"
 #include "qemu_dbus.h"
+#include "qemu_hotpatch.h"
 
 #include "cpu/cpu.h"
 #include "cpu/cpu_x86.h"
@@ -6684,6 +6685,7 @@ qemuProcessLaunch(virConnectPtr conn,
     g_autoptr(virQEMUDriverConfig) cfg = NULL;
     size_t nnicindexes = 0;
     g_autofree int *nicindexes = NULL;
+    g_autofree char *autoLoadStatus = NULL;
     size_t i;
 
     VIR_DEBUG("conn=%p driver=%p vm=%p name=%s if=%d asyncJob=%d "
@@ -6993,6 +6995,10 @@ qemuProcessLaunch(virConnectPtr conn,
         qemuProcessAutoDestroyAdd(driver, vm, conn) < 0)
         goto cleanup;
 
+    /* Autoload hotpatch */
+    if ((autoLoadStatus = qemuDomainHotpatchAutoload(vm, cfg->hotpatchPath)) == NULL) {
+        VIR_WARN("Failed to autoload the hotpatch for %s.", vm->def->name);
+    }
     ret = 0;
 
  cleanup:
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
index 813be4a0db..b5375ebd3e 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -14344,7 +14344,7 @@ static const vshCmdOptDef opts_hotpatch[] = {
     {.name = "action",
      .type = VSH_OT_DATA,
      .flags = VSH_OFLAG_REQ,
-     .help = N_("hotpatch action, choose from <apply>, <unapply> and <query>")
+     .help = N_("hotpatch action, choose from <apply>, <unapply>, <query> and <autoload>")
     },
     {.name = "patch",
      .type = VSH_OT_STRING,
@@ -14363,7 +14363,8 @@ VIR_ENUM_IMPL(virDomainHotpatchAction,
               "none",
               "apply",
               "unapply",
-              "query");
+              "query",
+              "autoload");
 
 static bool
 cmdHotpatch(vshControl *ctl,
-- 
2.30.0

