summaryrefslogtreecommitdiff
path: root/0011-add-runc-attach-implement.patch
diff options
context:
space:
mode:
Diffstat (limited to '0011-add-runc-attach-implement.patch')
-rw-r--r--0011-add-runc-attach-implement.patch1312
1 files changed, 1312 insertions, 0 deletions
diff --git a/0011-add-runc-attach-implement.patch b/0011-add-runc-attach-implement.patch
new file mode 100644
index 0000000..088e339
--- /dev/null
+++ b/0011-add-runc-attach-implement.patch
@@ -0,0 +1,1312 @@
+From aaf1b46c66aa596ec718c11c4f4270e41e7b570e Mon Sep 17 00:00:00 2001
+From: zhongtao <zhongtao17@huawei.com>
+Date: Tue, 7 Nov 2023 16:39:35 +0800
+Subject: [PATCH 11/14] add runc attach implement
+
+Signed-off-by: zhongtao <zhongtao17@huawei.com>
+---
+ .../connect/grpc/grpc_containers_client.cc | 9 +
+ src/cmd/isula/stream/attach.c | 12 +-
+ src/cmd/isulad-shim/common.c | 116 ++++-
+ src/cmd/isulad-shim/common.h | 30 ++
+ src/cmd/isulad-shim/main.c | 14 +
+ src/cmd/isulad-shim/process.c | 453 ++++++++++++++++--
+ src/cmd/isulad-shim/process.h | 7 +-
+ src/cmd/isulad-shim/terminal.c | 2 +-
+ .../executor/container_cb/execution_stream.c | 1 +
+ src/daemon/modules/api/runtime_api.h | 1 +
+ .../modules/runtime/isula/isula_rt_ops.c | 168 ++++++-
+ src/utils/cutils/error.h | 4 +-
+ 12 files changed, 755 insertions(+), 62 deletions(-)
+
+diff --git a/src/client/connect/grpc/grpc_containers_client.cc b/src/client/connect/grpc/grpc_containers_client.cc
+index 2dd73100..bcb1e8da 100644
+--- a/src/client/connect/grpc/grpc_containers_client.cc
++++ b/src/client/connect/grpc/grpc_containers_client.cc
+@@ -1394,6 +1394,8 @@ public:
+ auto run(const struct isula_attach_request *request, struct isula_attach_response *response) -> int override
+ {
+ ClientContext context;
++ bool detach = false;
++ std::string attach_detach_msg = "read escape sequence";
+
+ if (set_custom_header_metadata(context, request) != 0) {
+ ERROR("Failed to translate request to grpc");
+@@ -1415,6 +1417,9 @@ public:
+ break;
+ }
+ if (!stream_response.stdout().empty()) {
++ if (strcmp(stream_response.stdout().c_str(), attach_detach_msg.c_str()) == 0) {
++ detach = true;
++ }
+ std::cout << stream_response.stdout() << std::flush;
+ }
+ if (!stream_response.stderr().empty()) {
+@@ -1437,6 +1442,10 @@ public:
+ response->cc = ISULAD_ERR_EXEC;
+ }
+
++ if (detach) {
++ response->server_errono = ISULAD_INFO_DETACH;
++ }
++
+ out:
+ if (request->attach_stdin) {
+ pthread_cancel(writer.native_handle());
+diff --git a/src/cmd/isula/stream/attach.c b/src/cmd/isula/stream/attach.c
+index ff49af92..b61c9350 100644
+--- a/src/cmd/isula/stream/attach.c
++++ b/src/cmd/isula/stream/attach.c
+@@ -37,6 +37,7 @@
+ #include "connect.h"
+ #include "constants.h"
+ #include "client_helpers.h"
++#include "error.h"
+ #ifndef GRPC_CONNECTOR
+ #include "client_console.h"
+ #endif
+@@ -70,8 +71,10 @@ static int check_tty(bool tty, struct termios *oldtios, bool *reset_tty)
+ }
+ *reset_tty = true;
+ } else {
+- INFO("the input device is not a TTY");
+- return 0;
++ // if it is trying to attach to a container TTY
++ // from a non-TTY client input stream, returns -1.
++ COMMAND_ERROR("the input device is not a TTY");
++ return -1;
+ }
+
+ return 0;
+@@ -353,6 +356,7 @@ static int client_attach(struct client_arguments *args, uint32_t *exit_code)
+ #endif
+
+ config = get_connect_config(args);
++ // Obtain the container's real exit code by waiting for the container to stop.
+ container_wait_thread(args, exit_code, &sem_exited);
+ ret = ops->container.attach(&request, response, &config);
+ if (ret != 0) {
+@@ -374,7 +378,9 @@ static int client_attach(struct client_arguments *args, uint32_t *exit_code)
+
+ if (sem_timedwait(&sem_exited, &ts) != 0) {
+ if (errno == ETIMEDOUT) {
+- COMMAND_ERROR("Wait container status timeout.");
++ if (response->server_errono != ISULAD_INFO_DETACH) {
++ INFO("Wait container stopped status timeout.");
++ }
+ } else {
+ CMD_SYSERROR("Failed to wait sem");
+ }
+diff --git a/src/cmd/isulad-shim/common.c b/src/cmd/isulad-shim/common.c
+index 781dc004..48d266dc 100644
+--- a/src/cmd/isulad-shim/common.c
++++ b/src/cmd/isulad-shim/common.c
+@@ -33,16 +33,26 @@
+ #include <isula_libutils/utils_file.h>
+
+ int g_log_fd = -1;
++int g_attach_log_fd = -1;
+
+ int init_shim_log(void)
+ {
+- g_log_fd = open_no_inherit(SHIM_LOG_NAME, O_CREAT | O_WRONLY | O_APPEND | O_SYNC, 0640);
++ g_log_fd = open_no_inherit(SHIM_LOG_NAME, O_CREAT | O_WRONLY | O_APPEND | O_SYNC, LOG_FILE_MODE);
+ if (g_log_fd < 0) {
+ return SHIM_ERR;
+ }
+ return SHIM_OK;
+ }
+
++int init_attach_log(void)
++{
++ g_attach_log_fd = open_no_inherit(ATTACH_LOG_NAME, O_CREAT | O_WRONLY | O_APPEND | O_SYNC, LOG_FILE_MODE);
++ if (g_attach_log_fd < 0) {
++ return SHIM_ERR;
++ }
++ return SHIM_OK;
++}
++
+ void signal_routine(int sig)
+ {
+ switch (sig) {
+@@ -162,11 +172,24 @@ int generate_random_str(char *id, size_t len)
+ return SHIM_OK;
+ }
+
+-void write_message(const char *level, const char *fmt, ...)
+-{
+ #define MAX_MSG_JSON_TEMPLATE 32
+ #define MAX_MESSAGE_CONTENT_LEN 128
+ #define MAX_MESSAGE_LEN (MAX_MSG_JSON_TEMPLATE + MAX_MESSAGE_CONTENT_LEN)
++
++static void format_log_msg(const char *level, const char *buf, char *msg, int max_message_len)
++{
++ time_t current_time = time(NULL);
++ struct tm *local_time = localtime(&current_time);
++ char time_str[20];
++
++ strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", local_time);
++
++ (void)snprintf(msg, max_message_len - 1, "{\"time\": \"%s\", \"level\": \"%s\", \"msg\": \"%s\"}\n", time_str, level,
++ buf);
++}
++
++void write_message(const char *level, const char *fmt, ...)
++{
+ if (g_log_fd < 0) {
+ return;
+ }
+@@ -183,15 +206,31 @@ void write_message(const char *level, const char *fmt, ...)
+ return;
+ }
+
+- nwrite = snprintf(msg, MAX_MESSAGE_LEN - 1, "{\"level\": \"%s\", \"msg\": \"%s\"}\n", level, buf);
+- if (nwrite < 0 || (size_t)nwrite >= (MAX_MESSAGE_LEN - 1)) {
++ format_log_msg(level, buf, msg, MAX_MESSAGE_CONTENT_LEN);
++
++ (void)isula_file_total_write_nointr(g_log_fd, msg, strlen(msg));
++}
++
++void write_attach_message(const char *level, const char *fmt, ...)
++{
++ char buf[MAX_MESSAGE_CONTENT_LEN] = { 0 };
++ char msg[MAX_MESSAGE_LEN] = { 0 };
++ int nwrite = -1;
++
++ if (g_attach_log_fd < 0) {
+ return;
+ }
+-
+- nwrite = isula_file_total_write_nointr(g_log_fd, msg, strlen(msg));
+- if (nwrite < 0 || (size_t)nwrite != strlen(msg)) {
++ va_list arg_list;
++ va_start(arg_list, fmt);
++ nwrite = vsnprintf(buf, MAX_MESSAGE_CONTENT_LEN, fmt, arg_list);
++ va_end(arg_list);
++ if (nwrite < 0) {
+ return;
+ }
++
++ format_log_msg(level, buf, msg, MAX_MESSAGE_CONTENT_LEN);
++
++ (void)isula_file_total_write_nointr(g_attach_log_fd, msg, strlen(msg));
+ }
+
+ /* note: This function can only read small text file. */
+@@ -272,3 +311,64 @@ int open_no_inherit(const char *path, int flag, mode_t mode)
+
+ return fd;
+ }
++
++/* judge the fd whether is attach fifo */
++struct isula_linked_list *get_attach_fifo_item(int fd, struct isula_linked_list *list)
++{
++ struct isula_linked_list *it = NULL;
++ struct isula_linked_list *next = NULL;
++
++ if (fd <= 0 || list == NULL || isula_linked_list_empty(list)) {
++ return it;
++ }
++
++ isula_linked_list_for_each_safe(it, list, next) {
++ struct shim_fifos_fd *elem = (struct shim_fifos_fd *)it->elem;
++ if (elem == NULL) {
++ continue;
++ }
++ if (elem->in_fd == fd) {
++ return it;
++ }
++ if (elem->out_fd == fd) {
++ return it;
++ }
++ if (elem->err_fd == fd) {
++ return it;
++ }
++ }
++
++ return it;
++}
++
++void free_shim_fifos_fd(struct shim_fifos_fd *item)
++{
++ if (item == NULL) {
++ return;
++ }
++ if (item->in_fifo != NULL) {
++ free(item->in_fifo);
++ item->in_fifo = NULL;
++ }
++ if (item->out_fifo != NULL) {
++ free(item->out_fifo);
++ item->out_fifo = NULL;
++ }
++ if (item->err_fifo != NULL) {
++ free(item->err_fifo);
++ item->err_fifo = NULL;
++ }
++ if (item->in_fd >= 0) {
++ close(item->in_fd);
++ item->in_fd = -1;
++ }
++ if (item->out_fd >= 0) {
++ close(item->out_fd);
++ item->out_fd = -1;
++ }
++ if (item->err_fd >= 0) {
++ close(item->err_fd);
++ item->err_fd = -1;
++ }
++ free(item);
++}
+\ No newline at end of file
+diff --git a/src/cmd/isulad-shim/common.h b/src/cmd/isulad-shim/common.h
+index 55efdc28..2020a799 100644
+--- a/src/cmd/isulad-shim/common.h
++++ b/src/cmd/isulad-shim/common.h
+@@ -21,6 +21,7 @@
+ #include <sys/types.h>
+ #include <stdint.h>
+ #include <isula_libutils/utils.h>
++#include <isula_libutils/utils_linked_list.h>
+
+ #ifdef __cplusplus
+ extern "C" {
+@@ -59,8 +60,22 @@ extern "C" {
+ #define CONTAINER_ACTION_REBOOT 129
+ #define CONTAINER_ACTION_SHUTDOWN 130
+
++#define ATTACH_SOCKET "attach_socket.sock"
++#define ATTACH_LOG_NAME "attach-log.json"
++#define ATTACH_DETACH_MSG "read escape sequence"
++#define MAX_ATTACH_NUM 16
++
++#define CTRL_Q 0x11 // ASCII code control character ctrl + Q
++
++#define LOG_FILE_MODE 0600
++
++#define SOCKET_DIRECTORY_MODE 0600
++#define ATTACH_FIFOPATH_MODE 0600
++
+ int init_shim_log(void);
+
++int init_attach_log(void);
++
+ void signal_routine(int sig);
+
+ /**
+@@ -90,18 +105,33 @@ void signal_routine(int sig);
+ } \
+ } while (0)
+
++struct shim_fifos_fd {
++ char *in_fifo;
++ char *out_fifo;
++ char *err_fifo;
++ int in_fd;
++ int out_fd;
++ int err_fd;
++};
++
+ char *read_text_file(const char *path);
+
+ int cmd_combined_output(const char *binary, const char *params[], void *output, int *output_len);
+
+ void write_message(const char *level, const char *fmt, ...);
+
++void write_attach_message(const char *level, const char *fmt, ...);
++
+ int generate_random_str(char *id, size_t len);
+
+ void close_fd(int *pfd);
+
+ int open_no_inherit(const char *path, int flag, mode_t mode);
+
++struct isula_linked_list *get_attach_fifo_item(int fd, struct isula_linked_list *list);
++
++void free_shim_fifos_fd(struct shim_fifos_fd *item);
++
+ #ifdef __cplusplus
+ }
+ #endif
+diff --git a/src/cmd/isulad-shim/main.c b/src/cmd/isulad-shim/main.c
+index 454011d0..deb07271 100644
+--- a/src/cmd/isulad-shim/main.c
++++ b/src/cmd/isulad-shim/main.c
+@@ -145,6 +145,20 @@ int main(int argc, char **argv)
+ }
+ }
+
++ if (p->state->attach_socket != NULL) {
++ ret = prepare_attach_socket(p);
++ if (ret != SHIM_OK) {
++ write_message(ERR_MSG, "failed to prepare attach socket:%d", ret);
++ exit(EXIT_FAILURE);
++ }
++
++ ret = init_attach_log();
++ if (ret != SHIM_OK) {
++ write_message(ERR_MSG, "failed to init shim attach log");
++ exit(EXIT_FAILURE);
++ }
++ }
++
+ /* start epoll for io copy */
+ ret = process_io_start(p, &tid_epoll);
+ if (ret != SHIM_OK) {
+diff --git a/src/cmd/isulad-shim/process.c b/src/cmd/isulad-shim/process.c
+index 40908102..187067d2 100644
+--- a/src/cmd/isulad-shim/process.c
++++ b/src/cmd/isulad-shim/process.c
+@@ -19,20 +19,22 @@
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <sys/un.h>
++#include <sys/ioctl.h>
++#include <sys/eventfd.h>
++#include <sys/resource.h> // IWYU pragma: keep
++#include <sys/stat.h>
++#include <sys/wait.h>
+ #include <fcntl.h>
+ #include <unistd.h>
+ #include <limits.h>
+-#include <sys/wait.h>
+ #include <semaphore.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <errno.h>
+-#include <sys/ioctl.h>
+-#include <sys/eventfd.h>
+ #include <termios.h> // IWYU pragma: keep
+-#include <sys/resource.h> // IWYU pragma: keep
+ #include <stdint.h>
+ #include <stdio.h>
++#include <stddef.h>
+
+ #include <isula_libutils/json_common.h>
+ #include <isula_libutils/shim_client_process_state.h>
+@@ -42,6 +44,8 @@
+ #include <isula_libutils/utils_mainloop.h>
+ #include <isula_libutils/auto_cleanup.h>
+ #include <isula_libutils/utils_buffer.h>
++#include <isula_libutils/utils_linked_list.h>
++#include <isula_libutils/utils_array.h>
+
+ #include "common.h"
+ #include "terminal.h"
+@@ -57,7 +61,7 @@ static shim_client_process_state *load_process()
+
+ p_state = shim_client_process_state_parse_file("process.json", NULL, &err);
+ if (p_state == NULL) {
+- write_message(ERR_MSG, "parse process state failed");
++ write_message(ERR_MSG, "parse process state failed: %s", err);
+ }
+ /* "err" will definitely be allocated memory in the function above */
+ free(err);
+@@ -168,6 +172,99 @@ static int sync_exit_cb(int fd, uint32_t events, void *cbdata, isula_epoll_descr
+ return EPOLL_LOOP_HANDLE_CLOSE;
+ }
+
++static bool fifo_exists(const char *path)
++{
++ struct stat sb;
++ int ret;
++
++ ret = stat(path, &sb);
++ if (ret < 0) {
++ // could be something other than exist, just return false
++ return false;
++ }
++
++ return S_ISFIFO(sb.st_mode);
++}
++
++static int add_attach_terminal_fifos(const char *in, const char *out, const char *err, int *input_fd, process_t *p)
++{
++ __isula_auto_close int fifofd_in = -1;
++ __isula_auto_close int fifofd_out = -1;
++ __isula_auto_close int fifofd_err = -1;
++ struct shim_fifos_fd *fifos = NULL;
++ struct isula_linked_list *node = NULL;
++
++ bool invalid = (in != NULL && !fifo_exists(in)) || (out != NULL && !fifo_exists(out)) || (err != NULL &&
++ !fifo_exists(err));
++ if (invalid) {
++ write_attach_message(ERR_MSG, "File %s or %s or %s does not refer to a FIFO", in, out, err);
++ return -1;
++ }
++
++ if (in != NULL) {
++ fifofd_in = isula_file_open(in, O_RDONLY | O_NONBLOCK | O_CLOEXEC, 0);
++ if (fifofd_in < 0) {
++ write_attach_message(ERR_MSG, "Failed to open FIFO: %s", in);
++ return -1;
++ }
++ }
++
++ if (out != NULL) {
++ fifofd_out = isula_file_open(out, O_WRONLY | O_NONBLOCK | O_CLOEXEC, 0);
++ if (fifofd_out < 0) {
++ write_attach_message(ERR_MSG, "Failed to open FIFO: %s", out);
++ return -1;
++ }
++ }
++
++ if (err != NULL) {
++ fifofd_err = isula_file_open(err, O_WRONLY | O_NONBLOCK | O_CLOEXEC, 0);
++ if (fifofd_err < 0) {
++ write_attach_message(ERR_MSG, "Failed to open FIFO: %s", err);
++ return -1;
++ }
++ }
++
++ fifos = isula_common_calloc_s(sizeof(*fifos));
++ if (fifos == NULL) {
++ write_attach_message(ERR_MSG, "Out of memory");
++ goto err_out;
++ }
++
++ fifos->in_fifo = isula_strdup_s(in);
++ fifos->out_fifo = isula_strdup_s(out);
++ fifos->err_fifo = isula_strdup_s(err);
++
++ fifos->in_fd = isula_transfer_fd(fifofd_in);
++ fifos->out_fd = isula_transfer_fd(fifofd_out);
++ fifos->err_fd = isula_transfer_fd(fifofd_err);
++ node = isula_common_calloc_s(sizeof(struct isula_linked_list));
++ if (node == NULL) {
++ write_attach_message(ERR_MSG, "Out of memory");
++ goto err_out;
++ }
++
++ node->elem = fifos;
++ isula_linked_list_add(p->attach_fifos, node);
++
++ if (input_fd != NULL) {
++ *input_fd = fifos->in_fd;
++ }
++
++ return 0;
++err_out:
++ free_shim_fifos_fd(fifos);
++ return -1;
++}
++
++static void remove_attach_terminal_fifos(isula_epoll_descr_t *descr, struct isula_linked_list *item)
++{
++ struct shim_fifos_fd *elem = (struct shim_fifos_fd *)item->elem;
++ isula_epoll_remove_handler(descr, elem->in_fd);
++ isula_linked_list_del(item);
++ free_shim_fifos_fd(elem);
++}
++
+ static int stdin_cb(int fd, uint32_t events, void *cbdata, isula_epoll_descr_t *descr)
+ {
+ process_t *p = (process_t *)cbdata;
+@@ -210,6 +307,57 @@ static int stdin_cb(int fd, uint32_t events, void *cbdata, isula_epoll_descr_t *
+ return EPOLL_LOOP_HANDLE_CONTINUE;
+ }
+
++static int attach_stdin_cb(int fd, uint32_t events, void *cbdata, isula_epoll_descr_t *descr)
++{
++ process_t *p = (process_t *)cbdata;
++ int r_count = 0;
++ int w_count = 0;
++ int *fd_to = NULL;
++ struct isula_linked_list *item;
++
++ if (events & EPOLLHUP) {
++ write_message(ERR_MSG, "attach stdin %d received the EPOLLHUP event", fd);
++ goto err_out;
++ }
++
++ if (!(events & EPOLLIN)) {
++ return EPOLL_LOOP_HANDLE_CONTINUE;
++ }
++
++ (void)memset(p->buf, 0, DEFAULT_IO_COPY_BUF);
++
++ r_count = isula_file_read_nointr(fd, p->buf, DEFAULT_IO_COPY_BUF);
++ if (r_count <= 0) {
++ write_message(ERR_MSG, "failed to read from attach stdin %d, error:%d", fd, SHIM_SYS_ERR(errno));
++ goto err_out;
++ }
++
++ if (p->state->terminal) {
++ fd_to = &(p->recv_fd);
++ } else {
++ fd_to = &(p->shim_io->in);
++ }
++
++ if (fd_to == NULL || *fd_to == -1) {
++ return EPOLL_LOOP_HANDLE_CONTINUE;
++ }
++ w_count = isula_file_total_write_nointr(*fd_to, p->buf, r_count);
++ if (w_count < 0) {
++ /* When any error occurs, set the write fd -1 */
++ write_message(WARN_MSG, "write in_fd %d error:%d", *fd_to, SHIM_SYS_ERR(errno));
++ close(*fd_to);
++ *fd_to = -1;
++ }
++
++ return EPOLL_LOOP_HANDLE_CONTINUE;
++err_out:
++ item = get_attach_fifo_item(fd, p->attach_fifos);
++ if (item != NULL && item->elem != NULL) {
++ remove_attach_terminal_fifos(descr, item);
++ }
++ return EPOLL_LOOP_HANDLE_CONTINUE;
++}
++
+ static int stdout_cb(int fd, uint32_t events, void *cbdata, isula_epoll_descr_t *descr)
+ {
+ process_t *p = (process_t *)cbdata;
+@@ -237,16 +385,29 @@ static int stdout_cb(int fd, uint32_t events, void *cbdata, isula_epoll_descr_t
+
+ shim_write_container_log_file(p->terminal, STDID_OUT, p->buf, r_count);
+
+- if (p->isulad_io->out == -1) {
++ if (p->isulad_io->out != -1) {
++ w_count = isula_file_total_write_nointr(p->isulad_io->out, p->buf, r_count);
++ if (w_count < 0) {
++ /* When any error occurs, set the write fd -1 */
++ write_message(WARN_MSG, "write out_fd %d error:%d", p->isulad_io->out, SHIM_SYS_ERR(errno));
++ close(p->isulad_io->out);
++ p->isulad_io->out = -1;
++ }
++ }
++
++ if (isula_linked_list_empty(p->attach_fifos)) {
+ return EPOLL_LOOP_HANDLE_CONTINUE;
+ }
+
+- w_count = isula_file_total_write_nointr(p->isulad_io->out, p->buf, r_count);
+- if (w_count < 0) {
+- /* When any error occurs, set the write fd -1 */
+- write_message(WARN_MSG, "write out_fd %d error:%d", p->isulad_io->out, SHIM_SYS_ERR(errno));
+- close(p->isulad_io->out);
+- p->isulad_io->out = -1;
++ struct isula_linked_list *it = NULL;
++ struct isula_linked_list *next = NULL;
++
++ isula_linked_list_for_each_safe(it, p->attach_fifos, next) {
++ struct shim_fifos_fd *elem = (struct shim_fifos_fd *)it->elem;
++ w_count = isula_file_total_write_nointr(elem->out_fd, p->buf, r_count);
++ if (w_count < 0) {
++ remove_attach_terminal_fifos(descr, it);
++ }
+ }
+
+ return EPOLL_LOOP_HANDLE_CONTINUE;
+@@ -279,16 +440,29 @@ static int stderr_cb(int fd, uint32_t events, void *cbdata, isula_epoll_descr_t
+
+ shim_write_container_log_file(p->terminal, STDID_ERR, p->buf, r_count);
+
+- if (p->isulad_io->err == -1) {
++ if (p->isulad_io->err != -1) {
++ w_count = isula_file_total_write_nointr(p->isulad_io->err, p->buf, r_count);
++ if (w_count < 0) {
++ /* When any error occurs, set the write fd -1 */
++ write_message(WARN_MSG, "write err_fd %d error:%d", p->isulad_io->err, SHIM_SYS_ERR(errno));
++ close(p->isulad_io->err);
++ p->isulad_io->err = -1;
++ }
++ }
++
++ if (isula_linked_list_empty(p->attach_fifos)) {
+ return EPOLL_LOOP_HANDLE_CONTINUE;
+ }
+
+- w_count = isula_file_total_write_nointr(p->isulad_io->err, p->buf, r_count);
+- if (w_count < 0) {
+- /* When any error occurs, set the write fd -1 */
+- write_message(WARN_MSG, "write err_fd %d error:%d", p->isulad_io->err, SHIM_SYS_ERR(errno));
+- close(p->isulad_io->err);
+- p->isulad_io->err = -1;
++ struct isula_linked_list *it = NULL;
++ struct isula_linked_list *next = NULL;
++
++ isula_linked_list_for_each_safe(it, p->attach_fifos, next) {
++ struct shim_fifos_fd *elem = (struct shim_fifos_fd *)it->elem;
++ w_count = isula_file_total_write_nointr(elem->out_fd, p->buf, r_count);
++ if (w_count < 0) {
++ remove_attach_terminal_fifos(descr, it);
++ }
+ }
+
+ return EPOLL_LOOP_HANDLE_CONTINUE;
+@@ -326,6 +500,159 @@ static int resize_cb(int fd, uint32_t events, void *cbdata, isula_epoll_descr_t
+ return EPOLL_LOOP_HANDLE_CONTINUE;
+ }
+
++static bool attach_fifopath_security_check(process_t *p, const char *fifopath)
++{
++ struct stat st = { 0 };
++ char real_path[PATH_MAX] = { 0 };
++
++ if (isula_validate_absolute_path(fifopath) != 0) {
++ write_attach_message(ERR_MSG, "attach fifo path \"%s\" must be an valid absolute path", fifopath);
++ return false;
++ }
++
++ if (realpath(fifopath, real_path) == NULL) {
++ write_attach_message(ERR_MSG, "Failed to get realpath for '%s': %s.", real_path, SHIM_SYS_ERR(errno));
++ return false;
++ }
++
++ if (!isula_has_prefix(real_path, p->workdir)) {
++ write_attach_message(ERR_MSG, "attach fifo path \"%s\" must be under the state path", real_path, p->workdir);
++ return false;
++ }
++
++ if (lstat(real_path, &st) != 0) {
++ write_attach_message(ERR_MSG, "Failed to lstat %s : %s", real_path, SHIM_SYS_ERR(errno));
++ return false;
++ }
++
++ if (!S_ISFIFO(st.st_mode)) {
++ write_attach_message(ERR_MSG, "attach fifo path \"%s\" must be an FIFO", real_path);
++ return false;
++ }
++
++ if ((st.st_mode & 0777) != ATTACH_FIFOPATH_MODE) {
++ write_attach_message(ERR_MSG, "attach fifo path \"%s\" permission invalid", real_path);
++ return false;
++ }
++
++ if (st.st_uid != 0) {
++ write_attach_message(ERR_MSG, "attach fifo path \"%s\" uid invalid", real_path);
++ return false;
++ }
++
++ return true;
++}
++
++// attach_cb needs to read the content from communication fd and parse it.
++// at the same time, it also needs to establish a connection between the attach fd and the container fd.
++// 1. if it fails, it needs to write an error message to attach log file,
++// and write -1 to connection fd to let isulad know that it has failed.
++// 2. if it succeeds, write 0 to let isulad know that it is ready.
++// attach_cb returns EPOLL_LOOP_HANDLE_CONTINUE regardless of success or failure,
++// because whether the attach operation is successful or not does not affect the first process of the container.
++static int attach_cb(int fd, uint32_t events, void *cbdata, isula_epoll_descr_t *descr)
++{
++ process_t *p = (process_t *)cbdata;
++ int r_count = 0;
++ char tmp_buf[BUFSIZ + 1] = { 0 };
++ char *in = NULL, *out = NULL, *err = NULL;
++ int fifofd_in = -1;
++ isula_string_array *tmp_str_array = NULL;
++ int ret = 0;
++ // attach execution return value
++ int status = -1;
++ bool valid = true;
++
++ // after receiving the event that isulad closes the connection,
++ // close the communication fd and remove it from epoll.
++ if (events & EPOLLHUP) {
++ close(fd);
++ isula_epoll_remove_handler(descr, fd);
++ return EPOLL_LOOP_HANDLE_CONTINUE;
++ }
++
++ if (!(events & EPOLLIN)) {
++ return EPOLL_LOOP_HANDLE_CONTINUE;
++ }
++
++ r_count = isula_file_read_nointr(fd, tmp_buf, sizeof(tmp_buf) - 1);
++ if (r_count <= 0) {
++ write_attach_message(ERR_MSG, "Failed to read msg from attach conn fd");
++ goto out;
++ }
++
++ // limit the number of attach connections to MAX_ATTACH_NUM
++ if (isula_linked_list_len(p->attach_fifos) >= MAX_ATTACH_NUM) {
++ write_attach_message(ERR_MSG, "The number of attach connections exceeds the limit:%d, and this connection is rejected.",
++ MAX_ATTACH_NUM);
++ goto out;
++ }
++
++ tmp_str_array = isula_string_split_to_multi(tmp_buf, ' ');
++ if (tmp_str_array->len != 3) {
++ write_attach_message(ERR_MSG, "Invalid attach msg from isulad");
++ goto out;
++ }
++
++ for (int i = 0; i < tmp_str_array->len; i++) {
++ valid = valid && attach_fifopath_security_check(p, tmp_str_array->items[i]);
++ }
++
++ if (!valid) {
++ write_attach_message(ERR_MSG, "Invalid attach fifo path from isulad");
++ goto out;
++ }
++
++ in = tmp_str_array->items[0];
++ out = tmp_str_array->items[1];
++ err = tmp_str_array->items[2];
++
++ if (add_attach_terminal_fifos(in, out, err, &fifofd_in, p) < 0) {
++ write_attach_message(ERR_MSG, "Failed to add attach terminal fifos");
++ goto out;
++ }
++
++ // attach stdin --> container stdin
++ ret = isula_epoll_add_handler(descr, fifofd_in, attach_stdin_cb, p);
++ if (ret != SHIM_OK) {
++ write_attach_message(ERR_MSG, "add fifofd_in fd %d to epoll loop failed:%d", fifofd_in, SHIM_SYS_ERR(errno));
++ struct isula_linked_list *item = get_attach_fifo_item(fd, p->attach_fifos);
++ if (item != NULL && item->elem != NULL) {
++ remove_attach_terminal_fifos(descr, item);
++ }
++ goto out;
++ }
++
++ status = 0;
++out:
++ isula_string_array_free(tmp_str_array);
++ (void)isula_file_write_nointr(fd, &status, sizeof(int));
++ return EPOLL_LOOP_HANDLE_CONTINUE;
++}
++
++// do_attach_socket_accept returns EPOLL_LOOP_HANDLE_CONTINUE regardless of success or failure,
++// because whether the attach operation is successful or not does not affect the first process of the container.
++static int do_attach_socket_accept(int fd, uint32_t events, void *cbdata, isula_epoll_descr_t *descr)
++{
++ process_t *p = (process_t *)cbdata;
++ int conn_fd = -1;
++ int ret = SHIM_ERR;
++
++ conn_fd = accept(p->attach_socket_fd, NULL, NULL);
++ if (conn_fd < 0) {
++ write_attach_message(ERR_MSG, "accept from fd %d failed:%d", p->attach_socket_fd, SHIM_SYS_ERR(errno));
++ return EPOLL_LOOP_HANDLE_CONTINUE;
++ }
++
++ ret = isula_epoll_add_handler(descr, conn_fd, attach_cb, p);
++ if (ret != SHIM_OK) {
++ write_attach_message(ERR_MSG, "add recv_fd %d to epoll loop failed:%d", conn_fd, SHIM_SYS_ERR(errno));
++ close(conn_fd);
++ return EPOLL_LOOP_HANDLE_CONTINUE;
++ }
++ return EPOLL_LOOP_HANDLE_CONTINUE;
++}
++
+ static int task_console_accept(int fd, uint32_t events, void *cbdata, isula_epoll_descr_t *descr)
+ {
+ process_t *p = (process_t *)cbdata;
+@@ -399,6 +726,7 @@ static void stdio_release(int (*stdio_fd)[2])
+ for (j = 0; j < 2; j++) {
+ if (stdio_fd[i][j] > 0) {
+ close(stdio_fd[i][j]);
++ stdio_fd[i][j] = -1;
+ }
+ }
+ }
+@@ -568,24 +896,6 @@ static int open_generic_io(process_t *p, isula_epoll_descr_t *descr)
+ return SHIM_OK;
+ }
+
+-static int set_non_block(int fd)
+-{
+- int flag = -1;
+- int ret = SHIM_ERR;
+-
+- flag = fcntl(fd, F_GETFL, 0);
+- if (flag < 0) {
+- return SHIM_ERR;
+- }
+-
+- ret = fcntl(fd, F_SETFL, flag | O_NONBLOCK);
+- if (ret != 0) {
+- return SHIM_ERR;
+- }
+-
+- return SHIM_OK;
+-}
+-
+ /*
+ std_id: channel type
+ isulad_stdio: one side of the isulad fifo file
+@@ -623,6 +933,14 @@ static void *io_epoll_loop(void *data)
+ exit(EXIT_FAILURE);
+ }
+
++ if (p->state->attach_socket != NULL) {
++ ret = isula_epoll_add_handler(&descr, p->attach_socket_fd, do_attach_socket_accept, p);
++ if (ret != SHIM_OK) {
++ write_message(ERR_MSG, "add attach_socket_fd %d to epoll loop failed:%d", p->attach_socket_fd, SHIM_SYS_ERR(errno));
++ exit(EXIT_FAILURE);
++ }
++ }
++
+ if (p->state->terminal) {
+ ret = open_terminal_io(p, &descr);
+ } else {
+@@ -651,7 +969,7 @@ static void *io_epoll_loop(void *data)
+ }
+
+ if (fd_out > 0) {
+- ret = set_non_block(fd_out);
++ ret = isula_set_non_block(fd_out);
+ if (ret != SHIM_OK) {
+ write_message(ERR_MSG, "set fd %d non_block failed:%d", fd_out, SHIM_SYS_ERR(errno));
+ exit(EXIT_FAILURE);
+@@ -666,7 +984,7 @@ static void *io_epoll_loop(void *data)
+ }
+
+ if (fd_err > 0) {
+- ret = set_non_block(fd_err);
++ ret = isula_set_non_block(fd_err);
+ if (ret != SHIM_OK) {
+ write_message(ERR_MSG, "set fd %d non_block failed:%d", fd_err, SHIM_SYS_ERR(errno));
+ exit(EXIT_FAILURE);
+@@ -807,15 +1125,19 @@ failure:
+ if (p->isulad_io != NULL) {
+ if (p->isulad_io->in > 0) {
+ close(p->isulad_io->in);
++ p->isulad_io->in = -1;
+ }
+ if (p->isulad_io->out > 0) {
+ close(p->isulad_io->out);
++ p->isulad_io->out = -1;
+ }
+ if (p->isulad_io->err > 0) {
+ close(p->isulad_io->err);
++ p->isulad_io->err = -1;
+ }
+ if (p->isulad_io->resize > 0) {
+ close(p->isulad_io->resize);
++ p->isulad_io->resize = -1;
+ }
+ free(p->isulad_io);
+ p->isulad_io = NULL;
+@@ -937,6 +1259,13 @@ process_t *new_process(char *id, char *bundle, char *runtime)
+ goto failure;
+ }
+
++ p->attach_fifos = isula_common_calloc_s(sizeof(struct isula_linked_list));
++ if (p->attach_fifos == NULL) {
++ goto failure;
++ }
++
++ isula_linked_list_init(p->attach_fifos);
++
+ return p;
+
+ failure:
+@@ -1368,3 +1697,49 @@ int process_signal_handle_routine(process_t *p, const pthread_t tid_epoll, const
+ (void)isula_file_write_nointr(STDOUT_FILENO, &status, sizeof(int));
+ return SHIM_OK;
+ }
++
++int prepare_attach_socket(process_t *p)
++{
++ struct sockaddr_un addr;
++ int ret = -1;
++
++ if (strlen(p->state->attach_socket) >= sizeof(addr.sun_path)) {
++ write_message(ERR_MSG, "Invalid attach socket path: %s", p->state->attach_socket);
++ return SHIM_ERR;
++ }
++
++ p->attach_socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
++ if (p->attach_socket_fd < 0) {
++ write_message(ERR_MSG, "Failed to create socket:%d", SHIM_SYS_ERR(errno));
++ return SHIM_ERR;
++ }
++
++ (void)memset(&addr, 0, sizeof(addr));
++ addr.sun_family = AF_UNIX;
++ (void)strncpy(addr.sun_path, p->state->attach_socket, sizeof(addr.sun_path) - 1);
++
++ ret = bind(p->attach_socket_fd, (struct sockaddr *)&addr, sizeof(addr));
++ if (ret < 0) {
++ write_message(ERR_MSG, "bind console fd failed:%d", SHIM_SYS_ERR(errno));
++ return SHIM_ERR;
++ }
++
++ ret = chmod(p->state->attach_socket, SOCKET_DIRECTORY_MODE);
++ if (ret != 0) {
++ write_message(ERR_MSG, "Failed to chmod for socket: %s", p->state->attach_socket);
++ return SHIM_ERR;
++ }
++
++ //If the backlog argument is greater than the value in
++ // /proc/sys/net/core/somaxconn, then it is silently capped to that
++ // value. Since Linux 5.4, the default in this file is 4096; in
++ // earlier kernels, the default value is 128. Before Linux 2.4.25,
++ // this limit was a hard coded value, SOMAXCONN, with the value 128.
++ // The maximum number of attach we allow here is MAX_ATTACH_NUM, so just use it directly
++ ret = listen(p->attach_socket_fd, MAX_ATTACH_NUM);
++ if (ret < 0) {
++ write_message(ERR_MSG, "listen console fd failed:%d", SHIM_SYS_ERR(errno));
++ return SHIM_ERR;
++ }
++ return SHIM_OK;
++}
+\ No newline at end of file
+diff --git a/src/cmd/isulad-shim/process.h b/src/cmd/isulad-shim/process.h
+index 280d9874..5607316c 100644
+--- a/src/cmd/isulad-shim/process.h
++++ b/src/cmd/isulad-shim/process.h
+@@ -19,7 +19,8 @@
+ #include <pthread.h>
+ #include <semaphore.h>
+ #include <stdbool.h>
+-#include "isula_libutils/shim_client_process_state.h"
++#include <isula_libutils/shim_client_process_state.h>
++#include "isula_libutils/utils_linked_list.h"
+ #include "terminal.h"
+
+ #ifdef __cplusplus
+@@ -49,6 +50,7 @@ typedef struct process {
+ char *root_path;
+ int io_loop_fd;
+ int exit_fd;
++ int attach_socket_fd; // the server socket fd that establishes a connection with isulad
+ int ctr_pid;
+ int sync_fd;
+ int listen_fd;
+@@ -58,6 +60,7 @@ typedef struct process {
+ stdio_t *stdio; // shim to on runtime side, in:r out/err: w
+ stdio_t *shim_io; // shim io on isulad side, in: w out/err: r
+ stdio_t *isulad_io; // isulad io, in:r out/err: w
++ struct isula_linked_list *attach_fifos; /* isulad: fifos used to attach teminal */
+ shim_client_process_state *state;
+ sem_t sem_mainloop;
+ char *buf;
+@@ -70,6 +73,8 @@ typedef struct {
+
+ process_t* new_process(char *id, char *bundle, char *runtime);
+
++int prepare_attach_socket(process_t *p);
++
+ int process_io_start(process_t *p, pthread_t *tid_epoll);
+ int create_process(process_t *p);
+ int process_signal_handle_routine(process_t *p, const pthread_t tid_epoll, const uint64_t timeout);
+diff --git a/src/cmd/isulad-shim/terminal.c b/src/cmd/isulad-shim/terminal.c
+index 0653dc45..1c063300 100644
+--- a/src/cmd/isulad-shim/terminal.c
++++ b/src/cmd/isulad-shim/terminal.c
+@@ -162,7 +162,7 @@ static int shim_json_data_write(log_terminal *terminal, const char *buf, int rea
+ * shouldn't happen, otherwise, discard some last bytes.
+ */
+ nret = isula_file_total_write_nointr(terminal->fd, buf,
+- terminal->log_maxsize < read_count ? terminal->log_maxsize : read_count);
++ terminal->log_maxsize < read_count ? terminal->log_maxsize : read_count);
+ if (nret < 0) {
+ ret = -1;
+ goto out;
+diff --git a/src/daemon/executor/container_cb/execution_stream.c b/src/daemon/executor/container_cb/execution_stream.c
+index 7db96b19..124dcfe2 100644
+--- a/src/daemon/executor/container_cb/execution_stream.c
++++ b/src/daemon/executor/container_cb/execution_stream.c
+@@ -346,6 +346,7 @@ static int container_attach_cb(const container_attach_request *request, containe
+ }
+
+ params.rootpath = cont->root_path;
++ params.state = cont->state_path;
+ params.stdin = fifos[0];
+ params.stdout = fifos[1];
+ params.stderr = fifos[2];
+diff --git a/src/daemon/modules/api/runtime_api.h b/src/daemon/modules/api/runtime_api.h
+index edb33d02..3c2100f5 100644
+--- a/src/daemon/modules/api/runtime_api.h
++++ b/src/daemon/modules/api/runtime_api.h
+@@ -161,6 +161,7 @@ typedef struct _rt_resume_params_t {
+
+ typedef struct _rt_attach_params_t {
+ const char *rootpath;
++ const char *state;
+ const char *stdin;
+ const char *stdout;
+ const char *stderr;
+diff --git a/src/daemon/modules/runtime/isula/isula_rt_ops.c b/src/daemon/modules/runtime/isula/isula_rt_ops.c
+index cb1ee26f..1787170b 100644
+--- a/src/daemon/modules/runtime/isula/isula_rt_ops.c
++++ b/src/daemon/modules/runtime/isula/isula_rt_ops.c
+@@ -18,6 +18,10 @@
+ #include "isula_rt_ops.h"
+ #include <unistd.h>
+ #include <sys/wait.h>
++#include <stdio.h>
++#include <sys/socket.h>
++#include <sys/un.h>
++#include <sys/types.h>
+ #include <limits.h>
+ #include <errno.h>
+ #include <fcntl.h>
+@@ -26,6 +30,7 @@
+ #include <isula_libutils/isulad_daemon_configs.h>
+ #include <isula_libutils/json_common.h>
+ #include <isula_libutils/oci_runtime_spec.h>
++#include <isula_libutils/utils_file.h>
+ #include <signal.h>
+ #include <stdint.h>
+ #include <stdio.h>
+@@ -52,9 +57,11 @@
+
+ #define SHIM_BINARY "isulad-shim"
+ #define RESIZE_FIFO_NAME "resize_fifo"
++#define ATTACH_SOCKET "attach_socket.sock"
+ #define SHIM_LOG_SIZE ((BUFSIZ - 100) / 2)
+ #define RESIZE_DATA_SIZE 100
+ #define PID_WAIT_TIME 120
++#define ATTACH_WAIT_TIME 120
+ #define SHIM_EXIT_TIMEOUT 2
+
+ // file name formats of cgroup resources json
+@@ -223,6 +230,19 @@ static void show_shim_runtime_errlog(const char *workdir)
+ isulad_set_error_message(buf);
+ }
+
++static void show_shim_attach_errlog(const char *workdir)
++{
++ char buf[SHIM_LOG_SIZE] = { 0 };
++
++ if (g_isulad_errmsg != NULL) {
++ return;
++ }
++
++ get_err_message(buf, sizeof(buf), workdir, "attach-log.json");
++ ERROR("shim-log: %s", buf);
++ isulad_set_error_message("shim-log error:\n%s\n", buf);
++}
++
+ bool rt_isula_detect(const char *runtime)
+ {
+ if (runtime != NULL && (strcasecmp(runtime, "lcr") != 0)) {
+@@ -463,8 +483,9 @@ static void runtime_exec_param_init(runtime_exec_info *rei)
+ }
+ }
+
+-static int runtime_exec_info_init(runtime_exec_info *rei, const char *workdir, const char *root_path, const char *runtime, const char *subcmd,
+- const char **opts, size_t opts_len, const char *id, char **params, size_t params_num)
++static int runtime_exec_info_init(runtime_exec_info *rei, const char *workdir, const char *root_path,
++ const char *runtime, const char *subcmd, const char **opts, size_t opts_len, const char *id, char **params,
++ size_t params_num)
+ {
+ int ret = 0;
+ rei->workdir = workdir;
+@@ -1012,6 +1033,7 @@ int rt_isula_create(const char *id, const char *runtime, const rt_create_params_
+ size_t runtime_args_len = 0;
+ int ret = 0;
+ char workdir[PATH_MAX] = { 0 };
++ char attach_socket[PATH_MAX] = { 0 };
+ shim_client_process_state p = { 0 };
+ int shim_exit_code = 0;
+ int nret = 0;
+@@ -1034,6 +1056,13 @@ int rt_isula_create(const char *id, const char *runtime, const rt_create_params_
+ goto out;
+ }
+
++ nret = snprintf(attach_socket, sizeof(attach_socket), "%s/%s", workdir, ATTACH_SOCKET);
++ if (nret < 0 || (size_t)nret >= sizeof(attach_socket)) {
++ INFO("Failed to get full attach socket path");
++ ret = -1;
++ goto out;
++ }
++
+ p.exit_fifo = (char *)params->exit_fifo;
+ p.open_tty = params->tty;
+ p.open_stdin = params->open_stdin;
+@@ -1042,6 +1071,7 @@ int rt_isula_create(const char *id, const char *runtime, const rt_create_params_
+ p.isulad_stderr = (char *)params->stderr;
+ p.runtime_args = (char **)runtime_args;
+ p.runtime_args_len = runtime_args_len;
++ p.attach_socket = attach_socket;
+ copy_process(&p, config->process);
+ copy_annotations(&p, config->annotations);
+
+@@ -1224,7 +1254,7 @@ static bool fg_exec(const rt_exec_params_t *params)
+ return false;
+ }
+
+-static char *try_generate_exec_id()
++static char *try_generate_random_id()
+ {
+ char *id = NULL;
+
+@@ -1324,7 +1354,7 @@ int rt_isula_exec(const char *id, const char *runtime, const rt_exec_params_t *p
+ if (params->suffix != NULL) {
+ exec_id = util_strdup_s(params->suffix);
+ } else {
+- exec_id = try_generate_exec_id();
++ exec_id = try_generate_random_id();
+ }
+ if (exec_id == NULL) {
+ ERROR("Out of memory or generate exec id failed");
+@@ -1423,13 +1453,133 @@ out:
+ return ret;
+ }
+
+-int rt_isula_attach(const char *id, const char *runtime, const rt_attach_params_t *params)
++static int get_container_attach_statuscode(const char *workdir, int attach_shim_fd)
+ {
+- ERROR("isula attach not support on isulad-shim");
+- isulad_set_error_message("isula attach not support on isulad-shim");
++ int status_code = 0;
++ int ret = -1;
++ struct timespec beg = { 0 };
++ struct timespec end = { 0 };
++
++ if (clock_gettime(CLOCK_MONOTONIC, &beg) != 0) {
++ ERROR("Failed get time");
++ return -1;
++ }
++
++ while (true) {
++ if (clock_gettime(CLOCK_MONOTONIC, &end) != 0) {
++ ERROR("Failed get time");
++ return -1;
++ }
++ if (end.tv_sec - beg.tv_sec > ATTACH_WAIT_TIME) {
++ ERROR("Wait container attach exitcode timeout");
++ return -1;
++ }
++ ret = util_read_nointr(attach_shim_fd, &status_code, sizeof(int));
++ if (ret <= 0) {
++ if (shim_alive(workdir)) {
++ // wait 100 millisecond to read exit code
++ util_usleep_nointerupt(100000);
++ continue;
++ }
++ ERROR("Failed read pid from dead shim %s", workdir);
++ return -1;
++ }
++ return status_code; /* success */
++ }
+ return -1;
+ }
+
++static int get_attach_socketfd(const char *attach_socket, int *socket_fd)
++{
++ struct sockaddr_un addr = { 0 };
++ __isula_auto_close int tmp_socket = -1;
++
++ if (strlen(attach_socket) >= sizeof(addr.sun_path)) {
++ SYSERROR("Invalid attach socket path: %s", attach_socket);
++ return -1;
++ }
++
++ tmp_socket = socket(AF_UNIX, SOCK_STREAM, 0);
++ if (tmp_socket < 0) {
++ SYSERROR("Failed to create attach socket");
++ return -1;
++ }
++
++ if (isula_set_non_block(tmp_socket) < 0) {
++ SYSERROR("Failed to set socket non block");
++ return -1;
++ }
++
++ (void)memset(&addr, 0, sizeof(addr));
++ addr.sun_family = AF_UNIX;
++ (void)strcpy(addr.sun_path, attach_socket);
++
++ if (connect(tmp_socket, (void *)&addr, sizeof(addr)) < 0) {
++ SYSERROR("Failed to connect attach socket: %s", attach_socket);
++ return -1;
++ }
++ *socket_fd = isula_transfer_fd(tmp_socket);
++ return 0;
++}
++
++int rt_isula_attach(const char *id, const char *runtime, const rt_attach_params_t *params)
++{
++ int ret = 0;
++ int len = 0;
++ int status_code = 0;
++ __isula_auto_close int socket_fd = -1;
++ char buf[BUFSIZ] = { 0 };
++ char workdir[PATH_MAX] = { 0 };
++ char attach_socket[PATH_MAX] = { 0 };
++
++ if (id == NULL || runtime == NULL || params == NULL) {
++ ERROR("Null argument");
++ return -1;
++ }
++
++ ret = snprintf(workdir, sizeof(workdir), "%s/%s", params->state, id);
++ if (ret < 0 || (size_t)ret >= sizeof(workdir)) {
++ ERROR("Failed join exec full path");
++ return -1;
++ }
++
++ // the communication format between isulad and isulad-shim attach is:
++ // stdin-path stdout-path stderr-path
++ len = snprintf(buf, sizeof(buf), "%s %s %s", params->stdin, params->stdout, params->stderr);
++ if (len < 0 || (size_t)len >= sizeof(buf)) {
++ ERROR("Failed to snprintf string");
++ return -1;
++ }
++
++ ret = snprintf(attach_socket, sizeof(attach_socket), "%s/%s", workdir, ATTACH_SOCKET);
++ if (ret < 0 || (size_t)ret >= sizeof(attach_socket)) {
++ ERROR("Failed to get full attach socket path");
++ return -1;
++ }
++
++ ret = get_attach_socketfd(attach_socket, &socket_fd);
++ if (ret < 0) {
++ ERROR("Failed to get attach socketfd");
++ return -1;
++ }
++
++ DEBUG("write %s to attach fd", buf);
++
++ ret = isula_file_write_nointr(socket_fd, buf, len);
++ if (ret < 0) {
++ SYSERROR("Failed to write attach isulad fd");
++ return -1;
++ }
++
++ status_code = get_container_attach_statuscode(workdir, socket_fd);
++ if (status_code < 0) {
++ show_shim_attach_errlog(workdir);
++ return -1;
++ }
++
++ return 0;
++}
++
+ static int to_engine_resources_unified(const host_config *hostconfig, shim_client_cgroup_resources *cr)
+ {
+ int i;
+@@ -1673,7 +1823,7 @@ static int parse_ps_data(char *stdout_msg, rt_listpids_out_t *out)
+ }
+
+ static int runtime_call_ps(const char *workdir, const char *runtime, const char *id,
+- rt_listpids_out_t *out)
++ rt_listpids_out_t *out)
+ {
+ __isula_auto_free char *stdout_msg = NULL;
+ __isula_auto_free char *stderr_msg = NULL;
+@@ -1681,7 +1831,7 @@ static int runtime_call_ps(const char *workdir, const char *runtime, const char
+ int ret = 0;
+ int nret = 0;
+ char *params[PARAM_NUM] = { 0 };
+- const char *opts[2] = { "--format" , "json" };
++ const char *opts[2] = { "--format", "json" };
+ char root_path[PATH_MAX] = { 0 };
+
+ nret = snprintf(root_path, PATH_MAX, "%s/%s", workdir, runtime);
+diff --git a/src/utils/cutils/error.h b/src/utils/cutils/error.h
+index 088ed261..75eae760 100644
+--- a/src/utils/cutils/error.h
++++ b/src/utils/cutils/error.h
+@@ -44,8 +44,10 @@ extern "C" {
+ /* err in runtime module */ \
+ XX(ERR_RUNTIME, DEF_ERR_RUNTIME_STR) \
+ \
++ /* info for detach */ \
++ XX(INFO_DETACH, "Attach detach") \
+ /* err max */ \
+- XX(ERR_UNKNOWN, "Unknown error")
++ XX(ERR_UNKNOWN, "Unknown error")
+
+ #define ISULAD_ERRNO_GEN(n, s) ISULAD_##n,
+ typedef enum { ISULAD_ERRNO_MAP(ISULAD_ERRNO_GEN) } isulad_errno_t;
+--
+2.42.0
+