[PATCH 0/8] Allow pasta to take a command to spawn instead of shell
When not attaching to an existing network namespace, pasta always spawns an interactive shell in a new namespace to attach to. Most commands which can issue a shell in a modified environment can also issue other commands as well (e.g. env, strace). We want to allow pasta to do the same. Because of the way the non-option argument to pasta is currently overloaded, allowing this requires some other changes to the way we parse the command line. David Gibson (8): conf: Make the argument to --pcap option mandatory conf: Use "-D none" and "-S none" instead of missing empty option arguments Correct manpage for --userns Remove --nsrun-dir option Move ENOENT error message into conf_ns_opt() More deterministic detection of whether argument is a PID, PATH or NAME Use explicit --netns option rather than multiplexing with PID Allow pasta to take a command to execute conf.c | 269 +++++++++++++++++++++++++++++--------------------------- passt.1 | 54 ++++++------ pasta.c | 33 ++++--- pasta.h | 2 +- pcap.c | 28 ------ 5 files changed, 191 insertions(+), 195 deletions(-) -- 2.37.2
The --pcap or -p option can be used with or without an argument. If given,
the argument gives the name of the file to save a packet trace to. If
omitted, we generate a default name in /tmp.
Generating the default name isn't particularly useful though, since making
a suitable name can easily be done by the caller. Remove this feature.
Signed-off-by: David Gibson
Both the -D (--dns) and -S (--search) options take an optional argument.
If the argument is omitted the option is disabled entirely. However,
handling the optional argument requires some ugly special case handling if
it's the last option on the command line, and has potential ambiguity with
non-option arguments used with pasta. It can also make it more confusing
to read command lines.
Simplify the logic here by replacing the non-argument versions with an
explicit "-D none" or "-S none".
Signed-off-by: David Gibson
On Fri, 26 Aug 2022 14:58:33 +1000
David Gibson
Both the -D (--dns) and -S (--search) options take an optional argument. If the argument is omitted the option is disabled entirely. However, handling the optional argument requires some ugly special case handling if it's the last option on the command line, and has potential ambiguity with non-option arguments used with pasta. It can also make it more confusing to read command lines.
Simplify the logic here by replacing the non-argument versions with an explicit "-D none" or "-S none".
Signed-off-by: David Gibson
--- conf.c | 18 ++++-------------- passt.1 | 7 ++++--- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/conf.c b/conf.c index 4eb9e3d..6770be9 100644 --- a/conf.c +++ b/conf.c @@ -1022,8 +1022,8 @@ void conf(struct ctx *c, int argc, char **argv) {"mac-addr", required_argument, NULL, 'M' }, {"gateway", required_argument, NULL, 'g' }, {"interface", required_argument, NULL, 'i' }, - {"dns", optional_argument, NULL, 'D' }, - {"search", optional_argument, NULL, 'S' }, + {"dns", required_argument, NULL, 'D' }, + {"search", required_argument, NULL, 'S' }, {"no-tcp", no_argument, &c->no_tcp, 1 }, {"no-udp", no_argument, &c->no_udp, 1 }, {"no-icmp", no_argument, &c->no_icmp, 1 }, @@ -1077,16 +1077,6 @@ void conf(struct ctx *c, int argc, char **argv)
name = getopt_long(argc, argv, optstring, options, NULL);
- if ((name == 'D' || name == 'S') && !optarg && - optind < argc && *argv[optind] && *argv[optind] != '-') { - if (c->mode == MODE_PASTA) { - if (conf_ns_opt(c, nsdir, userns, argv[optind])) - optarg = argv[optind++]; - } else { - optarg = argv[optind++]; - } - } - switch (name) { case -1: case 0: @@ -1403,7 +1393,7 @@ void conf(struct ctx *c, int argc, char **argv) usage(argv[0]); }
- if (!optarg) { + if (!strcmp(optarg, "none")) { c->no_dns = 1; break; } @@ -1430,7 +1420,7 @@ void conf(struct ctx *c, int argc, char **argv) usage(argv[0]); }
- if (!optarg) { + if (!strcmp(optarg, "none")) { c->no_dns_search = 1; break; }
For these two hunks, clang-tidy reported something I missed in my review: /home/sbrivio/passt/conf.c:1417:9: error: Null pointer passed to 1st parameter expecting 'nonnull' [clang-analyzer-core.NonNullParamChecker,-warnings-as-errors] if (!strcmp(optarg, "none")) { ^ ~~~~~~ [...] /home/sbrivio/passt/conf.c:1411:8: note: Assuming field 'no_dns' is 0 if (c->no_dns || ^~~~~~~~~ /home/sbrivio/passt/conf.c:1411:8: note: Left side of '||' is false /home/sbrivio/passt/conf.c:1412:9: note: Assuming 'optarg' is null (!optarg && (dns4 - c->ip4.dns || dns6 - c->ip6.dns))) { ^~~~~~~ /home/sbrivio/passt/conf.c:1412:9: note: Left side of '&&' is true /home/sbrivio/passt/conf.c:1412:21: note: Left side of '||' is false (!optarg && (dns4 - c->ip4.dns || dns6 - c->ip6.dns))) { ...the validation logic needs to be updated here. I'm taking the liberty of fixing this up on merge, with this diff on top: diff --git a/conf.c b/conf.c index 162c2dd..e6d1c62 100644 --- a/conf.c +++ b/conf.c @@ -1408,17 +1408,26 @@ void conf(struct ctx *c, int argc, char **argv) } break; case 'D': - if (c->no_dns || - (!optarg && (dns4 - c->ip4.dns || dns6 - c->ip6.dns))) { - err("Empty and non-empty DNS options given"); - usage(argv[0]); - } - if (!strcmp(optarg, "none")) { + if (c->no_dns) { + err("Redundant DNS options"); + usage(argv[0]); + } + + if (dns4 - c->ip4.dns || dns6 - c->ip6.dns) { + err("Conflicting DNS options"); + usage(argv[0]); + } + c->no_dns = 1; break; } + if (c->no_dns) { + err("Conflicting DNS options"); + usage(argv[0]); + } + if (dns4 - &c->ip4.dns[0] < ARRAY_SIZE(c->ip4.dns) && inet_pton(AF_INET, optarg, dns4)) { dns4++; @@ -1435,17 +1444,26 @@ void conf(struct ctx *c, int argc, char **argv) usage(argv[0]); break; case 'S': - if (c->no_dns_search || - (!optarg && dnss != c->dns_search)) { - err("Empty and non-empty DNS search given"); - usage(argv[0]); - } - if (!strcmp(optarg, "none")) { + if (c->no_dns_search) { + err("Redundant DNS search options"); + usage(argv[0]); + } + + if (dnss != c->dns_search) { + err("Conflicting DNS search options"); + usage(argv[0]); + } + c->no_dns_search = 1; break; } + if (c->no_dns_search) { + err("Conflicting DNS search options"); + usage(argv[0]); + } + if (dnss - c->dns_search < ARRAY_SIZE(c->dns_search)) { ret = snprintf(dnss->n, sizeof(*c->dns_search), "%s", optarg); -- Stefano
The man page states that the --userns option can be given either as a path
or as a name relative to --nsrun-dir. This is not correct: as the name
suggests --nsrun-dir is (correctly) used only for *netns* resolution, not
*userns* resolution.
Signed-off-by: David Gibson
pasta can identify a netns as a "name", which is to say a path relative to
(usually) /run/netns, which is the place that ip(8) creates persistent
network namespaces. Alternatively a full path to a netns can be given.
The --nsrun-dir option allows the user to change the standard path where
netns names are resolved. However, there's no real point to this, if the
user wants to override the location of the netns, they can just as easily
use the full path to specify the netns.
Signed-off-by: David Gibson
After calling conf_ns_opt() we check for -ENOENT and print an error
message, but conf_ns_opt() prints messages for other errors itself. For
consistency move the ENOENT message into conf_ns_opt() as well.
Signed-off-by: David Gibson
pasta takes as its only non-option argument either a PID to attach to the
namespaces of, a PATH to a network namespace or a NAME of a network
namespace (relative to /run/netns). Currently to determine which it is
we try all 3 in that order, and if anything goes wrong we move onto the
next.
This has the potential to cause very confusing failure modes. e.g. if the
argument is intended to be a network namespace name, but a (non-namespace)
file of the same name exists in the current directory.
Make behaviour more predictable by choosing how to treat the argument based
only on the argument's contents, not anything else on the system:
- If it's a decimal integer treat it as a PID
- Otherwise, if it has no '/' characters, treat it as a netns name
(ip-netns doesn't allow '/' in netns names)
- Otherwise, treat it as a netns path
If you want to open a persistent netns in the current directory, you can
use './netns'.
This also allows us to split the parsing of the PID|PATH|NAME option from
the actual opening of the namespaces. In turn that allows us to put the
opening of existing namespaces next to the opening of new namespaces in
pasta_start_ns. That makes the logical flow easier to follow and will
enable later cleanups.
Caveats:
- The separation of functions mean we will always generate the basename
and dirname for the netns_quit system, even when using PID namespaces.
This is pointless, since the netns_quit system doesn't work for non
persistent namespaces, but is harmless.
Signed-off-by: David Gibson
When attaching to an existing namespace, pasta can take a PID or the name
or path of a network namespace as a non-option parameter. We disambiguate
based on what the parameter looks like. Make this more explicit by using
a --netns option for explicitly giving the path or name, and treating a
non-option argument always as a PID.
Signed-off-by: David Gibson
On Fri, 26 Aug 2022 14:58:38 +1000
David Gibson
[...]
+++ b/passt.1 @@ -15,7 +15,10 @@ [\fIOPTION\fR]... .br .B pasta -[\fIOPTION\fR]... [\fIPID\fR|\fIPATH\fR|\fINAME\fR] +[\fIOPTION\fR]... [\fIPID\fR] +.br +.B pasta +[\fIOPTION\fR]... \fB--netns\fR [\fIPATH\fR|\fINAME\fR]
.SH DESCRIPTION
@@ -59,7 +62,7 @@ or with the \fBqrap\fR(1) wrapper. equivalent functionality to network namespaces, as the one offered by \fBpasst\fR for virtual machines.
-If PID, PATH or NAME are given, \fBpasta\fR associates to an existing user and +If PID or --netns are given, \fBpasta\fR associates to an existing user and network namespace. Otherwise, \fBpasta\fR creates a new user and network namespace, and spawns an interactive shell within this context. A \fItap\fR device within the network namespace is created to provide network connectivity. @@ -445,7 +448,14 @@ Default is \fBauto\fR. Target user namespace to join, as a path. If PID is given, without this option, the user namespace will be the one of the corresponding process.
-This option requires PID, PATH or NAME to be specified. +This option requires --netns or a PID to be specified. + +.TP +.BR \-\-netns " " \fIspec +Target network namespace to join, as a path or a name. A name is treated as +with \fBip-netns(8)\fR as a equivalent to a path in \fI/run/netns\fR.
an equivalent (I can fix this up on merge). -- Stefano
On Mon, Aug 29, 2022 at 09:16:50PM +0200, Stefano Brivio wrote:
On Fri, 26 Aug 2022 14:58:38 +1000 David Gibson
wrote: [...]
+++ b/passt.1 @@ -15,7 +15,10 @@ [\fIOPTION\fR]... .br .B pasta -[\fIOPTION\fR]... [\fIPID\fR|\fIPATH\fR|\fINAME\fR] +[\fIOPTION\fR]... [\fIPID\fR] +.br +.B pasta +[\fIOPTION\fR]... \fB--netns\fR [\fIPATH\fR|\fINAME\fR]
.SH DESCRIPTION
@@ -59,7 +62,7 @@ or with the \fBqrap\fR(1) wrapper. equivalent functionality to network namespaces, as the one offered by \fBpasst\fR for virtual machines.
-If PID, PATH or NAME are given, \fBpasta\fR associates to an existing user and +If PID or --netns are given, \fBpasta\fR associates to an existing user and network namespace. Otherwise, \fBpasta\fR creates a new user and network namespace, and spawns an interactive shell within this context. A \fItap\fR device within the network namespace is created to provide network connectivity. @@ -445,7 +448,14 @@ Default is \fBauto\fR. Target user namespace to join, as a path. If PID is given, without this option, the user namespace will be the one of the corresponding process.
-This option requires PID, PATH or NAME to be specified. +This option requires --netns or a PID to be specified. + +.TP +.BR \-\-netns " " \fIspec +Target network namespace to join, as a path or a name. A name is treated as +with \fBip-netns(8)\fR as a equivalent to a path in \fI/run/netns\fR.
an equivalent (I can fix this up on merge).
Oops, sorry. I think I meant to say just "as equivalent", which I think reads better than "as an equivalent". -- David Gibson | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_ | _way_ _around_! http://www.ozlabs.org/~dgibson
On Tue, 30 Aug 2022 11:12:50 +1000
David Gibson
On Mon, Aug 29, 2022 at 09:16:50PM +0200, Stefano Brivio wrote:
On Fri, 26 Aug 2022 14:58:38 +1000 David Gibson
wrote: [...]
+++ b/passt.1 @@ -15,7 +15,10 @@ [\fIOPTION\fR]... .br .B pasta -[\fIOPTION\fR]... [\fIPID\fR|\fIPATH\fR|\fINAME\fR] +[\fIOPTION\fR]... [\fIPID\fR] +.br +.B pasta +[\fIOPTION\fR]... \fB--netns\fR [\fIPATH\fR|\fINAME\fR]
.SH DESCRIPTION
@@ -59,7 +62,7 @@ or with the \fBqrap\fR(1) wrapper. equivalent functionality to network namespaces, as the one offered by \fBpasst\fR for virtual machines.
-If PID, PATH or NAME are given, \fBpasta\fR associates to an existing user and +If PID or --netns are given, \fBpasta\fR associates to an existing user and network namespace. Otherwise, \fBpasta\fR creates a new user and network namespace, and spawns an interactive shell within this context. A \fItap\fR device within the network namespace is created to provide network connectivity. @@ -445,7 +448,14 @@ Default is \fBauto\fR. Target user namespace to join, as a path. If PID is given, without this option, the user namespace will be the one of the corresponding process.
-This option requires PID, PATH or NAME to be specified. +This option requires --netns or a PID to be specified. + +.TP +.BR \-\-netns " " \fIspec +Target network namespace to join, as a path or a name. A name is treated as +with \fBip-netns(8)\fR as a equivalent to a path in \fI/run/netns\fR.
an equivalent (I can fix this up on merge).
Oops, sorry. I think I meant to say just "as equivalent", which I think reads better than "as an equivalent".
Oh, right, then I'll change it to that. -- Stefano
When not given an existing PID or network namspace to attach to, pasta
spawns a shell. Most commands which can spawn a shell in an altered
environment can also run other commands in that same environment, which can
be useful in automation.
Allow pasta to do the same thing; it can be given an arbitrary command to
run in the network and user namespace which pasta creates. If neither a
command nor an existing PID or netns to attach to is given, continue to
spawn a default shell, as before.
Signed-off-by: David Gibson
On Fri, 26 Aug 2022 14:58:39 +1000
David Gibson
When not given an existing PID or network namspace to attach to, pasta spawns a shell. Most commands which can spawn a shell in an altered environment can also run other commands in that same environment, which can be useful in automation.
Allow pasta to do the same thing; it can be given an arbitrary command to run in the network and user namespace which pasta creates. If neither a command nor an existing PID or netns to attach to is given, continue to spawn a default shell, as before.
Signed-off-by: David Gibson
--- conf.c | 27 ++++++++++++++++++--------- passt.1 | 14 +++++++++----- pasta.c | 33 +++++++++++++++++++++++---------- pasta.h | 2 +- 4 files changed, 51 insertions(+), 25 deletions(-) diff --git a/conf.c b/conf.c index 2a18124..162c2dd 100644 --- a/conf.c +++ b/conf.c @@ -550,7 +550,8 @@ static int conf_ns_pid(char *userns, char *netns, const char *arg) return 0; }
- return -EINVAL; + /* Not a PID, later code will treat as a command */ + return 0; }
/** @@ -1480,14 +1481,18 @@ void conf(struct ctx *c, int argc, char **argv)
check_root(c);
- if (c->mode == MODE_PASTA && optind + 1 == argc) { - ret = conf_ns_pid(userns, netns, argv[optind]); - if (ret < 0) + if (c->mode == MODE_PASTA) { + if (*netns && optind != argc) { + err("Both --netns and PID or command given"); usage(argv[0]); - } else if (c->mode == MODE_PASTA && *userns - && !*netns && optind == argc) { - err("--userns requires --netns or PID"); - usage(argv[0]); + } else if (optind + 1 == argc) { + ret = conf_ns_pid(userns, netns, argv[optind]); + if (ret < 0) + usage(argv[0]); + } else if (*userns && !*netns && optind == argc) { + err("--userns requires --netns or PID"); + usage(argv[0]); + } } else if (optind != argc) { usage(argv[0]); }
[...]
I haven't really looked into this yet, but I guess we should now
handle getopts return codes a bit differently, because this works:
$ ./pasta --config-net -- sh -c 'sleep 1; ip li sh'
1: lo:
On Mon, Aug 29, 2022 at 09:16:58PM +0200, Stefano Brivio wrote:
On Fri, 26 Aug 2022 14:58:39 +1000 David Gibson
wrote: When not given an existing PID or network namspace to attach to, pasta spawns a shell. Most commands which can spawn a shell in an altered environment can also run other commands in that same environment, which can be useful in automation.
Allow pasta to do the same thing; it can be given an arbitrary command to run in the network and user namespace which pasta creates. If neither a command nor an existing PID or netns to attach to is given, continue to spawn a default shell, as before.
Signed-off-by: David Gibson
--- conf.c | 27 ++++++++++++++++++--------- passt.1 | 14 +++++++++----- pasta.c | 33 +++++++++++++++++++++++---------- pasta.h | 2 +- 4 files changed, 51 insertions(+), 25 deletions(-) diff --git a/conf.c b/conf.c index 2a18124..162c2dd 100644 --- a/conf.c +++ b/conf.c @@ -550,7 +550,8 @@ static int conf_ns_pid(char *userns, char *netns, const char *arg) return 0; }
- return -EINVAL; + /* Not a PID, later code will treat as a command */ + return 0; }
/** @@ -1480,14 +1481,18 @@ void conf(struct ctx *c, int argc, char **argv)
check_root(c);
- if (c->mode == MODE_PASTA && optind + 1 == argc) { - ret = conf_ns_pid(userns, netns, argv[optind]); - if (ret < 0) + if (c->mode == MODE_PASTA) { + if (*netns && optind != argc) { + err("Both --netns and PID or command given"); usage(argv[0]); - } else if (c->mode == MODE_PASTA && *userns - && !*netns && optind == argc) { - err("--userns requires --netns or PID"); - usage(argv[0]); + } else if (optind + 1 == argc) { + ret = conf_ns_pid(userns, netns, argv[optind]); + if (ret < 0) + usage(argv[0]); + } else if (*userns && !*netns && optind == argc) { + err("--userns requires --netns or PID"); + usage(argv[0]); + } } else if (optind != argc) { usage(argv[0]); }
[...]
I haven't really looked into this yet, but I guess we should now handle getopts return codes a bit differently, because this works:
$ ./pasta --config-net -- sh -c 'sleep 1; ip li sh' 1: lo:
mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: enp9s0: mtu 65520 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 1000 link/ether aa:3e:39:5f:c6:15 brd ff:ff:ff:ff:ff:ff while this doesn't:
$ ./pasta --config-net sh -c 'sleep 1; ip li sh' ./pasta: invalid option -- 'c' [...]
despite the fact that there's no ambiguity.
You mean because pasta itself doesn't have a -c option? Attempting to account for that sounds like a bad idea. Requiring -- when the command given has options of its own that aren't supposed to go to the wrapper is pretty common for these sorts of tools. Basically the trade-off is that you either need to require that, or you have to require that all non-option arguments of the wrapper come last (old style POSIXish command line parsing, as opposed to GNUish conventions). The latter is usually more awkward than the former. -- David Gibson | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_ | _way_ _around_! http://www.ozlabs.org/~dgibson
On Tue, 30 Aug 2022 11:16:05 +1000
David Gibson
On Mon, Aug 29, 2022 at 09:16:58PM +0200, Stefano Brivio wrote:
On Fri, 26 Aug 2022 14:58:39 +1000 David Gibson
wrote: When not given an existing PID or network namspace to attach to, pasta spawns a shell. Most commands which can spawn a shell in an altered environment can also run other commands in that same environment, which can be useful in automation.
Allow pasta to do the same thing; it can be given an arbitrary command to run in the network and user namespace which pasta creates. If neither a command nor an existing PID or netns to attach to is given, continue to spawn a default shell, as before.
Signed-off-by: David Gibson
--- conf.c | 27 ++++++++++++++++++--------- passt.1 | 14 +++++++++----- pasta.c | 33 +++++++++++++++++++++++---------- pasta.h | 2 +- 4 files changed, 51 insertions(+), 25 deletions(-) diff --git a/conf.c b/conf.c index 2a18124..162c2dd 100644 --- a/conf.c +++ b/conf.c @@ -550,7 +550,8 @@ static int conf_ns_pid(char *userns, char *netns, const char *arg) return 0; }
- return -EINVAL; + /* Not a PID, later code will treat as a command */ + return 0; }
/** @@ -1480,14 +1481,18 @@ void conf(struct ctx *c, int argc, char **argv)
check_root(c);
- if (c->mode == MODE_PASTA && optind + 1 == argc) { - ret = conf_ns_pid(userns, netns, argv[optind]); - if (ret < 0) + if (c->mode == MODE_PASTA) { + if (*netns && optind != argc) { + err("Both --netns and PID or command given"); usage(argv[0]); - } else if (c->mode == MODE_PASTA && *userns - && !*netns && optind == argc) { - err("--userns requires --netns or PID"); - usage(argv[0]); + } else if (optind + 1 == argc) { + ret = conf_ns_pid(userns, netns, argv[optind]); + if (ret < 0) + usage(argv[0]); + } else if (*userns && !*netns && optind == argc) { + err("--userns requires --netns or PID"); + usage(argv[0]); + } } else if (optind != argc) { usage(argv[0]); }
[...]
I haven't really looked into this yet, but I guess we should now handle getopts return codes a bit differently, because this works:
$ ./pasta --config-net -- sh -c 'sleep 1; ip li sh' 1: lo:
mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: enp9s0: mtu 65520 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 1000 link/ether aa:3e:39:5f:c6:15 brd ff:ff:ff:ff:ff:ff while this doesn't:
$ ./pasta --config-net sh -c 'sleep 1; ip li sh' ./pasta: invalid option -- 'c' [...]
despite the fact that there's no ambiguity.
You mean because pasta itself doesn't have a -c option?
Ah, no, I meant it's after 'sh', which is a non-option argument. However,
Attempting to account for that sounds like a bad idea. Requiring -- when the command given has options of its own that aren't supposed to go to the wrapper is pretty common for these sorts of tools. Basically the trade-off is that you either need to require that, or you have to require that all non-option arguments of the wrapper come last (old style POSIXish command line parsing, as opposed to GNUish conventions). The latter is usually more awkward than the former.
...right, my assumption was exactly that, but it's probably not a good one. Let's keep it this way then. I wonder, though, if in the man page: pasta [OPTION]... [COMMAND [ARG]...] we should explicitly mention this, because from this synopsis line it looks like it's enough to put any command, with any argument, at the end. Or maybe it's already covered by typical GNUish conventions and users are used to it. -- Stefano
On Tue, 30 Aug 2022 10:26:02 +0200
Stefano Brivio
On Tue, 30 Aug 2022 11:16:05 +1000 David Gibson
wrote: On Mon, Aug 29, 2022 at 09:16:58PM +0200, Stefano Brivio wrote:
On Fri, 26 Aug 2022 14:58:39 +1000 David Gibson
wrote: When not given an existing PID or network namspace to attach to, pasta spawns a shell. Most commands which can spawn a shell in an altered environment can also run other commands in that same environment, which can be useful in automation.
Allow pasta to do the same thing; it can be given an arbitrary command to run in the network and user namespace which pasta creates. If neither a command nor an existing PID or netns to attach to is given, continue to spawn a default shell, as before.
Signed-off-by: David Gibson
--- conf.c | 27 ++++++++++++++++++--------- passt.1 | 14 +++++++++----- pasta.c | 33 +++++++++++++++++++++++---------- pasta.h | 2 +- 4 files changed, 51 insertions(+), 25 deletions(-) diff --git a/conf.c b/conf.c index 2a18124..162c2dd 100644 --- a/conf.c +++ b/conf.c @@ -550,7 +550,8 @@ static int conf_ns_pid(char *userns, char *netns, const char *arg) return 0; }
- return -EINVAL; + /* Not a PID, later code will treat as a command */ + return 0; }
/** @@ -1480,14 +1481,18 @@ void conf(struct ctx *c, int argc, char **argv)
check_root(c);
- if (c->mode == MODE_PASTA && optind + 1 == argc) { - ret = conf_ns_pid(userns, netns, argv[optind]); - if (ret < 0) + if (c->mode == MODE_PASTA) { + if (*netns && optind != argc) { + err("Both --netns and PID or command given"); usage(argv[0]); - } else if (c->mode == MODE_PASTA && *userns - && !*netns && optind == argc) { - err("--userns requires --netns or PID"); - usage(argv[0]); + } else if (optind + 1 == argc) { + ret = conf_ns_pid(userns, netns, argv[optind]); + if (ret < 0) + usage(argv[0]); + } else if (*userns && !*netns && optind == argc) { + err("--userns requires --netns or PID"); + usage(argv[0]); + } } else if (optind != argc) { usage(argv[0]); }
[...]
I haven't really looked into this yet, but I guess we should now handle getopts return codes a bit differently, because this works:
$ ./pasta --config-net -- sh -c 'sleep 1; ip li sh' 1: lo:
mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: enp9s0: mtu 65520 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 1000 link/ether aa:3e:39:5f:c6:15 brd ff:ff:ff:ff:ff:ff while this doesn't:
$ ./pasta --config-net sh -c 'sleep 1; ip li sh' ./pasta: invalid option -- 'c' [...]
despite the fact that there's no ambiguity.
You mean because pasta itself doesn't have a -c option?
Ah, no, I meant it's after 'sh', which is a non-option argument. However,
Attempting to account for that sounds like a bad idea. Requiring -- when the command given has options of its own that aren't supposed to go to the wrapper is pretty common for these sorts of tools. Basically the trade-off is that you either need to require that, or you have to require that all non-option arguments of the wrapper come last (old style POSIXish command line parsing, as opposed to GNUish conventions). The latter is usually more awkward than the former.
...right, my assumption was exactly that, but it's probably not a good one. Let's keep it this way then. I wonder, though, if in the man page:
pasta [OPTION]... [COMMAND [ARG]...]
we should explicitly mention this, because from this synopsis line it looks like it's enough to put any command, with any argument, at the end. Or maybe it's already covered by typical GNUish conventions and users are used to it.
...I couldn't find any example of an explicit mention of "--" in man pages, so I'm not sure what's the convention for this, if any. I'd leave the man page as you patched it, if somebody has a better idea or stumbles upon this, we can improve it later. -- Stefano
On Fri, 26 Aug 2022 14:58:31 +1000
David Gibson
When not attaching to an existing network namespace, pasta always spawns an interactive shell in a new namespace to attach to. Most commands which can issue a shell in a modified environment can also issue other commands as well (e.g. env, strace). We want to allow pasta to do the same.
Because of the way the non-option argument to pasta is currently overloaded, allowing this requires some other changes to the way we parse the command line.
David Gibson (8): conf: Make the argument to --pcap option mandatory conf: Use "-D none" and "-S none" instead of missing empty option arguments Correct manpage for --userns Remove --nsrun-dir option Move ENOENT error message into conf_ns_opt() More deterministic detection of whether argument is a PID, PATH or NAME Use explicit --netns option rather than multiplexing with PID Allow pasta to take a command to execute
Applied now with those small changes, thanks a lot. -- Stefano
participants (2)
-
David Gibson
-
Stefano Brivio