mirror of
https://git.in.rschanz.org/ryan77627/guix.git
synced 2025-01-11 13:49:23 -05:00
pack: "-RR" produces PRoot-enabled relocatable binaries.
* gnu/packages/aux-files/run-in-namespace.c (exec_with_proot): New function. (main): When 'clone' fails, call 'rm_rf'. [PROOT_PROGRAM]: When 'clone' fails, call 'exec_with_proot'. * guix/scripts/pack.scm (wrapped-package): Add #:proot?. [proot]: New procedure. [build]: Compile with -DPROOT_PROGRAM when PROOT? is true. * guix/scripts/pack.scm (%options): Set the 'relocatable?' value to 'proot when "-R" is passed several times. (guix-pack): Pass #:proot? to 'wrapped-package'. * tests/guix-pack-relocatable.sh: Use "-RR" on Intel systems that lack user namespace support. * doc/guix.texi (Invoking guix pack): Document -RR.
This commit is contained in:
parent
c9b3a72b67
commit
99aec37a78
4 changed files with 119 additions and 21 deletions
|
@ -4760,14 +4760,24 @@ symlinks, as well as empty mount points for virtual file systems like
|
||||||
procfs.
|
procfs.
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
|
@cindex relocatable binaries
|
||||||
@item --relocatable
|
@item --relocatable
|
||||||
@itemx -R
|
@itemx -R
|
||||||
Produce @dfn{relocatable binaries}---i.e., binaries that can be placed
|
Produce @dfn{relocatable binaries}---i.e., binaries that can be placed
|
||||||
anywhere in the file system hierarchy and run from there. For example,
|
anywhere in the file system hierarchy and run from there.
|
||||||
if you create a pack containing Bash with:
|
|
||||||
|
When this option is passed once, the resulting binaries require support for
|
||||||
|
@dfn{user namespaces} in the kernel Linux; when passed
|
||||||
|
@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
|
||||||
|
Relocatable''. Neat, isn't it?}, relocatable binaries fall to back to PRoot
|
||||||
|
if user namespaces are unavailable, and essentially work anywhere---see below
|
||||||
|
for the implications.
|
||||||
|
|
||||||
|
For example, if you create a pack containing Bash with:
|
||||||
|
|
||||||
@example
|
@example
|
||||||
guix pack -R -S /mybin=bin bash
|
guix pack -RR -S /mybin=bin bash
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
@noindent
|
@noindent
|
||||||
|
@ -4786,12 +4796,23 @@ In that shell, if you type @code{ls /gnu/store}, you'll notice that
|
||||||
altogether! That is probably the simplest way to deploy Guix-built
|
altogether! That is probably the simplest way to deploy Guix-built
|
||||||
software on a non-Guix machine.
|
software on a non-Guix machine.
|
||||||
|
|
||||||
There's a gotcha though: this technique relies on the @dfn{user
|
@quotation Note
|
||||||
namespace} feature of the kernel Linux, which allows unprivileged users
|
By default, relocatable binaries rely on the @dfn{user namespace} feature of
|
||||||
to mount or change root. Old versions of Linux did not support it, and
|
the kernel Linux, which allows unprivileged users to mount or change root.
|
||||||
some GNU/Linux distributions turn it off; on these systems, programs
|
Old versions of Linux did not support it, and some GNU/Linux distributions
|
||||||
from the pack @emph{will fail to run}, unless they are unpacked in the
|
turn it off.
|
||||||
root file system.
|
|
||||||
|
To produce relocatable binaries that work even in the absence of user
|
||||||
|
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
|
||||||
|
namespaces are not supported.
|
||||||
|
|
||||||
|
The @uref{https://proot-me.github.io/, PRoot} program provides the necessary
|
||||||
|
support for file system virtualization. It achieves that by using the
|
||||||
|
@code{ptrace} system call on the running program. This approach has the
|
||||||
|
advantage to work without requiring special kernel support, but it incurs
|
||||||
|
run-time overhead every time a system call is made.
|
||||||
|
@end quotation
|
||||||
|
|
||||||
@item --expression=@var{expr}
|
@item --expression=@var{expr}
|
||||||
@itemx -e @var{expr}
|
@itemx -e @var{expr}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* GNU Guix --- Functional package management for GNU
|
/* GNU Guix --- Functional package management for GNU
|
||||||
Copyright (C) 2018 Ludovic Courtès <ludo@gnu.org>
|
Copyright (C) 2018, 2019 Ludovic Courtès <ludo@gnu.org>
|
||||||
|
|
||||||
This file is part of GNU Guix.
|
This file is part of GNU Guix.
|
||||||
|
|
||||||
|
@ -211,6 +211,46 @@ disallow_setgroups (pid_t pid)
|
||||||
close (fd);
|
close (fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef PROOT_PROGRAM
|
||||||
|
|
||||||
|
/* Execute the wrapped program with PRoot, passing it ARGC and ARGV, and
|
||||||
|
"bind-mounting" STORE in the right place. */
|
||||||
|
static void
|
||||||
|
exec_with_proot (const char *store, int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int proot_specific_argc = 4;
|
||||||
|
int proot_argc = argc + proot_specific_argc;
|
||||||
|
char *proot_argv[proot_argc], *proot;
|
||||||
|
char bind_spec[strlen (store) + 1 + sizeof "@STORE_DIRECTORY@"];
|
||||||
|
|
||||||
|
strcpy (bind_spec, store);
|
||||||
|
strcat (bind_spec, ":");
|
||||||
|
strcat (bind_spec, "@STORE_DIRECTORY@");
|
||||||
|
|
||||||
|
proot = concat (store, PROOT_PROGRAM);
|
||||||
|
|
||||||
|
proot_argv[0] = proot;
|
||||||
|
proot_argv[1] = "-b";
|
||||||
|
proot_argv[2] = bind_spec;
|
||||||
|
proot_argv[3] = "@WRAPPED_PROGRAM@";
|
||||||
|
|
||||||
|
for (int i = 0; i < argc; i++)
|
||||||
|
proot_argv[i + proot_specific_argc] = argv[i + 1];
|
||||||
|
|
||||||
|
proot_argv[proot_argc] = NULL;
|
||||||
|
|
||||||
|
/* Seccomp support seems to invariably lead to segfaults; disable it by
|
||||||
|
default. */
|
||||||
|
setenv ("PROOT_NO_SECCOMP", "1", 0);
|
||||||
|
|
||||||
|
int err = execv (proot, proot_argv);
|
||||||
|
if (err < 0)
|
||||||
|
assert_perror (errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main (int argc, char *argv[])
|
main (int argc, char *argv[])
|
||||||
|
@ -274,6 +314,10 @@ main (int argc, char *argv[])
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case -1:
|
case -1:
|
||||||
|
rm_rf (new_root);
|
||||||
|
#ifdef PROOT_PROGRAM
|
||||||
|
exec_with_proot (store, argc, argv);
|
||||||
|
#else
|
||||||
fprintf (stderr, "%s: error: 'clone' failed: %m\n", argv[0]);
|
fprintf (stderr, "%s: error: 'clone' failed: %m\n", argv[0]);
|
||||||
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\
|
||||||
|
@ -281,6 +325,7 @@ 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;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -517,10 +517,14 @@ (define (output-file args)
|
||||||
;;;
|
;;;
|
||||||
|
|
||||||
(define* (wrapped-package package
|
(define* (wrapped-package package
|
||||||
#:optional (compiler (c-compiler)))
|
#:optional (compiler (c-compiler))
|
||||||
|
#:key proot?)
|
||||||
(define runner
|
(define runner
|
||||||
(local-file (search-auxiliary-file "run-in-namespace.c")))
|
(local-file (search-auxiliary-file "run-in-namespace.c")))
|
||||||
|
|
||||||
|
(define (proot)
|
||||||
|
(specification->package "proot-static"))
|
||||||
|
|
||||||
(define build
|
(define build
|
||||||
(with-imported-modules (source-module-closure
|
(with-imported-modules (source-module-closure
|
||||||
'((guix build utils)
|
'((guix build utils)
|
||||||
|
@ -550,10 +554,19 @@ (define (build-wrapper program)
|
||||||
(("@STORE_DIRECTORY@") (%store-directory)))
|
(("@STORE_DIRECTORY@") (%store-directory)))
|
||||||
|
|
||||||
(let* ((base (strip-store-prefix program))
|
(let* ((base (strip-store-prefix program))
|
||||||
(result (string-append #$output "/" base)))
|
(result (string-append #$output "/" base))
|
||||||
|
(proot #$(and proot?
|
||||||
|
#~(string-drop
|
||||||
|
#$(file-append (proot) "/bin/proot")
|
||||||
|
(+ (string-length (%store-directory))
|
||||||
|
1)))))
|
||||||
(mkdir-p (dirname result))
|
(mkdir-p (dirname result))
|
||||||
(invoke #$compiler "-std=gnu99" "-static" "-Os" "-g0" "-Wall"
|
(apply invoke #$compiler "-std=gnu99" "-static" "-Os" "-g0" "-Wall"
|
||||||
"run.c" "-o" result)
|
"run.c" "-o" result
|
||||||
|
(if proot
|
||||||
|
(list (string-append "-DPROOT_PROGRAM=\""
|
||||||
|
proot "\""))
|
||||||
|
'()))
|
||||||
(delete-file "run.c")))
|
(delete-file "run.c")))
|
||||||
|
|
||||||
(setvbuf (current-output-port) 'line)
|
(setvbuf (current-output-port) 'line)
|
||||||
|
@ -646,7 +659,12 @@ (define %options
|
||||||
(exit 0)))
|
(exit 0)))
|
||||||
(option '(#\R "relocatable") #f #f
|
(option '(#\R "relocatable") #f #f
|
||||||
(lambda (opt name arg result)
|
(lambda (opt name arg result)
|
||||||
(alist-cons 'relocatable? #t result)))
|
(match (assq-ref result 'relocatable?)
|
||||||
|
(#f
|
||||||
|
(alist-cons 'relocatable? #t result))
|
||||||
|
(_
|
||||||
|
(alist-cons 'relocatable? 'proot
|
||||||
|
(alist-delete 'relocatable? result))))))
|
||||||
(option '(#\e "expression") #t #f
|
(option '(#\e "expression") #t #f
|
||||||
(lambda (opt name arg result)
|
(lambda (opt name arg result)
|
||||||
(alist-cons 'expression arg result)))
|
(alist-cons 'expression arg result)))
|
||||||
|
@ -821,11 +839,14 @@ (define properties
|
||||||
#:graft? (assoc-ref opts 'graft?))))
|
#:graft? (assoc-ref opts 'graft?))))
|
||||||
(let* ((dry-run? (assoc-ref opts 'dry-run?))
|
(let* ((dry-run? (assoc-ref opts 'dry-run?))
|
||||||
(relocatable? (assoc-ref opts 'relocatable?))
|
(relocatable? (assoc-ref opts 'relocatable?))
|
||||||
|
(proot? (eq? relocatable? 'proot))
|
||||||
(manifest (let ((manifest (manifest-from-args store opts)))
|
(manifest (let ((manifest (manifest-from-args store opts)))
|
||||||
;; Note: We cannot honor '--bootstrap' here because
|
;; Note: We cannot honor '--bootstrap' here because
|
||||||
;; 'glibc-bootstrap' lacks 'libc.a'.
|
;; 'glibc-bootstrap' lacks 'libc.a'.
|
||||||
(if relocatable?
|
(if relocatable?
|
||||||
(map-manifest-entries wrapped-package manifest)
|
(map-manifest-entries
|
||||||
|
(cut wrapped-package <> #:proot? proot?)
|
||||||
|
manifest)
|
||||||
manifest)))
|
manifest)))
|
||||||
(pack-format (assoc-ref opts 'format))
|
(pack-format (assoc-ref opts 'format))
|
||||||
(name (string-append (symbol->string pack-format)
|
(name (string-append (symbol->string pack-format)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# GNU Guix --- Functional package management for GNU
|
# GNU Guix --- Functional package management for GNU
|
||||||
# Copyright © 2018 Ludovic Courtès <ludo@gnu.org>
|
# Copyright © 2018, 2019 Ludovic Courtès <ludo@gnu.org>
|
||||||
#
|
#
|
||||||
# This file is part of GNU Guix.
|
# This file is part of GNU Guix.
|
||||||
#
|
#
|
||||||
|
@ -41,17 +41,28 @@ STORE_PARENT="`dirname $NIX_STORE_DIR`"
|
||||||
export STORE_PARENT
|
export STORE_PARENT
|
||||||
if test "$STORE_PARENT" = "/"; then exit 77; fi
|
if test "$STORE_PARENT" = "/"; then exit 77; fi
|
||||||
|
|
||||||
# This test requires user namespaces and associated command-line tools.
|
if unshare -mrf sh -c 'mount -t tmpfs none "$STORE_PARENT"'
|
||||||
if ! unshare -mrf sh -c 'mount -t tmpfs none "$STORE_PARENT"'
|
|
||||||
then
|
then
|
||||||
exit 77
|
# Test the wrapper that relies on user namespaces.
|
||||||
|
relocatable_option="-R"
|
||||||
|
else
|
||||||
|
case "`uname -m`" in
|
||||||
|
x86_64|i?86)
|
||||||
|
# Test the wrapper that falls back to PRoot.
|
||||||
|
relocatable_option="-RR";;
|
||||||
|
*)
|
||||||
|
# XXX: Our 'proot' package currently fails tests on non-Intel
|
||||||
|
# architectures, so skip this by default.
|
||||||
|
exit 77;;
|
||||||
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
test_directory="`mktemp -d`"
|
test_directory="`mktemp -d`"
|
||||||
export test_directory
|
export test_directory
|
||||||
trap 'chmod -Rf +w "$test_directory"; rm -rf "$test_directory"' EXIT
|
trap 'chmod -Rf +w "$test_directory"; rm -rf "$test_directory"' EXIT
|
||||||
|
|
||||||
tarball="`guix pack -R -S /Bin=bin sed`"
|
export relocatable_option
|
||||||
|
tarball="`guix pack $relocatable_option -S /Bin=bin sed`"
|
||||||
(cd "$test_directory"; tar xvf "$tarball")
|
(cd "$test_directory"; tar xvf "$tarball")
|
||||||
|
|
||||||
# Run that relocatable 'sed' in a user namespace where we "erase" the store by
|
# Run that relocatable 'sed' in a user namespace where we "erase" the store by
|
||||||
|
|
Loading…
Reference in a new issue