From 745497bdc5c5192709ecc7b3edc91a5170f5b30e Mon Sep 17 00:00:00 2001
From: jikai <jikai11@huawei.com>
Date: Fri, 29 Mar 2024 09:33:38 +0000
Subject: [PATCH 40/69] add support for GetContainerEvents

Signed-off-by: jikai <jikai11@huawei.com>
---
 src/daemon/CMakeLists.txt                     |   3 +
 src/daemon/common/cri/v1/v1_cri_helpers.cc    | 205 +++++++++++++++
 src/daemon/common/cri/v1/v1_cri_helpers.h     |   5 +
 .../{entry => common}/cri/v1/v1_naming.cc     |   0
 .../{entry => common}/cri/v1/v1_naming.h      |   0
 src/daemon/config/isulad_config.c             |   1 +
 .../entry/connect/grpc/cri/cri_service.cc     |   3 +-
 .../entry/connect/grpc/cri/cri_service.h      |   1 +
 .../cri/v1/cri_v1_runtime_runtime_service.cc  | 147 ++++++++++-
 .../cri/v1/cri_v1_runtime_runtime_service.h   |  11 +-
 src/daemon/entry/connect/grpc/grpc_service.cc |   6 +-
 .../v1/v1_cri_container_manager_service.cc    | 203 +--------------
 .../cri/v1/v1_cri_container_manager_service.h |  13 -
 .../v1/v1_cri_pod_sandbox_manager_service.cc  |  92 ++++++-
 .../v1/v1_cri_pod_sandbox_manager_service.h   |  10 +-
 .../entry/cri/v1/v1_cri_runtime_service.h     |   4 +-
 .../cri/v1/v1_cri_runtime_service_impl.cc     |  10 +-
 .../cri/v1/v1_cri_runtime_service_impl.h      |   7 +-
 src/daemon/executor/container_cb/execution.c  |  25 ++
 .../executor/container_cb/execution_create.c  |  12 +
 src/daemon/mailbox/CMakeLists.txt             |  11 +
 src/daemon/mailbox/mailbox.c                  | 167 +++++++++++++
 src/daemon/mailbox/mailbox.h                  |  82 ++++++
 src/daemon/mailbox/mailbox_message.c          |  94 +++++++
 src/daemon/mailbox/mailbox_message.h          |  50 ++++
 src/daemon/mailbox/message_queue.c            | 234 ++++++++++++++++++
 src/daemon/mailbox/message_queue.h            |  57 +++++
 src/daemon/mailbox/message_subscriber.c       |  85 +++++++
 src/daemon/mailbox/message_subscriber.h       |  41 +++
 src/daemon/modules/api/container_api.h        |   5 +
 .../modules/container/supervisor/supervisor.c |  18 ++
 src/daemon/sandbox/sandbox.cc                 |   9 +
 src/utils/cutils/blocking_queue.c             | 185 ++++++++++++++
 src/utils/cutils/blocking_queue.h             |  66 +++++
 test/mocks/mailbox_mock.cc                    |  30 +++
 test/mocks/mailbox_mock.h                     |  30 +++
 test/sandbox/controller/shim/CMakeLists.txt   |   1 +
 test/sandbox/sandbox/CMakeLists.txt           |   2 +
 38 files changed, 1681 insertions(+), 244 deletions(-)
 rename src/daemon/{entry => common}/cri/v1/v1_naming.cc (100%)
 rename src/daemon/{entry => common}/cri/v1/v1_naming.h (100%)
 create mode 100644 src/daemon/mailbox/CMakeLists.txt
 create mode 100644 src/daemon/mailbox/mailbox.c
 create mode 100644 src/daemon/mailbox/mailbox.h
 create mode 100644 src/daemon/mailbox/mailbox_message.c
 create mode 100644 src/daemon/mailbox/mailbox_message.h
 create mode 100644 src/daemon/mailbox/message_queue.c
 create mode 100644 src/daemon/mailbox/message_queue.h
 create mode 100644 src/daemon/mailbox/message_subscriber.c
 create mode 100644 src/daemon/mailbox/message_subscriber.h
 create mode 100644 src/utils/cutils/blocking_queue.c
 create mode 100644 src/utils/cutils/blocking_queue.h
 create mode 100644 test/mocks/mailbox_mock.cc
 create mode 100644 test/mocks/mailbox_mock.h

diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt
index d5280c88..29af3dca 100644
--- a/src/daemon/CMakeLists.txt
+++ b/src/daemon/CMakeLists.txt
@@ -3,6 +3,7 @@
 aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} daemon_top_srcs)
 add_subdirectory(executor)
 add_subdirectory(entry)
+add_subdirectory(mailbox)
 add_subdirectory(modules)
 add_subdirectory(config)
 add_subdirectory(common)
