* gnu/services/base.scm (<guix-configuration>)[max-silent-time] [timeout]: New fields. (guix-shepherd-service): Honor them. * doc/guix.texi (Base Services): Document them.
		
			
				
	
	
		
			1874 lines
		
	
	
	
		
			76 KiB
		
	
	
	
		
			Scheme
		
	
	
	
	
	
			
		
		
	
	
			1874 lines
		
	
	
	
		
			76 KiB
		
	
	
	
		
			Scheme
		
	
	
	
	
	
| ;;; GNU Guix --- Functional package management for GNU
 | ||
| ;;; Copyright © 2013, 2014, 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org>
 | ||
| ;;; Copyright © 2015, 2016 Alex Kost <alezost@gmail.com>
 | ||
| ;;; Copyright © 2015, 2016 Mark H Weaver <mhw@netris.org>
 | ||
| ;;; Copyright © 2015 Sou Bunnbu <iyzsong@gmail.com>
 | ||
| ;;; Copyright © 2016, 2017 Leo Famulari <leo@famulari.name>
 | ||
| ;;; Copyright © 2016 David Craven <david@craven.ch>
 | ||
| ;;; Copyright © 2016 Ricardo Wurmus <rekado@elephly.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 services base)
 | ||
|   #:use-module (guix store)
 | ||
|   #:use-module (gnu services)
 | ||
|   #:use-module (gnu services shepherd)
 | ||
|   #:use-module (gnu services networking)
 | ||
|   #:use-module (gnu system pam)
 | ||
|   #:use-module (gnu system shadow)                ; 'user-account', etc.
 | ||
|   #:use-module (gnu system file-systems)          ; 'file-system', etc.
 | ||
|   #:use-module (gnu system mapped-devices)
 | ||
|   #:use-module ((gnu system linux-initrd)
 | ||
|                 #:select (file-system-packages))
 | ||
|   #:use-module (gnu packages admin)
 | ||
|   #:use-module ((gnu packages linux)
 | ||
|                 #:select (alsa-utils crda eudev e2fsprogs fuse gpm kbd lvm2 rng-tools))
 | ||
|   #:use-module ((gnu packages base)
 | ||
|                 #:select (canonical-package glibc))
 | ||
|   #:use-module (gnu packages bash)
 | ||
|   #:use-module (gnu packages package-management)
 | ||
|   #:use-module (gnu packages linux)
 | ||
|   #:use-module (gnu packages lsof)
 | ||
|   #:use-module (gnu packages terminals)
 | ||
|   #:use-module ((gnu build file-systems)
 | ||
|                 #:select (mount-flags->bit-mask))
 | ||
|   #:use-module (guix gexp)
 | ||
|   #:use-module (guix records)
 | ||
|   #:use-module (srfi srfi-1)
 | ||
|   #:use-module (srfi srfi-26)
 | ||
|   #:use-module (ice-9 match)
 | ||
|   #:use-module (ice-9 format)
 | ||
|   #:export (fstab-service-type
 | ||
|             root-file-system-service
 | ||
|             file-system-service-type
 | ||
|             user-unmount-service
 | ||
|             swap-service
 | ||
|             user-processes-service
 | ||
|             session-environment-service
 | ||
|             session-environment-service-type
 | ||
|             host-name-service
 | ||
|             console-keymap-service
 | ||
|             %default-console-font
 | ||
|             console-font-service-type
 | ||
|             console-font-service
 | ||
| 
 | ||
|             udev-configuration
 | ||
|             udev-configuration?
 | ||
|             udev-configuration-rules
 | ||
|             udev-service-type
 | ||
|             udev-service
 | ||
|             udev-rule
 | ||
| 
 | ||
|             login-configuration
 | ||
|             login-configuration?
 | ||
|             login-service-type
 | ||
|             login-service
 | ||
| 
 | ||
|             agetty-configuration
 | ||
|             agetty-configuration?
 | ||
|             agetty-service
 | ||
|             agetty-service-type
 | ||
| 
 | ||
|             mingetty-configuration
 | ||
|             mingetty-configuration?
 | ||
|             mingetty-service
 | ||
|             mingetty-service-type
 | ||
| 
 | ||
|             %nscd-default-caches
 | ||
|             %nscd-default-configuration
 | ||
| 
 | ||
|             nscd-configuration
 | ||
|             nscd-configuration?
 | ||
| 
 | ||
|             nscd-cache
 | ||
|             nscd-cache?
 | ||
| 
 | ||
|             nscd-service-type
 | ||
|             nscd-service
 | ||
| 
 | ||
|             syslog-configuration
 | ||
|             syslog-configuration?
 | ||
|             syslog-service
 | ||
|             syslog-service-type
 | ||
|             %default-syslog.conf
 | ||
| 
 | ||
|             %default-authorized-guix-keys
 | ||
|             guix-configuration
 | ||
|             guix-configuration?
 | ||
| 
 | ||
|             guix-configuration-guix
 | ||
|             guix-configuration-build-group
 | ||
|             guix-configuration-build-accounts
 | ||
|             guix-configuration-authorize-key?
 | ||
|             guix-configuration-authorized-keys
 | ||
|             guix-configuration-use-substitutes?
 | ||
|             guix-configuration-substitute-urls
 | ||
|             guix-configuration-extra-options
 | ||
|             guix-configuration-log-file
 | ||
|             guix-configuration-lsof
 | ||
| 
 | ||
|             guix-service
 | ||
|             guix-service-type
 | ||
|             guix-publish-configuration
 | ||
|             guix-publish-configuration?
 | ||
|             guix-publish-configuration-guix
 | ||
|             guix-publish-configuration-port
 | ||
|             guix-publish-configuration-host
 | ||
|             guix-publish-configuration-compression-level
 | ||
|             guix-publish-configuration-nar-path
 | ||
|             guix-publish-configuration-cache
 | ||
|             guix-publish-configuration-ttl
 | ||
|             guix-publish-service
 | ||
|             guix-publish-service-type
 | ||
| 
 | ||
|             gpm-configuration
 | ||
|             gpm-configuration?
 | ||
|             gpm-service-type
 | ||
|             gpm-service
 | ||
| 
 | ||
|             urandom-seed-service-type
 | ||
|             urandom-seed-service
 | ||
| 
 | ||
|             rngd-configuration
 | ||
|             rngd-configuration?
 | ||
|             rngd-service-type
 | ||
|             rngd-service
 | ||
| 
 | ||
|             kmscon-configuration
 | ||
|             kmscon-configuration?
 | ||
|             kmscon-service-type
 | ||
| 
 | ||
|             pam-limits-service-type
 | ||
|             pam-limits-service
 | ||
| 
 | ||
|             %base-services))
 | ||
| 
 | ||
| ;;; Commentary:
 | ||
| ;;;
 | ||
| ;;; Base system services---i.e., services that 99% of the users will want to
 | ||
| ;;; use.
 | ||
| ;;;
 | ||
| ;;; Code:
 | ||
| 
 | ||
| 
 | ||
| ;;;
 | ||
| ;;; File systems.
 | ||
| ;;;
 | ||
| 
 | ||
| (define (file-system->fstab-entry file-system)
 | ||
|   "Return a @file{/etc/fstab} entry for @var{file-system}."
 | ||
|   (string-append (case (file-system-title file-system)
 | ||
|                    ((label)
 | ||
|                     (string-append "LABEL=" (file-system-device file-system)))
 | ||
|                    ((uuid)
 | ||
|                     (string-append
 | ||
|                      "UUID="
 | ||
|                      (uuid->string (file-system-device file-system))))
 | ||
|                    (else
 | ||
|                     (file-system-device file-system)))
 | ||
|                  "\t"
 | ||
|                  (file-system-mount-point file-system) "\t"
 | ||
|                  (file-system-type file-system) "\t"
 | ||
|                  (or (file-system-options file-system) "defaults") "\t"
 | ||
| 
 | ||
|                  ;; XXX: Omit the 'fs_freq' and 'fs_passno' fields because we
 | ||
|                  ;; don't have anything sensible to put in there.
 | ||
|                  ))
 | ||
| 
 | ||
| (define (file-systems->fstab file-systems)
 | ||
|   "Return a @file{/etc} entry for an @file{fstab} describing
 | ||
| @var{file-systems}."
 | ||
|   `(("fstab" ,(plain-file "fstab"
 | ||
|                           (string-append
 | ||
|                            "\
 | ||
| # This file was generated from your GuixSD configuration.  Any changes
 | ||
| # will be lost upon reboot or reconfiguration.\n\n"
 | ||
|                            (string-join (map file-system->fstab-entry
 | ||
|                                              file-systems)
 | ||
|                                         "\n")
 | ||
|                            "\n")))))
 | ||
| 
 | ||
| (define fstab-service-type
 | ||
|   ;; The /etc/fstab service.
 | ||
|   (service-type (name 'fstab)
 | ||
|                 (extensions
 | ||
|                  (list (service-extension etc-service-type
 | ||
|                                           file-systems->fstab)))
 | ||
|                 (compose concatenate)
 | ||
|                 (extend append)))
 | ||
| 
 | ||
| (define %root-file-system-shepherd-service
 | ||
|   (shepherd-service
 | ||
|    (documentation "Take care of the root file system.")
 | ||
|    (provision '(root-file-system))
 | ||
|    (start #~(const #t))
 | ||
|    (stop #~(lambda _
 | ||
|              ;; Return #f if successfully stopped.
 | ||
|              (sync)
 | ||
| 
 | ||
|              (call-with-blocked-asyncs
 | ||
|               (lambda ()
 | ||
|                 (let ((null (%make-void-port "w")))
 | ||
|                   ;; Close 'shepherd.log'.
 | ||
|                   (display "closing log\n")
 | ||
|                   ((@ (shepherd comm) stop-logging))
 | ||
| 
 | ||
|                   ;; Redirect the default output ports..
 | ||
|                   (set-current-output-port null)
 | ||
|                   (set-current-error-port null)
 | ||
| 
 | ||
|                   ;; Close /dev/console.
 | ||
|                   (for-each close-fdes '(0 1 2))
 | ||
| 
 | ||
|                   ;; At this point, there are no open files left, so the
 | ||
|                   ;; root file system can be re-mounted read-only.
 | ||
|                   (mount #f "/" #f
 | ||
|                          (logior MS_REMOUNT MS_RDONLY)
 | ||
|                          #:update-mtab? #f)
 | ||
| 
 | ||
|                   #f)))))
 | ||
|    (respawn? #f)))
 | ||
| 
 | ||
| (define root-file-system-service-type
 | ||
|   (shepherd-service-type 'root-file-system
 | ||
|                          (const %root-file-system-shepherd-service)))
 | ||
| 
 | ||
| (define (root-file-system-service)
 | ||
|   "Return a service whose sole purpose is to re-mount read-only the root file
 | ||
| system upon shutdown (aka. cleanly \"umounting\" root.)
 | ||
| 
 | ||
| This service must be the root of the service dependency graph so that its
 | ||
| 'stop' action is invoked when shepherd is the only process left."
 | ||
|   (service root-file-system-service-type #f))
 | ||
| 
 | ||
| (define (file-system->shepherd-service-name file-system)
 | ||
|   "Return the symbol that denotes the service mounting and unmounting
 | ||
| FILE-SYSTEM."
 | ||
|   (symbol-append 'file-system-
 | ||
|                  (string->symbol (file-system-mount-point file-system))))
 | ||
| 
 | ||
| (define (mapped-device->shepherd-service-name md)
 | ||
|   "Return the symbol that denotes the shepherd service of MD, a <mapped-device>."
 | ||
|   (symbol-append 'device-mapping-
 | ||
|                  (string->symbol (mapped-device-target md))))
 | ||
| 
 | ||
| (define dependency->shepherd-service-name
 | ||
|   (match-lambda
 | ||
|     ((? mapped-device? md)
 | ||
|      (mapped-device->shepherd-service-name md))
 | ||
|     ((? file-system? fs)
 | ||
|      (file-system->shepherd-service-name fs))))
 | ||
| 
 | ||
| (define (file-system-shepherd-service file-system)
 | ||
|   "Return the shepherd service for @var{file-system}, or @code{#f} if
 | ||
| @var{file-system} is not auto-mounted upon boot."
 | ||
|   (let ((target  (file-system-mount-point file-system))
 | ||
|         (device  (file-system-device file-system))
 | ||
|         (type    (file-system-type file-system))
 | ||
|         (title   (file-system-title file-system))
 | ||
|         (flags   (file-system-flags file-system))
 | ||
|         (options (file-system-options file-system))
 | ||
|         (check?  (file-system-check? file-system))
 | ||
|         (create? (file-system-create-mount-point? file-system))
 | ||
|         (dependencies (file-system-dependencies file-system))
 | ||
|         (packages (file-system-packages (list file-system))))
 | ||
|     (and (file-system-mount? file-system)
 | ||
|          (with-imported-modules '((gnu build file-systems)
 | ||
|                                   (guix build bournish))
 | ||
|            (shepherd-service
 | ||
|             (provision (list (file-system->shepherd-service-name file-system)))
 | ||
|             (requirement `(root-file-system
 | ||
|                            ,@(map dependency->shepherd-service-name dependencies)))
 | ||
