;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2013, 2014, 2015, 2016, 2018 Ludovic Courtès ;;; Copyright © 2017 Clément Lassieur ;;; ;;; 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 . (define-module (gnu services shepherd) #:use-module (guix ui) #:use-module (guix sets) #:use-module (guix gexp) #:use-module (guix store) #:use-modul
aboutsummaryrefslogtreecommitdiff
blob: 8d44b9e0e293f02ec6775e1402e41eec59731e14 (about) (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2015 Ricardo Wurmus <rekado@elephly.net>
;;; Copyright © 2016 Ben Woodcroft <donttrustben@gmail.com>
;;;
;;; 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 (test-import-utils)
  #:use-module (guix tests)
  #:use-module (guix import utils)
  #:use-module ((guix licenses) #:prefix license:)
  #:use-module (srfi srfi-64))

(test-begin "import-utils")

(test-equal "beautify-description: use double spacing"
  "This is a package.  It is great.  Trust me Mr.  Hendrix."
  (beautify-description
   "This is a package. It is great. Trust me Mr. Hendrix."))

(test-equal "beautify-description: transform fragment into sentence"
  "This package provides a function to establish world peace"
  (beautify-description "A function to establish world peace"))

(test-equal "license->symbol"
  'license:lgpl2.0
  (license->symbol license:lgpl2.0))

(test-end "import-utils")
ce-provision service))) (define (assert-valid-graph services) "Raise an error if SERVICES does not define a valid shepherd service graph, for instance if a service requires a nonexistent service, or if more than one service uses a given name. These are constraints that shepherd's 'register-service' verifies but we'd better verify them here statically than wait until PID 1 halts with an assertion failure." (define provisions ;; The set of provisions (symbols). Bail out if a symbol is given more ;; than once. (fold (lambda (service set) (define (assert-unique symbol) (when (set-contains? set symbol) (raise (condition (&message (message (format #f (G_ "service '~a' provided more than once") symbol))))))) (for-each assert-unique (shepherd-service-provision service)) (fold set-insert set (shepherd-service-provision service))) (setq 'shepherd) services)) (define (assert-satisfied-requirements service) ;; Bail out if the requirements of SERVICE aren't satisfied. (for-each (lambda (requirement) (unless (set-contains? provisions requirement) (raise (condition (&message (message (format #f (G_ "service '~a' requires '~a', \ which is not provided by any service") (match (shepherd-service-provision service) ((head . _) head) (_ service)) requirement))))))) (shepherd-service-requirement service))) (for-each assert-satisfied-requirements services)) (define (shepherd-service-file-name service) "Return the file name where the initialization code for SERVICE is to be stored." (let ((provisions (string-join (map symbol->string (shepherd-service-provision service))))) (string-append "shepherd-" (string-map (match-lambda (#\/ #\-) (#\ #\-) (chr chr)) provisions) ".scm"))) (define (shepherd-service-file service) "Return a file defining SERVICE." (scheme-file (shepherd-service-file-name service) (with-imported-modules %default-imported-modules #~(begin (use-modules #$@(shepherd-service-modules service)) (make #:docstring '#$(shepherd-service-documentation service) #:provides '#$(shepherd-service-provision service) #:requires '#$(shepherd-service-requirement service) #:respawn? '#$(shepherd-service-respawn? service) #:start #$(shepherd-service-start service) #:stop #$(shepherd-service-stop service)))))) (define (shepherd-configuration-file services) "Return the shepherd configuration file for SERVICES." (assert-valid-graph services) (let ((files (map shepherd-service-file services))) (define config #~(begin (use-modules (srfi srfi-34) (system repl error-handling)) ;; Arrange to spawn a REPL if something goes wrong. This is better ;; than a kernel panic. (call-with-error-handling (lambda () (apply register-services (map primitive-load '#$files)) ;; guix-daemon 0.6 aborts if 'PATH' is undefined, so work around ;; it. (setenv "PATH" "/run/current-system/profile/bin") (format #t "starting services...~%") (for-each (lambda (service) ;; In the Shepherd 0.3 the 'start' method can raise ;; '&action-runtime-error' if it fails, so protect ;; against it. (XXX: 'action-runtime-error?' is not ;; exported is 0.3, hence 'service-error?'.) (guard (c ((service-error? c) (format (current-error-port) "failed to start service '~a'~%" service))) (start service))) '#$(append-map shepherd-service-provision (filter shepherd-service-auto-start? services))))))) (scheme-file "shepherd.conf" config))) (define* (shepherd-service-lookup-procedure services #:optional (provision shepherd-service-provision)) "Return a procedure that, when passed a symbol, return the item among SERVICES that provides this symbol. PROVISION must be a one-argument procedure that takes a service and returns the list of symbols it provides." (let ((services (fold (lambda (service result) (fold (cut vhash-consq <> service <>) result (provision service))) vlist-null services))) (lambda (name) (match (vhash-assq name services) ((_ . service) service) (#f #f))))) (define* (shepherd-service-back-edges services #:key (provision shepherd-service-provision) (requirement shepherd-service-requirement)) "Return a procedure that, when given a from SERVICES, returns the list of that depend on it. Use PROVISION and REQUIREMENT as one-argument procedures that return the symbols provided/required by a service." (define provision->service (shepherd-service-lookup-procedure services provision)) (define edges (fold (lambda (service edges) (fold (lambda (requirement edges) (vhash-consq (provision->service requirement) service edges)) edges (requirement service))) vlist-null services)) (lambda (service) (vhash-foldq* cons '() service edges))) (define (shepherd-service-upgrade live target) "Return two values: the subset of LIVE (a list of ) that needs to be unloaded, and the subset of TARGET (a list of ) that needs to be loaded." (define (essential? service) (memq (first (live-service-provision service)) '(root shepherd))) (define lookup-target (shepherd-service-lookup-procedure target shepherd-service-provision)) (define lookup-live (shepherd-service-lookup-procedure live live-service-provision)) (define (running? service) (and=> (lookup-live (shepherd-service-canonical-name service)) live-service-running)) (define (stopped service) (match (lookup-live (shepherd-service-canonical-name service)) (#f #f) (service (and (not (live-service-running service)) service)))) (define live-service-dependents (shepherd-service-back-edges live #:provision live-service-provision #:requirement live-service-requirement)) (define (obsolete? service) (match (lookup-target (first (live-service-provision service))) (#f (every obsolete? (live-service-dependents service))) (_ #f))) (define to-load ;; Only load services that are either new or currently stopped. (remove running? target)) (define to-unload ;; Unload services that are (1) no longer required, or (2) are in TO-LOAD. (remove essential? (append (filter obsolete? live) (filter-map stopped to-load)))) (values to-unload to-load)) ;;; shepherd.scm ends here