Subject: [PATCH v3 2/2] services: mpd: Support socket activation.

From: Liliana Marie Prikler <[email-masked]>
Date: Sat, 23 Apr 2022 16:39:59 +0200

* gnu/services/audio.scm (<mpd-endpoint>): New record.
(<mpd-configuration>)[port, address]: Removed fields.
[endpoints]: New field.
(sanitize-mpd-endpoints, mpd-configuration-shepherd-endpoints): New variables.
(mpd-shepherd-service): Use mpd-configuration-shepherd-endpoints.
* doc/guix.texi (Music Player Daemon): Document it.
---
 doc/guix.texi          | 23 ++++++++--
 gnu/services/audio.scm | 98 +++++++++++++++++++++++++++++++++---------
 2 files changed, 97 insertions(+), 24 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 30cc3b6d45..d5b9de0de2 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -32192,15 +32192,30 @@ The location of the file that stores current MPD's state.
 @item @code{sticker-file} (default: @code{"~/.mpd/sticker.sql"})
 The location of the sticker database.
 
-@item @code{port} (default: @code{"6600"})
-The port to run mpd on.
+@item @code{endpoints} (default: @code{(list (mpd-endpoint))})
+The endpoints to bind MPD to.  This can be an mpd-endpoint (see below)
+or a list of shepherd endpoints.  If using shepherd endpoints, MPD
+will be spawned as a systemd-style service rather than as a
+forkexec-style one.  This delays the start of MPD until the first client
+connects.
+
+@item @code{outputs} (default: @code{"(list (mpd-output))"})
+The audio outputs that MPD can use.  By default this is a single output using pulseaudio.
+
+@end table
+@end deftp
+
+@deftp {Data Type} mpd-endpoint
+Data type representing the configuration of @command{mpd}.
+
+@table @asis
 
 @item @code{address} (default: @code{"any"})
 The address that mpd will bind to.  To use a Unix domain socket,
 an absolute path can be specified here.
 
-@item @code{outputs} (default: @code{"(list (mpd-output))"})
-The audio outputs that MPD can use.  By default this is a single output using pulseaudio.
+@item @code{port} (default: @code{"6600"})
+The port to run mpd on.
 
 @end table
 @end deftp
diff --git a/gnu/services/audio.scm b/gnu/services/audio.scm
index c60053f33c..6e8fd460a1 100644
--- a/gnu/services/audio.scm
+++ b/gnu/services/audio.scm
@@ -20,6 +20,8 @@
 
 (define-module (gnu services audio)
   #:use-module (guix gexp)
+  #:use-module (guix diagnostics)
+  #:use-module (guix i18n)
   #:use-module (gnu services)
   #:use-module (gnu services shepherd)
   #:use-module (gnu system shadow)
@@ -28,8 +30,14 @@ (define-module (gnu services audio)
   #:use-module (guix records)
   #:use-module (ice-9 match)
   #:use-module (ice-9 format)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-26)
+  #:use-module (srfi srfi-34)
+  #:use-module (srfi srfi-35)
   #:export (mpd-output
             mpd-output?
+            mpd-endpoint
+            mpd-endpoint?
             mpd-configuration
             mpd-configuration?
             mpd-service-type))
