From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp2.migadu.com ([2001:41d0:403:4876::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms13.migadu.com with LMTPS id +LWqBswUqmZtRAAAe85BDQ:P1 (envelope-from ) for ; Wed, 31 Jul 2024 10:41:16 +0000 Received: from aspmx1.migadu.com ([2001:41d0:403:4876::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp2.migadu.com with LMTPS id +LWqBswUqmZtRAAAe85BDQ (envelope-from ) for ; Wed, 31 Jul 2024 12:41:16 +0200 X-Envelope-To: larch@yhetil.org Authentication-Results: aspmx1.migadu.com; dkim=fail ("headers rsa verify failed") header.d=debbugs.gnu.org header.s=debbugs-gnu-org header.b=RxlJQFgY; dkim=fail ("headers rsa verify failed") header.d=fabionatali.com header.s=gm1 header.b=ohygmtAk; spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=gnu.org ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1722422476; h=from:from:sender:sender:reply-to: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:resent-cc: resent-from:resent-sender:resent-message-id:list-id:list-help: list-unsubscribe:list-subscribe:list-post:dkim-signature; bh=4K8dSUFFBNkrUZblC/cKmRl7bvPX+pkT3tiiKs5YZog=; b=ZPPz1+Lir38gwevUm2rCgrYoDPaz4mdsnLdk5ZYpmWL/cFZB6/2tbEWrsVjRuD5quYB7HA i+5WR5CKoH7Sv57efuRrDoLZtYMwdaA8SrdvLq46HW6zSxgAcZz7kmLwMdrNpp4QYiK4fU lW7abwdHX/Ib95VD4JqUBXHUu4e5KtnQXdRjb4y/x+flPQPD2RPx3xZm/pK+KNDXWrAuu8 77E66yR+N7/WH7A+m+6cBb8dk0+WLTxluCtApuw+RF0kJs7wL1CxklxrNEoJHuh9XUCXlY dhbGsbEe7QbXAPvV/QxC2ltru9K1RHR0KhgrU5jEDkPSQAL5eQO+OL7crgAhmw== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=fail ("headers rsa verify failed") header.d=debbugs.gnu.org header.s=debbugs-gnu-org header.b=RxlJQFgY; dkim=fail ("headers rsa verify failed") header.d=fabionatali.com header.s=gm1 header.b=ohygmtAk; spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=gnu.org ARC-Seal: i=1; s=key1; d=yhetil.org; t=1722422476; a=rsa-sha256; cv=none; b=tL8KtqI/8S9rDm36JWTtPdLcndxaDNVtfKogRc6HVtzjN4pbREyZLk2Q1sEw6+LLD6vf2F jiV0QHxiVrflWtfbIWGQiMgxE8TtgE0muqlJgPppP/kY7GsVb63GW+T2Xeg4/VULqcbtiN s6PRwh++mIzcPPiIWpgIQkwu7NaSqQo/jR1C5YXBjlGu4iqifUvUBj75N9UsBWx417VKhB KUqWJH0LMhUaZ4DE3Uw2M1mlxMRpXvCN0DDHOgo2oXWZFLra7xOhghthshHyHV93WGkujD wOZXeQ/lyLUmaaMPdJBcLqY2ggVh33vmz6edwsgF2BaKxI8U8R5uGa1UuAfsfg== 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 C2A1D6C1C5 for ; Wed, 31 Jul 2024 12:41:15 +0200 (CEST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sZ6lF-00045d-OK; Wed, 31 Jul 2024 06:40:49 -0400 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 1sZ6lE-00045N-CU for guix-patches@gnu.org; Wed, 31 Jul 2024 06:40:48 -0400 Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1sZ6lD-0008PC-Lc; Wed, 31 Jul 2024 06:40:47 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debbugs.gnu.org; s=debbugs-gnu-org; h=MIME-Version:Date:From:To:Subject; bh=4K8dSUFFBNkrUZblC/cKmRl7bvPX+pkT3tiiKs5YZog=; b=RxlJQFgYz/jo6F0UtPZgxSL+CMBecNA3SUoVKI/OpA1szweVGb/2ACBzwQmq8hRIpwzB0ByHYKw9huBZkK6mdtdH/9+sCcicB3Bb46P41d+RgDRj3ivbzg+C9OPR3ufUfdzk/iCbUCclffJ138IzOL9VocE4C5Fh0NU1Ch/8flpjpWT2xbCVZf2jRxY1wMTOCIQTOq3gAY555nm8yea2qr78sC7MDN1e7NFRvK57LxmTfSbMTm2cZI49R4/CLKmqGZHyttXKwtu3dU5+jg+Wf20gTUo0t9fL8eWYywdJVVPF5zJJCzQYwrhisfNMSSq+KdruS3fVo6H0ptr3SgZMWQ==; Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1sZ6lS-0004Cj-2Q; Wed, 31 Jul 2024 06:41:02 -0400 X-Loop: help-debbugs@gnu.org Subject: [bug#72398] [PATCH] services: Add readymedia-service-type. Resent-From: Fabio Natali Original-Sender: "Debbugs-submit" Resent-CC: pelzflorian@pelzflorian.de, ludo@gnu.org, matt@excalamus.com, maxim.cournoyer@gmail.com, guix-patches@gnu.org Resent-Date: Wed, 31 Jul 2024 10:41:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 72398 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: 72398@debbugs.gnu.org Cc: Fabio Natali , Florian Pelz , Ludovic =?UTF-8?Q?Court=C3=A8s?= , Matthew Trzcinski , Maxim Cournoyer X-Debbugs-Original-To: guix-patches@gnu.org X-Debbugs-Original-Xcc: Florian Pelz , Ludovic =?UTF-8?Q?Court=C3=A8s?= , Matthew Trzcinski , Maxim Cournoyer Received: via spool by submit@debbugs.gnu.org id=B.172242245216136 (code B ref -1); Wed, 31 Jul 2024 10:41:01 +0000 Received: (at submit) by debbugs.gnu.org; 31 Jul 2024 10:40:52 +0000 Received: from localhost ([127.0.0.1]:49248 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1sZ6lH-0004CB-5c for submit@debbugs.gnu.org; Wed, 31 Jul 2024 06:40:52 -0400 Received: from lists.gnu.org ([209.51.188.17]:45288) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1sZ6lE-0004C2-8B for submit@debbugs.gnu.org; Wed, 31 Jul 2024 06:40:49 -0400 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 1sZ6ky-00043E-Ep for guix-patches@gnu.org; Wed, 31 Jul 2024 06:40:32 -0400 Received: from relay4-d.mail.gandi.net ([2001:4b98:dc4:8::224]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sZ6ku-0008IO-2P for guix-patches@gnu.org; Wed, 31 Jul 2024 06:40:32 -0400 Received: by mail.gandi.net (Postfix) with ESMTPSA id 6DB65E000E; Wed, 31 Jul 2024 10:40:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fabionatali.com; s=gm1; t=1722422422; h=from:from: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; bh=4K8dSUFFBNkrUZblC/cKmRl7bvPX+pkT3tiiKs5YZog=; b=ohygmtAkRYXIVFbvGh+EZV4qyY5XG0DINOSJMZocSb0AYOCh5XZTM7DvKhDZnyvi1cy8ep zgrumlTcmXB+PQoDT89ltXiZ6H+TtGHe1KElVlSBD8xQyxfZMZAlhzrq2XzB/erUZf1PVC GIpci+nV4qwRWrHC4Lg6a7W8iegz0eYLUiHauPIr7Cpg/cVaniWSIj1+82xkeZeBiAeP6E ok09cWw4OfPOrFXIaVrP+UJ8o8hzDGo6DZI8VZSNqWjIWjzMrlKqQirNe1smnSTFJAyIFX yxFL9+UezmydV5BfokDTvsW2+5xQCkQN6956B+EP/EumBQkKncjaGh3+MugPjQ== Date: Wed, 31 Jul 2024 11:27:35 +0100 Message-ID: <4fee1c18adcfd29d40d5b557bf52db0e531c3f16.1722421592.git.me@fabionatali.com> X-Mailer: git-send-email 2.45.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-GND-Sasl: me@fabionatali.com Received-SPF: pass client-ip=2001:4b98:dc4:8::224; envelope-from=me@fabionatali.com; helo=relay4-d.mail.gandi.net X-Spam_score_int: -27 X-Spam_score: -2.8 X-Spam_bar: -- X-Spam_report: (-2.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-to: Fabio Natali X-ACL-Warn: , Fabio Natali via Guix-patches From: Fabio Natali via Guix-patches via Errors-To: guix-patches-bounces+larch=yhetil.org@gnu.org Sender: guix-patches-bounces+larch=yhetil.org@gnu.org X-Migadu-Country: US X-Migadu-Flow: FLOW_IN X-Spam-Score: -5.73 X-Migadu-Queue-Id: C2A1D6C1C5 X-Migadu-Scanner: mx10.migadu.com X-Migadu-Spam-Score: -5.73 X-TUID: vJFj4QSFakqI * gnu/services/upnp.scm: New file. * gnu/local.mk: Add this. * doc/guix.texi: Document this. Change-Id: I87c17d3afeaf94b5294b4add5649701b087b6897 --- Hi! 👋 This is to add 'readymedia-service-type'. ReadyMedia⁰ (formerly known as MiniDLNA) is a DLNA/UPnP-AV media server. The project’s daemon, 'minidlnad', can serve media files (audio, pictures, and video) to DLNA/UPnP-AV clients available in the network. 'readymedia-service-type' is a Guix service that wraps around ReadyMedia’s 'minidlnad'. For increased security, the service makes use of 'least-authority-wrapper' which limits the resources that the daemon has access to. The daemon runs as the readymedia unprivileged user, which is a member of the readymedia group. The 'readymedia-configuration' record gives the opportunity to configure various aspects, such as the media folders to serve content from, the service name, the service port, etc. An 'extra-config' field acts as a wildcard for all other ReadyMedia options that are not mapped into the record. I'm not very happy about the way some of the configuration options are hardcoded (e.g. the user, the cache and log folders). I thought this is "good enough" for now, but I'm looking forward to your comments. This is my first Guix service (yay!) so feedback is particularly welcome. Have a lovely day. Cheers, Fabio. ⁰ https://sourceforge.net/projects/minidlna/ PS: Guix's 'minidlnad' has a small bug at the moment. This patch requires this other fix to work properly: https://lists.gnu.org/archive/html/guix-patches/2024-07/msg01239.html doc/guix.texi | 93 +++++++++++++++++++++++ gnu/local.mk | 1 + gnu/services/upnp.scm | 170 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 264 insertions(+) create mode 100644 gnu/services/upnp.scm diff --git a/doc/guix.texi b/doc/guix.texi index 41814042f5..026246eeda 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -129,6 +129,7 @@ Copyright @copyright{} 2024 Richard Sent@* Copyright @copyright{} 2024 Dariqq@* Copyright @copyright{} 2024 Denis 'GNUtoo' Carikli@* +Copyright @copyright{} 2024 Fabio Natali@* Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or @@ -41594,6 +41595,98 @@ Miscellaneous Services @end deftp +@c %end of fragment + +@cindex DLNA/UPnP +@subsubheading DLNA/UPnP Services + +The @code{(gnu services upnp)} module offers services related to the +DLNA and UPnP-VA networking protocols. For now, it provides the +@code{readymedia-service-type}. + +@uref{https://sourceforge.net/projects/minidlna/, ReadyMedia} +(formerly known as MiniDLNA) is a DLNA/UPnP-AV media server. The +project's daemon, @code{minidlnad}, can serve media files (audio, +pictures, and video) to DLNA/UPnP-AV clients available in the network. + +@code{readymedia-service-type} is a Guix service that wraps around +ReadyMedia's @code{minidlnad}. For increased security, the service +makes use of @code{least-authority-wrapper} which limits the resources +that the daemon has access to. The daemon runs as the +@code{readymedia} unprivileged user, which is a member of the +@code{readymedia} group. + +Consider the following configuration: + +@lisp +(use-service-modules upnp @dots{}) + +(operating-system + ;; @dots{} + (services + (list + (service readymedia-service-type + (readymedia-configuration + (media-dirs + (list (readymedia-media-dir (path "/media/audio") + (type "A")) + (readymedia-media-dir (path "/media/video") + (type "V")) + (readymedia-media-dir (path "/media/misc")))))) +@end lisp + +This sets up the ReadyMedia daemon to serve files from the media +folders specified in @code{media-dirs}. The @code{media-dirs} field +is mandatory. All other fields (such as network ports and the server +name) come with a predefined default and can be omitted. + +@c %start of fragment + +@deftp {Data Type} readymedia-configuration +Available @code{readymedia-configuration} fields are: + +@table @asis +@item @code{readymedia} (default: @code{readymedia}) (type: package) +The ReadyMedia package to be used for the service. + +@item @code{friendly-name} (default: @code{#f}) (type: maybe-string) +A custom name that will be displayed on connected clients. + +@item @code{media-dirs} (type: list) +The list of media folders to serve content from. Each item is a +@code{readymedia-media-dir}. + +@item @code{port} (default: @code{#f}) (type: maybe-integer) +A custom port that the service will be listening on. + +@item @code{extra-config} (default: @code{'()}) (type: list-of-strings) +A list of further options, to be passed as key-value strings as +accepted by ReadyMedia. + +@end table + +@end deftp + +@c %end of fragment + +@c %start of fragment + +@deftp {Data Type} readymedia-media-dir +A @code{media-dirs} entry includes a @code{path} and, optionally, a +media type string. + +@table @asis +@item @code{path} (type: string) +The media folder location. + +@item @code{type} (default: @code{""}) (type: string) +Valid media types are @code{"A"} for audio, @code{"P"} for pictures, +@code{"V"} for video, and a combination of those individual letters +for mixed types. An empty string means no type specified. + +@end table + +@end deftp @c %end of fragment diff --git a/gnu/local.mk b/gnu/local.mk index fac7b5973b..2da8ec3be3 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -749,6 +749,7 @@ GNU_SYSTEM_MODULES = \ %D%/services/syncthing.scm \ %D%/services/sysctl.scm \ %D%/services/telephony.scm \ + %D%/services/upnp.scm \ %D%/services/version-control.scm \ %D%/services/vnc.scm \ %D%/services/vpn.scm \ diff --git a/gnu/services/upnp.scm b/gnu/services/upnp.scm new file mode 100644 index 0000000000..49f176861e --- /dev/null +++ b/gnu/services/upnp.scm @@ -0,0 +1,170 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2024 Fabio Natali +;;; +;;; 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 . + +(define-module (gnu services upnp) + #:use-module (gnu build linux-container) + #:use-module (gnu packages admin) + #:use-module (gnu packages upnp) + #:use-module (gnu services admin) + #:use-module (gnu services base) + #:use-module (gnu services shepherd) + #:use-module (gnu services) + #:use-module (gnu system file-systems) + #:use-module (gnu system shadow) + #:use-module (guix gexp) + #:use-module (guix least-authority) + #:use-module (guix records) + #:export (readymedia-configuration + readymedia-configuration-readymedia + readymedia-configuration-friendly-name + readymedia-configuration-media-dirs + readymedia-configuration-port + readymedia-configuration-extra-config + readymedia-configuration? + readymedia-media-dir + readymedia-media-dir-path + readymedia-media-dir-type + readymedia-media-dir? + readymedia-service-type)) + +;;; Commentary: +;;; +;;; UPnP services. +;;; +;;; Code: + +(define %readymedia-cache-dir "/var/cache/readymedia") +(define %readymedia-log-dir "/var/log/readymedia") +(define %readymedia-user-account "readymedia") +(define %readymedia-user-group "readymedia") + +(define-record-type* + readymedia-configuration make-readymedia-configuration + readymedia-configuration? + (readymedia readymedia-configuration-readymedia (default readymedia)) + (friendly-name readymedia-configuration-friendly-name (default #f)) + (media-dirs readymedia-configuration-media-dirs) + (port readymedia-configuration-port (default #f)) + (extra-config readymedia-configuration-extra-config (default '()))) + +;; READYMEDIA-MEDIA-DIR is a record that indicates path and media type of a +;; media folder. The media type string can be empty (no media type specified), +;; one character (a single media type, e.g. "A" for audio only), or more +;; characters (mixed media types, e.g. "PV" for pictures and video). The allowed +;; individual types are A for audio, P for pictures, V for video. +(define-record-type* + readymedia-media-dir make-readymedia-media-dir + readymedia-media-dir? + (path readymedia-media-dir-path) + (type readymedia-media-dir-type (default ""))) + +(define (readymedia-media-dir->string entry) + "Convert a media-dir ENTRY to a ReadyMedia/MiniDLNA media dir string." + (format #f + "media_dir=~a,~a" + (readymedia-media-dir-type entry) + (readymedia-media-dir-path entry))) + +(define (readymedia-configuration->config-file config) + "Return the ReadyMedia/MiniDLNA configuration file corresponding to CONFIG." + (let ((friendly-name (readymedia-configuration-friendly-name config)) + (media-dirs (readymedia-configuration-media-dirs config)) + (port (readymedia-configuration-port config)) + (extra-config (readymedia-configuration-extra-config config))) + (plain-file + "minidlna.conf" + (string-append + "db_dir=" %readymedia-cache-dir "\n" + "log_dir=" %readymedia-log-dir "\n" + (if friendly-name (format #f "friendly_name=~a\n" friendly-name) "") + (if port (format #f "port=~a\n" port) "") + (string-join (map readymedia-media-dir->string media-dirs) "\n" 'suffix) + (string-join extra-config "\n" 'suffix))))) + +(define (readymedia-shepherd-service config) + "Return a least-authority ReadyMedia/MiniDLNA Shepherd service." + (let* ((minidlna-conf (readymedia-configuration->config-file config)) + (media-dirs (readymedia-configuration-media-dirs config)) + (readymedia (least-authority-wrapper + (file-append + (readymedia-configuration-readymedia config) + "/sbin/minidlnad") + #:name "minidlna" + #:mappings (cons* + (file-system-mapping + (source %readymedia-cache-dir) + (target source) + (writable? #t)) + (file-system-mapping + (source %readymedia-log-dir) + (target source) + (writable? #t)) + (file-system-mapping + (source minidlna-conf) + (target source)) + (map + (lambda (e) + (file-system-mapping + (source (readymedia-media-dir-path e)) + (target source) + (writable? #f))) + media-dirs)) + #:namespaces (delq 'net %namespaces)))) + (list (shepherd-service + (documentation "Run the ReadyMedia/MiniDLNA daemon.") + (provision '(readymedia)) + (requirement '(networking user-processes)) + (start #~(make-forkexec-constructor + ;; "-S" is to daemonise minidlnad. + (list #$readymedia "-f" #$minidlna-conf "-S") + #:user "readymedia" + #:group "readymedia")) + (stop #~(make-kill-destructor)))))) + +(define readymedia-accounts + (list (user-group + (name %readymedia-user-group) + (system? #t)) + (user-account + (name %readymedia-user-account) + (group %readymedia-user-group) + (system? #t) + (comment "ReadyMedia/MiniDLNA daemon user") + (home-directory "/var/empty") + (shell (file-append shadow "/sbin/nologin"))))) + +(define (readymedia-activation config) + "Set up directories for ReadyMedia/MiniDLNA." + #~(begin + (use-modules (guix build utils)) + (define %user (getpw #$%readymedia-user-account)) + (mkdir-p #$%readymedia-cache-dir) + (chown #$%readymedia-cache-dir (passwd:uid %user) (passwd:gid %user)) + (mkdir-p #$%readymedia-log-dir) + (chown #$%readymedia-log-dir (passwd:uid %user) (passwd:gid %user)))) + +(define readymedia-service-type + (service-type + (name 'readymedia) + (extensions + (list + (service-extension shepherd-root-service-type readymedia-shepherd-service) + (service-extension account-service-type (const readymedia-accounts)) + (service-extension activation-service-type readymedia-activation))) + (description + "Run @command{minidlnad}, the ReadyMedia/MiniDLNA media server."))) base-commit: 46a64c7fdd057283063aae6df058579bb07c4b6a prerequisite-patch-id: d27309b891fb770961716c2ea652ac911cb58433 -- 2.45.2