[Toybox] [PATCH] nsenter: A tool to use setns(2)

Andy Lutomirski luto at amacapital.net
Sun Oct 19 16:13:01 PDT 2014


On Sun, Oct 19, 2014 at 2:48 PM, Rob Landley <rob at landley.net> wrote:
> I imported the first one, but applied the second as a patch because new
> commands go in the "pending" directory so I don't lose track of what
> I've fully reviewed yet.
>
> On 10/17/14 22:01, Andy Lutomirski wrote:
>> nsenter: A tool to use setns(2)
>
> I don't have this command on my host system, and it's not even in the
> python "install this package if you want this command" thing.

It's in util-linux.  I bet you're using Ubuntu or Debian :)  Except
for very new Debians (IIRC), they're both quite a few years behind on
util-linux updates.

>
>> This implements all of the namespace parts of nsenter, but UID and GID
>> switching are missing, as are -r and -w (both because they're not strictly
>> necessary and because the nsenter manpage has an insufficient
>> description of how they work).
>>
>> diff -r 5330d3f88fa3 -r 5520248c82b2 toys/other/nsenter.c
>> --- /dev/null Thu Jan 01 00:00:00 1970 +0000
>> +++ b/toys/other/nsenter.c    Fri Oct 17 20:00:58 2014 -0700
>> @@ -0,0 +1,104 @@
>> +/* nsenter.c - Enter existing namespaces
>> + *
>> + * Copyright 2014 andy Lutomirski <luto at amacapital.net>
>> +
>> +USE_NSENTER(NEWTOY(nsenter, "<1F(no-fork)t#(target)i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);", TOYFLAG_USR|TOYFLAG_BIN))
>
> All the ";" characters mean you can only supply the option to a longopt
> with =, as in "--ipc=abc" but "--ipc abc" are considered two separate
> arguments (as in "--ipc= abc").
>
> (Because ls --color works that way. Because gnu, apparently.)
>
> I'm guessing this is _not_ the behavior you want? I don't even remember
> what I made the short options do in that case, I designed it to apply to
> a bare longopt that needed it.

I think this is the behavior I want.  I haven't checked exactly for
longopts, but, for the short form, the behavior seems to match
util-linux's.

>
>> +config NSENTER
>> +  bool "nsenter"
>> +  default n
>> +  help
>> +    usage: nsenter [-t pid] [-F] [-i] [-m] [-n] [-p] [-u] [-U] COMMAND...
>> +
>> +    Run COMMAND in a different set of namespaces.
>> +
>> +    -T  PID to take namespaces from
>
> Lower case above, capital here.

Whoops.  Lowercase is correct; the "-T" is wrong.

>
>> +    -F  don't fork, even if -p is set
>> +
>> +    The namespaces to switch are:
>> +
>> +    -i       SysV IPC (message queues, semaphores, shared memory)
>> +    -m       Mount/unmount tree
>> +    -n       Network address, sockets, routing, iptables
>> +    -p       Process IDs and init (will fork unless -F is used)
>> +    -u       Host and domain names
>> +    -U       UIDs, GIDs, capabilities
>> +
>> +    Each of those options takes an optional argument giving the path of
>> +    the namespace file (usually in /proc).  This optional argument is
>> +    mandatory unless -t is used.
>> +*/
>
> Lower case again. and I note that the lib/args.c option parsing
> infrastructure isn't really designed to work that way. (I can probably
> extend it, just... What command are you copying the behavior of?)
>
>> +#define FOR_nsenter
>> +#define _GNU_SOURCE
>
> I never do that. This is not a GNU project, it's a Linux project. (If
> anything it stands in opposition to gnu.)
>
> The headers have #define _ALL_SOURCE for musl, for the rest I do fixups
> in portabilty.h as necessary (and include linux/*.h headers from the
> local command, since that doesn't belong in the global headers either).
>
>> +#include "toys.h"
>> +#include <errno.h>
>> +#include <sched.h>
>> +#include <linux/sched.h>
>
> I note toys.h already includes errno.h and sched.h in the standard
> headers, and I already did the sched.h namespace dance for this stuff in
> unshare.c. I'll probably put this command in that file so they can share
> the infrastructure.
>
>> +#define NUM_NSTYPES 6
>
> We have an ARRAY_LEN() macro.
>
>> +struct nstype {
>> +  int type;
>> +  const char *name;
>> +};
>> +
>> +struct nstype nstypes[NUM_NSTYPES] = {
>> +  {CLONE_NEWUSER, "user"}, /* must be first to allow non-root operation */
>> +  {CLONE_NEWUTS,  "uts"},
>> +  {CLONE_NEWPID,  "pid"},
>> +  {CLONE_NEWNET,  "net"},
>> +  {CLONE_NEWNS,   "mnt"},
>> +  {CLONE_NEWIPC,  "ipc"},
>> +};
>
> If you leave the array's [] empty it'll automatically allocate space for
> the number of entries you provide, and then you can ARRAY_LEN to see the
> length.
>
> That said, if unhare already has an array of these symbols we can reuse
> that and then ahve the names just be a "user\0uts\0pid\0net\0mnt\ipc"
> string.
>
> Also, I try not to define global data outside the GLOBALS block. If it
> needs to be initialized, I do that in the function's main(). (There are
> times when having static global data is sufficiently cheaper to be worth
> it, but it's not my first choice. Finding a way to avoid needing it is
> my first choice.)

That should have been const, in which case I think it makes sense to
have it outside GLOBALS.  But I'll defer to your proposed tinier
version.

>
> (P.S. I can do this cleanup, just explaining conceptually how I'd
> simplify it.)
>
>> +GLOBALS(
>> +  char *nsnames[6];
>> +  long targetpid;
>> +)
>
> You have a macro above, and a hardwired 6 here. (I know the macro
> doesn't apply to globals, but if you're willing to hardwire 6 here
> having the macro at all is not a net win.)

At least it reduces the number of them. :-/

>
>> +static void enter_by_name(int idx)
>> +{
>
> This function has exactly one caller, and can be inlined at that
> callsite. Doing so often reveals opportunities for further simplification.

I wrote it this way because I might want to split the loop if I ever
implement the setuid stuff.  But, on second thought, that might not be
necessary.

>
>> +  int fd, rc;
>> +  char buf[64];
>
> We have "toybuf", a static 4k global char array, available to all
> commands. (And there's another one "libbuf" for library code.)
>
>> +  char *filename = TT.nsnames[idx];
>> +
>> +  if (!(toys.optflags & (1<<idx))) return;
>> +
>> +  if (!filename || !*filename) {
>
> Ok, I'll bite: where did TT.nsnames[] get set to anything other than
> NULL, so that filename would not be NULL here? (The GLOBALS block starts
> zeroed.)

I think that happened when I did toybox nsenter -t PID -U command.
This should be interpreted as -U with no argument followed by a
command, and filename ended up being the empty string.

--Andy

 1413760381.0


More information about the Toybox mailing list