environment: Add '--inherit'.

* guix/scripts/environment.scm (purify-environment): Add 'white-list'
parameter and honor it.
(create-environment): Add #:white-list parameter and honor it.
(launch-environment): Likewise.
(launch-environment/fork): Likewise.
(show-help, %options): Add '--inherit'.
(guix-environment): Define 'white-list' and pass it to
'launch-environment/fork'.
* tests/guix-environment.sh: Test '--inherit'.
* doc/guix.texi (Invoking guix environment): Document it.
This commit is contained in:
Ludovic Courtès 2019-02-15 08:45:57 +01:00 committed by Ludovic Courtès
parent 3a34c9e62e
commit e6e599fa01
No known key found for this signature in database
GPG Key ID: 090B11993D9AEBB5
3 changed files with 69 additions and 20 deletions

View File

@ -4454,9 +4454,24 @@ default behavior. Packages appearing after are interpreted as packages
that will be added to the environment directly. that will be added to the environment directly.
@item --pure @item --pure
Unset existing environment variables when building the new environment. Unset existing environment variables when building the new environment, except
This has the effect of creating an environment in which search paths those specified with @option{--inherit} (see below.) This has the effect of
only contain package inputs. creating an environment in which search paths only contain package inputs.
@item --inherit=@var{regexp}
When used alongside @option{--pure}, inherit all the environment variables
matching @var{regexp}---in other words, put them on a ``white list'' of
environment variables that must be preserved.
@example
guix environment --pure --inherit=^SLURM --ad-hoc openmpi @dots{} \
-- mpirun @dots{}
@end example
This example runs @command{mpirun} in a context where the only environment
variables defined are @code{PATH}, environment variables whose name starts
with @code{SLURM}, as well as the usual ``precious'' variables (@code{HOME},
@code{USER}, etc.)
@item --search-paths @item --search-paths
Display the environment variable definitions that make up the Display the environment variable definitions that make up the

View File

