#:use-module (guix store)
#:use-module (gnu services)
#:use-module (gnu services shepherd)
- #:use-module (gnu services networking)
#:use-module (gnu system pam)
#:use-module (gnu system shadow) ; 'user-account', etc.
#:use-module (gnu system uuid)
%default-console-font
console-font-service-type
console-font-service
+ virtual-terminal-service-type
+
+ static-networking
+
+ static-networking?
+ static-networking-interface
+ static-networking-ip
+ static-networking-netmask
+ static-networking-gateway
+ static-networking-requirement
+
+ static-networking-service
+ static-networking-service-type
udev-configuration
udev-configuration?
"Return a service that sets the host name to @var{name}."
(service host-name-service-type name))
-(define (unicode-start tty)
- "Return a gexp to start Unicode support on @var{tty}."
- (with-imported-modules '((guix build syscalls))
- #~(let* ((fd (open-fdes #$tty O_RDWR))
- (termios (tcgetattr fd)))
- (define (set-utf8-input termios)
- (set-field termios (termios-input-flags)
- (logior (input-flags IUTF8)
- (termios-input-flags termios))))
-
- (tcsetattr fd (tcsetattr-action TCSAFLUSH)
- (set-utf8-input termios))
-
- ;; TODO: ioctl(fd, KDSKBMODE, K_UNICODE);
- (close-fdes fd)
- #t)))
+(define virtual-terminal-service-type
+ ;; Ensure that virtual terminals run in UTF-8 mode. This is the case by
+ ;; default with recent Linux kernels, but this service allows us to ensure
+ ;; this. This service must start before any 'term-' service so that newly
+ ;; created terminals inherit this property. See
+ ;; <https://bugs.gnu.org/30505> for a discussion.
+ (shepherd-service-type
+ 'virtual-terminal
+ (lambda (utf8?)
+ (shepherd-service
+ (documentation "Set virtual terminals in UTF-8 module.")
+ (provision '(virtual-terminal))
+ (requirement '(root-file-system))
+ (start #~(lambda _
+ (call-with-output-file
+ "/sys/module/vt/parameters/default_utf8"
+ (lambda (port)
+ (display 1 port)))
+ #t))
+ (stop #~(const #f))))
+ #t)) ;default to UTF-8
(define console-keymap-service-type
(shepherd-service-type
(requirement (list (symbol-append 'term-
(string->symbol tty))))
- (modules '((guix build syscalls) ;for 'tcsetattr'
- (srfi srfi-9 gnu))) ;for 'set-field'
(start #~(lambda _
;; It could be that mingetty is not fully ready yet,
;; which we check by calling 'ttyname'.
(usleep 500)
(loop (- i 1))))
- (and #$(unicode-start device)
- ;; 'setfont' returns EX_OSERR (71) when an
- ;; KDFONTOP ioctl fails, for example. Like
- ;; systemd's vconsole support, let's not treat
- ;; this as an error.
- (case (status:exit-val
- (system* #$(file-append kbd "/bin/setfont")
- "-C" #$device #$font))
- ((0 71) #t)
- (else #f)))))
+ ;; Assume the VT is already in UTF-8 mode, thanks to
+ ;; the 'virtual-terminal' service.
+ ;;
+ ;; 'setfont' returns EX_OSERR (71) when an
+ ;; KDFONTOP ioctl fails, for example. Like
+ ;; systemd's vconsole support, let's not treat
+ ;; this as an error.
+ (case (status:exit-val
+ (system* #$(file-append kbd "/bin/setfont")
+ "-C" #$device #$font))
+ ((0 71) #t)
+ (else #f))))
(stop #~(const #t))
(respawn? #f)))))
tty+font))
;; mingetty-shepherd-service).
(requirement '(user-processes host-name udev))
- (start #~(let ((tty #$(default-serial-port)))
- (if tty
- (make-forkexec-constructor
- (list #$(file-append util-linux "/sbin/agetty")
- #$@extra-options
- #$@(if eight-bits?
- #~("--8bits")
- #~())
- #$@(if no-reset?
- #~("--noreset")
- #~())
- #$@(if remote?
- #~("--remote")
- #~())
- #$@(if flow-control?
- #~("--flow-control")
- #~())
- #$@(if host
- #~("--host" #$host)
- #~())
- #$@(if no-issue?
- #~("--noissue")
- #~())
- #$@(if init-string
- #~("--init-string" #$init-string)
- #~())
- #$@(if no-clear?
- #~("--noclear")
- #~())
+ (start #~(lambda args
+ (let ((defaulted-tty #$(or tty (default-serial-port))))
+ (apply
+ (if defaulted-tty
+ (make-forkexec-constructor
+ (list #$(file-append util-linux "/sbin/agetty")
+ #$@extra-options
+ #$@(if eight-bits?
+ #~("--8bits")
+ #~())
+ #$@(if no-reset?
+ #~("--noreset")
+ #~())
+ #$@(if remote?
+ #~("--remote")
+ #~())
+ #$@(if flow-control?
+ #~("--flow-control")
+ #~())
+ #$@(if host
+ #~("--host" #$host)
+ #~())
+ #$@(if no-issue?
+ #~("--noissue")
+ #~())
+ #$@(if init-string
+ #~("--init-string" #$init-string)
+ #~())
+ #$@(if no-clear?
+ #~("--noclear")
+ #~())
;;; FIXME This doesn't work as expected. According to agetty(8), if this option
;;; is not passed, then the default is 'auto'. However, in my tests, when that
;;; option is selected, agetty never presents the login prompt, and the
;;; term-ttyS0 service respawns every few seconds.
- #$@(if local-line
- #~(#$(match local-line
- ('auto "--local-line=auto")
- ('always "--local-line=always")
- ('never "-local-line=never")))
- #~())
- #$@(if tty
- #~()
- #~("--keep-baud"))
- #$@(if extract-baud?
- #~("--extract-baud")
- #~())
- #$@(if skip-login?
- #~("--skip-login")
- #~())
- #$@(if no-newline?
- #~("--nonewline")
- #~())
- #$@(if login-options
- #~("--login-options" #$login-options)
- #~())
- #$@(if chroot
- #~("--chroot" #$chroot)
- #~())
- #$@(if hangup?
- #~("--hangup")
- #~())
- #$@(if keep-baud?
- #~("--keep-baud")
- #~())
- #$@(if timeout
- #~("--timeout" #$(number->string timeout))
- #~())
- #$@(if detect-case?
- #~("--detect-case")
- #~())
- #$@(if wait-cr?
- #~("--wait-cr")
- #~())
- #$@(if no-hints?
- #~("--nohints?")
- #~())
- #$@(if no-hostname?
- #~("--nohostname")
- #~())
- #$@(if long-hostname?
- #~("--long-hostname")
- #~())
- #$@(if erase-characters
- #~("--erase-chars" #$erase-characters)
- #~())
- #$@(if kill-characters
- #~("--kill-chars" #$kill-characters)
- #~())
- #$@(if chdir
- #~("--chdir" #$chdir)
- #~())
- #$@(if delay
- #~("--delay" #$(number->string delay))
- #~())
- #$@(if nice
- #~("--nice" #$(number->string nice))
- #~())
- #$@(if auto-login
- (list "--autologin" auto-login)
- '())
- #$@(if login-program
- #~("--login-program" #$login-program)
- #~())
- #$@(if login-pause?
- #~("--login-pause")
- #~())
- #$(or tty (default-serial-port))
- #$@(if baud-rate
- #~(#$baud-rate)
- #~())
- #$@(if term
- #~(#$term)
- #~()))))
- (const #f))) ; never start.
+ #$@(if local-line
+ #~(#$(match local-line
+ ('auto "--local-line=auto")
+ ('always "--local-line=always")
+ ('never "-local-line=never")))
+ #~())
+ #$@(if tty
+ #~()
+ #~("--keep-baud"))
+ #$@(if extract-baud?
+ #~("--extract-baud")
+ #~())
+ #$@(if skip-login?
+ #~("--skip-login")
+ #~())
+ #$@(if no-newline?
+ #~("--nonewline")
+ #~())
+ #$@(if login-options
+ #~("--login-options" #$login-options)
+ #~())
+ #$@(if chroot
+ #~("--chroot" #$chroot)
+ #~())
+ #$@(if hangup?
+ #~("--hangup")
+ #~())
+ #$@(if keep-baud?
+ #~("--keep-baud")
+ #~())
+ #$@(if timeout
+ #~("--timeout" #$(number->string timeout))
+ #~())
+ #$@(if detect-case?
+ #~("--detect-case")
+ #~())
+ #$@(if wait-cr?
+ #~("--wait-cr")
+ #~())
+ #$@(if no-hints?
+ #~("--nohints?")
+ #~())
+ #$@(if no-hostname?
+ #~("--nohostname")
+ #~())
+ #$@(if long-hostname?
+ #~("--long-hostname")
+ #~())
+ #$@(if erase-characters
+ #~("--erase-chars" #$erase-characters)
+ #~())
+ #$@(if kill-characters
+ #~("--kill-chars" #$kill-characters)
+ #~())
+ #$@(if chdir
+ #~("--chdir" #$chdir)
+ #~())
+ #$@(if delay
+ #~("--delay" #$(number->string delay))
+ #~())
+ #$@(if nice
+ #~("--nice" #$(number->string nice))
+ #~())
+ #$@(if auto-login
+ (list "--autologin" auto-login)
+ '())
+ #$@(if login-program
+ #~("--login-program" #$login-program)
+ #~())
+ #$@(if login-pause?
+ #~("--login-pause")
+ #~())
+ defaulted-tty
+ #$@(if baud-rate
+ #~(#$baud-rate)
+ #~())
+ #$@(if term
+ #~(#$term)
+ #~())))
+ (const #f)) ; never start.
+ args))))
(stop #~(make-kill-destructor)))))))
(define agetty-service-type
;; Since the login prompt shows the host name, wait for the 'host-name'
;; service to be done. Also wait for udev essentially so that the tty
;; text is not lost in the middle of kernel messages (XXX).
- (requirement '(user-processes host-name udev))
+ (requirement '(user-processes host-name udev virtual-terminal))
(start #~(make-forkexec-constructor
(list #$(file-append mingetty "/sbin/mingetty")
"Return a service that uses @var{device} as a swap device."
(service swap-service-type device))
+(define %default-gpm-options
+ ;; Default options for GPM.
+ '("-m" "/dev/input/mice" "-t" "ps2"))
+
(define-record-type* <gpm-configuration>
gpm-configuration make-gpm-configuration gpm-configuration?
- (gpm gpm-configuration-gpm) ;package
- (options gpm-configuration-options)) ;list of strings
+ (gpm gpm-configuration-gpm ;package
+ (default gpm))
+ (options gpm-configuration-options ;list of strings
+ (default %default-gpm-options)))
(define gpm-shepherd-service
(match-lambda
(extensions
(list (service-extension shepherd-root-service-type
gpm-shepherd-service)))
+ (default-value (gpm-configuration))
(description
"Run GPM, the general-purpose mouse daemon, with the given
command-line options. GPM allows users to use the mouse in the console,
notably to select, copy, and paste text. The default options use the
@code{ps2} protocol, which works for both USB and PS/2 mice.")))
-(define* (gpm-service #:key (gpm gpm)
- (options '("-m" "/dev/input/mice" "-t" "ps2")))
+(define* (gpm-service #:key (gpm gpm) ;deprecated
+ (options %default-gpm-options))
"Run @var{gpm}, the general-purpose mouse daemon, with the given
command-line @var{options}. GPM allows users to use the mouse in the console,
notably to select, copy, and paste text. The default value of @var{options}
(shepherd-service
(documentation "kmscon virtual terminal")
- (requirement '(user-processes udev dbus-system))
+ (requirement '(user-processes udev dbus-system virtual-terminal))
(provision (list (symbol-append 'term- (string->symbol virtual-terminal))))
(start #~(make-forkexec-constructor #$kmscon-command))
(stop #~(make-kill-destructor)))))))
+(define-record-type* <static-networking>
+ static-networking make-static-networking
+ static-networking?
+ (interface static-networking-interface)
+ (ip static-networking-ip)
+ (netmask static-networking-netmask
+ (default #f))
+ (gateway static-networking-gateway ;FIXME: doesn't belong here
+ (default #f))
+ (provision static-networking-provision
+ (default #f))
+ (requirement static-networking-requirement
+ (default '()))
+ (name-servers static-networking-name-servers ;FIXME: doesn't belong here
+ (default '())))
+
+(define static-networking-shepherd-service
+ (match-lambda
+ (($ <static-networking> interface ip netmask gateway provision
+ requirement name-servers)
+ (let ((loopback? (and provision (memq 'loopback provision))))
+ (shepherd-service
+
+ (documentation
+ "Bring up the networking interface using a static IP address.")
+ (requirement requirement)
+ (provision (or provision
+ (list (symbol-append 'networking-
+ (string->symbol interface)))))
+
+ (start #~(lambda _
+ ;; Return #t if successfully started.
+ (let* ((addr (inet-pton AF_INET #$ip))
+ (sockaddr (make-socket-address AF_INET addr 0))
+ (mask (and #$netmask
+ (inet-pton AF_INET #$netmask)))
+ (maskaddr (and mask
+ (make-socket-address AF_INET
+ mask 0)))
+ (gateway (and #$gateway
+ (inet-pton AF_INET #$gateway)))
+ (gatewayaddr (and gateway
+ (make-socket-address AF_INET
+ gateway 0))))
+ (configure-network-interface #$interface sockaddr
+ (logior IFF_UP
+ #$(if loopback?
+ #~IFF_LOOPBACK
+ 0))
+ #:netmask maskaddr)
+ (when gateway
+ (let ((sock (socket AF_INET SOCK_DGRAM 0)))
+ (add-network-route/gateway sock gatewayaddr)
+ (close-port sock))))))
+ (stop #~(lambda _
+ ;; Return #f is successfully stopped.
+ (let ((sock (socket AF_INET SOCK_STREAM 0)))
+ (when #$gateway
+ (delete-network-route sock
+ (make-socket-address
+ AF_INET INADDR_ANY 0)))
+ (set-network-interface-flags sock #$interface 0)
+ (close-port sock)
+: #f)))
+ (respawn? #f))))))
+
+(define (static-networking-etc-files interfaces)
+ "Return a /etc/resolv.conf entry for INTERFACES or the empty list."
+ (match (delete-duplicates
+ (append-map static-networking-name-servers
+ interfaces))
+ (()
+ '())
+ ((name-servers ...)
+ (let ((content (string-join
+ (map (cut string-append "nameserver " <>)
+ name-servers)
+ "\n" 'suffix)))
+ `(("resolv.conf"
+ ,(plain-file "resolv.conf"
+ (string-append "\
+# Generated by 'static-networking-service'.\n"
+ content))))))))
+
+(define (static-networking-shepherd-services interfaces)
+ "Return the list of Shepherd services to bring up INTERFACES, a list of
+<static-networking> objects."
+ (define (loopback? service)
+ (memq 'loopback (shepherd-service-provision service)))
+
+ (let ((services (map static-networking-shepherd-service interfaces)))
+ (match (remove loopback? services)
+ (()
+ ;; There's no interface other than 'loopback', so we assume that the
+ ;; 'networking' service will be provided by dhclient or similar.
+ services)
+ ((non-loopback ...)
+ ;; Assume we're providing all the interfaces, and thus, provide a
+ ;; 'networking' service.
+ (cons (shepherd-service
+ (provision '(networking))
+ (requirement (append-map shepherd-service-provision
+ services))
+ (start #~(const #t))
+ (stop #~(const #f))
+ (documentation "Bring up all the networking interfaces."))
+ services)))))
+
+(define static-networking-service-type
+ ;; The service type for statically-defined network interfaces.
+ (service-type (name 'static-networking)
+ (extensions
+ (list
+ (service-extension shepherd-root-service-type
+ static-networking-shepherd-services)
+ (service-extension etc-service-type
+ static-networking-etc-files)))
+ (compose concatenate)
+ (extend append)
+ (description
+ "Turn up the specified network interfaces upon startup,
+with the given IP address, gateway, netmask, and so on. The value for
+services of this type is a list of @code{static-networking} objects, one per
+network interface.")))
+
+(define* (static-networking-service interface ip
+ #:key
+ netmask gateway provision
+ ;; Most interfaces require udev to be usable.
+ (requirement '(udev))
+ (name-servers '()))
+ "Return a service that starts @var{interface} with address @var{ip}. If
+@var{netmask} is true, use it as the network mask. If @var{gateway} is true,
+it must be a string specifying the default network gateway.
+
+This procedure can be called several times, one for each network
+interface of interest. Behind the scenes what it does is extend
+@code{static-networking-service-type} with additional network interfaces
+to handle."
+ (simple-service 'static-network-interface
+ static-networking-service-type
+ (list (static-networking (interface interface) (ip ip)
+ (netmask netmask) (gateway gateway)
+ (provision provision)
+ (requirement requirement)
+ (name-servers name-servers)))))
+
\f
(define %base-services
;; Convenience variable holding the basic services.
(list (login-service)
+ (service virtual-terminal-service-type)
(service console-font-service-type
(map (lambda (tty)
(cons tty %default-console-font))