1{
2 config,
3 lib,
4 pkgs,
5 utils,
6 ...
7}:
8let
9 cfg = config.services.swapspace;
10 inherit (lib)
11 types
12 mkIf
13 mkOption
14 mkPackageOption
15 mkEnableOption
16 ;
17 inherit (pkgs)
18 makeWrapper
19 runCommand
20 writeText
21 ;
22 configFile = writeText "swapspace.conf" (lib.generators.toKeyValue { } cfg.settings);
23 userWrapper =
24 runCommand "swapspace"
25 {
26 buildInputs = [ makeWrapper ];
27 }
28 ''
29 mkdir -p "$out/bin"
30 makeWrapper '${lib.getExe cfg.package}' "$out/bin/swapspace" \
31 --add-flags "-c '${configFile}'"
32 '';
33in
34{
35 options.services.swapspace = {
36 enable = mkEnableOption "Swapspace, a dynamic swap space manager";
37 package = mkPackageOption pkgs "swapspace" { };
38 extraArgs = mkOption {
39 type = types.listOf types.str;
40 default = [ ];
41 example = [
42 "-P"
43 "-v"
44 ];
45 description = "Any extra arguments to pass to swapspace";
46 };
47 installWrapper = mkOption {
48 type = types.bool;
49 default = true;
50 description = ''
51 This will add swapspace wrapped with the generated config, to environment.systemPackages
52 '';
53 };
54 settings = mkOption {
55 type = types.submodule {
56 options = {
57 swappath = mkOption {
58 type = types.str;
59 default = "/var/lib/swapspace";
60 description = "Location where swapspace may create and delete swapfiles";
61 };
62 lower_freelimit = mkOption {
63 type = types.ints.between 0 99;
64 default = 20;
65 description = "Lower free-space threshold: if the percentage of free space drops below this number, additional swapspace is allocated";
66 };
67 upper_freelimit = mkOption {
68 type = types.ints.between 0 100;
69 default = 60;
70 description = "Upper free-space threshold: if the percentage of free space exceeds this number, swapspace will attempt to free up swapspace";
71 };
72 freetarget = mkOption {
73 type = types.ints.between 2 99;
74 default = 30;
75 description = ''
76 Percentage of free space swapspace should aim for when adding swapspace.
77 This should fall somewhere between lower_freelimit and upper_freelimit.
78 '';
79 };
80 min_swapsize = mkOption {
81 type = types.str;
82 default = "4m";
83 description = "Smallest allowed size for individual swapfiles";
84 };
85 max_swapsize = mkOption {
86 type = types.str;
87 default = "2t";
88 description = "Greatest allowed size for individual swapfiles";
89 };
90 cooldown = mkOption {
91 type = types.ints.unsigned;
92 default = 600;
93 description = ''
94 Duration (roughly in seconds) of the moratorium on swap allocation that is instated if disk space runs out, or the cooldown time after a new swapfile is successfully allocated before swapspace will consider deallocating swap space again.
95 The default cooldown period is about 10 minutes.
96 '';
97 };
98 buffer_elasticity = mkOption {
99 type = types.ints.between 0 100;
100 default = 30;
101 description = ''Percentage of buffer space considered to be "free"'';
102 };
103 cache_elasticity = mkOption {
104 type = types.ints.between 0 100;
105 default = 80;
106 description = ''Percentage of cache space considered to be "free"'';
107 };
108 };
109 };
110 default = { };
111 description = ''
112 Config file for swapspace.
113 See the options here: <https://github.com/Tookmund/Swapspace/blob/master/swapspace.conf>
114 '';
115 };
116 };
117
118 config = mkIf cfg.enable {
119 environment.systemPackages = [ (if cfg.installWrapper then userWrapper else cfg.package) ];
120 systemd.packages = [ cfg.package ];
121 systemd.services.swapspace = {
122 wantedBy = [ "multi-user.target" ];
123 serviceConfig = {
124 ExecStart = [
125 ""
126 "${lib.getExe cfg.package} -c ${configFile} ${utils.escapeSystemdExecArgs cfg.extraArgs}"
127 ];
128 };
129 };
130 systemd.tmpfiles.settings.swapspace = {
131 ${cfg.settings.swappath}.d = {
132 mode = "0700";
133 };
134 };
135 };
136
137 meta = {
138 maintainers = with lib.maintainers; [
139 Luflosi
140 phanirithvij
141 ];
142 };
143}