@ -57,20 +57,27 @@
(define %default-shell (define %default-shell
(or (getenv "SHELL") "/bin/sh")) (or (getenv "SHELL") "/bin/sh"))
(define (purify-environment) (define (purify-environment white-list)
"Unset almost all environment variables. A small number of variables such "Unset all environment variables except those that match the regexps in
as 'HOME' and 'USER' are left untouched." WHITE-LIST and those listed in %PRECIOUS-VARIABLES. A small number of
variables such as 'HOME' and 'USER' are left untouched."
(for-each unsetenv (for-each unsetenv
(remove (cut member <> %precious-variables) (remove (lambda (variable)
(or (member variable %precious-variables)
(find (cut regexp-exec <> variable)
white-list)))
(match (get-environment-variables) (match (get-environment-variables)
(((names . _) ...) (((names . _) ...)
names))))) names)))))
(define* (create-environment profile manifest #:key pure?) (define* (create-environment profile manifest
"Set the environment variables specified by MANIFEST for PROFILE. When PURE? #:key pure? (white-list '()))
is #t, unset the variables in the current environment. Otherwise, augment "Set the environment variables specified by MANIFEST for PROFILE. When
existing environment variables with additional search paths." PURE? is #t, unset the variables in the current environment except those that
(when pure? (purify-environment)) match the regexps in WHITE-LIST. Otherwise, augment existing environment
variables with additional search paths."
(when pure?
(purify-environment white-list))
(for-each (match-lambda (for-each (match-lambda
((($ <search-path-specification> variable _ separator) . value) ((($ <search-path-specification> variable _ separator) . value)
(let ((current (getenv variable))) (let ((current (getenv variable)))
@ -133,6 +140,8 @@ COMMAND or an interactive shell in that environment.\n"))
of only their inputs")) of only their inputs"))
(display (G_ " (display (G_ "
--pure unset existing environment variables")) --pure unset existing environment variables"))
(display (G_ "
--inherit=REGEXP inherit environment variables that match REGEXP"))
(display (G_ " (display (G_ "
--search-paths display needed environment variable definitions")) --search-paths display needed environment variable definitions"))
(display (G_ " (display (G_ "
@ -206,6 +215,11 @@ COMMAND or an interactive shell in that environment.\n"))
(option '("pure") #f #f (option '("pure") #f #f
(lambda (opt name arg result) (lambda (opt name arg result)
(alist-cons 'pure #t result))) (alist-cons 'pure #t result)))
(option '("inherit") #t #f
(lambda (opt name arg result)
(alist-cons 'inherit-regexp
(make-regexp* arg)
result)))
(option '(#\E "exec") #t #f ; deprecated (option '(#\E "exec") #t #f ; deprecated
(lambda (opt name arg result) (lambda (opt name arg result)
(alist-cons 'exec (list %default-shell "-c" arg) result))) (alist-cons 'exec (list %default-shell "-c" arg) result)))
@ -397,25 +411,30 @@ and suitable for 'exit'."
(define primitive-exit/status (compose primitive-exit status->exit-code)) (define primitive-exit/status (compose primitive-exit status->exit-code))
(define* (launch-environment command profile manifest (define* (launch-environment command profile manifest
#:key pure?) #:key pure? (white-list '()))
"Run COMMAND in a new environment containing INPUTS, using the native search "Run COMMAND in a new environment containing INPUTS, using the native search
paths defined by the list PATHS. When PURE?, pre-existing environment paths defined by the list PATHS. When PURE?, pre-existing environment
variables are cleared before setting the new ones." variables are cleared before setting the new ones, except those matching the
regexps in WHITE-LIST."
;; Properly handle SIGINT, so pressing C-c in an interactive terminal ;; Properly handle SIGINT, so pressing C-c in an interactive terminal
;; application works. ;; application works.
(sigaction SIGINT SIG_DFL) (sigaction SIGINT SIG_DFL)
(create-environment profile manifest #:pure? pure?) (create-environment profile manifest
#:pure? pure? #:white-list white-list)
(match command (match command
((program . args) ((program . args)
(apply execlp program program args)))) (apply execlp program program args))))
(define* (launch-environment/fork command profile manifest #:key pure?) (define* (launch-environment/fork command profile manifest
#:key pure? (white-list '()))
"Run COMMAND in a new process with an environment containing PROFILE, with "Run COMMAND in a new process with an environment containing PROFILE, with
the search paths specified by MANIFEST. When PURE?, pre-existing environment the search paths specified by MANIFEST. When PURE?, pre-existing environment
variables are cleared before setting the new ones." variables are cleared before setting the new ones, except those matching the
regexps in WHITE-LIST."
(match (primitive-fork) (match (primitive-fork)
(0 (launch-environment command profile manifest (0 (launch-environment command profile manifest
#:pure? pure?)) #:pure? pure?
#:white-list white-list))
(pid (match (waitpid pid) (pid (match (waitpid pid)
((_ . status) status))))) ((_ . status) status)))))
@ -672,7 +691,8 @@ message if any test fails."
;; within the container. ;; within the container.
'("/bin/sh") '("/bin/sh")
(list %default-shell)))) (list %default-shell))))
(mappings (pick-all opts 'file-system-mapping))) (mappings (pick-all opts 'file-system-mapping))
(white-list (pick-all opts 'inherit-regexp)))
(when container? (assert-container-features)) (when container? (assert-container-features))
@ -741,4 +761,5 @@ message if any test fails."
(return (return
(exit/status (exit/status
(launch-environment/fork command profile manifest (launch-environment/fork command profile manifest
#:white-list white-list
#:pure? pure?)))))))))))))) #:pure? pure?))))))))))))))

View File

@ -1,5 +1,5 @@
# GNU Guix --- Functional package management for GNU # GNU Guix --- Functional package management for GNU
# Copyright © 2015, 2016, 2017, 2018 Ludovic Courtès <ludo@gnu.org> # Copyright © 2015, 2016, 2017, 2018, 2019 Ludovic Courtès <ludo@gnu.org>
# #
# This file is part of GNU Guix. # This file is part of GNU Guix.
# #
@ -49,6 +49,19 @@ test -x `sed -r 's/^export PATH="(.*)"/\1/' "$tmpdir/a"`/guile
cmp "$tmpdir/a" "$tmpdir/b" cmp "$tmpdir/a" "$tmpdir/b"
# Check '--inherit'.
GUIX_TEST_ABC=1
GUIX_TEST_DEF=2
GUIX_TEST_XYZ=3
export GUIX_TEST_ABC GUIX_TEST_DEF GUIX_TEST_XYZ
guix environment --bootstrap --ad-hoc guile-bootstrap --pure \
--inherit='^GUIX_TEST_A' --inherit='^GUIX_TEST_D' \
-- "$SHELL" -c set > "$tmpdir/a"
grep '^PATH=' "$tmpdir/a"
grep '^GUIX_TEST_ABC=' "$tmpdir/a"
grep '^GUIX_TEST_DEF=' "$tmpdir/a"
if grep '^GUIX_TEST_XYZ=' "$tmpdir/a"; then false; else true; fi
# Make sure the exit value is preserved. # Make sure the exit value is preserved.
if guix environment --bootstrap --ad-hoc guile-bootstrap --pure \ if guix environment --bootstrap --ad-hoc guile-bootstrap --pure \
-- guile -c '(exit 42)' -- guile -c '(exit 42)'