From 21bca2bb054ed7a1b9b78e01965f8a6d9c3fd28d Mon Sep 17 00:00:00 2001
From: zhongtao <zhongtao17@huawei.com>
Date: Mon, 20 Nov 2023 12:58:26 +0000
Subject: [PATCH 19/65] !2254 lcr container with a damaged config file will
 rebuild the config during restore * lcr container with a damaged config file
 will rebuild the config during restore

---
 src/common/constants.h                        |  2 +
 src/daemon/modules/api/runtime_api.h          |  7 ++
 .../modules/container/restore/restore.c       | 28 ++++--
 .../modules/runtime/engines/lcr/lcr_rt_ops.c  | 99 ++++++++++++++++++-
 .../modules/runtime/engines/lcr/lcr_rt_ops.h  |  1 +
 .../modules/runtime/isula/isula_rt_ops.c      |  6 ++
 .../modules/runtime/isula/isula_rt_ops.h      |  1 +
 src/daemon/modules/runtime/runtime.c          | 24 +++++
 src/daemon/modules/runtime/shim/shim_rt_ops.c |  6 ++
 src/daemon/modules/runtime/shim/shim_rt_ops.h |  2 +
 10 files changed, 167 insertions(+), 9 deletions(-)

diff --git a/src/common/constants.h b/src/common/constants.h
index caf9b793..5f12ae25 100644
--- a/src/common/constants.h
+++ b/src/common/constants.h
@@ -86,6 +86,8 @@ extern "C" {
 
 #define LOG_MAX_RETRIES 10
 
+#define INVALID_CONFIG_ERR_CODE 2
+
 #define MAX_MSG_BUFFER_SIZE (32 * 1024)
 
 #define DEFAULT_WEBSOCKET_SERVER_LISTENING_PORT 10350
diff --git a/src/daemon/modules/api/runtime_api.h b/src/daemon/modules/api/runtime_api.h
index 3c2100f5..08558f42 100644
--- a/src/daemon/modules/api/runtime_api.h
+++ b/src/daemon/modules/api/runtime_api.h
@@ -41,6 +41,7 @@ typedef enum {
 struct runtime_container_status_info {
     bool has_pid;
     uint32_t pid;
+    int error_code;
     Runtime_Container_Status status;
 };
 
@@ -197,6 +198,10 @@ typedef struct _rt_exec_resize_params_t {
     unsigned int width;
 } rt_exec_resize_params_t;
 
+typedef struct _rt_runtime_rebuild_config_params_t {
+    const char *rootpath;
+} rt_rebuild_config_params_t;
+
 struct rt_ops {
     /* detect whether runtime is of this runtime type */
     bool (*detect)(const char *runtime);
@@ -233,6 +238,7 @@ struct rt_ops {
                        rt_listpids_out_t *out);
     int (*rt_resize)(const char *name, const char *runtime, const rt_resize_params_t *params);
     int (*rt_exec_resize)(const char *name, const char *runtime, const rt_exec_resize_params_t *params);
+    int (*rt_rebuild_config)(const char *name, const char *runtime, const rt_rebuild_config_params_t *params);
 };
 
 int runtime_create(const char *name, const char *runtime, const rt_create_params_t *params);
@@ -253,6 +259,7 @@ int runtime_attach(const char *name, const char *runtime, const rt_attach_params
 int runtime_update(const char *name, const char *runtime, const rt_update_params_t *params);
 
 int runtime_listpids(const char *name, const char *runtime, const rt_listpids_params_t *params, rt_listpids_out_t *out);
+int runtime_rebuild_config(const char *name, const char *runtime, const rt_rebuild_config_params_t *params);
 void free_rt_listpids_out_t(rt_listpids_out_t *out);
 int runtime_resize(const char *name, const char *runtime, const rt_resize_params_t *params);
 int runtime_exec_resize(const char *name, const char *runtime, const rt_exec_resize_params_t *params);
diff --git a/src/daemon/modules/container/restore/restore.c b/src/daemon/modules/container/restore/restore.c
index c26cf561..f6218fe6 100644
--- a/src/daemon/modules/container/restore/restore.c
+++ b/src/daemon/modules/container/restore/restore.c
@@ -16,15 +16,18 @@
 #include <stdio.h>
 #include <unistd.h>
 #include <limits.h>
-#include <isula_libutils/container_config_v2.h>
-#include <isula_libutils/host_config.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include <isula_libutils/container_config_v2.h>
+#include <isula_libutils/host_config.h>
+#include <isula_libutils/log.h>
+#include <isula_libutils/auto_cleanup.h>
+
 #include "isulad_config.h"
-#include "isula_libutils/log.h"
+
 #include "container_api.h"
 #include "supervisor.h"
 #include "containers_gc.h"
@@ -276,9 +279,22 @@ static void restore_state(container_t *cont)
 #endif
     nret = runtime_status(id, runtime, &params, &real_status);
     if (nret != 0) {
-        WARN("Failed to restore container %s, make real status to STOPPED. Due to can not load container with status %d",
-             id, status);
-        real_status.status = RUNTIME_CONTAINER_STATUS_STOPPED;
+        bool rebuild_config = (real_status.error_code == INVALID_CONFIG_ERR_CODE);
+        int tempret = -1;
+        // only the lcr container with a damaged config file will rebuild the config
+        if (rebuild_config) {
+            rt_rebuild_config_params_t rebuild_params = { 0 };
+            rebuild_params.rootpath = cont->root_path;
+            nret = runtime_rebuild_config(id, runtime, &rebuild_params);
+            EVENT("Rebuild config for container: %s, result : %d", id, nret);
+            if (nret == 0) {
+                tempret = runtime_status(id, runtime, &params, &real_status);
+            }
+        }
+        if (tempret != 0) {
+            WARN("Failed to restore container %s, make real status to STOPPED. Due to cannot load container with status %d", id, status);
+            real_status.status = RUNTIME_CONTAINER_STATUS_STOPPED;
+        }
     }
 
     if (real_status.status == RUNTIME_CONTAINER_STATUS_STOPPED) {
diff --git a/src/daemon/modules/runtime/engines/lcr/lcr_rt_ops.c b/src/daemon/modules/runtime/engines/lcr/lcr_rt_ops.c
index f61316d0..2f42909b 100644
--- a/src/daemon/modules/runtime/engines/lcr/lcr_rt_ops.c
+++ b/src/daemon/modules/runtime/engines/lcr/lcr_rt_ops.c
@@ -16,15 +16,18 @@
 #include <stdio.h>
 #include <limits.h>
 #include <errno.h>
-#include <isula_libutils/defs.h>
-#include <isula_libutils/host_config.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <strings.h>
 
+#include <isula_libutils/log.h>
+#include <isula_libutils/defs.h>
+#include <isula_libutils/host_config.h>
+#include <isula_libutils/auto_cleanup.h>
+#include <isula_libutils/oci_runtime_spec.h>
+
 #include "lcr_rt_ops.h"
-#include "isula_libutils/log.h"
 #include "engine.h"
 #include "error.h"
 #include "isulad_config.h"
@@ -32,6 +35,8 @@
 #include "runtime_api.h"
 #include "utils_file.h"
 
+#define LCR_CONFIG_FILE "config"
+
 bool rt_lcr_detect(const char *runtime)
 {
     /* now we just support lcr engine */
@@ -276,6 +281,17 @@ int rt_lcr_status(const char *name, const char *runtime, const rt_status_params_
     nret = engine_ops->engine_get_container_status_op(name, params->rootpath, status);
     if (nret != 0) {
         ret = -1;
+        const char *tmpmsg = NULL;
+        if (engine_ops->engine_get_errmsg_op != NULL) {
+            tmpmsg = engine_ops->engine_get_errmsg_op();
+        }
+        if (tmpmsg != NULL && strstr(tmpmsg, "Failed to load config") != NULL) {
+            status->error_code = INVALID_CONFIG_ERR_CODE;
+        }
+        isulad_set_error_message("Runtime state container error: %s",
+                                 (tmpmsg != NULL && strcmp(tmpmsg, DEF_SUCCESS_STR)) != 0 ? tmpmsg : DEF_ERR_RUNTIME_STR);
+        ERROR("Runtime state container error: %s",
+              (tmpmsg != NULL && strcmp(tmpmsg, DEF_SUCCESS_STR)) != 0 ? tmpmsg : DEF_ERR_RUNTIME_STR);
         goto out;
     }
 
@@ -756,3 +772,80 @@ int rt_lcr_kill(const char *id, const char *runtime, const rt_kill_params_t *par
 
     return 0;
 }
+
+int rt_lcr_rebuild_config(const char *name, const char *runtime, const rt_rebuild_config_params_t *params)
+{
+    int ret = -1;
+    int nret = 0;
+    char config_file[PATH_MAX] = { 0 };
+    char bak_config_file[PATH_MAX] = { 0 };
+    char oci_config_file[PATH_MAX] = { 0 };
+    struct engine_operation *engine_ops = NULL;
+    oci_runtime_spec *oci_spec = NULL;
+    __isula_auto_free char *json_container = NULL;
+    __isula_auto_free parser_error err = NULL;
+
+    engine_ops = engines_get_handler(runtime);
+    if (engine_ops == NULL || engine_ops->engine_create_op == NULL) {
+        ERROR("Failed to get engine rebuild config operations");
+        return -1;
+    }
+
+    nret = snprintf(config_file, PATH_MAX, "%s/%s/%s", params->rootpath, name, LCR_CONFIG_FILE);
+    if (nret < 0 || (size_t)nret >= PATH_MAX) {
+        ERROR("Failed to snprintf config file for container %s", name);
+        return -1;
+    }
+
+    nret = snprintf(bak_config_file, PATH_MAX, "%s/%s/%s", params->rootpath, name, ".tmp_config_bak");
+    if (nret < 0 || (size_t)nret >= PATH_MAX) {
+        ERROR("Failed to snprintf bak config file for container %s", name);
+        return -1;
+    }
+
+    nret = snprintf(oci_config_file, sizeof(oci_config_file), "%s/%s/%s", params->rootpath, name, OCI_CONFIG_JSON);
+    if (nret < 0 || (size_t)nret >= sizeof(oci_config_file)) {
+        ERROR("Failed to snprintf for config json");
+        return -1;
+    }
+
+    oci_spec = oci_runtime_spec_parse_file(oci_config_file, NULL, &err);
+    if (oci_spec == NULL) {
+        ERROR("Failed to parse oci config file:%s", err);
+        return -1;
+    }
+
+    // delete the bak config file to prevent the remnants of the previous bak file
+    if (util_fileself_exists(bak_config_file) && util_path_remove(bak_config_file) != 0) {
+        ERROR("Failed to remove bak_config_file for container: %s", name);
+        goto out;
+    }
+
+    if (util_fileself_exists(config_file) && rename(config_file, bak_config_file) != 0) {
+        ERROR("Failed to backup old config for container: %s", name);
+        goto out;
+    }
+
+    nret = engine_ops->engine_create_op(name, params->rootpath, (void *)oci_spec);
+    if (nret != 0) {
+        // delete the invalid config file to prevent rename failed
+        if (util_fileself_exists(config_file) && util_path_remove(config_file) != 0) {
+            WARN("Failed to remove bak_config_file for container %s", name);
+        }
+        if (util_fileself_exists(bak_config_file) && rename(bak_config_file, config_file) != 0) {
+            WARN("Failed to rename backup old config to config for container %s", name);
+        }
+    }
+
+    ret = 0;
+
+out:
+    if (engine_ops != NULL && engine_ops->engine_clear_errmsg_op != NULL) {
+        engine_ops->engine_clear_errmsg_op();
+    }
+    if (util_fileself_exists(bak_config_file) && util_path_remove(bak_config_file) != 0) {
+        WARN("Failed to remove bak_config_file for %s", name);
+    }
+    free_oci_runtime_spec(oci_spec);
+    return ret;
+}
diff --git a/src/daemon/modules/runtime/engines/lcr/lcr_rt_ops.h b/src/daemon/modules/runtime/engines/lcr/lcr_rt_ops.h
index 5b74ad6c..7403544d 100644
--- a/src/daemon/modules/runtime/engines/lcr/lcr_rt_ops.h
+++ b/src/daemon/modules/runtime/engines/lcr/lcr_rt_ops.h
@@ -47,6 +47,7 @@ int rt_lcr_resources_stats(const char *name, const char *runtime, const rt_stats
 int rt_lcr_resize(const char *id, const char *runtime, const rt_resize_params_t *params);
 int rt_lcr_exec_resize(const char *id, const char *runtime, const rt_exec_resize_params_t *params);
 int rt_lcr_kill(const char *id, const char *runtime, const rt_kill_params_t *params);
+int rt_lcr_rebuild_config(const char *name, const char *runtime, const rt_rebuild_config_params_t *params);
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/daemon/modules/runtime/isula/isula_rt_ops.c b/src/daemon/modules/runtime/isula/isula_rt_ops.c
index 1787170b..83214c1a 100644
--- a/src/daemon/modules/runtime/isula/isula_rt_ops.c
+++ b/src/daemon/modules/runtime/isula/isula_rt_ops.c
@@ -2013,3 +2013,9 @@ int rt_isula_kill(const char *id, const char *runtime, const rt_kill_params_t *p
 
     return 0;
 }
+
+// the config file of oci runtime is config.json. If it is damaged, it cannot be rebuilt.
+int rt_isula_rebuild_config(const char *name, const char *runtime, const rt_rebuild_config_params_t *params)
+{
+    return 0;
+}
\ No newline at end of file
diff --git a/src/daemon/modules/runtime/isula/isula_rt_ops.h b/src/daemon/modules/runtime/isula/isula_rt_ops.h
index 49b6cc0e..1e5e049a 100644
--- a/src/daemon/modules/runtime/isula/isula_rt_ops.h
+++ b/src/daemon/modules/runtime/isula/isula_rt_ops.h
@@ -46,6 +46,7 @@ int rt_isula_resources_stats(const char *name, const char *runtime, const rt_sta
 int rt_isula_resize(const char *id, const char *runtime, const rt_resize_params_t *params);
 int rt_isula_exec_resize(const char *id, const char *runtime, const rt_exec_resize_params_t *params);
 int rt_isula_kill(const char *id, const char *runtime, const rt_kill_params_t *params);
+int rt_isula_rebuild_config(const char *name, const char *runtime, const rt_rebuild_config_params_t *params);
 
 #ifdef __cplusplus
 }
diff --git a/src/daemon/modules/runtime/runtime.c b/src/daemon/modules/runtime/runtime.c
index cb383970..d9a332af 100644
--- a/src/daemon/modules/runtime/runtime.c
+++ b/src/daemon/modules/runtime/runtime.c
@@ -45,6 +45,7 @@ static const struct rt_ops g_lcr_rt_ops = {
     .rt_resize = rt_lcr_resize,
     .rt_exec_resize = rt_lcr_exec_resize,
     .rt_kill = rt_lcr_kill,
+    .rt_rebuild_config = rt_lcr_rebuild_config,
 };
 
 static const struct rt_ops g_isula_rt_ops = {
@@ -65,6 +66,7 @@ static const struct rt_ops g_isula_rt_ops = {
     .rt_resize = rt_isula_resize,
     .rt_exec_resize = rt_isula_exec_resize,
     .rt_kill = rt_isula_kill,
+    .rt_rebuild_config = rt_isula_rebuild_config,
 };
 
 #ifdef ENABLE_SHIM_V2
@@ -86,6 +88,7 @@ static const struct rt_ops g_shim_rt_ops = {
     .rt_resize = rt_shim_resize,
     .rt_exec_resize = rt_shim_exec_resize,
     .rt_kill = rt_shim_kill,
+    .rt_rebuild_config = rt_shim_rebuild_config,
 };
 #endif
 
@@ -465,6 +468,27 @@ out:
     return ret;
 }
 
+int runtime_rebuild_config(const char *name, const char *runtime, const rt_rebuild_config_params_t *params)
+{
+    int ret = 0;
+    const struct rt_ops *ops = NULL;
+
+    if (name == NULL || runtime == NULL || params == NULL) {
+        ERROR("Invalid arguments for runtime rebuild config");
+        return -1;
+    }
+
+    ops = rt_ops_query(runtime);
+    if (ops == NULL) {
+        ERROR("Failed to get runtime ops");
+        return -1;
+    }
+
+    ret = ops->rt_rebuild_config(name, runtime, params);
+
+    return ret;
+}
+
 int runtime_resize(const char *name, const char *runtime, const rt_resize_params_t *params)
 {
     int ret = 0;
diff --git a/src/daemon/modules/runtime/shim/shim_rt_ops.c b/src/daemon/modules/runtime/shim/shim_rt_ops.c
index 550b17f3..56fc43c2 100644
--- a/src/daemon/modules/runtime/shim/shim_rt_ops.c
+++ b/src/daemon/modules/runtime/shim/shim_rt_ops.c
@@ -805,3 +805,9 @@ int rt_shim_kill(const char *id, const char *runtime, const rt_kill_params_t *pa
 
     return 0;
 }
+
+// the config file of oci runtime is config.json. If it is damaged, it cannot be rebuilt.
+int rt_shim_rebuild_config(const char *name, const char *runtime, const rt_rebuild_config_params_t *params)
+{
+    return 0;
+}
\ No newline at end of file
diff --git a/src/daemon/modules/runtime/shim/shim_rt_ops.h b/src/daemon/modules/runtime/shim/shim_rt_ops.h
index 03b7c018..2df34f4c 100644
--- a/src/daemon/modules/runtime/shim/shim_rt_ops.h
+++ b/src/daemon/modules/runtime/shim/shim_rt_ops.h
@@ -62,6 +62,8 @@ int rt_shim_exec_resize(const char *id, const char *runtime, const rt_exec_resiz
 
 bool is_valid_v2_runtime(const char* name);
 
+int rt_shim_rebuild_config(const char *name, const char *runtime, const rt_rebuild_config_params_t *params);
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.42.0

