diff options
Diffstat (limited to 'custom')
-rw-r--r-- | custom/mpd.nix | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/custom/mpd.nix b/custom/mpd.nix new file mode 100644 index 00000000..8df2a955 --- /dev/null +++ b/custom/mpd.nix | |||
@@ -0,0 +1,199 @@ | |||
1 | { config, lib, pkgs, ... }: | ||
2 | |||
3 | with lib; | ||
4 | |||
5 | let | ||
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 | |||
29 | tcpAddress = { | ||
30 | options = { | ||
31 | address = mkOption { | ||
32 | type = with types; either (enum ["any"]) str; | ||
33 | example = "localhost"; | ||
34 | }; | ||
35 | |||
36 | port = mkOption { | ||
37 | type = types.int; | ||
38 | default = 6600; | ||
39 | }; | ||
40 | }; | ||
41 | }; | ||
42 | |||
43 | listenStream = arg: if isString arg then arg else (optionalString (arg.address != "any") (arg.address + ":")) + toString arg.port; | ||
44 | bindAddress = arg: if isString arg then arg | ||
45 | else arg.address + ":" + toString arg.port; | ||
46 | |||
47 | in { | ||
48 | |||
49 | disabledModules = [ "services/audio/mpd.nix" ]; | ||
50 | |||
51 | ###### interface | ||
52 | |||
53 | options = { | ||
54 | |||
55 | services.mpd = { | ||
56 | |||
57 | enable = mkOption { | ||
58 | type = types.bool; | ||
59 | default = false; | ||
60 | description = '' | ||
61 | Whether to enable MPD, the music player daemon. | ||
62 | ''; | ||
63 | }; | ||
64 | |||
65 | startWhenNeeded = mkOption { | ||
66 | type = types.bool; | ||
67 | default = false; | ||
68 | description = '' | ||
69 | If set, <command>mpd</command> is socket-activated; that | ||
70 | is, instead of having it permanently running as a daemon, | ||
71 | systemd will start it on the first incoming connection. | ||
72 | ''; | ||
73 | }; | ||
74 | |||
75 | musicDirectory = mkOption { | ||
76 | type = with types; either path str; | ||
77 | default = "${cfg.dataDir}/music"; | ||
78 | defaultText = ''''${dataDir}/music''; | ||
79 | description = '' | ||
80 | The directory where mpd reads music from. | ||
81 | ''; | ||
82 | }; | ||
83 | |||
84 | playlistDirectory = mkOption { | ||
85 | type = types.path; | ||
86 | default = "${cfg.dataDir}/playlists"; | ||
87 | defaultText = ''''${dataDir}/playlists''; | ||
88 | description = '' | ||
89 | The directory where mpd stores playlists. | ||
90 | ''; | ||
91 | }; | ||
92 | |||
93 | extraConfig = mkOption { | ||
94 | type = types.lines; | ||
95 | default = ""; | ||
96 | description = '' | ||
97 | Extra directives added to to the end of MPD's configuration file, | ||
98 | mpd.conf. Basic configuration like file location and uid/gid | ||
99 | is added automatically to the beginning of the file. For available | ||
100 | options see <literal>man 5 mpd.conf</literal>'. | ||
101 | ''; | ||
102 | }; | ||
103 | |||
104 | dataDir = mkOption { | ||
105 | type = types.path; | ||
106 | default = "/var/lib/${name}"; | ||
107 | description = '' | ||
108 | The directory where MPD stores its state, tag cache, | ||
109 | playlists etc. | ||
110 | ''; | ||
111 | }; | ||
112 | |||
113 | user = mkOption { | ||
114 | type = types.str; | ||
115 | default = name; | ||
116 | description = "User account under which MPD runs."; | ||
117 | }; | ||
118 | |||
119 | group = mkOption { | ||
120 | type = types.str; | ||
121 | default = name; | ||
122 | description = "Group account under which MPD runs."; | ||
123 | }; | ||
124 | |||
125 | listenAddresses = mkOption { | ||
126 | type = with types; listOf (either (submodule tcpAddress) str); | ||
127 | default = [{ address = "127.0.0.1"; }]; | ||
128 | }; | ||
129 | |||
130 | dbFile = mkOption { | ||
131 | type = types.str; | ||
132 | default = "${cfg.dataDir}/tag_cache"; | ||
133 | defaultText = ''''${dataDir}/tag_cache''; | ||
134 | description = '' | ||
135 | The path to MPD's database. | ||
136 | ''; | ||
137 | }; | ||
138 | }; | ||
139 | |||
140 | }; | ||
141 | |||
142 | |||
143 | ###### implementation | ||
144 | |||
145 | config = mkIf cfg.enable { | ||
146 | |||
147 | systemd.sockets.mpd = mkIf cfg.startWhenNeeded { | ||
148 | description = "Music Player Daemon Socket"; | ||
149 | wantedBy = [ "sockets.target" ]; | ||
150 | listenStreams = map listenStream cfg.listenAddresses; | ||
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 | } | ||