Guix Custom One-Shot Services

By: Owen T. Heisler
Published: 2024-03-06

Here are some Guix service definitions that demonstrate custom one-shot services that run at boot in Guix.

These examples were tested with Guix commit 956f44b931f050fbffca51737000b9049808b3ca.

Warning

These examples “worked for me” but I am not a Guix expert.

Using plain-file

Following is a single operating-system definition that contains four example services in a single file. Non-Scheme scripts are embedded with the plain-file procedure.

This definition can be tested by saving it to a file and running $(guix system vm $file --no-graphic) in a shell. This will set up and run (with Qemu) a virtual machine with the specified operating system. You can log in as root (the password is empty) if you want to. To exit, run shutdown as root or kill the virtual machine by typing Ctrl-a x.

(use-modules (gnu)
             (gnu services shepherd))
(use-package-modules admin bash bootloaders python)

;; Boot service written in Guile (runs as part of the boot script
;; rather than under Shepherd). The random number is the same at each
;; boot.
(define (guile-boot-hello-gexp _)
  #~(begin
      (let ((random_str (number->string (random 1000))))
        (display (string-append "guile-boot-service: Hello! " random_str "\n")
                 (current-error-port)))))
(define guile-boot-hello-service-type
  (service-type (name 'say-hello)
                (extensions (list (service-extension boot-service-type
                                                     guile-boot-hello-gexp)))
                (default-value 0)
                (description "Say hello during boot.")))

;; Shepherd service written in Guile. The random number is the same at
;; each boot.
(define guile-hello-gexp
  #~(begin
      (let ((port (open-pipe* OPEN_WRITE
                              #$(file-append inetutils "/bin/logger")
                              "-plocal0.alert"))
            (random_str (number->string (random 1000))))
        (display (string-append "guile-service: Hello! " random_str "\n") port)
        (close-pipe port))))
(define guile-hello-service
  (simple-service 'guile-hello-service shepherd-root-service-type
                  (list (shepherd-service (auto-start? #t)
                                          (documentation
                                           "Say hello with Guile.")
                                          (one-shot? #t)
                                          (provision '(guile-hello-service))
                                          (requirement '(syslogd))
                                          (respawn? #f)
                                          (start #~(lambda _
                                                     #$guile-hello-gexp))))))

;; Shepherd service written in Bash.
(define %bash-hello
  (plain-file "hello.sh" "
logger -plocal0.alert bash-service: Hello! $RANDOM
"))
(define bash-hello-service
  (simple-service 'bash-hello-service shepherd-root-service-type
                  (list (shepherd-service (auto-start? #t)
                                          (documentation
                                           "Say hello with Bash.")
                                          (one-shot? #t)
                                          (provision '(bash-hello-service))
                                          (requirement '(syslogd))
                                          (respawn? #f)
                                          (start
                                           #~(make-forkexec-constructor
                                              (list #$(file-append
                                                       bash-minimal
                                                       "/bin/bash")
                                                    #$%bash-hello)))))))

;; Shepherd service written in Python.
(define %python-hello
  (plain-file "hello.py"
              "
import random
import syslog

random_str = str(random.randint(0, 1000))
syslog.openlog(facility=syslog.LOG_LOCAL0)
syslog.syslog(syslog.LOG_ALERT, f'python-service: Hello! {random_str}')
"))
(define python-hello-service
  (simple-service 'python-hello-service shepherd-root-service-type
                  (list (shepherd-service (auto-start? #t)
                                          (documentation
                                           "Say hello with Python.")
                                          (one-shot? #t)
                                          (provision '(python-hello-service))
                                          (requirement '(syslogd))
                                          (respawn? #f)
                                          (start
                                           #~(make-forkexec-constructor
                                              (list #$(file-append
                                                       python-minimal
                                                       "/bin/python3")
                                                    #$%python-hello)))))))

(operating-system
  (bootloader (bootloader-configuration
                (bootloader grub-bootloader)
                (targets '("/dev/vda"))
                (terminal-inputs '(console serial))
                (terminal-outputs '(console serial))
                (timeout 1)))
  (file-systems (cons (file-system
                        (mount-point "/")
                        (device "/dev/vda1")
                        (type "ext4")) %base-file-systems))
  (host-name "test-guix-vm")
  (kernel-arguments (cons "console=ttyS0" %default-kernel-arguments))
  (services
   (append (list (service guile-boot-hello-service-type) guile-hello-service
                 bash-hello-service python-hello-service) %base-services)))

Using local-file

To load a script from another file, use the local-file procedure. Replace the shepherd-service start field in the example above with the following.

(start #~(make-forkexec-constructor (list #$(file-append bash-minimal
                                                         "/bin/bash")
                                          #$(local-file "growpart.sh"))))

Credits

  • Florian Pelz and Felix Lechner on the Help-guix mailing list (this thread)

  • The Guix documentation and source code, in general

See also