Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/linyaps_box/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ try {
return 0;
},
[](const command::exec_options &options) -> int {
command::exec(options);
__builtin_unreachable();
return command::exec(options);
},
[](const command::kill_options &options) {
command::kill(options);
Expand Down
29 changes: 17 additions & 12 deletions src/linyaps_box/command/exec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
#include "linyaps_box/command/exec.h"

#include "linyaps_box/impl/status_directory.h"
#include "linyaps_box/runtime.h"

Check warning on line 8 in src/linyaps_box/command/exec.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "linyaps_box/runtime.h" not found.
#include "linyaps_box/status_directory.h"

Check warning on line 9 in src/linyaps_box/command/exec.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "linyaps_box/status_directory.h" not found.

void linyaps_box::command::exec(const struct exec_options &options)
auto linyaps_box::command::exec(const struct exec_options &options) -> int
{
std::unique_ptr<status_directory> dir =
std::make_unique<impl::status_directory>(options.global_.get().root);
Expand All @@ -20,12 +20,17 @@
throw std::runtime_error("container not found");
}

config::process_t proc;
proc.cwd = options.cwd.value_or("/");
proc.args = options.command;
proc.terminal = isatty(STDIN_FILENO) == 1 && isatty(STDOUT_FILENO) == 1;
proc.no_new_privileges = options.no_new_privs;
proc.env = options.envs.value_or(std::vector<std::string>{});
exec_container_option option;
option.proc.cwd = options.cwd.value_or("/");
option.proc.args = options.command;
option.proc.terminal = options.tty;
option.proc.no_new_privileges = options.no_new_privs;
option.proc.env = options.envs.value_or(std::vector<std::string>{});
option.preserve_fds = options.preserve_fds;

if (option.proc.terminal && options.console_socket) {
option.console_socket = unixSocketClient::connect(options.console_socket.value());
}

#ifdef LINYAPS_BOX_ENABLE_CAP
if (options.caps) {
Expand All @@ -45,14 +50,14 @@
});
};

transform_cap(proc.capabilities.effective);
transform_cap(proc.capabilities.ambient);
transform_cap(proc.capabilities.bounding);
transform_cap(proc.capabilities.permitted);
transform_cap(option.proc.capabilities.effective);
transform_cap(option.proc.capabilities.ambient);
transform_cap(option.proc.capabilities.bounding);
transform_cap(option.proc.capabilities.permitted);
}
#endif

// TODO: support exec fully

container->second.exec(proc);
return container->second.exec(std::move(option));
}
2 changes: 1 addition & 1 deletion src/linyaps_box/command/exec.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@

namespace linyaps_box::command {

[[noreturn]] void exec(const exec_options &options);
[[nodiscard]] auto exec(const exec_options &options) -> int;

} // namespace linyaps_box::command
25 changes: 14 additions & 11 deletions src/linyaps_box/command/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,24 +96,27 @@ linyaps_box::command::options linyaps_box::command::parse(int argc, char *argv[]
"for example `1000` for UID=1000 "
"or `1000:1000` for UID=1000 and GID=1000")
->type_name("UID[:GID]");
cmd_exec->add_option("--cwd", exec_opt.cwd, "Current working directory.");
cmd_exec->add_option("--cwd", exec_opt.cwd, "Current working directory.")->type_name("PATH");
cmd_exec->add_option("--env", exec_opt.envs, "Environment variables to set")
->multi_option_policy(CLI::MultiOptionPolicy::TakeAll)
->check(
[](const std::string &str) {
if (str.find('=') == std::string::npos) {
return "invalid argument, env must be in the format of KEY=VALUE";
}
return "";
},
"env_check");
->type_name("ENV")
->take_all()
->check([](const std::string &str) {
if (str.find('=') == std::string::npos) {
return "invalid argument, env must be in the format of KEY=VALUE";
}
return "";
});
cmd_exec->add_option("--console-socket",
exec_opt.console_socket,
"Path to an unix socket that will receive the master end of the console's "
"pseudoterminal")
->type_name("SOCKET")
->check(socket_check);
cmd_exec->add_flag("--tty", exec_opt.tty, "Allocate a pseudo-TTY")->default_val(false);
cmd_exec->add_flag("-t,--tty", exec_opt.tty, "Allocate a pseudo-TTY")->take_last();
cmd_exec->add_option("--preserve-fds",
exec_opt.preserve_fds,
"Pass N additional file descriptors to the container")
->type_name("N");
// TODO: enable capabilities and no_new_privs support after rewrite exec,
// cmd_exec->add_option("-c,--cap", options.exec.caps, "Set capabilities")
// ->check(
Expand Down
12 changes: 6 additions & 6 deletions src/linyaps_box/command/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ struct list_options
struct exec_options
{
explicit exec_options(global_options &global)
: no_new_privs(false)
, global_(global)
: global_(global)
{
}

bool no_new_privs;
bool tty;
bool no_new_privs{ false };
bool tty{ false };
int preserve_fds{ 0 };
std::reference_wrapper<global_options> global_;
std::vector<std::string> command;
std::string user;
Expand All @@ -64,8 +64,8 @@ struct run_options
std::string ID;
std::string bundle;
std::string config;
std::string console_socket;
int preserve_fds{};
std::optional<std::string> console_socket;
int preserve_fds{ 0 };
};

