From 3bb5cc684955537f55c15581634eff1a7a930cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edward=20Tj=C3=B6rnhammar?= Date: Thu, 12 Mar 2020 18:57:40 +0100 Subject: [PATCH 1/9] mediatomb: make service compatible with the gerbera fork The duplication of the interface xml tag is needed for the daemon to respect the setting. --- nixos/modules/services/misc/mediatomb.nix | 78 ++++++++++++++--------- 1 file changed, 47 insertions(+), 31 deletions(-) diff --git a/nixos/modules/services/misc/mediatomb.nix b/nixos/modules/services/misc/mediatomb.nix index 529f584a201e4..ba2bbfbc221e7 100644 --- a/nixos/modules/services/misc/mediatomb.nix +++ b/nixos/modules/services/misc/mediatomb.nix @@ -6,37 +6,42 @@ let gid = config.ids.gids.mediatomb; cfg = config.services.mediatomb; + name = cfg.package.pname; + pkg = cfg.package; + optionYesNo = option: if option then "yes" else "no"; mtConf = pkgs.writeText "config.xml" '' + ${cfg.interface} - + ${cfg.serverName} uuid:${cfg.uuid} ${cfg.dataDir} - ${pkgs.mediatomb}/share/mediatomb/web + ${cfg.interface} + ${pkg}/share/${name}/web - mediatomb.db + ${name}.db - - ${if cfg.dsmSupport then '' + + ${lib.optionalString cfg.dsmSupport '' redsonic.com 105 - '' else ""} - ${if cfg.tg100Support then '' + ''} + ${optionalString cfg.tg100Support '' 101 - '' else ""} + ''} * @@ -48,10 +53,10 @@ let - ${pkgs.mediatomb}/share/mediatomb/js/common.js - ${pkgs.mediatomb}/share/mediatomb/js/playlists.js + ${pkg}/share/${name}/js/common.js + ${pkg}/share/${name}/js/playlists.js - ${pkgs.mediatomb}/share/mediatomb/js/import.js + ${pkg}/share/${name}/js/import.js @@ -75,12 +80,12 @@ let - ${if cfg.ps3Support then '' + ${optionalString cfg.ps3Support '' - '' else ""} - ${if cfg.dsmSupport then '' + ''} + ${optionalString cfg.dsmSupport '' - '' else ""} + ''} @@ -108,10 +113,10 @@ let - + - - + + @@ -158,18 +163,27 @@ in { type = types.bool; default = false; description = '' - Whether to enable the mediatomb DLNA server. + Whether to enable the Gerbera/Mediatomb DLNA server. ''; }; serverName = mkOption { type = types.str; - default = "mediatomb"; + default = "Gerbera (Mediatomb)"; description = '' How to identify the server on the network. ''; }; + package = mkOption { + type = types.package; + example = literalExample "pkgs.mediatomb"; + default = pkgs.gerbera; + description = '' + Underlying package to be used with the module (default: pkgs.gerbera). + ''; + }; + ps3Support = mkOption { type = types.bool; default = false; @@ -206,20 +220,20 @@ in { dataDir = mkOption { type = types.path; - default = "/var/lib/mediatomb"; + default = "/var/lib/${name}"; description = '' - The directory where mediatomb stores its state, data, etc. + The directory where ${cfg.serverName} stores its state, data, etc. ''; }; user = mkOption { default = "mediatomb"; - description = "User account under which mediatomb runs."; + description = "User account under which ${name} runs."; }; group = mkOption { default = "mediatomb"; - description = "Group account under which mediatomb runs."; + description = "Group account under which ${name} runs."; }; port = mkOption { @@ -247,7 +261,10 @@ in { type = types.bool; default = false; description = '' - Allow mediatomb to create and use its own config file inside ${cfg.dataDir}. + Allow ${name} to create and use its own config file inside ${cfg.dataDir}. + Deactivated by default, the service then runs with the configuration generated from this module. + Otherwise, when enabled, no service configuration is generated. Gerbera/Mediatomb then starts using + ${cfg.dataDir}/config.xml. It's up to the user to make a correct configuration file. ''; }; }; @@ -257,12 +274,11 @@ in { ###### implementation config = mkIf cfg.enable { - systemd.services.mediatomb = { - description = "MediaTomb media Server"; + systemd.services."${name}"= { + description = "${cfg.serverName} media Server"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - path = [ pkgs.mediatomb ]; - serviceConfig.ExecStart = "${pkgs.mediatomb}/bin/mediatomb -p ${toString cfg.port} ${if cfg.interface!="" then "-e ${cfg.interface}" else ""} ${if cfg.customCfg then "" else "-c ${mtConf}"} -m ${cfg.dataDir}"; + serviceConfig.ExecStart = "${pkg}/bin/${name} -p ${toString cfg.port} ${if cfg.interface!="" then "-e ${cfg.interface}" else ""} ${if cfg.customCfg then "" else "-c ${mtConf}"} -m ${cfg.dataDir}"; serviceConfig.User = "${cfg.user}"; }; @@ -276,11 +292,11 @@ in { group = cfg.group; home = "${cfg.dataDir}"; createHome = true; - description = "Mediatomb DLNA Server User"; + description = "${name} DLNA Server User"; }; }; - networking.firewall = { + networking.firewall.interfaces."${cfg.interface}" = { allowedUDPPorts = [ 1900 cfg.port ]; allowedTCPPorts = [ cfg.port ]; }; From 1db9813dd3ccb9d5aa05c61c9fb3bc5b1ab73c7c Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Sat, 18 Jul 2020 10:05:15 +0200 Subject: [PATCH 2/9] mediatomb/gerbera: Make transcoding option lazy and runnable if activated In the sense that the pkgs dependency will be pulled if the service is transcoding enabled. Otherwise, the transcoding part is completely dropped from the generated configuration. --- nixos/modules/services/misc/mediatomb.nix | 62 +++++++++++++---------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/nixos/modules/services/misc/mediatomb.nix b/nixos/modules/services/misc/mediatomb.nix index ba2bbfbc221e7..bc7d154582959 100644 --- a/nixos/modules/services/misc/mediatomb.nix +++ b/nixos/modules/services/misc/mediatomb.nix @@ -9,6 +9,38 @@ let name = cfg.package.pname; pkg = cfg.package; optionYesNo = option: if option then "yes" else "no"; + transcodingConfig = if cfg.transcoding then with pkgs; '' + + + + + + + + + + audio/mpeg + no + yes + no + + + + + video/mpeg + yes + yes + yes + + + + + +'' else '' + + +''; mtConf = pkgs.writeText "config.xml" '' @@ -121,38 +153,12 @@ let - - - - - - - - - - audio/L16 - no - yes - no - - - - - video/mpeg - yes - yes - yes - - - - - + ${transcodingConfig} - ''; +''; in { - ###### interface options = { From 96d1844746d1fd67f83901730eb7b8a9d3cb7985 Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Sat, 18 Jul 2020 10:06:59 +0200 Subject: [PATCH 3/9] mediatomb/gerbera: Introduce the pcDirectoryHide option --- nixos/modules/services/misc/mediatomb.nix | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/nixos/modules/services/misc/mediatomb.nix b/nixos/modules/services/misc/mediatomb.nix index bc7d154582959..e56e9d6e4ba11 100644 --- a/nixos/modules/services/misc/mediatomb.nix +++ b/nixos/modules/services/misc/mediatomb.nix @@ -57,13 +57,14 @@ let ${cfg.dataDir} ${cfg.interface} ${pkg}/share/${name}/web + ${name}.db - ${lib.optionalString cfg.dsmSupport '' + ${optionalString cfg.dsmSupport '' @@ -232,6 +233,14 @@ in { ''; }; + pcDirectoryHide = mkOption { + type = types.bool; + default = true; + description = '' + Whether to list the top-level directory or not (from upnp client standpoint). + ''; + }; + user = mkOption { default = "mediatomb"; description = "User account under which ${name} runs."; From de838249c7600ea3cdf375f767f6bb51ed72819d Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Sat, 18 Jul 2020 10:07:42 +0200 Subject: [PATCH 4/9] mediatomb/gerbera: Introduce the mediaDirectories option So users can declare their autoscan directories configuration from nix. --- nixos/modules/services/misc/mediatomb.nix | 42 +++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/nixos/modules/services/misc/mediatomb.nix b/nixos/modules/services/misc/mediatomb.nix index e56e9d6e4ba11..a06f6da7b234e 100644 --- a/nixos/modules/services/misc/mediatomb.nix +++ b/nixos/modules/services/misc/mediatomb.nix @@ -9,6 +9,29 @@ let name = cfg.package.pname; pkg = cfg.package; optionYesNo = option: if option then "yes" else "no"; + # configuration on media directory + mediaDirectory = { + options = { + path = mkOption { + type = types.str; + description = '' + Absolute directory path to the media directory to index. + ''; + }; + recursive = mkOption { + type = types.bool; + default = false; + description = "Whether the indexation must take place recursively or not."; + }; + hidden-files = mkOption { + type = types.bool; + default = true; + description = "Whether to index the hidden files or not."; + }; + }; + }; + toMediaDirectory = d: "\n"; + transcodingConfig = if cfg.transcoding then with pkgs; '' @@ -43,6 +66,10 @@ let ''; mtConf = pkgs.writeText "config.xml" '' + ${cfg.interface} @@ -85,6 +112,9 @@ let + + ${concatMapStrings toMediaDirectory cfg.mediaDirectories} + ${pkg}/share/${name}/js/common.js ${pkg}/share/${name}/js/playlists.js @@ -272,6 +302,18 @@ in { ''; }; + mediaDirectories = mkOption { + type = with types; listOf (submodule mediaDirectory); + default = {}; + description = '' + Declare media directories to index. + ''; + example = [ + { path = "/data/pictures"; recursive = false; hidden-files = false; } + { path = "/data/audio"; recursive = true; hidden-files = false; } + ]; + }; + customCfg = mkOption { type = types.bool; default = false; From fcb38d67738bec95e5116321f86d7ce7e6f5ed3e Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Sat, 18 Jul 2020 20:58:30 +0200 Subject: [PATCH 5/9] mediatomb/gerbera: Make the actual configuration generation lazy Also use verbose flag in cli command to make the intent clearer. --- nixos/modules/services/misc/mediatomb.nix | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/nixos/modules/services/misc/mediatomb.nix b/nixos/modules/services/misc/mediatomb.nix index a06f6da7b234e..9d73a64be91a8 100644 --- a/nixos/modules/services/misc/mediatomb.nix +++ b/nixos/modules/services/misc/mediatomb.nix @@ -65,11 +65,7 @@ let ''; - mtConf = pkgs.writeText "config.xml" '' - + configText = optionalString (! cfg.customCfg) '' ${cfg.interface} @@ -330,12 +326,15 @@ in { ###### implementation - config = mkIf cfg.enable { - systemd.services."${name}"= { + config = let binaryCommand = "${pkg}/bin/${name}"; + interfaceFlag = optionalString ( cfg.interface != "") "--interface ${cfg.interface}"; + configFlag = optionalString (! cfg.customCfg) "--config ${pkgs.writeText "config.xml" configText}"; + in mkIf cfg.enable { + systemd.services.mediatomb = { description = "${cfg.serverName} media Server"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - serviceConfig.ExecStart = "${pkg}/bin/${name} -p ${toString cfg.port} ${if cfg.interface!="" then "-e ${cfg.interface}" else ""} ${if cfg.customCfg then "" else "-c ${mtConf}"} -m ${cfg.dataDir}"; + serviceConfig.ExecStart = "${binaryCommand} --port ${toString cfg.port} ${interfaceFlag} ${configFlag} --home ${cfg.dataDir}"; serviceConfig.User = "${cfg.user}"; }; From 86e56d5322c527f75cb9570abb9fb56c22c2268c Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Sat, 18 Jul 2020 20:58:11 +0200 Subject: [PATCH 6/9] mediatomb/gerbera: Add missing types to options This also fixes some various small limitations: - Drop unnecessary quoting - Drop duplicated gerbera interface definition - Fix configuration indentation --- nixos/modules/services/misc/mediatomb.nix | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/nixos/modules/services/misc/mediatomb.nix b/nixos/modules/services/misc/mediatomb.nix index 9d73a64be91a8..9e5a0463faa12 100644 --- a/nixos/modules/services/misc/mediatomb.nix +++ b/nixos/modules/services/misc/mediatomb.nix @@ -66,9 +66,8 @@ let ''; configText = optionalString (! cfg.customCfg) '' - - - ${cfg.interface} + + @@ -268,16 +267,19 @@ in { }; user = mkOption { + type = types.str; default = "mediatomb"; description = "User account under which ${name} runs."; }; group = mkOption { + type = types.str; default = "mediatomb"; description = "Group account under which ${name} runs."; }; port = mkOption { + type = types.int; default = 49152; description = '' The network port to listen on. @@ -285,6 +287,7 @@ in { }; interface = mkOption { + type = types.str; default = ""; description = '' A specific interface to bind to. @@ -292,6 +295,7 @@ in { }; uuid = mkOption { + type = types.str; default = "fdfc8a4e-a3ad-4c1d-b43d-a2eedb03a687"; description = '' A unique (on your network) to identify the server by. @@ -335,7 +339,7 @@ in { after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig.ExecStart = "${binaryCommand} --port ${toString cfg.port} ${interfaceFlag} ${configFlag} --home ${cfg.dataDir}"; - serviceConfig.User = "${cfg.user}"; + serviceConfig.User = cfg.user; }; users.groups = optionalAttrs (cfg.group == "mediatomb") { @@ -346,7 +350,7 @@ in { mediatomb = { isSystemUser = true; group = cfg.group; - home = "${cfg.dataDir}"; + home = cfg.dataDir; createHome = true; description = "${name} DLNA Server User"; }; From 9fdd11c6a82f1480bcd6285e55623a60ebd3e0e5 Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Sun, 19 Jul 2020 02:01:16 +0200 Subject: [PATCH 7/9] mediatomb/gerbera: Bootstrap tests on service This exposes 2 scenario running the mediatomb service: - one running with the unmaintained mediatomb package - one running with the new maintained gerbera package --- nixos/tests/mediatomb.nix | 84 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 nixos/tests/mediatomb.nix diff --git a/nixos/tests/mediatomb.nix b/nixos/tests/mediatomb.nix new file mode 100644 index 0000000000000..f07e453aadc2b --- /dev/null +++ b/nixos/tests/mediatomb.nix @@ -0,0 +1,84 @@ +import ./make-test-python.nix ({ pkgs, ... }: + +{ + name = "mediatomb"; + + nodes = { + serverGerbera = + { ... }: + let port = 49152; + in { + imports = [ ../modules/profiles/minimal.nix ]; + services.mediatomb = { + enable = true; + serverName = "Gerbera"; + package = pkgs.gerbera; + interface = "eth1"; # accessible from test + mediaDirectories = [ + { path = "/var/lib/gerbera/pictures"; recursive = false; hidden-files = false; } + { path = "/var/lib/gerbera/audio"; recursive = true; hidden-files = false; } + ]; + }; + networking.firewall = { + allowedUDPPorts = [ 1900 port ]; + allowedTCPPorts = [ port ]; + }; + }; + + serverMediatomb = + { ... }: + let port = 49151; + in { + imports = [ ../modules/profiles/minimal.nix ]; + services.mediatomb = { + enable = true; + serverName = "Mediatomb"; + package = pkgs.mediatomb; + interface = "eth1"; + inherit port; + mediaDirectories = [ + { path = "/var/lib/mediatomb/pictures"; recursive = false; hidden-files = false; } + { path = "/var/lib/mediatomb/audio"; recursive = true; hidden-files = false; } + ]; + }; + networking.firewall = { + allowedUDPPorts = [ 1900 port ]; + allowedTCPPorts = [ port ]; + }; + }; + + client = { ... }: { }; + }; + + testScript = + '' + start_all() + + port = 49151 + serverMediatomb.succeed("mkdir -p /var/lib/mediatomb/{pictures,audio}") + serverMediatomb.succeed("chown -R mediatomb:mediatomb /var/lib/mediatomb") + serverMediatomb.wait_for_unit("mediatomb") + serverMediatomb.wait_for_open_port(port) + serverMediatomb.succeed(f"curl --fail http://serverMediatomb:{port}/") + page = client.succeed(f"curl --fail http://serverMediatomb:{port}/") + assert "MediaTomb" in page and "Gerbera" not in page + serverMediatomb.shutdown() + + port = 49152 + serverGerbera.succeed("mkdir -p /var/lib/mediatomb/{pictures,audio}") + serverGerbera.succeed("chown -R mediatomb:mediatomb /var/lib/mediatomb") + # service running gerbera fails the first time claiming something is already bound + # gerbera[715]: 2020-07-18 23:52:14 info: Please check if another instance of Gerbera or + # gerbera[715]: 2020-07-18 23:52:14 info: another application is running on port TCP 49152 or UDP 1900. + # I did not find anything so here I work around this + serverGerbera.succeed("sleep 2") + serverGerbera.wait_until_succeeds("systemctl restart mediatomb") + serverGerbera.wait_for_unit("mediatomb") + serverGerbera.succeed(f"curl --fail http://serverGerbera:{port}/") + page = client.succeed(f"curl --fail http://serverGerbera:{port}/") + assert "Gerbera" in page and "MediaTomb" not in page + + serverGerbera.shutdown() + client.shutdown() + ''; +}) From 3248506a002a668f8697d4752f1db211501c2823 Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Mon, 20 Jul 2020 08:47:16 +0200 Subject: [PATCH 8/9] mediatomb/gerbera: Improve firewall rules and open firewall option This changes the default behavior which opened by default the firewall rules. The users now need to declare explicitely they want to open the firewall. --- nixos/modules/services/misc/mediatomb.nix | 31 ++++++++++++++++++++--- nixos/tests/mediatomb.nix | 7 ++--- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/nixos/modules/services/misc/mediatomb.nix b/nixos/modules/services/misc/mediatomb.nix index 9e5a0463faa12..ec6ef3d7b53a7 100644 --- a/nixos/modules/services/misc/mediatomb.nix +++ b/nixos/modules/services/misc/mediatomb.nix @@ -182,6 +182,13 @@ let ${transcodingConfig} ''; + defaultFirewallRules = { + # udp 1900 port needs to be opened for SSDP (not configurable within + # mediatomb/gerbera) cf. + # http://docs.gerbera.io/en/latest/run.html?highlight=udp%20port#network-setup + allowedUDPPorts = [ 1900 cfg.port ]; + allowedTCPPorts = [ cfg.port ]; + }; in { @@ -294,6 +301,18 @@ in { ''; }; + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + If false (the default), this is up to the user to declare the firewall rules. + If true, this opens the 1900 (tcp and udp) and ${toString cfg.port} (tcp) ports. + If the option cfg.interface is set, the firewall rules opened are + dedicated to that interface. Otherwise, those rules are opened + globally. + ''; + }; + uuid = mkOption { type = types.str; default = "fdfc8a4e-a3ad-4c1d-b43d-a2eedb03a687"; @@ -324,6 +343,7 @@ in { ${cfg.dataDir}/config.xml. It's up to the user to make a correct configuration file. ''; }; + }; }; @@ -356,9 +376,12 @@ in { }; }; - networking.firewall.interfaces."${cfg.interface}" = { - allowedUDPPorts = [ 1900 cfg.port ]; - allowedTCPPorts = [ cfg.port ]; - }; + # Open firewall only if users enable it + networking.firewall = mkMerge [ + (mkIf (cfg.openFirewall && cfg.interface != "") { + interfaces."${cfg.interface}" = defaultFirewallRules; + }) + (mkIf (cfg.openFirewall && cfg.interface == "") defaultFirewallRules) + ]; }; } diff --git a/nixos/tests/mediatomb.nix b/nixos/tests/mediatomb.nix index f07e453aadc2b..b7a126a01ad5c 100644 --- a/nixos/tests/mediatomb.nix +++ b/nixos/tests/mediatomb.nix @@ -14,15 +14,12 @@ import ./make-test-python.nix ({ pkgs, ... }: serverName = "Gerbera"; package = pkgs.gerbera; interface = "eth1"; # accessible from test + openFirewall = true; mediaDirectories = [ { path = "/var/lib/gerbera/pictures"; recursive = false; hidden-files = false; } { path = "/var/lib/gerbera/audio"; recursive = true; hidden-files = false; } ]; }; - networking.firewall = { - allowedUDPPorts = [ 1900 port ]; - allowedTCPPorts = [ port ]; - }; }; serverMediatomb = @@ -41,7 +38,7 @@ import ./make-test-python.nix ({ pkgs, ... }: { path = "/var/lib/mediatomb/audio"; recursive = true; hidden-files = false; } ]; }; - networking.firewall = { + networking.firewall.interfaces.eth1 = { allowedUDPPorts = [ 1900 port ]; allowedTCPPorts = [ port ]; }; From a007e07abb4640c61112b7650012f9e42463dc76 Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Fri, 24 Jul 2020 07:53:40 +0200 Subject: [PATCH 9/9] mediatomb/gerbera: Add release note information for 20.09 Note that it made into 2 entries, one about new options in the first section. Another in the breaking compatibility section due to the openFirewall option which changes the behavior. --- nixos/doc/manual/release-notes/rl-2009.xml | 42 +++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/nixos/doc/manual/release-notes/rl-2009.xml b/nixos/doc/manual/release-notes/rl-2009.xml index c50bc58ca4513..6dcf8b6b23b9d 100644 --- a/nixos/doc/manual/release-notes/rl-2009.xml +++ b/nixos/doc/manual/release-notes/rl-2009.xml @@ -226,7 +226,30 @@ GRANT ALL PRIVILEGES ON *.* TO 'mysql'@'localhost' WITH GRANT OPTION; testing-python.nix respectively. - + + + The mediatomb service + declares new options. It also adapts existing options so the + configuration generation is now lazy. The existing option + customCfg (defaults to false), when enabled, stops + the service configuration generation completely. It then expects the + users to provide their own correct configuration at the right location + (whereas the configuration was generated and not used at all before). + The new option transcodingOption (defaults to no) + allows a generated configuration. It makes the mediatomb service pulls + the necessary runtime dependencies in the nix store (whereas it was + generated with hardcoded values before). The new option + mediaDirectories allows the users to declare autoscan + media directories from their nixos configuration: + + services.mediatomb.mediaDirectories = [ + { path = "/var/lib/mediatomb/pictures"; recursive = false; hidden-files = false; } + { path = "/var/lib/mediatomb/audio"; recursive = true; hidden-files = false; } + ]; + + + +
+ + + The mediatomb service is + now using by default the new and maintained fork + gerbera package instead of the unmaintained + mediatomb package. If you want to keep the old + behavior, you must declare it with: + + services.mediatomb.package = pkgs.mediatomb; + + One new option openFirewall has been introduced which + defaults to false. If you relied on the service declaration to add the + firewall rules itself before, you should now declare it with: + + services.mediatomb.openFirewall = true; + +