(define-module (gnu system vm)
#:use-module (guix config)
#:use-module (guix store)
+ #:use-module (guix gexp)
#:use-module (guix derivations)
#:use-module (guix packages)
#:use-module (guix monads)
((input (and (? string?) (? store-path?) file))
(return `(,input . ,file))))))
-;; An alias to circumvent name clashes.
-(define %imported-modules imported-modules)
+(define %linux-vm-file-systems
+ ;; File systems mounted for 'derivation-in-linux-vm'. The store and /xchg
+ ;; directory are shared with the host over 9p.
+ (list (file-system
+ (mount-point (%store-prefix))
+ (device "store")
+ (type "9p")
+ (needed-for-boot? #t)
+ (options "trans=virtio"))
+ (file-system
+ (mount-point "/xchg")
+ (device "xchg")
+ (type "9p")
+ (needed-for-boot? #t)
+ (options "trans=virtio"))))
(define* (expression->derivation-in-linux-vm name exp
#:key
(system (%current-system))
- (inputs '())
(linux linux-libre)
initrd
(qemu qemu-headless)
(env-vars '())
- (imported-modules
+ (modules
'((guix build vm)
(guix build linux-initrd)
(guix build utils)))
(disk-image-size
(* 100 (expt 2 20))))
"Evaluate EXP in a QEMU virtual machine running LINUX with INITRD (a
-derivation). In the virtual machine, EXP has access to all of INPUTS from the
+derivation). In the virtual machine, EXP has access to all its inputs from the
store; it should put its output files in the `/xchg' directory, which is
copied to the derivation's output when the VM terminates. The virtual machine
runs with MEMORY-SIZE MiB of memory.
When MAKE-DISK-IMAGE? is true, then create a QEMU disk image of
DISK-IMAGE-SIZE bytes and return it.
-IMPORTED-MODULES is the set of modules imported in the execution environment
-of EXP.
+MODULES is the set of modules imported in the execution environment of EXP.
When REFERENCES-GRAPHS is true, it must be a list of file name/store path
pairs, as for `derivation'. The files containing the reference graphs are
made available under the /xchg CIFS share."
- ;; FIXME: Add #:modules parameter, for the 'use-modules' form.
-
- (define input-alist
- (map input->name+output inputs))
-
- (define builder
- ;; Code that launches the VM that evaluates EXP.
- `(let ()
- (use-modules (guix build utils)
- (guix build vm))
-
- (let ((linux (string-append (assoc-ref %build-inputs "linux")
- "/bzImage"))
- (initrd (string-append (assoc-ref %build-inputs "initrd")
- "/initrd"))
- (loader (assoc-ref %build-inputs "loader"))
- (graphs ',(match references-graphs
- (((graph-files . _) ...) graph-files)
- (_ #f))))
-
- (set-path-environment-variable "PATH" '("bin")
- (map cdr %build-inputs))
-
- (load-in-linux-vm loader
- #:output (assoc-ref %outputs "out")
- #:linux linux #:initrd initrd
- #:memory-size ,memory-size
- #:make-disk-image? ,make-disk-image?
- #:disk-image-size ,disk-image-size
- #:references-graphs graphs))))
-
(mlet* %store-monad
- ((input-alist (sequence %store-monad input-alist))
- (module-dir (%imported-modules imported-modules))
- (compiled (compiled-modules imported-modules))
- (exp* -> `(let ((%build-inputs ',input-alist))
- ,exp))
- (user-builder (text-file "builder-in-linux-vm"
- (object->string exp*)))
- (loader (text-file* "linux-vm-loader" ; XXX: use 'sexp-file'
- "(begin (set! %load-path (cons \""
- module-dir "\" %load-path)) "
- "(set! %load-compiled-path (cons \""
- compiled "\" %load-compiled-path))"
- "(primitive-load \"" user-builder "\"))"))
+ ((module-dir (imported-modules modules))
+ (compiled (compiled-modules modules))
+ (user-builder (gexp->file "builder-in-linux-vm" exp))
+ (loader (gexp->file "linux-vm-loader"
+ #~(begin
+ (set! %load-path
+ (cons #$module-dir %load-path))
+ (set! %load-compiled-path
+ (cons #$compiled
+ %load-compiled-path))
+ (primitive-load #$user-builder))))
(coreutils -> (car (assoc-ref %final-inputs "coreutils")))
(initrd (if initrd ; use the default initrd?
(return initrd)
- (qemu-initrd #:guile-modules-in-chroot? #t
- #:mounts `((9p "store" ,(%store-prefix))
- (9p "xchg" "/xchg")))))
- (inputs (lower-inputs `(("qemu" ,qemu)
- ("linux" ,linux)
- ("initrd" ,initrd)
- ("coreutils" ,coreutils)
- ("builder" ,user-builder)
- ("loader" ,loader)
- ,@inputs))))
- (derivation-expression name builder
- ;; TODO: Require the "kvm" feature.
- #:system system
- #:inputs inputs
- #:env-vars env-vars
- #:modules (delete-duplicates
- `((guix build utils)
- (guix build vm)
- (guix build linux-initrd)
- ,@imported-modules))
- #:guile-for-build guile-for-build
- #:references-graphs references-graphs)))
+ (qemu-initrd %linux-vm-file-systems
+ #:guile-modules-in-chroot? #t))))
+
+ (define builder
+ ;; Code that launches the VM that evaluates EXP.
+ #~(begin
+ (use-modules (guix build utils)
+ (guix build vm))
+
+ (let ((inputs '#$(list qemu coreutils))
+ (linux (string-append #$linux "/bzImage"))
+ (initrd (string-append #$initrd "/initrd"))
+ (loader #$loader)
+ (graphs '#$(match references-graphs
+ (((graph-files . _) ...) graph-files)
+ (_ #f))))
+
+ (set-path-environment-variable "PATH" '("bin") inputs)
+
+ (load-in-linux-vm loader
+ #:output #$output
+ #:linux linux #:initrd initrd
+ #:memory-size #$memory-size
+ #:make-disk-image? #$make-disk-image?
+ #:disk-image-size #$disk-image-size
+ #:references-graphs graphs))))
+
+ (gexp->derivation name builder
+ ;; TODO: Require the "kvm" feature.
+ #:system system
+ #:env-vars env-vars
+ #:modules `((guix build utils)
+ (guix build vm)
+ (guix build linux-initrd))
+ #:guile-for-build guile-for-build
+ #:references-graphs references-graphs)))
(define* (qemu-image #:key
(name "qemu-image")
(system (%current-system))
+ (qemu qemu-headless)
(disk-image-size (* 100 (expt 2 20)))
grub-configuration
(initialize-store? #f)
(populate #f)
- (inputs '())
(inputs-to-copy '()))
"Return a bootable, stand-alone QEMU image. The returned image is a full
disk image, with a GRUB installation that uses GRUB-CONFIGURATION as its
((graph (sequence %store-monad
(map input->name+output inputs-to-copy))))
(expression->derivation-in-linux-vm
- "qemu-image"
- `(let ()
- (use-modules (guix build vm)
- (guix build utils))
-
- (set-path-environment-variable "PATH" '("bin" "sbin")
- (map cdr %build-inputs))
-
- (let ((graphs ',(match inputs-to-copy
- (((names . _) ...)
- names))))
- (initialize-hard-disk #:grub.cfg ,grub-configuration
- #:closures-to-copy graphs
- #:disk-image-size ,disk-image-size
- #:initialize-store? ,initialize-store?
- #:directives ',populate)
- (reboot)))
+ name
+ #~(begin
+ (use-modules (guix build vm)
+ (guix build utils))
+
+ (let ((inputs
+ '#$(append (list qemu parted grub e2fsprogs util-linux)
+ (map (compose car (cut assoc-ref %final-inputs <>))
+ '("sed" "grep" "coreutils" "findutils" "gawk"))
+ (if initialize-store? (list guix) '())))
+
+ ;; This variable is unused but allows us to add INPUTS-TO-COPY
+ ;; as inputs.
+ (to-copy
+ '#$(map (match-lambda
+ ((name thing) thing)
+ ((name thing output) `(,thing ,output)))
+ inputs-to-copy)))
+
+ (set-path-environment-variable "PATH" '("bin" "sbin") inputs)
+
+ (let ((graphs '#$(match inputs-to-copy
+ (((names . _) ...)
+ names))))
+ (initialize-hard-disk #:grub.cfg #$grub-configuration
+ #:closures-to-copy graphs
+ #:disk-image-size #$disk-image-size
+ #:initialize-store? #$initialize-store?
+ #:directives '#$populate)
+ (reboot))))
#:system system
- #:inputs `(("parted" ,parted)
- ("grub" ,grub)
- ("e2fsprogs" ,e2fsprogs)
-
- ;; For shell scripts.
- ("sed" ,(car (assoc-ref %final-inputs "sed")))
- ("grep" ,(car (assoc-ref %final-inputs "grep")))
- ("coreutils" ,(car (assoc-ref %final-inputs "coreutils")))
- ("findutils" ,(car (assoc-ref %final-inputs "findutils")))
- ("gawk" ,(car (assoc-ref %final-inputs "gawk")))
- ("util-linux" ,util-linux)
-
- ,@(if initialize-store?
- `(("guix" ,guix))
- '())
-
- ,@inputs-to-copy)
#:make-disk-image? #t
#:disk-image-size disk-image-size
#:references-graphs graph)))
(gid (or (user-account-gid user) 0))
(root (string-append "/var/guix/profiles/per-user/"
(user-account-name user))))
- `((directory ,root ,uid ,gid)
- (directory ,home ,uid ,gid))))
+ #~((directory #$root #$uid #$gid)
+ (directory #$home #$uid #$gid))))
(mlet* %store-monad ((os-drv (operating-system-derivation os))
- (os-dir -> (derivation->output-path os-drv))
(build-gid (operating-system-build-gid os))
- (profile (operating-system-profile-directory os)))
- (return `((directory ,(%store-prefix) 0 ,(or build-gid 0))
- (directory "/etc")
- (directory "/var/log") ; for dmd
- (directory "/var/run/nscd")
- (directory "/var/guix/gcroots")
- ("/var/guix/gcroots/system" -> ,os-dir)
- (directory "/run")
- ("/run/current-system" -> ,profile)
- (directory "/bin")
- ("/bin/sh" -> "/run/current-system/bin/bash")
- (directory "/tmp")
- (directory "/var/guix/profiles/per-user/root" 0 0)
-
- (directory "/root" 0 0) ; an exception
- ,@(append-map user-directories
- (operating-system-users os))))))
+ (profile (operating-system-profile os)))
+ (return #~((directory #$(%store-prefix) 0 #$(or build-gid 0))
+ (directory "/etc")
+ (directory "/var/log") ; for dmd
+ (directory "/var/run/nscd")
+ (directory "/var/guix/gcroots")
+ ("/var/guix/gcroots/system" -> #$os-drv)
+ (directory "/run")
+ ("/run/current-system" -> #$profile)
+ (directory "/bin")
+ ("/bin/sh" -> "/run/current-system/bin/bash")
+ (directory "/tmp")
+ (directory "/var/guix/profiles/per-user/root" 0 0)
+
+ (directory "/root" 0 0) ; an exception
+ #$@(append-map user-directories
+ (operating-system-users os))))))
(define* (system-qemu-image os
#:key (disk-image-size (* 900 (expt 2 20))))
#:initialize-store? #t
#:inputs-to-copy `(("system" ,os-drv)))))
+(define (virtualized-operating-system os)
+ "Return an operating system based on OS suitable for use in a virtualized
+environment with the store shared with the host."
+ (operating-system (inherit os)
+ (initrd (cut qemu-initrd <> #:volatile-root? #t))
+ (file-systems (list (file-system
+ (mount-point "/")
+ (device "/dev/vda1")
+ (type "ext3"))
+ (file-system
+ (mount-point (%store-prefix))
+ (device "store")
+ (type "9p")
+ (needed-for-boot? #t)
+ (options "trans=virtio"))))))
+
(define* (system-qemu-image/shared-store
os
#:key (disk-image-size (* 15 (expt 2 20))))
(graphic? #t))
"Return a derivation that builds a script to run a virtual machine image of
OS that shares its store with the host."
- (let* ((initrd (qemu-initrd #:mounts `((9p "store" ,(%store-prefix)))
- #:volatile-root? #t))
- (os (operating-system (inherit os) (initrd initrd))))
+ (mlet* %store-monad
+ ((os -> (virtualized-operating-system os))
+ (os-drv (operating-system-derivation os))
+ (image (system-qemu-image/shared-store os)))
(define builder
- (mlet %store-monad ((image (system-qemu-image/shared-store os))
- (qemu (package-file qemu
- "bin/qemu-system-x86_64"))
- (bash (package-file bash "bin/sh"))
- (kernel (package-file (operating-system-kernel os)
- "bzImage"))
- (initrd initrd)
- (os-drv (operating-system-derivation os)))
- (return `(let ((out (assoc-ref %outputs "out")))
- (call-with-output-file out
- (lambda (port)
- (display
- (string-append "#!" ,bash "
-exec " ,qemu " -enable-kvm -no-reboot -net nic,model=virtio \
- -virtfs local,path=" ,(%store-prefix) ",security_model=none,mount_tag=store \
+ #~(call-with-output-file #$output
+ (lambda (port)
+ (display
+ (string-append "#!" #$bash "/bin/sh
+exec " #$qemu "/bin/qemu-system-x86_64 -enable-kvm -no-reboot -net nic,model=virtio \
+ -virtfs local,path=" #$(%store-prefix) ",security_model=none,mount_tag=store \
-net user \
- -kernel " ,kernel " -initrd "
- ,(string-append (derivation->output-path initrd) "/initrd") " \
--append \"" ,(if graphic? "" "console=ttyS0 ")
-"--load=" ,(derivation->output-path os-drv) "/boot --root=/dev/vda1\" \
- -drive file=" ,(derivation->output-path image)
+ -kernel " #$(operating-system-kernel os) "/bzImage \
+ -initrd " #$os-drv "/initrd \
+-append \"" #$(if graphic? "" "console=ttyS0 ")
+ "--load=" #$os-drv "/boot --root=/dev/vda1\" \
+ -drive file=" #$image
",if=virtio,cache=writeback,werror=report,readonly\n")
- port)))
- (chmod out #o555)
- #t))))
-
- (mlet %store-monad ((image (system-qemu-image/shared-store os))
- (initrd initrd)
- (qemu (package->derivation qemu))
- (bash (package->derivation bash))
- (os (operating-system-derivation os))
- (builder builder))
- (derivation-expression "run-vm.sh" builder
- #:inputs `(("qemu" ,qemu)
- ("image" ,image)
- ("bash" ,bash)
- ("initrd" ,initrd)
- ("os" ,os))))))
+ port)
+ (chmod port #o555))))
+
+ (gexp->derivation "run-vm.sh" builder)))
;;; vm.scm ends here