struct kill_options
Expand Down
4 changes: 2 additions & 2 deletions src/linyaps_box/command/run.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ auto linyaps_box::command::run(const struct run_options &options) -> int
run_container_options_t run_options;
run_options.preserve_fds = options.preserve_fds;

if (container.get_config().process.terminal && !options.console_socket.empty()) {
run_options.console_socket = unixSocketClient::connect(options.console_socket);
if (container.get_config().process.terminal && options.console_socket) {
run_options.console_socket = unixSocketClient::connect(options.console_socket.value());
}

return container.run(std::move(run_options));
Expand Down
156 changes: 100 additions & 56 deletions src/linyaps_box/container_ref.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@
//
// SPDX-License-Identifier: LGPL-3.0-or-later

#include "linyaps_box/container_ref.h"

Check warning on line 5 in src/linyaps_box/container_ref.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "linyaps_box/container_ref.h" not found.

#include "linyaps_box/container_monitor.h"

Check warning on line 7 in src/linyaps_box/container_ref.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "linyaps_box/container_monitor.h" not found.
#include "linyaps_box/terminal.h"

Check warning on line 8 in src/linyaps_box/container_ref.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "linyaps_box/terminal.h" not found.
#include "linyaps_box/utils/file.h"

Check warning on line 9 in src/linyaps_box/container_ref.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "linyaps_box/utils/file.h" not found.
#include "linyaps_box/utils/log.h"

Check warning on line 10 in src/linyaps_box/container_ref.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "linyaps_box/utils/log.h" not found.
#include "linyaps_box/utils/process.h"

Check warning on line 11 in src/linyaps_box/container_ref.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "linyaps_box/utils/process.h" not found.
#include "linyaps_box/utils/session.h"

Check warning on line 12 in src/linyaps_box/container_ref.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "linyaps_box/utils/session.h" not found.
#include "linyaps_box/utils/socket.h"

Check warning on line 13 in src/linyaps_box/container_ref.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "linyaps_box/utils/socket.h" not found.
#include "linyaps_box/utils/terminal.h"

#include <csignal> // IWYU pragma: keep
#include <utility>
Expand Down Expand Up @@ -38,73 +45,110 @@
throw std::system_error(errno, std::system_category(), std::move(ss).str());
}

void linyaps_box::container_ref::exec(const linyaps_box::config::process_t &process)
auto linyaps_box::container_ref::exec(exec_container_option option) -> int
{
auto target = std::to_string(this->status().PID);

std::vector<const char *> argv{
"nsenter",
"--target",
target.c_str(),
"--user",
"--mount",
"--pid",
"--no-fork",
// FIXME:
// Old nsenter command do not support --wdns,
// so we have to implement nsenter by ourself in the future.
"--preserve-credentials",
};
// TODO: support detach later
utils::prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0);

