diff options
Diffstat (limited to 'nixos/modules/services/audio/mpd.nix')
-rw-r--r-- | nixos/modules/services/audio/mpd.nix | 127 |
1 files changed, 99 insertions, 28 deletions
diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix index 1d2a982ac53..e33e860d883 100644 --- a/nixos/modules/services/audio/mpd.nix +++ b/nixos/modules/services/audio/mpd.nix @@ -10,7 +10,19 @@ let gid = config.ids.gids.mpd; cfg = config.services.mpd; + credentialsPlaceholder = (creds: + let + placeholders = (imap0 + (i: c: ''password "{{password-${toString i}}}@${concatStringsSep "," c.permissions}"'') + creds); + in + concatStringsSep "\n" placeholders); + mpdConf = pkgs.writeText "mpd.conf" '' + # This file was automatically generated by NixOS. Edit mpd's configuration + # via NixOS' configuration.nix, as this file will be rewritten upon mpd's + # restart. + music_directory "${cfg.musicDirectory}" playlist_directory "${cfg.playlistDirectory}" ${lib.optionalString (cfg.dbFile != null) '' @@ -28,6 +40,8 @@ let } ''} + ${optionalString (cfg.credentials != []) (credentialsPlaceholder cfg.credentials)} + ${cfg.extraConfig} ''; @@ -60,18 +74,24 @@ in { musicDirectory = mkOption { type = with types; either path (strMatching "(http|https|nfs|smb)://.+"); default = "${cfg.dataDir}/music"; - defaultText = ''''${dataDir}/music''; + defaultText = "\${dataDir}/music"; description = '' - The directory or NFS/SMB network share where mpd reads music from. + The directory or NFS/SMB network share where MPD reads music from. If left + as the default value this directory will automatically be created before + the MPD server starts, otherwise the sysadmin is responsible for ensuring + the directory exists with appropriate ownership and permissions. ''; }; playlistDirectory = mkOption { type = types.path; default = "${cfg.dataDir}/playlists"; - defaultText = ''''${dataDir}/playlists''; + defaultText = "\${dataDir}/playlists"; description = '' - The directory where mpd stores playlists. + The directory where MPD stores playlists. If left as the default value + this directory will automatically be created before the MPD server starts, + otherwise the sysadmin is responsible for ensuring the directory exists + with appropriate ownership and permissions. ''; }; @@ -90,8 +110,10 @@ in { type = types.path; default = "/var/lib/${name}"; description = '' - The directory where MPD stores its state, tag cache, - playlists etc. + The directory where MPD stores its state, tag cache, playlists etc. If + left as the default value this directory will automatically be created + before the MPD server starts, otherwise the sysadmin is responsible for + ensuring the directory exists with appropriate ownership and permissions. ''; }; @@ -133,13 +155,44 @@ in { dbFile = mkOption { type = types.nullOr types.str; default = "${cfg.dataDir}/tag_cache"; - defaultText = ''''${dataDir}/tag_cache''; + defaultText = "\${dataDir}/tag_cache"; description = '' The path to MPD's database. If set to <literal>null</literal> the parameter is omitted from the configuration. ''; }; + credentials = mkOption { + type = types.listOf (types.submodule { + options = { + passwordFile = mkOption { + type = types.path; + description = '' + Path to file containing the password. + ''; + }; + permissions = let + perms = ["read" "add" "control" "admin"]; + in mkOption { + type = types.listOf (types.enum perms); + default = [ "read" ]; + description = '' + List of permissions that are granted with this password. + Permissions can be "${concatStringsSep "\", \"" perms}". + ''; + }; + }; + }); + description = '' + Credentials and permissions for accessing the mpd server. + ''; + default = []; + example = [ + {passwordFile = "/var/lib/secrets/mpd_readonly_password"; permissions = [ "read" ];} + {passwordFile = "/var/lib/secrets/mpd_admin_password"; permissions = ["read" "add" "control" "admin"];} + ]; + }; + fluidsynth = mkOption { type = types.bool; default = false; @@ -160,7 +213,9 @@ in { description = "Music Player Daemon Socket"; wantedBy = [ "sockets.target" ]; listenStreams = [ - "${optionalString (cfg.network.listenAddress != "any") "${cfg.network.listenAddress}:"}${toString cfg.network.port}" + (if pkgs.lib.hasPrefix "/" cfg.network.listenAddress + then cfg.network.listenAddress + else "${optionalString (cfg.network.listenAddress != "any") "${cfg.network.listenAddress}:"}${toString cfg.network.port}") ]; socketConfig = { Backlog = 5; @@ -169,31 +224,47 @@ in { }; }; - systemd.tmpfiles.rules = [ - "d '${cfg.dataDir}' - ${cfg.user} ${cfg.group} - -" - "d '${cfg.playlistDirectory}' - ${cfg.user} ${cfg.group} - -" - ]; - systemd.services.mpd = { after = [ "network.target" "sound.target" ]; description = "Music Player Daemon"; wantedBy = optional (!cfg.startWhenNeeded) "multi-user.target"; - serviceConfig = { - User = "${cfg.user}"; - ExecStart = "${pkgs.mpd}/bin/mpd --no-daemon ${mpdConf}"; - Type = "notify"; - LimitRTPRIO = 50; - LimitRTTIME = "infinity"; - ProtectSystem = true; - NoNewPrivileges = true; - ProtectKernelTunables = true; - ProtectControlGroups = true; - ProtectKernelModules = true; - RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX AF_NETLINK"; - RestrictNamespaces = true; - Restart = "always"; - }; + serviceConfig = mkMerge [ + { + User = "${cfg.user}"; + ExecStart = "${pkgs.mpd}/bin/mpd --no-daemon /run/mpd/mpd.conf"; + ExecStartPre = pkgs.writeShellScript "mpd-start-pre" ('' + set -euo pipefail + install -m 600 ${mpdConf} /run/mpd/mpd.conf + '' + optionalString (cfg.credentials != []) + (concatStringsSep "\n" + (imap0 + (i: c: ''${pkgs.replace-secret}/bin/replace-secret '{{password-${toString i}}}' '${c.passwordFile}' /run/mpd/mpd.conf'') + cfg.credentials)) + ); + RuntimeDirectory = "mpd"; + Type = "notify"; + LimitRTPRIO = 50; + LimitRTTIME = "infinity"; + ProtectSystem = true; + NoNewPrivileges = true; + ProtectKernelTunables = true; + ProtectControlGroups = true; + ProtectKernelModules = true; + RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX AF_NETLINK"; + RestrictNamespaces = true; + Restart = "always"; + } + (mkIf (cfg.dataDir == "/var/lib/${name}") { + StateDirectory = [ name ]; + }) + (mkIf (cfg.playlistDirectory == "/var/lib/${name}/playlists") { + StateDirectory = [ name "${name}/playlists" ]; + }) + (mkIf (cfg.musicDirectory == "/var/lib/${name}/music") { + StateDirectory = [ name "${name}/music" ]; + }) + ]; }; users.users = optionalAttrs (cfg.user == name) { |