at 23.11-pre 6.1 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 pkg = pkgs.sane-backends.override { 8 scanSnapDriversUnfree = config.hardware.sane.drivers.scanSnap.enable; 9 scanSnapDriversPackage = config.hardware.sane.drivers.scanSnap.package; 10 }; 11 12 sanedConf = pkgs.writeTextFile { 13 name = "saned.conf"; 14 destination = "/etc/sane.d/saned.conf"; 15 text = '' 16 localhost 17 ${config.services.saned.extraConfig} 18 ''; 19 }; 20 21 netConf = pkgs.writeTextFile { 22 name = "net.conf"; 23 destination = "/etc/sane.d/net.conf"; 24 text = '' 25 ${lib.optionalString config.services.saned.enable "localhost"} 26 ${config.hardware.sane.netConf} 27 ''; 28 }; 29 30 env = { 31 SANE_CONFIG_DIR = "/etc/sane-config"; 32 LD_LIBRARY_PATH = [ "/etc/sane-libs" ]; 33 }; 34 35 backends = [ pkg netConf ] ++ optional config.services.saned.enable sanedConf ++ config.hardware.sane.extraBackends; 36 saneConfig = pkgs.mkSaneConfig { paths = backends; inherit (config.hardware.sane) disabledDefaultBackends; }; 37 38 enabled = config.hardware.sane.enable || config.services.saned.enable; 39 40in 41 42{ 43 44 ###### interface 45 46 options = { 47 48 hardware.sane.enable = mkOption { 49 type = types.bool; 50 default = false; 51 description = lib.mdDoc '' 52 Enable support for SANE scanners. 53 54 ::: {.note} 55 Users in the "scanner" group will gain access to the scanner, or the "lp" group if it's also a printer. 56 ::: 57 ''; 58 }; 59 60 hardware.sane.snapshot = mkOption { 61 type = types.bool; 62 default = false; 63 description = lib.mdDoc "Use a development snapshot of SANE scanner drivers."; 64 }; 65 66 hardware.sane.extraBackends = mkOption { 67 type = types.listOf types.path; 68 default = []; 69 description = lib.mdDoc '' 70 Packages providing extra SANE backends to enable. 71 72 ::: {.note} 73 The example contains the package for HP scanners, and the package for 74 Apple AirScan and Microsoft WSD support (supports many 75 vendors/devices). 76 ::: 77 ''; 78 example = literalExpression "[ pkgs.hplipWithPlugin pkgs.sane-airscan ]"; 79 }; 80 81 hardware.sane.disabledDefaultBackends = mkOption { 82 type = types.listOf types.str; 83 default = []; 84 example = [ "v4l" ]; 85 description = lib.mdDoc '' 86 Names of backends which are enabled by default but should be disabled. 87 See `$SANE_CONFIG_DIR/dll.conf` for the list of possible names. 88 ''; 89 }; 90 91 hardware.sane.configDir = mkOption { 92 type = types.str; 93 internal = true; 94 description = lib.mdDoc "The value of SANE_CONFIG_DIR."; 95 }; 96 97 hardware.sane.netConf = mkOption { 98 type = types.lines; 99 default = ""; 100 example = "192.168.0.16"; 101 description = lib.mdDoc '' 102 Network hosts that should be probed for remote scanners. 103 ''; 104 }; 105 106 hardware.sane.drivers.scanSnap.enable = mkOption { 107 type = types.bool; 108 default = false; 109 example = true; 110 description = lib.mdDoc '' 111 Whether to enable drivers for the Fujitsu ScanSnap scanners. 112 113 The driver files are unfree and extracted from the Windows driver image. 114 ''; 115 }; 116 117 hardware.sane.drivers.scanSnap.package = mkOption { 118 type = types.package; 119 default = pkgs.sane-drivers.epjitsu; 120 defaultText = literalExpression "pkgs.sane-drivers.epjitsu"; 121 description = lib.mdDoc '' 122 Epjitsu driver package to use. Useful if you want to extract the driver files yourself. 123 124 The process is described in the `/etc/sane.d/epjitsu.conf` file in 125 the `sane-backends` package. 126 ''; 127 }; 128 129 hardware.sane.openFirewall = mkOption { 130 type = types.bool; 131 default = false; 132 description = lib.mdDoc '' 133 Open ports needed for discovery of scanners on the local network, e.g. 134 needed for Canon scanners (BJNP protocol). 135 ''; 136 }; 137 138 services.saned.enable = mkOption { 139 type = types.bool; 140 default = false; 141 description = lib.mdDoc '' 142 Enable saned network daemon for remote connection to scanners. 143 144 saned would be run from `scanner` user; to allow 145 access to hardware that doesn't have `scanner` group 146 you should add needed groups to this user. 147 ''; 148 }; 149 150 services.saned.extraConfig = mkOption { 151 type = types.lines; 152 default = ""; 153 example = "192.168.0.0/24"; 154 description = lib.mdDoc '' 155 Extra saned configuration lines. 156 ''; 157 }; 158 159 }; 160 161 162 ###### implementation 163 164 config = mkMerge [ 165 (mkIf enabled { 166 hardware.sane.configDir = mkDefault "${saneConfig}/etc/sane.d"; 167 168 environment.systemPackages = backends; 169 environment.sessionVariables = env; 170 environment.etc."sane-config".source = config.hardware.sane.configDir; 171 environment.etc."sane-libs".source = "${saneConfig}/lib/sane"; 172 services.udev.packages = backends; 173 174 users.groups.scanner.gid = config.ids.gids.scanner; 175 networking.firewall.allowedUDPPorts = mkIf config.hardware.sane.openFirewall [ 8612 ]; 176 }) 177 178 (mkIf config.services.saned.enable { 179 networking.firewall.connectionTrackingModules = [ "sane" ]; 180 181 systemd.services."saned@" = { 182 description = "Scanner Service"; 183 environment = mapAttrs (name: val: toString val) env; 184 serviceConfig = { 185 User = "scanner"; 186 Group = "scanner"; 187 ExecStart = "${pkg}/bin/saned"; 188 }; 189 }; 190 191 systemd.sockets.saned = { 192 description = "saned incoming socket"; 193 wantedBy = [ "sockets.target" ]; 194 listenStreams = [ "0.0.0.0:6566" "[::]:6566" ]; 195 socketConfig = { 196 # saned needs to distinguish between IPv4 and IPv6 to open matching data sockets. 197 BindIPv6Only = "ipv6-only"; 198 Accept = true; 199 MaxConnections = 64; 200 }; 201 }; 202 203 users.users.scanner = { 204 uid = config.ids.uids.scanner; 205 group = "scanner"; 206 extraGroups = [ "lp" ] ++ optionals config.services.avahi.enable [ "avahi" ]; 207 }; 208 }) 209 ]; 210 211}