@@ -11,6 +12,7 @@ set(local_daemon_srcs
     ${daemon_top_srcs}
     ${EXECUTOR_SRCS}
     ${ENTRY_SRCS}
+    ${MAILBOX_SRCS}
     ${MODULES_SRCS}
     ${CONFIG_SRCS}
     ${DAEMON_COMMON_SRCS}
@@ -20,6 +22,7 @@ set(local_daemon_incs
     ${CMAKE_CURRENT_SOURCE_DIR}
     ${EXECUTOR_INCS}
     ${ENTRY_INCS}
+    ${MAILBOX_INCS}
     ${MODULES_INCS}
     ${CONFIG_INCS}
     ${DAEMON_COMMON_INCS}
diff --git a/src/daemon/common/cri/v1/v1_cri_helpers.cc b/src/daemon/common/cri/v1/v1_cri_helpers.cc
index c57301ce..a3488894 100644
--- a/src/daemon/common/cri/v1/v1_cri_helpers.cc
+++ b/src/daemon/common/cri/v1/v1_cri_helpers.cc
@@ -32,6 +32,7 @@
 #include "service_container_api.h"
 #include "isulad_config.h"
 #include "sha256.h"
+#include "v1_naming.h"
 
 namespace CRIHelpersV1 {
 
@@ -458,4 +459,208 @@ void ApplySandboxSecurityContextToHostConfig(const runtime::v1::LinuxSandboxSecu
     }
 }
 
+void PackContainerImageToStatus(
+    container_inspect *inspect, std::unique_ptr<runtime::v1::ContainerStatus> &contStatus, Errors &error)
+{
+    if (inspect->config == nullptr) {
+        return;
+    }
+
+    if (inspect->config->image != nullptr) {
+        contStatus->mutable_image()->set_image(inspect->config->image);
+    }
+
+    contStatus->set_image_ref(CRIHelpers::ToPullableImageID(inspect->config->image, inspect->config->image_ref));
+}
+
+void UpdateBaseStatusFromInspect(
+    container_inspect *inspect, int64_t &createdAt, int64_t &startedAt, int64_t &finishedAt,
+    std::unique_ptr<runtime::v1::ContainerStatus> &contStatus)
+{
+    runtime::v1::ContainerState state { runtime::v1::CONTAINER_UNKNOWN };
+    std::string reason;
+    std::string message;
+    int32_t exitCode { 0 };
+
+    if (inspect->state == nullptr) {
+        goto pack_status;
+    }
+
+    if (inspect->state->running) {
+        // Container is running
+        state = runtime::v1::CONTAINER_RUNNING;
+    } else {
+        // Container is not running.
+        if (finishedAt != 0) { // Case 1
+            state = runtime::v1::CONTAINER_EXITED;
+            if (inspect->state->exit_code == 0) {
+                reason = "Completed";
+            } else {
+                reason = "Error";
+            }
+        } else if (inspect->state->exit_code != 0) { // Case 2
+            state = runtime::v1::CONTAINER_EXITED;
+            finishedAt = createdAt;
+            startedAt = createdAt;
+            reason = "ContainerCannotRun";
+        } else { // Case 3
+            state = runtime::v1::CONTAINER_CREATED;
+        }
+        if (inspect->state->error != nullptr) {
+            message = inspect->state->error;
+        }
+        exitCode = (int32_t)inspect->state->exit_code;
+    }
+
+pack_status:
+    contStatus->set_exit_code(exitCode);
+    contStatus->set_state(state);
+    contStatus->set_created_at(createdAt);
+    contStatus->set_started_at(startedAt);
+    contStatus->set_finished_at(finishedAt);
+    contStatus->set_reason(reason);
+    contStatus->set_message(message);
+}
+
+void PackLabelsToStatus(container_inspect *inspect,
+                        std::unique_ptr<runtime::v1::ContainerStatus> &contStatus)
+{
+    if (inspect->config == nullptr || inspect->config->labels == nullptr) {
+        return;
+    }
+    CRIHelpers::ExtractLabels(inspect->config->labels, *contStatus->mutable_labels());
+    CRIHelpers::ExtractAnnotations(inspect->config->annotations, *contStatus->mutable_annotations());
+    for (size_t i = 0; i < inspect->config->labels->len; i++) {
+        if (strcmp(inspect->config->labels->keys[i], CRIHelpers::Constants::CONTAINER_LOGPATH_LABEL_KEY.c_str()) == 0) {
+            contStatus->set_log_path(inspect->config->labels->values[i]);
+            break;
+        }
+    }
+}
+
+void ConvertMountsToStatus(container_inspect *inspect,
+                           std::unique_ptr<runtime::v1::ContainerStatus> &contStatus)
+{
+    for (size_t i = 0; i < inspect->mounts_len; i++) {
+        runtime::v1::Mount *mount = contStatus->add_mounts();
+        mount->set_host_path(inspect->mounts[i]->source);
+        mount->set_container_path(inspect->mounts[i]->destination);
+        mount->set_readonly(!inspect->mounts[i]->rw);
+        if (inspect->mounts[i]->propagation == nullptr || strcmp(inspect->mounts[i]->propagation, "rprivate") == 0) {
+            mount->set_propagation(runtime::v1::PROPAGATION_PRIVATE);
+        } else if (strcmp(inspect->mounts[i]->propagation, "rslave") == 0) {
+            mount->set_propagation(runtime::v1::PROPAGATION_HOST_TO_CONTAINER);
+        } else if (strcmp(inspect->mounts[i]->propagation, "rshared") == 0) {
+            mount->set_propagation(runtime::v1::PROPAGATION_BIDIRECTIONAL);
+        }
+        // Note: Can't set SeLinuxRelabel
+    }
+}
+
+void ConvertResourcesToStatus(container_inspect *inspect,
+                              std::unique_ptr<runtime::v1::ContainerStatus> &contStatus)
+{
+    if (inspect->resources == nullptr) {
+        return;
+    }
+    runtime::v1::LinuxContainerResources *resources = contStatus->mutable_resources()->mutable_linux();
+    if (inspect->resources->cpu_shares != 0) {
+        resources->set_cpu_shares(inspect->resources->cpu_shares);
+    }
+    if (inspect->resources->cpu_period != 0) {
+        resources->set_cpu_period(inspect->resources->cpu_period);
+    }
+    if (inspect->resources->cpu_quota != 0) {
+        resources->set_cpu_quota(inspect->resources->cpu_quota);
+    }
+    if (inspect->resources->memory != 0) {
+        resources->set_memory_limit_in_bytes(inspect->resources->memory);
+    }
+    if (inspect->resources->memory_swap != 0) {
+        resources->set_memory_swap_limit_in_bytes(inspect->resources->memory_swap);
+    }
+    for (size_t i = 0; i < inspect->resources->hugetlbs_len; i++) {
+        runtime::v1::HugepageLimit *hugepage = resources->add_hugepage_limits();
+        hugepage->set_page_size(inspect->resources->hugetlbs[i]->page_size);
+        hugepage->set_limit(inspect->resources->hugetlbs[i]->limit);
+    }
+    if (inspect->resources->unified != nullptr) {
+        for (size_t i = 0; i < inspect->resources->unified->len; i++) {
+            auto &resUnified = *(resources->mutable_unified());
+            resUnified[inspect->resources->unified->keys[i]] = inspect->resources->unified->values[i];
+        }
+    }
+}
+
+void ContainerStatusToGRPC(container_inspect *inspect,
+                           std::unique_ptr<runtime::v1::ContainerStatus> &contStatus,
+                           Errors &error)
+{
+    if (inspect->id != nullptr) {
+        contStatus->set_id(inspect->id);
+    }
+
+    int64_t createdAt {};
+    int64_t startedAt {};
+    int64_t finishedAt {};
+    CRIHelpers::GetContainerTimeStamps(inspect, &createdAt, &startedAt, &finishedAt, error);
+    if (error.NotEmpty()) {
+        return;
+    }
+    contStatus->set_created_at(createdAt);
+    contStatus->set_started_at(startedAt);
+    contStatus->set_finished_at(finishedAt);
+
+    PackContainerImageToStatus(inspect, contStatus, error);
+    UpdateBaseStatusFromInspect(inspect, createdAt, startedAt, finishedAt, contStatus);
+    PackLabelsToStatus(inspect, contStatus);
+    CRINamingV1::ParseContainerName(contStatus->annotations(), contStatus->mutable_metadata(), error);
+    if (error.NotEmpty()) {
+        return;
+    }
+    ConvertMountsToStatus(inspect, contStatus);
+    ConvertResourcesToStatus(inspect, contStatus);
+}
+
+std::unique_ptr<runtime::v1::ContainerStatus> GetContainerStatus(service_executor_t *m_cb, const std::string &containerID, Errors &error)
+{
+    if (m_cb == nullptr) {
+        error.SetError("Invalid input arguments: empty service executor");
+        return nullptr;
+    }
+
+    if (containerID.empty()) {
+        error.SetError("Empty container id");
+        return nullptr;
+    }
+
+    std::string realContainerID = CRIHelpers::GetRealContainerOrSandboxID(m_cb, containerID, false, error);
+    if (error.NotEmpty()) {
+        ERROR("Failed to find container id %s: %s", containerID.c_str(), error.GetCMessage());
+        error.Errorf("Failed to find container id %s: %s", containerID.c_str(), error.GetCMessage());
+        return nullptr;
+    }
+
+    container_inspect *inspect = CRIHelpers::InspectContainer(realContainerID, error, false);
+    if (error.NotEmpty()) {
+        return nullptr;
+    }
+    if (inspect == nullptr) {
+        error.SetError("Get null inspect");
+        return nullptr;
+    }
+    using ContainerStatusPtr = std::unique_ptr<runtime::v1::ContainerStatus>;
+    ContainerStatusPtr contStatus(new (std::nothrow) runtime::v1::ContainerStatus);
+    if (contStatus == nullptr) {
+        error.SetError("Out of memory");
+        free_container_inspect(inspect);
+        return nullptr;
+    }
+
+    ContainerStatusToGRPC(inspect, contStatus, error);
+
+    free_container_inspect(inspect);
+    return contStatus;
+}
+
 } // v1 namespace CRIHelpers
diff --git a/src/daemon/common/cri/v1/v1_cri_helpers.h b/src/daemon/common/cri/v1/v1_cri_helpers.h
index b6e6aec6..1578c428 100644
--- a/src/daemon/common/cri/v1/v1_cri_helpers.h
+++ b/src/daemon/common/cri/v1/v1_cri_helpers.h
@@ -27,6 +27,8 @@
 #include "checkpoint_handler.h"
 #include "constants.h"
 #include "errors.h"
+#include "callback.h"
+#include "cstruct_wrapper.h"
 
 namespace CRIHelpersV1 {
 
@@ -78,6 +80,9 @@ std::string CRISandboxerConvert(const std::string &runtime);
 void ApplySandboxSecurityContextToHostConfig(const runtime::v1::LinuxSandboxSecurityContext &context, host_config *hc,
                                              Errors &error);
 
+auto GetContainerStatus(service_executor_t *m_cb, const std::string &containerID, Errors &error)
+-> std::unique_ptr<runtime::v1::ContainerStatus>;
+
 }; // namespace CRIHelpers
 
 #endif // DAEMON_ENTRY_CRI_V1ALPHA_CRI_HELPERS_H
diff --git a/src/daemon/entry/cri/v1/v1_naming.cc b/src/daemon/common/cri/v1/v1_naming.cc
similarity index 100%
rename from src/daemon/entry/cri/v1/v1_naming.cc
rename to src/daemon/common/cri/v1/v1_naming.cc
diff --git a/src/daemon/entry/cri/v1/v1_naming.h b/src/daemon/common/cri/v1/v1_naming.h
similarity index 100%
rename from src/daemon/entry/cri/v1/v1_naming.h
rename to src/daemon/common/cri/v1/v1_naming.h
diff --git a/src/daemon/config/isulad_config.c b/src/daemon/config/isulad_config.c
index 8179558e..778ff921 100644
--- a/src/daemon/config/isulad_config.c
+++ b/src/daemon/config/isulad_config.c
@@ -1760,6 +1760,7 @@ int merge_json_confs_into_global(struct service_arguments *args)
     args->json_confs->cri_sandboxers = tmp_json_confs->cri_sandboxers;
     tmp_json_confs->cri_sandboxers = NULL;
     args->json_confs->enable_cri_v1 = tmp_json_confs->enable_cri_v1;
+    args->json_confs->enable_pod_events = tmp_json_confs->enable_pod_events;
 #endif
 
     args->json_confs->systemd_cgroup = tmp_json_confs->systemd_cgroup;
diff --git a/src/daemon/entry/connect/grpc/cri/cri_service.cc b/src/daemon/entry/connect/grpc/cri/cri_service.cc
index c1986c44..d10a60b5 100644
--- a/src/daemon/entry/connect/grpc/cri/cri_service.cc
+++ b/src/daemon/entry/connect/grpc/cri/cri_service.cc
@@ -89,8 +89,9 @@ int CRIService::Init(const isulad_daemon_configs *config)
 
 #ifdef ENABLE_CRI_API_V1
     m_enableCRIV1 = config->enable_cri_v1;
+    m_enablePodEvents = config->enable_pod_events;
     if (m_enableCRIV1) {
-        m_runtimeV1RuntimeService.Init(m_podSandboxImage, m_pluginManager, err);
+        m_runtimeV1RuntimeService.Init(m_podSandboxImage, m_pluginManager, m_enablePodEvents, err);
         if (err.NotEmpty()) {
             ERROR("Init CRI v1 runtime service failed: %s", err.GetCMessage());
             return -1;
diff --git a/src/daemon/entry/connect/grpc/cri/cri_service.h b/src/daemon/entry/connect/grpc/cri/cri_service.h
index 77b2eb72..041c7c63 100644
--- a/src/daemon/entry/connect/grpc/cri/cri_service.h
+++ b/src/daemon/entry/connect/grpc/cri/cri_service.h
@@ -56,6 +56,7 @@ private:
     std::string m_podSandboxImage;
     std::shared_ptr<Network::PluginManager> m_pluginManager;
     bool m_enableCRIV1;
+    bool m_enablePodEvents;
 };
 
 }
diff --git a/src/daemon/entry/connect/grpc/cri/v1/cri_v1_runtime_runtime_service.cc b/src/daemon/entry/connect/grpc/cri/v1/cri_v1_runtime_runtime_service.cc
index 76e393f3..bc5ab591 100644
--- a/src/daemon/entry/connect/grpc/cri/v1/cri_v1_runtime_runtime_service.cc
+++ b/src/daemon/entry/connect/grpc/cri/v1/cri_v1_runtime_runtime_service.cc
@@ -22,11 +22,37 @@
 #include "callback.h"
 #include "network_plugin.h"
 #include "v1_cri_runtime_service_impl.h"
+#include "mailbox.h"
+#include "mailbox_message.h"
 
 using namespace CRIV1;
 
+static void *cri_container_topic_handler(void *context, void *arg)
+{
+    if (context == nullptr || arg == nullptr) {
+        ERROR("Invalid input arguments");
+        return nullptr;
+    }
+
+    auto v1runtimeService = static_cast<RuntimeV1RuntimeServiceImpl *>(context);
+    auto msg = static_cast<cri_container_message_t *>(arg);
+    return v1runtimeService->GenerateCRIContainerEvent(msg->container_id, msg->sandbox_id,
+                                                       static_cast<runtime::v1::ContainerEventType>(msg->type));
+}
+
+static void cri_container_topic_release(void *arg)
+{
+    if (arg == nullptr) {
+        return;
+    }
+
+    auto resp = static_cast<runtime::v1::ContainerEventResponse *>(arg);
+    delete resp;
+}
+
 void RuntimeV1RuntimeServiceImpl::Init(std::string &podSandboxImage,
-                                       std::shared_ptr<Network::PluginManager> networkPlugin, Errors &err)
+                                       std::shared_ptr<Network::PluginManager> networkPlugin,
+                                       bool enablePodEvents, Errors &err)
 {
     // Assembly implementation for CRIRuntimeServiceImpl
     service_executor_t *cb = get_service_executor();
@@ -36,7 +62,18 @@ void RuntimeV1RuntimeServiceImpl::Init(std::string &podSandboxImage,
         return;
     }
 
-    m_rService = std::unique_ptr<CRIV1::CRIRuntimeService>(new CRIRuntimeServiceImpl(podSandboxImage, cb, networkPlugin));
+    if (enablePodEvents) {
+        if (mailbox_register_topic_handler(MAILBOX_TOPIC_CRI_CONTAINER, cri_container_topic_handler,
+                                           this, cri_container_topic_release, true) != 0) {
+            ERROR("Failed to register container topic handler");
+            err.SetError("Failed to register container topic handler");
+            return;
+        }
+        m_enablePodEvents = enablePodEvents;
+    }
+
+
+    m_rService = std::unique_ptr<CRIV1::CRIRuntimeService>(new CRIRuntimeServiceImpl(podSandboxImage, cb, networkPlugin, m_enablePodEvents));
 }
 
 void RuntimeV1RuntimeServiceImpl::Wait()
