commands: add ability to audit fd connection and cleanup path

parent ab92468c
...@@ -284,6 +284,9 @@ static int lxc_cmd(const char *name, struct lxc_cmd_rr *cmd, int *stopped, ...@@ -284,6 +284,9 @@ static int lxc_cmd(const char *name, struct lxc_cmd_rr *cmd, int *stopped,
if (ret < 0 && errno == ECONNRESET) if (ret < 0 && errno == ECONNRESET)
*stopped = 1; *stopped = 1;
TRACE("Opened new command socket connection fd %d for command \"%s\"",
client_fd, lxc_cmd_str(cmd->req.cmd));
if (stay_connected && ret > 0) if (stay_connected && ret > 0)
cmd->rsp.ret = move_fd(client_fd); cmd->rsp.ret = move_fd(client_fd);
...@@ -357,11 +360,16 @@ static int lxc_cmd_get_init_pid_callback(int fd, struct lxc_cmd_req *req, ...@@ -357,11 +360,16 @@ static int lxc_cmd_get_init_pid_callback(int fd, struct lxc_cmd_req *req,
struct lxc_handler *handler, struct lxc_handler *handler,
struct lxc_epoll_descr *descr) struct lxc_epoll_descr *descr)
{ {
int ret;
struct lxc_cmd_rsp rsp = { struct lxc_cmd_rsp rsp = {
.data = PID_TO_PTR(handler->pid) .data = PID_TO_PTR(handler->pid)
}; };
return lxc_cmd_rsp_send(fd, &rsp); ret = lxc_cmd_rsp_send(fd, &rsp);
if (ret < 0)
return LXC_CMD_REAP_CLIENT_FD;
return 0;
} }
/* /*
...@@ -392,11 +400,16 @@ static int lxc_cmd_get_clone_flags_callback(int fd, struct lxc_cmd_req *req, ...@@ -392,11 +400,16 @@ static int lxc_cmd_get_clone_flags_callback(int fd, struct lxc_cmd_req *req,
struct lxc_handler *handler, struct lxc_handler *handler,
struct lxc_epoll_descr *descr) struct lxc_epoll_descr *descr)
{ {
int ret;
struct lxc_cmd_rsp rsp = { struct lxc_cmd_rsp rsp = {
.data = INT_TO_PTR(handler->ns_clone_flags), .data = INT_TO_PTR(handler->ns_clone_flags),
}; };
return lxc_cmd_rsp_send(fd, &rsp); ret = lxc_cmd_rsp_send(fd, &rsp);
if (ret < 0)
return LXC_CMD_REAP_CLIENT_FD;
return 0;
} }
/* /*
...@@ -445,6 +458,7 @@ static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req, ...@@ -445,6 +458,7 @@ static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req,
struct lxc_handler *handler, struct lxc_handler *handler,
struct lxc_epoll_descr *descr) struct lxc_epoll_descr *descr)
{ {
int ret;
const char *path; const char *path;
struct lxc_cmd_rsp rsp; struct lxc_cmd_rsp rsp;
struct cgroup_ops *cgroup_ops = handler->cgroup_ops; struct cgroup_ops *cgroup_ops = handler->cgroup_ops;
...@@ -460,7 +474,11 @@ static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req, ...@@ -460,7 +474,11 @@ static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req,
rsp.datalen = strlen(path) + 1; rsp.datalen = strlen(path) + 1;
rsp.data = (char *)path; rsp.data = (char *)path;
return lxc_cmd_rsp_send(fd, &rsp); ret = lxc_cmd_rsp_send(fd, &rsp);
if (ret < 0)
return LXC_CMD_REAP_CLIENT_FD;
return 0;
} }
/* /*
...@@ -525,7 +543,11 @@ static int lxc_cmd_get_config_item_callback(int fd, struct lxc_cmd_req *req, ...@@ -525,7 +543,11 @@ static int lxc_cmd_get_config_item_callback(int fd, struct lxc_cmd_req *req,
err1: err1:
rsp.ret = -1; rsp.ret = -1;
out: out:
return lxc_cmd_rsp_send(fd, &rsp); cilen = lxc_cmd_rsp_send(fd, &rsp);
if (cilen < 0)
return LXC_CMD_REAP_CLIENT_FD;
return 0;
} }
/* /*
...@@ -564,11 +586,16 @@ static int lxc_cmd_get_state_callback(int fd, struct lxc_cmd_req *req, ...@@ -564,11 +586,16 @@ static int lxc_cmd_get_state_callback(int fd, struct lxc_cmd_req *req,
struct lxc_handler *handler, struct lxc_handler *handler,
struct lxc_epoll_descr *descr) struct lxc_epoll_descr *descr)
{ {
int ret;
struct lxc_cmd_rsp rsp = { struct lxc_cmd_rsp rsp = {
.data = INT_TO_PTR(handler->state), .data = INT_TO_PTR(handler->state),
}; };
return lxc_cmd_rsp_send(fd, &rsp); ret = lxc_cmd_rsp_send(fd, &rsp);
if (ret < 0)
return LXC_CMD_REAP_CLIENT_FD;
return 0;
} }
/* /*
...@@ -613,6 +640,7 @@ static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req, ...@@ -613,6 +640,7 @@ static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req,
struct lxc_cmd_rsp rsp; struct lxc_cmd_rsp rsp;
int stopsignal = SIGKILL; int stopsignal = SIGKILL;
struct cgroup_ops *cgroup_ops = handler->cgroup_ops; struct cgroup_ops *cgroup_ops = handler->cgroup_ops;
int ret;
if (handler->conf->stopsignal) if (handler->conf->stopsignal)
stopsignal = handler->conf->stopsignal; stopsignal = handler->conf->stopsignal;
...@@ -629,7 +657,11 @@ static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req, ...@@ -629,7 +657,11 @@ static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req,
rsp.ret = -errno; rsp.ret = -errno;
} }
return lxc_cmd_rsp_send(fd, &rsp); ret = lxc_cmd_rsp_send(fd, &rsp);
if (ret < 0)
return LXC_CMD_REAP_CLIENT_FD;
return 0;
} }
/* /*
...@@ -706,24 +738,18 @@ static int lxc_cmd_console_callback(int fd, struct lxc_cmd_req *req, ...@@ -706,24 +738,18 @@ static int lxc_cmd_console_callback(int fd, struct lxc_cmd_req *req,
masterfd = lxc_terminal_allocate(handler->conf, fd, &ttynum); masterfd = lxc_terminal_allocate(handler->conf, fd, &ttynum);
if (masterfd < 0) if (masterfd < 0)
goto out_close; return LXC_CMD_REAP_CLIENT_FD;
memset(&rsp, 0, sizeof(rsp)); memset(&rsp, 0, sizeof(rsp));
rsp.data = INT_TO_PTR(ttynum); rsp.data = INT_TO_PTR(ttynum);
ret = lxc_abstract_unix_send_fds(fd, &masterfd, 1, &rsp, sizeof(rsp)); ret = lxc_abstract_unix_send_fds(fd, &masterfd, 1, &rsp, sizeof(rsp));
if (ret < 0) { if (ret < 0) {
SYSERROR("Failed to send tty to client");
lxc_terminal_free(handler->conf, fd); lxc_terminal_free(handler->conf, fd);
goto out_close; return log_error_errno(LXC_CMD_REAP_CLIENT_FD, errno,
"Failed to send tty to client");
} }
return 0; return 0;
out_close:
/* Special indicator to lxc_cmd_handler() to close the fd and do
* related cleanup.
*/
return 1;
} }
/* /*
...@@ -756,6 +782,7 @@ static int lxc_cmd_get_name_callback(int fd, struct lxc_cmd_req *req, ...@@ -756,6 +782,7 @@ static int lxc_cmd_get_name_callback(int fd, struct lxc_cmd_req *req,
struct lxc_handler *handler, struct lxc_handler *handler,
struct lxc_epoll_descr *descr) struct lxc_epoll_descr *descr)
{ {
int ret;
struct lxc_cmd_rsp rsp; struct lxc_cmd_rsp rsp;
memset(&rsp, 0, sizeof(rsp)); memset(&rsp, 0, sizeof(rsp));
...@@ -764,7 +791,11 @@ static int lxc_cmd_get_name_callback(int fd, struct lxc_cmd_req *req, ...@@ -764,7 +791,11 @@ static int lxc_cmd_get_name_callback(int fd, struct lxc_cmd_req *req,
rsp.datalen = strlen(handler->name) + 1; rsp.datalen = strlen(handler->name) + 1;
rsp.ret = 0; rsp.ret = 0;
return lxc_cmd_rsp_send(fd, &rsp); ret = lxc_cmd_rsp_send(fd, &rsp);
if (ret < 0)
return LXC_CMD_REAP_CLIENT_FD;
return 0;
} }
/* /*
...@@ -797,13 +828,18 @@ static int lxc_cmd_get_lxcpath_callback(int fd, struct lxc_cmd_req *req, ...@@ -797,13 +828,18 @@ static int lxc_cmd_get_lxcpath_callback(int fd, struct lxc_cmd_req *req,
struct lxc_handler *handler, struct lxc_handler *handler,
struct lxc_epoll_descr *descr) struct lxc_epoll_descr *descr)
{ {
int ret;
struct lxc_cmd_rsp rsp = { struct lxc_cmd_rsp rsp = {
.ret = 0, .ret = 0,
.data = (char *)handler->lxcpath, .data = (char *)handler->lxcpath,
.datalen = strlen(handler->lxcpath) + 1, .datalen = strlen(handler->lxcpath) + 1,
}; };
return lxc_cmd_rsp_send(fd, &rsp); ret = lxc_cmd_rsp_send(fd, &rsp);
if (ret < 0)
return LXC_CMD_REAP_CLIENT_FD;
return 0;
} }
int lxc_cmd_add_state_client(const char *name, const char *lxcpath, int lxc_cmd_add_state_client(const char *name, const char *lxcpath,
...@@ -844,7 +880,8 @@ int lxc_cmd_add_state_client(const char *name, const char *lxcpath, ...@@ -844,7 +880,8 @@ int lxc_cmd_add_state_client(const char *name, const char *lxcpath,
return log_trace(state, "Container is already in requested state %s", lxc_state2str(state)); return log_trace(state, "Container is already in requested state %s", lxc_state2str(state));
*state_client_fd = move_fd(clientfd); *state_client_fd = move_fd(clientfd);
return log_trace(MAX_STATE, "Added state client %d to state client list", *state_client_fd); TRACE("State connection fd %d ready to listen for container state changes", *state_client_fd);
return MAX_STATE;
} }
static int lxc_cmd_add_state_client_callback(__owns int fd, struct lxc_cmd_req *req, static int lxc_cmd_add_state_client_callback(__owns int fd, struct lxc_cmd_req *req,
...@@ -855,31 +892,25 @@ static int lxc_cmd_add_state_client_callback(__owns int fd, struct lxc_cmd_req * ...@@ -855,31 +892,25 @@ static int lxc_cmd_add_state_client_callback(__owns int fd, struct lxc_cmd_req *
struct lxc_cmd_rsp rsp = {0}; struct lxc_cmd_rsp rsp = {0};
if (req->datalen < 0) if (req->datalen < 0)
goto reap_client_fd; return LXC_CMD_REAP_CLIENT_FD;
if (req->datalen != (sizeof(lxc_state_t) * MAX_STATE)) if (req->datalen != (sizeof(lxc_state_t) * MAX_STATE))
goto reap_client_fd; return LXC_CMD_REAP_CLIENT_FD;
if (!req->data) if (!req->data)
goto reap_client_fd; return LXC_CMD_REAP_CLIENT_FD;
rsp.ret = lxc_add_state_client(fd, handler, (lxc_state_t *)req->data); rsp.ret = lxc_add_state_client(fd, handler, (lxc_state_t *)req->data);
if (rsp.ret < 0) if (rsp.ret < 0)
goto reap_client_fd; return LXC_CMD_REAP_CLIENT_FD;
rsp.data = INT_TO_PTR(rsp.ret); rsp.data = INT_TO_PTR(rsp.ret);
ret = lxc_cmd_rsp_send(fd, &rsp); ret = lxc_cmd_rsp_send(fd, &rsp);
if (ret < 0) if (ret < 0)
goto reap_client_fd; return LXC_CMD_REAP_CLIENT_FD;
return 0; return 0;
reap_client_fd:
/* Special indicator to lxc_cmd_handler() to close the fd and do related
* cleanup.
*/
return 1;
} }
int lxc_cmd_add_bpf_device_cgroup(const char *name, const char *lxcpath, int lxc_cmd_add_bpf_device_cgroup(const char *name, const char *lxcpath,
...@@ -925,13 +956,13 @@ static int lxc_cmd_add_bpf_device_cgroup_callback(int fd, struct lxc_cmd_req *re ...@@ -925,13 +956,13 @@ static int lxc_cmd_add_bpf_device_cgroup_callback(int fd, struct lxc_cmd_req *re
struct bpf_program *devices_old; struct bpf_program *devices_old;
if (req->datalen <= 0) if (req->datalen <= 0)
goto reap_client_fd; return LXC_CMD_REAP_CLIENT_FD;
if (req->datalen != sizeof(struct device_item)) if (req->datalen != sizeof(struct device_item))
goto reap_client_fd; return LXC_CMD_REAP_CLIENT_FD;
if (!req->data) if (!req->data)
goto reap_client_fd; return LXC_CMD_REAP_CLIENT_FD;
device = (struct device_item *)req->data; device = (struct device_item *)req->data;
rsp.ret = -1; rsp.ret = -1;
...@@ -978,15 +1009,9 @@ static int lxc_cmd_add_bpf_device_cgroup_callback(int fd, struct lxc_cmd_req *re ...@@ -978,15 +1009,9 @@ static int lxc_cmd_add_bpf_device_cgroup_callback(int fd, struct lxc_cmd_req *re
respond: respond:
ret = lxc_cmd_rsp_send(fd, &rsp); ret = lxc_cmd_rsp_send(fd, &rsp);
if (ret < 0) if (ret < 0)
goto reap_client_fd; return LXC_CMD_REAP_CLIENT_FD;
return 0; return 0;
reap_client_fd:
/* Special indicator to lxc_cmd_handler() to close the fd and do related
* cleanup.
*/
return 1;
#else #else
return ret_set_errno(-1, ENOSYS); return ret_set_errno(-1, ENOSYS);
#endif #endif
...@@ -1097,20 +1122,13 @@ static int lxc_cmd_serve_state_clients_callback(int fd, struct lxc_cmd_req *req, ...@@ -1097,20 +1122,13 @@ static int lxc_cmd_serve_state_clients_callback(int fd, struct lxc_cmd_req *req,
ret = lxc_serve_state_clients(handler->name, handler, state); ret = lxc_serve_state_clients(handler->name, handler, state);
if (ret < 0) if (ret < 0)
goto reap_client_fd; return LXC_CMD_REAP_CLIENT_FD;
ret = lxc_cmd_rsp_send(fd, &rsp); ret = lxc_cmd_rsp_send(fd, &rsp);
if (ret < 0) if (ret < 0)
goto reap_client_fd; return LXC_CMD_REAP_CLIENT_FD;
return 0; return 0;
reap_client_fd:
/*
* Special indicator to lxc_cmd_handler() to close the fd and do
* related cleanup.
*/
return 1;
} }
int lxc_cmd_seccomp_notify_add_listener(const char *name, const char *lxcpath, int lxc_cmd_seccomp_notify_add_listener(const char *name, const char *lxcpath,
...@@ -1347,7 +1365,7 @@ static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler, ...@@ -1347,7 +1365,7 @@ static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler,
* No need to walk the whole list. If we found the state * No need to walk the whole list. If we found the state
* client fd there can't be a second one. * client fd there can't be a second one.
*/ */
TRACE("Found state client fd %d in state client list", fd); TRACE("Found state client fd %d in state client list for command \"%s\"", fd, lxc_cmd_str(cmd));
break; break;
} }
...@@ -1357,9 +1375,10 @@ static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler, ...@@ -1357,9 +1375,10 @@ static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler,
* was already reached by the time we were ready to add it. So * was already reached by the time we were ready to add it. So
* fallthrough and clean it up. * fallthrough and clean it up.
*/ */
TRACE("Closing state client fd %d", fd); TRACE("Closing state client fd %d for command \"%s\"", fd, lxc_cmd_str(cmd));
} }
TRACE("Closing client fd %d for command \"%s\"", fd, lxc_cmd_str(cmd));
close(fd); close(fd);
} }
...@@ -1373,12 +1392,13 @@ static int lxc_cmd_handler(int fd, uint32_t events, void *data, ...@@ -1373,12 +1392,13 @@ static int lxc_cmd_handler(int fd, uint32_t events, void *data,
ret = lxc_abstract_unix_rcv_credential(fd, &req, sizeof(req)); ret = lxc_abstract_unix_rcv_credential(fd, &req, sizeof(req));
if (ret < 0) { if (ret < 0) {
SYSERROR("Failed to receive data on command socket for command " SYSERROR("Failed to receive data on command socket for command \"%s\"", lxc_cmd_str(req.cmd));
"\"%s\"", lxc_cmd_str(req.cmd));
if (errno == EACCES) { if (errno == EACCES) {
/* We don't care for the peer, just send and close. */ /* We don't care for the peer, just send and close. */
struct lxc_cmd_rsp rsp = {.ret = ret}; struct lxc_cmd_rsp rsp = {
.ret = ret,
};
lxc_cmd_rsp_send(fd, &rsp); lxc_cmd_rsp_send(fd, &rsp);
} }
...@@ -1390,16 +1410,13 @@ static int lxc_cmd_handler(int fd, uint32_t events, void *data, ...@@ -1390,16 +1410,13 @@ static int lxc_cmd_handler(int fd, uint32_t events, void *data,
goto out_close; goto out_close;
if (ret != sizeof(req)) { if (ret != sizeof(req)) {
WARN("Failed to receive full command request. Ignoring request " WARN("Failed to receive full command request. Ignoring request for \"%s\"", lxc_cmd_str(req.cmd));
"for \"%s\"", lxc_cmd_str(req.cmd));
ret = -1; ret = -1;
goto out_close; goto out_close;
} }
if ((req.datalen > LXC_CMD_DATA_MAX) && if ((req.datalen > LXC_CMD_DATA_MAX) && (req.cmd != LXC_CMD_CONSOLE_LOG)) {
(req.cmd != LXC_CMD_CONSOLE_LOG)) { ERROR("Received command data length %d is too large for command \"%s\"", req.datalen, lxc_cmd_str(req.cmd));
ERROR("Received command data length %d is too large for "
"command \"%s\"", req.datalen, lxc_cmd_str(req.cmd));
errno = EFBIG; errno = EFBIG;
ret = -EFBIG; ret = -EFBIG;
goto out_close; goto out_close;
...@@ -1409,8 +1426,7 @@ static int lxc_cmd_handler(int fd, uint32_t events, void *data, ...@@ -1409,8 +1426,7 @@ static int lxc_cmd_handler(int fd, uint32_t events, void *data,
reqdata = must_realloc(NULL, req.datalen); reqdata = must_realloc(NULL, req.datalen);
ret = lxc_recv_nointr(fd, reqdata, req.datalen, 0); ret = lxc_recv_nointr(fd, reqdata, req.datalen, 0);
if (ret != req.datalen) { if (ret != req.datalen) {
WARN("Failed to receive full command request. Ignoring " WARN("Failed to receive full command request. Ignoring request for \"%s\"", lxc_cmd_str(req.cmd));
"request for \"%s\"", lxc_cmd_str(req.cmd));
ret = LXC_MAINLOOP_ERROR; ret = LXC_MAINLOOP_ERROR;
goto out_close; goto out_close;
} }
...@@ -1455,6 +1471,7 @@ static int lxc_cmd_accept(int fd, uint32_t events, void *data, ...@@ -1455,6 +1471,7 @@ static int lxc_cmd_accept(int fd, uint32_t events, void *data,
if (ret) if (ret)
return log_error(ret, "Failed to add command handler"); return log_error(ret, "Failed to add command handler");
TRACE("Accepted new client as fd %d on command server fd %d", connection, fd);
move_fd(connection); move_fd(connection);
return ret; return ret;
} }
...@@ -1487,13 +1504,11 @@ int lxc_cmd_init(const char *name, const char *lxcpath, const char *suffix) ...@@ -1487,13 +1504,11 @@ int lxc_cmd_init(const char *name, const char *lxcpath, const char *suffix)
int lxc_cmd_mainloop_add(const char *name, struct lxc_epoll_descr *descr, int lxc_cmd_mainloop_add(const char *name, struct lxc_epoll_descr *descr,
struct lxc_handler *handler) struct lxc_handler *handler)
{ {
__do_close_prot_errno int fd = handler->conf->maincmd_fd;
int ret; int ret;
ret = lxc_mainloop_add_handler(descr, fd, lxc_cmd_accept, handler); ret = lxc_mainloop_add_handler(descr, handler->conf->maincmd_fd, lxc_cmd_accept, handler);
if (ret < 0) if (ret < 0)
return log_error(ret, "Failed to add handler for command socket"); return log_error(ret, "Failed to add handler for command socket fd %d", handler->conf->maincmd_fd);
move_fd(fd);
return ret; return ret;
} }
...@@ -11,6 +11,13 @@ ...@@ -11,6 +11,13 @@
#include "macro.h" #include "macro.h"
#include "state.h" #include "state.h"
/*
* Value command callbacks should return when they want the client fd to be
* cleaned up by the main loop. This is most certainly what you want unless you
* have specific reasons to keep the file descriptor alive.
*/
#define LXC_CMD_REAP_CLIENT_FD 1
typedef enum { typedef enum {
LXC_CMD_CONSOLE, LXC_CMD_CONSOLE,
LXC_CMD_TERMINAL_WINCH, LXC_CMD_TERMINAL_WINCH,
......
...@@ -185,5 +185,6 @@ int lxc_add_state_client(int state_client_fd, struct lxc_handler *handler, ...@@ -185,5 +185,6 @@ int lxc_add_state_client(int state_client_fd, struct lxc_handler *handler,
move_ptr(newclient); move_ptr(newclient);
move_ptr(tmplist); move_ptr(tmplist);
return log_trace(MAX_STATE, "Added state client %d to state client list", state_client_fd); TRACE("Added state client fd %d to state client list", state_client_fd);
return MAX_STATE;
} }
...@@ -2064,10 +2064,11 @@ WRAP_API_1(bool, lxcapi_reboot2, int) ...@@ -2064,10 +2064,11 @@ WRAP_API_1(bool, lxcapi_reboot2, int)
static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout) static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout)
{ {
__do_close_prot_errno int state_client_fd = -EBADF;
int haltsignal = SIGPWR;
lxc_state_t states[MAX_STATE] = {0};
int killret, ret; int killret, ret;
pid_t pid; pid_t pid;
int haltsignal = SIGPWR, state_client_fd = -EBADF;
lxc_state_t states[MAX_STATE] = {0};
if (!c) if (!c)
return false; return false;
...@@ -2107,20 +2108,15 @@ static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout) ...@@ -2107,20 +2108,15 @@ static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout)
/* Send shutdown signal to container. */ /* Send shutdown signal to container. */
killret = kill(pid, haltsignal); killret = kill(pid, haltsignal);
if (killret < 0) { if (killret < 0)
if (state_client_fd >= 0) return log_warn(false, "Failed to send signal %d to pid %d", haltsignal, pid);
close(state_client_fd);
WARN("Failed to send signal %d to pid %d", haltsignal, pid);
return false;
}
TRACE("Sent signal %d to pid %d", haltsignal, pid); TRACE("Sent signal %d to pid %d", haltsignal, pid);
if (timeout == 0) if (timeout == 0)
return true; return true;
ret = lxc_cmd_sock_rcv_state(state_client_fd, timeout); ret = lxc_cmd_sock_rcv_state(state_client_fd, timeout);
close(state_client_fd);
if (ret < 0) if (ret < 0)
return false; return false;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment