Add (gnu tests) and (gnu build marionette).
* gnu/build/marionette.scm, gnu/tests.scm: New files. * gnu/local.mk (GNU_SYSTEM_MODULES): Add them. * gnu/system/vm.scm (common-qemu-options): Remove '-serial stdio'.
This commit is contained in:
		
							parent
							
								
									b2fef041fc
								
							
						
					
					
						commit
						957afcae3c
					
				
					 4 changed files with 341 additions and 2 deletions
				
			
		
							
								
								
									
										206
									
								
								gnu/build/marionette.scm
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								gnu/build/marionette.scm
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,206 @@
 | 
				
			||||||
 | 
					;;; GNU Guix --- Functional package management for GNU
 | 
				
			||||||
 | 
					;;; Copyright © 2016 Ludovic Courtès <ludo@gnu.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 build marionette)
 | 
				
			||||||
 | 
					  #:use-module (srfi srfi-9)
 | 
				
			||||||
 | 
					  #:use-module (srfi srfi-26)
 | 
				
			||||||
 | 
					  #:use-module (rnrs io ports)
 | 
				
			||||||
 | 
					  #:use-module (ice-9 match)
 | 
				
			||||||
 | 
					  #:export (marionette?
 | 
				
			||||||
 | 
					            make-marionette
 | 
				
			||||||
 | 
					            marionette-eval
 | 
				
			||||||
 | 
					            marionette-control
 | 
				
			||||||
 | 
					            %qwerty-us-keystrokes
 | 
				
			||||||
 | 
					            marionette-type))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;; Commentary:
 | 
				
			||||||
 | 
					;;;
 | 
				
			||||||
 | 
					;;; Instrumentation tools for QEMU virtual machines (VMs).  A "marionette" is
 | 
				
			||||||
 | 
					;;; essentially a VM (a QEMU instance) with its monitor connected to a
 | 
				
			||||||
 | 
					;;; Unix-domain socket, and with a REPL inside the guest listening on a
 | 
				
			||||||
 | 
					;;; virtual console, which is itself connected to the host via a Unix-domain
 | 
				
			||||||
 | 
					;;; socket--these are the marionette's strings, connecting it to the almighty
 | 
				
			||||||
 | 
					;;; puppeteer.
 | 
				
			||||||
 | 
					;;;
 | 
				
			||||||
 | 
					;;; Code:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define-record-type <marionette>
 | 
				
			||||||
 | 
					  (marionette command pid monitor repl)
 | 
				
			||||||
 | 
					  marionette?
 | 
				
			||||||
 | 
					  (command    marionette-command)                 ;list of strings
 | 
				
			||||||
 | 
					  (pid        marionette-pid)                     ;integer
 | 
				
			||||||
 | 
					  (monitor    marionette-monitor)                 ;port
 | 
				
			||||||
 | 
					  (repl       marionette-repl))                   ;port
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define* (wait-for-monitor-prompt port #:key (quiet? #t))
 | 
				
			||||||
 | 
					  "Read from PORT until we have seen all of QEMU's monitor prompt.  When
 | 
				
			||||||
 | 
					QUIET? is false, the monitor's output is written to the current output port."
 | 
				
			||||||
 | 
					  (define full-prompt
 | 
				
			||||||
 | 
					    (string->list "(qemu) "))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  (let loop ((prompt full-prompt)
 | 
				
			||||||
 | 
					             (matches '())
 | 
				
			||||||
 | 
					             (prefix  '()))
 | 
				
			||||||
 | 
					    (match prompt
 | 
				
			||||||
 | 
					      (()
 | 
				
			||||||
 | 
					       ;; It's useful to set QUIET? so we don't display the echo of our own
 | 
				
			||||||
 | 
					       ;; commands.
 | 
				
			||||||
 | 
					       (unless quiet?
 | 
				
			||||||
 | 
					         (for-each (lambda (line)
 | 
				
			||||||
 | 
					                     (format #t "qemu monitor: ~a~%" line))
 | 
				
			||||||
 | 
					                   (string-tokenize (list->string (reverse prefix))
 | 
				
			||||||
 | 
					                                    (char-set-complement (char-set #\newline))))))
 | 
				
			||||||
 | 
					      ((chr rest ...)
 | 
				
			||||||
 | 
					       (let ((read (read-char port)))
 | 
				
			||||||
 | 
					         (cond ((eqv? read chr)
 | 
				
			||||||
 | 
					                (loop rest (cons read matches) prefix))
 | 
				
			||||||
 | 
					               ((eof-object? read)
 | 
				
			||||||
 | 
					                (error "EOF while waiting for QEMU monitor prompt"
 | 
				
			||||||
 | 
					                       (list->string (reverse prefix))))
 | 
				
			||||||
 | 
					               (else
 | 
				
			||||||
 | 
					                (loop full-prompt
 | 
				
			||||||
 | 
					                      '()
 | 
				
			||||||
 | 
					                      (cons read (append matches prefix))))))))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define* (make-marionette command
 | 
				
			||||||
 | 
					                          #:key (socket-directory "/tmp") (timeout 20))
 | 
				
			||||||
 | 
					  "Return a QEMU marionette--i.e., a virtual machine with open connections to the
 | 
				
			||||||
 | 
					QEMU monitor and to the guest's backdoor REPL."
 | 
				
			||||||
 | 
					  (define (file->sockaddr file)
 | 
				
			||||||
 | 
					    (make-socket-address AF_UNIX
 | 
				
			||||||
 | 
					                         (string-append socket-directory "/" file)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  (define extra-options
 | 
				
			||||||
 | 
					    (list "-nographic"
 | 
				
			||||||
 | 
					          "-monitor" (string-append "unix:" socket-directory "/monitor")
 | 
				
			||||||
 | 
					          "-chardev" (string-append "socket,id=repl,path=" socket-directory
 | 
				
			||||||
 | 
					                                    "/repl")
 | 
				
			||||||
 | 
					          "-device" "virtio-serial"
 | 
				
			||||||
 | 
					          "-device" "virtconsole,chardev=repl"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  (let ((monitor (socket AF_UNIX SOCK_STREAM 0))
 | 
				
			||||||
 | 
					        (repl    (socket AF_UNIX SOCK_STREAM 0)))
 | 
				
			||||||
 | 
					    (bind monitor (file->sockaddr "monitor"))
 | 
				
			||||||
 | 
					    (listen monitor 1)
 | 
				
			||||||
 | 
					    (bind repl (file->sockaddr "repl"))
 | 
				
			||||||
 | 
					    (listen repl 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (match (primitive-fork)
 | 
				
			||||||
 | 
					      (0
 | 
				
			||||||
 | 
					       (catch #t
 | 
				
			||||||
 | 
					         (lambda ()
 | 
				
			||||||
 | 
					           (close monitor)
 | 
				
			||||||
 | 
					           (close repl)
 | 
				
			||||||
 | 
					           (match command
 | 
				
			||||||
 | 
					             ((program . args)
 | 
				
			||||||
 | 
					              (apply execl program program
 | 
				
			||||||
 | 
					                     (append args extra-options)))))
 | 
				
			||||||
 | 
					         (lambda (key . args)
 | 
				
			||||||
 | 
					           (print-exception (current-error-port)
 | 
				
			||||||
 | 
					                            (stack-ref (make-stack #t) 1)
 | 
				
			||||||
 | 
					                            key args)
 | 
				
			||||||
 | 
					           (primitive-exit 1))))
 | 
				
			||||||
 | 
					      (pid
 | 
				
			||||||
 | 
					       (format #t "QEMU runs as PID ~a~%" pid)
 | 
				
			||||||
 | 
					       (sigaction SIGALRM
 | 
				
			||||||
 | 
					         (lambda (signum)
 | 
				
			||||||
 | 
					           (display "time is up!\n")              ;FIXME: break
 | 
				
			||||||
 | 
					           #t))
 | 
				
			||||||
 | 
					       (alarm timeout)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       (match (accept monitor)
 | 
				
			||||||
 | 
					         ((monitor-conn . _)
 | 
				
			||||||
 | 
					          (display "connected to QEMU's monitor\n")
 | 
				
			||||||
 | 
					          (close-port monitor)
 | 
				
			||||||
 | 
					          (wait-for-monitor-prompt monitor-conn)
 | 
				
			||||||
 | 
					          (display "read QEMU monitor prompt\n")
 | 
				
			||||||
 | 
					          (match (accept repl)
 | 
				
			||||||
 | 
					            ((repl-conn . addr)
 | 
				
			||||||
 | 
					             (display "connected to guest REPL\n")
 | 
				
			||||||
 | 
					             (close-port repl)
 | 
				
			||||||
 | 
					             (match (read repl-conn)
 | 
				
			||||||
 | 
					               ('ready
 | 
				
			||||||
 | 
					                (alarm 0)
 | 
				
			||||||
 | 
					                (sigaction SIGALRM SIG_DFL)
 | 
				
			||||||
 | 
					                (display "marionette is ready\n")
 | 
				
			||||||
 | 
					                (marionette (append command extra-options) pid
 | 
				
			||||||
 | 
					                            monitor-conn repl-conn)))))))))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (marionette-eval exp marionette)
 | 
				
			||||||
 | 
					  "Evaluate EXP in MARIONETTE's backdoor REPL.  Return the result."
 | 
				
			||||||
 | 
					  (match marionette
 | 
				
			||||||
 | 
					    (($ <marionette> command pid monitor repl)
 | 
				
			||||||
 | 
					     (write exp repl)
 | 
				
			||||||
 | 
					     (newline repl)
 | 
				
			||||||
 | 
					     (read repl))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (marionette-control command marionette)
 | 
				
			||||||
 | 
					  "Run COMMAND in the QEMU monitor of MARIONETTE.  COMMAND is a string such as
 | 
				
			||||||
 | 
					\"sendkey ctrl-alt-f1\" or \"screendump foo.ppm\" (info \"(qemu-doc)
 | 
				
			||||||
 | 
					pcsys_monitor\")."
 | 
				
			||||||
 | 
					  (match marionette
 | 
				
			||||||
 | 
					    (($ <marionette> _ _ monitor)
 | 
				
			||||||
 | 
					     (display command monitor)
 | 
				
			||||||
 | 
					     (newline monitor)
 | 
				
			||||||
 | 
					     (wait-for-monitor-prompt monitor))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define %qwerty-us-keystrokes
 | 
				
			||||||
 | 
					  ;; Maps "special" characters to their keystrokes.
 | 
				
			||||||
 | 
					  '((#\newline . "ret")
 | 
				
			||||||
 | 
					    (#\space . "spc")
 | 
				
			||||||
 | 
					    (#\- . "minus")
 | 
				
			||||||
 | 
					    (#\+ . "shift-equal")
 | 
				
			||||||
 | 
					    (#\* . "shift-8")
 | 
				
			||||||
 | 
					    (#\= . "equal")
 | 
				
			||||||
 | 
					    (#\? . "shift-slash")
 | 
				
			||||||
 | 
					    (#\[ . "bracket_left")
 | 
				
			||||||
 | 
					    (#\] . "bracket_right")
 | 
				
			||||||
 | 
					    (#\( . "shift-9")
 | 
				
			||||||
 | 
					    (#\) . "shift-0")
 | 
				
			||||||
 | 
					    (#\/ . "slash")
 | 
				
			||||||
 | 
					    (#\< . "less")
 | 
				
			||||||
 | 
					    (#\> . "shift-less")
 | 
				
			||||||
 | 
					    (#\. . "dot")
 | 
				
			||||||
 | 
					    (#\, . "comma")
 | 
				
			||||||
 | 
					    (#\; . "semicolon")
 | 
				
			||||||
 | 
					    (#\bs . "backspace")
 | 
				
			||||||
 | 
					    (#\tab . "tab")))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define* (string->keystroke-commands str
 | 
				
			||||||
 | 
					                                     #:optional
 | 
				
			||||||
 | 
					                                     (keystrokes
 | 
				
			||||||
 | 
					                                      %qwerty-us-keystrokes))
 | 
				
			||||||
 | 
					  "Return a list of QEMU monitor commands to send the keystrokes corresponding
 | 
				
			||||||
 | 
					to STR.  KEYSTROKES is an alist specifying a mapping from characters to
 | 
				
			||||||
 | 
					keystrokes."
 | 
				
			||||||
 | 
					  (string-fold-right (lambda (chr result)
 | 
				
			||||||
 | 
					                       (cons (string-append "sendkey "
 | 
				
			||||||
 | 
					                                            (or (assoc-ref keystrokes chr)
 | 
				
			||||||
 | 
					                                                (string chr)))
 | 
				
			||||||
 | 
					                             result))
 | 
				
			||||||
 | 
					                     '()
 | 
				
			||||||
 | 
					                     str))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define* (marionette-type str marionette
 | 
				
			||||||
 | 
					                          #:key (keystrokes %qwerty-us-keystrokes))
 | 
				
			||||||
 | 
					  "Type STR on MARIONETTE's keyboard, using the KEYSTROKES alist to map characters
 | 
				
			||||||
 | 
					to actual keystrokes."
 | 
				
			||||||
 | 
					  (for-each (cut marionette-control <> marionette)
 | 
				
			||||||
 | 
					            (string->keystroke-commands str keystrokes)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;; marionette.scm ends here
 | 
				
			||||||
| 
						 | 
					@ -398,7 +398,10 @@ GNU_SYSTEM_MODULES =				\
 | 
				
			||||||
  gnu/build/linux-container.scm			\
 | 
					  gnu/build/linux-container.scm			\
 | 
				
			||||||
  gnu/build/linux-initrd.scm			\
 | 
					  gnu/build/linux-initrd.scm			\
 | 
				
			||||||
  gnu/build/linux-modules.scm			\
 | 
					  gnu/build/linux-modules.scm			\
 | 
				
			||||||
  gnu/build/vm.scm
 | 
					  gnu/build/marionette.scm			\
 | 
				
			||||||
 | 
					  gnu/build/vm.scm				\
 | 
				
			||||||
 | 
											\
 | 
				
			||||||
 | 
					  gnu/tests.scm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
patchdir = $(guilemoduledir)/gnu/packages/patches
 | 
					patchdir = $(guilemoduledir)/gnu/packages/patches
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -468,7 +468,7 @@ with '-virtfs' options for the host file systems listed in SHARED-FS."
 | 
				
			||||||
     " -no-reboot -net nic,model=virtio \
 | 
					     " -no-reboot -net nic,model=virtio \
 | 
				
			||||||
  " #$@(map virtfs-option shared-fs) " \
 | 
					  " #$@(map virtfs-option shared-fs) " \
 | 
				
			||||||
  -net user \
 | 
					  -net user \
 | 
				
			||||||
  -serial stdio -vga std \
 | 
					  -vga std \
 | 
				
			||||||
  -drive file=" #$image
 | 
					  -drive file=" #$image
 | 
				
			||||||
  ",if=virtio,cache=writeback,werror=report,readonly \
 | 
					  ",if=virtio,cache=writeback,werror=report,readonly \
 | 
				
			||||||
  -m 256"))
 | 
					  -m 256"))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										130
									
								
								gnu/tests.scm
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								gnu/tests.scm
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,130 @@
 | 
				
			||||||
 | 
					;;; GNU Guix --- Functional package management for GNU
 | 
				
			||||||
 | 
					;;; Copyright © 2016 Ludovic Courtès <ludo@gnu.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 tests)
 | 
				
			||||||
 | 
					  #:use-module (guix gexp)
 | 
				
			||||||
 | 
					  #:use-module (gnu system)
 | 
				
			||||||
 | 
					  #:use-module (gnu services)
 | 
				
			||||||
 | 
					  #:use-module (gnu services shepherd)
 | 
				
			||||||
 | 
					  #:export (backdoor-service-type
 | 
				
			||||||
 | 
					            marionette-operating-system))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;; Commentary:
 | 
				
			||||||
 | 
					;;;
 | 
				
			||||||
 | 
					;;; This module provides the infrastructure to run operating system tests.
 | 
				
			||||||
 | 
					;;; The most important part of that is tools to instrument the OS under test,
 | 
				
			||||||
 | 
					;;; essentially allowing to run in a virtual machine controlled by the host
 | 
				
			||||||
 | 
					;;; system--hence the name "marionette".
 | 
				
			||||||
 | 
					;;;
 | 
				
			||||||
 | 
					;;; Code:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (marionette-shepherd-service imported-modules)
 | 
				
			||||||
 | 
					  "Return the Shepherd service for the marionette REPL"
 | 
				
			||||||
 | 
					  (define device
 | 
				
			||||||
 | 
					    "/dev/hvc0")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  (list (shepherd-service
 | 
				
			||||||
 | 
					         (provision '(marionette))
 | 
				
			||||||
 | 
					         (requirement '(udev))                    ;so that DEVICE is available
 | 
				
			||||||
 | 
					         (modules '((ice-9 match)
 | 
				
			||||||
 | 
					                    (srfi srfi-9 gnu)
 | 
				
			||||||
 | 
					                    (guix build syscalls)
 | 
				
			||||||
 | 
					                    (rnrs bytevectors)))
 | 
				
			||||||
 | 
					         (imported-modules `((guix build syscalls)
 | 
				
			||||||
 | 
					                             ,@imported-modules))
 | 
				
			||||||
 | 
					         (start
 | 
				
			||||||
 | 
					          #~(lambda ()
 | 
				
			||||||
 | 
					              (define (clear-echo termios)
 | 
				
			||||||
 | 
					                (set-field termios (termios-local-flags)
 | 
				
			||||||
 | 
					                           (logand (lognot (local-flags ECHO))
 | 
				
			||||||
 | 
					                                   (termios-local-flags termios))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              (define (self-quoting? x)
 | 
				
			||||||
 | 
					                (letrec-syntax ((one-of (syntax-rules ()
 | 
				
			||||||
 | 
					                                          ((_) #f)
 | 
				
			||||||
 | 
					                                          ((_ pred rest ...)
 | 
				
			||||||
 | 
					                                           (or (pred x)
 | 
				
			||||||
 | 
					                                               (one-of rest ...))))))
 | 
				
			||||||
 | 
					                  (one-of symbol? string? pair? null? vector?
 | 
				
			||||||
 | 
					                          bytevector? number? boolean?)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              (match (primitive-fork)
 | 
				
			||||||
 | 
					                (0
 | 
				
			||||||
 | 
					                 (dynamic-wind
 | 
				
			||||||
 | 
					                   (const #t)
 | 
				
			||||||
 | 
					                   (lambda ()
 | 
				
			||||||
 | 
					                     (let* ((repl    (open-file #$device "r+0"))
 | 
				
			||||||
 | 
					                            (termios (tcgetattr (fileno repl)))
 | 
				
			||||||
 | 
					                            (console (open-file "/dev/console" "r+0")))
 | 
				
			||||||
 | 
					                       ;; Don't echo input back.
 | 
				
			||||||
 | 
					                       (tcsetattr (fileno repl) (tcsetattr-action TCSANOW)
 | 
				
			||||||
 | 
					                                  (clear-echo termios))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                       ;; Redirect output to the console.
 | 
				
			||||||
 | 
					                       (close-fdes 1)
 | 
				
			||||||
 | 
					                       (close-fdes 2)
 | 
				
			||||||
 | 
					                       (dup2 (fileno console) 1)
 | 
				
			||||||
 | 
					                       (dup2 (fileno console) 2)
 | 
				
			||||||
 | 
					                       (close-port console)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                       (display 'ready repl)
 | 
				
			||||||
 | 
					                       (let loop ()
 | 
				
			||||||
 | 
					                         (newline repl)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                         (match (read repl)
 | 
				
			||||||
 | 
					                           ((? eof-object?)
 | 
				
			||||||
 | 
					                            (primitive-exit 0))
 | 
				
			||||||
 | 
					                           (expr
 | 
				
			||||||
 | 
					                            (catch #t
 | 
				
			||||||
 | 
					                              (lambda ()
 | 
				
			||||||
 | 
					                                (let ((result (primitive-eval expr)))
 | 
				
			||||||
 | 
					                                  (write (if (self-quoting? result)
 | 
				
			||||||
 | 
					                                             result
 | 
				
			||||||
 | 
					                                             (object->string result))
 | 
				
			||||||
 | 
					                                         repl)))
 | 
				
			||||||
 | 
					                              (lambda (key . args)
 | 
				
			||||||
 | 
					                                (print-exception (current-error-port)
 | 
				
			||||||
 | 
					                                                 (stack-ref (make-stack #t) 1)
 | 
				
			||||||
 | 
					                                                 key args)
 | 
				
			||||||
 | 
					                                (write #f repl)))))
 | 
				
			||||||
 | 
					                         (loop))))
 | 
				
			||||||
 | 
					                   (lambda ()
 | 
				
			||||||
 | 
					                     (primitive-exit 1))))
 | 
				
			||||||
 | 
					                (pid
 | 
				
			||||||
 | 
					                 pid))))
 | 
				
			||||||
 | 
					         (stop #~(make-kill-destructor)))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define marionette-service-type
 | 
				
			||||||
 | 
					  ;; This is the type of the "marionette" service, allowing a guest system to
 | 
				
			||||||
 | 
					  ;; be manipulated from the host.  This marionette REPL is essentially a
 | 
				
			||||||
 | 
					  ;; universal marionette.
 | 
				
			||||||
 | 
					  (service-type (name 'marionette-repl)
 | 
				
			||||||
 | 
					                (extensions
 | 
				
			||||||
 | 
					                 (list (service-extension shepherd-root-service-type
 | 
				
			||||||
 | 
					                                          marionette-shepherd-service)))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define* (marionette-operating-system os
 | 
				
			||||||
 | 
					                                      #:key (imported-modules '()))
 | 
				
			||||||
 | 
					  "Return a marionetteed variant of OS such that OS can be used as a marionette
 | 
				
			||||||
 | 
					in a virtual machine--i.e., controlled from the host system."
 | 
				
			||||||
 | 
					  (operating-system
 | 
				
			||||||
 | 
					    (inherit os)
 | 
				
			||||||
 | 
					    (services (cons (service marionette-service-type imported-modules)
 | 
				
			||||||
 | 
					                    (operating-system-user-services os)))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;; tests.scm ends here
 | 
				
			||||||
		Reference in a new issue