From dc61694d013cc7bc65006770b0a5278cd56440a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Forsman?= Date: Sun, 21 Jul 2013 17:21:06 +0200 Subject: [PATCH] apcupsd-service: add services.apcupsd.hooks option Each attribute in this option should name an apcupsd event and the string value it contains will be executed in a shell in response to that event. See "man apccontrol" for the list of events and what they represent. Now it is easy to hook into the apcupsd event system: services.apcupsd.hooks = { onbattery = ''# shell commands to run when the onbattery event is emitted''; doshutdown = ''# shell commands to notify that the computer is shutting down''; }; --- modules/services/monitoring/apcupsd.nix | 97 ++++++++++++++++++++++--- 1 file changed, 87 insertions(+), 10 deletions(-) diff --git a/modules/services/monitoring/apcupsd.nix b/modules/services/monitoring/apcupsd.nix index 5541fb92fe1a..053851be398f 100644 --- a/modules/services/monitoring/apcupsd.nix +++ b/modules/services/monitoring/apcupsd.nix @@ -2,12 +2,66 @@ with pkgs.lib; -let cfg = config.services.apcupsd; - configFile = pkgs.writeText "apcupsd.conf" '' - ## apcupsd.conf v1.1 ## - # apcupsd complains if the first line is not like above. - ${cfg.configText} - ''; +let + cfg = config.services.apcupsd; + + configFile = pkgs.writeText "apcupsd.conf" '' + ## apcupsd.conf v1.1 ## + # apcupsd complains if the first line is not like above. + ${cfg.configText} + SCRIPTDIR ${toString scriptDir} + ''; + + # List of events from "man apccontrol" + eventList = [ + "annoyme" + "battattach" + "battdetach" + "changeme" + "commfailure" + "commok" + "doreboot" + "doshutdown" + "emergency" + "failing" + "killpower" + "loadlimit" + "mainsback" + "onbattery" + "offbattery" + "powerout" + "remotedown" + "runlimit" + "timeout" + "startselftest" + "endselftest" + ]; + + shellCmdsForEventScript = eventname: commands: '' + echo "#!${pkgs.stdenv.shell}" > "$out/${eventname}" + echo "${commands}" >> "$out/${eventname}" + chmod a+x "$out/${eventname}" + ''; + + eventToShellCmds = event: if builtins.hasAttr event cfg.hooks then (shellCmdsForEventScript event (builtins.getAttr event cfg.hooks)) else ""; + + scriptDir = pkgs.runCommand "apcupsd-scriptdir" {} ('' + mkdir "$out" + # Copy SCRIPTDIR from apcupsd package + cp -r ${pkgs.apcupsd}/etc/apcupsd/* "$out"/ + # Make the files writeable (nix will unset the write bits afterwards) + chmod u+w "$out"/* + # Remove the sample event notification scripts, because they don't work + # anyways (they try to send mail to "root" with the "mail" command) + (cd "$out" && rm changeme commok commfailure onbattery offbattery) + # Remove the sample apcupsd.conf file (we're generating our own) + rm "$out/apcupsd.conf" + # Set the SCRIPTDIR= line in apccontrol to the dir we're creating now + sed -i -e "s|^SCRIPTDIR=.*|SCRIPTDIR=$out|" "$out/apccontrol" + '' + concatStringsSep "\n" (map eventToShellCmds eventList) + + ); + in { @@ -47,6 +101,24 @@ in ''; }; + hooks = mkOption { + default = {}; + example = { + doshutdown = ''# shell commands to notify that the computer is shutting down''; + }; + type = types.attrsOf types.string; + description = '' + Each attribute in this option names an apcupsd event and the string + value it contains will be executed in a shell, in response to that + event (prior to the default action). See "man apccontrol" for the + list of events and what they represent. + + A hook script can stop apccontrol from doing its default action by + exiting with value 99. Do not do this unless you know what you're + doing. + ''; + }; + }; }; @@ -56,6 +128,15 @@ in config = mkIf cfg.enable { + assertions = [ { + assertion = let hooknames = builtins.attrNames cfg.hooks; in all (x: elem x eventList) hooknames; + message = '' + One (or more) attribute names in services.apcupsd.hooks are invalid. + Current attribute names: ${toString (builtins.attrNames cfg.hooks)} + Valid attribute names : ${toString eventList} + ''; + } ]; + # Give users access to the "apcaccess" tool environment.systemPackages = [ pkgs.apcupsd ]; @@ -66,10 +147,6 @@ in # not connected to a tty (it is connected to the journal): # wall: cannot get tty name: Inappropriate ioctl for device # The message still gets through. - # - # TODO: apcupsd calls "mail" on powerfail etc. events, how should we - # handle that? A configurable mail package or let the event logic be - # configured from nix expressions? systemd.services.apcupsd = { description = "UPS daemon"; wantedBy = [ "multi-user.target" ];