From 544c87716cb912dad3cc9568781470dcde5c64d2 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Sat, 21 Apr 2018 13:07:12 +0200 Subject: Touch up mpd --- bragi.nix | 49 +++++++------- custom/mpd.nix | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 224 insertions(+), 24 deletions(-) create mode 100644 custom/mpd.nix diff --git a/bragi.nix b/bragi.nix index ba615e97..660cfb06 100644 --- a/bragi.nix +++ b/bragi.nix @@ -10,6 +10,7 @@ in rec { ./users.nix ./custom/unit-status-mail.nix ./custom/trivmix-service.nix + ./custom/mpd.nix ./utils/nix/module.nix ]; @@ -84,27 +85,29 @@ in rec { }; nixpkgs.config = { - packageOverrides = oldPkgs: with oldPkgs; { - haskellPackages = haskellPackages.override { - overrides = self: super: with self; { - encoding = haskell.lib.overrideCabal encoding ( oldAttrs: { - src = fetchFromGitHub { owner = "pngwjpgh"; repo = "encoding"; rev = "extended-version-bounds"; sha256 = "0pzxixp384a1ywzj56pl7xc4ln7i9x6mq8spqjwcs80y0pgfpp9s"; }; - }); - inherit - (lib.mapAttrs (name: haskell.lib.dontCheck) super) - Glob filelock hedgehog; - inherit - (callPackage ./custom/thermoprint { inherit runCommand makeWrapper; extraPackages = (p: with p; [ persistent-postgresql ]); }) - thermoprint-spec thermoprint-bbcode thermoprint-client thermoprint-server thermoprint-webgui tprint bbcode; + overlays = [ + (selfPkgs: superPkgs: with superPkgs; { + haskellPackages = haskellPackages.override { + overrides = self: super: with self; { + encoding = haskell.lib.overrideCabal encoding ( oldAttrs: { + src = fetchFromGitHub { owner = "pngwjpgh"; repo = "encoding"; rev = "extended-version-bounds"; sha256 = "0pzxixp384a1ywzj56pl7xc4ln7i9x6mq8spqjwcs80y0pgfpp9s"; }; + }); + inherit + (lib.mapAttrs (name: haskell.lib.dontCheck) super) + Glob filelock hedgehog; + inherit + (callPackage ./custom/thermoprint { inherit runCommand makeWrapper; extraPackages = (p: with p; [ persistent-postgresql ]); }) + thermoprint-spec thermoprint-bbcode thermoprint-client thermoprint-server thermoprint-webgui tprint bbcode; + }; }; - }; - jack2Full = jack2Full.override { dbus = null; }; + jack2Full = jack2Full.override { dbus = null; }; - mpd = mpd.override { gmeSupport = false; pulseaudioSupport = false; }; + mpd = mpd.override { gmeSupport = false; pulseaudioSupport = false; }; - inherit (pkgs.haskellPackages) thermoprint-server thermoprint-webgui tprint; - }; + inherit (pkgs.haskellPackages) thermoprint-server thermoprint-webgui tprint; + }) + ]; allowUnfree = true; }; @@ -225,14 +228,12 @@ in rec { services.mpd = { enable = true; musicDirectory = "smb://odin.asgard.yggdrasil/media/music"; - network.listenAddress = "any"; # Just so the module won't produce a bind_to_adress line + listenAddresses = [ + { address = "any"; } + "/var/lib/mpd/socket" + ]; + startWhenNeeded = true; extraConfig = '' - bind_to_address "bragi.bragisheimr.yggdrasil" - bind_to_address "bragi.asgard.yggdrasil" - bind_to_address "localhost" - - bind_to_address "/var/lib/mpd/socket" - audio_output { name "JACK" 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 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + name = "mpd"; + + uid = config.ids.uids.mpd; + gid = config.ids.gids.mpd; + cfg = config.services.mpd; + + mpdConf = pkgs.writeText "mpd.conf" '' + music_directory "${cfg.musicDirectory}" + playlist_directory "${cfg.playlistDirectory}" + db_file "${cfg.dbFile}" + state_file "${cfg.dataDir}/state" + sticker_file "${cfg.dataDir}/sticker.sql" + log_file "syslog" + user "${cfg.user}" + group "${cfg.group}" + + ${optionalString (cfg.network.listenAddress != "any") ''bind_to_address "${cfg.network.listenAddress}"''} + ${optionalString (cfg.network.port != 6600) ''port "${toString cfg.network.port}"''} + + ${cfg.extraConfig} + ''; + + tcpAddress = { + options = { + address = mkOption { + type = with types; either (enum ["any"]) str; + example = "localhost"; + }; + + port = mkOption { + type = types.int; + default = 6600; + }; + }; + }; + + listenStream = arg: if isString arg then arg else (optionalString (arg.address != "any") (arg.address + ":")) + toString arg.port; + bindAddress = arg: if isString arg then arg + else arg.address + ":" + toString arg.port; + +in { + + disabledModules = [ "services/audio/mpd.nix" ]; + + ###### interface + + options = { + + services.mpd = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable MPD, the music player daemon. + ''; + }; + + startWhenNeeded = mkOption { + type = types.bool; + default = false; + description = '' + If set, mpd is socket-activated; that + is, instead of having it permanently running as a daemon, + systemd will start it on the first incoming connection. + ''; + }; + + musicDirectory = mkOption { + type = with types; either path str; + default = "${cfg.dataDir}/music"; + defaultText = ''''${dataDir}/music''; + description = '' + The directory where mpd reads music from. + ''; + }; + + playlistDirectory = mkOption { + type = types.path; + default = "${cfg.dataDir}/playlists"; + defaultText = ''''${dataDir}/playlists''; + description = '' + The directory where mpd stores playlists. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra directives added to to the end of MPD's configuration file, + mpd.conf. Basic configuration like file location and uid/gid + is added automatically to the beginning of the file. For available + options see man 5 mpd.conf'. + ''; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/${name}"; + description = '' + The directory where MPD stores its state, tag cache, + playlists etc. + ''; + }; + + user = mkOption { + type = types.str; + default = name; + description = "User account under which MPD runs."; + }; + + group = mkOption { + type = types.str; + default = name; + description = "Group account under which MPD runs."; + }; + + listenAddresses = mkOption { + type = with types; listOf (either (submodule tcpAddress) str); + default = [{ address = "127.0.0.1"; }]; + }; + + dbFile = mkOption { + type = types.str; + default = "${cfg.dataDir}/tag_cache"; + defaultText = ''''${dataDir}/tag_cache''; + description = '' + The path to MPD's database. + ''; + }; + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + systemd.sockets.mpd = mkIf cfg.startWhenNeeded { + description = "Music Player Daemon Socket"; + wantedBy = [ "sockets.target" ]; + listenStreams = map listenStream cfg.listenAddresses; + socketConfig = { + Backlog = 5; + KeepAlive = true; + PassCredentials = true; + }; + }; + + systemd.services.mpd = { + after = [ "network.target" "sound.target" ]; + description = "Music Player Daemon"; + wantedBy = optional (!cfg.startWhenNeeded) "multi-user.target"; + + preStart = '' + mkdir -p "${cfg.dataDir}" && chown -R ${cfg.user}:${cfg.group} "${cfg.dataDir}" + mkdir -p "${cfg.playlistDirectory}" && chown -R ${cfg.user}:${cfg.group} "${cfg.playlistDirectory}" + ''; + serviceConfig = { + User = "${cfg.user}"; + PermissionsStartOnly = true; + 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; + }; + }; + + users.extraUsers = optionalAttrs (cfg.user == name) (singleton { + inherit uid; + inherit name; + group = cfg.group; + extraGroups = [ "audio" ]; + description = "Music Player Daemon user"; + home = "${cfg.dataDir}"; + }); + + users.extraGroups = optionalAttrs (cfg.group == name) (singleton { + inherit name; + gid = gid; + }); + }; + +} -- cgit v1.2.3