nixos/preload: init
[NixPkgs.git] / nixos / modules / services / hardware / sane.nix
blob2cac2e8e8bb471100df92e8e79e6fc28ea5182c2
1 { config, lib, pkgs, ... }:
3 with lib;
5 let
7   pkg = pkgs.sane-backends.override {
8     scanSnapDriversUnfree = config.hardware.sane.drivers.scanSnap.enable;
9     scanSnapDriversPackage = config.hardware.sane.drivers.scanSnap.package;
10   };
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   };
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   };
30   env = {
31     SANE_CONFIG_DIR = "/etc/sane-config";
32     LD_LIBRARY_PATH = [ "/etc/sane-libs" ];
33   };
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; };
38   enabled = config.hardware.sane.enable || config.services.saned.enable;
44   ###### interface
46   options = {
48     hardware.sane.enable = mkOption {
49       type = types.bool;
50       default = false;
51       description = lib.mdDoc ''
52         Enable support for SANE scanners.
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     };
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     };
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.
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     };
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     };
91     hardware.sane.configDir = mkOption {
92       type = types.str;
93       internal = true;
94       description = lib.mdDoc "The value of SANE_CONFIG_DIR.";
95     };
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     };
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.
113         The driver files are unfree and extracted from the Windows driver image.
114       '';
115     };
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.
124         The process is described in the `/etc/sane.d/epjitsu.conf` file in
125         the `sane-backends` package.
126       '';
127     };
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     };
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.
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     };
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     };
159   };
162   ###### implementation
164   config = mkMerge [
165     (mkIf enabled {
166       hardware.sane.configDir = mkDefault "${saneConfig}/etc/sane.d";
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;
174       users.groups.scanner.gid = config.ids.gids.scanner;
175       networking.firewall.allowedUDPPorts = mkIf config.hardware.sane.openFirewall [ 8612 ];
176     })
178     (mkIf config.services.saned.enable {
179       networking.firewall.connectionTrackingModules = [ "sane" ];
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       };
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       };
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   ];