for (const auto &arg : process.args) {
argv.push_back(arg.c_str());
std::optional<unixSocketClient> recv_socketpair;
if (option.proc.terminal && !option.console_socket) {
auto [socket1, socket2] = utils::socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
option.console_socket = unixSocketClient{ std::move(socket1) };
recv_socketpair = unixSocketClient{ std::move(socket2) };
}
argv.push_back(nullptr);

std::vector<const char *> c_env;
c_env.reserve(process.env.size());
for (const auto &env : process.env) {
c_env.push_back(env.c_str());
auto child = fork();
if (child < 0) {
throw std::system_error(errno, std::system_category(), "fork");
}
c_env.push_back(nullptr);

LINYAPS_BOX_DEBUG() << [&argv]() -> std::string {
auto result = std::accumulate(argv.cbegin(),
argv.cend() - 1,
std::string{ "args:[" },
[](std::string init, const std::string &val) {
init += val;
init.push_back(' ');
return init;
});
result.push_back(']');
result.insert(0, "execvp nsenter with arguments: ");
return result;
}();

// FIXME:
// We only handle the command arguments for now
// here are some other fields in process we need to consider:
// terminal
// console.height
// console.width
// cwd
// env
// rlimits
// apparmor_profile
// capabilities
// no_new_privileges
// oom_score_adj

::execvpe("nsenter", const_cast<char **>(argv.data()), const_cast<char **>(c_env.data()));
if (child == 0) {
// TODO: create terminal after rewrite exec. it should be created in the container namespace
if (option.console_socket) {
utils::setsid();
auto [master, slave] = create_pty_pair();

slave.setup_stdio();
// TODO: use fchown after we implement exec option `--user`
slave.set_size({});

option.console_socket->send_fd(std::move(master).take());
option.console_socket.reset();
}

std::vector<const char *> argv{
"nsenter",
"--target",
target.c_str(),
"--user",
"--mount",
"--pid",
// FIXME:
// Old nsenter command do not support --wdns,
// so we have to implement nsenter by ourself in the future.
"--preserve-credentials",
};

for (const auto &arg : option.proc.args) {
argv.push_back(arg.c_str());
}
argv.push_back(nullptr);

std::vector<const char *> c_env;
c_env.reserve(option.proc.env.size());
for (const auto &env : option.proc.env) {
c_env.push_back(env.c_str());
}
c_env.push_back(nullptr);

std::stringstream ss;
ss << "execvp nsenter with arguments:";
for (const auto &arg : argv) {
ss << " " << arg;
// FIXME:
// We only handle the command arguments for now
// here are some other fields in process we need to consider:
// terminal
// console.height
// console.width
// cwd
// env
// rlimits
// apparmor_profile
// capabilities
// no_new_privileges
// oom_score_adj

::execvpe("nsenter", const_cast<char **>(argv.data()), const_cast<char **>(c_env.data()));
_exit(EXIT_FAILURE);
}

throw std::system_error(errno, std::system_category(), std::move(ss).str());
auto in = utils::file_descriptor{ utils::fileno(stdin), false };
auto out = utils::file_descriptor{ utils::fileno(stdout), false };

container_monitor monitor{ child };

[&recv_socketpair, &monitor, &in, &out]() {
if (!recv_socketpair) {
return;
}

LINYAPS_BOX_DEBUG() << "Container requires a terminal";

std::string payload;
auto master = terminal_master{ recv_socketpair->recv_fd(payload) };

recv_socketpair->release();

in.set_nonblock(true);
out.set_nonblock(true);

monitor.enable_io_forwarding(std::move(master), in, out);
}();

if (!monitor.enable_signal_forwarding()) {
return 0;
}

return monitor.wait_container_exit();
}

const linyaps_box::status_directory &linyaps_box::container_ref::status_dir() const
Expand Down
10 changes: 9 additions & 1 deletion src/linyaps_box/container_ref.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,17 @@
#include "linyaps_box/config.h"
#include "linyaps_box/container_status.h"
#include "linyaps_box/status_directory.h"
#include "linyaps_box/unixsocket.h"

namespace linyaps_box {

struct exec_container_option
{
int preserve_fds;
config::process_t proc;
std::optional<unixSocketClient> console_socket;
};

class container_ref
{
public:
Expand All @@ -23,7 +31,7 @@ class container_ref

[[nodiscard]] auto status() const -> container_status_t;
void kill(int signal) const;
[[noreturn]] void exec(const config::process_t &process);
[[nodiscard]] auto exec(exec_container_option option) -> int;

protected:
[[nodiscard]] auto status_dir() const -> const status_directory &;
Expand Down
1 change: 1 addition & 0 deletions src/linyaps_box/status_directory.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <vector>

namespace linyaps_box {
// TODO: place container status into a directory
class status_directory : public virtual interface
{
protected:
Expand Down