python3Packages.orjson: Disable failing tests on 32 bit
[NixPkgs.git] / nixos / modules / services / monitoring / apcupsd.nix
blobd4216b44cdc8c9058d943ad77ee2cff1867ee5f0
1 { config, lib, pkgs, ... }:
3 with lib;
5 let
6   cfg = config.services.apcupsd;
8   configFile = pkgs.writeText "apcupsd.conf" ''
9     ## apcupsd.conf v1.1 ##
10     # apcupsd complains if the first line is not like above.
11     ${cfg.configText}
12     SCRIPTDIR ${toString scriptDir}
13   '';
15   # List of events from "man apccontrol"
16   eventList = [
17     "annoyme"
18     "battattach"
19     "battdetach"
20     "changeme"
21     "commfailure"
22     "commok"
23     "doreboot"
24     "doshutdown"
25     "emergency"
26     "failing"
27     "killpower"
28     "loadlimit"
29     "mainsback"
30     "onbattery"
31     "offbattery"
32     "powerout"
33     "remotedown"
34     "runlimit"
35     "timeout"
36     "startselftest"
37     "endselftest"
38   ];
40   shellCmdsForEventScript = eventname: commands: ''
41     echo "#!${pkgs.runtimeShell}" > "$out/${eventname}"
42     echo '${commands}' >> "$out/${eventname}"
43     chmod a+x "$out/${eventname}"
44   '';
46   eventToShellCmds = event: if builtins.hasAttr event cfg.hooks then (shellCmdsForEventScript event (builtins.getAttr event cfg.hooks)) else "";
48   scriptDir = pkgs.runCommand "apcupsd-scriptdir" { preferLocalBuild = true; } (''
49     mkdir "$out"
50     # Copy SCRIPTDIR from apcupsd package
51     cp -r ${pkgs.apcupsd}/etc/apcupsd/* "$out"/
52     # Make the files writeable (nix will unset the write bits afterwards)
53     chmod u+w "$out"/*
54     # Remove the sample event notification scripts, because they don't work
55     # anyways (they try to send mail to "root" with the "mail" command)
56     (cd "$out" && rm changeme commok commfailure onbattery offbattery)
57     # Remove the sample apcupsd.conf file (we're generating our own)
58     rm "$out/apcupsd.conf"
59     # Set the SCRIPTDIR= line in apccontrol to the dir we're creating now
60     sed -i -e "s|^SCRIPTDIR=.*|SCRIPTDIR=$out|" "$out/apccontrol"
61     '' + concatStringsSep "\n" (map eventToShellCmds eventList)
63   );
69   ###### interface
71   options = {
73     services.apcupsd = {
75       enable = mkOption {
76         default = false;
77         type = types.bool;
78         description = lib.mdDoc ''
79           Whether to enable the APC UPS daemon. apcupsd monitors your UPS and
80           permits orderly shutdown of your computer in the event of a power
81           failure. User manual: http://www.apcupsd.com/manual/manual.html.
82           Note that apcupsd runs as root (to allow shutdown of computer).
83           You can check the status of your UPS with the "apcaccess" command.
84         '';
85       };
87       configText = mkOption {
88         default = ''
89           UPSTYPE usb
90           NISIP 127.0.0.1
91           BATTERYLEVEL 50
92           MINUTES 5
93         '';
94         type = types.lines;
95         description = lib.mdDoc ''
96           Contents of the runtime configuration file, apcupsd.conf. The default
97           settings makes apcupsd autodetect USB UPSes, limit network access to
98           localhost and shutdown the system when the battery level is below 50
99           percent, or when the UPS has calculated that it has 5 minutes or less
100           of remaining power-on time. See man apcupsd.conf for details.
101         '';
102       };
104       hooks = mkOption {
105         default = {};
106         example = {
107           doshutdown = "# shell commands to notify that the computer is shutting down";
108         };
109         type = types.attrsOf types.lines;
110         description = lib.mdDoc ''
111           Each attribute in this option names an apcupsd event and the string
112           value it contains will be executed in a shell, in response to that
113           event (prior to the default action). See "man apccontrol" for the
114           list of events and what they represent.
116           A hook script can stop apccontrol from doing its default action by
117           exiting with value 99. Do not do this unless you know what you're
118           doing.
119         '';
120       };
122     };
124   };
127   ###### implementation
129   config = mkIf cfg.enable {
131     assertions = [ {
132       assertion = let hooknames = builtins.attrNames cfg.hooks; in all (x: elem x eventList) hooknames;
133       message = ''
134         One (or more) attribute names in services.apcupsd.hooks are invalid.
135         Current attribute names: ${toString (builtins.attrNames cfg.hooks)}
136         Valid attribute names  : ${toString eventList}
137       '';
138     } ];
140     # Give users access to the "apcaccess" tool
141     environment.systemPackages = [ pkgs.apcupsd ];
143     # NOTE 1: apcupsd runs as root because it needs permission to run
144     # "shutdown"
145     #
146     # NOTE 2: When apcupsd calls "wall", it prints an error because stdout is
147     # not connected to a tty (it is connected to the journal):
148     #   wall: cannot get tty name: Inappropriate ioctl for device
149     # The message still gets through.
150     systemd.services.apcupsd = {
151       description = "APC UPS Daemon";
152       wantedBy = [ "multi-user.target" ];
153       preStart = "mkdir -p /run/apcupsd/";
154       serviceConfig = {
155         ExecStart = "${pkgs.apcupsd}/bin/apcupsd -b -f ${configFile} -d1";
156         # TODO: When apcupsd has initiated a shutdown, systemd always ends up
157         # waiting for it to stop ("A stop job is running for UPS daemon"). This
158         # is weird, because in the journal one can clearly see that apcupsd has
159         # received the SIGTERM signal and has already quit (or so it seems).
160         # This reduces the wait time from 90 seconds (default) to just 5. Then
161         # systemd kills it with SIGKILL.
162         TimeoutStopSec = 5;
163       };
164       unitConfig.Documentation = "man:apcupsd(8)";
165     };
167     # A special service to tell the UPS to power down/hibernate just before the
168     # computer shuts down. (The UPS has a built in delay before it actually
169     # shuts off power.) Copied from here:
170     # http://forums.opensuse.org/english/get-technical-help-here/applications/479499-apcupsd-systemd-killpower-issues.html
171     systemd.services.apcupsd-killpower = {
172       description = "APC UPS Kill Power";
173       after = [ "shutdown.target" ]; # append umount.target?
174       before = [ "final.target" ];
175       wantedBy = [ "shutdown.target" ];
176       unitConfig = {
177         ConditionPathExists = "/run/apcupsd/powerfail";
178         DefaultDependencies = "no";
179       };
180       serviceConfig = {
181         Type = "oneshot";
182         ExecStart = "${pkgs.apcupsd}/bin/apcupsd --killpower -f ${configFile}";
183         TimeoutSec = "infinity";
184         StandardOutput = "tty";
185         RemainAfterExit = "yes";
186       };
187     };
189   };