1{ config, lib, pkgs, ... }: 2 3# TODO: This is not secure, have a look at the file docs/security.txt inside 4# the project sources. 5with lib; 6 7let 8 cfg = config.power.ups; 9in 10 11let 12 upsOptions = {name, config, ...}: 13 { 14 options = { 15 # This can be infered from the UPS model by looking at 16 # /nix/store/nut/share/driver.list 17 driver = mkOption { 18 type = types.str; 19 description = '' 20 Specify the program to run to talk to this UPS. apcsmart, 21 bestups, and sec are some examples. 22 ''; 23 }; 24 25 port = mkOption { 26 type = types.str; 27 description = '' 28 The serial port to which your UPS is connected. /dev/ttyS0 is 29 usually the first port on Linux boxes, for example. 30 ''; 31 }; 32 33 shutdownOrder = mkOption { 34 default = 0; 35 type = types.int; 36 description = '' 37 When you have multiple UPSes on your system, you usually need to 38 turn them off in a certain order. upsdrvctl shuts down all the 39 0s, then the 1s, 2s, and so on. To exclude a UPS from the 40 shutdown sequence, set this to -1. 41 ''; 42 }; 43 44 maxStartDelay = mkOption { 45 default = null; 46 type = types.uniq (types.nullOr types.int); 47 description = '' 48 This can be set as a global variable above your first UPS 49 definition and it can also be set in a UPS section. This value 50 controls how long upsdrvctl will wait for the driver to finish 51 starting. This keeps your system from getting stuck due to a 52 broken driver or UPS. 53 ''; 54 }; 55 56 description = mkOption { 57 default = ""; 58 type = types.string; 59 description = '' 60 Description of the UPS. 61 ''; 62 }; 63 64 directives = mkOption { 65 default = []; 66 type = types.listOf types.str; 67 description = '' 68 List of configuration directives for this UPS. 69 ''; 70 }; 71 72 summary = mkOption { 73 default = ""; 74 type = types.string; 75 description = '' 76 Lines which would be added inside ups.conf for handling this UPS. 77 ''; 78 }; 79 80 }; 81 82 config = { 83 directives = mkHeader ([ 84 "driver = ${config.driver}" 85 "port = ${config.port}" 86 ''desc = "${config.description}"'' 87 "sdorder = ${toString config.shutdownOrder}" 88 ] ++ (optional (config.maxStartDelay != null) 89 "maxstartdelay = ${toString config.maxStartDelay}") 90 ); 91 92 summary = 93 concatStringsSep "\n " 94 (["[${name}]"] ++ config.directives); 95 }; 96 }; 97 98in 99 100 101{ 102 options = { 103 # powerManagement.powerDownCommands 104 105 power.ups = { 106 enable = mkOption { 107 default = false; 108 type = with types; bool; 109 description = '' 110 Enables support for Power Devices, such as Uninterruptible Power 111 Supplies, Power Distribution Units and Solar Controllers. 112 ''; 113 }; 114 115 # This option is not used yet. 116 mode = mkOption { 117 default = "standalone"; 118 type = types.str; 119 description = '' 120 The MODE determines which part of the NUT is to be started, and 121 which configuration files must be modified. 122 123 The values of MODE can be: 124 125 - none: NUT is not configured, or use the Integrated Power 126 Management, or use some external system to startup NUT 127 components. So nothing is to be started. 128 129 - standalone: This mode address a local only configuration, with 1 130 UPS protecting the local system. This implies to start the 3 NUT 131 layers (driver, upsd and upsmon) and the matching configuration 132 files. This mode can also address UPS redundancy. 133 134 - netserver: same as for the standalone configuration, but also 135 need some more ACLs and possibly a specific LISTEN directive in 136 upsd.conf. Since this MODE is opened to the network, a special 137 care should be applied to security concerns. 138 139 - netclient: this mode only requires upsmon. 140 ''; 141 }; 142 143 schedulerRules = mkOption { 144 example = "/etc/nixos/upssched.conf"; 145 type = types.str; 146 description = '' 147 File which contains the rules to handle UPS events. 148 ''; 149 }; 150 151 152 maxStartDelay = mkOption { 153 default = 45; 154 type = types.int; 155 description = '' 156 This can be set as a global variable above your first UPS 157 definition and it can also be set in a UPS section. This value 158 controls how long upsdrvctl will wait for the driver to finish 159 starting. This keeps your system from getting stuck due to a 160 broken driver or UPS. 161 ''; 162 }; 163 164 ups = mkOption { 165 default = {}; 166 # see nut/etc/ups.conf.sample 167 description = '' 168 This is where you configure all the UPSes that this system will be 169 monitoring directly. These are usually attached to serial ports, 170 but USB devices are also supported. 171 ''; 172 type = types.attrsOf types.optionSet; 173 options = [ upsOptions ]; 174 }; 175 176 }; 177 }; 178 179 config = mkIf cfg.enable { 180 181 environment.systemPackages = [ pkgs.nut ]; 182 183 jobs.upsmon = { 184 description = "Uninterruptible Power Supplies (Monitor)"; 185 startOn = "ip-up"; 186 daemonType = "fork"; 187 exec = ''${pkgs.nut}/sbin/upsmon''; 188 environment.NUT_CONFPATH = "/etc/nut/"; 189 environment.NUT_STATEPATH = "/var/lib/nut/"; 190 }; 191 192 jobs.upsd = { 193 description = "Uninterruptible Power Supplies (Daemon)"; 194 startOn = "started network-interfaces and started upsmon"; 195 daemonType = "fork"; 196 # TODO: replace 'root' by another username. 197 exec = ''${pkgs.nut}/sbin/upsd -u root''; 198 environment.NUT_CONFPATH = "/etc/nut/"; 199 environment.NUT_STATEPATH = "/var/lib/nut/"; 200 }; 201 202 jobs.upsdrv = { 203 description = "Uninterruptible Power Supplies (Register all UPS)"; 204 startOn = "started upsd"; 205 # TODO: replace 'root' by another username. 206 exec = ''${pkgs.nut}/bin/upsdrvctl -u root start''; 207 task = true; 208 environment.NUT_CONFPATH = "/etc/nut/"; 209 environment.NUT_STATEPATH = "/var/lib/nut/"; 210 }; 211 212 environment.etc = [ 213 { source = pkgs.writeText "nut.conf" 214 '' 215 MODE = ${cfg.mode} 216 ''; 217 target = "nut/nut.conf"; 218 } 219 { source = pkgs.writeText "ups.conf" 220 '' 221 maxstartdelay = ${toString cfg.maxStartDelay} 222 223 ${flip concatStringsSep (flip map (attrValues cfg.ups) (ups: ups.summary)) " 224 225 "} 226 ''; 227 target = "nut/ups.conf"; 228 } 229 { source = cfg.schedulerRules; 230 target = "nut/upssched.conf"; 231 } 232 # These file are containing private informations and thus should not 233 # be stored inside the Nix store. 234 /* 235 { source = ; 236 target = "nut/upsd.conf"; 237 } 238 { source = ; 239 target = "nut/upsd.users"; 240 } 241 { source = ; 242 target = "nut/upsmon.conf; 243 } 244 */ 245 ]; 246 247 power.ups.schedulerRules = mkDefault "${pkgs.nut}/etc/upssched.conf.sample"; 248 249 system.activationScripts.upsSetup = stringAfter [ "users" "groups" ] 250 '' 251 # Used to store pid files of drivers. 252 mkdir -p /var/state/ups 253 ''; 254 255 256/* 257 users.extraUsers = [ 258 { name = "nut"; 259 uid = 84; 260 home = "/var/lib/nut"; 261 createHome = true; 262 group = "nut"; 263 description = "UPnP A/V Media Server user"; 264 } 265 ]; 266 267 users.extraGroups = [ 268 { name = "nut"; 269 gid = 84; 270 } 271 ]; 272*/ 273 274 }; 275}