pack: Wrapper honors 'GUIX_EXECUTION_ENGINE' environment variable.

* gnu/packages/aux-files/run-in-namespace.c (struct engine): New type.
(exec_default): New function.
(engines): New variable.
(execution_engine): New function.
(main): Use it instead of calling 'exec_in_user_namespace' and
'exec_with_proot' directly.
* tests/guix-pack-relocatable.sh: Add test with 'GUIX_EXECUTION_ENGINE'.
* doc/guix.texi (Invoking guix pack): Document 'GUIX_EXECUTION_ENGINE'.
This commit is contained in:
Ludovic Courtès 2020-05-11 16:32:24 +02:00
parent 80963744a2
commit fde2aec3f4
No known key found for this signature in database
GPG Key ID: 090B11993D9AEBB5
3 changed files with 110 additions and 15 deletions

View File

@ -5187,9 +5187,9 @@ When this option is passed once, the resulting binaries require support for
@dfn{user namespaces} in the kernel Linux; when passed @dfn{user namespaces} in the kernel Linux; when passed
@emph{twice}@footnote{Here's a trick to memorize it: @code{-RR}, which adds @emph{twice}@footnote{Here's a trick to memorize it: @code{-RR}, which adds
PRoot support, can be thought of as the abbreviation of ``Really PRoot support, can be thought of as the abbreviation of ``Really
Relocatable''. Neat, isn't it?}, relocatable binaries fall to back to PRoot Relocatable''. Neat, isn't it?}, relocatable binaries fall to back to
if user namespaces are unavailable, and essentially work anywhere---see below other techniques if user namespaces are unavailable, and essentially
for the implications. work anywhere---see below for the implications.
For example, if you create a pack containing Bash with: For example, if you create a pack containing Bash with:
@ -5221,14 +5221,32 @@ turn it off.
To produce relocatable binaries that work even in the absence of user To produce relocatable binaries that work even in the absence of user
namespaces, pass @option{--relocatable} or @option{-R} @emph{twice}. In that namespaces, pass @option{--relocatable} or @option{-R} @emph{twice}. In that
case, binaries will try user namespace support and fall back to PRoot if user case, binaries will try user namespace support and fall back to another
namespaces are not supported. @dfn{execution engine} if user namespaces are not supported. The
following execution engines are supported:
The @uref{https://proot-me.github.io/, PRoot} program provides the necessary @table @code
@item default
Try user namespaces and fall back to PRoot if user namespaces are not
supported (see below).
@item userns
Run the program through user namespaces and abort if they are not
supported.
@item proot
Run through PRoot. The @uref{https://proot-me.github.io/, PRoot} program
provides the necessary
support for file system virtualization. It achieves that by using the support for file system virtualization. It achieves that by using the
@code{ptrace} system call on the running program. This approach has the @code{ptrace} system call on the running program. This approach has the
advantage to work without requiring special kernel support, but it incurs advantage to work without requiring special kernel support, but it incurs
run-time overhead every time a system call is made. run-time overhead every time a system call is made.
@end table
@vindex GUIX_EXECUTION_ENGINE
When running a wrapped program, you can explicitly request one of the
execution engines listed above by setting the
@code{GUIX_EXECUTION_ENGINE} environment variable accordingly.
@end quotation @end quotation
@cindex entry point, for Docker images @cindex entry point, for Docker images

View File

@ -336,6 +336,71 @@ exec_with_proot (const char *store, int argc, char *argv[])
#endif #endif
/* Execution engines. */
struct engine
{
const char *name;
void (* exec) (const char *, int, char **);
};
static void
buffer_stderr (void)
{
static char stderr_buffer[4096];
setvbuf (stderr, stderr_buffer, _IOFBF, sizeof stderr_buffer);
}
/* The default engine. */
static void
exec_default (const char *store, int argc, char *argv[])
{
/* Buffer stderr so that nothing's displayed if 'exec_in_user_namespace'
fails but 'exec_with_proot' works. */
buffer_stderr ();
exec_in_user_namespace (store, argc, argv);
#ifdef PROOT_PROGRAM
exec_with_proot (store, argc, argv);
#endif
}
/* List of supported engines. */
static const struct engine engines[] =
{
{ "default", exec_default },
{ "userns", exec_in_user_namespace },
#ifdef PROOT_PROGRAM
{ "proot", exec_with_proot },
#endif
{ NULL, NULL }
};
/* Return the "execution engine" to use. */
static const struct engine *
execution_engine (void)
{
const char *str = getenv ("GUIX_EXECUTION_ENGINE");
if (str == NULL)
str = "default";
try:
for (const struct engine *engine = engines;
engine->name != NULL;
engine++)
{
if (strcmp (engine->name, str) == 0)
return engine;
}
fprintf (stderr, "%s: unsupported Guix execution engine; ignoring\n",
str);
str = "default";
goto try;
}
int int
main (int argc, char *argv[]) main (int argc, char *argv[])
@ -362,22 +427,17 @@ main (int argc, char *argv[])
if (strcmp (store, "@STORE_DIRECTORY@") != 0 if (strcmp (store, "@STORE_DIRECTORY@") != 0
&& lstat ("@WRAPPED_PROGRAM@", &statbuf) != 0) && lstat ("@WRAPPED_PROGRAM@", &statbuf) != 0)
{ {
/* Buffer stderr so that nothing's displayed if 'exec_in_user_namespace' const struct engine *engine = execution_engine ();
fails but 'exec_with_proot' works. */ engine->exec (store, argc, argv);
static char stderr_buffer[4096];
setvbuf (stderr, stderr_buffer, _IOFBF, sizeof stderr_buffer);
exec_in_user_namespace (store, argc, argv); /* If we reach this point, that's because ENGINE failed to do the
#ifdef PROOT_PROGRAM job. */
exec_with_proot (store, argc, argv);
#else
fprintf (stderr, "\ fprintf (stderr, "\
This may be because \"user namespaces\" are not supported on this system.\n\ This may be because \"user namespaces\" are not supported on this system.\n\
Consequently, we cannot run '@WRAPPED_PROGRAM@',\n\ Consequently, we cannot run '@WRAPPED_PROGRAM@',\n\
unless you move it to the '@STORE_DIRECTORY@' directory.\n\ unless you move it to the '@STORE_DIRECTORY@' directory.\n\
\n\ \n\
Please refer to the 'guix pack' documentation for more information.\n"); Please refer to the 'guix pack' documentation for more information.\n");
#endif
return EXIT_FAILURE; return EXIT_FAILURE;
} }

View File

@ -84,6 +84,23 @@ fi
grep 'GNU sed' "$test_directory/output" grep 'GNU sed' "$test_directory/output"
chmod -Rf +w "$test_directory"; rm -rf "$test_directory"/* chmod -Rf +w "$test_directory"; rm -rf "$test_directory"/*
case "`uname -m`" in
x86_64|i?86)
# Try '-RR' and PRoot.
tarball="`guix pack -RR -S /Bin=bin sed`"
tar tvf "$tarball" | grep /bin/proot
(cd "$test_directory"; tar xvf "$tarball")
GUIX_EXECUTION_ENGINE="proot"
export GUIX_EXECUTION_ENGINE
"$test_directory/Bin/sed" --version > "$test_directory/output"
grep 'GNU sed' "$test_directory/output"
chmod -Rf +w "$test_directory"; rm -rf "$test_directory"/*
;;
*)
echo "skipping PRoot test" >&2
;;
esac
# Ensure '-R' works with outputs other than "out". # Ensure '-R' works with outputs other than "out".
tarball="`guix pack -R -S /share=share groff:doc`" tarball="`guix pack -R -S /share=share groff:doc`"
(cd "$test_directory"; tar xvf "$tarball") (cd "$test_directory"; tar xvf "$tarball")