From 44dd71a4d76eb464b81890ea3cfa2ac9c6c3d990 Mon Sep 17 00:00:00 2001 From: haozi007 Date: Tue, 19 Jul 2022 14:40:59 +0800 Subject: [PATCH] refactor patch code of isulad for selinux/attach Signed-off-by: haozi007 --- src/lxc/exec_commands.c | 471 +++++++++++++++++++++++++++++++++++++ src/lxc/lsm/selinux.c | 258 ++++++++++++++++++++ src/lxc/tools/lxc_attach.c | 413 +++++++++++++++++++++++++++++++- 3 files changed, 1141 insertions(+), 1 deletion(-) create mode 100644 src/lxc/exec_commands.c diff --git a/src/lxc/exec_commands.c b/src/lxc/exec_commands.c new file mode 100644 index 0000000..50246fa --- /dev/null +++ b/src/lxc/exec_commands.c @@ -0,0 +1,471 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * Author: lifeng + * Create: 2019-12-08 + * Description: provide container definition + * lxc: linux Container library + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + ******************************************************************************/ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "af_unix.h" +#include "cgroup.h" +#include "exec_commands.h" +#include "commands_utils.h" +#include "conf.h" +#include "config.h" +#include "confile.h" +#include "log.h" +#include "lxc.h" +#include "lxclock.h" +#include "mainloop.h" +#include "monitor.h" +#include "terminal.h" +#include "utils.h" + +lxc_log_define(commands_exec, lxc); + +static const char *lxc_exec_cmd_str(lxc_exec_cmd_t cmd) +{ + static const char *const cmdname[LXC_EXEC_CMD_MAX] = { + [LXC_EXEC_CMD_SET_TERMINAL_WINCH] = "set_exec_terminal_winch", + }; + + if (cmd >= LXC_EXEC_CMD_MAX) + return "Invalid request"; + + return cmdname[cmd]; +} + +static int lxc_exec_cmd_rsp_recv(int sock, struct lxc_exec_cmd_rr *cmd) +{ + int ret, rspfd; + struct lxc_exec_cmd_rsp *rsp = &cmd->rsp; + + ret = lxc_abstract_unix_recv_fds_timeout(sock, &rspfd, 1, rsp, sizeof(*rsp), 1000 * 1000); + if (ret < 0) { + SYSERROR("Failed to receive response for command \"%s\"", + lxc_exec_cmd_str(cmd->req.cmd)); + + if (errno == ECONNRESET || errno == EAGAIN || errno == EWOULDBLOCK) { + errno = ECONNRESET; /*isulad set errno ECONNRESET when timeout */ + return -1; + } + + return -1; + } + TRACE("Command \"%s\" received response", lxc_exec_cmd_str(cmd->req.cmd)); + + if (rsp->datalen == 0) { + DEBUG("Response data length for command \"%s\" is 0", + lxc_exec_cmd_str(cmd->req.cmd)); + return ret; + } + + if (rsp->datalen > LXC_CMD_DATA_MAX) { + ERROR("Response data for command \"%s\" is too long: %d bytes > %d", + lxc_exec_cmd_str(cmd->req.cmd), rsp->datalen, LXC_CMD_DATA_MAX); + return -1; + } + + rsp->data = malloc(rsp->datalen); + if (!rsp->data) { + errno = ENOMEM; + ERROR("Failed to allocate response buffer for command \"%s\"", + lxc_exec_cmd_str(cmd->req.cmd)); + return -1; + } + + ret = lxc_recv_nointr(sock, rsp->data, rsp->datalen, 0); + if (ret != rsp->datalen) { + SYSERROR("Failed to receive response data for command \"%s\"", + lxc_exec_cmd_str(cmd->req.cmd)); + return -1; + } + + return ret; +} + +static int lxc_exec_cmd_rsp_send(int fd, struct lxc_exec_cmd_rsp *rsp) +{ + ssize_t ret; + + errno = EMSGSIZE; + ret = lxc_send_nointr(fd, rsp, sizeof(*rsp), MSG_NOSIGNAL); + if (ret < 0 || (size_t)ret != sizeof(*rsp)) { + SYSERROR("Failed to send command response %zd", ret); + return -1; + } + + if (!rsp->data || rsp->datalen <= 0) + return 0; + + errno = EMSGSIZE; + ret = lxc_send_nointr(fd, rsp->data, rsp->datalen, MSG_NOSIGNAL); + if (ret < 0 || ret != (ssize_t)rsp->datalen) { + SYSWARN("Failed to send command response data %zd", ret); + return -1; + } + + return 0; +} + +static int lxc_exec_cmd_send(const char *name, struct lxc_exec_cmd_rr *cmd, + const char *lxcpath, const char *hashed_sock_name, const char *suffix) +{ + int client_fd, saved_errno; + ssize_t ret = -1; + + client_fd = lxc_cmd_connect(name, lxcpath, hashed_sock_name, suffix); + if (client_fd < 0) + return -1; + + ret = lxc_abstract_unix_send_credential(client_fd, &cmd->req, + sizeof(cmd->req)); + if (ret < 0 || (size_t)ret != sizeof(cmd->req)) + goto on_error; + + if (cmd->req.datalen <= 0) + return client_fd; + + errno = EMSGSIZE; + ret = lxc_send_nointr(client_fd, (void *)cmd->req.data, + cmd->req.datalen, MSG_NOSIGNAL); + if (ret < 0 || ret != (ssize_t)cmd->req.datalen) + goto on_error; + + return client_fd; + +on_error: + saved_errno = errno; + close(client_fd); + errno = saved_errno; + + return -1; +} + +static int lxc_exec_cmd(const char *name, struct lxc_exec_cmd_rr *cmd, const char *lxcpath, const char *hashed_sock_name, const char *suffix) +{ + int client_fd = -1; + int saved_errno; + int ret = -1; + + client_fd = lxc_exec_cmd_send(name, cmd, lxcpath, hashed_sock_name, suffix); + if (client_fd < 0) { + SYSTRACE("Command \"%s\" failed to connect command socket", + lxc_exec_cmd_str(cmd->req.cmd)); + return -1; + } + + ret = lxc_exec_cmd_rsp_recv(client_fd, cmd); + + saved_errno = errno; + close(client_fd); + errno = saved_errno; + return ret; +} + +int lxc_exec_cmd_set_terminal_winch(const char *name, const char *lxcpath, const char *suffix, unsigned int height, unsigned int width) +{ + int ret = 0; + struct lxc_exec_cmd_set_terminal_winch_request data = { 0 }; + + data.height = height; + data.width = width; + + struct lxc_exec_cmd_rr cmd = { + .req = { + .cmd = LXC_EXEC_CMD_SET_TERMINAL_WINCH, + .datalen = sizeof(struct lxc_exec_cmd_set_terminal_winch_request), + .data = &data, + }, + }; + + ret = lxc_exec_cmd(name, &cmd, lxcpath, NULL, suffix); + if (ret < 0) { + ERROR("Failed to send command to container"); + return -1; + } + + if (cmd.rsp.ret != 0) { + ERROR("Command response error:%d", cmd.rsp.ret); + return -1; + } + return 0; +} + +static int lxc_exec_cmd_set_terminal_winch_callback(int fd, struct lxc_exec_cmd_req *req, + struct lxc_exec_command_handler *handler) +{ + struct lxc_exec_cmd_rsp rsp; + struct lxc_exec_cmd_set_terminal_winch_request *data = (struct lxc_exec_cmd_set_terminal_winch_request *)(req->data); + memset(&rsp, 0, sizeof(rsp)); + + rsp.ret = lxc_set_terminal_winsz(handler->terminal, data->height, data->width);; + + return lxc_exec_cmd_rsp_send(fd, &rsp); + +} + +static int lxc_exec_cmd_process(int fd, struct lxc_exec_cmd_req *req, + struct lxc_exec_command_handler *handler) +{ + typedef int (*callback)(int, struct lxc_exec_cmd_req *, struct lxc_exec_command_handler *); + + callback cb[LXC_EXEC_CMD_MAX] = { + [LXC_EXEC_CMD_SET_TERMINAL_WINCH] = lxc_exec_cmd_set_terminal_winch_callback, + }; + + if (req->cmd >= LXC_EXEC_CMD_MAX) { + ERROR("Undefined command id %d", req->cmd); + return -1; + } + return cb[req->cmd](fd, req, handler); +} + +static void lxc_exec_cmd_fd_cleanup(int fd, struct lxc_epoll_descr *descr) +{ + lxc_mainloop_del_handler(descr, fd); + close(fd); + return; +} + +static int lxc_exec_cmd_handler(int fd, uint32_t events, void *data, + struct lxc_epoll_descr *descr) +{ + int ret; + struct lxc_exec_cmd_req req; + void *reqdata = NULL; + struct lxc_exec_command_handler *handler = data; + + ret = lxc_abstract_unix_rcv_credential(fd, &req, sizeof(req)); + if (ret < 0) { + SYSERROR("Failed to receive data on command socket for command " + "\"%s\"", lxc_exec_cmd_str(req.cmd)); + + if (errno == EACCES) { + /* We don't care for the peer, just send and close. */ + struct lxc_exec_cmd_rsp rsp = {.ret = ret}; + + lxc_exec_cmd_rsp_send(fd, &rsp); + } + + goto out_close; + } + + if (ret == 0) + goto out_close; + + if (ret != sizeof(req)) { + WARN("Failed to receive full command request. Ignoring request " + "for \"%s\"", lxc_exec_cmd_str(req.cmd)); + ret = -1; + goto out_close; + } + + if (req.datalen > LXC_CMD_DATA_MAX) { + ERROR("Received command data length %d is too large for " + "command \"%s\"", req.datalen, lxc_exec_cmd_str(req.cmd)); + errno = EFBIG; + ret = -EFBIG; + goto out_close; + } + + if (req.datalen > 0) { + reqdata = alloca(req.datalen); + if (!reqdata) { + ERROR("Failed to allocate memory for \"%s\" command", + lxc_exec_cmd_str(req.cmd)); + errno = ENOMEM; + ret = -ENOMEM; + goto out_close; + } + + ret = lxc_recv_nointr(fd, reqdata, req.datalen, 0); + if (ret != req.datalen) { + WARN("Failed to receive full command request. Ignoring " + "request for \"%s\"", lxc_exec_cmd_str(req.cmd)); + ret = LXC_MAINLOOP_ERROR; + goto out_close; + } + + req.data = reqdata; + } + + ret = lxc_exec_cmd_process(fd, &req, handler); + if (ret) { + /* This is not an error, but only a request to close fd. */ + ret = LXC_MAINLOOP_CONTINUE; + goto out_close; + } + +out: + return ret; + +out_close: + lxc_exec_cmd_fd_cleanup(fd, descr); + goto out; +} + +static int lxc_exec_cmd_accept(int fd, uint32_t events, void *data, + struct lxc_epoll_descr *descr) +{ + int connection = -1; + int opt = 1, ret = -1; + + connection = accept(fd, NULL, 0); + if (connection < 0) { + SYSERROR("Failed to accept connection to run command"); + return LXC_MAINLOOP_ERROR; + } + + ret = fcntl(connection, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + SYSERROR("Failed to set close-on-exec on incoming command connection"); + goto out_close; + } + + ret = setsockopt(connection, SOL_SOCKET, SO_PASSCRED, &opt, sizeof(opt)); + if (ret < 0) { + SYSERROR("Failed to enable necessary credentials on command socket"); + goto out_close; + } + + ret = lxc_mainloop_add_handler(descr, connection, lxc_exec_cmd_handler, data); + if (ret) { + ERROR("Failed to add command handler"); + goto out_close; + } + +out: + return ret; + +out_close: + close(connection); + goto out; +} +#ifdef HAVE_ISULAD +int lxc_exec_unix_sock_delete(const char *name, const char *suffix) +{ + char path[LXC_AUDS_ADDR_LEN] = {0}; + + if (name == NULL || suffix == NULL) + return -1; + + if (generate_named_unix_sock_path(name, suffix, path, sizeof(path)) != 0) + return -1; + + (void)unlink(path); + + return 0; +} + +int lxc_exec_cmd_init(const char *name, const char *lxcpath, const char *suffix) +{ + __do_close int fd = -EBADF; + int ret; + char path[LXC_AUDS_ADDR_LEN] = {0}; + __do_free char *exec_sock_dir = NULL; + + exec_sock_dir = generate_named_unix_sock_dir(name); + if (exec_sock_dir == NULL) + return -1; + + if (mkdir_p(exec_sock_dir, 0600) < 0) + return log_error_errno(-1, errno, "Failed to create exec sock directory %s", path); + + if (generate_named_unix_sock_path(name, suffix, path, sizeof(path)) != 0) + return -1; + + TRACE("Creating unix socket \"%s\"", path); + + fd = lxc_named_unix_open(path, SOCK_STREAM, 0); + if (fd < 0) { + if (errno == EADDRINUSE) { + WARN("Container \"%s\" exec unix sock is occupied", name); + (void)unlink(path); + fd = lxc_named_unix_open(path, SOCK_STREAM, 0); + if (fd < 0) + return log_error_errno(-1, errno, "Failed to create command socket %s", path); + } else { + return log_error_errno(-1, errno, "Failed to create command socket %s", path); + } + } + + ret = fcntl(fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to set FD_CLOEXEC on command socket file descriptor"); + + return log_trace(move_fd(fd), "Created unix socket \"%s\"", path); +} +#else +int lxc_exec_cmd_init(const char *name, const char *lxcpath, const char *suffix) +{ + int fd, ret; + char path[LXC_AUDS_ADDR_LEN] = {0}; + + ret = lxc_make_abstract_socket_name(path, sizeof(path), name, lxcpath, NULL, suffix); + if (ret < 0) + return -1; + TRACE("Creating abstract unix socket \"%s\"", &path[1]); + + fd = lxc_abstract_unix_open(path, SOCK_STREAM, 0); + if (fd < 0) { + SYSERROR("Failed to create command socket %s", &path[1]); + if (errno == EADDRINUSE) + ERROR("Container \"%s\" appears to be already running", name); + + return -1; + } + + ret = fcntl(fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + SYSERROR("Failed to set FD_CLOEXEC on command socket file descriptor"); + close(fd); + return -1; + } + + return fd; +} +#endif + +int lxc_exec_cmd_mainloop_add(struct lxc_epoll_descr *descr, struct lxc_exec_command_handler *handler) +{ + int ret; + int fd = handler->maincmd_fd; + + ret = lxc_mainloop_add_handler(descr, fd, lxc_exec_cmd_accept, handler); + if (ret < 0) { + ERROR("Failed to add handler for command socket"); + close(fd); + } + + return ret; +} diff --git a/src/lxc/lsm/selinux.c b/src/lxc/lsm/selinux.c index dba0ab5..bd4f449 100644 --- a/src/lxc/lsm/selinux.c +++ b/src/lxc/lsm/selinux.c @@ -16,6 +16,10 @@ #include "log.h" #include "lsm.h" +#ifdef HAVE_ISULAD +#include +#endif + #define DEFAULT_LABEL "unconfined_t" lxc_log_define(selinux, lsm); @@ -85,6 +89,256 @@ static int selinux_process_label_set(const char *inlabel, struct lxc_conf *conf, return 0; } +#ifdef HAVE_ISULAD +/* + * selinux_file_label_set: Set SELinux context of a file + * + * @path : a file + * @label : label string + * + * Returns 0 on success, < 0 on failure + */ +static int selinux_file_label_set(const char *path, const char *label) +{ + if (path == NULL || label == NULL || strcmp(label, "unconfined_t") == 0) { + return 0; + } + + if (!is_selinux_enabled()) { + return 0; + } + + if (lsetfilecon(path, label) != 0) { + SYSERROR("Failed to setSELinux context to \"%s\": %s", label, path); + return -1; + } + + INFO("Changed SELinux context to \"%s\": %s", label, path); + return 0; +} + +/* + * is_exclude_relabel_path: Determine whether it is a excluded path to label + * + * @path : a file or directory + * + * Returns 0 on success, < 0 on failure + */ +static bool is_exclude_relabel_path(const char *path) +{ + const char *exclude_path[] = { "/", "/usr", "/etc", "/tmp", "/home", "/run", "/var", "/root" }; + size_t i; + + for (i = 0; i < sizeof(exclude_path) / sizeof(char *); i++) { + if (strcmp(path, exclude_path[i]) == 0) { + return true; + } + } + + return false; +} + +/* + * bad_prefix: Prevent users from relabing system files + * + * @path : a file or directory + * + * Returns 0 on success, < 0 on failure + */ +static int bad_prefix(const char *fpath) +{ + const char *bad_prefixes = "/usr"; + + if (fpath == NULL) { + ERROR("Empty file path"); + return -1; + } + + if (strncmp(fpath, bad_prefixes, strlen(bad_prefixes)) == 0) { + ERROR("relabeling content in %s is not allowed", bad_prefixes); + return -1; + } + + return 0; +} + +/* + * recurse_set_file_label: Recursively label files or folders + * + * @path : a file or directory + * @label : label string + * + * Returns 0 on success, < 0 on failure + */ +static int recurse_set_file_label(const char *basePath, const char *label) +{ + int ret = 0; + __do_closedir DIR *dir = NULL; + struct dirent *ptr = NULL; + char base[PATH_MAX] = { 0 }; + + if ((dir = opendir(basePath)) == NULL) { + ERROR("Failed to Open dir: %s", basePath); + return -1; + } + + ret = lsetfilecon(basePath, label); + if (ret != 0) { + ERROR("Failed to set file label"); + return ret; + } + + while ((ptr = readdir(dir)) != NULL) { + if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) { + continue; + } else { + int nret = snprintf(base, sizeof(base), "%s/%s", basePath, ptr->d_name); + if (nret < 0 || nret >= sizeof(base)) { + ERROR("Failed to get path"); + return -1; + } + if (ptr->d_type == DT_DIR) { + ret = recurse_set_file_label(base, label); + if (ret != 0) { + ERROR("Failed to set dir label"); + return ret; + } + } else { + ret = lsetfilecon(base, label); + if (ret != 0) { + ERROR("Failed to set file label"); + return ret; + } + } + } + } + + return 0; +} + +/* + * selinux_chcon: Chcon changes the `fpath` file object to the SELinux label `label`. + * If `fpath` is a directory and `recurse`` is true, Chcon will walk the + * directory tree setting the label. + * + * @fpath : a file or directory + * @label : label string + * @recurse : whether to recurse + * + * Returns 0 on success, < 0 on failure + */ +static int selinux_chcon(const char *fpath, const char *label, bool recurse) +{ + struct stat s_buf; + + if (fpath == NULL || label == NULL) { + ERROR("Invalid parameters!"); + return -1; + } + + if (bad_prefix(fpath) != 0) { + return -1; + } + if (stat(fpath, &s_buf) != 0) { + return -1; + } + if (recurse && S_ISDIR(s_buf.st_mode)) { + return recurse_set_file_label(fpath, label); + } + + if (lsetfilecon(fpath, label) != 0) { + ERROR("Failed to set file label"); + return -1; + } + + return 0; +} + +/* + * convert_context_to_share_mode: set sensitivity to s0 and remove categories + * user:role:type:sensitivity[:categories] => user:role:type:s0 + * + * @label : label string + * + * Returns label with share mode on success, NULL on failure + */ +static char *convert_context_to_share_mode(const char *label) { + __do_free char *converted_label = strdup(label); + char *s = converted_label; + const char *shared_level = "s0"; + int cnt = 0; + + // selinux label format: user:role:type:sensitivity[:categories] + // locates the ":" position in front of the sensitivity + while (cnt++ < 3 && (s = strchr(s, ':')) != NULL) { + s++; + } + + // make sure sensitivity can set s0 value + if (s == NULL || strlen(s) < strlen(shared_level)) { + ERROR("Invalid selinux file context: %s", label); + return NULL; + } + + if (strcmp(s, shared_level) == 0) { + return move_ptr(converted_label); + } + + *s = '\0'; + strcat(converted_label, shared_level); + + return move_ptr(converted_label); +} + +/* + * selinux_relabel: Relabel changes the label of path to the filelabel string. + * It changes the MCS label to s0 if shared is true. + * This will allow all containers to share the content. + * + * @path : a file or directory + * @label : label string + * @shared : whether to use share mode + * + * Returns 0 on success, < 0 on failure + */ +static int selinux_relabel(const char *path, const char *label, bool shared) +{ + __do_free char *tmp_file_label = NULL; + + if (path == NULL || label == NULL) { + return 0; + } + + if (!is_selinux_enabled()) { + return 0; + } + + if (is_exclude_relabel_path(path)) { + ERROR("SELinux relabeling of %s is not allowed", path); + return -1; + } + + if (shared) { + tmp_file_label = convert_context_to_share_mode(label); + if (tmp_file_label == NULL) { + ERROR("Failed to convert context to share mode: %s", label); + return -1; + } + } else { + tmp_file_label = strdup(label); + } + + + if (selinux_chcon(path, tmp_file_label, true) != 0) { + ERROR("Failed to modify %s's selinux context: %s", path, tmp_file_label); + return -1; + } + + return 0; +} + +#endif + /* * selinux_keyring_label_set: Set SELinux context that will be assigned to the keyring * @@ -103,6 +357,10 @@ static struct lsm_drv selinux_drv = { .process_label_get = selinux_process_label_get, .process_label_set = selinux_process_label_set, .keyring_label_set = selinux_keyring_label_set, +#ifdef HAVE_ISULAD + .file_label_set = selinux_file_label_set, + .relabel = selinux_relabel, +#endif }; struct lsm_drv *lsm_selinux_drv_init(void) diff --git a/src/lxc/tools/lxc_attach.c b/src/lxc/tools/lxc_attach.c index a8f493a..1a5a241 100644 --- a/src/lxc/tools/lxc_attach.c +++ b/src/lxc/tools/lxc_attach.c @@ -72,8 +72,20 @@ static const struct option my_longopts[] = { {"set-var", required_argument, 0, 'v'}, {"pty-log", required_argument, 0, 'L'}, {"rcfile", required_argument, 0, 'f'}, +#ifndef HAVE_ISULAD {"uid", required_argument, 0, 'u'}, {"gid", required_argument, 0, 'g'}, +#else + {"workdir", required_argument, 0, 'w'}, + {"user", required_argument, 0, 'u'}, + {"in-fifo", required_argument, 0, OPT_INPUT_FIFO}, /* isulad add terminal fifos*/ + {"out-fifo", required_argument, 0, OPT_OUTPUT_FIFO}, + {"err-fifo", required_argument, 0, OPT_STDERR_FIFO}, + {"suffix", required_argument, 0, OPT_ATTACH_SUFFIX}, + {"timeout", required_argument, 0, OPT_ATTACH_TIMEOUT}, + {"disable-pty", no_argument, 0, OPT_DISABLE_PTY}, + {"open-stdin", no_argument, 0, OPT_OPEN_STDIN}, +#endif LXC_COMMON_OPTIONS }; @@ -124,9 +136,26 @@ Options :\n\ multiple times.\n\ -f, --rcfile=FILE\n\ Load configuration file FILE\n\ +" +#ifndef HAVE_ISULAD +"\ -u, --uid=UID Execute COMMAND with UID inside the container\n\ -g, --gid=GID Execute COMMAND with GID inside the container\n\ -", +" +#else +"\ + -w, --workdir Working directory inside the container.\n\ + -u, --user User ID (format: UID[:GID])\n\ + --in-fifo Stdin fifo path\n\ + --out-fifo Stdout fifo path\n\ + --err-fifo Stderr fifo path\n\ + --suffi ID for mutli-attach on one container\n\ + --timeout Timeout in seconds (default: 0)\n\ + --disable-pty Disable pty for attach\n\ + --open-stdin Open stdin for attach\n\ +" +#endif +, .options = my_longopts, .parser = my_parser, .checker = NULL, @@ -136,6 +165,71 @@ Options :\n\ .gid = LXC_INVALID_GID, }; +#ifdef HAVE_ISULAD +static int parse_user_id(const char *username, char **uid, char **gid, char **tmp_dup) +{ + char *tmp = NULL; + char *pdot = NULL; + + if (uid == NULL || gid == NULL || tmp_dup == NULL) { + return -1; + } + + if (username != NULL) { + tmp = strdup(username); + if (tmp == NULL) { + ERROR("Failed to duplicate user name"); + return -1; + } + + // for free tmp in caller + *tmp_dup = tmp; + pdot = strstr(tmp, ":"); + if (pdot != NULL) { + *pdot = '\0'; + if (pdot != tmp) { + // uid found + *uid = tmp; + } + + if (*(pdot + 1) != '\0') { + // gid found + *gid = pdot + 1; + } + } else { + // No : found + if (*tmp != '\0') { + *uid = tmp; + } + } + } + + return 0; +} + +static int get_attach_uid_gid(const char *username, uid_t *user_id, gid_t *group_id) +{ + char *tmp = NULL; + char *uid = NULL; + char *gid = NULL; + + // parse uid and gid by username + if (parse_user_id(username, &uid, &gid, &tmp) != 0) { + return -1; + } + + if (uid != NULL) { + *user_id = (unsigned int)atoll(uid); + } + if (gid != NULL) { + *group_id = (unsigned int)atoll(gid); + } + + free(tmp); + return 0; +} +#endif + static int my_parser(struct lxc_arguments *args, int c, char *arg) { int ret; @@ -193,6 +287,7 @@ static int my_parser(struct lxc_arguments *args, int c, char *arg) case 'f': args->rcfile = arg; break; +#ifndef HAVE_ISULAD case 'u': if (lxc_safe_uint(arg, &args->uid) < 0) return -1; @@ -201,6 +296,42 @@ static int my_parser(struct lxc_arguments *args, int c, char *arg) if (lxc_safe_uint(arg, &args->gid) < 0) return -1; break; +#else + case 'u': + if (get_attach_uid_gid(arg, &args->uid, &args->gid) != 0) { + ERROR("Failed to get attach user U/GID"); + return -1; + } + break; + case 'w': + args->workdir=arg; + break; + case OPT_INPUT_FIFO: + args->terminal_fifos[0] = arg; + break; + case OPT_OUTPUT_FIFO: + args->terminal_fifos[1] = arg; + break; + case OPT_STDERR_FIFO: + args->terminal_fifos[2] = arg; + break; + case OPT_ATTACH_SUFFIX: + args->suffix = arg; + break; + case OPT_ATTACH_TIMEOUT: + if(!is_non_negative_num(arg)) { + ERROR("Error attach timeout parameter:%s.\n", arg); + return -1; + } + args->attach_timeout = (unsigned int)atoll(arg); + break; + case OPT_DISABLE_PTY: + args->disable_pty = 1; + break; + case OPT_OPEN_STDIN: + args->open_stdin = 1; + break; +#endif } return 0; @@ -264,6 +395,285 @@ static int lxc_attach_create_log_file(const char *log_file) return fd; } +#ifdef HAVE_ISULAD +// isulad: send '128 + signal' if container is killed by signal. +#define EXIT_SIGNAL_OFFSET 128 + +/*isulad: attach with terminal*/ +static int do_attach_foreground(struct lxc_container *c, lxc_attach_command_t *command, + lxc_attach_options_t *attach_options, + char **errmsg) +{ + int ret = 0; + pid_t pid; + int wexit = -1; + int signal; + + if (command->program) + ret = c->attach(c, lxc_attach_run_command, command, attach_options, &pid); + else + ret = c->attach(c, lxc_attach_run_shell, NULL, attach_options, &pid); + if (ret < 0) { + *errmsg = safe_strdup("Internal error, failed to call attach"); + goto out; + } + + ret = lxc_wait_for_pid_status(pid); + if (ret < 0) { + free(*errmsg); + *errmsg = safe_strdup("Internal error, failed to wait attached process"); + goto out; + } + + if (WIFEXITED(ret)) + wexit = WEXITSTATUS(ret); + else + wexit = -1; + + if (WIFSIGNALED(ret)) { + signal = WTERMSIG(ret); + wexit = EXIT_SIGNAL_OFFSET + signal; + } + + WARN("Execd pid %d exit with %d", pid, wexit); + +out: + if (c->lxc_conf->errmsg) { + free(*errmsg); + *errmsg = safe_strdup(c->lxc_conf->errmsg); + } + return wexit; +} + +static void close_msg_pipe(int *errpipe) +{ + if (errpipe[0] >= 0) { + close(errpipe[0]); + errpipe[0] = -1; + } + if (errpipe[1] >= 0) { + close(errpipe[1]); + errpipe[1] = -1; + } +} + +/*isulad: attach without terminal in background */ +static int do_attach_background(struct lxc_container *c, lxc_attach_command_t *command, + lxc_attach_options_t *attach_options, + char **errmsg) +{ + int ret = 0; + int msgpipe[2]; + pid_t pid = 0; + ssize_t size_read; + char msgbuf[BUFSIZ + 1] = {0}; + + //pipdfd for get error message of child or grandchild process. + if (pipe2(msgpipe, O_CLOEXEC) != 0) { + SYSERROR("Failed to init msgpipe"); + return -1; + } + + pid = fork(); + if (pid < 0) { + close_msg_pipe(msgpipe); + return -1; + } + + if (pid != 0) { + close(msgpipe[1]); + msgpipe[1] = -1; + size_read = read(msgpipe[0], msgbuf, BUFSIZ); + if (size_read > 0) { + *errmsg = safe_strdup(msgbuf); + ret = -1; + } + + close(msgpipe[0]); + msgpipe[0] = -1; + + return ret; + } + + /* second fork to be reparented by init */ + pid = fork(); + if (pid < 0) { + SYSERROR("Error doing dual-fork"); + close_msg_pipe(msgpipe); + exit(1); + } + if (pid != 0) { + close_msg_pipe(msgpipe); + exit(0); + } + + close(msgpipe[0]); + msgpipe[0] = -1; + + if (null_stdfds() < 0) { + ERROR("failed to close fds"); + exit(1); + } + setsid(); + + if (command->program) + ret = c->attach(c, lxc_attach_run_command, command, attach_options, &pid); + else + ret = c->attach(c, lxc_attach_run_shell, NULL, attach_options, &pid); + if (ret < 0) { + if (c->lxc_conf->errmsg) + lxc_write_error_message(msgpipe[1], "%s", c->lxc_conf->errmsg); + else + lxc_write_error_message(msgpipe[1], "Failed to attach container"); + close(msgpipe[1]); + msgpipe[1] = -1; + ret = -1; + goto out; + } + + close(msgpipe[1]); + msgpipe[1] = -1; + + ret = wait_for_pid(pid); +out: + lxc_container_put(c); + if (ret) + exit(EXIT_FAILURE); + else + exit(0); +} + +int main(int argc, char *argv[]) +{ + int wexit = 0; + struct lxc_log log; + char *errmsg = NULL; + lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT; + lxc_attach_command_t command = (lxc_attach_command_t){.program = NULL}; + + if (lxc_caps_init()) + exit(EXIT_FAILURE); + + if (lxc_arguments_parse(&my_args, argc, argv)) + exit(EXIT_FAILURE); + + log.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; + + if (lxc_log_init(&log)) + exit(EXIT_FAILURE); + + if (geteuid()) + if (access(my_args.lxcpath[0], O_RDONLY) < 0) { + ERROR("You lack access to %s", my_args.lxcpath[0]); + exit(EXIT_FAILURE); + } + + struct lxc_container *c = lxc_container_new(my_args.name, my_args.lxcpath[0]); + if (!c) + exit(EXIT_FAILURE); + + if (my_args.rcfile) { + c->clear_config(c); + if (!c->load_config(c, my_args.rcfile)) { + ERROR("Failed to load rcfile"); + lxc_container_put(c); + exit(EXIT_FAILURE); + } + + c->configfile = strdup(my_args.rcfile); + if (!c->configfile) { + ERROR("Out of memory setting new config filename"); + lxc_container_put(c); + exit(EXIT_FAILURE); + } + } + + if (!c->may_control(c)) { + ERROR("Insufficent privileges to control %s", c->name); + lxc_container_put(c); + exit(EXIT_FAILURE); + } + + if (remount_sys_proc) + attach_options.attach_flags |= LXC_ATTACH_REMOUNT_PROC_SYS; + + if (elevated_privileges) + attach_options.attach_flags &= ~(elevated_privileges); + + if (my_args.terminal_fifos[0] || my_args.terminal_fifos[1] || my_args.terminal_fifos[2]) { + attach_options.init_fifo[0] = my_args.terminal_fifos[0]; + attach_options.init_fifo[1] = my_args.terminal_fifos[1]; + attach_options.init_fifo[2] = my_args.terminal_fifos[2]; + attach_options.attach_flags |= LXC_ATTACH_TERMINAL; + } else if (stdfd_is_pty()) { + attach_options.attach_flags |= LXC_ATTACH_TERMINAL; + } + + attach_options.namespaces = namespace_flags; + attach_options.personality = new_personality; + attach_options.env_policy = env_policy; + attach_options.extra_env_vars = extra_env; + attach_options.extra_keep_env = extra_keep; + attach_options.timeout = my_args.attach_timeout; + + if (my_args.argc > 0) { + command.program = my_args.argv[0]; + command.argv = (char**)my_args.argv; + } + + if (my_args.console_log) { + attach_options.log_fd = lxc_attach_create_log_file(my_args.console_log); + if (attach_options.log_fd < 0) { + ERROR("Failed to create log file for %s", c->name); + lxc_container_put(c); + exit(EXIT_FAILURE); + } + } + + if (my_args.uid != LXC_INVALID_UID) + attach_options.uid = my_args.uid; + + if (my_args.gid != LXC_INVALID_GID) + attach_options.gid = my_args.gid; + + attach_options.suffix = my_args.suffix; + + if (my_args.disable_pty) { + attach_options.disable_pty = true; + } + + if (my_args.open_stdin) { + attach_options.open_stdin = true; + } + + if (my_args.workdir) { + attach_options.initial_cwd = my_args.workdir; + } + + /* isulad: add do attach background */ + if (attach_options.attach_flags & LXC_ATTACH_TERMINAL) + wexit = do_attach_foreground(c, &command, &attach_options, &errmsg); + else + wexit = do_attach_background(c, &command, &attach_options, &errmsg); + + if (errmsg) { + fprintf(stderr, "%s:%s:%s:%d starting container process caused \"%s\"", c->name, + __FILE__, __func__, __LINE__, errmsg); + free(errmsg); + } + + lxc_container_put(c); + if (wexit >= 0) + exit(wexit); + + exit(EXIT_FAILURE); +} +#else int main(int argc, char *argv[]) { int ret = -1; @@ -377,3 +787,4 @@ out: exit(EXIT_FAILURE); } +#endif -- 2.25.1