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