Add --hostname to set the sandbox's hostname under -r/--run
Long-option only. Threaded through run_container() into build_bwrap_args(), which passes it as bwrap's own --hostname only when --unshare-uts is actually among the flags being given to bwrap (bwrap itself refuses --hostname without it) -- otherwise logs a warning and leaves the hostname alone, since a stock Android kernel in degraded mode may not support a UTS namespace at all. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gv3s5jckJKzh6JkMoi2Akz
This commit is contained in:
@@ -47,7 +47,10 @@ Source layout (all under `src/`):
|
|||||||
`run_container()` resolves each spec (erroring out, `ok = false`, same as a
|
`run_container()` resolves each spec (erroring out, `ok = false`, same as a
|
||||||
failed `--user` resolution — `bwrap` is skipped but unmount/cleanup still runs)
|
failed `--user` resolution — `bwrap` is skipped but unmount/cleanup still runs)
|
||||||
into a `ResolvedVolumeMount`, rejecting a duplicate or non-absolute container
|
into a `ResolvedVolumeMount`, rejecting a duplicate or non-absolute container
|
||||||
path first, and passes the resolved list to `run_bwrap()`.
|
path first, and passes the resolved list to `run_bwrap()`. `--hostname <name>`
|
||||||
|
(long-option only, no short form) is likewise threaded straight through
|
||||||
|
`run_container()` into `run_bwrap()`/`build_bwrap_args()` (`bwrap.{h,cpp}`) —
|
||||||
|
see there for how/when it actually takes effect.
|
||||||
- `oci_image.{h,cpp}` — validates/parses the OCI Image Layout tar (libarchive +
|
- `oci_image.{h,cpp}` — validates/parses the OCI Image Layout tar (libarchive +
|
||||||
nlohmann_json) and extracts layer blobs. `list_oci_images()` scans a directory
|
nlohmann_json) and extracts layer blobs. `list_oci_images()` scans a directory
|
||||||
(non-recursively) for `*.tar`/`*.tar.*` files and, for each valid OCI archive,
|
(non-recursively) for `*.tar`/`*.tar.*` files and, for each valid OCI archive,
|
||||||
@@ -80,6 +83,12 @@ Source layout (all under `src/`):
|
|||||||
`slirp4netns`), unsharing it just leaves the sandbox with no network at all. Re-add
|
`slirp4netns`), unsharing it just leaves the sandbox with no network at all. Re-add
|
||||||
once network isolation is implemented; `detect_bwrap_unshare_args()` itself still
|
once network isolation is implemented; `detect_bwrap_unshare_args()` itself still
|
||||||
probes/reports it (e.g. via `-t/--test`), since that's kernel capability, not policy.
|
probes/reports it (e.g. via `-t/--test`), since that's kernel capability, not policy.
|
||||||
|
`build_bwrap_args()`/`run_bwrap()` also take an optional `hostname` (from
|
||||||
|
`--hostname`, long-option only): passed through as bwrap's own `--hostname` only
|
||||||
|
when `--unshare-uts` is actually among the flags `bwrap` is being given (bwrap
|
||||||
|
itself refuses `--hostname` without it) — otherwise logs a warning and leaves the
|
||||||
|
sandbox's hostname alone, since a stock Android kernel in degraded mode may not
|
||||||
|
support a UTS namespace at all.
|
||||||
Never requests `--unshare-user` when running as root: root doesn't need a fresh
|
Never requests `--unshare-user` when running as root: root doesn't need a fresh
|
||||||
user namespace for privilege, and bwrap's own single-mapping uid/gid setup for one
|
user namespace for privilege, and bwrap's own single-mapping uid/gid setup for one
|
||||||
triggers the kernel's unprivileged-userns setgroups() restriction, which showed up
|
triggers the kernel's unprivileged-userns setgroups() restriction, which showed up
|
||||||
@@ -212,8 +221,8 @@ Build directory is `buildDir/` (already configured).
|
|||||||
- Run the executable: `./buildDir/slocker-lite -m <image.tar>` (see `--help` for the
|
- Run the executable: `./buildDir/slocker-lite -m <image.tar>` (see `--help` for the
|
||||||
full flag list: `-m/--mount`, `-r/--run`, `-u/--umount`, `-c/--cleanup`,
|
full flag list: `-m/--mount`, `-r/--run`, `-u/--umount`, `-c/--cleanup`,
|
||||||
`-l/--list-images`, `-i/--inspect`, `-n/--no-nsenter`, `--user`, `--group`,
|
`-l/--list-images`, `-i/--inspect`, `-n/--no-nsenter`, `--user`, `--group`,
|
||||||
`-v/--volume`, `--list-volumes`, `--delete-volume`, `--delete-volume-full`,
|
`--hostname`, `-v/--volume`, `--list-volumes`, `--delete-volume`,
|
||||||
`-t/--test`, `--log-level`, `-h/--help`, `-V/--version`)
|
`--delete-volume-full`, `-t/--test`, `--log-level`, `-h/--help`, `-V/--version`)
|
||||||
- Run tests: `meson test -C buildDir`
|
- Run tests: `meson test -C buildDir`
|
||||||
|
|
||||||
## Code style
|
## Code style
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ slocker-lite -V|--version
|
|||||||
| `-n, --no-nsenter` | With `--run`, bind the mount directly instead of `nsenter`-ing into `fuse-overlayfs`'s namespace. Automatic when running as root; use this to force it off otherwise. |
|
| `-n, --no-nsenter` | With `--run`, bind the mount directly instead of `nsenter`-ing into `fuse-overlayfs`'s namespace. Automatic when running as root; use this to force it off otherwise. |
|
||||||
| `--user <user>` | With `--run`, run the command as this user (name or numeric uid) instead of the image's own declared user (or root, if it declares none). Resolved against the image's own `/etc/passwd`. Only takes effect when `--run` executes as root. |
|
| `--user <user>` | With `--run`, run the command as this user (name or numeric uid) instead of the image's own declared user (or root, if it declares none). Resolved against the image's own `/etc/passwd`. Only takes effect when `--run` executes as root. |
|
||||||
| `--group <group>` | With `--user`, use this group (name or numeric gid) instead of the user's primary group. |
|
| `--group <group>` | With `--user`, use this group (name or numeric gid) instead of the user's primary group. |
|
||||||
|
| `--hostname <name>` | With `--run`, set the sandbox's hostname. Only takes effect if the running kernel supports `--unshare-uts`; ignored with a warning otherwise. |
|
||||||
| `-l, --list-images <dir>` | List OCI Image Layout tars (`*.tar`, `*.tar.*`) found directly in `<dir>`, with their `name:tag`. |
|
| `-l, --list-images <dir>` | List OCI Image Layout tars (`*.tar`, `*.tar.*`) found directly in `<dir>`, with their `name:tag`. |
|
||||||
| `-i, --inspect <image.tar>` | Print an image's declared user, exposed ports, env, volumes, and default command, without mounting or running it. |
|
| `-i, --inspect <image.tar>` | Print an image's declared user, exposed ports, env, volumes, and default command, without mounting or running it. |
|
||||||
| `-v, --volume <name> <dir>` | Create a named volume mapped to a host directory (created if missing), recorded in the config file's `volumes` section. Fails if the name or directory is already used by an existing volume. Volume names can't contain `/`. With `--run`, instead mounts a volume into the sandbox (repeatable): `<name>` is an existing named volume, or, if it contains `/`, a host directory path (created if missing); `<dir>` is the absolute path inside the container to mount it at. If the host directory is empty and the image already has content there, that content is copied in first, preserving numeric ownership/permissions/links and, where the host filesystem supports them, extended attributes/ACLs (skipped with a warning otherwise). |
|
| `-v, --volume <name> <dir>` | Create a named volume mapped to a host directory (created if missing), recorded in the config file's `volumes` section. Fails if the name or directory is already used by an existing volume. Volume names can't contain `/`. With `--run`, instead mounts a volume into the sandbox (repeatable): `<name>` is an existing named volume, or, if it contains `/`, a host directory path (created if missing); `<dir>` is the absolute path inside the container to mount it at. If the host directory is empty and the image already has content there, that content is copied in first, preserving numeric ownership/permissions/links and, where the host filesystem supports them, extended attributes/ACLs (skipped with a warning otherwise). |
|
||||||
@@ -102,6 +103,9 @@ slocker-lite -V|--version
|
|||||||
# Run as a specific user (as root only)
|
# Run as a specific user (as root only)
|
||||||
sudo ./buildDir/slocker-lite -r myimage.tar --user git
|
sudo ./buildDir/slocker-lite -r myimage.tar --user git
|
||||||
|
|
||||||
|
# Run with a custom hostname inside the sandbox
|
||||||
|
./buildDir/slocker-lite -r myimage.tar --hostname mybox
|
||||||
|
|
||||||
# List every OCI image tar in a directory
|
# List every OCI image tar in a directory
|
||||||
./buildDir/slocker-lite -l ./images
|
./buildDir/slocker-lite -l ./images
|
||||||
|
|
||||||
|
|||||||
+20
-3
@@ -179,7 +179,8 @@ std::vector<std::string> detect_bwrap_unshare_args() {
|
|||||||
std::vector<std::string> build_bwrap_args(const std::string& root,
|
std::vector<std::string> build_bwrap_args(const std::string& root,
|
||||||
const std::vector<std::string>& command,
|
const std::vector<std::string>& command,
|
||||||
const std::vector<ResolvedVolumeMount>& volumes,
|
const std::vector<ResolvedVolumeMount>& volumes,
|
||||||
std::optional<ResolvedUser> user) {
|
std::optional<ResolvedUser> user,
|
||||||
|
const std::optional<std::string>& hostname) {
|
||||||
// --new-session detaches from the controlling terminal, which breaks job
|
// --new-session detaches from the controlling terminal, which breaks job
|
||||||
// control for an interactive foreground shell ("can't access tty"). Re-enable
|
// control for an interactive foreground shell ("can't access tty"). Re-enable
|
||||||
// once background/daemonized runs are implemented, where that's the point.
|
// once background/daemonized runs are implemented, where that's the point.
|
||||||
@@ -187,6 +188,7 @@ std::vector<std::string> build_bwrap_args(const std::string& root,
|
|||||||
|
|
||||||
auto unshare_args = detect_bwrap_unshare_args();
|
auto unshare_args = detect_bwrap_unshare_args();
|
||||||
bool has_pid_ns = false;
|
bool has_pid_ns = false;
|
||||||
|
bool has_uts_ns = false;
|
||||||
for (const auto& arg : unshare_args) {
|
for (const auto& arg : unshare_args) {
|
||||||
// Not requested yet: without any network setup (slirp4netns or similar),
|
// Not requested yet: without any network setup (slirp4netns or similar),
|
||||||
// unsharing it just leaves the sandbox with no network at all. Re-add once
|
// unsharing it just leaves the sandbox with no network at all. Re-add once
|
||||||
@@ -199,12 +201,26 @@ std::vector<std::string> build_bwrap_args(const std::string& root,
|
|||||||
args.push_back(arg);
|
args.push_back(arg);
|
||||||
if (arg == "--unshare-pid") {
|
if (arg == "--unshare-pid") {
|
||||||
has_pid_ns = true;
|
has_pid_ns = true;
|
||||||
|
} else if (arg == "--unshare-uts") {
|
||||||
|
has_uts_ns = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (has_pid_ns) {
|
if (has_pid_ns) {
|
||||||
args.push_back("--as-pid-1");
|
args.push_back("--as-pid-1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hostname) {
|
||||||
|
// bwrap itself refuses --hostname without --unshare-uts, so this must be
|
||||||
|
// guarded the same way rather than always passed through -- the kernel may
|
||||||
|
// not support a UTS namespace at all (degraded-mode Android kernels).
|
||||||
|
if (has_uts_ns) {
|
||||||
|
args.insert(args.end(), {"--hostname", *hostname});
|
||||||
|
} else {
|
||||||
|
spdlog::warn("--hostname requires an unshared UTS namespace, which the running "
|
||||||
|
"kernel doesn't support here; ignoring --hostname");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> filesystem_args = {
|
std::vector<std::string> filesystem_args = {
|
||||||
"--bind",
|
"--bind",
|
||||||
root,
|
root,
|
||||||
@@ -288,14 +304,15 @@ std::optional<std::vector<std::string>> wrap_for_root_namespace(const std::strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
int run_bwrap(const std::string& root, const std::vector<std::string>& command, bool use_nsenter,
|
int run_bwrap(const std::string& root, const std::vector<std::string>& command, bool use_nsenter,
|
||||||
const std::vector<ResolvedVolumeMount>& volumes, std::optional<ResolvedUser> user) {
|
const std::vector<ResolvedVolumeMount>& volumes, std::optional<ResolvedUser> user,
|
||||||
|
const std::optional<std::string>& hostname) {
|
||||||
if (user && !find_priv_drop_helper()) {
|
if (user && !find_priv_drop_helper()) {
|
||||||
spdlog::error("could not find the {} helper next to this binary; --user/--group requires it",
|
spdlog::error("could not find the {} helper next to this binary; --user/--group requires it",
|
||||||
kPrivDropHelperName);
|
kPrivDropHelperName);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto bwrap_args = build_bwrap_args(root, command, volumes, user);
|
auto bwrap_args = build_bwrap_args(root, command, volumes, user, hostname);
|
||||||
auto argv = wrap_for_root_namespace(root, use_nsenter, bwrap_args);
|
auto argv = wrap_for_root_namespace(root, use_nsenter, bwrap_args);
|
||||||
if (!argv) {
|
if (!argv) {
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
+10
-4
@@ -55,11 +55,14 @@ struct ResolvedUser {
|
|||||||
// in volume_mount.h). If `user` is set, the command is wrapped so it drops to that
|
// in volume_mount.h). If `user` is set, the command is wrapped so it drops to that
|
||||||
// uid/gid before running -- see run_bwrap() for how, since bwrap's own --uid/--gid
|
// uid/gid before running -- see run_bwrap() for how, since bwrap's own --uid/--gid
|
||||||
// require --unshare-user, which isn't requested when running as root (see
|
// require --unshare-user, which isn't requested when running as root (see
|
||||||
// detect_bwrap_unshare_args()).
|
// detect_bwrap_unshare_args()). If `hostname` is set and the kernel supports
|
||||||
|
// --unshare-uts (bwrap refuses --hostname without it), passes it as bwrap's own
|
||||||
|
// --hostname; otherwise logs a warning and leaves the sandbox's hostname alone.
|
||||||
std::vector<std::string> build_bwrap_args(const std::string& root,
|
std::vector<std::string> build_bwrap_args(const std::string& root,
|
||||||
const std::vector<std::string>& command,
|
const std::vector<std::string>& command,
|
||||||
const std::vector<ResolvedVolumeMount>& volumes,
|
const std::vector<ResolvedVolumeMount>& volumes,
|
||||||
std::optional<ResolvedUser> user);
|
std::optional<ResolvedUser> user,
|
||||||
|
const std::optional<std::string>& hostname);
|
||||||
|
|
||||||
// Runs bwrap against `root` (the merged mount path from mount_layer()) in the
|
// Runs bwrap against `root` (the merged mount path from mount_layer()) in the
|
||||||
// foreground and waits for it to exit. If `use_nsenter` is true, first locates the
|
// foreground and waits for it to exit. If `use_nsenter` is true, first locates the
|
||||||
@@ -71,6 +74,9 @@ std::vector<std::string> build_bwrap_args(const std::string& root,
|
|||||||
// (observed on kernels older than 4.18, per fuse-overlayfs's own release notes).
|
// (observed on kernels older than 4.18, per fuse-overlayfs's own release notes).
|
||||||
// If `user` is set, this process's own binary is bind-mounted into the sandbox and
|
// If `user` is set, this process's own binary is bind-mounted into the sandbox and
|
||||||
// used to drop privileges to that uid/gid before running `command` -- see
|
// used to drop privileges to that uid/gid before running `command` -- see
|
||||||
// build_bwrap_args(). Returns bwrap's exit code, or -1 on failure to launch.
|
// build_bwrap_args(). `hostname`, if set, is forwarded to build_bwrap_args() --
|
||||||
|
// see there for when it does/doesn't take effect. Returns bwrap's exit code, or -1
|
||||||
|
// on failure to launch.
|
||||||
int run_bwrap(const std::string& root, const std::vector<std::string>& command, bool use_nsenter,
|
int run_bwrap(const std::string& root, const std::vector<std::string>& command, bool use_nsenter,
|
||||||
const std::vector<ResolvedVolumeMount>& volumes, std::optional<ResolvedUser> user);
|
const std::vector<ResolvedVolumeMount>& volumes, std::optional<ResolvedUser> user,
|
||||||
|
const std::optional<std::string>& hostname);
|
||||||
|
|||||||
+19
-7
@@ -58,18 +58,19 @@ enum class Mode {
|
|||||||
kInspect
|
kInspect
|
||||||
};
|
};
|
||||||
|
|
||||||
// --log-level/--user/--group/--list-volumes/--delete-volume[-full] have no short
|
// --log-level/--user/--group/--list-volumes/--delete-volume[-full]/--hostname have
|
||||||
// form (--log-level's was freed up so -l could become --list-images; -u is already
|
// no short form (--log-level's was freed up so -l could become --list-images; -u is
|
||||||
// --umount; the rest have no natural free letter left, or don't need one), so they
|
// already --umount; the rest have no natural free letter left, or don't need one),
|
||||||
// need long-option vals outside the printable-char range short options use.
|
// so they need long-option vals outside the printable-char range short options use.
|
||||||
constexpr int kLogLevelOpt = 256;
|
constexpr int kLogLevelOpt = 256;
|
||||||
constexpr int kUserOpt = 257;
|
constexpr int kUserOpt = 257;
|
||||||
constexpr int kGroupOpt = 258;
|
constexpr int kGroupOpt = 258;
|
||||||
constexpr int kListVolumesOpt = 259;
|
constexpr int kListVolumesOpt = 259;
|
||||||
constexpr int kDeleteVolumeOpt = 260;
|
constexpr int kDeleteVolumeOpt = 260;
|
||||||
constexpr int kDeleteVolumeFullOpt = 261;
|
constexpr int kDeleteVolumeFullOpt = 261;
|
||||||
|
constexpr int kHostnameOpt = 262;
|
||||||
|
|
||||||
constexpr std::array<struct option, 18> kLongOptions = {{
|
constexpr std::array<struct option, 19> kLongOptions = {{
|
||||||
{"help", no_argument, nullptr, 'h'},
|
{"help", no_argument, nullptr, 'h'},
|
||||||
{"version", no_argument, nullptr, 'V'},
|
{"version", no_argument, nullptr, 'V'},
|
||||||
{"test", no_argument, nullptr, 't'},
|
{"test", no_argument, nullptr, 't'},
|
||||||
@@ -87,6 +88,7 @@ constexpr std::array<struct option, 18> kLongOptions = {{
|
|||||||
{"delete-volume", required_argument, nullptr, kDeleteVolumeOpt},
|
{"delete-volume", required_argument, nullptr, kDeleteVolumeOpt},
|
||||||
{"delete-volume-full", required_argument, nullptr, kDeleteVolumeFullOpt},
|
{"delete-volume-full", required_argument, nullptr, kDeleteVolumeFullOpt},
|
||||||
{"inspect", required_argument, nullptr, 'i'},
|
{"inspect", required_argument, nullptr, 'i'},
|
||||||
|
{"hostname", required_argument, nullptr, kHostnameOpt},
|
||||||
{nullptr, 0, nullptr, 0},
|
{nullptr, 0, nullptr, 0},
|
||||||
}};
|
}};
|
||||||
|
|
||||||
@@ -131,6 +133,10 @@ void print_usage(const char* prog) {
|
|||||||
" (no user namespace involved)\n"
|
" (no user namespace involved)\n"
|
||||||
" --group <group> with --user, use this group (name or numeric\n"
|
" --group <group> with --user, use this group (name or numeric\n"
|
||||||
" gid) instead of the user's own primary group\n"
|
" gid) instead of the user's own primary group\n"
|
||||||
|
" --hostname <name> with --run, set the sandbox's hostname (only\n"
|
||||||
|
" takes effect if the running kernel supports\n"
|
||||||
|
" --unshare-uts; otherwise ignored with a\n"
|
||||||
|
" warning)\n"
|
||||||
" -l, --list-images <dir> list OCI Image Layout tars (*.tar, *.tar.*) found\n"
|
" -l, --list-images <dir> list OCI Image Layout tars (*.tar, *.tar.*) found\n"
|
||||||
" directly in <dir>, with their name:tag\n"
|
" directly in <dir>, with their name:tag\n"
|
||||||
" -i, --inspect <image.tar> print an image's declared user, exposed ports,\n"
|
" -i, --inspect <image.tar> print an image's declared user, exposed ports,\n"
|
||||||
@@ -453,6 +459,7 @@ int delete_volume_command(const std::string& name, const std::filesystem::path&
|
|||||||
int run_container(const std::filesystem::path& image_tar,
|
int run_container(const std::filesystem::path& image_tar,
|
||||||
const std::vector<std::string>& requested_command, bool use_nsenter,
|
const std::vector<std::string>& requested_command, bool use_nsenter,
|
||||||
const std::optional<std::string>& user, const std::optional<std::string>& group,
|
const std::optional<std::string>& user, const std::optional<std::string>& group,
|
||||||
|
const std::optional<std::string>& hostname,
|
||||||
const std::vector<std::pair<std::string, std::string>>& volume_specs,
|
const std::vector<std::pair<std::string, std::string>>& volume_specs,
|
||||||
const AppConfig& app_config) {
|
const AppConfig& app_config) {
|
||||||
auto mounted = mount_image(image_tar);
|
auto mounted = mount_image(image_tar);
|
||||||
@@ -517,7 +524,7 @@ int run_container(const std::filesystem::path& image_tar,
|
|||||||
|
|
||||||
int exit_code = -1;
|
int exit_code = -1;
|
||||||
if (ok) {
|
if (ok) {
|
||||||
exit_code = run_bwrap(mounted->merged_path, command, use_nsenter, volume_mounts, resolved_user);
|
exit_code = run_bwrap(mounted->merged_path, command, use_nsenter, volume_mounts, resolved_user, hostname);
|
||||||
if (exit_code < 0) {
|
if (exit_code < 0) {
|
||||||
spdlog::error("failed to run bwrap");
|
spdlog::error("failed to run bwrap");
|
||||||
}
|
}
|
||||||
@@ -550,6 +557,7 @@ int main(int argc, char* argv[]) {
|
|||||||
bool disable_nsenter = false;
|
bool disable_nsenter = false;
|
||||||
std::optional<std::string> user_flag;
|
std::optional<std::string> user_flag;
|
||||||
std::optional<std::string> group_flag;
|
std::optional<std::string> group_flag;
|
||||||
|
std::optional<std::string> hostname_flag;
|
||||||
std::vector<std::pair<std::string, std::string>> volume_specs;
|
std::vector<std::pair<std::string, std::string>> volume_specs;
|
||||||
|
|
||||||
opterr = 0;
|
opterr = 0;
|
||||||
@@ -645,6 +653,9 @@ int main(int argc, char* argv[]) {
|
|||||||
case kGroupOpt:
|
case kGroupOpt:
|
||||||
group_flag = optarg;
|
group_flag = optarg;
|
||||||
break;
|
break;
|
||||||
|
case kHostnameOpt:
|
||||||
|
hostname_flag = optarg;
|
||||||
|
break;
|
||||||
case ':':
|
case ':':
|
||||||
spdlog::error("option requires an argument: -{}", static_cast<char>(optopt));
|
spdlog::error("option requires an argument: -{}", static_cast<char>(optopt));
|
||||||
print_usage(argv[0]);
|
print_usage(argv[0]);
|
||||||
@@ -720,7 +731,8 @@ int main(int argc, char* argv[]) {
|
|||||||
if (geteuid() == 0 && !disable_nsenter) {
|
if (geteuid() == 0 && !disable_nsenter) {
|
||||||
spdlog::debug("running as root; skipping nsenter (the mount is already directly visible)");
|
spdlog::debug("running as root; skipping nsenter (the mount is already directly visible)");
|
||||||
}
|
}
|
||||||
return run_container(mode_arg, command, use_nsenter, user_flag, group_flag, volume_specs, *config);
|
return run_container(mode_arg, command, use_nsenter, user_flag, group_flag, hostname_flag, volume_specs,
|
||||||
|
*config);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto mounted = mount_image(mode_arg);
|
auto mounted = mount_image(mode_arg);
|
||||||
|
|||||||
Reference in New Issue
Block a user