@@ -45,6 +82,54 @@ void RuntimeV1RuntimeServiceImpl::Wait()
 
 void RuntimeV1RuntimeServiceImpl::Shutdown()
 {
+    mailbox_unregister_topic_handler(MAILBOX_TOPIC_CRI_CONTAINER);
+}
+
+auto RuntimeV1RuntimeServiceImpl::GenerateCRIContainerEvent(const char *container_id, const char *sandbox_id,
+                                                            runtime::v1::ContainerEventType type) -> runtime::v1::ContainerEventResponse *
+{
+    if (container_id == nullptr || sandbox_id == nullptr) {
+        ERROR("Invalid input arguments");
+        return nullptr;
+    }
+
+    if (type <  runtime::v1::ContainerEventType::CONTAINER_CREATED_EVENT ||
+        type > runtime::v1::ContainerEventType::CONTAINER_DELETED_EVENT) {
+        ERROR("Invalid container event type %d", type);
+        return nullptr;
+    }
+
+    std::string containerID(container_id), sandboxID(sandbox_id);
+    Errors error;
+    runtime::v1::ContainerEventResponse *response = new (std::nothrow) runtime::v1::ContainerEventResponse();
+    if (response == nullptr) {
+        ERROR("Out of memory");
+        return nullptr;
+    }
+
+    runtime::v1::PodSandboxStatusResponse *statusReply = new (std::nothrow) runtime::v1::PodSandboxStatusResponse();
+    if (statusReply == nullptr) {
+        ERROR("Out of memory");
+        delete response;
+        return nullptr;
+    }
+
+    m_rService->PodSandboxStatus(sandboxID, statusReply, error);
+    if (!error.Empty()) {
+        WARN("Object: CRI, Type: Failed to status pod:%s due to %s", sandboxID.c_str(),
+              error.GetMessage().c_str());
+    } else {
+        *(response->mutable_pod_sandbox_status()) = *(statusReply->mutable_status());
+        for (auto &containerStatus : statusReply->containers_statuses()) {
+            *(response->add_containers_statuses()) = containerStatus;
+        }
+    }
+
+    response->set_container_event_type((runtime::v1::ContainerEventType)type);
+    response->set_container_id(containerID);
+    response->set_created_at(util_get_now_time_nanos());
+
+    return response;
 }
 
 grpc::Status RuntimeV1RuntimeServiceImpl::Version(grpc::ServerContext *context,
@@ -398,14 +483,12 @@ grpc::Status RuntimeV1RuntimeServiceImpl::PodSandboxStatus(grpc::ServerContext *
 
     INFO("Event: {Object: CRI, Type: Status Pod: %s}", request->pod_sandbox_id().c_str());
 
-    std::unique_ptr<runtime::v1::PodSandboxStatus> podStatus;
-    podStatus = m_rService->PodSandboxStatus(request->pod_sandbox_id(), error);
-    if (!error.Empty() || !podStatus) {
+    m_rService->PodSandboxStatus(request->pod_sandbox_id(), reply, error);
+    if (!error.Empty()) {
         ERROR("Object: CRI, Type: Failed to status pod:%s due to %s", request->pod_sandbox_id().c_str(),
               error.GetMessage().c_str());
         return grpc::Status(grpc::StatusCode::UNKNOWN, error.GetMessage());
     }
-    *(reply->mutable_status()) = *podStatus;
 
     INFO("Event: {Object: CRI, Type: Statused Pod: %s}", request->pod_sandbox_id().c_str());
 
@@ -657,3 +740,55 @@ RuntimeV1RuntimeServiceImpl::RuntimeConfig(grpc::ServerContext *context,
 
     return grpc::Status::OK;
 }
+
+grpc::Status RuntimeV1RuntimeServiceImpl::GetContainerEvents(grpc::ServerContext *context,
+                                                             const runtime::v1::GetEventsRequest *request,
+                                                             grpc::ServerWriter<runtime::v1::ContainerEventResponse> *writer)
+{
+    Errors error;
+
+    if (context == nullptr || request == nullptr || writer == nullptr) {
+        ERROR("Invalid input arguments");
+        return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, "Invalid input arguments");
+    }
+
+    if (!m_enablePodEvents) {
+        ERROR("Pod events is not enabled");
+        return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Pod events is not enabled");
+    }
+
+    INFO("Event: {Object: CRI, Type: Getting Container Events}");
+
+    __isula_auto_subscriber auto sub = mailbox_subscribe(MAILBOX_TOPIC_CRI_CONTAINER);
+    if (sub == nullptr) {
+        ERROR("Object: CRI, Type: Failed to subscribe container events");
+        return grpc::Status(grpc::StatusCode::UNKNOWN, "Failed to subscribe container events");
+    }
+
+    for (;;) {
+        __isula_auto_mailbox_message mailbox_message *msg = NULL;
+        int ret = message_subscriber_pop(sub, &msg);
+        if (ret == 0) {
+            if (msg == nullptr) {
+                // nullptr response indicates eventqueue being shutdown, not need to unscribe now
+                return grpc::Status(grpc::StatusCode::UNKNOWN, "Event queue is shutdown");
+            }
+            auto *response = static_cast<runtime::v1::ContainerEventResponse *>(msg->data);
+            if (!writer->Write(*response)) {
+                break;
+            }
+        } else if (ret != ETIMEDOUT) {
+            ERROR("Failed to pop message from subscriber");
+            break;
+        }
+        if (context->IsCancelled()) {
+            INFO("Object: CRI, Type: GetContainerEvents is cancelled");
+            break;
+        }
+    }
+
+    mailbox_unsubscribe(MAILBOX_TOPIC_CRI_CONTAINER, sub);
+    INFO("Event: {Object: CRI, Type: Got Container Events}");
+
+    return grpc::Status::OK;
+}
diff --git a/src/daemon/entry/connect/grpc/cri/v1/cri_v1_runtime_runtime_service.h b/src/daemon/entry/connect/grpc/cri/v1/cri_v1_runtime_runtime_service.h
index 52cc6b99..842d1811 100644
--- a/src/daemon/entry/connect/grpc/cri/v1/cri_v1_runtime_runtime_service.h
+++ b/src/daemon/entry/connect/grpc/cri/v1/cri_v1_runtime_runtime_service.h
@@ -26,9 +26,13 @@
 // Implement of runtime RuntimeService
 class RuntimeV1RuntimeServiceImpl : public runtime::v1::RuntimeService::Service {
 public:
-    void Init(std::string &podSandboxImage, std::shared_ptr<Network::PluginManager> networkPlugin, Errors &err);
+    void Init(std::string &podSandboxImage, std::shared_ptr<Network::PluginManager> networkPlugin,
+              bool enablePodEvents, Errors &err);
     void Wait();
     void Shutdown();
+    auto GenerateCRIContainerEvent(const char *container_id, const char *sandbox_id, runtime::v1::ContainerEventType type)
+    -> runtime::v1::ContainerEventResponse *;
+
     grpc::Status Version(grpc::ServerContext *context, const runtime::v1::VersionRequest *request,
                          runtime::v1::VersionResponse *reply) override;
 
@@ -105,8 +109,13 @@ public:
                                const runtime::v1::RuntimeConfigRequest *request,
                                runtime::v1::RuntimeConfigResponse *reply) override;
 
+    grpc::Status GetContainerEvents(grpc::ServerContext *context,
+                                    const runtime::v1::GetEventsRequest *request,
+                                    grpc::ServerWriter<runtime::v1::ContainerEventResponse> *writer) override;
+
 private:
     std::unique_ptr<CRIV1::CRIRuntimeService> m_rService;
+    bool m_enablePodEvents;
 };
 
 #endif // DAEMON_ENTRY_CONNECT_GRPC_CRI_V1_RUNTIME_RUNTIME_SERVICE_H
diff --git a/src/daemon/entry/connect/grpc/grpc_service.cc b/src/daemon/entry/connect/grpc/grpc_service.cc
index 61e284f3..1d8de922 100644
--- a/src/daemon/entry/connect/grpc/grpc_service.cc
+++ b/src/daemon/entry/connect/grpc/grpc_service.cc
@@ -108,11 +108,11 @@ public:
 
     void Shutdown(void)
     {
-        m_server->Shutdown();
-
-        // call CRI to shutdown stream server
+        // call CRI to shutdown stream server, shutdown cri first to notify events thread to exit
         m_criService.Shutdown();
 
+        m_server->Shutdown();
+
         // Shutdown daemon, this operation should remove socket file.
         for (const auto &address : m_socketPath) {
             if (address.find(UNIX_SOCKET_PREFIX) == 0) {
diff --git a/src/daemon/entry/cri/v1/v1_cri_container_manager_service.cc b/src/daemon/entry/cri/v1/v1_cri_container_manager_service.cc
index cac5c0ba..e86dafae 100644
--- a/src/daemon/entry/cri/v1/v1_cri_container_manager_service.cc
+++ b/src/daemon/entry/cri/v1/v1_cri_container_manager_service.cc
@@ -1007,208 +1007,9 @@ cleanup:
     return contStats;
 }
 
-void ContainerManagerService::PackContainerImageToStatus(
-    container_inspect *inspect, std::unique_ptr<runtime::v1::ContainerStatus> &contStatus, Errors &error)
+std::unique_ptr<runtime::v1::ContainerStatus> ContainerManagerService::ContainerStatus(const std::string &containerID, Errors &error)
 {
-    if (inspect->config == nullptr) {
-        return;
-    }
-
-    if (inspect->config->image != nullptr) {
-        contStatus->mutable_image()->set_image(inspect->config->image);
-    }
-
-    contStatus->set_image_ref(CRIHelpers::ToPullableImageID(inspect->config->image, inspect->config->image_ref));
-    return;
-}
-
-void ContainerManagerService::UpdateBaseStatusFromInspect(
-    container_inspect *inspect, int64_t &createdAt, int64_t &startedAt, int64_t &finishedAt,
-    std::unique_ptr<runtime::v1::ContainerStatus> &contStatus)
-{
-    runtime::v1::ContainerState state { runtime::v1::CONTAINER_UNKNOWN };
-    std::string reason;
-    std::string message;
-    int32_t exitCode { 0 };
-
-    if (inspect->state == nullptr) {
-        goto pack_status;
-    }
-
-    if (inspect->state->running) {
-        // Container is running
-        state = runtime::v1::CONTAINER_RUNNING;
-    } else {
-        // Container is not running.
-        if (finishedAt != 0) { // Case 1
-            state = runtime::v1::CONTAINER_EXITED;
-            if (inspect->state->exit_code == 0) {
-                reason = "Completed";
-            } else {
-                reason = "Error";
-            }
-        } else if (inspect->state->exit_code != 0) { // Case 2
-            state = runtime::v1::CONTAINER_EXITED;
-            finishedAt = createdAt;
-            startedAt = createdAt;
-            reason = "ContainerCannotRun";
-        } else { // Case 3
-            state = runtime::v1::CONTAINER_CREATED;
-        }
-        if (inspect->state->oom_killed) {
-            reason = "OOMKilled";
-        }
-        if (inspect->state->error != nullptr) {
-            message = inspect->state->error;
-        }
-        exitCode = (int32_t)inspect->state->exit_code;
-    }
-
-pack_status:
-    contStatus->set_exit_code(exitCode);
-    contStatus->set_state(state);
-    contStatus->set_created_at(createdAt);
-    contStatus->set_started_at(startedAt);
-    contStatus->set_finished_at(finishedAt);
-    contStatus->set_reason(reason);
-    contStatus->set_message(message);
-}
-
-void ContainerManagerService::PackLabelsToStatus(container_inspect *inspect,
-                                                 std::unique_ptr<runtime::v1::ContainerStatus> &contStatus)
-{
-    if (inspect->config == nullptr || inspect->config->labels == nullptr) {
-        return;
-    }
-    CRIHelpers::ExtractLabels(inspect->config->labels, *contStatus->mutable_labels());
-    CRIHelpers::ExtractAnnotations(inspect->config->annotations, *contStatus->mutable_annotations());
-    for (size_t i = 0; i < inspect->config->labels->len; i++) {
-        if (strcmp(inspect->config->labels->keys[i], CRIHelpers::Constants::CONTAINER_LOGPATH_LABEL_KEY.c_str()) == 0) {
-            contStatus->set_log_path(inspect->config->labels->values[i]);
-            break;
-        }
-    }
-}
-
-void ContainerManagerService::ConvertMountsToStatus(container_inspect *inspect,
-                                                    std::unique_ptr<runtime::v1::ContainerStatus> &contStatus)
-{
-    for (size_t i = 0; i < inspect->mounts_len; i++) {
-        runtime::v1::Mount *mount = contStatus->add_mounts();
-        mount->set_host_path(inspect->mounts[i]->source);
-        mount->set_container_path(inspect->mounts[i]->destination);
-        mount->set_readonly(!inspect->mounts[i]->rw);
-        if (inspect->mounts[i]->propagation == nullptr || strcmp(inspect->mounts[i]->propagation, "rprivate") == 0) {
-            mount->set_propagation(runtime::v1::PROPAGATION_PRIVATE);
-        } else if (strcmp(inspect->mounts[i]->propagation, "rslave") == 0) {
-            mount->set_propagation(runtime::v1::PROPAGATION_HOST_TO_CONTAINER);
-        } else if (strcmp(inspect->mounts[i]->propagation, "rshared") == 0) {
-            mount->set_propagation(runtime::v1::PROPAGATION_BIDIRECTIONAL);
-        }
-        // Note: Can't set SeLinuxRelabel
-    }
-}
-
-void ContainerManagerService::ConvertResourcesToStatus(container_inspect *inspect,
-                                                       std::unique_ptr<runtime::v1::ContainerStatus> &contStatus)
-{
-    if (inspect->resources == nullptr) {
-        return;
-    }
-    runtime::v1::LinuxContainerResources *resources = contStatus->mutable_resources()->mutable_linux();
-    if (inspect->resources->cpu_shares != 0) {
-        resources->set_cpu_shares(inspect->resources->cpu_shares);
-    }
-    if (inspect->resources->cpu_period != 0) {
-        resources->set_cpu_period(inspect->resources->cpu_period);
-    }
-    if (inspect->resources->cpu_quota != 0) {
-        resources->set_cpu_quota(inspect->resources->cpu_quota);
-    }
-    if (inspect->resources->memory != 0) {
-        resources->set_memory_limit_in_bytes(inspect->resources->memory);
-    }
-    if (inspect->resources->memory_swap != 0) {
-        resources->set_memory_swap_limit_in_bytes(inspect->resources->memory_swap);
-    }
-    for (size_t i = 0; i < inspect->resources->hugetlbs_len; i++) {
-        runtime::v1::HugepageLimit *hugepage = resources->add_hugepage_limits();
-        hugepage->set_page_size(inspect->resources->hugetlbs[i]->page_size);
-        hugepage->set_limit(inspect->resources->hugetlbs[i]->limit);
-    }
-    if (inspect->resources->unified != nullptr) {
-        for (size_t i = 0; i < inspect->resources->unified->len; i++) {
-            auto &resUnified = *(resources->mutable_unified());
-            resUnified[inspect->resources->unified->keys[i]] = inspect->resources->unified->values[i];
-        }
-    }
-}
-
-void ContainerManagerService::ContainerStatusToGRPC(container_inspect *inspect,
-                                                    std::unique_ptr<runtime::v1::ContainerStatus> &contStatus,
-                                                    Errors &error)
-{
-    if (inspect->id != nullptr) {
-        contStatus->set_id(inspect->id);
-    }
-
-    int64_t createdAt {};
-    int64_t startedAt {};
-    int64_t finishedAt {};
-    CRIHelpers::GetContainerTimeStamps(inspect, &createdAt, &startedAt, &finishedAt, error);
-    if (error.NotEmpty()) {
-        return;
-    }
-    contStatus->set_created_at(createdAt);
-    contStatus->set_started_at(startedAt);
-    contStatus->set_finished_at(finishedAt);
-
-    PackContainerImageToStatus(inspect, contStatus, error);
-    UpdateBaseStatusFromInspect(inspect, createdAt, startedAt, finishedAt, contStatus);
-    PackLabelsToStatus(inspect, contStatus);
-    CRINamingV1::ParseContainerName(contStatus->annotations(), contStatus->mutable_metadata(), error);
-    if (error.NotEmpty()) {
-        return;
-    }
-    ConvertMountsToStatus(inspect, contStatus);
-    ConvertResourcesToStatus(inspect, contStatus);
-}
-
-std::unique_ptr<runtime::v1::ContainerStatus>
-ContainerManagerService::ContainerStatus(const std::string &containerID, Errors &error)
-{
-    if (containerID.empty()) {
-        error.SetError("Empty container id");
-        return nullptr;
-    }
-
-    std::string realContainerID = CRIHelpers::GetRealContainerOrSandboxID(m_cb, containerID, false, error);
-    if (error.NotEmpty()) {
-        ERROR("Failed to find container id %s: %s", containerID.c_str(), error.GetCMessage());
-        error.Errorf("Failed to find container id %s: %s", containerID.c_str(), error.GetCMessage());
-        return nullptr;
-    }
-
-    container_inspect *inspect = CRIHelpers::InspectContainer(realContainerID, error, false);
-    if (error.NotEmpty()) {
-        return nullptr;
-    }
-    if (inspect == nullptr) {
-        error.SetError("Get null inspect");
-        return nullptr;
-    }
-    using ContainerStatusPtr = std::unique_ptr<runtime::v1::ContainerStatus>;
-    ContainerStatusPtr contStatus(new (std::nothrow) runtime::v1::ContainerStatus);
-    if (contStatus == nullptr) {
-        error.SetError("Out of memory");
-        free_container_inspect(inspect);
-        return nullptr;
-    }
-
-    ContainerStatusToGRPC(inspect, contStatus, error);
-
-    free_container_inspect(inspect);
-    return contStatus;
+    return CRIHelpersV1::GetContainerStatus(m_cb, containerID, error);
 }
 
 void ContainerManagerService::UpdateContainerResources(const std::string &containerID,
diff --git a/src/daemon/entry/cri/v1/v1_cri_container_manager_service.h b/src/daemon/entry/cri/v1/v1_cri_container_manager_service.h
index 4e772bda..50f5ed69 100644
--- a/src/daemon/entry/cri/v1/v1_cri_container_manager_service.h
+++ b/src/daemon/entry/cri/v1/v1_cri_container_manager_service.h
@@ -111,19 +111,6 @@ private:
                                            std::unique_ptr<runtime::v1::ContainerStats> &container);
     void SetFsUsage(const imagetool_fs_info *fs_usage, int64_t timestamp,
                     std::unique_ptr<runtime::v1::ContainerStats> &container);
-    void ContainerStatusToGRPC(container_inspect *inspect,
-                               std::unique_ptr<runtime::v1::ContainerStatus> &contStatus, Errors &error);
-    void PackContainerImageToStatus(container_inspect *inspect,
-                                    std::unique_ptr<runtime::v1::ContainerStatus> &contStatus, Errors &error);
-    void UpdateBaseStatusFromInspect(container_inspect *inspect, int64_t &createdAt, int64_t &startedAt,
-                                     int64_t &finishedAt,
-                                     std::unique_ptr<runtime::v1::ContainerStatus> &contStatus);
-    void PackLabelsToStatus(container_inspect *inspect,
-                            std::unique_ptr<runtime::v1::ContainerStatus> &contStatus);
-    void ConvertMountsToStatus(container_inspect *inspect,
-                               std::unique_ptr<runtime::v1::ContainerStatus> &contStatus);
-    void ConvertResourcesToStatus(container_inspect *inspect,
-                                  std::unique_ptr<runtime::v1::ContainerStatus> &contStatus);
     void ExecSyncFromGRPC(const std::string &containerID, const google::protobuf::RepeatedPtrField<std::string> &cmd,
                           int64_t timeout, container_exec_request **request, Errors &error);
     auto ValidateExecRequest(const runtime::v1::ExecRequest &req, Errors &error) -> int;
diff --git a/src/daemon/entry/cri/v1/v1_cri_pod_sandbox_manager_service.cc b/src/daemon/entry/cri/v1/v1_cri_pod_sandbox_manager_service.cc
index f125e714..4291d8a0 100644
--- a/src/daemon/entry/cri/v1/v1_cri_pod_sandbox_manager_service.cc
+++ b/src/daemon/entry/cri/v1/v1_cri_pod_sandbox_manager_service.cc
@@ -36,6 +36,7 @@
 #include "sandbox_manager.h"
 #include "transform.h"
 #include "isulad_config.h"
+#include "mailbox.h"
 
 namespace CRIV1 {
 void PodSandboxManagerService::PrepareSandboxData(const runtime::v1::PodSandboxConfig &config,
@@ -302,6 +303,7 @@ auto PodSandboxManagerService::RunPodSandbox(const runtime::v1::PodSandboxConfig
     std::string jsonCheckpoint;
     std::string network_setting_json;
     runtime::v1::PodSandboxConfig copyConfig = config;
+    cri_container_message_t msg = { 0 };
 
     // Step 1: Parepare sandbox name, runtime and networkMode
     PrepareSandboxData(config, runtimeHandler, sandboxName, runtimeInfo, networkMode, error);
@@ -372,6 +374,11 @@ auto PodSandboxManagerService::RunPodSandbox(const runtime::v1::PodSandboxConfig
         goto cleanup_network;
     }
 
+    msg.container_id = sandbox->GetId().c_str();
+    msg.sandbox_id = sandbox->GetId().c_str();
+    msg.type = CRI_CONTAINER_MESSAGE_TYPE_CREATED;
+    mailbox_publish(MAILBOX_TOPIC_CRI_CONTAINER, &msg);
+
     // Step 10: Save network settings json to disk
     // Update network settings before start sandbox since sandbox container will use the sandbox key
     if (namespace_is_cni(networkMode.c_str())) {
@@ -391,6 +398,9 @@ auto PodSandboxManagerService::RunPodSandbox(const runtime::v1::PodSandboxConfig
         return response_id;
     }
 
+    msg.type = CRI_CONTAINER_MESSAGE_TYPE_STARTED;
+    mailbox_publish(MAILBOX_TOPIC_CRI_CONTAINER, &msg);
+
     return sandbox->GetId();
 
 cleanup_network:
@@ -700,6 +710,13 @@ void PodSandboxManagerService::RemovePodSandbox(const std::string &podSandboxID,
         ERROR("Failed to delete sandbox %s: %s", podSandboxID.c_str(), error.GetCMessage());
     }
 
+    if (error.Empty()) {
+        cri_container_message_t msg = { 0 };
+        msg.container_id = sandbox->GetId().c_str();
+        msg.sandbox_id = sandbox->GetId().c_str();
+        msg.type = CRI_CONTAINER_MESSAGE_TYPE_DELETED;
+        mailbox_publish(MAILBOX_TOPIC_CRI_CONTAINER, &msg);
+    }
 }
 
 auto PodSandboxManagerService::SharesHostNetwork(const container_inspect *inspect) -> runtime::v1::NamespaceMode
@@ -800,10 +817,29 @@ void PodSandboxManagerService::SetSandboxStatusNetwork(std::shared_ptr<sandbox::
     }
 }
 
-std::unique_ptr<runtime::v1::PodSandboxStatus>
-PodSandboxManagerService::PodSandboxStatus(const std::string &podSandboxID, Errors &error)
+void PodSandboxManagerService::GetContainerStatuses(const std::string &podSandboxID,
+                                                    std::vector<std::unique_ptr<runtime::v1::ContainerStatus>> &containerStatuses,
+                                                    std::vector<std::string> &errors) {
+    auto list_response_wrapper = GetContainerListResponse(podSandboxID, errors);
+    if (list_response_wrapper == nullptr) {
+        return;
+    }
+
+    auto list_response = list_response_wrapper->get();
+    // Remove all containers in the sandbox.
+    for (size_t i = 0; i < list_response->containers_len; i++) {
+        Errors stError;
+        containerStatuses.push_back(CRIHelpersV1::GetContainerStatus(m_cb, list_response->containers[i]->id, stError));
+        if (stError.NotEmpty()) {
+            ERROR("Error get container status: %s: %s", list_response->containers[i]->id, stError.GetCMessage());
+            errors.push_back(stError.GetMessage());
+        }
+    }
+}
+
+std::unique_ptr<runtime::v1::PodSandboxStatus> PodSandboxManagerService::GetPodSandboxStatus(const std::string &podSandboxID, Errors &error)
 {
-    std::unique_ptr<runtime::v1::PodSandboxStatus> podStatus(new runtime::v1::PodSandboxStatus);
+    std::unique_ptr<runtime::v1::PodSandboxStatus> podStatus(new (std::nothrow) runtime::v1::PodSandboxStatus);
     if (podStatus == nullptr) {
         ERROR("Out of memory");
         error.SetError("Out of memory");
@@ -831,6 +867,50 @@ PodSandboxManagerService::PodSandboxStatus(const std::string &podSandboxID, Erro
     return podStatus;
 }
 
+void PodSandboxManagerService::PodSandboxStatus(const std::string &podSandboxID,
+                                                runtime::v1::PodSandboxStatusResponse *reply, Errors &error)
+{
+    if (reply == nullptr) {
+        ERROR("Invalid NULL reply");
+        error.SetError("Invalid NULL reply");
+        return;
+    }
+
+ 
+    auto podStatus = GetPodSandboxStatus(podSandboxID, error);
+    if (error.NotEmpty()) {
+        ERROR("Failed to get pod sandbox status: %s", error.GetCMessage());
+        return;
+    }
+
+    auto sandbox = sandbox::SandboxManager::GetInstance()->GetSandbox(podSandboxID);
+    if (sandbox == nullptr) {
+        ERROR("Failed to find sandbox id %s", podSandboxID.c_str());
+        error.Errorf("Failed to find sandbox id %s", podSandboxID.c_str());
+        return;
+    }
+
+    *(reply->mutable_status()) = *podStatus;
+
+
+    if (!m_enablePodEvents) {
+        return;
+    }
+
+    std::vector<std::unique_ptr<runtime::v1::ContainerStatus>> containerStatuses;
+    std::vector<std::string> errors;
+    GetContainerStatuses(sandbox->GetId(), containerStatuses, errors);
+    if (errors.size() != 0) {
+        error.SetAggregate(errors);
+        return;
+    }
+
+    for (auto &containerStatus : containerStatuses) {
+        *(reply->add_containers_statuses()) = *containerStatus;
+    }
+    return;
+}
+
 void PodSandboxManagerService::ListPodSandbox(const runtime::v1::PodSandboxFilter &filter,
                                               std::vector<std::unique_ptr<runtime::v1::PodSandbox>> &pods,
                                               Errors &error)
@@ -944,7 +1024,7 @@ void PodSandboxManagerService::GetPodSandboxNetworkMetrics(const std::string &ne
 void PodSandboxManagerService::PackagePodSandboxStatsAttributes(
     const std::string &id, std::unique_ptr<runtime::v1::PodSandboxStats> &podStatsPtr, Errors &error)
 {
-    auto status = PodSandboxStatus(id, error);
+    auto status = GetPodSandboxStatus(id, error);
     if (error.NotEmpty()) {
         return;
     }
@@ -1111,8 +1191,8 @@ auto PodSandboxManagerService::PodSandboxStats(const std::string &podSandboxID,
     auto &config = sandbox->GetSandboxConfig();
     auto oldStatsRec = sandbox->GetStatsInfo();
 
-    auto status = PodSandboxStatus(sandbox->GetId(), tmpErr);
-    if (error.NotEmpty()) {
+    auto status = GetPodSandboxStatus(sandbox->GetId(), tmpErr);
+    if (tmpErr.NotEmpty()) {
         ERROR("Failed to get podsandbox %s status: %s", sandbox->GetId().c_str(), tmpErr.GetCMessage());
         error.Errorf("Failed to get podsandbox %s status", sandbox->GetId().c_str());
         return nullptr;
diff --git a/src/daemon/entry/cri/v1/v1_cri_pod_sandbox_manager_service.h b/src/daemon/entry/cri/v1/v1_cri_pod_sandbox_manager_service.h
index c3d98b8c..3872c4c9 100644
--- a/src/daemon/entry/cri/v1/v1_cri_pod_sandbox_manager_service.h
+++ b/src/daemon/entry/cri/v1/v1_cri_pod_sandbox_manager_service.h
@@ -38,10 +38,11 @@ namespace CRIV1 {
 class PodSandboxManagerService {
 public:
     PodSandboxManagerService(const std::string &podSandboxImage, service_executor_t *cb,
-                             std::shared_ptr<Network::PluginManager> pluginManager)
+                             std::shared_ptr<Network::PluginManager> pluginManager, bool enablePodEvents)
         : m_podSandboxImage(podSandboxImage)
         , m_cb(cb)
         , m_pluginManager(pluginManager)
+        , m_enablePodEvents(enablePodEvents)
     {
     }
     PodSandboxManagerService(const PodSandboxManagerService &) = delete;
@@ -55,8 +56,7 @@ public:
 
     void RemovePodSandbox(const std::string &podSandboxID, Errors &error);
 
-    auto PodSandboxStatus(const std::string &podSandboxID, Errors &error)
-    -> std::unique_ptr<runtime::v1::PodSandboxStatus>;
+    void PodSandboxStatus(const std::string &podSandboxID, runtime::v1::PodSandboxStatusResponse *reply, Errors &error);
 
     void ListPodSandbox(const runtime::v1::PodSandboxFilter &filter,
                         std::vector<std::unique_ptr<runtime::v1::PodSandbox>> &pods, Errors &error);
@@ -129,6 +129,9 @@ private:
                              std::vector<std::string> &podSandboxIDs, Errors &error);
     void ApplySandboxLinuxOptions(const runtime::v1::LinuxPodSandboxConfig &lc, host_config *hc,
                                   container_config *custom_config, Errors &error);
+    auto GetPodSandboxStatus(const std::string &podSandboxID, Errors &error) -> std::unique_ptr<runtime::v1::PodSandboxStatus>;
+    void GetContainerStatuses(const std::string &podSandboxID, std::vector<std::unique_ptr<runtime::v1::ContainerStatus>> &containerStatuses,
+                              std::vector<std::string> &errors);
 
 private:
     std::string m_podSandboxImage;
@@ -136,6 +139,7 @@ private:
     std::map<std::string, bool> m_networkReady;
     service_executor_t *m_cb { nullptr };
     std::shared_ptr<Network::PluginManager> m_pluginManager { nullptr };
+    bool m_enablePodEvents;
 };
 } // namespace CRI
 
diff --git a/src/daemon/entry/cri/v1/v1_cri_runtime_service.h b/src/daemon/entry/cri/v1/v1_cri_runtime_service.h
index 839f6724..4521e3df 100644
--- a/src/daemon/entry/cri/v1/v1_cri_runtime_service.h
+++ b/src/daemon/entry/cri/v1/v1_cri_runtime_service.h
@@ -70,8 +70,8 @@ public:
 
     virtual void RemovePodSandbox(const std::string &podSandboxID, Errors &error) = 0;
 
-    virtual auto PodSandboxStatus(const std::string &podSandboxID,
-                                  Errors &error) -> std::unique_ptr<runtime::v1::PodSandboxStatus> = 0;
+    virtual void PodSandboxStatus(const std::string &podSandboxID, runtime::v1::PodSandboxStatusResponse *reply,
+                                  Errors &error) = 0;
 
     virtual void ListPodSandbox(const runtime::v1::PodSandboxFilter &filter,
                                 std::vector<std::unique_ptr<runtime::v1::PodSandbox>> &pods, Errors &error) = 0;
diff --git a/src/daemon/entry/cri/v1/v1_cri_runtime_service_impl.cc b/src/daemon/entry/cri/v1/v1_cri_runtime_service_impl.cc
index aa5ae516..7b40e29d 100644
--- a/src/daemon/entry/cri/v1/v1_cri_runtime_service_impl.cc
+++ b/src/daemon/entry/cri/v1/v1_cri_runtime_service_impl.cc
@@ -19,11 +19,12 @@
 
 namespace CRIV1 {
 CRIRuntimeServiceImpl::CRIRuntimeServiceImpl(const std::string &podSandboxImage, service_executor_t *cb,
-                                             std::shared_ptr<Network::PluginManager> pluginManager)
+                                             std::shared_ptr<Network::PluginManager> pluginManager, bool enablePodEvents)
     : m_runtimeVersioner(new RuntimeVersionerService(cb))
     , m_containerManager(new ContainerManagerService(cb))
-    , m_podSandboxManager(new PodSandboxManagerService(podSandboxImage, cb, pluginManager))
+    , m_podSandboxManager(new PodSandboxManagerService(podSandboxImage, cb, pluginManager, enablePodEvents))
     , m_runtimeManager(new RuntimeManagerService(cb, pluginManager))
+    , m_enablePodEvents(enablePodEvents)
 {
 }
 
@@ -124,10 +125,9 @@ void CRIRuntimeServiceImpl::RemovePodSandbox(const std::string &podSandboxID, Er
     m_podSandboxManager->RemovePodSandbox(podSandboxID, error);
 }
 
-auto CRIRuntimeServiceImpl::PodSandboxStatus(const std::string &podSandboxID, Errors &error)
--> std::unique_ptr<runtime::v1::PodSandboxStatus>
+void CRIRuntimeServiceImpl::PodSandboxStatus(const std::string &podSandboxID, runtime::v1::PodSandboxStatusResponse *reply, Errors &error)
 {
-    return m_podSandboxManager->PodSandboxStatus(podSandboxID, error);
+    m_podSandboxManager->PodSandboxStatus(podSandboxID, reply, error);
 }
 
 void CRIRuntimeServiceImpl::ListPodSandbox(const runtime::v1::PodSandboxFilter &filter,
diff --git a/src/daemon/entry/cri/v1/v1_cri_runtime_service_impl.h b/src/daemon/entry/cri/v1/v1_cri_runtime_service_impl.h
index 0a25749f..6ae59bfa 100644
--- a/src/daemon/entry/cri/v1/v1_cri_runtime_service_impl.h
+++ b/src/daemon/entry/cri/v1/v1_cri_runtime_service_impl.h
@@ -26,7 +26,8 @@ namespace CRIV1 {
 class CRIRuntimeServiceImpl : public CRIRuntimeService {
 public:
     CRIRuntimeServiceImpl(const std::string &podSandboxImage, service_executor_t *cb,
-                          std::shared_ptr<Network::PluginManager> pluginManager);
+                          std::shared_ptr<Network::PluginManager> pluginManager,
+                          bool enablePodEvents);
     CRIRuntimeServiceImpl(const CRIRuntimeServiceImpl &) = delete;
     auto operator=(const CRIRuntimeServiceImpl &) -> CRIRuntimeServiceImpl & = delete;
     virtual ~CRIRuntimeServiceImpl() = default;
@@ -72,8 +73,7 @@ public:
 
     void RemovePodSandbox(const std::string &podSandboxID, Errors &error) override;
 
-    auto PodSandboxStatus(const std::string &podSandboxID, Errors &error)
-    -> std::unique_ptr<runtime::v1::PodSandboxStatus> override;
+    void PodSandboxStatus(const std::string &podSandboxID, runtime::v1::PodSandboxStatusResponse *reply, Errors &error) override;
 
     void ListPodSandbox(const runtime::v1::PodSandboxFilter &filter,
                         std::vector<std::unique_ptr<runtime::v1::PodSandbox>> &pods, Errors &error) override;
@@ -103,6 +103,7 @@ protected:
 private:
     std::string m_podSandboxImage;
     std::shared_ptr<Network::PluginManager> m_pluginManager { nullptr };
+    bool m_enablePodEvents;
 };
 } // namespace CRIV1
 #endif // DAEMON_ENTRY_CRI_V1_CRI_RUNTIME_SERVICE_IMPL_H
diff --git a/src/daemon/executor/container_cb/execution.c b/src/daemon/executor/container_cb/execution.c
index 88c6b354..e5c96628 100644
--- a/src/daemon/executor/container_cb/execution.c
+++ b/src/daemon/executor/container_cb/execution.c
@@ -62,6 +62,7 @@
 #include "event_type.h"
 #include "utils_timestamp.h"
 #include "utils_verify.h"
+#include "mailbox.h"
 #ifdef ENABLE_NATIVE_NETWORK
 #include "service_network_api.h"
 
@@ -542,6 +543,9 @@ static int container_start_cb(const container_start_request *request, container_
     container_t *cont = NULL;
     int sync_fd = -1;
     pthread_t thread_id = 0;
+#ifdef ENABLE_CRI_API_V1
+    cri_container_message_t message;
+#endif
 
     DAEMON_CLEAR_ERRMSG();
 
@@ -596,6 +600,15 @@ static int container_start_cb(const container_start_request *request, container_
     EVENT("Event: {Object: %s, Type: Running}", id);
     (void)isulad_monitor_send_container_event(id, START, -1, 0, NULL, NULL);
 
+#ifdef ENABLE_CRI_API_V1
+    if (is_container_in_sandbox(cont->common_config->sandbox_info)) {
+        message.container_id = id;
+        message.sandbox_id = cont->common_config->sandbox_info->id;
+        message.type = CRI_CONTAINER_MESSAGE_TYPE_STARTED;
+        mailbox_publish(MAILBOX_TOPIC_CRI_CONTAINER, &message);
+    }
+#endif
+
 pack_response:
     handle_start_io_thread_by_cc(cc, sync_fd, thread_id);
     delete_daemon_fifos(fifopath, (const char **)fifos);
@@ -1009,6 +1022,9 @@ static int container_delete_cb(const container_delete_request *request, containe
     char *name = NULL;
     char *id = NULL;
     container_t *cont = NULL;
+#ifdef ENABLE_CRI_API_V1
+    cri_container_message_t message;
+#endif
 
     DAEMON_CLEAR_ERRMSG();
     if (request == NULL || response == NULL) {
@@ -1063,6 +1079,15 @@ static int container_delete_cb(const container_delete_request *request, containe
 
     EVENT("Event: {Object: %s, Type: Deleted}", id);
 
+#ifdef ENABLE_CRI_API_V1
+    if (is_container_in_sandbox(cont->common_config->sandbox_info)) {
+        message.container_id = cont->common_config->id;
+        message.sandbox_id = cont->common_config->sandbox_info->id;
+        message.type = CRI_CONTAINER_MESSAGE_TYPE_DELETED;
+        mailbox_publish(MAILBOX_TOPIC_CRI_CONTAINER, &message);
+    }
+#endif
+
 pack_response:
     pack_delete_response(*response, cc, id);
     container_unref(cont);
diff --git a/src/daemon/executor/container_cb/execution_create.c b/src/daemon/executor/container_cb/execution_create.c
index e00afb68..a9102226 100644
--- a/src/daemon/executor/container_cb/execution_create.c
+++ b/src/daemon/executor/container_cb/execution_create.c
@@ -62,6 +62,7 @@
 #include "opt_log.h"
 #include "runtime_api.h"
 #include "id_name_manager.h"
+#include "mailbox.h"
 
 #ifdef ENABLE_CRI_API_V1
 static bool validate_sandbox_info(const container_sandbox_info *sandbox)
@@ -1389,6 +1390,9 @@ int container_create_cb(const container_create_request *request, container_creat
     bool skip_id_name_manage = false;
     bool skip_sandbox_key_manage = false;
     __isula_auto_sysinfo_t sysinfo_t *sysinfo = NULL;
+#ifdef ENABLE_CRI_API_V1
+    cri_container_message_t message;
+#endif
 
     DAEMON_CLEAR_ERRMSG();
 
@@ -1572,6 +1576,14 @@ int container_create_cb(const container_create_request *request, container_creat
 
     EVENT("Event: {Object: %s, Type: Created %s}", id, name);
     (void)isulad_monitor_send_container_event(id, CREATE, -1, 0, NULL, NULL);
+#ifdef ENABLE_CRI_API_V1
+    if (is_container_in_sandbox(request->sandbox)) {
+        message.container_id = id;
+        message.sandbox_id = request->sandbox->id;
+        message.type = CRI_CONTAINER_MESSAGE_TYPE_CREATED;
+        mailbox_publish(MAILBOX_TOPIC_CRI_CONTAINER, &message);
+    }
+#endif
     goto pack_response;
 
 umount_channel:
diff --git a/src/daemon/mailbox/CMakeLists.txt b/src/daemon/mailbox/CMakeLists.txt
new file mode 100644
index 00000000..984f9acb
--- /dev/null
+++ b/src/daemon/mailbox/CMakeLists.txt
@@ -0,0 +1,11 @@
+# get current directory sources files
+aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} mailbox_top_srcs)
+
+set(MAILBOX_SRCS
+    ${mailbox_top_srcs}
+    PARENT_SCOPE
+    )
+set(MAILBOX_INCS
+    ${CMAKE_CURRENT_SOURCE_DIR}
+    PARENT_SCOPE
+    )
\ No newline at end of file
diff --git a/src/daemon/mailbox/mailbox.c b/src/daemon/mailbox/mailbox.c
new file mode 100644
index 00000000..732b91b9
--- /dev/null
+++ b/src/daemon/mailbox/mailbox.c
@@ -0,0 +1,167 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
+ * iSulad licensed under the Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *     http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
+ * PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ * Author: jikai
+ * Create: 2024-03-25
+ * Description: provide common event definition
+ ******************************************************************************/
+
+#include "mailbox.h"
+
+#include <isula_libutils/log.h>
+
+#include "message_queue.h"
+#include "mailbox_message.h"
+#include "message_subscriber.h"
+
+mailbox_topic_handler_t mailbox_topic_handlers[MAILBOX_TOPIC_MAX] = { 0 };
+
+static bool mailbox_topic_valid(mailbox_topic topic) {
+    return topic > MAILBOX_TOPIC_INVALID && topic < MAILBOX_TOPIC_MAX;
+}
+
+static bool mailbox_should_publish(mailbox_topic topic)
+{
+    if (!mailbox_topic_valid(topic)) {
+        ERROR("Invalid topic %d", topic);
+        return false;
+    }
+
+    if (!mailbox_topic_handlers[topic].registered) {
+        return false;
+    }
+
+    if (mailbox_topic_handlers[topic].queue == NULL) {
+        return true;
+    }
+
+    // for async queues, only publish if anyone subscribe
+    return message_queue_have_subscribers(mailbox_topic_handlers[topic].queue);
+}
+
+// only register once when iSulad start, no need to free the queue
+int mailbox_register_topic_handler(mailbox_topic topic, message_generator_t generator, void *context,
+                                   message_release_t release, bool async)
+{
+    if (!mailbox_topic_valid(topic)) {
+        ERROR("Invalid topic %d", topic);
+        return -1;
+    }
+
+    if (generator == NULL) {
+        ERROR("Invalid generator for topic %d", topic);
+        return -1;
+    }
+
+    mailbox_topic_handlers[topic].generator = generator;
+    mailbox_topic_handlers[topic].context = context;
+    mailbox_topic_handlers[topic].release = release;
+    if (async) {
+        mailbox_topic_handlers[topic].queue = message_queue_create(release);
+        if (mailbox_topic_handlers[topic].queue == NULL) {
+            ERROR("Failed to create message queue for topic %d", topic);
+            return -1;
+        }
+    }
+    mailbox_topic_handlers[topic].registered = true;
+    return 0;
+}
+
+// unregister only when iSulad shutdown, no need to free the queue
+void mailbox_unregister_topic_handler(mailbox_topic topic)
+{
+    if (!mailbox_topic_valid(topic)) {
+        ERROR("Invalid topic %d", topic);
+        return;
+    }
+
+    if (mailbox_topic_handlers[topic].queue != NULL) {
+        message_queue_shutdown(mailbox_topic_handlers[topic].queue);
+    }
+    mailbox_topic_handlers[topic].registered = false;
+}
+
+void mailbox_publish(mailbox_topic topic, void *data)
+{
+    if (!mailbox_should_publish(topic)) {
+        return;
+    }
+
+    message_generator_t  generator = mailbox_topic_handlers[topic].generator;
+    void *context = mailbox_topic_handlers[topic].context;
+    message_release_t release = mailbox_topic_handlers[topic].release;
+    message_queue *queue = mailbox_topic_handlers[topic].queue;
+
+    if (generator == NULL) {
+        ERROR("No handler for topic %d", topic);
+        return;
+    }
+
+    void *middle = generator(context, data);
+    if (middle == NULL) {
+        return;
+    }
+
+    if (queue != NULL) {
+        mailbox_message *msg = mailbox_message_create(middle, release);
+        if (msg == NULL) {
+            ERROR("Failed to create mailbox message");
+            if (release) {
+                release(middle);
+            }
+            return;
+        }
+        if (message_queue_publish(queue, msg) != 0) {
+            ERROR("Failed to publish event");
+            mailbox_message_unref(msg);
+            return;
+        }
+    }
+}
+
+message_subscriber *mailbox_subscribe(mailbox_topic topic)
+{
+    if (!mailbox_topic_valid(topic)) {
+        ERROR("Invalid topic %d", topic);
+        return NULL;
+    }
+
+    if (!mailbox_topic_handlers[topic].registered) {
+        ERROR("Handler for topic %d not registered", topic);
+        return NULL;
+    }
+
+    if (mailbox_topic_handlers[topic].queue != NULL) {
+        return message_queue_subscribe(mailbox_topic_handlers[topic].queue,
+                                       mailbox_topic_handlers[topic].release);
+    }
+
+    // For sync queues, there is no need to subscribe, just return
+    return NULL;
+}
+
+void mailbox_unsubscribe(mailbox_topic topic, message_subscriber *sub)
+{
+    if (!mailbox_topic_valid(topic)) {
+        ERROR("Invalid topic %d", topic);
+        return;
+    }
+
+    if (!mailbox_topic_handlers[topic].registered) {
+        ERROR("Handler for topic %d not registered", topic);
+        return;
+    }
+
+    if (mailbox_topic_handlers[topic].queue != NULL) {
+        return message_queue_unsubscribe(mailbox_topic_handlers[topic].queue, sub);
+    }
+
+    return;
+}
diff --git a/src/daemon/mailbox/mailbox.h b/src/daemon/mailbox/mailbox.h
new file mode 100644
index 00000000..1dc2e934
--- /dev/null
+++ b/src/daemon/mailbox/mailbox.h
@@ -0,0 +1,82 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
+ * iSulad licensed under the Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *     http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
+ * PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ * Author: jikai
+ * Create: 2024-03-25
+ * Description: provide common event definition
+ ******************************************************************************/
+
+#ifndef DAEMON_MAILBOX_MAILBOX_H
+#define DAEMON_MAILBOX_MAILBOX_H
+
+#include "daemon_arguments.h"
+#include "blocking_queue.h"
+#include "message_queue.h"
+#include "message_subscriber.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+typedef enum {
+    MAILBOX_TOPIC_INVALID = -1,
+    MAILBOX_TOPIC_CRI_CONTAINER,
+    MAILBOX_TOPIC_MAX
+} mailbox_topic;
+
+// for async message, it generates a true message to publish
+// for sync message, it is the callback function to handle the data to publish
+typedef void *(*message_generator_t)(void *, void *);
+// release function of message generated by generator, if any
+typedef void (*message_release_t)(void *);
+
+typedef struct {
+    // to generate a message
+    message_generator_t generator;
+    // context of handler
+    void *context;
+    // release function of message, if any
+    message_release_t release;
+    // message queue
+    message_queue *queue;
+    // if registered
+    bool registered;
+} mailbox_topic_handler_t;
+
+typedef enum {
+    CRI_CONTAINER_MESSAGE_TYPE_INVALID = -1,
+    CRI_CONTAINER_MESSAGE_TYPE_CREATED,
+    CRI_CONTAINER_MESSAGE_TYPE_STARTED,
+    CRI_CONTAINER_MESSAGE_TYPE_STOPPED,
+    CRI_CONTAINER_MESSAGE_TYPE_DELETED,
+    CRI_CONTAINER_MESSAGE_TYPE_MAX
+} cri_container_message_type;
+
+typedef struct {
+    const char *container_id;
+    const char *sandbox_id;
+    cri_container_message_type type;
+} cri_container_message_t;
+
+int mailbox_register_topic_handler(mailbox_topic topic, message_generator_t handle, void *context,
+                                   message_release_t release, bool async);
+
+void mailbox_unregister_topic_handler(mailbox_topic topic);
+
+void mailbox_publish(mailbox_topic topic, void *data);
+
+message_subscriber *mailbox_subscribe(mailbox_topic topic);
+
+void mailbox_unsubscribe(mailbox_topic, message_subscriber *sub);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/daemon/mailbox/mailbox_message.c b/src/daemon/mailbox/mailbox_message.c
new file mode 100644
index 00000000..b16a1bdd
--- /dev/null
+++ b/src/daemon/mailbox/mailbox_message.c
@@ -0,0 +1,94 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
+ * iSulad licensed under the Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *     http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
+ * PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ * Author: jikai
+ * Create: 2024-03-25
+ * Description: provide mailbox message definition
+ ******************************************************************************/
+
+#include "mailbox_message.h"
+
+#include <isula_libutils/log.h>
+
+#include "utils.h"
+
+// Once the create succeeds, the ownership is transferred to the mailbox_message.
+mailbox_message *mailbox_message_create(void *data, void (*destroy)(void *)) {
+    __isula_auto_free mailbox_message *msg = NULL;
+    msg = util_common_calloc_s(sizeof(mailbox_message));
+    if (msg == NULL) {
+        ERROR("Out of memory");
+        return NULL;
+    }
+
+    msg->data = data;
+    msg->destroy = destroy;
+    msg->ref_count = 1;
+
+    if (pthread_mutex_init(&msg->lock, NULL) != 0) {
+        ERROR("Failed to init mutex");
+        return NULL;
+    }
+
+    return isula_transfer_ptr(msg);
+}
+
+int mailbox_message_ref(mailbox_message *dest) {
+    __isula_auto_pm_unlock pthread_mutex_t *lock = NULL;
+    if (dest == NULL) {
+        ERROR("Invalid mailbox_message");
+        return -1;
+    }
+
+    if (pthread_mutex_lock(&dest->lock) != 0) {
+        ERROR("Failed to lock mutex");
+        return -1;
+    }
+    lock = &dest->lock;
+
+    if (dest->ref_count == INT_MAX) {
+        ERROR("Reference count overflow");
+        return -1;
+    }
+
+    dest->ref_count++;
+
+    return 0;
+}
+
+void mailbox_message_unref(mailbox_message *dest) {
+    __isula_auto_pm_unlock pthread_mutex_t *lock = NULL;
+    if (dest == NULL) {
+        return;
+    }
+
+    if (pthread_mutex_lock(&dest->lock) != 0) {
+        ERROR("Failed to lock mutex");
+        return;
+    }
+    lock = &dest->lock;
+
+    if (dest->ref_count == 0) {
+        ERROR("Reference count underflow, should not reach here");
+        return;
+    }
+
+    dest->ref_count--;
+    if (dest->ref_count == 0) {
+        if (dest->destroy) {
+            dest->destroy(dest->data);
+        }
+        lock = NULL;
+        (void)pthread_mutex_unlock(&dest->lock);
+        (void)pthread_mutex_destroy(&dest->lock);
+        free(dest);
+    }
+    return;
+}
diff --git a/src/daemon/mailbox/mailbox_message.h b/src/daemon/mailbox/mailbox_message.h
new file mode 100644
index 00000000..39e40b70
--- /dev/null
+++ b/src/daemon/mailbox/mailbox_message.h
@@ -0,0 +1,50 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
+ * iSulad licensed under the Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *     http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
+ * PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ * Author: jikai
+ * Create: 2024-03-25
+ * Description: provide ref counted ptr definition
+ ******************************************************************************/
+
+#ifndef DAEMON_MAILBOX_MAILBOX_MESSAGE_H
+#define DAEMON_MAILBOX_MAILBOX_MESSAGE_H
+
+#include <pthread.h>
+#include <stdbool.h>
+
+#include <isula_libutils/auto_cleanup.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct mailbox_message {
+    void *data;
+    size_t ref_count;
+    pthread_mutex_t lock;
+    void (*destroy)(void *);
+} mailbox_message;
+
+mailbox_message *mailbox_message_create(void *ptr, void (*destroy)(void *));
+
+int mailbox_message_ref(mailbox_message *p);
+
+void mailbox_message_unref(mailbox_message *p);
+
+// define auto free function callback for mailbox_message
+define_auto_cleanup_callback(mailbox_message_unref, mailbox_message);
+// define auto free macro for char *
+#define __isula_auto_mailbox_message auto_cleanup_tag(mailbox_message_unref)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/daemon/mailbox/message_queue.c b/src/daemon/mailbox/message_queue.c
new file mode 100644
index 00000000..7fe044f2
--- /dev/null
+++ b/src/daemon/mailbox/message_queue.c
@@ -0,0 +1,234 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
+ * iSulad licensed under the Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *     http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
+ * PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ * Author: jikai
+ * Create: 2024-03-25
+ * Description: provide message queue definition
+ ******************************************************************************/
+
+#include "message_queue.h"
+
+#include <sys/prctl.h>
+#include <isula_libutils/log.h>
+
+#include "utils.h"
+
+// default set subscriber timeout to 1000ms, maybe could be configured later
+const int64_t subscribe_timeout = 1000;
+
+static void message_queue_subscriber_free(void *key, void *val)
+{
+    return;
+}
+
+static void *message_queue_thread(void *arg)
+{
+    int ret = 0;
+
+    ret = pthread_detach(pthread_self());
+    if (ret != 0) {
+        CRIT("Set thread detach fail");
+        return NULL;
+    }
+
+    prctl(PR_SET_NAME, "Message Queue");
+
+    message_queue *mq = (message_queue *)arg;
+    if (mq == NULL) {
+        ERROR("Invalid argument");
+        return NULL;
+    }
+
+    for (;;) {
+        void *data = NULL;
+        if (blocking_queue_pop(mq->messages, &data) != 0) {
+            ERROR("Fail to get message, message queue thread exit");
+            break;
+        }
+
+        __isula_auto_mailbox_message mailbox_message *msg = (mailbox_message *)data;
+        // an empty msg indicates shutdown
+        if (pthread_rwlock_rdlock(&mq->rwlock) != 0) {
+            ERROR("Failed to lock rwlock");
+            continue;
+        }
+
+        bool should_shutdown = (msg == NULL);
+        map_itor *itor = map_itor_new(mq->subscribers);
+        if (itor == NULL) {
+            ERROR("Out of memory");
+            if (pthread_rwlock_unlock(&mq->rwlock) != 0) {
+                ERROR("Failed to lock rwlock");
+            }
+            break;
+        }
+
+        for (; map_itor_valid(itor); map_itor_next(itor)) {
+            void *sub = map_itor_key(itor);
+            if (should_shutdown) {
+                message_subscriber_shutdown(sub);
+            } else {
+                if (message_subscriber_push(sub, msg) != 0) {
+                    ERROR("Failed to push event to subscriber");
+                }
+            }
+        }
+        map_itor_free(itor);
+
+        if (pthread_rwlock_unlock(&mq->rwlock) != 0) {
+            ERROR("Failed to unlock rwlock");
+        }
+
+        // if msg is NULL, it is a shutdown signal
+        if (should_shutdown) {
+            break;
+        }
+    }
+
+    return NULL;
+}
+
+message_queue *message_queue_create(void (*release)(void *))
+{
+    __isula_auto_free message_queue *mq = NULL;
+    __isula_auto_blocking_queue blocking_queue *bq = NULL;
+    pthread_t message_queue_tid;
+    mq = util_common_calloc_s(sizeof(message_queue));
+    if (mq == NULL) {
+        ERROR("Out of memory");
+        return NULL;
+    }
+
+    bq = blocking_queue_create(BLOCKING_QUEUE_NO_TIMEOUT, release);
+    if (bq == NULL) {
+        ERROR("Failed to create events queue");
+        return NULL;
+    }
+
+    mq->subscribers = map_new(MAP_PTR_INT, MAP_DEFAULT_CMP_FUNC, message_queue_subscriber_free);
+    if (mq->subscribers == NULL) {
+        ERROR("Failed to create subscribers map");
+        return NULL;
+    }
+
+    if (pthread_rwlock_init(&mq->rwlock, NULL) != 0) {
+        ERROR("Failed to init rwlock");
+        map_free(mq->subscribers);
+        return NULL;
+    }
+
+    if (pthread_create(&message_queue_tid, NULL, message_queue_thread, mq) != 0) {
+        ERROR("Failed to create message queue thread");
+        pthread_rwlock_destroy(&mq->rwlock);
+        map_free(mq->subscribers);
+        return NULL;
+    }
+
+    mq->messages = isula_transfer_ptr(bq);
+    return isula_transfer_ptr(mq);
+}
+
+// message queue should be global value, it will be destroyed when daemon exit
+void message_queue_shutdown(message_queue *mq)
+{
+    if (mq == NULL) {
+        return;
+    }
+
+    blocking_queue_clear(mq->messages);
+
+    // push a nullptr to notify the thread to exit
+    if (blocking_queue_push(mq->messages, NULL) != 0) {
+        ERROR("Failed to push nullptr to message queue");
+    }
+}
+
+message_subscriber *message_queue_subscribe(message_queue *mq, void (*release)(void *))
+{
+    __isula_auto_subscriber message_subscriber *sub = NULL;
+    __isula_auto_prw_unlock pthread_rwlock_t *lock = NULL;
+    int val = 0;
+    if (mq == NULL) {
+        ERROR("Invalid argument");
+        return NULL;
+    }
+
+    sub = message_subscriber_create(subscribe_timeout, release);
+    if (sub == NULL) {
+        ERROR("Failed to create subscriber");
+        return NULL;
+    }
+
+    if (pthread_rwlock_wrlock(&mq->rwlock) != 0) {
+        ERROR("Failed to lock rwlock");
+        return NULL;
+    }
+    lock = &mq->rwlock;
+
+    if (map_insert(mq->subscribers, sub, (void *)&val) == false) {
+        ERROR("Failed to insert subscriber");
+        return NULL;
+    }
+
+    return isula_transfer_ptr(sub);
+}
+
+void message_queue_unsubscribe(message_queue *mq, message_subscriber *sub)
+{
+    __isula_auto_prw_unlock pthread_rwlock_t *lock = NULL;
+    if (mq == NULL) {
+        ERROR("Invalid argument");
+        return;
+    }
+
+    if (pthread_rwlock_wrlock(&mq->rwlock) != 0) {
+        ERROR("Failed to lock rwlock");
+        return;
+    }
+    lock = &mq->rwlock;
+
+    if (map_remove(mq->subscribers, sub) == false) {
+        ERROR("Failed to remove subscriber");
+        return;
+    }
+
+    return;
+}
+
+int message_queue_publish(message_queue *mq, mailbox_message *msg)
+{
+    if (mq == NULL || msg == NULL) {
+        ERROR("Invalid argument");
+        return -1;
+    }
+
+    if (blocking_queue_push(mq->messages, msg) != 0) {
+        ERROR("Failed to push message");
+        return -1;
+    }
+    return 0;
+}
+
+bool message_queue_have_subscribers(message_queue *mq)
+{
+    __isula_auto_prw_unlock pthread_rwlock_t *lock = NULL;
+    if (mq == NULL) {
+        ERROR("Invalid argument");
+        return false;
+    }
+
+    if (pthread_rwlock_wrlock(&mq->rwlock) != 0) {
+        ERROR("Failed to lock rwlock");
+        return false;
+    }
+    lock = &mq->rwlock;
+
+    return map_size(mq->subscribers) > 0;
+}
diff --git a/src/daemon/mailbox/message_queue.h b/src/daemon/mailbox/message_queue.h
new file mode 100644
index 00000000..7905840f
--- /dev/null
+++ b/src/daemon/mailbox/message_queue.h
@@ -0,0 +1,57 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
+ * iSulad licensed under the Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *     http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
+ * PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ * Author: jikai
+ * Create: 2024-03-25
+ * Description: provide message queue definition
+ ******************************************************************************/
+
+#ifndef DAEMON_MESSAGE_MESSAGE_QUEUE_H
+#define DAEMON_MESSAGE_MESSAGE_QUEUE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <pthread.h>
+
+#include "blocking_queue.h"
+#include "mailbox_message.h"
+#include "map.h"
+#include "message_subscriber.h"
+
+typedef struct message_queue {
+    blocking_queue *messages;
+
+    // lock for set of subscribers
+    pthread_rwlock_t rwlock;
+
+    map_t *subscribers;
+
+    int64_t sub_timeout;
+} message_queue;
+
+message_queue *message_queue_create(void (*release)(void *));
+
+void message_queue_shutdown(message_queue *mq);
+
+message_subscriber *message_queue_subscribe(message_queue *mq, void (*release)(void *));
+
+void message_queue_unsubscribe(message_queue *mq, message_subscriber *sub);
+
+int message_queue_publish(message_queue *mq, mailbox_message *msg);
+
+bool message_queue_have_subscribers(message_queue *mq);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/daemon/mailbox/message_subscriber.c b/src/daemon/mailbox/message_subscriber.c
new file mode 100644
index 00000000..8ef3cb58
--- /dev/null
+++ b/src/daemon/mailbox/message_subscriber.c
@@ -0,0 +1,85 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
+ * iSulad licensed under the Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *     http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
+ * PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ * Author: jikai
+ * Create: 2024-03-25
+ * Description: provide message subscriber definition
+ ******************************************************************************/
+
+#include "message_subscriber.h"
+
+#include <isula_libutils/log.h>
+
+#include "utils.h"
+
+message_subscriber *message_subscriber_create(int64_t timeout, void (*release)(void *))
+{
+    message_subscriber *sub = (message_subscriber *)util_common_calloc_s(sizeof(message_subscriber));
+    if (sub == NULL) {
+        ERROR("Out of memory");
+        return NULL;
+    }
+    sub->queue = blocking_queue_create(timeout, release);
+    if (sub->queue == NULL) {
+        ERROR("Failed to create blocking queue");
+        free(sub);
+        return NULL;
+    }
+    return sub;
+}
+
+int message_subscriber_push(message_subscriber *sub, mailbox_message *msg)
+{
+    if (sub == NULL || msg == NULL) {
+        ERROR("Invalid argument");
+        return -1;
+    }
+
+    if (mailbox_message_ref(msg) != 0) {
+        ERROR("Failed to get message");
+        return -1;
+    }
+
+    if (blocking_queue_push(sub->queue, msg) != 0) {
+        ERROR("Failed to push message to queue");
+        mailbox_message_unref(msg);
+        return -1;
+    }
+
+    return 0;
+}
+
+int message_subscriber_pop(message_subscriber *sub, mailbox_message **msg)
+{
+    if (sub == NULL) {
+        ERROR("Invalid argument");
+        return -1;
+    }
+    return blocking_queue_pop(sub->queue, (void **)msg);
+}
+
+void message_subscriber_shutdown(message_subscriber *sub)
+{
+    if (sub == NULL) {
+        return;
+    }
+
+    blocking_queue_clear(sub->queue);
+    (void)blocking_queue_push(sub->queue, NULL);
+}
+
+void message_subscriber_destroy(message_subscriber *sub)
+{
+    if (sub == NULL) {
+        return;
+    }
+    blocking_queue_destroy(sub->queue);
+    free(sub);
+}
diff --git a/src/daemon/mailbox/message_subscriber.h b/src/daemon/mailbox/message_subscriber.h
new file mode 100644
index 00000000..de4574d9
--- /dev/null
+++ b/src/daemon/mailbox/message_subscriber.h
@@ -0,0 +1,41 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
+ * iSulad licensed under the Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *     http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
+ * PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ * Author: jikai
+ * Create: 2024-03-25
+ * Description: provide message subscriber definition
+ ******************************************************************************/
+
+#ifndef SRC_DAEMON_MAILBOX_MESSAGE_SUBSCRIBER_H
+#define SRC_DAEMON_MAILBOX_MESSAGE_SUBSCRIBER_H
+
+#include "blocking_queue.h"
+#include "mailbox_message.h"
+
+typedef struct {
+    blocking_queue *queue;
+} message_subscriber;
+
+message_subscriber *message_subscriber_create(int64_t timeout, void (*release)(void *));
+
+void message_subscriber_shutdown(message_subscriber *sub);
+
+void message_subscriber_destroy(message_subscriber *sub);
+
+int message_subscriber_push(message_subscriber *sub, mailbox_message *msg);
+
+int message_subscriber_pop(message_subscriber *sub, mailbox_message **msg);
+
+// define auto free function callback for blocking queue
+define_auto_cleanup_callback(message_subscriber_destroy, message_subscriber);
+// define auto free macro for blocking queue
+#define __isula_auto_subscriber auto_cleanup_tag(message_subscriber_destroy)
+
+#endif
diff --git a/src/daemon/modules/api/container_api.h b/src/daemon/modules/api/container_api.h
index 830fd696..55c59980 100644
--- a/src/daemon/modules/api/container_api.h
+++ b/src/daemon/modules/api/container_api.h
@@ -289,6 +289,11 @@ static inline bool is_sandbox_container(container_sandbox_info *sandbox)
 {
     return sandbox != NULL && sandbox->is_sandbox_container;
 }
+
+static inline bool is_container_in_sandbox(container_sandbox_info *sandbox)
+{
+    return sandbox != NULL && !sandbox->is_sandbox_container;
+}
 #endif
 
 #if defined(__cplusplus) || defined(c_plusplus)
diff --git a/src/daemon/modules/container/supervisor/supervisor.c b/src/daemon/modules/container/supervisor/supervisor.c
index 1b7da383..83d46268 100644
--- a/src/daemon/modules/container/supervisor/supervisor.c
+++ b/src/daemon/modules/container/supervisor/supervisor.c
@@ -38,6 +38,7 @@
 #include "container_api.h"
 #include "event_type.h"
 #include "utils_file.h"
+#include "mailbox.h"
 #ifdef ENABLE_CRI_API_V1
 #include "sandbox_ops.h"
 #endif
@@ -51,6 +52,7 @@ struct supervisor_handler_data {
     int fd;
     int exit_code;
     char *name;
+    char *sandbox_name;
     char *runtime;
     bool is_sandbox_container;
     pid_ppid_info_t pid_info;
@@ -152,6 +154,9 @@ static void supervisor_handler_data_free(struct supervisor_handler_data *data)
     free(data->name);
     data->name = NULL;
 
+    free(data->sandbox_name);
+    data->sandbox_name = NULL;
+
     free(data->runtime);
     data->runtime = NULL;
 
@@ -172,6 +177,9 @@ static void *clean_resources_thread(void *arg)
     pid_t pid = data->pid_info.pid;
     int retry_count = 0;
     int max_retry = 10;
+#ifdef ENABLE_CRI_API_V1
+    cri_container_message_t msg;
+#endif
 
     ret = pthread_detach(pthread_self());
     if (ret != 0) {
@@ -218,6 +226,13 @@ retry:
     (void)isulad_monitor_send_container_event(name, STOPPED, (int)pid, data->exit_code, NULL, NULL);
 
 #ifdef ENABLE_CRI_API_V1
+    if (data->sandbox_name) {
+        msg.container_id = name;
+        msg.sandbox_id = data->sandbox_name;
+        msg.type = CRI_CONTAINER_MESSAGE_TYPE_STOPPED;
+        mailbox_publish(MAILBOX_TOPIC_CRI_CONTAINER, &msg);
+    }
+
     if (data->is_sandbox_container) {
         if (sandbox_on_sandbox_exit(name, data->exit_code) < 0) {
             ERROR("Failed to handle sandbox %s exit", name);
@@ -329,6 +344,9 @@ int container_supervisor_add_exit_monitor(int fd, const char *exit_fifo, const p
     data->runtime = util_strdup_s(cont->runtime);
 #ifdef ENABLE_CRI_API_V1
     data->is_sandbox_container = is_sandbox_container(cont->common_config->sandbox_info);
+    if (is_container_in_sandbox(cont->common_config->sandbox_info)) {
+        data->sandbox_name = util_strdup_s(cont->common_config->sandbox_info->id);
+    }
 #endif
     data->pid_info.pid = pid_info->pid;
     data->pid_info.start_time = pid_info->start_time;
diff --git a/src/daemon/sandbox/sandbox.cc b/src/daemon/sandbox/sandbox.cc
index 7b6496ed..c70116c1 100644
--- a/src/daemon/sandbox/sandbox.cc
+++ b/src/daemon/sandbox/sandbox.cc
@@ -37,6 +37,7 @@
 #include "cxxutils.h"
 #include "controller_manager.h"
 #include "utils_timestamp.h"
+#include "mailbox.h"
 
 #define SANDBOX_READY_STATE_STR "SANDBOX_READY"
 #define SANDBOX_NOTREADY_STATE_STR "SANDBOX_NOTREADY"
@@ -527,6 +528,14 @@ void Sandbox::OnSandboxExit(const ControllerExitInfo &exitInfo)
     if (!SaveState(error)) {
         ERROR("Failed to save sandbox state, %s", m_id.c_str());
     }
+
+    if (error.Empty()) {
+        cri_container_message_t msg = { 0 };
+        msg.container_id = GetId().c_str();
+        msg.sandbox_id = GetId().c_str();
+        msg.type = CRI_CONTAINER_MESSAGE_TYPE_STOPPED;
+        mailbox_publish(MAILBOX_TOPIC_CRI_CONTAINER, &msg);
+    }
 }
 
 auto Sandbox::UpdateStatus(Errors &error) -> bool
diff --git a/src/utils/cutils/blocking_queue.c b/src/utils/cutils/blocking_queue.c
new file mode 100644
index 00000000..7c9c5f50
--- /dev/null
+++ b/src/utils/cutils/blocking_queue.c
@@ -0,0 +1,185 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
+ * iSulad licensed under the Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *     http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
+ * PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ * Author: jikai
+ * Create: 2024-03-25
+ * Description: provide blocking queue definition
+ ******************************************************************************/
+
+#include "blocking_queue.h"
+
+#include <pthread.h>
+#include <time.h>
+#include <isula_libutils/log.h>
+
+#include "utils.h"
+#include "utils_timestamp.h"
+
+// create blocking queue with timeout(ms), if timeout < 0, then with no timeout
+blocking_queue *blocking_queue_create(int64_t timeout, void (*release)(void *))
+{
+    __isula_auto_free blocking_queue *queue = NULL;
+    __isula_auto_free blocking_node *node = NULL;
+    queue = (blocking_queue *)util_common_calloc_s(sizeof(blocking_queue));
+    if (queue == NULL) {
+        ERROR("Out of memory");
+        return NULL;
+    }
+    node = (blocking_node *)util_common_calloc_s(sizeof(blocking_node));
+    if (node == NULL) {
+        ERROR("Out of memory");
+        return NULL;
+    }
+
+    if (pthread_mutex_init(&queue->lock, NULL) != 0) {
+        ERROR("Failed to init mutex");
+        return NULL;
+    }
+
+    if (pthread_cond_init(&queue->not_empty, NULL) != 0) {
+        ERROR("Failed to init cond");
+        (void)pthread_mutex_destroy(&queue->lock);
+        return NULL;
+    }
+
+    queue->head = node;
+    queue->tail = node;
+    node = NULL;
+    queue->release = release;
+
+    if (timeout >= 0) {
+        queue->timeout.tv_sec = timeout / (Time_Second / Time_Milli);
+        queue->timeout.tv_nsec = (timeout % (Time_Second / Time_Milli) ) * Time_Milli;
+    } else {
+        queue->timeout.tv_sec = -1;
+    }
+    
+    return isula_transfer_ptr(queue);
+}
+
+int blocking_queue_push(blocking_queue *queue, void *data)
+{
+    __isula_auto_free blocking_node *new_node = NULL;
+    __isula_auto_pm_unlock pthread_mutex_t *lock = NULL;
+    if (queue == NULL) {
+        ERROR("Invalid NULL arguments");
+        return -1;
+    }
+
+    new_node = (blocking_node *)util_common_calloc_s(sizeof(blocking_node));
+    if (new_node == NULL) {
+        ERROR("Out of memory");
+        return -1;
+    }
+    new_node->data = data;
+    new_node->next = NULL;
+
+    if (pthread_mutex_lock(&queue->lock) != 0) {
+        ERROR("Failed to lock mutex");
+        return -1;
+    }
+    lock = &queue->lock;
+
+    queue->tail->next = new_node;
+    queue->tail = new_node;
+    new_node = NULL;
+
+    if (pthread_cond_broadcast(&queue->not_empty) != 0) {
+        ERROR("Failed to broadcast cond");
+    }
+
+    return 0;
+}
+
+int blocking_queue_pop(blocking_queue *queue, void **data) {
+    if (queue == NULL || data == NULL) {
+        ERROR("Invalid NULL arguments");
+        return -1;
+    }
+
+    __isula_auto_pm_unlock pthread_mutex_t *lock = NULL;
+    if (pthread_mutex_lock(&queue->lock) != 0) {
+        ERROR("Failed to lock mutex");
+        return -1;
+    }
+    lock = &queue->lock;
+
+    while (queue->head->next == NULL) {
+        if (queue->timeout.tv_sec >= 0) {
+            int ret = pthread_cond_timedwait(&queue->not_empty, &queue->lock, &queue->timeout);
+            if (ret != 0) {
+                if (ret != ETIMEDOUT) {
+                    ERROR("Failed to wait cond");
+                }
+                return ret;
+            }
+        } else {
+            int ret = pthread_cond_wait(&queue->not_empty, &queue->lock);
+            if (ret != 0) {
+                ERROR("Failed to wait cond");
+                return ret;
+            }
+        }
+    }
+
+    blocking_node *old_head = queue->head;
+    blocking_node *new_head = old_head->next;
+    *data = new_head->data;
+    queue->head = new_head;
+
+    free(old_head);
+    return 0;
+}
+
+void blocking_queue_clear(blocking_queue *queue)
+{
+    if (queue == NULL) {
+        return;
+    }
+
+    __isula_auto_pm_unlock pthread_mutex_t *lock = NULL;
+    // clear all nodes in queue
+    if (queue == NULL) {
+        ERROR("Invalid NULL arguments");
+        return;
+    }
+
+    if (pthread_mutex_lock(&queue->lock) != 0) {
+        ERROR("Failed to lock mutex");
+        return;
+    }
+    lock = &queue->lock;
+
+    while (queue->head->next != NULL) {
+        blocking_node *old_head = queue->head;
+        blocking_node *new_head = old_head->next;
+        if (queue->release) {
+            queue->release(old_head->data);
+        }
+        free(old_head);
+        queue->head = new_head;
+    }
+}
+
+// ensure there is no other thread executing enqueue or dequeue operation
+void blocking_queue_destroy(blocking_queue *queue)
+{
+    if (queue == NULL) {
+        return;
+    }
+
+    blocking_queue_clear(queue);
+
+    (void)pthread_mutex_destroy(&queue->lock);
+
+    (void)pthread_cond_destroy(&queue->not_empty);
+
+    free(queue);
+}
diff --git a/src/utils/cutils/blocking_queue.h b/src/utils/cutils/blocking_queue.h
new file mode 100644
index 00000000..1c52a9d3
--- /dev/null
+++ b/src/utils/cutils/blocking_queue.h
@@ -0,0 +1,66 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
+ * iSulad licensed under the Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *     http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
+ * PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ * Author: jikai
+ * Create: 2024-03-25
+ * Description: provide blocking queue definition
+ ******************************************************************************/
+
+#ifndef DAEMON_UTILS_CUTILS_BLOCKING_QUEUE_H
+#define DAEMON_UTILS_CUTILS_BLOCKING_QUEUE_H
+
+#include <pthread.h>
+#include <time.h>
+#include <isula_libutils/auto_cleanup.h>
+
+#include "utils_timestamp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BLOCKING_QUEUE_NO_TIMEOUT -1
+
+typedef struct blocking_node {
+    void *data;
+    struct blocking_node *next;
+} blocking_node;
+
+typedef struct blocking_queue {
+    blocking_node *head;
+    blocking_node *tail;
+    pthread_mutex_t lock;
+    struct timespec timeout;
+    pthread_cond_t not_empty;
+    void (*release)(void *);
+} blocking_queue;
+
+// create blocking queue with timeout(ms), if timeout < 0, then with no timeout
+blocking_queue *blocking_queue_create(int64_t timeout, void (*release)(void *));
+
+int blocking_queue_push(blocking_queue *queue, void *data);
+
+int blocking_queue_pop(blocking_queue *queue, void **data);
+
+void blocking_queue_clear(blocking_queue *queue);
+
+// ensure there is no other thread executing enqueue or dequeue operation
+void blocking_queue_destroy(blocking_queue *queue);
+
+// define auto free function callback for blocking queue
+define_auto_cleanup_callback(blocking_queue_destroy, blocking_queue);
+// define auto free macro for blocking queue
+#define __isula_auto_blocking_queue auto_cleanup_tag(blocking_queue_destroy)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/test/mocks/mailbox_mock.cc b/test/mocks/mailbox_mock.cc
new file mode 100644
index 00000000..601b804e
--- /dev/null
+++ b/test/mocks/mailbox_mock.cc
@@ -0,0 +1,30 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved.
+ * iSulad licensed under the Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *     http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
+ * PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ * Author: jikai
+ * Create: 2024-04-02
+ * Description: mailbox mock
+ ******************************************************************************/
+
+#include "mailbox_mock.h"
+
+MockMailbox *g_mailbox_mock = nullptr;
+
+void Mailbox_SetMock(MockMailbox* mock)
+{
+    g_mailbox_mock = mock;
+}
+
+void mailbox_publish(mailbox_topic topic, void *data)
+{
+    if (g_mailbox_mock != nullptr) {
+        g_mailbox_mock->MailboxPublish(topic, data);
+    }
+}
diff --git a/test/mocks/mailbox_mock.h b/test/mocks/mailbox_mock.h
new file mode 100644
index 00000000..ce48f0fc
--- /dev/null
+++ b/test/mocks/mailbox_mock.h
@@ -0,0 +1,30 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved.
+ * iSulad licensed under the Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *     http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
+ * PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ * Author: jikai
+ * Create: 2024-04-02
+ * Description: mailbox mock
+ ******************************************************************************/
+
+#ifndef _ISULAD_TEST_MOCKS_MAILBOX_MOCK_H
+#define _ISULAD_TEST_MOCKS_MAILBOX_MOCK_H
+
+#include <gmock/gmock.h>
+#include "mailbox.h"
+
+class MockMailbox {
+public:
+    virtual ~MockMailbox() = default;
+    MOCK_METHOD2(MailboxPublish, void(mailbox_topic topic, void *data));
+};
+
+void Mailbox_SetMock(MockMailbox* mock);
+
+#endif
diff --git a/test/sandbox/controller/shim/CMakeLists.txt b/test/sandbox/controller/shim/CMakeLists.txt
index 6423bb80..26a66e51 100644
--- a/test/sandbox/controller/shim/CMakeLists.txt
+++ b/test/sandbox/controller/shim/CMakeLists.txt
@@ -7,6 +7,7 @@ add_executable(${EXE}
     ${CMAKE_BINARY_DIR}/grpc/src/api/services/cri/gogo.pb.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/daemon/common/cri/v1/v1_cri_helpers.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/daemon/common/cri/v1/v1_cri_security_context.cc
+    ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/daemon/common/cri/v1/v1_naming.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/daemon/common/cri/checkpoint_handler.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/daemon/common/cri/cri_constants.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/daemon/common/cri/cri_helpers.cc
diff --git a/test/sandbox/sandbox/CMakeLists.txt b/test/sandbox/sandbox/CMakeLists.txt
index 138d4d8d..2a35388f 100644
--- a/test/sandbox/sandbox/CMakeLists.txt
+++ b/test/sandbox/sandbox/CMakeLists.txt
@@ -23,6 +23,7 @@ add_executable(${EXE}
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../test/mocks/grpc_sandboxer_client_mock.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../test/mocks/controller_stub_mock.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../test/mocks/shim_controller_mock.cc
+    ${CMAKE_CURRENT_SOURCE_DIR}/../../../test/mocks/mailbox_mock.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/daemon/common/err_msg.c
     ${CMAKE_CURRENT_SOURCE_DIR}/sandbox_ut.cc)
 
@@ -33,6 +34,7 @@ target_include_directories(${EXE} PUBLIC
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/daemon/config
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/daemon/entry/cri
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/daemon/executor
+    ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/daemon/mailbox
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/daemon/sandbox/controller
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/daemon/sandbox/controller/shim
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/daemon/sandbox/controller/sandboxer
-- 
2.34.1

