# Copy/pasted from nixos/modules/services/network-filesystems/tahoe.nix :/ We # require control over additional configuration options compared to upstream # and it's not clear how to do this without duplicating everything. { config, lib, pkgs, ... }: with lib; let cfg = config.services.tahoe; ini = pkgs.callPackage ../lib/ini.nix { }; in { # Upstream tahoe-lafs module conflicts with ours (since ours is a # copy/paste/edit of upstream's...). Disable it. # # https://nixos.org/nixos/manual/#sec-replace-modules disabledModules = [ "services/network-filesystems/tahoe.nix" ]; options.services.tahoe = { introducers = mkOption { default = {}; type = with types; attrsOf (submodule { options = { nickname = mkOption { type = types.str; description = '' The nickname of this Tahoe introducer. ''; }; tub.port = mkOption { default = 3458; type = types.int; description = '' The port on which the introducer will listen. ''; }; tub.location = mkOption { default = null; type = types.nullOr types.str; description = '' The external location that the introducer should listen on. If specified, the port should be included. ''; }; package = mkOption { default = pkgs.tahoelafs; defaultText = "pkgs.tahoelafs"; type = types.package; example = "pkgs.tahoelafs"; description = '' The package to use for the Tahoe LAFS daemon. ''; }; }; }); description = '' The Tahoe introducers. ''; }; nodes = mkOption { default = {}; type = with types; attrsOf (submodule { options = { sections = mkOption { type = types.attrs; description = '' Top-level configuration sections. ''; default = { "node" = { }; "client" = { }; "storage" = { }; }; }; package = mkOption { default = pkgs.tahoelafs; defaultText = "pkgs.tahoelafs"; type = types.package; example = "pkgs.tahoelafs"; description = '' The package to use for the Tahoe LAFS daemon. ''; }; }; }); description = '' The Tahoe nodes. ''; }; }; config = mkMerge [ (mkIf (cfg.introducers != {}) { environment = { etc = flip mapAttrs' cfg.introducers (node: settings: nameValuePair "tahoe-lafs/introducer-${node}.cfg" { mode = "0444"; text = '' # This configuration is generated by Nix. Edit at your own # peril; here be dragons. [node] nickname = ${settings.nickname} tub.port = ${toString settings.tub.port} ${optionalString (settings.tub.location != null) "tub.location = ${settings.tub.location}"} ''; }); # Actually require Tahoe, so that we will have it installed. systemPackages = flip mapAttrsToList cfg.introducers (node: settings: settings.package ); }; # Open up the firewall. # networking.firewall.allowedTCPPorts = flip mapAttrsToList cfg.introducers # (node: settings: settings.tub.port); systemd.services = flip mapAttrs' cfg.introducers (node: settings: let pidfile = "/run/tahoe.introducer-${node}.pid"; # This is a directory, but it has no trailing slash. Tahoe commands # get antsy when there's a trailing slash. nodedir = "/var/db/tahoe-lafs/introducer-${node}"; in nameValuePair "tahoe.introducer-${node}" { description = "Tahoe LAFS node ${node}"; wantedBy = [ "multi-user.target" ]; path = [ settings.package ]; restartTriggers = [ config.environment.etc."tahoe-lafs/introducer-${node}.cfg".source ]; serviceConfig = { Type = "simple"; PIDFile = pidfile; # Believe it or not, Tahoe is very brittle about the order of # arguments to $(tahoe run). The node directory must come first, # and arguments which alter Twisted's behavior come afterwards. ExecStart = '' ${settings.package}/bin/tahoe run ${lib.escapeShellArg nodedir} -n -l- --pidfile=${lib.escapeShellArg pidfile} ''; }; preStart = '' if [ ! -d ${lib.escapeShellArg nodedir} ]; then mkdir -p /var/db/tahoe-lafs tahoe create-introducer ${lib.escapeShellArg nodedir} fi # Tahoe has created a predefined tahoe.cfg which we must now # scribble over. # XXX I thought that a symlink would work here, but it doesn't, so # we must do this on every prestart. Fixes welcome. # rm ${nodedir}/tahoe.cfg # ln -s /etc/tahoe-lafs/introducer-${node}.cfg ${nodedir}/tahoe.cfg cp /etc/tahoe-lafs/introducer-"${node}".cfg ${lib.escapeShellArg nodedir}/tahoe.cfg ''; }); users.users = flip mapAttrs' cfg.introducers (node: _: nameValuePair "tahoe.introducer-${node}" { description = "Tahoe node user for introducer ${node}"; isSystemUser = true; }); }) (mkIf (cfg.nodes != {}) { environment = { etc = flip mapAttrs' cfg.nodes (node: settings: nameValuePair "tahoe-lafs/${node}.cfg" { mode = "0444"; text = '' # This configuration is generated by Nix. Edit at your own # peril; here be dragons. ${ini.allConfigSectionsText settings.sections} ''; }); # Actually require Tahoe, so that we will have it installed. systemPackages = flip mapAttrsToList cfg.nodes (node: settings: settings.package ); }; # Open up the firewall. # networking.firewall.allowedTCPPorts = flip mapAttrsToList cfg.nodes # (node: settings: settings.tub.port); systemd.services = flip mapAttrs' cfg.nodes (node: settings: let pidfile = "/run/tahoe.${lib.escapeShellArg node}.pid"; # This is a directory, but it has no trailing slash. Tahoe commands # get antsy when there's a trailing slash. nodedir = "/var/db/tahoe-lafs/${lib.escapeShellArg node}"; eliotLog = "file:${nodedir}/logs/eliot.json,rotate_length=${toString (1024 * 1024 * 32)},max_rotated_files=32"; in nameValuePair "tahoe.${node}" { description = "Tahoe LAFS node ${node}"; wantedBy = [ "multi-user.target" ]; path = [ settings.package ]; restartTriggers = [ config.environment.etc."tahoe-lafs/${node}.cfg".source ]; serviceConfig = { Type = "simple"; PIDFile = pidfile; # Believe it or not, Tahoe is very brittle about the order of # arguments to $(tahoe run). The node directory must come first, # and arguments which alter Twisted's behavior come afterwards. ExecStart = '' ${settings.package}/bin/tahoe --eliot-destination ${eliotLog} run ${nodedir} -n -l- --pidfile=${pidfile} ''; # The rlimit on number of open files controls how many # connections a particular storage server can accept (factoring # in the number of non-connection files the server needs open - # eg for logging, reading and writing shares, etc). # # Once the maximum number of open files, as controlled by rlimit # is reached, service suffers dramatically. New connections # cannot be accepted. Shares cannot be read or written. # # The default limit on open files is fairly low, perhaps 1024 # (2^10) or 8192 (2^13). This can easily be raised. If it is # raised to 2^16 then the rlimit is approximately equal to the # limit imposed by TCP (which only has around 2^16 ports # available per IP address). If we want each connection to also # be able to read or write a share file, a limit of 2^15 would # allow this. Then, we should scale the limit linearly with the # number of IP addresses available. If the service can be # reached on 2 IP addresses, allow twice as many files (2^15 * 2 # = 2^16). If it can be reached on 3 IP addresses, (2^16 * # 3). etc. # # Python also sometimes wants to open files as a side effect of # other things going. For example, if there's a traceback, it # opens the source files to read lines to put into the # traceback. If random numbers are generated, /dev/urandom # might be opened, etc. There is also some fixed overhead for # listening ports and such. This currently doesn't factor into # our choice but perhaps it could somehow. # # Tahoe-LAFS has no logic to raise soft limit to hard limit so # make it the same. # # There is only one IPv4 address assigned to each host right # now. So it makes sense to have the limit be 2^15 right now. LimitNOFILE = 32768; }; preStart = let created = "${nodedir}.created"; atomic = "${nodedir}.atomic"; in '' set -eo pipefail if [ ! -e ${created} ]; then mkdir -p /var/db/tahoe-lafs/ # Get rid of any prior partial efforts. It might not exist. # Don't let this tank us. rm -rv ${atomic} || [ ! -e ${atomic} ] # Really create the node. tahoe create-node --hostname=localhost ${atomic} # Get rid of any existing partially created node directory # that might be in the way. if [ -e ${nodedir} ]; then for backup in $(seq 1 100); do if [ ! -e ${nodedir}.$backup ]; then mv ${nodedir} ${nodedir}.$backup break fi done fi # Move the new thing to the real location. We don't create it # in-place because we might fail partway through and leave # inconsistent state. Also, systemd probably created # logs/incidents/ already and `create-node` complains if it # finds these exist already. mv ${atomic} ${nodedir} # Record our complete, consistent success. touch ${created} fi # Tahoe has created a predefined tahoe.cfg which we must now # scribble over. # XXX I thought that a symlink would work here, but it doesn't, so # we must do this on every prestart. Fixes welcome. # rm ${nodedir}/tahoe.cfg # ln -s /etc/tahoe-lafs/${lib.escapeShellArg node}.cfg ${nodedir}/tahoe.cfg cp /etc/tahoe-lafs/${lib.escapeShellArg node}.cfg ${nodedir}/tahoe.cfg ''; }); users.users = flip mapAttrs' cfg.nodes (node: _: nameValuePair "tahoe.${node}" { description = "Tahoe node user for node ${node}"; isSystemUser = true; }); }) ]; }