guix-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [WIP Patch] Adding an FHS container to guix shell


From: Maxim Cournoyer
Subject: Re: [WIP Patch] Adding an FHS container to guix shell
Date: Fri, 15 Jul 2022 09:43:24 -0400
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/28.1 (gnu/linux)

Hi John,

John Kehayias <john.kehayias@protonmail.com> writes:

> Hi Guixers,
>
> Apologies for the long email, so let me start with the punchline:
> attached is a diff which adds an '--fhs-container' (or -F) option to
> guix shell/environment to set up an FHS-like container. This includes
> the usual /lib directory and a glibc which loads (a generated in the
> container) /etc/ld.so.cache. This should allow running most things
> that expect a more "typical" Linux environment. Give it a try!

Neat hack!

[...]

> diff --git a/gnu/packages/base.scm b/gnu/packages/base.scm
> index 4bdc3e7792..1b4c99d3e9 100644
> --- a/gnu/packages/base.scm
> +++ b/gnu/packages/base.scm
> @@ -928,6 +928,20 @@ (define-public glibc
>     (license lgpl2.0+)
>     (home-page "https://www.gnu.org/software/libc/";)))
>
> +;; Define glibc-for-fhs (with a name that allows grafts for glibc), a 
> variation
> +;; of glibc which uses the default ld.so.cache, useful in FHS containers.
> +;; Note: should this be hidden?

^ This should definitely be hidden since it's for internal consumption
only.  Also, if grafting ended up not being needed, the variable should
eb renamed 'glibc-for-fhs', an mentions of grafting in the comments
removed.

