{ config, lib, pkgs, ... }: with lib; let inherit (pkgs) cups cups-pk-helper cups_filters; cfg = config.services.printing; avahiEnabled = config.services.avahi.enable; polkitEnabled = config.security.polkit.enable; additionalBackends = pkgs.runCommand "additional-cups-backends" { } '' mkdir -p $out if [ ! -e ${cups}/lib/cups/backend/smb ]; then mkdir -p $out/lib/cups/backend ln -sv ${pkgs.samba}/bin/smbspool $out/lib/cups/backend/smb fi # Provide support for printing via HTTPS. if [ ! -e ${cups}/lib/cups/backend/https ]; then mkdir -p $out/lib/cups/backend ln -sv ${cups}/lib/cups/backend/ipp $out/lib/cups/backend/https fi ''; # Here we can enable additional backends, filters, etc. that are not # part of CUPS itself, e.g. the SMB backend is part of Samba. Since # we can't update ${cups}/lib/cups itself, we create a symlink tree # here and add the additional programs. The ServerBin directive in # cupsd.conf tells cupsd to use this tree. bindir = pkgs.buildEnv { name = "cups-progs"; paths = cfg.drivers; pathsToLink = [ "/lib/cups" "/share/cups" "/bin" "/etc/cups" ]; postBuild = cfg.bindirCmds; ignoreCollisions = true; }; in { ###### interface options = { services.printing = { enable = mkOption { type = types.bool; default = false; description = '' Whether to enable printing support through the CUPS daemon. ''; }; listenAddresses = mkOption { type = types.listOf types.str; default = [ "127.0.0.1:631" ]; example = [ "*:631" ]; description = '' A list of addresses and ports on which to listen. ''; }; bindirCmds = mkOption { type = types.lines; internal = true; default = ""; description = '' Additional commands executed while creating the directory containing the CUPS server binaries. ''; }; defaultShared = mkOption { type = types.bool; default = false; description = '' Specifies whether local printers are shared by default. ''; }; browsing = mkOption { type = types.bool; default = false; description = '' Specifies whether shared printers are advertised. ''; }; webInterface = mkOption { type = types.bool; default = true; description = '' Specifies whether the web interface is enabled. ''; }; cupsdConf = mkOption { type = types.lines; default = ""; example = '' BrowsePoll cups.example.com LogLevel debug ''; description = '' The contents of the configuration file of the CUPS daemon (cupsd.conf). ''; }; cupsFilesConf = mkOption { type = types.lines; default = ""; description = '' The contents of the configuration file of the CUPS daemon (cups-files.conf). ''; }; extraConf = mkOption { type = types.lines; default = ""; example = '' BrowsePoll cups.example.com LogLevel debug ''; description = '' Extra contents of the configuration file of the CUPS daemon (cupsd.conf). ''; }; clientConf = mkOption { type = types.lines; default = ""; example = '' ServerName server.example.com Encryption Never ''; description = '' The contents of the client configuration. (client.conf) ''; }; browsedConf = mkOption { type = types.lines; default = ""; example = '' BrowsePoll cups.example.com ''; description = '' The contents of the configuration. file of the CUPS Browsed daemon (cups-browsed.conf) ''; }; snmpConf = mkOption { type = types.lines; default = '' Address @LOCAL ''; description = '' The contents of /etc/cups/snmp.conf. See "man cups-snmp.conf" for a complete description. ''; }; drivers = mkOption { type = types.listOf types.path; example = literalExample "[ pkgs.splix ]"; description = '' CUPS drivers to use. Drivers provided by CUPS, cups-filters, Ghostscript and Samba are added unconditionally. ''; }; tempDir = mkOption { type = types.path; default = "/tmp"; example = "/tmp/cups"; description = '' CUPSd temporary directory. ''; }; }; }; ###### implementation config = mkIf config.services.printing.enable { users.extraUsers = singleton { name = "cups"; uid = config.ids.uids.cups; group = "lp"; description = "CUPS printing services"; }; environment.systemPackages = [ cups ] ++ optional polkitEnabled cups-pk-helper; environment.etc."cups/client.conf".text = cfg.clientConf; environment.etc."cups/cups-files.conf".text = cfg.cupsFilesConf; environment.etc."cups/cupsd.conf".text = cfg.cupsdConf; environment.etc."cups/cups-browsed.conf".text = cfg.browsedConf; environment.etc."cups/snmp.conf".text = cfg.snmpConf; services.dbus.packages = [ cups ] ++ optional polkitEnabled cups-pk-helper; # Cups uses libusb to talk to printers, and does not use the # linux kernel driver. If the driver is not in a black list, it # gets loaded, and then cups cannot access the printers. boot.blacklistedKernelModules = [ "usblp" ]; systemd.packages = [ cups ]; systemd.services.cups = { wantedBy = [ "multi-user.target" ]; wants = [ "network.target" ]; after = [ "network.target" ]; path = [ cups ]; preStart = '' mkdir -m 0755 -p /etc/cups mkdir -m 0700 -p /var/cache/cups mkdir -m 0700 -p /var/spool/cups mkdir -m 0755 -p ${cfg.tempDir} ''; restartTriggers = [ config.environment.etc."cups/cups-files.conf".source config.environment.etc."cups/cupsd.conf".source ]; }; systemd.services.cups-browsed = mkIf avahiEnabled { description = "CUPS Remote Printer Discovery"; wantedBy = [ "multi-user.target" ]; wants = [ "cups.service" "avahi-daemon.service" ]; bindsTo = [ "cups.service" "avahi-daemon.service" ]; partOf = [ "cups.service" "avahi-daemon.service" ]; after = [ "cups.service" "avahi-daemon.service" ]; path = [ cups ]; serviceConfig.ExecStart = "${cups_filters}/bin/cups-browsed"; restartTriggers = [ config.environment.etc."cups/cups-browsed.conf".source ]; }; services.printing.drivers = [ cups pkgs.ghostscript pkgs.cups_filters additionalBackends pkgs.perl pkgs.coreutils pkgs.gnused pkgs.bc pkgs.gawk pkgs.gnugrep ]; services.printing.cupsFilesConf = '' SystemGroup root wheel ServerBin ${bindir}/lib/cups DataDir ${bindir}/share/cups AccessLog syslog ErrorLog syslog PageLog syslog TempDir ${cfg.tempDir} # User and group used to run external programs, including # those that actually send the job to the printer. Note that # Udev sets the group of printer devices to `lp', so we want # these programs to run as `lp' as well. User cups Group lp ''; services.printing.cupsdConf = '' LogLevel info ${concatMapStrings (addr: '' Listen ${addr} '') cfg.listenAddresses} Listen /var/run/cups/cups.sock SetEnv PATH ${bindir}/lib/cups/filter:${bindir}/bin:${bindir}/sbin DefaultShared ${if cfg.defaultShared then "Yes" else "No"} Browsing ${if cfg.browsing then "Yes" else "No"} WebInterface ${if cfg.webInterface then "Yes" else "No"} DefaultAuthType Basic Order allow,deny Allow localhost Order allow,deny Allow localhost AuthType Basic Require user @SYSTEM Order allow,deny Allow localhost Require user @OWNER @SYSTEM Order deny,allow AuthType Basic Require user @SYSTEM Order deny,allow Require user @OWNER @SYSTEM Order deny,allow Order deny,allow ${cfg.extraConf} ''; security.pam.services.cups = {}; }; }