|             (documentation "Check, mount, and unmount the given file system.")
 | ||
|             (start #~(lambda args
 | ||
|                        #$(if create?
 | ||
|                              #~(mkdir-p #$target)
 | ||
|                              #t)
 | ||
| 
 | ||
|                        (let (($PATH (getenv "PATH")))
 | ||
|                          ;; Make sure fsck.ext2 & co. can be found.
 | ||
|                          (dynamic-wind
 | ||
|                            (lambda ()
 | ||
|                              ;; Don’t display the PATH settings.
 | ||
|                              (with-output-to-port (%make-void-port "w")
 | ||
|                                (lambda ()
 | ||
|                                  (set-path-environment-variable "PATH"
 | ||
|                                                                 '("bin" "sbin")
 | ||
|                                                                 '#$packages))))
 | ||
|                            (lambda ()
 | ||
|                              (mount-file-system
 | ||
|                               `(#$device #$title #$target #$type #$flags
 | ||
|                                          #$options #$check?)
 | ||
|                               #:root "/"))
 | ||
|                            (lambda ()
 | ||
|                              (setenv "PATH" $PATH)))
 | ||
|                          #t)))
 | ||
|             (stop #~(lambda args
 | ||
|                       ;; Normally there are no processes left at this point, so
 | ||
|                       ;; TARGET can be safely unmounted.
 | ||
| 
 | ||
|                       ;; Make sure PID 1 doesn't keep TARGET busy.
 | ||
|                       (chdir "/")
 | ||
| 
 | ||
|                       (umount #$target)
 | ||
|                       #f))
 | ||
| 
 | ||
|             ;; We need an additional module.
 | ||
|             (modules `(((gnu build file-systems)
 | ||
|                         #:select (mount-file-system))
 | ||
|                        ,@%default-modules)))))))
 | ||
| 
 | ||
| (define (file-system-shepherd-services file-systems)
 | ||
|   "Return the list of Shepherd services for FILE-SYSTEMS."
 | ||
|   (let* ((file-systems (filter file-system-mount? file-systems)))
 | ||
|     (define sink
 | ||
|       (shepherd-service
 | ||
|        (provision '(file-systems))
 | ||
|        (requirement (cons* 'root-file-system 'user-file-systems
 | ||
|                            (map file-system->shepherd-service-name
 | ||
|                                 file-systems)))
 | ||
|        (documentation "Target for all the initially-mounted file systems")
 | ||
|        (start #~(const #t))
 | ||
|        (stop #~(const #f))))
 | ||
| 
 | ||
|     (cons sink (map file-system-shepherd-service file-systems))))
 | ||
| 
 | ||
| (define file-system-service-type
 | ||
|   (service-type (name 'file-systems)
 | ||
|                 (extensions
 | ||
|                  (list (service-extension shepherd-root-service-type
 | ||
|                                           file-system-shepherd-services)
 | ||
|                        (service-extension fstab-service-type
 | ||
|                                           identity)))
 | ||
|                 (compose concatenate)
 | ||
|                 (extend append)))
 | ||
| 
 | ||
| (define user-unmount-service-type
 | ||
|   (shepherd-service-type
 | ||
|    'user-file-systems
 | ||
|    (lambda (known-mount-points)
 | ||
|      (shepherd-service
 | ||
|       (documentation "Unmount manually-mounted file systems.")
 | ||
|       (provision '(user-file-systems))
 | ||
|       (start #~(const #t))
 | ||
|       (stop #~(lambda args
 | ||
|                 (define (known? mount-point)
 | ||
|                   (member mount-point
 | ||
|                           (cons* "/proc" "/sys" '#$known-mount-points)))
 | ||
| 
 | ||
|                 ;; Make sure we don't keep the user's mount points busy.
 | ||
|                 (chdir "/")
 | ||
| 
 | ||
|                 (for-each (lambda (mount-point)
 | ||
|                             (format #t "unmounting '~a'...~%" mount-point)
 | ||
|                             (catch 'system-error
 | ||
|                               (lambda ()
 | ||
|                                 (umount mount-point))
 | ||
|                               (lambda args
 | ||
|                                 (let ((errno (system-error-errno args)))
 | ||
|                                   (format #t "failed to unmount '~a': ~a~%"
 | ||
|                                           mount-point (strerror errno))))))
 | ||
|                           (filter (negate known?) (mount-points)))
 | ||
|                 #f))))))
 | ||
| 
 | ||
| (define (user-unmount-service known-mount-points)
 | ||
|   "Return a service whose sole purpose is to unmount file systems not listed
 | ||
| in KNOWN-MOUNT-POINTS when it is stopped."
 | ||
|   (service user-unmount-service-type known-mount-points))
 | ||
| 
 | ||
| (define %do-not-kill-file
 | ||
|   ;; Name of the file listing PIDs of processes that must survive when halting
 | ||
|   ;; the system.  Typical example is user-space file systems.
 | ||
|   "/etc/shepherd/do-not-kill")
 | ||
| 
 | ||
| (define user-processes-service-type
 | ||
|   (shepherd-service-type
 | ||
|    'user-processes
 | ||
|    (lambda (grace-delay)
 | ||
|      (shepherd-service
 | ||
|       (documentation "When stopped, terminate all user processes.")
 | ||
|       (provision '(user-processes))
 | ||
|       (requirement '(file-systems))
 | ||
|       (start #~(const #t))
 | ||
|       (stop #~(lambda _
 | ||
|                 (define (kill-except omit signal)
 | ||
|                   ;; Kill all the processes with SIGNAL except those listed
 | ||
|                   ;; in OMIT and the current process.
 | ||
|                   (let ((omit (cons (getpid) omit)))
 | ||
|                     (for-each (lambda (pid)
 | ||
|                                 (unless (memv pid omit)
 | ||
|                                   (false-if-exception
 | ||
|                                    (kill pid signal))))
 | ||
|                               (processes))))
 | ||
| 
 | ||
|                 (define omitted-pids
 | ||
|                   ;; List of PIDs that must not be killed.
 | ||
|                   (if (file-exists? #$%do-not-kill-file)
 | ||
|                       (map string->number
 | ||
|                            (call-with-input-file #$%do-not-kill-file
 | ||
|                              (compose string-tokenize
 | ||
|                                       (@ (ice-9 rdelim) read-string))))
 | ||
|                       '()))
 | ||
| 
 | ||
|                 (define (now)
 | ||
|                   (car (gettimeofday)))
 | ||
| 
 | ||
|                 (define (sleep* n)
 | ||
|                   ;; Really sleep N seconds.
 | ||
|                   ;; Work around <http://bugs.gnu.org/19581>.
 | ||
|                   (define start (now))
 | ||
|                   (let loop ((elapsed 0))
 | ||
|                     (when (> n elapsed)
 | ||
|                       (sleep (- n elapsed))
 | ||
|                       (loop (- (now) start)))))
 | ||
| 
 | ||
|                 (define lset= (@ (srfi srfi-1) lset=))
 | ||
| 
 | ||
|                 (display "sending all processes the TERM signal\n")
 | ||
| 
 | ||
|                 (if (null? omitted-pids)
 | ||
|                     (begin
 | ||
|                       ;; Easy: terminate all of them.
 | ||
|                       (kill -1 SIGTERM)
 | ||
|                       (sleep* #$grace-delay)
 | ||
|                       (kill -1 SIGKILL))
 | ||
|                     (begin
 | ||
|                       ;; Kill them all except OMITTED-PIDS.  XXX: We would
 | ||
|                       ;; like to (kill -1 SIGSTOP) to get a fixed list of
 | ||
|                       ;; processes, like 'killall5' does, but that seems
 | ||
|                       ;; unreliable.
 | ||
|                       (kill-except omitted-pids SIGTERM)
 | ||
|                       (sleep* #$grace-delay)
 | ||
|                       (kill-except omitted-pids SIGKILL)
 | ||
|                       (delete-file #$%do-not-kill-file)))
 | ||
| 
 | ||
|                 (let wait ()
 | ||
|                   (let ((pids (processes)))
 | ||
|                     (unless (lset= = pids (cons 1 omitted-pids))
 | ||
|                       (format #t "waiting for process termination\
 | ||
|  (processes left: ~s)~%"
 | ||
|                               pids)
 | ||
|                       (sleep* 2)
 | ||
|                       (wait))))
 | ||
| 
 | ||
|                 (display "all processes have been terminated\n")
 | ||
|                 #f))
 | ||
|       (respawn? #f)))))
 | ||
| 
 | ||
| (define* (user-processes-service #:key (grace-delay 4))
 | ||
|   "Return the service that is responsible for terminating all the processes so
 | ||
| that the root file system can be re-mounted read-only, just before
 | ||
| rebooting/halting.  Processes still running GRACE-DELAY seconds after SIGTERM
 | ||
| has been sent are terminated with SIGKILL.
 | ||
| 
 | ||
| The returned service will depend on 'file-systems', meaning that it is
 | ||
| considered started after all the auto-mount file systems have been mounted.
 | ||
| 
 | ||
| All the services that spawn processes must depend on this one so that they are
 | ||
| stopped before 'kill' is called."
 | ||
|   (service user-processes-service-type grace-delay))
 | ||
| 
 | ||
| 
 | ||
| ;;;
 | ||
| ;;; Preserve entropy to seed /dev/urandom on boot.
 | ||
| ;;;
 | ||
| 
 | ||
| (define %random-seed-file
 | ||
|   "/var/lib/random-seed")
 | ||
| 
 | ||
| (define (urandom-seed-shepherd-service _)
 | ||
|   "Return a shepherd service for the /dev/urandom seed."
 | ||
|   (list (shepherd-service
 | ||
|          (documentation "Preserve entropy across reboots for /dev/urandom.")
 | ||
|          (provision '(urandom-seed))
 | ||
|          (requirement '(user-processes))
 | ||
|          (start #~(lambda _
 | ||
|                     ;; On boot, write random seed into /dev/urandom.
 | ||
|                     (when (file-exists? #$%random-seed-file)
 | ||
|                       (call-with-input-file #$%random-seed-file
 | ||
|                         (lambda (seed)
 | ||
|                           (call-with-output-file "/dev/urandom"
 | ||
|                             (lambda (urandom)
 | ||
|                               (dump-port seed urandom))))))
 | ||
|                     ;; Immediately refresh the seed in case the system doesn't
 | ||
|                     ;; shut down cleanly.
 | ||
|                     (call-with-input-file "/dev/urandom"
 | ||
|                       (lambda (urandom)
 | ||
|                         (let ((previous-umask (umask #o077))
 | ||
|                               (buf (make-bytevector 512)))
 | ||
|                           (mkdir-p (dirname #$%random-seed-file))
 | ||
|                           (get-bytevector-n! urandom buf 0 512)
 | ||
|                           (call-with-output-file #$%random-seed-file
 | ||
|                             (lambda (seed)
 | ||
|                               (put-bytevector seed buf)))
 | ||
|                           (umask previous-umask))))
 | ||
|                     #t))
 | ||
|          (stop #~(lambda _
 | ||
|                    ;; During shutdown, write from /dev/urandom into random seed.
 | ||
|                    (let ((buf (make-bytevector 512)))
 | ||
|                      (call-with-input-file "/dev/urandom"
 | ||
|                        (lambda (urandom)
 | ||
|                          (let ((previous-umask (umask #o077)))
 | ||
|                            (get-bytevector-n! urandom buf 0 512)
 | ||
|                            (mkdir-p (dirname #$%random-seed-file))
 | ||
|                            (call-with-output-file #$%random-seed-file
 | ||
|                              (lambda (seed)
 | ||
|                                (put-bytevector seed buf)))
 | ||
|                            (umask previous-umask))
 | ||
|                          #t)))))
 | ||
|          (modules `((rnrs bytevectors)
 | ||
|                     (rnrs io ports)
 | ||
|                     ,@%default-modules)))))
 | ||
| 
 | ||
| (define urandom-seed-service-type
 | ||
|   (service-type (name 'urandom-seed)
 | ||
|                 (extensions
 | ||
|                  (list (service-extension shepherd-root-service-type
 | ||
|                                           urandom-seed-shepherd-service)))))
 | ||
| 
 | ||
| (define (urandom-seed-service)
 | ||
|   (service urandom-seed-service-type #f))
 | ||
| 
 | ||
| 
 | ||
| ;;;
 | ||
| ;;; Add hardware random number generator to entropy pool.
 | ||
| ;;;
 | ||
| 
 | ||
| (define-record-type* <rngd-configuration>
 | ||
|   rngd-configuration make-rngd-configuration
 | ||
|   rngd-configuration?
 | ||
|   (rng-tools rngd-configuration-rng-tools)        ;package
 | ||
|   (device    rngd-configuration-device))          ;string
 | ||
| 
 | ||
| (define rngd-service-type
 | ||
|   (shepherd-service-type
 | ||
|     'rngd
 | ||
|     (lambda (config)
 | ||
|       (define rng-tools (rngd-configuration-rng-tools config))
 | ||
|       (define device (rngd-configuration-device config))
 | ||
| 
 | ||
|       (define rngd-command
 | ||
|         (list (file-append rng-tools "/sbin/rngd")
 | ||
|               "-f" "-r" device))
 | ||
| 
 | ||
|       (shepherd-service
 | ||
|         (documentation "Add TRNG to entropy pool.")
 | ||
|         (requirement '(udev))
 | ||
|         (provision '(trng))
 | ||
|         (start #~(make-forkexec-constructor #$@rngd-command))
 | ||
|         (stop #~(make-kill-destructor))))))
 | ||
| 
 | ||
| (define* (rngd-service #:key
 | ||
|                        (rng-tools rng-tools)
 | ||
|                        (device "/dev/hwrng"))
 | ||
|   "Return a service that runs the @command{rngd} program from @var{rng-tools}
 | ||
| to add @var{device} to the kernel's entropy pool.  The service will fail if
 | ||
| @var{device} does not exist."
 | ||
|   (service rngd-service-type
 | ||
|            (rngd-configuration
 | ||
|             (rng-tools rng-tools)
 | ||
|             (device device))))
 | ||
| 
 | ||
| 
 | ||
| ;;;
 | ||
| ;;; System-wide environment variables.
 | ||
| ;;;
 | ||
| 
 | ||
| (define (environment-variables->environment-file vars)
 | ||
|   "Return a file for pam_env(8) that contains environment variables VARS."
 | ||
|   (apply mixed-text-file "environment"
 | ||
|          (append-map (match-lambda
 | ||
|                        ((key . value)
 | ||
|                         (list key "=" value "\n")))
 | ||
|                      vars)))
 | ||
| 
 | ||
| (define session-environment-service-type
 | ||
|   (service-type
 | ||
|    (name 'session-environment)
 | ||
|    (extensions
 | ||
|     (list (service-extension
 | ||
|            etc-service-type
 | ||
|            (lambda (vars)
 | ||
|              (list `("environment"
 | ||
|                      ,(environment-variables->environment-file vars)))))))
 | ||
|    (compose concatenate)
 | ||
|    (extend append)))
 | ||
| 
 | ||
| (define (session-environment-service vars)
 | ||
|   "Return a service that builds the @file{/etc/environment}, which can be read
 | ||
| by PAM-aware applications to set environment variables for sessions.
 | ||
| 
 | ||
| VARS should be an association list in which both the keys and the values are
 | ||
| strings or string-valued gexps."
 | ||
|   (service session-environment-service-type vars))
 | ||
| 
 | ||
| 
 | ||
| ;;;
 | ||
| ;;; Console & co.
 | ||
| ;;;
 | ||
| 
 | ||
| (define host-name-service-type
 | ||
|   (shepherd-service-type
 | ||
|    'host-name
 | ||
|    (lambda (name)
 | ||
|      (shepherd-service
 | ||
|       (documentation "Initialize the machine's host name.")
 | ||
|       (provision '(host-name))
 | ||
|       (start #~(lambda _
 | ||
|                  (sethostname #$name)))
 | ||
|       (respawn? #f)))))
 | ||
| 
 | ||
| (define (host-name-service name)
 | ||
|   "Return a service that sets the host name to @var{name}."
 | ||
|   (service host-name-service-type name))
 | ||
| 
 | ||
| (define (unicode-start tty)
 | ||
|   "Return a gexp to start Unicode support on @var{tty}."
 | ||
| 
 | ||
|   ;; We have to run 'unicode_start' in a pipe so that when it invokes the
 | ||
|   ;; 'tty' command, that command returns TTY.
 | ||
|   #~(begin
 | ||
|       (let ((pid (primitive-fork)))
 | ||
|         (case pid
 | ||
|           ((0)
 | ||
|            (close-fdes 0)
 | ||
|            (dup2 (open-fdes #$tty O_RDONLY) 0)
 | ||
|            (close-fdes 1)
 | ||
|            (dup2 (open-fdes #$tty O_WRONLY) 1)
 | ||
|            (execl #$(file-append kbd "/bin/unicode_start")
 | ||
|                   "unicode_start"))
 | ||
|           (else
 | ||
|            (zero? (cdr (waitpid pid))))))))
 | ||
| 
 | ||
| (define console-keymap-service-type
 | ||
|   (shepherd-service-type
 | ||
|    'console-keymap
 | ||
|    (lambda (files)
 | ||
|      (shepherd-service
 | ||
|       (documentation (string-append "Load console keymap (loadkeys)."))
 | ||
|       (provision '(console-keymap))
 | ||
|       (start #~(lambda _
 | ||
|                  (zero? (system* #$(file-append kbd "/bin/loadkeys")
 | ||
|                                  #$@files))))
 | ||
|       (respawn? #f)))))
 | ||
| 
 | ||
| (define (console-keymap-service . files)
 | ||
|   "Return a service to load console keymaps from @var{files}."
 | ||
|   (service console-keymap-service-type files))
 | ||
| 
 | ||
| (define %default-console-font
 | ||
|   ;; Note: 'LatGrkCyr-8x16' has the advantage of providing three common
 | ||
|   ;; scripts as well as glyphs for em dash, quotation marks, and other Unicode
 | ||
|   ;; codepoints notably found in the UTF-8 manual.
 | ||
|   "LatGrkCyr-8x16")
 | ||
| 
 | ||
| (define (console-font-shepherd-services tty+font)
 | ||
|   "Return a list of Shepherd services for each pair in TTY+FONT."
 | ||
|   (map (match-lambda
 | ||
|          ((tty . font)
 | ||
|           (let ((device (string-append "/dev/" tty)))
 | ||
|             (shepherd-service
 | ||
|              (documentation "Load a Unicode console font.")
 | ||
|              (provision (list (symbol-append 'console-font-
 | ||
|                                              (string->symbol tty))))
 | ||
| 
 | ||
|              ;; Start after mingetty has been started on TTY, otherwise the settings
 | ||
|              ;; are ignored.
 | ||
|              (requirement (list (symbol-append 'term-
 | ||
|                                                (string->symbol tty))))
 | ||
| 
 | ||
|              (start #~(lambda _
 | ||
|                         (and #$(unicode-start device)
 | ||
|                              (zero?
 | ||
|                               (system* #$(file-append kbd "/bin/setfont")
 | ||
|                                        "-C" #$device #$font)))))
 | ||
|              (stop #~(const #t))
 | ||
|              (respawn? #f)))))
 | ||
|        tty+font))
 | ||
| 
 | ||
| (define console-font-service-type
 | ||
|   (service-type (name 'console-fonts)
 | ||
|                 (extensions
 | ||
|                  (list (service-extension shepherd-root-service-type
 | ||
|                                           console-font-shepherd-services)))
 | ||
|                 (compose concatenate)
 | ||
|                 (extend append)))
 | ||
| 
 | ||
| (define* (console-font-service tty #:optional (font "LatGrkCyr-8x16"))
 | ||
|   "This procedure is deprecated in favor of @code{console-font-service-type}.
 | ||
| 
 | ||
| Return a service that sets up Unicode support in @var{tty} and loads
 | ||
| @var{font} for that tty (fonts are per virtual console in Linux.)"
 | ||
|   (simple-service (symbol-append 'console-font- (string->symbol tty))
 | ||
|                   console-font-service-type `((,tty . ,font))))
 | ||
| 
 | ||
| (define %default-motd
 | ||
|   (plain-file "motd" "This is the GNU operating system, welcome!\n\n"))
 | ||
| 
 | ||
| (define-record-type* <login-configuration>
 | ||
|   login-configuration make-login-configuration
 | ||
|   login-configuration?
 | ||
|   (motd                   login-configuration-motd     ;file-like
 | ||
|                           (default %default-motd))
 | ||
|   ;; Allow empty passwords by default so that first-time users can log in when
 | ||
|   ;; the 'root' account has just been created.
 | ||
|   (allow-empty-passwords? login-configuration-allow-empty-passwords?
 | ||
|                           (default #t)))               ;Boolean
 | ||
| 
 | ||
| (define (login-pam-service config)
 | ||
|   "Return the list of PAM service needed for CONF."
 | ||
|   ;; Let 'login' be known to PAM.
 | ||
|   (list (unix-pam-service "login"
 | ||
|                           #:allow-empty-passwords?
 | ||
|                           (login-configuration-allow-empty-passwords? config)
 | ||
|                           #:motd
 | ||
|                           (login-configuration-motd config))))
 | ||
| 
 | ||
| (define login-service-type
 | ||
|   (service-type (name 'login)
 | ||
|                 (extensions (list (service-extension pam-root-service-type
 | ||
|                                                      login-pam-service)))))
 | ||
| 
 | ||
| (define* (login-service #:optional (config (login-configuration)))
 | ||
|   "Return a service configure login according to @var{config}, which specifies
 | ||
| the message of the day, among other things."
 | ||
|   (service login-service-type config))
 | ||
| 
 | ||
| (define-record-type* <agetty-configuration>
 | ||
|   agetty-configuration make-agetty-configuration
 | ||
|   agetty-configuration?
 | ||
|   (agetty           agetty-configuration-agetty   ;<package>
 | ||
|                     (default util-linux))
 | ||
|   (tty              agetty-configuration-tty)     ;string
 | ||
|   (term             agetty-term                   ;string | #f
 | ||
|                     (default #f))
 | ||
|   (baud-rate        agetty-baud-rate              ;string | #f
 | ||
|                     (default #f))
 | ||
|   (auto-login       agetty-auto-login             ;list of strings | #f
 | ||
|                     (default #f))
 | ||
|   (login-program    agetty-login-program          ;gexp
 | ||
|                     (default (file-append shadow "/bin/login")))
 | ||
|   (login-pause?     agetty-login-pause?           ;Boolean
 | ||
|                     (default #f))
 | ||
|   (eight-bits?      agetty-eight-bits?            ;Boolean
 | ||
|                     (default #f))
 | ||
|   (no-reset?        agetty-no-reset?              ;Boolean
 | ||
|                     (default #f))
 | ||
|   (remote?          agetty-remote?                ;Boolean
 | ||
|                     (default #f))
 | ||
|   (flow-control?    agetty-flow-control?          ;Boolean
 | ||
|                     (default #f))
 | ||
|   (host             agetty-host                   ;string | #f
 | ||
|                     (default #f))
 | ||
|   (no-issue?        agetty-no-issue?              ;Boolean
 | ||
|                     (default #f))
 | ||
|   (init-string      agetty-init-string            ;string | #f
 | ||
|                     (default #f))
 | ||
|   (no-clear?        agetty-no-clear?              ;Boolean
 | ||
|                     (default #f))
 | ||
|   (local-line       agetty-local-line             ;always | never | auto
 | ||
|                     (default #f))
 | ||
|   (extract-baud?    agetty-extract-baud?          ;Boolean
 | ||
|                     (default #f))
 | ||
|   (skip-login?      agetty-skip-login?            ;Boolean
 | ||
|                     (default #f))
 | ||
|   (no-newline?      agetty-no-newline?            ;Boolean
 | ||
|                     (default #f))
 | ||
|   (login-options    agetty-login-options          ;string | #f
 | ||
|                     (default #f))
 | ||
|   (chroot           agetty-chroot                 ;string | #f
 | ||
|                     (default #f))
 | ||
|   (hangup?          agetty-hangup?                ;Boolean
 | ||
|                     (default #f))
 | ||
|   (keep-baud?       agetty-keep-baud?             ;Boolean
 | ||
|                     (default #f))
 | ||
|   (timeout          agetty-timeout                ;integer | #f
 | ||
|                     (default #f))
 | ||
|   (detect-case?     agetty-detect-case?           ;Boolean
 | ||
|                     (default #f))
 | ||
|   (wait-cr?         agetty-wait-cr?               ;Boolean
 | ||
|                     (default #f))
 | ||
|   (no-hints?        agetty-no-hints?              ;Boolean
 | ||
|                     (default #f))
 | ||
|   (no-hostname?     agetty-no hostname?           ;Boolean
 | ||
|                     (default #f))
 | ||
|   (long-hostname?   agetty-long-hostname?         ;Boolean
 | ||
|                     (default #f))
 | ||
|   (erase-characters agetty-erase-characters       ;string | #f
 | ||
|                     (default #f))
 | ||
|   (kill-characters  agetty-kill-characters        ;string | #f
 | ||
|                     (default #f))
 | ||
|   (chdir            agetty-chdir                  ;string | #f
 | ||
|                     (default #f))
 | ||
|   (delay            agetty-delay                  ;integer | #f
 | ||
|                     (default #f))
 | ||
|   (nice             agetty-nice                   ;integer | #f
 | ||
|                     (default #f))
 | ||
|   ;; "Escape hatch" for passing arbitrary command-line arguments.
 | ||
|   (extra-options    agetty-extra-options          ;list of strings
 | ||
|                     (default '()))
 | ||
| ;;; XXX Unimplemented for now!
 | ||
| ;;; (issue-file     agetty-issue-file             ;file-like
 | ||
| ;;;                 (default #f))
 | ||
|   )
 | ||
| 
 | ||
| (define agetty-shepherd-service
 | ||
|   (match-lambda
 | ||
|     (($ <agetty-configuration> agetty tty term baud-rate auto-login
 | ||
|         login-program login-pause? eight-bits? no-reset? remote? flow-control?
 | ||
|         host no-issue? init-string no-clear? local-line extract-baud?
 | ||
|         skip-login? no-newline? login-options chroot hangup? keep-baud? timeout
 | ||
|         detect-case? wait-cr? no-hints? no-hostname? long-hostname?
 | ||
|         erase-characters kill-characters chdir delay nice extra-options)
 | ||
|      (list
 | ||
|        (shepherd-service
 | ||
|          (documentation "Run agetty on a tty.")
 | ||
|          (provision (list (symbol-append 'term- (string->symbol tty))))
 | ||
| 
 | ||
|          ;; Since the login prompt shows the host name, wait for the 'host-name'
 | ||
|          ;; service to be done.  Also wait for udev essentially so that the tty
 | ||
|          ;; text is not lost in the middle of kernel messages (see also
 | ||
|          ;; mingetty-shepherd-service).
 | ||
|          (requirement '(user-processes host-name udev))
 | ||
| 
 | ||
|          (start #~(make-forkexec-constructor
 | ||
|                     (list #$(file-append util-linux "/sbin/agetty")
 | ||
|                           #$@extra-options
 | ||
|                           #$@(if eight-bits?
 | ||
|                                  #~("--8bits")
 | ||
|                                  #~())
 | ||
|                           #$@(if no-reset?
 | ||
|                                  #~("--noreset")
 | ||
|                                  #~())
 | ||
|                           #$@(if remote?
 | ||
|                                  #~("--remote")
 | ||
|                                  #~())
 | ||
|                           #$@(if flow-control?
 | ||
|                                  #~("--flow-control")
 | ||
|                                  #~())
 | ||
|                           #$@(if host
 | ||
|                                  #~("--host" #$host)
 | ||
|                                  #~())
 | ||
|                           #$@(if no-issue?
 | ||
|                                  #~("--noissue")
 | ||
|                                  #~())
 | ||
|                           #$@(if init-string
 | ||
|                                  #~("--init-string" #$init-string)
 | ||
|                                  #~())
 | ||
|                           #$@(if no-clear?
 | ||
|                                  #~("--noclear")
 | ||
|                                  #~())
 | ||
| ;;; FIXME This doesn't work as expected. According to agetty(8), if this option
 | ||
| ;;; is not passed, then the default is 'auto'. However, in my tests, when that
 | ||
| ;;; option is selected, agetty never presents the login prompt, and the
 | ||
| ;;; term-ttyS0 service respawns every few seconds.
 | ||
|                           #$@(if local-line
 | ||
|                                  #~(#$(match local-line
 | ||
|                                         ('auto "--local-line=auto")
 | ||
|                                         ('always "--local-line=always")
 | ||
|                                         ('never "-local-line=never")))
 | ||
|                                  #~())
 | ||
|                           #$@(if extract-baud?
 | ||
|                                  #~("--extract-baud")
 | ||
|                                  #~())
 | ||
|                           #$@(if skip-login?
 | ||
|                                  #~("--skip-login")
 | ||
|                                  #~())
 | ||
|                           #$@(if no-newline?
 | ||
|                                  #~("--nonewline")
 | ||
|                                  #~())
 | ||
|                           #$@(if login-options
 | ||
|                                  #~("--login-options" #$login-options)
 | ||
|                                  #~())
 | ||
|                           #$@(if chroot
 | ||
|                                  #~("--chroot" #$chroot)
 | ||
|                                  #~())
 | ||
|                           #$@(if hangup?
 | ||
|                                  #~("--hangup")
 | ||
|                                  #~())
 | ||
|                           #$@(if keep-baud?
 | ||
|                                  #~("--keep-baud")
 | ||
|                                  #~())
 | ||
|                           #$@(if timeout
 | ||
|                                  #~("--timeout" #$(number->string timeout))
 | ||
|                                  #~())
 | ||
|                           #$@(if detect-case?
 | ||
|                                  #~("--detect-case")
 | ||
|                                  #~())
 | ||
|                           #$@(if wait-cr?
 | ||
|                                  #~("--wait-cr")
 | ||
|                                  #~())
 | ||
|                           #$@(if no-hints?
 | ||
|                                  #~("--nohints?")
 | ||
|                                  #~())
 | ||
|                           #$@(if no-hostname?
 | ||
|                                  #~("--nohostname")
 | ||
|                                  #~())
 | ||
|                           #$@(if long-hostname?
 | ||
|                                  #~("--long-hostname")
 | ||
|                                  #~())
 | ||
|                           #$@(if erase-characters
 | ||
|                                  #~("--erase-chars" #$erase-characters)
 | ||
|                                  #~())
 | ||
|                           #$@(if kill-characters
 | ||
|                                  #~("--kill-chars" #$kill-characters)
 | ||
|                                  #~())
 | ||
|                           #$@(if chdir
 | ||
|                                  #~("--chdir" #$chdir)
 | ||
|                                  #~())
 | ||
|                           #$@(if delay
 | ||
|                                  #~("--delay" #$(number->string delay))
 | ||
|                                  #~())
 | ||
|                           #$@(if nice
 | ||
|                                  #~("--nice" #$(number->string nice))
 | ||
|                                  #~())
 | ||
|                           #$@(if auto-login
 | ||
|                                  (list "--autologin" auto-login)
 | ||
|                                  '())
 | ||
|                           #$@(if login-program
 | ||
|                                  #~("--login-program" #$login-program)
 | ||
|                                  #~())
 | ||
|                           #$@(if login-pause?
 | ||
|                                  #~("--login-pause")
 | ||
|                                  #~())
 | ||
|                           #$tty
 | ||
|                           #$@(if baud-rate
 | ||
|                                  #~(#$baud-rate)
 | ||
|                                  #~())
 | ||
|                           #$@(if term
 | ||
|                                  #~(#$term)
 | ||
|                                  #~()))))
 | ||
|          (stop #~(make-kill-destructor)))))))
 | ||
| 
 | ||
| (define agetty-service-type
 | ||
|   (service-type (name 'agetty)
 | ||
|                 (extensions (list (service-extension shepherd-root-service-type
 | ||
|                                                      agetty-shepherd-service)))))
 | ||
| 
 | ||
| (define* (agetty-service config)
 | ||
|   "Return a service to run agetty according to @var{config}, which specifies
 | ||
| the tty to run, among other things."
 | ||
|   (service agetty-service-type config))
 | ||
| 
 | ||
| (define-record-type* <mingetty-configuration>
 | ||
|   mingetty-configuration make-mingetty-configuration
 | ||
|   mingetty-configuration?
 | ||
|   (mingetty       mingetty-configuration-mingetty ;<package>
 | ||
|                   (default mingetty))
 | ||
|   (tty            mingetty-configuration-tty)     ;string
 | ||
|   (auto-login     mingetty-auto-login             ;string | #f
 | ||
|                   (default #f))
 | ||
|   (login-program  mingetty-login-program          ;gexp
 | ||
|                   (default #f))
 | ||
|   (login-pause?   mingetty-login-pause?           ;Boolean
 | ||
|                   (default #f)))
 | ||
| 
 | ||
| (define mingetty-shepherd-service
 | ||
|   (match-lambda
 | ||
|     (($ <mingetty-configuration> mingetty tty auto-login login-program
 | ||
|                                  login-pause?)
 | ||
|      (list
 | ||
|       (shepherd-service
 | ||
|        (documentation "Run mingetty on an tty.")
 | ||
|        (provision (list (symbol-append 'term- (string->symbol tty))))
 | ||
| 
 | ||
|        ;; Since the login prompt shows the host name, wait for the 'host-name'
 | ||
|        ;; service to be done.  Also wait for udev essentially so that the tty
 | ||
|        ;; text is not lost in the middle of kernel messages (XXX).
 | ||
|        (requirement '(user-processes host-name udev))
 | ||
| 
 | ||
|        (start  #~(make-forkexec-constructor
 | ||
|                   (list #$(file-append mingetty "/sbin/mingetty")
 | ||
|                         "--noclear" #$tty
 | ||
|                         #$@(if auto-login
 | ||
|                                #~("--autologin" #$auto-login)
 | ||
|                                #~())
 | ||
|                         #$@(if login-program
 | ||
|                                #~("--loginprog" #$login-program)
 | ||
|                                #~())
 | ||
|                         #$@(if login-pause?
 | ||
|                                #~("--loginpause")
 | ||
|                                #~()))))
 | ||
|        (stop   #~(make-kill-destructor)))))))
 | ||
| 
 | ||
| (define mingetty-service-type
 | ||
|   (service-type (name 'mingetty)
 | ||
|                 (extensions (list (service-extension shepherd-root-service-type
 | ||
|                                                      mingetty-shepherd-service)))))
 | ||
| 
 | ||
| (define* (mingetty-service config)
 | ||
|   "Return a service to run mingetty according to @var{config}, which specifies
 | ||
| the tty to run, among other things."
 | ||
|   (service mingetty-service-type config))
 | ||
| 
 | ||
| (define-record-type* <nscd-configuration> nscd-configuration
 | ||
|   make-nscd-configuration
 | ||
|   nscd-configuration?
 | ||
|   (log-file    nscd-configuration-log-file        ;string
 | ||
|                (default "/var/log/nscd.log"))
 | ||
|   (debug-level nscd-debug-level                   ;integer
 | ||
|                (default 0))
 | ||
|   ;; TODO: See nscd.conf in glibc for other options to add.
 | ||
|   (caches     nscd-configuration-caches           ;list of <nscd-cache>
 | ||
|               (default %nscd-default-caches))
 | ||
|   (name-services nscd-configuration-name-services ;list of <packages>
 | ||
|                  (default '()))
 | ||
|   (glibc      nscd-configuration-glibc            ;<package>
 | ||
|               (default (canonical-package glibc))))
 | ||
| 
 | ||
| (define-record-type* <nscd-cache> nscd-cache make-nscd-cache
 | ||
|   nscd-cache?
 | ||
|   (database              nscd-cache-database)              ;symbol
 | ||
|   (positive-time-to-live nscd-cache-positive-time-to-live) ;integer
 | ||
|   (negative-time-to-live nscd-cache-negative-time-to-live
 | ||
|                          (default 20))             ;integer
 | ||
|   (suggested-size        nscd-cache-suggested-size ;integer ("default module
 | ||
|                                                    ;of hash table")
 | ||
|                          (default 211))
 | ||
|   (check-files?          nscd-cache-check-files?  ;Boolean
 | ||
|                          (default #t))
 | ||
|   (persistent?           nscd-cache-persistent?   ;Boolean
 | ||
|                          (default #t))
 | ||
|   (shared?               nscd-cache-shared?       ;Boolean
 | ||
|                          (default #t))
 | ||
|   (max-database-size     nscd-cache-max-database-size ;integer
 | ||
|                          (default (* 32 (expt 2 20))))
 | ||
|   (auto-propagate?       nscd-cache-auto-propagate? ;Boolean
 | ||
|                          (default #t)))
 | ||
| 
 | ||
| (define %nscd-default-caches
 | ||
|   ;; Caches that we want to enable by default.  Note that when providing an
 | ||
|   ;; empty nscd.conf, all caches are disabled.
 | ||
|   (list (nscd-cache (database 'hosts)
 | ||
| 
 | ||
|                     ;; Aggressively cache the host name cache to improve
 | ||
|                     ;; privacy and resilience.
 | ||
|                     (positive-time-to-live (* 3600 12))
 | ||
|                     (negative-time-to-live 20)
 | ||
|                     (persistent? #t))
 | ||
| 
 | ||
|         (nscd-cache (database 'services)
 | ||
| 
 | ||
|                     ;; Services are unlikely to change, so we can be even more
 | ||
|                     ;; aggressive.
 | ||
|                     (positive-time-to-live (* 3600 24))
 | ||
|                     (negative-time-to-live 3600)
 | ||
|                     (check-files? #t)             ;check /etc/services changes
 | ||
|                     (persistent? #t))))
 | ||
| 
 | ||
| (define %nscd-default-configuration
 | ||
|   ;; Default nscd configuration.
 | ||
|   (nscd-configuration))
 | ||
| 
 | ||
| (define (nscd.conf-file config)
 | ||
|   "Return the @file{nscd.conf} configuration file for @var{config}, an
 | ||
| @code{<nscd-configuration>} object."
 | ||
|   (define cache->config
 | ||
|     (match-lambda
 | ||
|       (($ <nscd-cache> (= symbol->string database)
 | ||
|                        positive-ttl negative-ttl size check-files?
 | ||
|                        persistent? shared? max-size propagate?)
 | ||
|        (string-append "\nenable-cache\t" database "\tyes\n"
 | ||
| 
 | ||
|                       "positive-time-to-live\t" database "\t"
 | ||
|                       (number->string positive-ttl) "\n"
 | ||
|                       "negative-time-to-live\t" database "\t"
 | ||
|                       (number->string negative-ttl) "\n"
 | ||
|                       "suggested-size\t" database "\t"
 | ||
|                       (number->string size) "\n"
 | ||
|                       "check-files\t" database "\t"
 | ||
|                       (if check-files? "yes\n" "no\n")
 | ||
|                       "persistent\t" database "\t"
 | ||
|                       (if persistent? "yes\n" "no\n")
 | ||
|                       "shared\t" database "\t"
 | ||
|                       (if shared? "yes\n" "no\n")
 | ||
|                       "max-db-size\t" database "\t"
 | ||
|                       (number->string max-size) "\n"
 | ||
|                       "auto-propagate\t" database "\t"
 | ||
|                       (if propagate? "yes\n" "no\n")))))
 | ||
| 
 | ||
|   (match config
 | ||
|     (($ <nscd-configuration> log-file debug-level caches)
 | ||
|      (plain-file "nscd.conf"
 | ||
|                  (string-append "\
 | ||
| # Configuration of libc's name service cache daemon (nscd).\n\n"
 | ||
|                                 (if log-file
 | ||
|                                     (string-append "logfile\t" log-file)
 | ||
|                                     "")
 | ||
|                                 "\n"
 | ||
|                                 (if debug-level
 | ||
|                                     (string-append "debug-level\t"
 | ||
|                                                    (number->string debug-level))
 | ||
|                                     "")
 | ||
|                                 "\n"
 | ||
|                                 (string-concatenate
 | ||
|                                  (map cache->config caches)))))))
 | ||
| 
 | ||
| (define (nscd-shepherd-service config)
 | ||
|   "Return a shepherd service for CONFIG, an <nscd-configuration> object."
 | ||
|   (let ((nscd.conf     (nscd.conf-file config))
 | ||
|         (name-services (nscd-configuration-name-services config)))
 | ||
|     (list (shepherd-service
 | ||
|            (documentation "Run libc's name service cache daemon (nscd).")
 | ||
|            (provision '(nscd))
 | ||
|            (requirement '(user-processes))
 | ||
|            (start #~(make-forkexec-constructor
 | ||
|                      (list #$(file-append (nscd-configuration-glibc config)
 | ||
|                                           "/sbin/nscd")
 | ||
|                            "-f" #$nscd.conf "--foreground")
 | ||
| 
 | ||
|                      ;; Wait for the PID file.  However, the PID file is
 | ||
|                      ;; written before nscd is actually listening on its
 | ||
|                      ;; socket (XXX).
 | ||
|                      #:pid-file "/var/run/nscd/nscd.pid"
 | ||
| 
 | ||
|                      #:environment-variables
 | ||
|                      (list (string-append "LD_LIBRARY_PATH="
 | ||
|                                           (string-join
 | ||
|                                            (map (lambda (dir)
 | ||
|                                                   (string-append dir "/lib"))
 | ||
|                                                 (list #$@name-services))
 | ||
|                                            ":")))))
 | ||
|            (stop #~(make-kill-destructor))))))
 | ||
| 
 | ||
| (define nscd-activation
 | ||
|   ;; Actions to take before starting nscd.
 | ||
|   #~(begin
 | ||
|       (use-modules (guix build utils))
 | ||
|       (mkdir-p "/var/run/nscd")
 | ||
|       (mkdir-p "/var/db/nscd")                    ;for the persistent cache
 | ||
| 
 | ||
|       ;; In libc 2.25 nscd uses inotify to watch /etc/resolv.conf, but only if
 | ||
|       ;; that file exists when it is started.  Thus create it here.  Note: on
 | ||
|       ;; some systems, such as when NetworkManager is used, /etc/resolv.conf
 | ||
|       ;; is a symlink, hence 'lstat'.
 | ||
|       (unless (false-if-exception (lstat "/etc/resolv.conf"))
 | ||
|         (call-with-output-file "/etc/resolv.conf"
 | ||
|           (lambda (port)
 | ||
|             (display "# This is a placeholder.\n" port))))))
 | ||
| 
 | ||
| (define nscd-service-type
 | ||
|   (service-type (name 'nscd)
 | ||
|                 (extensions
 | ||
|                  (list (service-extension activation-service-type
 | ||
|                                           (const nscd-activation))
 | ||
|                        (service-extension shepherd-root-service-type
 | ||
|                                           nscd-shepherd-service)))
 | ||
| 
 | ||
|                 ;; This can be extended by providing additional name services
 | ||
|                 ;; such as nss-mdns.
 | ||
|                 (compose concatenate)
 | ||
|                 (extend (lambda (config name-services)
 | ||
|                           (nscd-configuration
 | ||
|                            (inherit config)
 | ||
|                            (name-services (append
 | ||
|                                            (nscd-configuration-name-services config)
 | ||
|                                            name-services)))))))
 | ||
| 
 | ||
| (define* (nscd-service #:optional (config %nscd-default-configuration))
 | ||
|   "Return a service that runs libc's name service cache daemon (nscd) with the
 | ||
| given @var{config}---an @code{<nscd-configuration>} object.  @xref{Name
 | ||
| Service Switch}, for an example."
 | ||
|   (service nscd-service-type config))
 | ||
| 
 | ||
| 
 | ||
| (define-record-type* <syslog-configuration>
 | ||
|   syslog-configuration  make-syslog-configuration
 | ||
|   syslog-configuration?
 | ||
|   (syslogd              syslog-configuration-syslogd
 | ||
|                         (default (file-append inetutils "/libexec/syslogd")))
 | ||
|   (config-file          syslog-configuration-config-file
 | ||
|                         (default %default-syslog.conf)))
 | ||
| 
 | ||
| (define syslog-service-type
 | ||
|   (shepherd-service-type
 | ||
|    'syslog
 | ||
|    (lambda (config)
 | ||
|      (shepherd-service
 | ||
|       (documentation "Run the syslog daemon (syslogd).")
 | ||
|       (provision '(syslogd))
 | ||
|       (requirement '(user-processes))
 | ||
|       (start #~(make-forkexec-constructor
 | ||
|                 (list #$(syslog-configuration-syslogd config)
 | ||
|                       "--rcfile" #$(syslog-configuration-config-file config))
 | ||
|                 #:pid-file "/var/run/syslog.pid"))
 | ||
|       (stop #~(make-kill-destructor))))))
 | ||
| 
 | ||
| ;; Snippet adapted from the GNU inetutils manual.
 | ||
| (define %default-syslog.conf
 | ||
|   (plain-file "syslog.conf" "
 | ||
|      # Log all error messages, authentication messages of
 | ||
|      # level notice or higher and anything of level err or
 | ||
|      # higher to the console.
 | ||
|      # Don't log private authentication messages!
 | ||
|      *.alert;auth.notice;authpriv.none       /dev/console
 | ||
| 
 | ||
|      # Log anything (except mail) of level info or higher.
 | ||
|      # Don't log private authentication messages!
 | ||
|      *.info;mail.none;authpriv.none          /var/log/messages
 | ||
| 
 | ||
|      # Same, in a different place.
 | ||
|      *.info;mail.none;authpriv.none          /dev/tty12
 | ||
| 
 | ||
|      # The authpriv file has restricted access.
 | ||
|      authpriv.*                              /var/log/secure
 | ||
| 
 | ||
|      # Log all the mail messages in one place.
 | ||
|      mail.*                                  /var/log/maillog
 | ||
| "))
 | ||
| 
 | ||
| (define* (syslog-service #:optional (config (syslog-configuration)))
 | ||
|   "Return a service that runs @command{syslogd} and takes
 | ||
| @var{<syslog-configuration>} as a parameter.
 | ||
| 
 | ||
| @xref{syslogd invocation,,, inetutils, GNU Inetutils}, for more
 | ||
| information on the configuration file syntax."
 | ||
|   (service syslog-service-type config))
 | ||
| 
 | ||
| 
 | ||
| (define pam-limits-service-type
 | ||
|   (let ((security-limits
 | ||
|          ;; Create /etc/security containing the provided "limits.conf" file.
 | ||
|          (lambda (limits-file)
 | ||
|            `(("security"
 | ||
|               ,(computed-file
 | ||
|                 "security"
 | ||
|                 #~(begin
 | ||
|                     (mkdir #$output)
 | ||
|                     (stat #$limits-file)
 | ||
|                     (symlink #$limits-file
 | ||
|                              (string-append #$output "/limits.conf"))))))))
 | ||
|         (pam-extension
 | ||
|          (lambda (pam)
 | ||
|            (let ((pam-limits (pam-entry
 | ||
|                               (control "required")
 | ||
|                               (module "pam_limits.so")
 | ||
|                               (arguments '("conf=/etc/security/limits.conf")))))
 | ||
|              (if (member (pam-service-name pam)
 | ||
|                          '("login" "su" "slim"))
 | ||
|                  (pam-service
 | ||
|                   (inherit pam)
 | ||
|                   (session (cons pam-limits
 | ||
|                                  (pam-service-session pam))))
 | ||
|                  pam)))))
 | ||
|     (service-type
 | ||
|      (name 'limits)
 | ||
|      (extensions
 | ||
|       (list (service-extension etc-service-type security-limits)
 | ||
|             (service-extension pam-root-service-type
 | ||
|                                (lambda _ (list pam-extension))))))))
 | ||
| 
 | ||
| (define* (pam-limits-service #:optional (limits '()))
 | ||
|   "Return a service that makes selected programs respect the list of
 | ||
| pam-limits-entry specified in LIMITS via pam_limits.so."
 | ||
|   (service pam-limits-service-type
 | ||
|            (plain-file "limits.conf"
 | ||
|                        (string-join (map pam-limits-entry->string limits)
 | ||
|                                     "\n"))))
 | ||
| 
 | ||
| 
 | ||
| ;;;
 | ||
| ;;; Guix services.
 | ||
| ;;;
 | ||
| 
 | ||
| (define* (guix-build-accounts count #:key
 | ||
|                               (group "guixbuild")
 | ||
|                               (first-uid 30001)
 | ||
|                               (shadow shadow))
 | ||
|   "Return a list of COUNT user accounts for Guix build users, with UIDs
 | ||
| starting at FIRST-UID, and under GID."
 | ||
|   (unfold (cut > <> count)
 | ||
|           (lambda (n)
 | ||
|             (user-account
 | ||
|              (name (format #f "guixbuilder~2,'0d" n))
 | ||
|              (system? #t)
 | ||
|              (uid (+ first-uid n -1))
 | ||
|              (group group)
 | ||
| 
 | ||
|              ;; guix-daemon expects GROUP to be listed as a
 | ||
|              ;; supplementary group too:
 | ||
|              ;; <http://lists.gnu.org/archive/html/bug-guix/2013-01/msg00239.html>.
 | ||
|              (supplementary-groups (list group "kvm"))
 | ||
| 
 | ||
|              (comment (format #f "Guix Build User ~2d" n))
 | ||
|              (home-directory "/var/empty")
 | ||
|              (shell (file-append shadow "/sbin/nologin"))))
 | ||
|           1+
 | ||
|           1))
 | ||
| 
 | ||
| (define (hydra-key-authorization key guix)
 | ||
|   "Return a gexp with code to register KEY, a file containing a 'guix archive'
 | ||
| public key, with GUIX."
 | ||
|   #~(unless (file-exists? "/etc/guix/acl")
 | ||
|       (let ((pid (primitive-fork)))
 | ||
|         (case pid
 | ||
|           ((0)
 | ||
|            (let* ((key  #$key)
 | ||
|                   (port (open-file key "r0b")))
 | ||
|              (format #t "registering public key '~a'...~%" key)
 | ||
|              (close-port (current-input-port))
 | ||
|              (dup port 0)
 | ||
|              (execl #$(file-append guix "/bin/guix")
 | ||
|                     "guix" "archive" "--authorize")
 | ||
|              (exit 1)))
 | ||
|           (else
 | ||
|            (let ((status (cdr (waitpid pid))))
 | ||
|              (unless (zero? status)
 | ||
|                (format (current-error-port) "warning: \
 | ||
| failed to register hydra.gnu.org public key: ~a~%" status))))))))
 | ||
| 
 | ||
| (define %default-authorized-guix-keys
 | ||
|   ;; List of authorized substitute keys.
 | ||
|   (list (file-append guix "/share/guix/hydra.gnu.org.pub")
 | ||
|         (file-append guix "/share/guix/bayfront.guixsd.org.pub")))
 | ||
| 
 | ||
| (define-record-type* <guix-configuration>
 | ||
|   guix-configuration make-guix-configuration
 | ||
|   guix-configuration?
 | ||
|   (guix             guix-configuration-guix       ;<package>
 | ||
|                     (default guix))
 | ||
|   (build-group      guix-configuration-build-group ;string
 | ||
|                     (default "guixbuild"))
 | ||
|   (build-accounts   guix-configuration-build-accounts ;integer
 | ||
|                     (default 10))
 | ||
|   (authorize-key?   guix-configuration-authorize-key? ;Boolean
 | ||
|                     (default #t))
 | ||
|   (authorized-keys  guix-configuration-authorized-keys ;list of gexps
 | ||
|                     (default %default-authorized-guix-keys))
 | ||
|   (use-substitutes? guix-configuration-use-substitutes? ;Boolean
 | ||
|                     (default #t))
 | ||
|   (substitute-urls  guix-configuration-substitute-urls ;list of strings
 | ||
|                     (default %default-substitute-urls))
 | ||
|   (max-silent-time  guix-configuration-max-silent-time ;integer
 | ||
|                     (default 0))
 | ||
|   (timeout          guix-configuration-timeout    ;integer
 | ||
|                     (default 0))
 | ||
|   (extra-options    guix-configuration-extra-options ;list of strings
 | ||
|                     (default '()))
 | ||
|   (log-file         guix-configuration-log-file   ;string
 | ||
|                     (default "/var/log/guix-daemon.log"))
 | ||
|   (lsof             guix-configuration-lsof       ;<package>
 | ||
|                     (default lsof))
 | ||
|   (http-proxy       guix-http-proxy               ;string | #f
 | ||
|                     (default #f))
 | ||
|   (tmpdir           guix-tmpdir                   ;string | #f
 | ||
|                     (default #f)))
 | ||
| 
 | ||
| (define %default-guix-configuration
 | ||
|   (guix-configuration))
 | ||
| 
 | ||
| (define (guix-shepherd-service config)
 | ||
|   "Return a <shepherd-service> for the Guix daemon service with CONFIG."
 | ||
|   (match config
 | ||
|     (($ <guix-configuration> guix build-group build-accounts
 | ||
|                              authorize-key? keys
 | ||
|                              use-substitutes? substitute-urls
 | ||
|                              max-silent-time timeout
 | ||
|                              extra-options
 | ||
|                              log-file lsof http-proxy tmpdir)
 | ||
|      (list (shepherd-service
 | ||
|             (documentation "Run the Guix daemon.")
 | ||
|             (provision '(guix-daemon))
 | ||
|             (requirement '(user-processes))
 | ||
|             (start
 | ||
|              #~(make-forkexec-constructor
 | ||
|                 (list #$(file-append guix "/bin/guix-daemon")
 | ||
|                       "--build-users-group" #$build-group
 | ||
|                       "--max-silent-time" #$(number->string max-silent-time)
 | ||
|                       "--timeout" #$(number->string timeout)
 | ||
|                       #$@(if use-substitutes?
 | ||
|                              '()
 | ||
|                              '("--no-substitutes"))
 | ||
|                       "--substitute-urls" #$(string-join substitute-urls)
 | ||
|                       #$@extra-options)
 | ||
| 
 | ||
|                 ;; Add 'lsof' (for the GC) to the daemon's $PATH.
 | ||
|                 #:environment-variables
 | ||
|                 (list (string-append "PATH=" #$lsof "/bin")
 | ||
|                       #$@(if http-proxy
 | ||
|                              (list (string-append "http_proxy=" http-proxy))
 | ||
|                              '())
 | ||
|                       #$@(if tmpdir
 | ||
|                              (list (string-append "TMPDIR=" tmpdir))
 | ||
|                              '()))
 | ||
| 
 | ||
|                 #:log-file #$log-file))
 | ||
|             (stop #~(make-kill-destructor)))))))
 | ||
| 
 | ||
| (define (guix-accounts config)
 | ||
|   "Return the user accounts and user groups for CONFIG."
 | ||
|   (match config
 | ||
|     (($ <guix-configuration> _ build-group build-accounts)
 | ||
|      (cons (user-group
 | ||
|             (name build-group)
 | ||
|             (system? #t)
 | ||
| 
 | ||
|             ;; Use a fixed GID so that we can create the store with the right
 | ||
|             ;; owner.
 | ||
|             (id 30000))
 | ||
|            (guix-build-accounts build-accounts
 | ||
|                                 #:group build-group)))))
 | ||
| 
 | ||
| (define (guix-activation config)
 | ||
|   "Return the activation gexp for CONFIG."
 | ||
|   (match config
 | ||
|     (($ <guix-configuration> guix build-group build-accounts authorize-key? keys)
 | ||
|      ;; Assume that the store has BUILD-GROUP as its group.  We could
 | ||
|      ;; otherwise call 'chown' here, but the problem is that on a COW unionfs,
 | ||
|      ;; chown leads to an entire copy of the tree, which is a bad idea.
 | ||
| 
 | ||
|      ;; Optionally authorize hydra.gnu.org's key.
 | ||
|      (if authorize-key?
 | ||
|          #~(begin
 | ||
|              #$@(map (cut hydra-key-authorization <> guix) keys))
 | ||
|          #~#f))))
 | ||
| 
 | ||
| (define guix-service-type
 | ||
|   (service-type
 | ||
|    (name 'guix)
 | ||
|    (extensions
 | ||
|     (list (service-extension shepherd-root-service-type guix-shepherd-service)
 | ||
|           (service-extension account-service-type guix-accounts)
 | ||
|           (service-extension activation-service-type guix-activation)
 | ||
|           (service-extension profile-service-type
 | ||
|                              (compose list guix-configuration-guix))))
 | ||
|    (default-value (guix-configuration))))
 | ||
| 
 | ||
| (define* (guix-service #:optional (config %default-guix-configuration))
 | ||
|   "Return a service that runs the Guix build daemon according to
 | ||
| @var{config}."
 | ||
|   (service guix-service-type config))
 | ||
| 
 | ||
| 
 | ||
| (define-record-type* <guix-publish-configuration>
 | ||
|   guix-publish-configuration make-guix-publish-configuration
 | ||
|   guix-publish-configuration?
 | ||
|   (guix    guix-publish-configuration-guix        ;package
 | ||
|            (default guix))
 | ||
|   (port    guix-publish-configuration-port        ;number
 | ||
|            (default 80))
 | ||
|   (host    guix-publish-configuration-host        ;string
 | ||
|            (default "localhost"))
 | ||
|   (compression-level guix-publish-configuration-compression-level ;integer
 | ||
|                      (default 3))
 | ||
|   (nar-path    guix-publish-configuration-nar-path ;string
 | ||
|                (default "nar"))
 | ||
|   (cache       guix-publish-configuration-cache   ;#f | string
 | ||
|                (default #f))
 | ||
|   (workers     guix-publish-configuration-workers ;#f | integer
 | ||
|                (default #f))
 | ||
|   (ttl         guix-publish-configuration-ttl     ;#f | integer
 | ||
|                (default #f)))
 | ||
| 
 | ||
| (define guix-publish-shepherd-service
 | ||
|   (match-lambda
 | ||
|     (($ <guix-publish-configuration> guix port host compression
 | ||
|                                      nar-path cache workers ttl)
 | ||
|      (list (shepherd-service
 | ||
|             (provision '(guix-publish))
 | ||
|             (requirement '(guix-daemon))
 | ||
|             (start #~(make-forkexec-constructor
 | ||
|                       (list #$(file-append guix "/bin/guix")
 | ||
|                             "publish" "-u" "guix-publish"
 | ||
|                             "-p" #$(number->string port)
 | ||
|                             "-C" #$(number->string compression)
 | ||
|                             (string-append "--nar-path=" #$nar-path)
 | ||
|                             (string-append "--listen=" #$host)
 | ||
|                             #$@(if workers
 | ||
|                                    #~((string-append "--workers="
 | ||
|                                                      #$(number->string
 | ||
|                                                         workers)))
 | ||
|                                    #~())
 | ||
|                             #$@(if ttl
 | ||
|                                    #~((string-append "--ttl="
 | ||
|                                                      #$(number->string ttl)
 | ||
|                                                      "s"))
 | ||
|                                    #~())
 | ||
|                             #$@(if cache
 | ||
|                                    #~((string-append "--cache=" #$cache))
 | ||
|                                    #~()))))
 | ||
|             (stop #~(make-kill-destructor)))))))
 | ||
| 
 | ||
| (define %guix-publish-accounts
 | ||
|   (list (user-group (name "guix-publish") (system? #t))
 | ||
|         (user-account
 | ||
|          (name "guix-publish")
 | ||
|          (group "guix-publish")
 | ||
|          (system? #t)
 | ||
|          (comment "guix publish user")
 | ||
|          (home-directory "/var/empty")
 | ||
|          (shell (file-append shadow "/sbin/nologin")))))
 | ||
| 
 | ||
| (define (guix-publish-activation config)
 | ||
|   (let ((cache (guix-publish-configuration-cache config)))
 | ||
|     (if cache
 | ||
|         (with-imported-modules '((guix build utils))
 | ||
|           #~(begin
 | ||
|               (use-modules (guix build utils))
 | ||
| 
 | ||
|               (mkdir-p #$cache)
 | ||
|               (let* ((pw  (getpw "guix-publish"))
 | ||
|                      (uid (passwd:uid pw))
 | ||
|                      (gid (passwd:gid pw)))
 | ||
|                 (chown #$cache uid gid))))
 | ||
|         #t)))
 | ||
| 
 | ||
| (define guix-publish-service-type
 | ||
|   (service-type (name 'guix-publish)
 | ||
|                 (extensions
 | ||
|                  (list (service-extension shepherd-root-service-type
 | ||
|                                           guix-publish-shepherd-service)
 | ||
|                        (service-extension account-service-type
 | ||
|                                           (const %guix-publish-accounts))
 | ||
|                        (service-extension activation-service-type
 | ||
|                                           guix-publish-activation)))
 | ||
|                 (default-value (guix-publish-configuration))))
 | ||
| 
 | ||
| (define* (guix-publish-service #:key (guix guix) (port 80) (host "localhost"))
 | ||
|   "Return a service that runs @command{guix publish} listening on @var{host}
 | ||
| and @var{port} (@pxref{Invoking guix publish}).
 | ||
| 
 | ||
| This assumes that @file{/etc/guix} already contains a signing key pair as
 | ||
| created by @command{guix archive --generate-key} (@pxref{Invoking guix
 | ||
| archive}).  If that is not the case, the service will fail to start."
 | ||
|   ;; Deprecated.
 | ||
|   (service guix-publish-service-type
 | ||
|            (guix-publish-configuration (guix guix) (port port) (host host))))
 | ||
| 
 | ||
| 
 | ||
| ;;;
 | ||
| ;;; Udev.
 | ||
| ;;;
 | ||
| 
 | ||
| (define-record-type* <udev-configuration>
 | ||
|   udev-configuration make-udev-configuration
 | ||
|   udev-configuration?
 | ||
|   (udev   udev-configuration-udev                 ;<package>
 | ||
|           (default udev))
 | ||
|   (rules  udev-configuration-rules                ;list of <package>
 | ||
|           (default '())))
 | ||
| 
 | ||
| (define (udev-rules-union packages)
 | ||
|   "Return the union of the @code{lib/udev/rules.d} directories found in each
 | ||
| item of @var{packages}."
 | ||
|   (define build
 | ||
|     (with-imported-modules '((guix build union)
 | ||
|                              (guix build utils))
 | ||
|       #~(begin
 | ||
|           (use-modules (guix build union)
 | ||
|                        (guix build utils)
 | ||
|                        (srfi srfi-1)
 | ||
|                        (srfi srfi-26))
 | ||
| 
 | ||
|           (define %standard-locations
 | ||
|             '("/lib/udev/rules.d" "/libexec/udev/rules.d"))
 | ||
| 
 | ||
|           (define (rules-sub-directory directory)
 | ||
|             ;; Return the sub-directory of DIRECTORY containing udev rules, or
 | ||
|             ;; #f if none was found.
 | ||
|             (find directory-exists?
 | ||
|                   (map (cut string-append directory <>) %standard-locations)))
 | ||
| 
 | ||
|           (mkdir-p (string-append #$output "/lib/udev"))
 | ||
|           (union-build (string-append #$output "/lib/udev/rules.d")
 | ||
|                        (filter-map rules-sub-directory '#$packages)))))
 | ||
| 
 | ||
|   (computed-file "udev-rules" build))
 | ||
| 
 | ||
| (define (udev-rule file-name contents)
 | ||
|   "Return a directory with a udev rule file FILE-NAME containing CONTENTS."
 | ||
|   (computed-file file-name
 | ||
|                  (with-imported-modules '((guix build utils))
 | ||
|                    #~(begin
 | ||
|                        (use-modules (guix build utils))
 | ||
| 
 | ||
|                        (define rules.d
 | ||
|                          (string-append #$output "/lib/udev/rules.d"))
 | ||
| 
 | ||
|                        (mkdir-p rules.d)
 | ||
|                        (call-with-output-file
 | ||
|                            (string-append rules.d "/" #$file-name)
 | ||
|                          (lambda (port)
 | ||
|                            (display #$contents port)))))))
 | ||
| 
 | ||
| (define kvm-udev-rule
 | ||
|   ;; Return a directory with a udev rule that changes the group of /dev/kvm to
 | ||
|   ;; "kvm" and makes it #o660.  Apparently QEMU-KVM used to ship this rule,
 | ||
|   ;; but now we have to add it by ourselves.
 | ||
| 
 | ||
|   ;; Build users are part of the "kvm" group, so we can fearlessly make
 | ||
|   ;; /dev/kvm 660 (see <http://bugs.gnu.org/18994>, for background.)
 | ||
|   (udev-rule "90-kvm.rules"
 | ||
|              "KERNEL==\"kvm\", GROUP=\"kvm\", MODE=\"0660\"\n"))
 | ||
| 
 | ||
| (define udev-shepherd-service
 | ||
|   ;; Return a <shepherd-service> for UDEV with RULES.
 | ||
|   (match-lambda
 | ||
|     (($ <udev-configuration> udev rules)
 | ||
|      (let* ((rules     (udev-rules-union (cons* udev kvm-udev-rule rules)))
 | ||
|             (udev.conf (computed-file "udev.conf"
 | ||
|                                       #~(call-with-output-file #$output
 | ||
|                                           (lambda (port)
 | ||
|                                             (format port
 | ||
|                                                     "udev_rules=\"~a/lib/udev/rules.d\"\n"
 | ||
|                                                     #$rules))))))
 | ||
|        (list
 | ||
|         (shepherd-service
 | ||
|          (provision '(udev))
 | ||
| 
 | ||
|          ;; Udev needs /dev to be a 'devtmpfs' mount so that new device nodes can
 | ||
|          ;; be added: see
 | ||
|          ;; <http://www.linuxfromscratch.org/lfs/view/development/chapter07/udev.html>.
 | ||
|          (requirement '(root-file-system))
 | ||
| 
 | ||
|          (documentation "Populate the /dev directory, dynamically.")
 | ||
|          (start #~(lambda ()
 | ||
|                     (define find
 | ||
|                       (@ (srfi srfi-1) find))
 | ||
| 
 | ||
|                     (define udevd
 | ||
|                       ;; Choose the right 'udevd'.
 | ||
|                       (find file-exists?
 | ||
|                             (map (lambda (suffix)
 | ||
|                                    (string-append #$udev suffix))
 | ||
|                                  '("/libexec/udev/udevd" ;udev
 | ||
|                                    "/sbin/udevd"))))     ;eudev
 | ||
| 
 | ||
|                     (define (wait-for-udevd)
 | ||
|                       ;; Wait until someone's listening on udevd's control
 | ||
|                       ;; socket.
 | ||
|                       (let ((sock (socket AF_UNIX SOCK_SEQPACKET 0)))
 | ||
|                         (let try ()
 | ||
|                           (catch 'system-error
 | ||
|                             (lambda ()
 | ||
|                               (connect sock PF_UNIX "/run/udev/control")
 | ||
|                               (close-port sock))
 | ||
|                             (lambda args
 | ||
|                               (format #t "waiting for udevd...~%")
 | ||
|                               (usleep 500000)
 | ||
|                               (try))))))
 | ||
| 
 | ||
|                     ;; Allow udev to find the modules.
 | ||
|                     (setenv "LINUX_MODULE_DIRECTORY"
 | ||
|                             "/run/booted-system/kernel/lib/modules")
 | ||
| 
 | ||
|                     ;; The first one is for udev, the second one for eudev.
 | ||
|                     (setenv "UDEV_CONFIG_FILE" #$udev.conf)
 | ||
|                     (setenv "EUDEV_RULES_DIRECTORY"
 | ||
|                             #$(file-append rules "/lib/udev/rules.d"))
 | ||
| 
 | ||
|                     (let ((pid (primitive-fork)))
 | ||
|                       (case pid
 | ||
|                         ((0)
 | ||
|                          (exec-command (list udevd)))
 | ||
|                         (else
 | ||
|                          ;; Wait until udevd is up and running.  This
 | ||
|                          ;; appears to be needed so that the events
 | ||
|                          ;; triggered below are actually handled.
 | ||
|                          (wait-for-udevd)
 | ||
| 
 | ||
|                          ;; Trigger device node creation.
 | ||
|                          (system* #$(file-append udev "/bin/udevadm")
 | ||
|                                   "trigger" "--action=add")
 | ||
| 
 | ||
|                          ;; Wait for things to settle down.
 | ||
|                          (system* #$(file-append udev "/bin/udevadm")
 | ||
|                                   "settle")
 | ||
|                          pid)))))
 | ||
|          (stop #~(make-kill-destructor))
 | ||
| 
 | ||
|          ;; When halting the system, 'udev' is actually killed by
 | ||
|          ;; 'user-processes', i.e., before its own 'stop' method was called.
 | ||
|          ;; Thus, make sure it is not respawned.
 | ||
|          (respawn? #f)))))))
 | ||
| 
 | ||
| (define udev-service-type
 | ||
|   (service-type (name 'udev)
 | ||
|                 (extensions
 | ||
|                  (list (service-extension shepherd-root-service-type
 | ||
|                                           udev-shepherd-service)))
 | ||
| 
 | ||
|                 (compose concatenate)           ;concatenate the list of rules
 | ||
|                 (extend (lambda (config rules)
 | ||
|                           (match config
 | ||
|                             (($ <udev-configuration> udev initial-rules)
 | ||
|                              (udev-configuration
 | ||
|                               (udev udev)
 | ||
|                               (rules (append initial-rules rules)))))))))
 | ||
| 
 | ||
| (define* (udev-service #:key (udev eudev) (rules '()))
 | ||
|   "Run @var{udev}, which populates the @file{/dev} directory dynamically.  Get
 | ||
| extra rules from the packages listed in @var{rules}."
 | ||
|   (service udev-service-type
 | ||
|            (udev-configuration (udev udev) (rules rules))))
 | ||
| 
 | ||
| (define swap-service-type
 | ||
|   (shepherd-service-type
 | ||
|    'swap
 | ||
|    (lambda (device)
 | ||
|      (define requirement
 | ||
|        (if (string-prefix? "/dev/mapper/" device)
 | ||
|            (list (symbol-append 'device-mapping-
 | ||
|                                 (string->symbol (basename device))))
 | ||
|            '()))
 | ||
| 
 | ||
|      (shepherd-service
 | ||
|       (provision (list (symbol-append 'swap- (string->symbol device))))
 | ||
|       (requirement `(udev ,@requirement))
 | ||
|       (documentation "Enable the given swap device.")
 | ||
|       (start #~(lambda ()
 | ||
|                  (restart-on-EINTR (swapon #$device))
 | ||
|                  #t))
 | ||
|       (stop #~(lambda _
 | ||
|                 (restart-on-EINTR (swapoff #$device))
 | ||
|                 #f))
 | ||
|       (respawn? #f)))))
 | ||
| 
 | ||
| (define (swap-service device)
 | ||
|   "Return a service that uses @var{device} as a swap device."
 | ||
|   (service swap-service-type device))
 | ||
| 
 | ||
| (define-record-type* <gpm-configuration>
 | ||
|   gpm-configuration make-gpm-configuration gpm-configuration?
 | ||
|   (gpm      gpm-configuration-gpm)                ;package
 | ||
|   (options  gpm-configuration-options))           ;list of strings
 | ||
| 
 | ||
| (define gpm-shepherd-service
 | ||
|   (match-lambda
 | ||
|     (($ <gpm-configuration> gpm options)
 | ||
|      (list (shepherd-service
 | ||
|             (requirement '(udev))
 | ||
|             (provision '(gpm))
 | ||
|             (start #~(lambda ()
 | ||
|                        ;; 'gpm' runs in the background and sets a PID file.
 | ||
|                        ;; Note that it requires running as "root".
 | ||
|                        (false-if-exception (delete-file "/var/run/gpm.pid"))
 | ||
|                        (fork+exec-command (list #$(file-append gpm "/sbin/gpm")
 | ||
|                                                 #$@options))
 | ||
| 
 | ||
|                        ;; Wait for the PID file to appear; declare failure if
 | ||
|                        ;; it doesn't show up.
 | ||
|                        (let loop ((i 3))
 | ||
|                          (or (file-exists? "/var/run/gpm.pid")
 | ||
|                              (if (zero? i)
 | ||
|                                  #f
 | ||
|                                  (begin
 | ||
|                                    (sleep 1)
 | ||
|                                    (loop (1- i))))))))
 | ||
| 
 | ||
|             (stop #~(lambda (_)
 | ||
|                       ;; Return #f if successfully stopped.
 | ||
|                       (not (zero? (system* #$(file-append gpm "/sbin/gpm")
 | ||
|                                            "-k"))))))))))
 | ||
| 
 | ||
| (define gpm-service-type
 | ||
|   (service-type (name 'gpm)
 | ||
|                 (extensions
 | ||
|                  (list (service-extension shepherd-root-service-type
 | ||
|                                           gpm-shepherd-service)))))
 | ||
| 
 | ||
| (define* (gpm-service #:key (gpm gpm)
 | ||
|                       (options '("-m" "/dev/input/mice" "-t" "ps2")))
 | ||
|   "Run @var{gpm}, the general-purpose mouse daemon, with the given
 | ||
| command-line @var{options}.  GPM allows users to use the mouse in the console,
 | ||
| notably to select, copy, and paste text.  The default value of @var{options}
 | ||
| uses the @code{ps2} protocol, which works for both USB and PS/2 mice.
 | ||
| 
 | ||
| This service is not part of @var{%base-services}."
 | ||
|   ;; To test in QEMU, use "-usbdevice mouse" and then, in the monitor, use
 | ||
|   ;; "info mice" and "mouse_set X" to use the right mouse.
 | ||
|   (service gpm-service-type
 | ||
|            (gpm-configuration (gpm gpm) (options options))))
 | ||
| 
 | ||
| (define-record-type* <kmscon-configuration>
 | ||
|   kmscon-configuration     make-kmscon-configuration
 | ||
|   kmscon-configuration?
 | ||
|   (kmscon                  kmscon-configuration-kmscon
 | ||
|                            (default kmscon))
 | ||
|   (virtual-terminal        kmscon-configuration-virtual-terminal)
 | ||
|   (login-program           kmscon-configuration-login-program
 | ||
|                            (default (file-append shadow "/bin/login")))
 | ||
|   (login-arguments         kmscon-configuration-login-arguments
 | ||
|                            (default '("-p")))
 | ||
|   (hardware-acceleration?  kmscon-configuration-hardware-acceleration?
 | ||
|                            (default #f))) ; #t causes failure
 | ||
| 
 | ||
| (define kmscon-service-type
 | ||
|   (shepherd-service-type
 | ||
|    'kmscon
 | ||
|    (lambda (config)
 | ||
|      (let ((kmscon (kmscon-configuration-kmscon config))
 | ||
|            (virtual-terminal (kmscon-configuration-virtual-terminal config))
 | ||
|            (login-program (kmscon-configuration-login-program config))
 | ||
|            (login-arguments (kmscon-configuration-login-arguments config))
 | ||
|            (hardware-acceleration? (kmscon-configuration-hardware-acceleration? config)))
 | ||
| 
 | ||
|        (define kmscon-command
 | ||
|          #~(list
 | ||
|             #$(file-append kmscon "/bin/kmscon") "--login"
 | ||
|             "--vt" #$virtual-terminal
 | ||
|             #$@(if hardware-acceleration? '("--hwaccel") '())
 | ||
|             "--" #$login-program #$@login-arguments))
 | ||
| 
 | ||
|        (shepherd-service
 | ||
|         (documentation "kmscon virtual terminal")
 | ||
|         (requirement '(user-processes udev dbus-system))
 | ||
|         (provision (list (symbol-append 'term- (string->symbol virtual-terminal))))
 | ||
|         (start #~(make-forkexec-constructor #$kmscon-command))
 | ||
|         (stop #~(make-kill-destructor)))))))
 | ||
| 
 | ||
| 
 | ||
| (define %base-services
 | ||
|   ;; Convenience variable holding the basic services.
 | ||
|   (list (login-service)
 | ||
| 
 | ||
|         (service console-font-service-type
 | ||
|                  (map (lambda (tty)
 | ||
|                         (cons tty %default-console-font))
 | ||
|                       '("tty1" "tty2" "tty3" "tty4" "tty5" "tty6")))
 | ||
| 
 | ||
|         (mingetty-service (mingetty-configuration
 | ||
|                            (tty "tty1")))
 | ||
|         (mingetty-service (mingetty-configuration
 | ||
|                            (tty "tty2")))
 | ||
|         (mingetty-service (mingetty-configuration
 | ||
|                            (tty "tty3")))
 | ||
|         (mingetty-service (mingetty-configuration
 | ||
|                            (tty "tty4")))
 | ||
|         (mingetty-service (mingetty-configuration
 | ||
|                            (tty "tty5")))
 | ||
|         (mingetty-service (mingetty-configuration
 | ||
|                            (tty "tty6")))
 | ||
| 
 | ||
|         (service static-networking-service-type
 | ||
|                  (list (static-networking (interface "lo")
 | ||
|                                           (ip "127.0.0.1")
 | ||
|                                           (provision '(loopback)))))
 | ||
|         (syslog-service)
 | ||
|         (urandom-seed-service)
 | ||
|         (guix-service)
 | ||
|         (nscd-service)
 | ||
| 
 | ||
|         ;; The LVM2 rules are needed as soon as LVM2 or the device-mapper is
 | ||
|         ;; used, so enable them by default.  The FUSE and ALSA rules are
 | ||
|         ;; less critical, but handy.
 | ||
|         (udev-service #:rules (list lvm2 fuse alsa-utils crda))
 | ||
| 
 | ||
|         (service special-files-service-type
 | ||
|                  `(("/bin/sh" ,(file-append (canonical-package bash)
 | ||
|                                             "/bin/sh"))))))
 | ||
| 
 | ||
| ;;; base.scm ends here
 |