1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.pcscd;
7 cfgFile = pkgs.writeText "reader.conf" config.services.pcscd.readerConfig;
8
9 package = if config.security.polkit.enable
10 then pkgs.pcscliteWithPolkit
11 else pkgs.pcsclite;
12
13 pluginEnv = pkgs.buildEnv {
14 name = "pcscd-plugins";
15 paths = map (p: "${p}/pcsc/drivers") config.services.pcscd.plugins;
16 };
17
18in
19{
20 options.services.pcscd = {
21 enable = mkEnableOption "PCSC-Lite daemon, to access smart cards using SCard API (PC/SC)";
22
23 plugins = mkOption {
24 type = types.listOf types.package;
25 defaultText = literalExpression "[ pkgs.ccid ]";
26 example = literalExpression "[ pkgs.pcsc-cyberjack ]";
27 description = "Plugin packages to be used for PCSC-Lite.";
28 };
29
30 readerConfig = mkOption {
31 type = types.lines;
32 default = "";
33 example = ''
34 FRIENDLYNAME "Some serial reader"
35 DEVICENAME /dev/ttyS0
36 LIBPATH /path/to/serial_reader.so
37 CHANNELID 1
38 '';
39 description = ''
40 Configuration for devices that aren't hotpluggable.
41
42 See {manpage}`reader.conf(5)` for valid options.
43 '';
44 };
45
46 extraArgs = mkOption {
47 type = types.listOf types.str;
48 default = [ ];
49 description = "Extra command line arguments to be passed to the PCSC daemon.";
50 };
51 };
52
53 config = mkIf config.services.pcscd.enable {
54 environment.etc."reader.conf".source = cfgFile;
55
56 environment.systemPackages = [ package ];
57 systemd.packages = [ package ];
58
59 services.pcscd.plugins = [ pkgs.ccid ];
60
61 systemd.sockets.pcscd.wantedBy = [ "sockets.target" ];
62
63 systemd.services.pcscd = {
64 environment.PCSCLITE_HP_DROPDIR = pluginEnv;
65
66 # If the cfgFile is empty and not specified (in which case the default
67 # /etc/reader.conf is assumed), pcscd will happily start going through the
68 # entire confdir (/etc in our case) looking for a config file and try to
69 # parse everything it finds. Doesn't take a lot of imagination to see how
70 # well that works. It really shouldn't do that to begin with, but to work
71 # around it, we force the path to the cfgFile.
72 #
73 # https://github.com/NixOS/nixpkgs/issues/121088
74 serviceConfig.ExecStart = [ "" "${lib.getExe package} -f -x -c ${cfgFile} ${lib.escapeShellArgs cfg.extraArgs}" ];
75 };
76 };
77}