From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp12.migadu.com ([2001:41d0:2:bcc0::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms5.migadu.com with LMTPS id uOGYO7mfe2O/mAAAbAwnHQ (envelope-from ) for ; Mon, 21 Nov 2022 16:56:42 +0100 Received: from aspmx1.migadu.com ([2001:41d0:2:bcc0::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp12.migadu.com with LMTPS id eKZzO7mfe2MaAgAAauVa8A (envelope-from ) for ; Mon, 21 Nov 2022 16:56:41 +0100 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 82A2B309BB for ; Mon, 21 Nov 2022 16:56:41 +0100 (CET) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ox99Z-0008B1-Pd; Mon, 21 Nov 2022 10:56:13 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ox94K-0000Xe-HO for guix-devel@gnu.org; Mon, 21 Nov 2022 10:50:48 -0500 Received: from smtpmciv3.myservices.hosting ([185.26.107.239]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ox94H-0002Yu-Mb for guix-devel@gnu.org; Mon, 21 Nov 2022 10:50:48 -0500 Received: from mail1.netim.hosting (unknown [185.26.106.172]) by smtpmciv3.myservices.hosting (Postfix) with ESMTP id 271E720285 for ; Mon, 21 Nov 2022 16:50:43 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by mail1.netim.hosting (Postfix) with ESMTP id BE58780097; Mon, 21 Nov 2022 16:50:43 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at mail1.netim.hosting Received: from mail1.netim.hosting ([127.0.0.1]) by localhost (mail1-1.netim.hosting [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id 8aZFgwAy71iw; Mon, 21 Nov 2022 16:50:43 +0100 (CET) Received: from guix-nuc.home.arpa (bl12-93-156.dsl.telepac.pt [85.245.93.156]) (Authenticated sender: lumen@makinata.eu) by mail1.netim.hosting (Postfix) with ESMTPSA id C273C80093; Mon, 21 Nov 2022 16:50:42 +0100 (CET) From: mirai@makinata.eu To: guix-devel@gnu.org Cc: Bruno Victal Subject: [PATCH 1/1] gnu: audio: Add mympd-service-type. Date: Mon, 21 Nov 2022 15:50:15 +0000 Message-Id: <032d0d21b86593a832f4c2344697ffe31fd33efe.1669044438.git.mirai@makinata.eu> X-Mailer: git-send-email 2.38.1 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Received-SPF: pass client-ip=185.26.107.239; envelope-from=mirai@makinata.eu; helo=smtpmciv3.myservices.hosting X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Mon, 21 Nov 2022 10:56:06 -0500 X-BeenThere: guix-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Development of GNU Guix and the GNU System distribution." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-devel-bounces+larch=yhetil.org@gnu.org Sender: guix-devel-bounces+larch=yhetil.org@gnu.org X-Migadu-Flow: FLOW_IN X-Migadu-Country: US ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1669046201; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:list-id:list-help: list-unsubscribe:list-subscribe:list-post; bh=uRVbyZXP/q88V980dZE5Y5Vb2M/KceNFXZiAdcJd7z8=; b=XKo3O40rrAhOrfeozNsIpIetbggFuDm1w2UQ0vy6IiFIx2qnsW3/Xlb6j4KGf8qv4bFdNS jC8e4zPdKVaxBbQJBYxYHpduQNCDaCxlFEFI5WnzBRnrKEsqXfmJH7G6CjYGznDc6MP7fx JkSrI4UR+D1gzNKTz5algt+Otdmry539993kBQ1rLQvUyS6lVxLpH8qhs8NaTL7//Bj3Yp 4797F6zwcS7pBHWnrnrRqrI9lwE/i4wUqmzWfyNvSDCzTVMAb6vISkj1idIaVwvZkm9G9t 3aM6zq5GlfzB3lqZmkjbT46uutGfgy61nge4iUvROk2UvmZgd+JB4cB4Ort2WQ== ARC-Seal: i=1; s=key1; d=yhetil.org; t=1669046201; a=rsa-sha256; cv=none; b=lJW2eOnGtW2jCP2MO3xtm46S+ixLIQSiAuyw++UihFbxv+XKq2C36JGoW6vVH9lbgyZaCk LaYjGo7cmTCJiLuVhUVdgZ2/OEp07QyujIYFWpJRMKSjgmrCHo0EnSWg8cnAjp8APEmPH2 qpuNaYWPBljpaEHWpjJN2LJAe8gIaxxY2kGdeYN4UkTgEph21eHy10kog+SNWjWgl5pz9t wKvThVqJvtqR1vjgT0Z+BrnbA/zSNq5Q20U7xoBl5t/sd5SimDZ7kTtsQLDocV60TxEN0U Yt89K2B1ran+DhUWRQNCYD+u0SJI+f9IxkvxwOlLRj1D0h5Z1JGqQrx9xi4XNQ== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=none; dmarc=none; spf=pass (aspmx1.migadu.com: domain of "guix-devel-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-devel-bounces+larch=yhetil.org@gnu.org" X-Migadu-Spam-Score: -1.81 Authentication-Results: aspmx1.migadu.com; dkim=none; dmarc=none; spf=pass (aspmx1.migadu.com: domain of "guix-devel-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-devel-bounces+larch=yhetil.org@gnu.org" X-Migadu-Queue-Id: 82A2B309BB X-Spam-Score: -1.81 X-Migadu-Scanner: scn1.migadu.com X-TUID: RdjNJZjwp9Mg From: Bruno Victal --- Potential issues: * The use 'define-maybe' [string | comma-separated-string] might interfere with other service definitions that are added over time. * Should 'log-file' used in 'shepherd-service' be a configuration parameter? * Service configuration is not in the 'store'. To understand this, myMPD uses a directory it calls 'working-directory' to store/read both the configuration and mutable data. So if it uses '/var/lib/mympd' as its "working-directory", it must read the configuration from a 'config' subdirectory ('/var/lib/mympd/config/'). When it comes to configuration, each directive corresponds to a file under this '/config' directory. How it sets/reads the values, myMPD provides two ways to do so: 1. It can read from the environment-variables iff the "config" directory within its 'working-directory' is empty. In this case, they are read and written to the corresponding file. Subsequent runs will ignore the environment variable values, even if they differ (with the exception of 'MYMPD_LOGLEVEL' which is always used). 2. Read directly from the files present within 'config' subdirectory. This is essentially what happens after the first launch described above. The current approach used erases this 'config' directory on every 'guix system reconfigure'. Downsides is that it is prone to overdelete things depending on further upstream developments and the expectation that every parameter is adjustable via environment variables (at the moment, there's one that can't be set but its of a peculiar nature that I'm not sure if it should be added to the store. It's an optional sha256 password hash. (https://jcorporation.github.io/myMPD/configuration/) * A warning that the default group used (nogroup) is duplicated. I reasoned that there's little point in creating a specific group that might end up unused yet allow for a custom group to be used if necessary. * 'comma-separated-string-list' code duplication. This function was lifted from 'gnu/services/mail.scm' but its already duplicated at 'gnu/services/cups.scm'. It doesn't seem to be an uncommon pattern used in configuration, maybe this could be added to 'gnu/services/configuration.scm' as a readily-available type for convenience? doc/guix.texi | 79 +++++++++++++++++++ gnu/services/audio.scm | 174 ++++++++++++++++++++++++++++++++++++++++- gnu/tests/audio.scm | 54 ++++++++++++- 3 files changed, 305 insertions(+), 2 deletions(-) diff --git a/doc/guix.texi b/doc/guix.texi index eaecfd0daa..10727d5196 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -109,6 +109,7 @@ Copyright @copyright{} 2022 Reily Siegel@* Copyright @copyright{} 2022 Simon Streit@* Copyright @copyright{} 2022 (@* Copyright @copyright{} 2022 John Kehayias@* +Copyright @copyright{} 2022 Bruno Victal@* Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or @@ -32889,6 +32890,84 @@ an HTTP audio streaming output. (port . "8080")))))))) @end lisp +@subsubheading myMPD + +@cindex MPD, web interface +@cindex myMPD service + +@uref{https://jcorporation.github.io/myMPD/, myMPD} is a service that provides a +mobile friendly web client for MPD. + +The following example shows a myMPD instance listening on port 80, with album cover caching disabled. + +@lisp +(service mympd-service-configuration + (mympd-configuration + (port 80) + (covercache-ttl 0))) +@end lisp + +@c auto-generated with (configuration->documentation) +@deftp {Data Type} mympd-configuration +Available @code{mympd-configuration} fields are: + +@table @asis +@item @code{package} (default: @code{mympd}) (type: file-like) +The package object of the myMPD server. + +@item @code{user} (default: @code{"mympd"}) (type: string) +Owner of the @code{mympd} process. + +@item @code{group} (default: @code{"nogroup"}) (type: string) +Owner's group of the @code{mympd} process. + +@item @code{work-directory} (default: @code{"/var/lib/mympd"}) (type: string) +Where myMPD will store its data. + +@item @code{cache-directory} (default: @code{"/var/cache/mympd"}) (type: string) +Where myMPD will store its cache. + +@item @code{acl} (type: maybe-comma-separated-string-list) +ACL to access the myMPD webserver. See +@uref{https://jcorporation.github.io/myMPD/configuration/acl,myMPD ACL} +for syntax. + +@item @code{covercache-ttl} (default: @code{31}) (type: number) +How long to keep cached covers. Setting to @code{0} disables caching. + +@item @code{host} (default: @code{"[::]"}) (type: string) +Host name to listen on. + +@item @code{port} (default: @code{80}) (type: number) +Port to listen on. + +@item @code{loglevel} (default: @code{5}) (type: number) +Log level to output logs, possible values: @code{0} to @code{7}. + +@item @code{lualibs} (default: @code{"all"}) (type: string) +See +@uref{https://jcorporation.github.io/myMPD/scripting/#lua-standard-libraries}. + +@item @code{script-acl} (default: @code{("+127.0.0.1")}) (type: comma-separated-string-list) +ACL to access the myMPD script backend. + +@item @code{ssl?} (default: @code{#f}) (type: boolean) +SSL/TLS support + +@item @code{ssl-port} (default: @code{443}) (type: number) +Port to listen for HTTPS. + +@item @code{ssl-cert} (type: maybe-string) +Path to PEM encoded X.509 SSL/TLS certificate (public key). + +@item @code{ssl-key} (type: maybe-string) +Path to PEM encoded SSL/TLS private key. + +@end table + +@end deftp +@c end auto-generated + @node Virtualization Services @subsection Virtualization Services diff --git a/gnu/services/audio.scm b/gnu/services/audio.scm index c60053f33c..a793bf253a 100644 --- a/gnu/services/audio.scm +++ b/gnu/services/audio.scm @@ -2,6 +2,7 @@ ;;; Copyright © 2017 Peter Mikkelsen ;;; Copyright © 2019 Ricardo Wurmus ;;; Copyright © 2020 Ludovic Courtès +;;; Copyright © 2022 Bruno Victal ;;; ;;; This file is part of GNU Guix. ;;; @@ -21,6 +22,7 @@ (define-module (gnu services audio) #:use-module (guix gexp) #:use-module (gnu services) + #:use-module (gnu services configuration) #:use-module (gnu services shepherd) #:use-module (gnu system shadow) #:use-module (gnu packages admin) @@ -28,11 +30,15 @@ (define-module (gnu services audio) #:use-module (guix records) #:use-module (ice-9 match) #:use-module (ice-9 format) + #:use-module (srfi srfi-1) #:export (mpd-output mpd-output? mpd-configuration mpd-configuration? - mpd-service-type)) + mpd-service-type + + mympd-service-type + mympd-configuration)) ;;; Commentary: ;;; @@ -197,3 +203,169 @@ (define mpd-service-type (service-extension activation-service-type mpd-service-activation))) (default-value (mpd-configuration)))) + + +(define (comma-separated-string-list? value) + (and (list? value) + (and-map (lambda (x) + (and (string? x) (not (string-index x #\,)))) + value))) + +(define mympd-field-to-env + (match-lambda + ('acl "MYMPD_ACL") + ('covercache-ttl "MYMPD_COVERCACHE_KEEP_DAYS") + ('host "MYMPD_HTTP_HOST") + ('port "MYMPD_HTTP_PORT") + ('loglevel "MYMPD_LOGLEVEL") + ('lualibs "MYMPD_LUALIBS") + ('script-acl "MYMPD_SCRIPTACL") + ('ssl? "MYMPD_SSL") + ('ssl-port "MYMPD_SSL_PORT") + ('ssl-cert "MYMPD_SSL_CERT") + ('ssl-key "MYMPD_SSL_KEY"))) + +(define (mympd-serialize-string field-name value) + (string-join (list (mympd-field-to-env field-name) value) "=")) + +(define (mympd-serialize-number field-name value) + (mympd-serialize-string field-name (number->string value))) + +(define (mympd-serialize-boolean field-name value) + (mympd-serialize-string field-name (if value "true" "false"))) + +(define (mympd-serialize-comma-separated-string-list field-name value) + (mympd-serialize-string field-name (string-join value ","))) + +(define (mympd-serialize-configuration config fields) + (remove string-null? + (map (lambda (field) + ((configuration-field-serializer field) + (configuration-field-name field) + ((configuration-field-getter field) config))) + (filter-configuration-fields fields + '(package user group work-directory cache-directory) + #t)))) + +(define-maybe string (prefix mympd-)) +(define-maybe comma-separated-string-list + (prefix mympd-)) + +(define-configuration mympd-configuration + (package + (file-like mympd) + "The package object of the myMPD server." + empty-serializer) + (user + (string "mympd") + "Owner of the @code{mympd} process." + empty-serializer) + (group + (string "nogroup") + "Owner's group of the @code{mympd} process." + empty-serializer) + (work-directory + (string "/var/lib/mympd") + "Where myMPD will store its data." + empty-serializer) + (cache-directory + (string "/var/cache/mympd") + "Where myMPD will store its cache." + empty-serializer) + (acl + maybe-comma-separated-string-list + "ACL to access the myMPD webserver. See @uref{https://jcorporation.github.io/myMPD/configuration/acl,myMPD ACL} for syntax.") + (covercache-ttl + (number 31) + "How long to keep cached covers. Setting to @code{0} disables caching.") + (host + (string "[::]") + "Host name to listen on.") + (port + (number 80) + "Port to listen on.") + (loglevel + (number 5) + "Log level to output logs, possible values: @code{0} to @code{7}.") + (lualibs + (string "all") + "See @url{https://jcorporation.github.io/myMPD/scripting/#lua-standard-libraries}.") + (script-acl + (comma-separated-string-list '("+127.0.0.1")) + "ACL to access the myMPD script backend.") + (ssl? + (boolean #f) + "SSL/TLS support") + (ssl-port + (number 443) + "Port to listen for HTTPS.") + (ssl-cert + maybe-string + "Path to PEM encoded X.509 SSL/TLS certificate (public key).") + (ssl-key + maybe-string + "Path to PEM encoded SSL/TLS private key.") + (prefix mympd-)) + + +(define (mympd-activation config) + (match-record config (user work-directory cache-directory) + #~(begin + (use-modules (guix build utils)) + + (let* ((pw (getpwnam #$user)) + (uid (passwd:uid pw)) + (gid (passwd:gid pw))) + (for-each (lambda (dir) + (mkdir-p dir) + (chown dir uid gid)) + (list #$work-directory #$cache-directory))) + + ;; remove stale config + (let ((config-directory #$(string-append work-directory "/config"))) + (when (file-exists? config-directory) + (delete-file-recursively config-directory)))))) + + +(define (mympd-shepherd-service config) + (match-record config + (user + work-directory + cache-directory) + (shepherd-service + (documentation "Run the myMPD daemon.") + (requirement '(loopback user-processes)) + (provision '(mympd)) + (start #~(make-forkexec-constructor + (list #$(file-append mympd "/bin/mympd") + "--user" #$user + "--workdir" #$work-directory + "--cachedir" #$cache-directory) + #:environment-variables '#$(mympd-serialize-configuration config + mympd-configuration-fields) + #:log-file "/var/log/mympd.log")) + (stop #~(make-kill-destructor))))) + +(define (mympd-accounts config) + (match-record config (user group) + (list (user-group (name group) + (system? #t)) + (user-account (name user) + (group group) + (system? #t) + (comment "myMPD user") + (home-directory "/var/empty") + (shell (file-append shadow "/sbin/nologin")))))) + +(define mympd-service-type + (service-type + (name 'mympd) + (extensions + (list (service-extension shepherd-root-service-type + (compose list mympd-shepherd-service)) + (service-extension account-service-type + mympd-accounts) + (service-extension activation-service-type + mympd-activation))) + (description "Run myMPD, a frontend for MPD. (Music Player Daemon)") + (default-value (mympd-configuration)))) diff --git a/gnu/tests/audio.scm b/gnu/tests/audio.scm index 8aa6d1e818..701496ee23 100644 --- a/gnu/tests/audio.scm +++ b/gnu/tests/audio.scm @@ -1,5 +1,6 @@ ;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2017 Peter Mikkelsen +;;; Copyright © 2022 Bruno Victal ;;; ;;; This file is part of GNU Guix. ;;; @@ -22,9 +23,11 @@ (define-module (gnu tests audio) #:use-module (gnu system vm) #:use-module (gnu services) #:use-module (gnu services audio) + #:use-module (gnu services networking) #:use-module (gnu packages mpd) #:use-module (guix gexp) - #:export (%test-mpd)) + #:export (%test-mpd + %test-mympd)) (define %mpd-os (simple-operating-system @@ -76,3 +79,52 @@ (define %test-mpd (name "mpd") (description "Test that the mpd can run and be connected to.") (value (run-mpd-test)))) + + +(define (run-mympd-test) + (define os (marionette-operating-system + (simple-operating-system (service dhcp-client-service-type) + (service mympd-service-type)) + #:imported-modules '((gnu services herd)))) + + (define vm + (virtual-machine + (operating-system os) + (port-forwardings '((8080 . 80))))) + + (define test + (with-imported-modules '((gnu build marionette)) + #~(begin + (use-modules (srfi srfi-64) + (srfi srfi-8) + (web client) + (web response) + (gnu build marionette)) + + (define marionette + (make-marionette (list #$vm))) + + (test-runner-current (system-test-runner #$output)) + (test-begin "mympd") + (test-assert "service is running" + (marionette-eval '(begin + (use-modules (gnu services herd)) + + (start-service 'mympd)) + marionette)) + + (test-assert "HTTP port ready" + (wait-for-tcp-port 80 marionette)) + + (test-equal "http-head" + 200 + (receive (x _) (http-head "http://localhost:8080") (response-code x))) + + (test-end)))) + (gexp->derivation "mympd-test" test)) + +(define %test-mympd + (system-test + (name "mympd") + (description "Connect to a running myMPD service.") + (value (run-mympd-test)))) -- 2.38.1