aboutsummaryrefslogtreecommitdiff
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2016 David Craven <david@craven.ch>
;;; Copyright © 2017 Clément Lassieur <clement@lassieur.org>
;;;
;;; 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 spice)
  #:use-module (gnu packages spice)
  #:use-module (gnu services)
  #:use-module (gnu services shepherd)
  #:use-module (guix deprecation)
  #:use-module (guix gexp)
  #:use-module (guix records)
  #:export (spice-vdagent-configuration
            spice-vdagent-configuration?
            spice-vdagent-service-type
            spice-vdagent-service))  ; deprecated

(define-record-type* <spice-vdagent-configuration>
  spice-vdagent-configuration make-spice-vdagent-configuration
  spice-vdagent-configuration?
  (spice-vdagent spice-vdagent-configuration-spice-vdagent
                 (default spice-vdagent)))

(define (spice-vdagent-shepherd-service config)
  "Return a <shepherd-service> for spice-vdagentd with CONFIG."
  (define spice-vdagent (spice-vdagent-configuration-spice-vdagent config))

  (define spice-vdagentd-command
    (list
     (file-append spice-vdagent "/sbin/spice-vdagentd")
     "-x"))

  (list
   (shepherd-service
    (documentation "Spice vdagentd service")
    (requirement '(dbus-system))
    (provision '(spice-vdagentd))
    (start #~(lambda args
               ;; spice-vdagentd supports being activated upon the client
               ;; connecting to its socket; when not using such feature, the
               ;; socket should not exist before vdagentd creates it itself.
               (mkdir-p "/run/spice-vdagentd")
               (false-if-exception
                (delete-file "/run/spice-vdagentd/spice-vdagent-sock"))
               (fork+exec-command '#$spice-vdagentd-command)))
    (stop #~(make-kill-destructor)))))

(define spice-vdagent-profile
  (compose list spice-vdagent-configuration-spice-vdagent))

(define spice-vdagent-service-type
  (service-type
   (name 'spice-vdagent)
   (default-value (spice-vdagent-configuration))
   (extensions
    (list (service-extension shepherd-root-service-type
                             spice-vdagent-shepherd-service)
          (service-extension profile-service-type
                             spice-vdagent-profile)))
   (description "Start the @command{vdagentd} and @command{vdagent} daemons
from the @code{spice-vdagent} package to enable window resizing and clipboard
sharing for @acronym{VM, virtual machine} guests.")))

(define-deprecated (spice-vdagent-service
                    #:optional (config (spice-vdagent-configuration)))
  "Start the @command{vdagentd} and @command{vdagent} daemons
from @var{spice-vdagent} to enable guest window resizing and
clipboard sharing."
  (service spice-vdagent-service-type config))
elf")) grafted) properties))))) (test-assert "graft-derivation, grafted item uses a different name" (let* ((build `(begin (mkdir %output) (chdir %output) (symlink %output "self") (symlink ,%bash "sh"))) (orig (build-expression->derivation %store "grafted" build #:inputs `(("a" ,%bash)))) (repl (add-text-to-store %store "BaSH" "fake bash")) (grafted (graft-derivation %store orig (list (graft (origin %bash) (replacement repl)))))) (and (build-derivations %store (list grafted)) (let ((grafted (derivation->output-path grafted))) (and (string=? (readlink (string-append grafted "/sh")) repl) (string=? (readlink (string-append grafted "/self")) grafted)))))) ;; Make sure 'derivation-file-name' always gets to see an absolute file name. (fluid-set! %file-port-name-canonicalization 'absolute) (test-assert "graft-derivation, grafted item is an indirect dependency" (let* ((build `(begin (mkdir %output) (chdir %output) (symlink %output "self") (call-with-output-file "text" (lambda (output) (format output "foo/~a/bar" ,%mkdir))) (symlink ,%bash "sh"))) (dep (build-expression->derivation %store "dep" build #:inputs `(("a" ,%bash) ("b" ,%mkdir)))) (orig (build-expression->derivation %store "thing" '(symlink (assoc-ref %build-inputs "dep") %output) #:inputs `(("dep" ,dep)))) (one (add-text-to-store %store "bash" "fake bash")) (two (build-expression->derivation %store "mkdir" '(call-with-output-file %output (lambda (port) (display "fake mkdir" port))))) (grafted (graft-derivation %store orig (list (graft (origin %bash) (replacement one)) (graft (origin %mkdir) (replacement two)))))) (and (build-derivations %store (list grafted)) (let* ((two (derivation->output-path two)) (grafted (derivation->output-path grafted)) (dep (readlink grafted))) (and (string=? (format #f "foo/~a/bar" two) (call-with-input-file (string-append dep "/text") get-string-all)) (string=? (readlink (string-append dep "/sh")) one) (string=? (readlink (string-append dep "/self")) dep) (equal? (references %store grafted) (list dep)) (lset= string=? (list one two dep) (references %store dep))))))) (test-assert "graft-derivation, preserve empty directories" (run-with-store %store (mlet* %store-monad ((fake (text-file "bash" "Fake bash.")) (graft -> (graft (origin %bash) (replacement fake))) (drv (gexp->derivation "to-graft" (with-imported-modules '((guix build utils)) #~(begin (use-modules (guix build utils)) (mkdir-p (string-append #$output "/a/b/c/d")) (symlink #$%bash (string-append #$output "/bash")))))) (grafted ((store-lift graft-derivation) drv (list graft))) (_ (built-derivations (list grafted))) (out -> (derivation->output-path grafted))) (return (and (string=? (readlink (string-append out "/bash")) fake) (file-is-directory? (string-append out "/a/b/c/d"))))))) (test-assert "graft-derivation, no dependencies on grafted output" (run-with-store %store (mlet* %store-monad ((fake (text-file "bash" "Fake bash.")) (graft -> (graft (origin %bash) (replacement fake))) (drv (gexp->derivation "foo" #~(mkdir #$output))) (grafted ((store-lift graft-derivation) drv (list graft)))) (return (eq? grafted drv))))) (test-assert "graft-derivation, multiple outputs" (let* ((build `(begin (symlink (assoc-ref %build-inputs "a") (assoc-ref %outputs "one")) (symlink (assoc-ref %outputs "one") (assoc-ref %outputs "two")))) (orig (build-expression->derivation %store "grafted" build #:inputs `(("a" ,%bash)) #:outputs '("one" "two"))) (repl (add-text-to-store %store "bash" "fake bash")) (grafted (graft-derivation %store orig (list (graft (origin %bash) (replacement repl)))))) (and (build-derivations %store (list grafted)) (let ((one (derivation->output-path grafted "one")) (two (derivation->output-path grafted "two"))) (and (string=? (readlink one) repl) (string=? (readlink two) one)))))) (test-assert "graft-derivation, replaced derivation has multiple outputs" ;; Here we have a replacement just for output "one" of P1 and not for the ;; other output. Make sure the graft for P1:one correctly applies to the ;; dependents of P1. See <http://bugs.gnu.org/24712>. (let* ((p1 (build-expression->derivation %store "p1" `(let ((one (assoc-ref %outputs "one")) (two (assoc-ref %outputs "two"))) (mkdir one) (mkdir two)) #:outputs '("one" "two"))) (p1r (build-expression->derivation %store "P1" `(let ((other (assoc-ref %outputs "ONE"))) (mkdir other) (call-with-output-file (string-append other "/replacement") (const #t))) #:outputs '("ONE"))) (p2 (build-expression->derivation %store "p2" `(let ((out (assoc-ref %outputs "aaa"))) (mkdir (assoc-ref %outputs "zzz")) (mkdir out) (chdir out) (symlink (assoc-ref %build-inputs "p1:one") "one") (symlink (assoc-ref %build-inputs "p1:two") "two")) #:outputs '("aaa" "zzz") #:inputs `(("p1:one" ,p1 "one") ("p1:two" ,p1 "two")))) (p3 (build-expression->derivation %store "p3" `(symlink (assoc-ref %build-inputs "p2:aaa") (assoc-ref %outputs "out")) #:inputs `(("p2:aaa" ,p2 "aaa") ("p2:zzz" ,p2 "zzz")))) (p1g (graft (origin p1) (origin-output "one") (replacement p1r) (replacement-output "ONE"))) (p3d (graft-derivation %store p3 (list p1g)))) (and (not (find (lambda (input) ;; INPUT should not be P2:zzz since the result of P3 ;; does not depend on it. See ;; <http://bugs.gnu.org/24886>. (and (string=? (derivation-input-path input) (derivation-file-name p2)) (member "zzz" (derivation-input-sub-derivations input)))) (derivation-inputs p3d))) (build-derivations %store (list p3d)) (let ((out (derivation->output-path (pk 'p2d p3d)))) (and (not (string=? (readlink out) (derivation->output-path p2 "aaa"))) (string=? (derivation->output-path p1 "two") (readlink (string-append out "/two"))) (file-exists? (string-append out "/one/replacement"))))))) (test-assert "graft-derivation, multiple outputs need to be replaced" ;; Build a reference graph like this: ;; ;; ,- p2:out --. ;; v v ;; p1:one <---- p1:two ;; | ;; `-> p0 ;; ;; Graft p0r in lieu of p0, and make sure all the paths from the grafted p2 ;; lead to p0r. See <https://issues.guix.gnu.org/66662>. (let* ((p0 (build-expression->derivation %store "p0" '(mkdir (assoc-ref %outputs "out")))) (p0r (build-expression->derivation %store "P0" '(let ((out (assoc-ref %outputs "out"))) (mkdir out) (call-with-output-file (string-append out "/replacement") (const #t))))) (p1 (build-expression->derivation %store "p1" `(let ((one (assoc-ref %outputs "one")) (two (assoc-ref %outputs "two")) (p0 (assoc-ref %build-inputs "p0"))) (mkdir one) (mkdir two) (symlink p0 (string-append one "/p0")) (symlink one (string-append two "/link"))) #:inputs `(("p0" ,p0)) #:outputs '("one" "two"))) (p2 (build-expression->derivation %store "p2" `(let ((out (assoc-ref %outputs "out"))) (mkdir out) (chdir out) (symlink (assoc-ref %build-inputs "p1:one") "one") (symlink (assoc-ref %build-inputs "p1:two") "two")) #:inputs `(("p1:one" ,p1 "one") ("p1:two" ,p1 "two")))) (p0g (list (graft (origin p0) (replacement p0r)))) (p2d (graft-derivation %store p2 p0g))) (build-derivations %store (list p2d)) (let ((out (derivation->output-path (pk 'p2d p2d)))) (equal? (stat (string-append out "/one/p0/replacement")) (stat (string-append out "/two/link/p0/replacement")))))) (test-assert "graft-derivation with #:outputs" ;; Call 'graft-derivation' with a narrowed set of outputs passed as ;; #:outputs. (let* ((p1 (build-expression->derivation %store "p1" `(let ((one (assoc-ref %outputs "one")) (two (assoc-ref %outputs "two"))) (mkdir one) (mkdir two)) #:outputs '("one" "two"))) (p1r (build-expression->derivation %store "P1" `(let ((other (assoc-ref %outputs "ONE"))) (mkdir other) (call-with-output-file (string-append other "/replacement") (const #t))) #:outputs '("ONE"))) (p2 (build-expression->derivation %store "p2" `(let ((aaa (assoc-ref %outputs "aaa")) (zzz (assoc-ref %outputs "zzz"))) (mkdir zzz) (chdir zzz) (mkdir aaa) (chdir aaa) (symlink (assoc-ref %build-inputs "p1:two") "two")) #:outputs '("aaa" "zzz") #:inputs `(("p1:one" ,p1 "one") ("p1:two" ,p1 "two")))) (p1g (graft (origin p1) (origin-output "one") (replacement p1r) (replacement-output "ONE"))) (p2g (graft-derivation %store p2 (list p1g) #:outputs '("aaa")))) ;; P2:aaa depends on P1:two, but not on P1:one, so nothing to graft. (eq? p2g p2))) (test-equal "graft-derivation, unused outputs not depended on" '("aaa") ;; Make sure that the result of 'graft-derivation' does not pull outputs ;; that are irrelevant to the grafting process. See ;; <http://bugs.gnu.org/24886>. (let* ((p1 (build-expression->derivation %store "p1" `(let ((one (assoc-ref %outputs "one")) (two (assoc-ref %outputs "two"))) (mkdir one) (mkdir two)) #:outputs '("one" "two"))) (p1r (build-expression->derivation %store "P1" `(let ((other (assoc-ref %outputs "ONE"))) (mkdir other) (call-with-output-file (string-append other "/replacement") (const #t))) #:outputs '("ONE"))) (p2 (build-expression->derivation %store "p2" `(let ((aaa (assoc-ref %outputs "aaa")) (zzz (assoc-ref %outputs "zzz"))) (mkdir zzz) (chdir zzz) (symlink (assoc-ref %build-inputs "p1:two") "two") (mkdir aaa) (chdir aaa) (symlink (assoc-ref %build-inputs "p1:one") "one")) #:outputs '("aaa" "zzz") #:inputs `(("p1:one" ,p1 "one") ("p1:two" ,p1 "two")))) (p1g (graft (origin p1) (origin-output "one") (replacement p1r) (replacement-output "ONE"))) (p2g (graft-derivation %store p2 (list p1g) #:outputs '("aaa")))) ;; Here P2G should only depend on P1:one and P1R:one; it must not depend ;; on P1:two or P1R:two since these are unused in the grafting process. (and (not (eq? p2g p2)) (let* ((inputs (derivation-inputs p2g)) (match-input (lambda (drv) (lambda (input) (string=? (derivation-input-path input) (derivation-file-name drv))))) (p1-inputs (filter (match-input p1) inputs)) (p1r-inputs (filter (match-input p1r) inputs)) (p2-inputs (filter (match-input p2) inputs))) (and (equal? p1-inputs (list (derivation-input p1 '("one")))) (equal? p1r-inputs (list (derivation-input p1r '("ONE")))) (equal? p2-inputs (list (derivation-input p2 '("aaa")))) (derivation-output-names p2g)))))) (test-assert "graft-derivation, renaming" ;<http://bugs.gnu.org/23132> (let* ((build `(begin (use-modules (guix build utils)) (mkdir-p (string-append (assoc-ref %outputs "out") "/" (assoc-ref %build-inputs "in"))))) (orig (build-expression->derivation %store "thing-to-graft" build #:modules '((guix build utils)) #:inputs `(("in" ,%bash)))) (repl (add-text-to-store %store "bash" "fake bash")) (grafted (graft-derivation %store orig (list (graft (origin %bash) (replacement repl)))))) (and (build-derivations %store (list grafted)) (let ((out (derivation->output-path grafted))) (file-is-directory? (string-append out "/" repl)))))) (test-assert "graft-derivation, grafts are not shadowed" ;; We build a DAG as below, where dotted arrows represent replacements and ;; solid arrows represent dependencies: ;; ;; P1 ·············> P1R ;; |\__________________. ;; v v ;; P2 ·············> P2R ;; | ;; v ;; P3 ;; ;; We want to make sure that the two grafts we want to apply to P3 are ;; honored and not shadowed by other computed grafts. (let* ((p1 (build-expression->derivation %store "p1" '(mkdir (assoc-ref %outputs "out")))) (p1r (build-expression->derivation %store "P1" '(let ((out (assoc-ref %outputs "out"))) (mkdir out) (call-with-output-file (string-append out "/replacement") (const #t))))) (p2 (build-expression->derivation %store "p2" `(let ((out (assoc-ref %outputs "out"))) (mkdir out) (chdir out) (symlink (assoc-ref %build-inputs "p1") "p1")) #:inputs `(("p1" ,p1)))) (p2r (build-expression->derivation %store "P2" `(let ((out (assoc-ref %outputs "out"))) (mkdir out) (chdir out) (symlink (assoc-ref %build-inputs "p1") "p1") (call-with-output-file (string-append out "/replacement") (const #t))) #:inputs `(("p1" ,p1)))) (p3 (build-expression->derivation %store "p3" `(let ((out (assoc-ref %outputs "out"))) (mkdir out) (chdir out) (symlink (assoc-ref %build-inputs "p2") "p2")) #:inputs `(("p2" ,p2)))) (p1g (graft (origin p1) (replacement p1r))) (p2g (graft (origin p2) (replacement (graft-derivation %store p2r (list p1g))))) (p3d (graft-derivation %store p3 (list p1g p2g)))) (and (build-derivations %store (list p3d)) (let ((out (derivation->output-path (pk p3d)))) ;; Make sure OUT refers to the replacement of P2, which in turn ;; refers to the replacement of P1, as specified by P1G and P2G. ;; It used to be the case that P2G would be shadowed by a simple ;; P2->P2R graft, which is not what we want. (and (file-exists? (string-append out "/p2/replacement")) (file-exists? (string-append out "/p2/p1/replacement"))))))) (define buffer-size ;; Must be equal to REQUEST-SIZE in 'replace-store-references'. (expt 2 20)) (test-equal "replace-store-references, <http://bugs.gnu.org/28212>" (string-append (make-string (- buffer-size 47) #\a) "/gnu/store/" (make-string 32 #\8) "-SoMeTHiNG" (list->string (map integer->char (iota 77 33)))) ;; Create input data where the right-hand-size of the dash ("-something" ;; here) goes beyond the end of the internal buffer of ;; 'replace-store-references'. (let* ((content (string-append (make-string (- buffer-size 47) #\a) "/gnu/store/" (make-string 32 #\7) "-something" (list->string (map integer->char (iota 77 33))))) (replacement (alist->vhash `((,(make-string 32 #\7) . ,(string->utf8 (string-append (make-string 32 #\8) "-SoMeTHiNG"))))))) (call-with-output-string (lambda (output) ((@@ (guix build graft) replace-store-references) (open-input-string content) output replacement "/gnu/store"))))) (define (insert-nuls char-size str) (string-join (map string (string->list str)) (make-string (- char-size 1) #\nul))) (define (nuls-to-underscores s) (string-replace-substring s "\0" "_")) (define (annotate-buffer-boundary s) (string-append (string-take s buffer-size) "|" (string-drop s buffer-size))) (define (abbreviate-leading-fill s) (let ((s* (string-trim s #\=))) (format #f "[~a =s]~a" (- (string-length s) (string-length s*)) s*))) (define (prettify-for-display s) (abbreviate-leading-fill (annotate-buffer-boundary (nuls-to-underscores s)))) (define (two-sample-refs-with-gap char-size1 char-size2 gap offset char1 name1 char2 name2) (string-append (make-string (- buffer-size offset) #\=) (insert-nuls char-size1 (string-append "/gnu/store/" (make-string 32 char1) name1)) gap (insert-nuls char-size2 (string-append "/gnu/store/" (make-string 32 char2) name2)) (list->string (map integer->char (iota 77 33))))) (define (sample-map-entry old-char new-char new-name) (cons (make-string 32 old-char) (string->utf8 (string-append (make-string 32 new-char) new-name)))) (define (test-two-refs-with-gap char-size1 char-size2 gap offset) (test-equal (format #f "test-two-refs-with-gap, char-sizes ~a ~a, gap ~s, offset ~a" char-size1 char-size2 gap offset) (prettify-for-display (two-sample-refs-with-gap char-size1 char-size2 gap offset #\6 "-BlahBlaH" #\8"-SoMeTHiNG")) (prettify-for-display (let* ((content (two-sample-refs-with-gap char-size1 char-size2 gap offset #\5 "-blahblah" #\7 "-something")) (replacement (alist->vhash (list (sample-map-entry #\5 #\6 "-BlahBlaH") (sample-map-entry #\7 #\8 "-SoMeTHiNG"))))) (call-with-output-string (lambda (output) ((@@ (guix build graft) replace-store-references) (open-input-string content) output replacement "/gnu/store"))))))) (for-each (lambda (char-size1) (for-each (lambda (char-size2) (for-each (lambda (gap) (for-each (lambda (offset) (test-two-refs-with-gap char-size1 char-size2 gap offset)) ;; offsets to test (map (lambda (i) (+ i (* 40 char-size1))) (iota 30)))) ;; gaps '("" "-" " " "a"))) ;; char-size2 values to test '(1 2))) ;; char-size1 values to test '(1 2 4)) (test-end)