1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 name = "mpd";
8
9 uid = config.ids.uids.mpd;
10 gid = config.ids.gids.mpd;
11 cfg = config.services.mpd;
12
13 mpdConf = pkgs.writeText "mpd.conf" ''
14 music_directory "${cfg.musicDirectory}"
15 playlist_directory "${cfg.playlistDirectory}"
16 ${lib.optionalString (cfg.dbFile != null) ''
17 db_file "${cfg.dbFile}"
18 ''}
19 state_file "${cfg.dataDir}/state"
20 sticker_file "${cfg.dataDir}/sticker.sql"
21 log_file "syslog"
22 user "${cfg.user}"
23 group "${cfg.group}"
24
25 ${optionalString (cfg.network.listenAddress != "any") ''bind_to_address "${cfg.network.listenAddress}"''}
26 ${optionalString (cfg.network.port != 6600) ''port "${toString cfg.network.port}"''}
27
28 ${cfg.extraConfig}
29 '';
30
31in {
32
33 ###### interface
34
35 options = {
36
37 services.mpd = {
38
39 enable = mkOption {
40 type = types.bool;
41 default = false;
42 description = ''
43 Whether to enable MPD, the music player daemon.
44 '';
45 };
46
47 startWhenNeeded = mkOption {
48 type = types.bool;
49 default = false;
50 description = ''
51 If set, <command>mpd</command> is socket-activated; that
52 is, instead of having it permanently running as a daemon,
53 systemd will start it on the first incoming connection.
54 '';
55 };
56
57 musicDirectory = mkOption {
58 type = with types; either path (strMatching "(http|https|nfs|smb)://.+");
59 default = "${cfg.dataDir}/music";
60 defaultText = ''''${dataDir}/music'';
61 description = ''
62 The directory or NFS/SMB network share where mpd reads music from.
63 '';
64 };
65
66 playlistDirectory = mkOption {
67 type = types.path;
68 default = "${cfg.dataDir}/playlists";
69 defaultText = ''''${dataDir}/playlists'';
70 description = ''
71 The directory where mpd stores playlists.
72 '';
73 };
74
75 extraConfig = mkOption {
76 type = types.lines;
77 default = "";
78 description = ''
79 Extra directives added to to the end of MPD's configuration file,
80 mpd.conf. Basic configuration like file location and uid/gid
81 is added automatically to the beginning of the file. For available
82 options see <literal>man 5 mpd.conf</literal>'.
83 '';
84 };
85
86 dataDir = mkOption {
87 type = types.path;
88 default = "/var/lib/${name}";
89 description = ''
90 The directory where MPD stores its state, tag cache,
91 playlists etc.
92 '';
93 };
94
95 user = mkOption {
96 type = types.str;
97 default = name;
98 description = "User account under which MPD runs.";
99 };
100
101 group = mkOption {
102 type = types.str;
103 default = name;
104 description = "Group account under which MPD runs.";
105 };
106
107 network = {
108
109 listenAddress = mkOption {
110 type = types.str;
111 default = "127.0.0.1";
112 example = "any";
113 description = ''
114 The address for the daemon to listen on.
115 Use <literal>any</literal> to listen on all addresses.
116 '';
117 };
118
119 port = mkOption {
120 type = types.int;
121 default = 6600;
122 description = ''
123 This setting is the TCP port that is desired for the daemon to get assigned
124 to.
125 '';
126 };
127
128 };
129
130 dbFile = mkOption {
131 type = types.nullOr types.str;
132 default = "${cfg.dataDir}/tag_cache";
133 defaultText = ''''${dataDir}/tag_cache'';
134 description = ''
135 The path to MPD's database. If set to <literal>null</literal> the
136 parameter is omitted from the configuration.
137 '';
138 };
139 };
140
141 };
142
143
144 ###### implementation
145
146 config = mkIf cfg.enable {
147
148 systemd.sockets.mpd = mkIf cfg.startWhenNeeded {
149 description = "Music Player Daemon Socket";
150 wantedBy = [ "sockets.target" ];
151 listenStreams = [
152 "${optionalString (cfg.network.listenAddress != "any") "${cfg.network.listenAddress}:"}${toString cfg.network.port}"
153 ];
154 socketConfig = {
155 Backlog = 5;
156 KeepAlive = true;
157 PassCredentials = true;
158 };
159 };
160
161 systemd.services.mpd = {
162 after = [ "network.target" "sound.target" ];
163 description = "Music Player Daemon";
164 wantedBy = optional (!cfg.startWhenNeeded) "multi-user.target";
165
166 preStart = ''
167 mkdir -p "${cfg.dataDir}" && chown -R ${cfg.user}:${cfg.group} "${cfg.dataDir}"
168 mkdir -p "${cfg.playlistDirectory}" && chown -R ${cfg.user}:${cfg.group} "${cfg.playlistDirectory}"
169 '';
170 serviceConfig = {
171 User = "${cfg.user}";
172 PermissionsStartOnly = true;
173 ExecStart = "${pkgs.mpd}/bin/mpd --no-daemon ${mpdConf}";
174 Type = "notify";
175 LimitRTPRIO = 50;
176 LimitRTTIME = "infinity";
177 ProtectSystem = true;
178 NoNewPrivileges = true;
179 ProtectKernelTunables = true;
180 ProtectControlGroups = true;
181 ProtectKernelModules = true;
182 RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX AF_NETLINK";
183 RestrictNamespaces = true;
184 };
185 };
186
187 users.users = optionalAttrs (cfg.user == name) (singleton {
188 inherit uid;
189 inherit name;
190 group = cfg.group;
191 extraGroups = [ "audio" ];
192 description = "Music Player Daemon user";
193 home = "${cfg.dataDir}";
194 });
195
196 users.groups = optionalAttrs (cfg.group == name) (singleton {
197 inherit name;
198 gid = gid;
199 });
200 };
201
202}