zfs_unstable: 2.3.0-rc3 -> 2.3.0-rc4 (#365045)
[NixPkgs.git] / nixos / modules / services / audio / mpd.nix
bloba6cd80384533678dd28676c9571de42709fab589
1 { config, lib, pkgs, ... }:
2 let
4   name = "mpd";
6   uid = config.ids.uids.mpd;
7   gid = config.ids.gids.mpd;
8   cfg = config.services.mpd;
10   credentialsPlaceholder = (creds:
11     let
12       placeholders = (lib.imap0
13         (i: c: ''password "{{password-${toString i}}}@${lib.concatStringsSep "," c.permissions}"'')
14         creds);
15     in
16       lib.concatStringsSep "\n" placeholders);
18   mpdConf = pkgs.writeText "mpd.conf" ''
19     # This file was automatically generated by NixOS. Edit mpd's configuration
20     # via NixOS' configuration.nix, as this file will be rewritten upon mpd's
21     # restart.
23     music_directory     "${cfg.musicDirectory}"
24     playlist_directory  "${cfg.playlistDirectory}"
25     ${lib.optionalString (cfg.dbFile != null) ''
26       db_file             "${cfg.dbFile}"
27     ''}
28     state_file          "${cfg.dataDir}/state"
29     sticker_file        "${cfg.dataDir}/sticker.sql"
31     ${lib.optionalString (cfg.network.listenAddress != "any") ''bind_to_address "${cfg.network.listenAddress}"''}
32     ${lib.optionalString (cfg.network.port != 6600)  ''port "${toString cfg.network.port}"''}
33     ${lib.optionalString (cfg.fluidsynth) ''
34       decoder {
35               plugin "fluidsynth"
36               soundfont "${pkgs.soundfont-fluid}/share/soundfonts/FluidR3_GM2-2.sf2"
37       }
38     ''}
40     ${lib.optionalString (cfg.credentials != []) (credentialsPlaceholder cfg.credentials)}
42     ${cfg.extraConfig}
43   '';
45 in {
47   ###### interface
49   options = {
51     services.mpd = {
53       enable = lib.mkOption {
54         type = lib.types.bool;
55         default = false;
56         description = ''
57           Whether to enable MPD, the music player daemon.
58         '';
59       };
61       startWhenNeeded = lib.mkOption {
62         type = lib.types.bool;
63         default = false;
64         description = ''
65           If set, {command}`mpd` is socket-activated; that
66           is, instead of having it permanently running as a daemon,
67           systemd will start it on the first incoming connection.
68         '';
69       };
71       musicDirectory = lib.mkOption {
72         type = with lib.types; either path (strMatching "(http|https|nfs|smb)://.+");
73         default = "${cfg.dataDir}/music";
74         defaultText = lib.literalExpression ''"''${dataDir}/music"'';
75         description = ''
76           The directory or NFS/SMB network share where MPD reads music from. If left
77           as the default value this directory will automatically be created before
78           the MPD server starts, otherwise the sysadmin is responsible for ensuring
79           the directory exists with appropriate ownership and permissions.
80         '';
81       };
83       playlistDirectory = lib.mkOption {
84         type = lib.types.path;
85         default = "${cfg.dataDir}/playlists";
86         defaultText = lib.literalExpression ''"''${dataDir}/playlists"'';
87         description = ''
88           The directory where MPD stores playlists. If left as the default value
89           this directory will automatically be created before the MPD server starts,
90           otherwise the sysadmin is responsible for ensuring the directory exists
91           with appropriate ownership and permissions.
92         '';
93       };
95       extraConfig = lib.mkOption {
96         type = lib.types.lines;
97         default = "";
98         description = ''
99           Extra directives added to to the end of MPD's configuration file,
100           mpd.conf. Basic configuration like file location and uid/gid
101           is added automatically to the beginning of the file. For available
102           options see {manpage}`mpd.conf(5)`.
103         '';
104       };
106       dataDir = lib.mkOption {
107         type = lib.types.path;
108         default = "/var/lib/${name}";
109         description = ''
110           The directory where MPD stores its state, tag cache, playlists etc. If
111           left as the default value this directory will automatically be created
112           before the MPD server starts, otherwise the sysadmin is responsible for
113           ensuring the directory exists with appropriate ownership and permissions.
114         '';
115       };
117       user = lib.mkOption {
118         type = lib.types.str;
119         default = name;
120         description = "User account under which MPD runs.";
121       };
123       group = lib.mkOption {
124         type = lib.types.str;
125         default = name;
126         description = "Group account under which MPD runs.";
127       };
129       network = {
131         listenAddress = lib.mkOption {
132           type = lib.types.str;
133           default = "127.0.0.1";
134           example = "any";
135           description = ''
136             The address for the daemon to listen on.
137             Use `any` to listen on all addresses.
138           '';
139         };
141         port = lib.mkOption {
142           type = lib.types.port;
143           default = 6600;
144           description = ''
145             This setting is the TCP port that is desired for the daemon to get assigned
146             to.
147           '';
148         };
150       };
152       dbFile = lib.mkOption {
153         type = lib.types.nullOr lib.types.str;
154         default = "${cfg.dataDir}/tag_cache";
155         defaultText = lib.literalExpression ''"''${dataDir}/tag_cache"'';
156         description = ''
157           The path to MPD's database. If set to `null` the
158           parameter is omitted from the configuration.
159         '';
160       };
162       credentials = lib.mkOption {
163         type = lib.types.listOf (lib.types.submodule {
164           options = {
165             passwordFile = lib.mkOption {
166               type = lib.types.path;
167               description = ''
168                 Path to file containing the password.
169               '';
170             };
171             permissions = let
172               perms = ["read" "add" "control" "admin"];
173             in lib.mkOption {
174               type = lib.types.listOf (lib.types.enum perms);
175               default = [ "read" ];
176               description = ''
177                 List of permissions that are granted with this password.
178                 Permissions can be "${lib.concatStringsSep "\", \"" perms}".
179               '';
180             };
181           };
182         });
183         description = ''
184           Credentials and permissions for accessing the mpd server.
185         '';
186         default = [];
187         example = [
188           {passwordFile = "/var/lib/secrets/mpd_readonly_password"; permissions = [ "read" ];}
189           {passwordFile = "/var/lib/secrets/mpd_admin_password"; permissions = ["read" "add" "control" "admin"];}
190         ];
191       };
193       fluidsynth = lib.mkOption {
194         type = lib.types.bool;
195         default = false;
196         description = ''
197           If set, add fluidsynth soundfont and configure the plugin.
198         '';
199       };
200     };
202   };
205   ###### implementation
207   config = lib.mkIf cfg.enable {
209     # install mpd units
210     systemd.packages = [ pkgs.mpd ];
212     systemd.sockets.mpd = lib.mkIf cfg.startWhenNeeded {
213       wantedBy = [ "sockets.target" ];
214       listenStreams = [
215         ""  # Note: this is needed to override the upstream unit
216         (if pkgs.lib.hasPrefix "/" cfg.network.listenAddress
217           then cfg.network.listenAddress
218           else "${lib.optionalString (cfg.network.listenAddress != "any") "${cfg.network.listenAddress}:"}${toString cfg.network.port}")
219       ];
220     };
222     systemd.services.mpd = {
223       wantedBy = lib.optional (!cfg.startWhenNeeded) "multi-user.target";
225       preStart =
226         ''
227           set -euo pipefail
228           install -m 600 ${mpdConf} /run/mpd/mpd.conf
229         '' + lib.optionalString (cfg.credentials != [])
230         (lib.concatStringsSep "\n"
231           (lib.imap0
232             (i: c: ''${pkgs.replace-secret}/bin/replace-secret '{{password-${toString i}}}' '${c.passwordFile}' /run/mpd/mpd.conf'')
233             cfg.credentials));
235       serviceConfig =
236         {
237           User = "${cfg.user}";
238           # Note: the first "" overrides the ExecStart from the upstream unit
239           ExecStart = [ "" "${pkgs.mpd}/bin/mpd --systemd /run/mpd/mpd.conf" ];
240           RuntimeDirectory = "mpd";
241           StateDirectory = []
242             ++ lib.optionals (cfg.dataDir == "/var/lib/${name}") [ name ]
243             ++ lib.optionals (cfg.playlistDirectory == "/var/lib/${name}/playlists") [ name "${name}/playlists" ]
244             ++ lib.optionals (cfg.musicDirectory == "/var/lib/${name}/music")        [ name "${name}/music" ];
245         };
246     };
248     users.users = lib.optionalAttrs (cfg.user == name) {
249       ${name} = {
250         inherit uid;
251         group = cfg.group;
252         extraGroups = [ "audio" ];
253         description = "Music Player Daemon user";
254         home = "${cfg.dataDir}";
255       };
256     };
258     users.groups = lib.optionalAttrs (cfg.group == name) {
259       ${name}.gid = gid;
260     };
261   };