diff options
author | Florian Jacob <projects+git@florianjacob.de> | 2019-08-05 20:24:18 +0200 |
---|---|---|
committer | Florian Jacob <projects+git@florianjacob.de> | 2019-09-01 15:38:30 +0200 |
commit | 18a5d23b55d0b0d1bff6bfd971d471d3064011c2 (patch) | |
tree | d530cf3a953cd8443aace8f41a9aa3f9dc9ca8b2 /nixos/modules/hardware | |
parent | d4283f47dca04ae5af0188b72c7f8d2b8e0cb7f1 (diff) |
nixos/printers: declarative configuration
Diffstat (limited to 'nixos/modules/hardware')
-rw-r--r-- | nixos/modules/hardware/printers.nix | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/nixos/modules/hardware/printers.nix b/nixos/modules/hardware/printers.nix new file mode 100644 index 000000000000..12ee5516d4ed --- /dev/null +++ b/nixos/modules/hardware/printers.nix @@ -0,0 +1,135 @@ +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.hardware.printers; + ppdOptionsString = options: optionalString (options != {}) + (concatStringsSep " " + (mapAttrsToList (name: value: "-o '${name}'='${value}'") options) + ); + ensurePrinter = p: '' + ${pkgs.cups}/bin/lpadmin -p '${p.name}' -E \ + ${optionalString (p.location != null) "-L '${p.location}'"} \ + ${optionalString (p.description != null) "-D '${p.description}'"} \ + -v '${p.deviceUri}' \ + -m '${p.model}' \ + ${ppdOptionsString p.ppdOptions} + ''; + ensureDefaultPrinter = name: '' + ${pkgs.cups}/bin/lpoptions -d '${name}' + ''; + + # "graph but not # or /" can't be implemented as regex alone due to missing lookahead support + noInvalidChars = str: all (c: c != "#" && c != "/") (stringToCharacters str); + printerName = (types.addCheck (types.strMatching "[[:graph:]]+") noInvalidChars) + // { description = "printable string without spaces, # and /"; }; + + +in { + options = { + hardware.printers = { + ensureDefaultPrinter = mkOption { + type = types.nullOr printerName; + default = null; + description = '' + Ensures the named printer is the default CUPS printer / printer queue. + ''; + }; + ensurePrinters = mkOption { + description = '' + Will regularly ensure that the given CUPS printers are configured as declared here. + If a printer's options are manually changed afterwards, they will be overwritten eventually. + This option will never delete any printer, even if removed from this list. + You can check existing printers with <command>lpstat -s</command> + and remove printers with <command>lpadmin -x <printer-name></command>. + Printers not listed here can still be manually configured. + ''; + default = []; + type = types.listOf (types.submodule { + options = { + name = mkOption { + type = printerName; + example = "BrotherHL_Workroom"; + description = '' + Name of the printer / printer queue. + May contain any printable characters except "/", "#", and space. + ''; + }; + location = mkOption { + type = types.nullOr types.str; + default = null; + example = "Workroom"; + description = '' + Optional human-readable location. + ''; + }; + description = mkOption { + type = types.nullOr types.str; + default = null; + example = "Brother HL-5140"; + description = '' + Optional human-readable description. + ''; + }; + deviceUri = mkOption { + type = types.str; + example = [ + "ipp://printserver.local/printers/BrotherHL_Workroom" + "usb://HP/DESKJET%20940C?serial=CN16E6C364BH" + ]; + description = '' + How to reach the printer. + <command>lpinfo -v</command> shows a list of supported device URIs and schemes. + ''; + }; + model = mkOption { + type = types.str; + example = literalExample '' + gutenprint.''${lib.version.majorMinor (lib.getVersion pkgs.cups)}://brother-hl-5140/expert + ''; + description = '' + Location of the ppd driver file for the printer. + <command>lpinfo -m</command> shows a list of supported models. + ''; + }; + ppdOptions = mkOption { + type = types.attrsOf types.str; + example = { + "PageSize" = "A4"; + "Duplex" = "DuplexNoTumble"; + }; + default = {}; + description = '' + Sets PPD options for the printer. + <command>lpoptions [-p printername] -l</command> shows suported PPD options for the given printer. + ''; + }; + }; + }); + }; + }; + }; + + config = mkIf (cfg.ensurePrinters != [] && config.services.printing.enable) { + systemd.services."ensure-printers" = let + cupsUnit = if config.services.printing.startWhenNeeded then "cups.socket" else "cups.service"; + in { + description = "Ensure NixOS-configured CUPS printers"; + wantedBy = [ "multi-user.target" ]; + requires = [ cupsUnit ]; + # in contrast to cups.socket, for cups.service, this is actually not enough, + # as the cups service reports its activation before clients can actually interact with it. + # Because of this, commands like `lpinfo -v` will report a bad file descriptor + # due to the missing UNIX socket without sufficient sleep time. + after = [ cupsUnit ]; + + serviceConfig = { + Type = "oneshot"; + }; + + # sleep 10 is required to wait until cups.service is actually initialized and has created its UNIX socket file + script = (optionalString (!config.services.printing.startWhenNeeded) "sleep 10\n") + + (concatMapStringsSep "\n" ensurePrinter cfg.ensurePrinters) + + optionalString (cfg.ensureDefaultPrinter != null) (ensureDefaultPrinter cfg.ensureDefaultPrinter); + }; + }; +} |