systemctl has the ability to display a list of documentation URLs in the output of "systemctl status <service-name>".
		
			
				
	
	
		
			192 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
{ config, lib, pkgs, ... }:
 | 
						|
 | 
						|
with 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}
 | 
						|
    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
 | 
						|
 | 
						|
{
 | 
						|
 | 
						|
  ###### interface
 | 
						|
 | 
						|
  options = {
 | 
						|
 | 
						|
    services.apcupsd = {
 | 
						|
 | 
						|
      enable = mkOption {
 | 
						|
        default = false;
 | 
						|
        type = types.uniq types.bool;
 | 
						|
        description = ''
 | 
						|
          Whether to enable the APC UPS daemon. apcupsd monitors your UPS and
 | 
						|
          permits orderly shutdown of your computer in the event of a power
 | 
						|
          failure. User manual: http://www.apcupsd.com/manual/manual.html.
 | 
						|
          Note that apcupsd runs as root (to allow shutdown of computer).
 | 
						|
          You can check the status of your UPS with the "apcaccess" command.
 | 
						|
        '';
 | 
						|
      };
 | 
						|
 | 
						|
      configText = mkOption {
 | 
						|
        default = ''
 | 
						|
          UPSTYPE usb
 | 
						|
          NISIP 127.0.0.1
 | 
						|
          BATTERYLEVEL 50
 | 
						|
          MINUTES 5
 | 
						|
        '';
 | 
						|
        type = types.string;
 | 
						|
        description = ''
 | 
						|
          Contents of the runtime configuration file, apcupsd.conf. The default
 | 
						|
          settings makes apcupsd autodetect USB UPSes, limit network access to
 | 
						|
          localhost and shutdown the system when the battery level is below 50
 | 
						|
          percent, or when the UPS has calculated that it has 5 minutes or less
 | 
						|
          of remaining power-on time. See man apcupsd.conf for details.
 | 
						|
        '';
 | 
						|
      };
 | 
						|
 | 
						|
      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.
 | 
						|
        '';
 | 
						|
      };
 | 
						|
 | 
						|
    };
 | 
						|
 | 
						|
  };
 | 
						|
 | 
						|
 | 
						|
  ###### implementation
 | 
						|
 | 
						|
  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 ];
 | 
						|
 | 
						|
    # NOTE 1: apcupsd runs as root because it needs permission to run
 | 
						|
    # "shutdown"
 | 
						|
    #
 | 
						|
    # NOTE 2: When apcupsd calls "wall", it prints an error because stdout is
 | 
						|
    # 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.
 | 
						|
    systemd.services.apcupsd = {
 | 
						|
      description = "APC UPS Daemon";
 | 
						|
      wantedBy = [ "multi-user.target" ];
 | 
						|
      preStart = "mkdir -p /run/apcupsd/";
 | 
						|
      serviceConfig = {
 | 
						|
        ExecStart = "${pkgs.apcupsd}/bin/apcupsd -b -f ${configFile} -d1";
 | 
						|
        # TODO: When apcupsd has initiated a shutdown, systemd always ends up
 | 
						|
        # waiting for it to stop ("A stop job is running for UPS daemon"). This
 | 
						|
        # is weird, because in the journal one can clearly see that apcupsd has
 | 
						|
        # received the SIGTERM signal and has already quit (or so it seems).
 | 
						|
        # This reduces the wait time from 90 seconds (default) to just 5. Then
 | 
						|
        # systemd kills it with SIGKILL.
 | 
						|
        TimeoutStopSec = 5;
 | 
						|
      };
 | 
						|
      unitConfig.Documentation = "man:apcupsd(8)";
 | 
						|
    };
 | 
						|
 | 
						|
    # A special service to tell the UPS to power down/hibernate just before the
 | 
						|
    # computer shuts down. (The UPS has a built in delay before it actually
 | 
						|
    # shuts off power.) Copied from here:
 | 
						|
    # http://forums.opensuse.org/english/get-technical-help-here/applications/479499-apcupsd-systemd-killpower-issues.html
 | 
						|
    systemd.services.apcupsd-killpower = {
 | 
						|
      description = "APC UPS Kill Power";
 | 
						|
      after = [ "shutdown.target" ]; # append umount.target?
 | 
						|
      before = [ "final.target" ];
 | 
						|
      wantedBy = [ "shutdown.target" ];
 | 
						|
      unitConfig = {
 | 
						|
        ConditionPathExists = "/run/apcupsd/powerfail";
 | 
						|
        DefaultDependencies = "no";
 | 
						|
      };
 | 
						|
      serviceConfig = {
 | 
						|
        Type = "oneshot";
 | 
						|
        ExecStart = "${pkgs.apcupsd}/bin/apcupsd --killpower -f ${configFile}";
 | 
						|
        TimeoutSec = 0;
 | 
						|
        StandardOutput = "tty";
 | 
						|
        RemainAfterExit = "yes";
 | 
						|
      };
 | 
						|
    };
 | 
						|
 | 
						|
  };
 | 
						|
 | 
						|
}
 |