;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2017, 2018, 2019, 2020, 2022 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2020 Mathieu Othacehe <othacehe@gnu.org>
;;; Copyright © 2022 Leo Nikkilä <hello@lnikki.la>
;;; Copyright © 2022 Arun Isaac <arunisaac@systemreboot.net>
;;;
;;; This file is part of GNU Guix.
;;;
;;; GNU Guix is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; GNU Guix is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.

(define-module (gnu build shepherd)
  #:use-module (gnu system file-systems)
  #:use-module (gnu build linux-container)
  #:use-module (guix build utils)
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-26)
  #:use-module (ice-9 match)
  ;; XXX: Lazy-bind the Shepherd to avoid a compile-time dependency.
  #:autoload (shepherd service) (fork+exec-command
                                 read-pid-file
                                 exec-command
                                 %precious-signals)
  #:autoload (shepherd system) (unblock-signals)
  #:export (default-mounts
            fork+exec-command/container))

;;; Commentary:
;;;
;;; This module provides extensions to the GNU Shepherd.  In particular, it
;;; provides a helper to start services in a container.
;;;
;;; Code:

(define (clean-up file)
  (when file
    (catch 'system-error
      (lambda ()
        (delete-file file))
      (lambda args
        (unless (= ENOENT (system-error-errno args))
          (apply throw args))))))

