Skip to content

Commit

Permalink
cli: re-instate --share as the main way to share namespaces
Browse files Browse the repository at this point in the history
This is the first step towards a more consistent syntax between --share,
--persist, and the soon-to-be --unshare.

All individual --share-<ns> are now removed.
  • Loading branch information
Snaipe committed Oct 17, 2020
1 parent c728641 commit 03a46da
Show file tree
Hide file tree
Showing 6 changed files with 341 additions and 401 deletions.
139 changes: 62 additions & 77 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "enter.h"
#include "kvlist.h"
#include "util.h"
#include "path.h"

enum {
OPTION_VERSION = 128,
Expand Down Expand Up @@ -53,15 +54,7 @@ enum {
OPTION_LIMIT_STACK,
_OPTION_LIMIT_END = OPTION_LIMIT_STACK,
OPTION_LIMIT_NO_COPY,
OPTION_SHARE_CGROUP,
OPTION_SHARE_IPC,
OPTION_SHARE_MNT,
OPTION_SHARE_NET,
OPTION_SHARE_PID,
OPTION_SHARE_TIME,
OPTION_SHARE_USER,
OPTION_SHARE_UTS,
OPTION_SHARE_ALL,
OPTION_SHARE,
OPTION_ARGV0,
OPTION_HOSTNAME,
OPTION_DOMAIN,
Expand All @@ -80,42 +73,62 @@ enum {
OPTION_NO_LOOPBACK_SETUP,
OPTION_NO_INIT,
OPTION_NO_ENV,
OPTION_SHARE_DEPRECATED,
};

/* Usage is generated from usage.txt. Note that the array is not null-terminated,
I couldn't find a way to convince xxd to do that for me, so instead
I just replace the last newline with a NUL and print an extra newline
from the usage function. */
extern unsigned char usage_txt[];
extern unsigned int usage_txt_len;

static void process_share_deprecated(struct entry_settings *opts, const char *optarg)
static void process_nslist_entry(const char **out, const char *share, const char *path, int multiple)
{
for (const char *share = strtok((char *) optarg, ","); share; share = strtok(NULL, ",")) {
int found = 0;
if (!strcmp(share, "network")) {
share = "net";
}
if (!strcmp(share, "mount")) {
share = "mnt";
}
for (enum nstype ns = 0; ns < MAX_NS; ns++) {
if (!strcmp(share, ns_name(ns))) {
opts->shares[ns] = SHARE_WITH_PARENT;
found = 1;
}
}
if (!found) {
fprintf(stderr, "namespace `%s` does not exist.\n", share);
fprintf(stderr, "valid namespaces are: ");
for (enum nstype ns = 0; ns < MAX_NS; ns++) {
fprintf(stderr, "%s%s", ns == 0 ? "" : ", ", ns_name(ns));
if (!strcmp(share, "network")) {
share = "net";
}
if (!strcmp(share, "mount")) {
share = "mnt";
}
for (enum nstype ns = 0; ns < MAX_NS; ns++) {
if (!strcmp(share, ns_name(ns))) {
if (path) {
if (multiple) {
out[ns] = strdup(makepath("%s/%s", path, ns_name(ns)));
} else {
out[ns] = path;
}
} else {
out[ns] = SHARE_WITH_PARENT;
}
fprintf(stderr, ".\n");
exit(1);
return;
}
}
fprintf(stderr, "namespace `%s` does not exist.\n", share);
fprintf(stderr, "valid namespaces are: ");
for (enum nstype ns = 0; ns < MAX_NS; ns++) {
fprintf(stderr, "%s%s", ns == 0 ? "" : ", ", ns_name(ns));
}
fprintf(stderr, ".\n");
exit(2);
}

static void process_share(const char **out, const char *optarg)
{
char *nsnames = strtok((char *) optarg, "=");
char *path = strtok(NULL, "");

/* Specifying a standalone path means that all namespaces should be entered
from the nsfs files relative to that directory path. */
char all_namespaces[] = "cgroup,ipc,mnt,net,pid,time,user,uts";
if (nsnames[0] == '/' || nsnames[0] == '.') {
path = nsnames;
nsnames = all_namespaces;
}
if (strcmp(nsnames, "all") == 0) {
nsnames = all_namespaces;
}

size_t nsnames_len = strlen(nsnames);
const char *share = strtok(nsnames, ",");
bool multiple = share + strlen(share) != nsnames + nsnames_len;

for (; share; share = strtok(NULL, ",")) {
process_nslist_entry(out, share, path, multiple);
}
}

static void handle_limit_arg(int option_num, struct entry_settings *opts, char *arg)
Expand Down Expand Up @@ -160,6 +173,13 @@ static void handle_limit_arg(int option_num, struct entry_settings *opts, char *

int usage(int error, char *argv0)
{
/* Usage is generated from usage.txt. Note that the array is not null-terminated,
I couldn't find a way to convince xxd to do that for me, so instead
I just replace the last newline with a NUL and print an extra newline
from the usage function. */
extern unsigned char usage_txt[];
extern unsigned int usage_txt_len;

usage_txt[usage_txt_len - 1] = 0;
FILE *out = error ? stderr : stdout;
fprintf(out, (char *) usage_txt, argv0);
Expand Down Expand Up @@ -205,15 +225,7 @@ int main(int argc, char *argv[], char *envp[])
{ "limit-rttime", required_argument, NULL, OPTION_LIMIT_RTTIME },
{ "limit-sigpending", required_argument, NULL, OPTION_LIMIT_SIGPENDING},
{ "limit-stack", required_argument, NULL, OPTION_LIMIT_STACK },
{ "share-cgroup", optional_argument, NULL, OPTION_SHARE_CGROUP },
{ "share-ipc", optional_argument, NULL, OPTION_SHARE_IPC },
{ "share-mnt", optional_argument, NULL, OPTION_SHARE_MNT },
{ "share-net", optional_argument, NULL, OPTION_SHARE_NET },
{ "share-pid", optional_argument, NULL, OPTION_SHARE_PID },
{ "share-time", optional_argument, NULL, OPTION_SHARE_TIME },
{ "share-user", optional_argument, NULL, OPTION_SHARE_USER },
{ "share-uts", optional_argument, NULL, OPTION_SHARE_UTS },
{ "share-all", optional_argument, NULL, OPTION_SHARE_ALL },
{ "share", required_argument, NULL, OPTION_SHARE },
{ "argv0", required_argument, NULL, OPTION_ARGV0 },
{ "hostname", required_argument, NULL, OPTION_HOSTNAME },
{ "domainname", required_argument, NULL, OPTION_DOMAIN },
Expand All @@ -236,9 +248,6 @@ int main(int argc, char *argv[], char *envp[])
{ "no-init", no_argument, NULL, OPTION_NO_INIT },
{ "no-env", no_argument, NULL, OPTION_NO_ENV },

/* Deprecated flags */
{ "share", required_argument, NULL, OPTION_SHARE_DEPRECATED },

{ 0, 0, 0, 0 }
};

Expand Down Expand Up @@ -350,34 +359,10 @@ int main(int argc, char *argv[], char *envp[])
opts.no_copy_hard_limits = 1;
break;

case OPTION_SHARE_CGROUP:
case OPTION_SHARE_IPC:
case OPTION_SHARE_MNT:
case OPTION_SHARE_NET:
case OPTION_SHARE_PID:
case OPTION_SHARE_TIME:
case OPTION_SHARE_USER:
case OPTION_SHARE_UTS:
opts.shares[c - OPTION_SHARE_CGROUP] = optarg ? optarg : SHARE_WITH_PARENT;
case OPTION_SHARE:
process_share(opts.shares, optarg);
break;

case OPTION_SHARE_ALL:
for (enum nstype ns = 0; ns < MAX_NS; ns++) {
char buf[PATH_MAX];
if (optarg) {
snprintf(buf, sizeof(buf), "%s/%s", optarg, ns_name(ns));
buf[sizeof(buf) - 1] = 0;
opts.shares[ns] = strdup(buf);
} else {
opts.shares[ns] = SHARE_WITH_PARENT;
}
}
break;

case OPTION_SHARE_DEPRECATED:
process_share_deprecated(&opts, optarg);
break;

case OPTION_ARGV0:
argv0 = optarg;
break;
Expand Down
20 changes: 16 additions & 4 deletions man/bst.1.scd
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,22 @@ _VAR=value_ before the executable to run.
Run _executable_ with the specified value for _argv[0]_.
By default, _argv[0]_ is _executable_ itself.

\--share-<ns>[=<file>]
\--share <ns>[=<file>]++
\--share <ns,ns...>[=<directory>]++
\--share <directory>
Share the specified namespaces with the namespace anchored by
*<file>*, which can be a file like /proc/[pid]/ns/mnt,
or a bind-mount of that file, or a file created by
_bst --persist=_*<dir>*. If no *=<file>* is given, then share the
given namespace with the process invoking bst.

Multiple namespaces may be specified in a comma-separated list. If more
than one namespace is specified, and a target path is specified, it is
interpreted as a directory name in which to find the eponymous nsfs files.

_--share_ can also take a lone directory path, in which case all namespaces
are presumed to be shared from their respective nsfs files under that path.

Available namespaces are:
- *cgroup* (since Linux 4.6)
- *ipc* (since Linux 2.6.19)
Expand All @@ -49,14 +58,17 @@ _VAR=value_ before the executable to run.
- *uts* (since Linux 2.6.19)
- *user* (since Linux 3.8)

All namespaces are unshared by default; _e.g.,_ if _--share-mnt_
A special value of *all* can also be used to signify to share all
namespaces.

All namespaces are unshared by default; _e.g.,_ if _--share mnt_
is not given, then the child process runs in a new (unshared)
mount namespace initialized as a copy of bst's parent's mount
namespace.

\--persist <dir>
Persist all namespaces of the new process into files in the
given directory, allowing re-entry via _--share-\*=<dir>_ even after bst
given directory, allowing re-entry via _--share <dir>_ even after bst
exits (but note that pid namespaces whose init died cannot be re-entered).

The files are named the same as the namespace files in /proc/[pid]/ns.
Expand Down Expand Up @@ -172,7 +184,7 @@ _VAR=value_ before the executable to run.
all allotted sub[ug]ids for the current [UG]ID as written in
_/etc/sub[ug]id_ (see *subuid*(5), *subgid*(5)).

You cannot use this option with _--share-user_.
You cannot use this option with _--share=user_.

\--nic [name=]<name>,[type=]<type>[,[options]]
Create a network interface in the inner process' network namespace.
Expand Down
4 changes: 2 additions & 2 deletions ns.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ void opts_to_nsactions(const char *shares[], enum nsaction *nsactions)
/* If the nsfd refers to the same namespace as the one we are
currently in, treat as for SHARE_WITH_PARENT.
We want to give semantics to --share-<ns>=/proc/self/ns/<ns>
being the same as --share-<ns>. */
We want to give semantics to --share <ns>=/proc/self/ns/<ns>
being the same as --share <ns>. */
if (is_nsfd_current(nsactions[ns], ns_name(ns))) {
close(nsactions[ns]);
nsactions[ns] = NSACTION_SHARE_WITH_PARENT;
Expand Down
24 changes: 12 additions & 12 deletions test/bst.t
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,21 @@ Testing that we are in our own namespaces by default

Testing namespace sharing

$ [ "$(bst --share-all readlink /proc/self/ns/user)" = "$(readlink /proc/self/ns/user)" ]
$ [ "$(bst --share-cgroup readlink /proc/self/ns/cgroup)" = "$(readlink /proc/self/ns/cgroup)" ]
$ [ "$(bst --share-ipc readlink /proc/self/ns/ipc)" = "$(readlink /proc/self/ns/ipc)" ]
$ [ "$(bst --share-mnt readlink /proc/self/ns/mnt)" = "$(readlink /proc/self/ns/mnt)" ]
$ [ "$(bst --share-net readlink /proc/self/ns/net)" = "$(readlink /proc/self/ns/net)" ]
$ [ "$(bst --share-uts readlink /proc/self/ns/uts)" = "$(readlink /proc/self/ns/uts)" ]
$ [ "$(bst --share-pid readlink /proc/self/ns/pid)" = "$(readlink /proc/self/ns/pid)" ]
$ [ "$(bst --share-all ls -l /proc/self/ns)" = "$(ls -l /proc/self/ns)" ]
$ bst sh -c '[ "$(bst --share user readlink /proc/self/ns/user)" = "$(readlink /proc/self/ns/user)" ]'
$ [ "$(bst --share cgroup readlink /proc/self/ns/cgroup)" = "$(readlink /proc/self/ns/cgroup)" ]
$ [ "$(bst --share ipc readlink /proc/self/ns/ipc)" = "$(readlink /proc/self/ns/ipc)" ]
$ [ "$(bst --share mnt readlink /proc/self/ns/mnt)" = "$(readlink /proc/self/ns/mnt)" ]
$ [ "$(bst --share net readlink /proc/self/ns/net)" = "$(readlink /proc/self/ns/net)" ]
$ [ "$(bst --share uts readlink /proc/self/ns/uts)" = "$(readlink /proc/self/ns/uts)" ]
$ [ "$(bst --share pid readlink /proc/self/ns/pid)" = "$(readlink /proc/self/ns/pid)" ]
$ [ "$(bst --share all ls -l /proc/self/ns)" = "$(ls -l /proc/self/ns)" ]

Testing uid/gid/groups semantics

$ bst id | sed -e 's/,65534([^)]*)//'
uid=0(root) gid=0(root) groups=0(root)

$ [ "$(bst --share-all id)" = "$(id)" ]
$ [ "$(bst --share all id)" = "$(id)" ]

$ bst --workdir=/ --uid=1 --gid=2 --groups=3,4 sh -c 'id -u; id -g; id -G'
1
Expand Down Expand Up @@ -95,7 +95,7 @@ Testing exit code handling
$ bst sh -c "exit 17"
[17]

$ bst --share-pid sh -c 'kill -9 $$'
$ bst --share pid sh -c 'kill -9 $$'
[137]

Testing --argv0
Expand All @@ -114,13 +114,13 @@ Testing hostname semantics
$ bst --hostname foobar uname -n
foobar

$ bst --share-uts --hostname foobar false
$ bst --share uts --hostname foobar false
bst: attempted to set host or domain names on the host UTS namespace.
[1]

Testing persistence

$ mkdir -p foo bar; trap 'bst-unpersist foo && rmdir foo bar' EXIT; bst --persist=foo sh -c 'mount -t tmpfs none bar && echo hello > bar/greeting' && [ ! -f bar/greeting ] && bst --share-mnt=foo/mnt --share-user=foo/user sh -c '[ "$(cat '"$PWD"'/bar/greeting)" = "hello" ]'
$ mkdir -p foo bar; trap 'bst-unpersist foo && rmdir foo bar' EXIT; bst --persist=foo sh -c 'mount -t tmpfs none bar && echo hello > bar/greeting' && [ ! -f bar/greeting ] && bst --share mnt,user=foo sh -c '[ "$(cat '"$PWD"'/bar/greeting)" = "hello" ]'

Testing --limit-core / general tests
$ bst --limit-core=0 test/print_limits core
Expand Down
Loading

0 comments on commit 03a46da

Please sign in to comment.