summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bragi.nix49
-rw-r--r--custom/mpd.nix199
2 files changed, 224 insertions, 24 deletions
diff --git a/bragi.nix b/bragi.nix
index ba615e97..660cfb06 100644
--- a/bragi.nix
+++ b/bragi.nix
@@ -10,6 +10,7 @@ in rec {
10 ./users.nix 10 ./users.nix
11 ./custom/unit-status-mail.nix 11 ./custom/unit-status-mail.nix
12 ./custom/trivmix-service.nix 12 ./custom/trivmix-service.nix
13 ./custom/mpd.nix
13 ./utils/nix/module.nix 14 ./utils/nix/module.nix
14 ]; 15 ];
15 16
@@ -84,27 +85,29 @@ in rec {
84 }; 85 };
85 86
86 nixpkgs.config = { 87 nixpkgs.config = {
87 packageOverrides = oldPkgs: with oldPkgs; { 88 overlays = [
88 haskellPackages = haskellPackages.override { 89 (selfPkgs: superPkgs: with superPkgs; {
89 overrides = self: super: with self; { 90 haskellPackages = haskellPackages.override {
90 encoding = haskell.lib.overrideCabal encoding ( oldAttrs: { 91 overrides = self: super: with self; {
91 src = fetchFromGitHub { owner = "pngwjpgh"; repo = "encoding"; rev = "extended-version-bounds"; sha256 = "0pzxixp384a1ywzj56pl7xc4ln7i9x6mq8spqjwcs80y0pgfpp9s"; }; 92 encoding = haskell.lib.overrideCabal encoding ( oldAttrs: {
92 }); 93 src = fetchFromGitHub { owner = "pngwjpgh"; repo = "encoding"; rev = "extended-version-bounds"; sha256 = "0pzxixp384a1ywzj56pl7xc4ln7i9x6mq8spqjwcs80y0pgfpp9s"; };
93 inherit 94 });
94 (lib.mapAttrs (name: haskell.lib.dontCheck) super) 95 inherit
95 Glob filelock hedgehog; 96 (lib.mapAttrs (name: haskell.lib.dontCheck) super)
96 inherit 97 Glob filelock hedgehog;
97 (callPackage ./custom/thermoprint { inherit runCommand makeWrapper; extraPackages = (p: with p; [ persistent-postgresql ]); }) 98 inherit
98 thermoprint-spec thermoprint-bbcode thermoprint-client thermoprint-server thermoprint-webgui tprint bbcode; 99 (callPackage ./custom/thermoprint { inherit runCommand makeWrapper; extraPackages = (p: with p; [ persistent-postgresql ]); })
100 thermoprint-spec thermoprint-bbcode thermoprint-client thermoprint-server thermoprint-webgui tprint bbcode;
101 };
99 }; 102 };
100 };
101 103
102 jack2Full = jack2Full.override { dbus = null; }; 104 jack2Full = jack2Full.override { dbus = null; };
103 105
104 mpd = mpd.override { gmeSupport = false; pulseaudioSupport = false; }; 106 mpd = mpd.override { gmeSupport = false; pulseaudioSupport = false; };
105 107
106 inherit (pkgs.haskellPackages) thermoprint-server thermoprint-webgui tprint; 108 inherit (pkgs.haskellPackages) thermoprint-server thermoprint-webgui tprint;
107 }; 109 })
110 ];
108 111
109 allowUnfree = true; 112 allowUnfree = true;
110 }; 113 };
@@ -225,14 +228,12 @@ in rec {
225 services.mpd = { 228 services.mpd = {
226 enable = true; 229 enable = true;
227 musicDirectory = "smb://odin.asgard.yggdrasil/media/music"; 230 musicDirectory = "smb://odin.asgard.yggdrasil/media/music";
228 network.listenAddress = "any"; # Just so the module won't produce a bind_to_adress line 231 listenAddresses = [
232 { address = "any"; }
233 "/var/lib/mpd/socket"
234 ];
235 startWhenNeeded = true;
229 extraConfig = '' 236 extraConfig = ''
230 bind_to_address "bragi.bragisheimr.yggdrasil"
231 bind_to_address "bragi.asgard.yggdrasil"
232 bind_to_address "localhost"
233
234 bind_to_address "/var/lib/mpd/socket"
235
236 audio_output { 237 audio_output {
237 name "JACK" 238 name "JACK"
238 type "jack" 239 type "jack"
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
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
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
47in {
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}