commands: introduce LXC_CMD_GET_CGROUP_CTX

parent 89b2bfaa
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <linux/types.h>
#include <sys/types.h> #include <sys/types.h>
#include <linux/magic.h> #include <linux/magic.h>
...@@ -42,6 +43,17 @@ typedef enum { ...@@ -42,6 +43,17 @@ typedef enum {
#define DEVICES_CONTROLLER (1U << 0) #define DEVICES_CONTROLLER (1U << 0)
#define FREEZER_CONTROLLER (1U << 1) #define FREEZER_CONTROLLER (1U << 1)
/* That's plenty of hierarchies. */
#define CGROUP_CTX_MAX_FD 20
// BUILD_BUG_ON(CGROUP_CTX_MAX_FD > KERNEL_SCM_MAX_FD);
struct cgroup_ctx {
__s32 cgroup_layout;
__u32 utilities;
__u32 fd_len;
__s32 fd[CGROUP_CTX_MAX_FD];
} __attribute__((aligned(8)));
/* A descriptor for a mounted hierarchy /* A descriptor for a mounted hierarchy
* *
* @controllers * @controllers
...@@ -258,15 +270,36 @@ static inline int cgroup_unified_fd(const struct cgroup_ops *ops) ...@@ -258,15 +270,36 @@ static inline int cgroup_unified_fd(const struct cgroup_ops *ops)
__first, __VA_ARGS__); \ __first, __VA_ARGS__); \
}) })
static inline ssize_t cgroup_fds(struct cgroup_ops *ops, static void put_cgroup_ctx(struct cgroup_ctx *ctx)
int dfds_con[KERNEL_SCM_MAX_FD]) {
if (!IS_ERR_OR_NULL(ctx)) {
for (__u32 idx = 0; idx < ctx->fd_len; idx++)
close_prot_errno_disarm(ctx->fd[idx]);
}
}
define_cleanup_function(struct cgroup_ctx *, put_cgroup_ctx);
static inline int prepare_cgroup_ctx(struct cgroup_ops *ops,
struct cgroup_ctx *ctx)
{ {
ssize_t num_dfds = 0; __u32 idx;
for (idx = 0; ops->hierarchies[idx]; idx++) {
if (idx >= CGROUP_CTX_MAX_FD)
return ret_errno(E2BIG);
ctx->fd[idx] = ops->hierarchies[idx]->dfd_con;
}
if (idx == 0)
return ret_errno(ENOENT);
for (num_dfds = 0; ops->hierarchies[num_dfds]; num_dfds++) ctx->fd_len = idx;
dfds_con[num_dfds] = ops->hierarchies[num_dfds]->dfd_con; ctx->cgroup_layout = ops->cgroup_layout;
if (ops->unified && ops->unified->dfd_con > 0)
ctx->utilities = ops->unified->utilities;
return num_dfds; return 0;
} }
#endif /* __LXC_CGROUP_H */ #endif /* __LXC_CGROUP_H */
...@@ -88,7 +88,7 @@ static const char *lxc_cmd_str(lxc_cmd_t cmd) ...@@ -88,7 +88,7 @@ static const char *lxc_cmd_str(lxc_cmd_t cmd)
[LXC_CMD_GET_LIMITING_CGROUP2_FD] = "get_limiting_cgroup2_fd", [LXC_CMD_GET_LIMITING_CGROUP2_FD] = "get_limiting_cgroup2_fd",
[LXC_CMD_GET_DEVPTS_FD] = "get_devpts_fd", [LXC_CMD_GET_DEVPTS_FD] = "get_devpts_fd",
[LXC_CMD_GET_SECCOMP_NOTIFY_FD] = "get_seccomp_notify_fd", [LXC_CMD_GET_SECCOMP_NOTIFY_FD] = "get_seccomp_notify_fd",
[LXC_CMD_GET_CGROUP_FD] = "get_cgroup_fd", [LXC_CMD_GET_CGROUP_CTX] = "get_cgroup_ctx",
}; };
if (cmd >= LXC_CMD_MAX) if (cmd >= LXC_CMD_MAX)
...@@ -97,6 +97,19 @@ static const char *lxc_cmd_str(lxc_cmd_t cmd) ...@@ -97,6 +97,19 @@ static const char *lxc_cmd_str(lxc_cmd_t cmd)
return cmdname[cmd]; return cmdname[cmd];
} }
static int __transfer_cgroup_ctx_fds(struct unix_fds *fds, struct cgroup_ctx *ctx)
{
/* This shouldn't be able to happen but better safe than sorry. */
if (ctx->fd_len != fds->fd_count_ret ||
fds->fd_count_ret > CGROUP_CTX_MAX_FD)
return syswarn_set(-EINVAL, "Unexpected number of file descriptors received %u != %u",
ctx->fd_len, fds->fd_count_ret);
memcpy(ctx->fd, fds->fd, ctx->fd_len * sizeof(__s32));
fds->fd_count_ret = 0;
return 0;
}
/* /*
* lxc_cmd_rsp_recv: Receive a response to a command * lxc_cmd_rsp_recv: Receive a response to a command
* *
...@@ -118,6 +131,7 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd) ...@@ -118,6 +131,7 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
{ {
call_cleaner(put_unix_fds) struct unix_fds *fds = &(struct unix_fds){}; call_cleaner(put_unix_fds) struct unix_fds *fds = &(struct unix_fds){};
struct lxc_cmd_rsp *rsp = &cmd->rsp; struct lxc_cmd_rsp *rsp = &cmd->rsp;
const char *reqstr = lxc_cmd_str(cmd->req.cmd);
int ret; int ret;
switch (cmd->req.cmd) { switch (cmd->req.cmd) {
...@@ -134,17 +148,17 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd) ...@@ -134,17 +148,17 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
case LXC_CMD_CONSOLE: case LXC_CMD_CONSOLE:
fds->fd_count_max = 1; fds->fd_count_max = 1;
break; break;
case LXC_CMD_GET_CGROUP_FD: case LXC_CMD_GET_CGROUP_CTX:
fds->fd_count_max = KERNEL_SCM_MAX_FD; fds->fd_count_max = CGROUP_CTX_MAX_FD;
break; break;
default: default:
fds->fd_count_max = 0; fds->fd_count_max = 0;
break;
} }
ret = lxc_abstract_unix_recv_fds(sock, fds, rsp, sizeof(*rsp)); ret = lxc_abstract_unix_recv_fds(sock, fds, rsp, sizeof(*rsp));
if (ret < 0) if (ret < 0)
return syserrno(ret, "Failed to receive response for command \"%s\"", return syserrno(ret, "Failed to receive response for command \"%s\"", reqstr);
lxc_cmd_str(cmd->req.cmd)); TRACE("Command \"%s\" received response with %u file descriptors", reqstr, fds->fd_count_ret);
TRACE("Command \"%s\" received response", lxc_cmd_str(cmd->req.cmd));
if (cmd->req.cmd == LXC_CMD_CONSOLE) { if (cmd->req.cmd == LXC_CMD_CONSOLE) {
struct lxc_cmd_console_rsp_data *rspdata; struct lxc_cmd_console_rsp_data *rspdata;
...@@ -157,9 +171,7 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd) ...@@ -157,9 +171,7 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
rspdata = malloc(sizeof(*rspdata)); rspdata = malloc(sizeof(*rspdata));
if (!rspdata) if (!rspdata)
return log_warn_errno(-1, return syserrno_set(-ENOMEM, "Failed to receive response for command \"%s\"", reqstr);
ENOMEM, "Failed to receive response for command \"%s\"",
lxc_cmd_str(cmd->req.cmd));
rspdata->ptxfd = move_fd(fds->fd[0]); rspdata->ptxfd = move_fd(fds->fd[0]);
rspdata->ttynum = PTR_TO_INT(rsp->data); rspdata->ttynum = PTR_TO_INT(rsp->data);
...@@ -177,43 +189,42 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd) ...@@ -177,43 +189,42 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
__fallthrough; __fallthrough;
case LXC_CMD_GET_SECCOMP_NOTIFY_FD: case LXC_CMD_GET_SECCOMP_NOTIFY_FD:
rsp->data = INT_TO_PTR(move_fd(fds->fd[0])); rsp->data = INT_TO_PTR(move_fd(fds->fd[0]));
return log_debug(ret, "Finished processing \"%s\"", lxc_cmd_str(cmd->req.cmd)); return log_debug(ret, "Finished processing \"%s\"", reqstr);
case LXC_CMD_GET_CGROUP_FD: case LXC_CMD_GET_CGROUP_CTX:
rsp->data = move_ptr(fds); if (rsp->datalen > sizeof(struct cgroup_ctx))
rsp->datalen = sizeof(struct unix_fds); return syserrno_set(-EINVAL, "Invalid response size from server for \"%s\"", reqstr);
return log_debug(ret, "Finished processing \"%s\"", lxc_cmd_str(cmd->req.cmd));
/* Don't pointlessly allocate. */
rsp->data = (void *)cmd->req.data;
break; break;
default: default:
break; break;
} }
if (rsp->datalen == 0) if (rsp->datalen == 0)
return log_debug(ret, return log_debug(ret, "Response data length for command \"%s\" is 0", reqstr);
"Response data length for command \"%s\" is 0",
lxc_cmd_str(cmd->req.cmd));
if ((rsp->datalen > LXC_CMD_DATA_MAX) && if ((rsp->datalen > LXC_CMD_DATA_MAX) &&
(cmd->req.cmd != LXC_CMD_CONSOLE_LOG)) (cmd->req.cmd != LXC_CMD_CONSOLE_LOG))
return log_error(-1, "Response data for command \"%s\" is too long: %d bytes > %d", return syserrno_set(-E2BIG, "Response data for command \"%s\" is too long: %d bytes > %d",
lxc_cmd_str(cmd->req.cmd), rsp->datalen, reqstr, rsp->datalen, LXC_CMD_DATA_MAX);
LXC_CMD_DATA_MAX);
if (cmd->req.cmd == LXC_CMD_CONSOLE_LOG) { if (cmd->req.cmd == LXC_CMD_CONSOLE_LOG)
rsp->data = malloc(rsp->datalen + 1); rsp->data = zalloc(rsp->datalen + 1);
((char *)rsp->data)[rsp->datalen] = '\0'; else if (cmd->req.cmd != LXC_CMD_GET_CGROUP_CTX)
} else {
rsp->data = malloc(rsp->datalen); rsp->data = malloc(rsp->datalen);
}
if (!rsp->data) if (!rsp->data)
return log_error_errno(-1, return syserrno_set(-ENOMEM, "Failed to allocate response buffer for command \"%s\"", reqstr);
ENOMEM, "Failed to allocate response buffer for command \"%s\"",
lxc_cmd_str(cmd->req.cmd));
ret = lxc_recv_nointr(sock, rsp->data, rsp->datalen, 0); ret = lxc_recv_nointr(sock, rsp->data, rsp->datalen, 0);
if (ret != rsp->datalen) if (ret != rsp->datalen)
return log_error_errno(-1, return syserrno(-errno, "Failed to receive response data for command \"%s\"", reqstr);
errno, "Failed to receive response data for command \"%s\"",
lxc_cmd_str(cmd->req.cmd)); if (cmd->req.cmd == LXC_CMD_GET_CGROUP_CTX) {
ret = __transfer_cgroup_ctx_fds(fds, rsp->data);
if (ret < 0)
return syserrno(ret, "Failed to transfer file descriptors for \"%s\"", reqstr);
}
return ret; return ret;
} }
...@@ -266,15 +277,30 @@ static inline int rsp_one_fd(int fd, int fd_send, struct lxc_cmd_rsp *rsp) ...@@ -266,15 +277,30 @@ static inline int rsp_one_fd(int fd, int fd_send, struct lxc_cmd_rsp *rsp)
return LXC_CMD_REAP_CLIENT_FD; return LXC_CMD_REAP_CLIENT_FD;
} }
static inline int rsp_many_fds(int fd, struct unix_fds *fds, struct lxc_cmd_rsp *rsp) static inline int rsp_many_fds(int fd, __u32 fds_len,
const __s32 fds[KERNEL_SCM_MAX_FD],
struct lxc_cmd_rsp *rsp)
{ {
int ret; ssize_t ret;
ret = lxc_abstract_unix_send_fds(fd, fds->fd, fds->fd_count_max, if (fds_len > KERNEL_SCM_MAX_FD) {
rsp, sizeof(*rsp)); rsp->ret = -E2BIG;
return lxc_cmd_rsp_send_reap(fd, rsp);
} else if (fds_len == 0) {
rsp->ret = -ENOENT;
return lxc_cmd_rsp_send_reap(fd, rsp);
}
ret = lxc_abstract_unix_send_fds(fd, fds, fds_len, rsp, sizeof(*rsp));
if (ret < 0) if (ret < 0)
return ret; return ret;
if (rsp->data && rsp->datalen > 0) {
ret = lxc_send_nointr(fd, rsp->data, rsp->datalen, MSG_NOSIGNAL);
if (ret < 0 || ret != (ssize_t)rsp->datalen)
return syswarn(-errno, "Failed to send command response %zd", ret);
}
return LXC_CMD_REAP_CLIENT_FD; return LXC_CMD_REAP_CLIENT_FD;
} }
...@@ -573,16 +599,18 @@ static int lxc_cmd_get_seccomp_notify_fd_callback(int fd, struct lxc_cmd_req *re ...@@ -573,16 +599,18 @@ static int lxc_cmd_get_seccomp_notify_fd_callback(int fd, struct lxc_cmd_req *re
#endif #endif
} }
int lxc_cmd_get_cgroup_fd(const char *name, const char *lxcpath, int lxc_cmd_get_cgroup_ctx(const char *name, const char *lxcpath,
const char *controller, bool batch, const char *controller, bool batch,
struct unix_fds *ret_fds) size_t size_ret_ctx, struct cgroup_ctx *ret_ctx)
{ {
int ret, stopped;
struct lxc_cmd_rr cmd = { struct lxc_cmd_rr cmd = {
.req = { .req = {
.cmd = LXC_CMD_GET_CGROUP_FD, .cmd = LXC_CMD_GET_CGROUP_CTX,
.datalen = size_ret_ctx,
.data = ret_ctx,
}, },
}; };
int ret, stopped;
if (batch && !is_empty_string(controller)) if (batch && !is_empty_string(controller))
return ret_errno(EINVAL); return ret_errno(EINVAL);
...@@ -594,29 +622,34 @@ int lxc_cmd_get_cgroup_fd(const char *name, const char *lxcpath, ...@@ -594,29 +622,34 @@ int lxc_cmd_get_cgroup_fd(const char *name, const char *lxcpath,
if (cmd.rsp.ret < 0) if (cmd.rsp.ret < 0)
return log_debug_errno(-EBADF, errno, "Failed to receive cgroup fds"); return log_debug_errno(-EBADF, errno, "Failed to receive cgroup fds");
*ret_fds = *(struct unix_fds *)cmd.rsp.data;
return 0; return 0;
} }
static int lxc_cmd_get_cgroup_fd_callback(int fd, struct lxc_cmd_req *req, static int lxc_cmd_get_cgroup_ctx_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)
{ {
struct lxc_cmd_rsp rsp = { struct lxc_cmd_rsp rsp = {
.ret = 0, .ret = EINVAL,
}; };
struct cgroup_ops *cgroup_ops = handler->cgroup_ops; struct cgroup_ops *cgroup_ops = handler->cgroup_ops;
struct unix_fds *fds = {}; struct cgroup_ctx ctx_server = {};
int ret; int ret;
fds->fd_count_max = cgroup_fds(cgroup_ops, fds->fd); ret = copy_struct_from_client(sizeof(struct cgroup_ctx), &ctx_server,
ret = lxc_abstract_unix_send_fds(fd, fds->fd, fds->fd_count_max, req->datalen, req->data);
&rsp, sizeof(rsp));
if (ret < 0) if (ret < 0)
return log_error(ret, "Failed to send cgroup fds"); return lxc_cmd_rsp_send_reap(fd, &rsp);
ret = prepare_cgroup_ctx(cgroup_ops, &ctx_server);
if (ret < 0) {
rsp.ret = ret;
return lxc_cmd_rsp_send_reap(fd, &rsp);
}
return log_trace(LXC_CMD_REAP_CLIENT_FD, "Sent cgroup fds"); rsp.data = &ctx_server;
rsp.datalen = min(sizeof(struct cgroup_ctx), (size_t)req->datalen);
return rsp_many_fds(fd, ctx_server.fd_len, ctx_server.fd, &rsp);
} }
/* /*
...@@ -1613,7 +1646,7 @@ static int lxc_cmd_process(int fd, struct lxc_cmd_req *req, ...@@ -1613,7 +1646,7 @@ static int lxc_cmd_process(int fd, struct lxc_cmd_req *req,
[LXC_CMD_GET_LIMITING_CGROUP2_FD] = lxc_cmd_get_limiting_cgroup2_fd_callback, [LXC_CMD_GET_LIMITING_CGROUP2_FD] = lxc_cmd_get_limiting_cgroup2_fd_callback,
[LXC_CMD_GET_DEVPTS_FD] = lxc_cmd_get_devpts_fd_callback, [LXC_CMD_GET_DEVPTS_FD] = lxc_cmd_get_devpts_fd_callback,
[LXC_CMD_GET_SECCOMP_NOTIFY_FD] = lxc_cmd_get_seccomp_notify_fd_callback, [LXC_CMD_GET_SECCOMP_NOTIFY_FD] = lxc_cmd_get_seccomp_notify_fd_callback,
[LXC_CMD_GET_CGROUP_FD] = lxc_cmd_get_cgroup_fd_callback, [LXC_CMD_GET_CGROUP_CTX] = lxc_cmd_get_cgroup_ctx_callback,
}; };
if (req->cmd >= LXC_CMD_MAX) if (req->cmd >= LXC_CMD_MAX)
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <unistd.h> #include <unistd.h>
#include "compiler.h" #include "compiler.h"
#include "cgroups/cgroup.h"
#include "lxccontainer.h" #include "lxccontainer.h"
#include "macro.h" #include "macro.h"
#include "state.h" #include "state.h"
...@@ -43,7 +44,7 @@ typedef enum { ...@@ -43,7 +44,7 @@ typedef enum {
LXC_CMD_GET_LIMITING_CGROUP2_FD = 20, LXC_CMD_GET_LIMITING_CGROUP2_FD = 20,
LXC_CMD_GET_DEVPTS_FD = 21, LXC_CMD_GET_DEVPTS_FD = 21,
LXC_CMD_GET_SECCOMP_NOTIFY_FD = 22, LXC_CMD_GET_SECCOMP_NOTIFY_FD = 22,
LXC_CMD_GET_CGROUP_FD = 23, LXC_CMD_GET_CGROUP_CTX = 23,
LXC_CMD_MAX, LXC_CMD_MAX,
} lxc_cmd_t; } lxc_cmd_t;
...@@ -123,9 +124,11 @@ __hidden extern int lxc_try_cmd(const char *name, const char *lxcpath); ...@@ -123,9 +124,11 @@ __hidden extern int lxc_try_cmd(const char *name, const char *lxcpath);
__hidden extern int lxc_cmd_console_log(const char *name, const char *lxcpath, __hidden extern int lxc_cmd_console_log(const char *name, const char *lxcpath,
struct lxc_console_log *log); struct lxc_console_log *log);
__hidden extern int lxc_cmd_get_seccomp_notify_fd(const char *name, const char *lxcpath); __hidden extern int lxc_cmd_get_seccomp_notify_fd(const char *name, const char *lxcpath);
__hidden extern int lxc_cmd_get_cgroup_fd(const char *name, const char *lxcpath, __hidden extern int lxc_cmd_get_cgroup_ctx(const char *name, const char *lxcpath,
const char *controller, bool batch, const char *controller, bool batch,
struct unix_fds *ret_fds); size_t size_ret_ctx,
struct cgroup_ctx *ret_ctx)
__access_r(6, 5);
__hidden extern int lxc_cmd_seccomp_notify_add_listener(const char *name, const char *lxcpath, int fd, __hidden extern int lxc_cmd_seccomp_notify_add_listener(const char *name, const char *lxcpath, int fd,
/* unused */ unsigned int command, /* unused */ unsigned int command,
/* unused */ unsigned int flags); /* unused */ unsigned int flags);
......
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