(define-syntax-rule (catch-system-error exp)
  (catch 'system-error
    (lambda ()
      exp)
    (const #f)))

(define (default-namespaces args)
  ;; Most daemons are here to talk to the network, and most of them expect to
  ;; run under a non-zero UID.
  (fold delq %namespaces '(net user)))

(define* (default-mounts #:key (namespaces (default-namespaces '())))
  (define (tmpfs directory)
    (file-system
      (device "none")
      (mount-point directory)
      (type "tmpfs")
      (check? #f)))

  (define accounts
    ;; This is for processes in the default user namespace but living in a
    ;; different mount namespace, so that they can lookup users.
    (list (file-system-mapping
           (source "/etc/passwd") (target source))
          (file-system-mapping
           (source "/etc/group") (target source))))

  (append (cons (tmpfs "/tmp") %container-file-systems)
          (let ((mappings `(,@(if (memq 'net namespaces)
                                  '()
                                  %network-file-mappings)
                            ,@(if (and (memq 'mnt namespaces)
                                       (not (memq 'user namespaces)))
                                  accounts
                                  '())

                            ;; Tell the process what timezone we're in.  This
                            ;; makes sure that, for instance, its syslog
                            ;; messages have the correct timestamp.
                            ,(file-system-mapping
                              (source "/etc/localtime")
                              (target source))

                            ,%store-mapping)))    ;XXX: coarse-grain
            (map file-system-mapping->bind-mount
                 (filter (lambda (mapping)
                           (file-exists? (file-system-mapping-source mapping)))
                         mappings)))))

(define* (exec-command* command #:key user group log-file pid-file
                        (supplementary-groups '())
                        (directory "/") (environment-variables (environ)))
  "Like 'exec-command', but first restore signal handles modified by
shepherd (PID 1)."
  ;; First restore the default handlers.
  (for-each (cut sigaction <> SIG_DFL) %precious-signals)

  ;; Unblock any signals that have been blocked by the parent process.
  (unblock-signals %precious-signals)

  (mkdir-p "/var/run")
  (clean-up pid-file)

  (exec-command command
                #:user user
                #:group group
                #:supplementary-groups supplementary-groups
                #:log-file log-file
                #:directory directory
                #:environment-variables environment-variables))

(define* (fork+exec-command/container command
                                      #:key pid
                                      #:allow-other-keys
                                      #:rest args)
  "This is a variant of 'fork+exec-command' procedure, that joins the
namespaces of process PID beforehand.  If there is no support for containers,
on Hurd systems for instance, fallback to direct forking."
  (define (strip-pid args)
    ;; TODO: Replace with 'strip-keyword-arguments' when that no longer pulls
    ;; in (guix config).
    (let loop ((args args)
               (result '()))
      (match args
        (()
         (reverse result))
        ((#:pid _ . rest)
         (loop rest result))
        ((head . rest)
         (loop rest (cons head result))))))

  (let ((container-support? (file-exists? "/proc/self/ns")))
    (if (and container-support?
             (not (and pid (= pid (getpid)))))
        (container-excursion* pid
          (lambda ()
            ;; Note: In the Shepherd 0.9, 'fork+exec-command' expects to be
            ;; called from the shepherd process (because it creates a pipe to
            ;; capture stdout/stderr and spawns a logging fiber) so we cannot
            ;; use it here.
            (match (primitive-fork)
              (0 (dynamic-wind
                   (const #t)
                   (lambda ()
                     (apply exec-command* command (strip-pid args)))
                   (lambda ()
                     (primitive-_exit 127))))
              (pid pid))))               ;XXX: assuming the same PID namespace
        (apply fork+exec-command command (strip-pid args)))))

;; Local Variables:
;; eval: (put 'container-excursion* 'scheme-indent-function 1)
;; End:

;;; shepherd.scm ends here
utton' href='/guix/plain/tests/combinators.scm?id=63504133252e23616742bbe308d283efd8a62d09'>plainabout -rw-r--r--containers.scm11158logplainabout -rw-r--r--cpan.scm3659logplainabout -rw-r--r--cpio.scm3154logplainabout -rw-r--r--cran.scm4994logplainabout -rw-r--r--crate.scm22188logplainabout -rw-r--r--cve-sample.json46447logplainabout -rw-r--r--cve.scm3672logplainabout -rw-r--r--debug-link.scm6709logplainabout -rw-r--r--derivations.scm62881logplainabout -rw-r--r--discovery.scm2540logplainabout -rw-r--r--dsa.key1432logplainabout -rw-r--r--ed25519.key417logplainabout -rw-r--r--ed25519.sec467logplainabout -rw-r--r--ed25519bis.key409logplainabout -rw-r--r--ed25519bis.sec459logplainabout -rw-r--r--egg.scm4429logplainabout -rw-r--r--elpa.scm2848logplainabout -rw-r--r--file-systems.scm4717logplainabout -rw-r--r--gem.scm6455logplainabout -rw-r--r--gexp.scm73497logplainabout -rw-r--r--git-authenticate.scm15597logplainabout -rw-r--r--git.scm7748logplainabout -rw-r--r--glob.scm2551logplainabout -rw-r--r--gnu-maintenance.scm2449logplainabout -rw-r--r--go.scm20078logplainabout -rw-r--r--grafts.scm25794logplainabout -rw-r--r--graph.scm23089logplainabout -rw-r--r--gremlin.scm3478logplainabout -rw-r--r--guix-archive.sh2803logplainabout -rw-r--r--guix-authenticate.sh3045logplainabout -rw-r--r--guix-build-branch.sh2222logplainabout -rw-r--r--guix-build.sh12426logplainabout -rw-r--r--guix-daemon.sh8308logplainabout -rw-r--r--guix-describe.sh1405logplainabout -rw-r--r--guix-download.sh1485logplainabout -rw-r--r--guix-environment-container.sh6944logplainabout -rw-r--r--guix-environment.sh9270logplainabout -rw-r--r--guix-gc.sh2682logplainabout -rw-r--r--guix-git-authenticate.sh2154logplainabout -rw-r--r--guix-graph.sh2918logplainabout -rw-r--r--guix-hash.sh2631logplainabout -rw-r--r--guix-home.sh3987logplainabout -rw-r--r--guix-lint.sh3213logplainabout -rw-r--r--guix-pack-localstatedir.sh2565logplainabout -rw-r--r--guix-pack-relocatable.sh9237logplainabout -rw-r--r--guix-pack.sh5061logplainabout -rw-r--r--guix-package-aliases.sh2155logplainabout -rw-r--r--guix-package-net.sh7624logplainabout -rw-r--r--guix-package.sh16490logplainabout -rw-r--r--guix-repl.sh2376logplainabout -rw-r--r--guix-shell.sh3651logplainabout -rw-r--r--guix-system.sh10602logplainabout -rw-r--r--hackage.scm11666logplainabout -rw-r--r--home-import.scm5830logplainabout -rw-r--r--import-git.scm10047logplainabout -rw-r--r--import-utils.scm8902logplainabout -rw-r--r--inferior.scm13057logplainabout -rw-r--r--ipfs.scm2021logplainabout -rw-r--r--lint.scm55807logplainabout -rw-r--r--minetest.scm19429logplainabout -rw-r--r--modules.scm2582logplainabout -rw-r--r--monads.scm9099logplainabout -rw-r--r--nar.scm22167logplainabout -rw-r--r--networking.scm4024logplainabout -rw-r--r--offload.scm2683logplainabout -rw-r--r--opam.scm7512logplainabout -rw-r--r--openpgp.scm10284logplainabout -rw-r--r--pack.scm17228logplainabout -rw-r--r--packages.scm80767logplainabout -rw-r--r--pki.scm5220logplainabout -rw-r--r--print.scm5859logplainabout -rw-r--r--processes.scm4691logplainabout -rw-r--r--profiles.scm34985logplainabout -rw-r--r--publish.scm30741logplainabout -rw-r--r--pypi.scm17703logplainabout -rw-r--r--records.scm16887logplainabout -rw-r--r--rsa.key953logplainabout -rw-r--r--scripts.scm2034logplainabout -rw-r--r--search-paths.scm1768logplainabout -rw-r--r--services.scm11536logplainabout d---------services167logplain -rw-r--r--sets.scm1507logplainabout -rw-r--r--signing-key.pub302logplainabout -rw-r--r--signing-key.sec983logplainabout -rw-r--r--size.scm4576logplainabout -rw-r--r--status.scm12235logplainabout -rw-r--r--store-database.scm5421logplainabout -rw-r--r--store-deduplication.scm6535logplainabout -rw-r--r--store-roots.scm1899logplainabout -rw-r--r--store.scm65562logplainabout -rw-r--r--substitute.scm24753logplainabout -rw-r--r--swh.scm4936logplainabout -rw-r--r--syscalls.scm22514logplainabout -rw-r--r--system.scm5151logplainabout -rw-r--r--test.drv5922logplainabout -rw-r--r--texlive.scm10144logplainabout -rw-r--r--transformations.scm21896logplainabout -rw-r--r--ui.scm8936logplainabout -rw-r--r--union.scm9714logplainabout -rw-r--r--upstream.scm1963logplainabout -rw-r--r--utils.scm12288logplainabout -rw-r--r--uuid.scm2755logplainabout -rw-r--r--workers.scm2172logplainabout