@@ -59,6 +67,30 @@ (define-record-type* <mpd-output>
   (extra-options mpd-output-extra-options
                  (default '())))
 
+(define-record-type* <mpd-endpoint>
+  mpd-endpoint make-mpd-endpoint
+  mpd-endpoint?
+  (address mpd-endpoint-address (default "any"))
+  (port    mpd-endpoint-port (default "6600")))
+
+(define-with-syntax-properties (sanitize-mpd-endpoints (endpoints properties))
+  (match endpoints
+    ((? mpd-endpoint? endpoint) (list endpoint))
+    (((? mpd-endpoint?)) endpoints)
+    (((? mpd-endpoint?) . _)
+     (raise (make-compound-condition
+             (condition (&message
+                         (message
+                          (G_ "\
+<mpd-endpoint> is mutually exclusive with other endpoints."))))
+             (condition (&error-location
+                         (location (source-properties->location properties))))
+             (condition (&fix-hint
+                         (hint "\
+Make sure you only need one endpoint or use shepherd endpoints instead."))))))
+    (((? shepherd-endpoint?) . (? (cute every shepherd-endpoint? <>)))
+     endpoints)))
+
 (define-record-type* <mpd-configuration>
   mpd-configuration make-mpd-configuration
   mpd-configuration?
@@ -74,10 +106,9 @@ (define-record-type* <mpd-configuration>
                 (default "~/.mpd/state"))
   (sticker-file mpd-configuration-sticker-file
                 (default "~/.mpd/sticker.sql"))
-  (port         mpd-configuration-port
-                (default "6600"))
-  (address      mpd-configuration-address
-                (default "any"))
+  (endpoints    mpd-configuration-endpoints     ; list of <shepherd-endpoint>
+                (default (list (mpd-endpoint))) ;       | <mpd-endpoint>
+                (sanitize sanitize-mpd-endpoints))
   (outputs      mpd-configuration-outputs
                 (default (list (mpd-output)))))
 
@@ -124,9 +155,12 @@ (define (mpd-config->file config)
                   ("playlist_directory" ,mpd-configuration-playlist-dir)
                   ("db_file" ,mpd-configuration-db-file)
                   ("state_file" ,mpd-configuration-state-file)
-                  ("sticker_file" ,mpd-configuration-sticker-file)
-                  ("port" ,mpd-configuration-port)
-                  ("bind_to_address" ,mpd-configuration-address))))))
+                  ("sticker_file" ,mpd-configuration-sticker-file)))
+           (match (mpd-configuration-endpoints config)
+             (($ <mpd-endpoint> address port)
+              `(("bind_to_address" ,address)
+                ("port" ,port)))
+             (_ '())))))
 
 (define (mpd-file-name config file)
   "Return a path in /var/run/mpd/ that is writable
@@ -135,24 +169,48 @@ (define (mpd-file-name config file)
                  (mpd-configuration-user config)
                  "/" file))
 
+(define (mpd-configuration-shepherd-endpoints config)
+  "Return @code{(mpd-configuration-endpoints config)}"
+  (let ((endpoints (mpd-configuration-endpoints config)))
+    (match endpoints
+      (((? shepherd-endpoint?) . _) endpoints)
+      (_ #f))))
+
 (define (mpd-shepherd-service config)
   (shepherd-service
    (documentation "Run the MPD (Music Player Daemon)")
    (requirement '(user-processes))
    (provision '(mpd))
-   (start #~(make-forkexec-constructor
-             (list #$(file-append mpd "/bin/mpd")
-                   "--no-daemon"
-                   #$(mpd-config->file config))
-             #:environment-variables
-             ;; Required to detect PulseAudio when run under a user account.
-             (list (string-append
-                    "XDG_RUNTIME_DIR=/run/user/"
-                    (number->string
-                     (passwd:uid
-                      (getpwnam #$(mpd-configuration-user config))))))
-             #:log-file #$(mpd-file-name config "log")))
-   (stop  #~(make-kill-destructor))))
+   (start (if (mpd-configuration-shepherd-endpoints config)
+              #~(make-systemd-constructor
+                 (list #$(file-append mpd "/bin/mpd")
+                       "--systemd"
+                       #$(mpd-config->file config))
+                 (list #$@(map shepherd-endpoint->sexp
+                               (mpd-configuration-shepherd-endpoints config)))
+                 #:environment-variables
+                 ;; Required to detect PulseAudio when run under a user account.
+                 (list (string-append
+                        "XDG_RUNTIME_DIR=/run/user/"
+                        (number->string
+                         (passwd:uid
+                          (getpwnam #$(mpd-configuration-user config))))))
+                 #:log-file #$(mpd-file-name config "log"))
+              #~(make-forkexec-constructor
+                 (list #$(file-append mpd "/bin/mpd")
+                       "--no-daemon"
+                       #$(mpd-config->file config))
+                 #:environment-variables
+                 ;; Required to detect PulseAudio when run under a user account.
+                 (list (string-append
+                        "XDG_RUNTIME_DIR=/run/user/"
+                        (number->string
+                         (passwd:uid
+                          (getpwnam #$(mpd-configuration-user config))))))
+                 #:log-file #$(mpd-file-name config "log"))))
+   (stop  (if (mpd-configuration-shepherd-endpoints config)
+              #~(make-systemd-destructor)
+              #~(make-kill-destructor)))))
 
 (define (mpd-service-activation config)
   (with-imported-modules '((guix build utils))
-- 
2.37.3