> +(define-public gcfhs
> +  (package
> +    (inherit glibc)
> +    (name "gcfhs")
> +    (source (origin (inherit (package-source glibc))
> +                    ;; Remove Guix's patch to read ld.so.cache from 
> /gnu/store
> +                    ;; directories, re-enabling the default /etc/ld.so.cache
> +                    ;; behavior.
> +                    (patches (delete (car (search-patches 
> "glibc-dl-cache.patch"))
> +                                     (origin-patches (package-source 
> glibc))))))))
> +
>  ;; Below are old libc versions, which we use mostly to build locale data in
>  ;; the old format (which the new libc cannot cope with.)
>  (define-public glibc-2.32
> diff --git a/guix/scripts/environment.scm b/guix/scripts/environment.scm
> index 3216235937..425649b843 100644
> --- a/guix/scripts/environment.scm
> +++ b/guix/scripts/environment.scm
> @@ -2,6 +2,7 @@
>  ;;; Copyright © 2014, 2015, 2018 David Thompson <davet@gnu.org>
>  ;;; Copyright © 2015-2022 Ludovic Courtès <ludo@gnu.org>
>  ;;; Copyright © 2018 Mike Gerwitz <mtg@gnu.org>
> +;;; Copyright © 2021 John Kehayias <john.kehayias@protonmail.com>
>  ;;;
>  ;;; This file is part of GNU Guix.
>  ;;;
> @@ -45,6 +46,7 @@ (define-module (guix scripts environment)
>    #:autoload   (guix build syscalls) (set-network-interface-up openpty 
> login-tty)
>    #:use-module (gnu system file-systems)
>    #:autoload   (gnu packages) (specification->package+output)
> +  #:autoload   (gnu packages base) (gcfhs)
>    #:autoload   (gnu packages bash) (bash)
>    #:autoload   (gnu packages bootstrap) (bootstrap-executable 
> %bootstrap-guile)
>    #:use-module (ice-9 match)
> @@ -101,6 +103,8 @@ (define (show-environment-options-help)
>    (display (G_ "
>    -C, --container        run command within an isolated container"))
>    (display (G_ "
> +  -F, --fhs-container    run command within an isolated FHS container"))
> +  (display (G_ "
>    -N, --network          allow containers to access the network"))
>    (display (G_ "
>    -P, --link-profile     link environment profile to ~/.guix-profile within
> @@ -229,6 +233,10 @@ (define %options
>           (option '(#\C "container") #f #f
>                   (lambda (opt name arg result)
>                     (alist-cons 'container? #t result)))
> +         (option '(#\F "fhs-container") #f #f
> +                 (lambda (opt name arg result)
> +                   (alist-cons 'fhs-container? #t
> +                               (alist-cons 'container? #t result))))
>           (option '(#\N "network") #f #f
>                   (lambda (opt name arg result)
>                     (alist-cons 'network? #t result)))
> @@ -606,9 +614,10 @@ (define* (launch-environment/fork command profile 
> manifest
>             ((_ . status)
>              (validate-exit-status profile command status))))))
>
> -(define* (launch-environment/container #:key command bash user user-mappings
> -                                       profile manifest link-profile? 
> network?
> -                                       map-cwd? (white-list '()))
> +(define* (launch-environment/container #:key command bash fhs-container? user
> +                                       user-mappings profile manifest
> +                                       link-profile? network? map-cwd?
> +                                       (white-list '()))
>    "Run COMMAND within a container that features the software in PROFILE.
>  Environment variables are set according to the search paths of MANIFEST.
>  The global shell is BASH, a file name for a GNU Bash binary in the
> @@ -709,6 +718,49 @@ (define* (launch-environment/container #:key command 
> bash user user-mappings
>              (mkdir-p home-dir)
>              (setenv "HOME" home-dir)
>
> +            ;; Set up an FHS container.

I feel like FHS should be spelled in full somewhere (in the doc would be
nice).

> +            (when fhs-container?
> +              ;; Set up the expected bin and library directories as symlinks 
> to
> +              ;; the profile lib directory.  Note that this is assuming a 
> 64bit
> +              ;; architecture.
> +              (let ((lib-dir (string-append profile "/lib")))
> +                (symlink lib-dir "/lib64")

I think /lib64 is not needed (see below).

> +                (symlink lib-dir "/lib")
> +                (mkdir-p "/usr")
> +                (symlink lib-dir "/usr/lib"))
> +              ;; Note: can't symlink full /bin in the container due to the sh
> +              ;; symlink.

Can you explain more exactly why symlinks prevent this to work?  I think
I've had issues like this before (e.g., #46782), but couldn't really
pinpoint the exact nature of the limitation.

> +              (symlink (string-append profile "/bin") "/usr/bin")
> +              (symlink (string-append profile "/sbin") "/sbin")
> +              (symlink (string-append profile "/sbin") "/usr/sbin")

Other symbolic links I think we should include:

* /include -> /usr/include (section 4.3 of the FHS 3.0 spec)
* /libexec -> /usr/libexec (optional, section 4.7)
* /share -> /usr/share (section 4.11)

> +              ;; Provide a frequently expected 'cc' symlink to gcc, though 
> this
> +              ;; could also be done by the user in the container, e.g. in
> +              ;; $HOME/.local/bin and adding that to $PATH.  Note: we do this
> +              ;; in /bin since that already has the sh symlink and can't 
> write
> +              ;; to the other bin directories that are already symlinks 
> themselves.
> +              (symlink (string-append profile "/bin/gcc") "/bin/cc")
> +              ;; TODO: python may also be expected to symlink to python3.

No need for a TODO, I think users wanting that could simply use
'python-wrapper', which exists for this purpose, no?

> +              ;; Guix's ldconfig doesn't seem to search in FHS default
> +              ;; locations, so provide a minimal ld.so.conf.
> +              ;; TODO: this may need more, e.g. libnss3 is in /lib/nss
> +              (call-with-output-file "/tmp/ld.so.conf"
> +                (lambda (port)
> +                  (display "/lib64" port)
> +                  (newline port)))

Shouldn't this be '/lib' rather than '/lib64'?  Per sections 3.9 and
3.10 of the FHS 3.0 [0], '/lib' is essential, while '/lib<qual>' is
optional.

[0] https://refspecs.linuxfoundation.org/FHS_3.0/fhs-3.0.pdf

> +              ;; Define an entry script to start the container: generate
> +              ;; ld.so.cache, supplement $PATH, and include command.
> +              (call-with-output-file "/tmp/fhs.sh"
> +                (lambda (port)
> +                  (display "ldconfig -X -f /tmp/ld.so.conf" port)
> +                  (newline port)
> +                  (display "export PATH=/bin:/usr/bin:/sbin:/usr/sbin:$PATH" 
> port)
> +                  (newline port)
> +                  (display (car command) port)
> +                  (newline port))))
> +
>              ;; If requested, link $GUIX_ENVIRONMENT to $HOME/.guix-profile;
>              ;; this allows programs expecting that path to continue working 
> as
>              ;; expected within a container.
> @@ -746,7 +798,12 @@ (define* (launch-environment/container #:key command 
> bash user user-mappings
>              (primitive-exit/status
>               ;; A container's environment is already purified, so no need to
>               ;; request it be purified again.
> -             (launch-environment command
> +             (launch-environment (if fhs-container?
> +                                     ;; Use the FHS start script.
> +                                     ;; FIXME: probably the default command 
> should
> +                                     ;; be different as it spawns a 
> different shell?
> +                                     '("/bin/sh" "/tmp/fhs.sh")

I'm not sure what could be improved here (why the FIXME?); could you
explain what you had on mind in more details?

> +                                     command)
>                                   (if link-profile?
>                                       (string-append home-dir 
> "/.guix-profile")
>                                       profile)
> @@ -874,15 +931,16 @@ (define (guix-environment* opts)
>    "Run the 'guix environment' command on OPTS, an alist resulting for
>  command-line option processing with 'parse-command-line'."
>    (with-error-handling
> -    (let* ((pure?      (assoc-ref opts 'pure))
> -           (container? (assoc-ref opts 'container?))
> -           (link-prof? (assoc-ref opts 'link-profile?))
> -           (network?   (assoc-ref opts 'network?))
> -           (no-cwd?    (assoc-ref opts 'no-cwd?))
> -           (user       (assoc-ref opts 'user))
> -           (bootstrap? (assoc-ref opts 'bootstrap?))
> -           (system     (assoc-ref opts 'system))
> -           (profile    (assoc-ref opts 'profile))
> +    (let* ((pure?          (assoc-ref opts 'pure))
> +           (container?     (assoc-ref opts 'container?))
> +           (fhs-container? (assoc-ref opts 'fhs-container?))
> +           (link-prof?     (assoc-ref opts 'link-profile?))
> +           (network?       (assoc-ref opts 'network?))
> +           (no-cwd?        (assoc-ref opts 'no-cwd?))
> +           (user           (assoc-ref opts 'user))
> +           (bootstrap?     (assoc-ref opts 'bootstrap?))
> +           (system         (assoc-ref opts 'system))
> +           (profile        (assoc-ref opts 'profile))
>             (command    (or (assoc-ref opts 'exec)
>                             ;; Spawn a shell if the user didn't specify
>                             ;; anything in particular.
> @@ -927,7 +985,16 @@ (define (guix-environment* opts)
>        (with-store/maybe store
>          (with-status-verbosity (assoc-ref opts 'verbosity)
>            (define manifest-from-opts
> -            (options/resolve-packages store opts))
> +            (options/resolve-packages store
> +                                      ;; For an FHS-container, add a glibc 
> that uses
> +                                      ;; /etc/ld.so.cache.
> +                                      (if fhs-container?
> +                                          (alist-cons 'package 
> '(ad-hoc-package "gcfhs")
> +                                                      opts)
> +                                          ;; Alternatively, could graft all 
> packages with
> +                                          ;; this glibc, though that seems 
> unnecessary.
> +                                          ;; (alist-cons 'with-graft 
> "glibc=gcfhs" opts)
> +                                          opts)))

It's interesting that it's unnecessary.  If we're confident it really
is, then I'd remove this dead code and comment.

>
>            (define manifest
>              (if profile
> @@ -994,6 +1061,7 @@ (define (guix-environment* opts)
>                                                "/bin/sh"))))
>                        (launch-environment/container #:command command
>                                                      #:bash bash-binary
> +                                                    #:fhs-container? 
> fhs-container?
>                                                      #:user user
>                                                      #:user-mappings mappings
>                                                      #:profile profile

That's surprisingly little code, which is nice!  Thanks for looking into
it :-).

I've yet to try it, but I will shortly.

Thank you,

Maxim



reply via email to

[Prev in Thread] Current Thread [Next in Thread]