;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2015, 2016 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2016 Chris Marusich <cmmarusich@gmail.com>
;;;
;;; This file is part of GNU Guix.
;;;
#:use-module (guix profiles)
#:use-module (guix sets)
#:use-module (guix ui)
+ #:use-module (guix modules)
#:use-module (gnu packages base)
#:use-module (gnu packages bash)
#:use-module (srfi srfi-1)
#:use-module (ice-9 match)
#:export (service-extension
service-extension?
+ service-extension-target
+ service-extension-compute
service-type
service-type?
service-kind
service-parameters
+ simple-service
modify-services
service-back-edges
fold-services
setuid-program-service-type
profile-service-type
firmware-service-type
+ gc-root-service-type
%boot-service
%activation-service
(type service-kind)
(parameters service-parameters))
+(define (simple-service name target value)
+ "Return a service that extends TARGET with VALUE. This works by creating a
+singleton service type NAME, of which the returned service is an instance."
+ (let* ((extension (service-extension target identity))
+ (type (service-type (name name)
+ (extensions (list extension)))))
+ (service type value)))
(define-syntax %modify-service
(syntax-rules (=>)
(define-syntax modify-services
(syntax-rules ()
- "Modify the services listed in SERVICES according to CLAUSES. Each clause
-must have the form:
+ "Modify the services listed in SERVICES according to CLAUSES and return
+the resulting list of services. Each clause must have the form:
(TYPE VARIABLE => BODY)
(define (cleanup-gexp _)
"Return as a monadic value a gexp to clean up /tmp and similar places upon
boot."
- (define %modules
- '((guix build utils)))
-
- (mlet %store-monad ((modules (imported-modules %modules))
- (compiled (compiled-modules %modules)))
- (return #~(begin
- (eval-when (expand load eval)
- ;; Make sure 'use-modules' below succeeds.
- (set! %load-path (cons #$modules %load-path))
- (set! %load-compiled-path
- (cons #$compiled %load-compiled-path)))
-
- (use-modules (guix build utils))
-
- ;; Clean out /tmp and /var/run.
- ;;
- ;; XXX This needs to happen before service activations, so it
- ;; has to be here, but this also implicitly assumes that /tmp
- ;; and /var/run are on the root partition.
- (letrec-syntax ((fail-safe (syntax-rules ()
- ((_ exp rest ...)
- (begin
- (catch 'system-error
- (lambda () exp)
- (const #f))
- (fail-safe rest ...)))
- ((_)
- #t))))
- ;; Ignore I/O errors so the system can boot.
- (fail-safe
- (delete-file-recursively "/tmp")
- (delete-file-recursively "/var/run")
- (mkdir "/tmp")
- (chmod "/tmp" #o1777)
- (mkdir "/var/run")
- (chmod "/var/run" #o755)))))))
+ (with-monad %store-monad
+ (with-imported-modules '((guix build utils))
+ (return #~(begin
+ (use-modules (guix build utils))
+
+ ;; Clean out /tmp and /var/run.
+ ;;
+ ;; XXX This needs to happen before service activations, so it
+ ;; has to be here, but this also implicitly assumes that /tmp
+ ;; and /var/run are on the root partition.
+ (letrec-syntax ((fail-safe (syntax-rules ()
+ ((_ exp rest ...)
+ (begin
+ (catch 'system-error
+ (lambda () exp)
+ (const #f))
+ (fail-safe rest ...)))
+ ((_)
+ #t))))
+ ;; Ignore I/O errors so the system can boot.
+ (fail-safe
+ (delete-file-recursively "/tmp")
+ (delete-file-recursively "/var/run")
+ (mkdir "/tmp")
+ (chmod "/tmp" #o1777)
+ (mkdir "/var/run")
+ (chmod "/var/run" #o755))))))))
(define cleanup-service-type
;; Service that cleans things up in /tmp and similar.
one)
(_
(computed-file name
- #~(begin
- (use-modules (guix build union))
- (union-build #$output '#$things))
- #:modules '((guix build union))))))
+ (with-imported-modules '((guix build union))
+ #~(begin
+ (use-modules (guix build union))
+ (union-build #$output '#$things)))))))
(define* (activation-service->script service)
"Return as a monadic value the activation script for SERVICE, a service of
(define (activation-script gexps)
"Return the system's activation script, which evaluates GEXPS."
- (define %modules
- '((gnu build activation)
- (gnu build linux-boot)
- (gnu build linux-modules)
- (gnu build file-systems)
- (guix build utils)
- (guix build syscalls)
- (guix elf)))
-
(define (service-activations)
;; Return the activation scripts for SERVICES.
(mapm %store-monad
(cut gexp->file "activate-service" <>)
gexps))
- (mlet* %store-monad ((actions (service-activations))
- (modules (imported-modules %modules))
- (compiled (compiled-modules %modules)))
+ (mlet* %store-monad ((actions (service-activations)))
(gexp->file "activate"
- #~(begin
- (eval-when (expand load eval)
- ;; Make sure 'use-modules' below succeeds.
- (set! %load-path (cons #$modules %load-path))
- (set! %load-compiled-path
- (cons #$compiled %load-compiled-path)))
+ (with-imported-modules (source-module-closure
+ '((gnu build activation)))
+ #~(begin
+ (use-modules (gnu build activation))
- (use-modules (gnu build activation))
+ ;; Make sure /bin/sh is valid and current.
+ (activate-/bin/sh
+ (string-append #$(canonical-package bash) "/bin/sh"))
- ;; Make sure /bin/sh is valid and current.
- (activate-/bin/sh
- (string-append #$(canonical-package bash) "/bin/sh"))
+ ;; Make sure the user accounting database exists. If it
+ ;; does not exist, 'setutxent' does not create it and
+ ;; thus there is no accounting at all.
+ (close-port (open-file "/var/run/utmpx" "a0"))
- ;; Run the services' activation snippets.
- ;; TODO: Use 'load-compiled'.
- (for-each primitive-load '#$actions)
+ ;; Set up /run/current-system. Among other things this
+ ;; sets up locales, which the activation snippets
+ ;; executed below may expect.
+ (activate-current-system)
- ;; Set up /run/current-system.
- (activate-current-system)))))
+ ;; Run the services' activation snippets.
+ ;; TODO: Use 'load-compiled'.
+ (for-each primitive-load '#$actions))))))
(define (gexps->activation-gexp gexps)
"Return a gexp that runs the activation script containing GEXPS."
(define (packages->profile-entry packages)
"Return a system entry for the profile containing PACKAGES."
(mlet %store-monad ((profile (profile-derivation
- (manifest (map package->manifest-entry
- (delete-duplicates packages eq?))))))
+ (packages->manifest
+ (delete-duplicates packages eq?)))))
(return `(("profile" ,profile)))))
(define profile-service-type
(compose concatenate)
(extend append)))
+(define (gc-roots->system-entry roots)
+ "Return an entry in the system's output containing symlinks to ROOTS."
+ (mlet %store-monad ((entry (gexp->derivation
+ "gc-roots"
+ #~(let ((roots '#$roots))
+ (mkdir #$output)
+ (chdir #$output)
+ (for-each symlink
+ roots
+ (map number->string
+ (iota (length roots))))))))
+ (return (if (null? roots)
+ '()
+ `(("gc-roots" ,entry))))))
+
+(define gc-root-service-type
+ ;; A service to associate extra garbage-collector roots to the system. This
+ ;; is a simple hack that guarantees that the system retains references to
+ ;; the given list of roots. Roots must be "lowerable" objects like
+ ;; packages, or derivations.
+ (service-type (name 'gc-roots)
+ (extensions
+ (list (service-extension system-service-type
+ gc-roots->system-entry)))
+ (compose concatenate)
+ (extend append)))
+
\f
;;;
;;; Service folding.