From feab16815eb81f28d72518562a811ccb9bd9b292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Forsman?= Date: Thu, 8 Jan 2026 18:56:21 +0100 Subject: [PATCH 1/8] nixosTests.pcsclite: init Add a small test to verify that the pcscd socket unit works, and that a client can connect (which requires auto-starting pcscd.service). --- nixos/tests/all-tests.nix | 1 + nixos/tests/pcsclite.nix | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 nixos/tests/pcsclite.nix diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 282835431246..3f21e9b9e63c 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1227,6 +1227,7 @@ in pass-secret-service = runTest ./pass-secret-service.nix; password-option-override-ordering = runTest ./password-option-override-ordering.nix; patroni = handleTestOn [ "x86_64-linux" ] ./patroni.nix { }; + pcsclite = runTest ./pcsclite.nix; pdns-recursor = runTest ./pdns-recursor.nix; peerflix = runTest ./peerflix.nix; peering-manager = runTest ./web-apps/peering-manager.nix; diff --git a/nixos/tests/pcsclite.nix b/nixos/tests/pcsclite.nix new file mode 100644 index 000000000000..de70e7fb9313 --- /dev/null +++ b/nixos/tests/pcsclite.nix @@ -0,0 +1,29 @@ +{ + lib, + ... +}: + +{ + name = "pcsclite"; + meta.maintainers = [ lib.maintainers.bjornfor ]; + + nodes = { + machine = + { pkgs, ... }: + { + environment.systemPackages = [ + pkgs.pcsc-tools + ]; + services.pcscd = { + enable = true; + }; + }; + }; + + testScript = '' + machine.wait_for_unit("pcscd.socket") + + with subtest("client can connect"): + machine.succeed("pcsc_scan -r") + ''; +} From 2dbc074f02c0973396b7129a0d80630f2cec754c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Forsman?= Date: Thu, 8 Jan 2026 20:19:49 +0100 Subject: [PATCH 2/8] pcsclite: add nixosTests.pcsclite to passthru.tests --- pkgs/by-name/pc/pcsclite/package.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkgs/by-name/pc/pcsclite/package.nix b/pkgs/by-name/pc/pcsclite/package.nix index b222c64655a3..e103ca1b6749 100644 --- a/pkgs/by-name/pc/pcsclite/package.nix +++ b/pkgs/by-name/pc/pcsclite/package.nix @@ -21,6 +21,7 @@ nix-update-script, pname ? "pcsclite", polkitSupport ? false, + nixosTests, }: stdenv.mkDerivation (finalAttrs: { @@ -110,6 +111,7 @@ stdenv.mkDerivation (finalAttrs: { passthru = { tests = { + nixos = nixosTests.pcsclite; pkg-config = testers.testMetaPkgConfig finalAttrs.finalPackage; version = testers.testVersion { package = finalAttrs.finalPackage; From a3df30af6a5cd577e18ab8a01ef29d3e1e6e448a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Forsman?= Date: Thu, 8 Jan 2026 20:41:19 +0100 Subject: [PATCH 3/8] pcsclite: remove outdated comment Fixes: 96d6f0a2ddb07916cd1a3230fbe6a21e4dc6b29e ("pcsclite: 2.1.0 -> 2.2.3") --- pkgs/by-name/pc/pcsclite/package.nix | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkgs/by-name/pc/pcsclite/package.nix b/pkgs/by-name/pc/pcsclite/package.nix index e103ca1b6749..0312b812f1eb 100644 --- a/pkgs/by-name/pc/pcsclite/package.nix +++ b/pkgs/by-name/pc/pcsclite/package.nix @@ -68,8 +68,6 @@ stdenv.mkDerivation (finalAttrs: { (lib.mesonBool "libudev" false) ]; - # disable building pcsc-wirecheck{,-gen} when cross compiling - # see also: https://github.com/LudovicRousseau/PCSC/issues/25 postPatch = '' substituteInPlace src/libredirect.c src/spy/libpcscspy.c \ --replace-fail "libpcsclite_real.so.1" "$lib/lib/libpcsclite_real.so.1" From 045a0c2af3bcd905eaaee4dab2e8338994c7d7b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Forsman?= Date: Thu, 8 Jan 2026 18:55:03 +0100 Subject: [PATCH 4/8] nixos/pcscd: add 'pcscd' user and group In preparation for pcsclite (>=2.4.0) upgrade, where this user and group is required. --- nixos/modules/services/hardware/pcscd.nix | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/nixos/modules/services/hardware/pcscd.nix b/nixos/modules/services/hardware/pcscd.nix index 10229c468740..88d6a7296ac6 100644 --- a/nixos/modules/services/hardware/pcscd.nix +++ b/nixos/modules/services/hardware/pcscd.nix @@ -134,5 +134,12 @@ in "${lib.getExe package} -f -x -c ${cfgFile} ${lib.escapeShellArgs cfg.extraArgs}" ]; }; + + users.users.pcscd = { + isSystemUser = true; + group = "pcscd"; + }; + + users.groups.pcscd = { }; }; } From 45683f63b42b6a1db131c2ac2eabe2e49916a4d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Forsman?= Date: Sun, 1 Feb 2026 22:40:50 +0100 Subject: [PATCH 5/8] ccid: prepare udev rules for use in NixOS There is a RUN+="/usr/sbin/Kobil_mIDentity_switch" directive in the udev rules file, and since we don't have that program (we don't build ccid with "extras"), NixOS fails use it: Checking that all programs called by absolute paths in udev rules exist... FAIL /usr/sbin/Kobil_mIDentity_switch is called in udev rules but is not executable or does not exist Fix it by disabling that rule for now. --- pkgs/by-name/cc/ccid/package.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkgs/by-name/cc/ccid/package.nix b/pkgs/by-name/cc/ccid/package.nix index c5bc1aa625d3..b1792ddd96be 100644 --- a/pkgs/by-name/cc/ccid/package.nix +++ b/pkgs/by-name/cc/ccid/package.nix @@ -61,6 +61,10 @@ stdenv.mkDerivation (finalAttrs: { install -Dm 0444 -t $out/lib/udev/rules.d ../src/92_pcscd_ccid.rules substituteInPlace $out/lib/udev/rules.d/92_pcscd_ccid.rules \ --replace-fail "/usr/sbin/pcscd" "${pcsclite}/bin/pcscd" + # Disable reference to binary that we don't build. + substituteInPlace $out/lib/udev/rules.d/92_pcscd_ccid.rules \ + --replace-fail 'ATTRS{idVendor}=="0d46", ATTRS{idProduct}=="4081", RUN+="/usr/sbin/Kobil_mIDentity_switch"' \ + '# disabled: ATTRS{idVendor}=="0d46", ATTRS{idProduct}=="4081", RUN+="/usr/sbin/Kobil_mIDentity_switch"' ''; # The resulting shared object ends up outside of the default paths which are From 9b0996207a7e9e5ad48ea5d03d417e63bdc7095f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Forsman?= Date: Sun, 1 Feb 2026 14:54:23 +0100 Subject: [PATCH 6/8] nixos/pcscd: add ccid udev rules In preparation for pcsclite (>= 2.4.0) upgrade, where the ccid (>= 1.7.0) udev rules are needed to provide smart card reader access to the 'pcscd' group. --- nixos/modules/services/hardware/pcscd.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nixos/modules/services/hardware/pcscd.nix b/nixos/modules/services/hardware/pcscd.nix index 88d6a7296ac6..f7e23df84f01 100644 --- a/nixos/modules/services/hardware/pcscd.nix +++ b/nixos/modules/services/hardware/pcscd.nix @@ -106,6 +106,8 @@ in services.pcscd.plugins = [ pkgs.ccid ]; + services.udev.packages = [ pkgs.ccid ]; + systemd.sockets.pcscd.wantedBy = [ "sockets.target" ]; systemd.services.pcscd = { From 1b6de4b006bf576ad0b6c15bb35ac78281a97e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Forsman?= Date: Thu, 8 Jan 2026 20:07:08 +0100 Subject: [PATCH 7/8] ccid: 1.6.2 -> 1.7.0 Changelog: https://github.com/LudovicRousseau/CCID/releases/tag/1.7.0 Notably, this updates the udev rules to give the 'pcscd' group access to CCID devices. That's needed for pcsclite>=2.4.0. Upstream have added installation of the udev rules, but it does so outside of PREFIX. Keep using our own installation code instead, which is simpler than patching upstream build system. --- pkgs/by-name/cc/ccid/package.nix | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkgs/by-name/cc/ccid/package.nix b/pkgs/by-name/cc/ccid/package.nix index b1792ddd96be..3157c01c0fd2 100644 --- a/pkgs/by-name/cc/ccid/package.nix +++ b/pkgs/by-name/cc/ccid/package.nix @@ -15,13 +15,13 @@ stdenv.mkDerivation (finalAttrs: { pname = "ccid"; - version = "1.6.2"; + version = "1.7.0"; src = fetchFromGitHub { owner = "LudovicRousseau"; repo = "CCID"; tag = finalAttrs.version; - hash = "sha256-n7rOjnLZH4RLmddtBycr3FK2Bi/OLR+9IjWBRbWjnUw="; + hash = "sha256-6eaznSIQZl1bpIe1F9EtwotF9BjOruJ9g/c2QrTgfUg="; }; postPatch = '' @@ -33,6 +33,9 @@ stdenv.mkDerivation (finalAttrs: { mesonFlags = [ (lib.mesonBool "serial" true) + # Upstream tries to install udev outside of PREFIX. It's easier to disable + # this and install it ourself than to patch meson.build. + (lib.mesonBool "udev-rules" false) ]; # error: call to undeclared function 'InterruptRead'; From c15181077bfcb9a1fd17dc25a8665a1a21821b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Forsman?= Date: Mon, 5 Jan 2026 11:18:21 +0100 Subject: [PATCH 8/8] pcsclite: 2.3.0 -> 2.4.1 Changelog: https://github.com/LudovicRousseau/PCSC/blob/2.4.1/ChangeLog Remove patch that is now included in upstream release. --- pkgs/by-name/pc/pcsclite/package.nix | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/pkgs/by-name/pc/pcsclite/package.nix b/pkgs/by-name/pc/pcsclite/package.nix index 0312b812f1eb..c4d14fe7a137 100644 --- a/pkgs/by-name/pc/pcsclite/package.nix +++ b/pkgs/by-name/pc/pcsclite/package.nix @@ -2,7 +2,6 @@ stdenv, lib, fetchFromGitLab, - fetchpatch, meson, ninja, flex, @@ -26,7 +25,7 @@ stdenv.mkDerivation (finalAttrs: { inherit pname; - version = "2.3.0"; + version = "2.4.1"; outputs = [ "out" @@ -41,18 +40,9 @@ stdenv.mkDerivation (finalAttrs: { owner = "rousseau"; repo = "PCSC"; tag = finalAttrs.version; - hash = "sha256-37qeWGEuutF0cOOidoLchKJLQCvJFdVRZXepWzD4pZs="; + hash = "sha256-I4kWToLixfkP0XaONiWqNLXXmz+3n+LafbITfZOxLZw="; }; - # fix build with macOS 11 SDK - patches = [ - (fetchpatch { - url = "https://salsa.debian.org/rousseau/PCSC/-/commit/f41fdaaf7c82bc270af6d7439c6da037bf149be8.patch"; - revert = true; - hash = "sha256-8A76JfYqcILi52X9l/uIpJXeRJDf2dkrNEToOsxGZXk="; - }) - ]; - mesonFlags = [ (lib.mesonOption "sysconfdir" "/etc") # The OS should care on preparing the drivers into this location @@ -71,12 +61,20 @@ stdenv.mkDerivation (finalAttrs: { postPatch = '' substituteInPlace src/libredirect.c src/spy/libpcscspy.c \ --replace-fail "libpcsclite_real.so.1" "$lib/lib/libpcsclite_real.so.1" + substituteInPlace meson.build \ + --replace-fail \ + "install_dir : get_option('sysconfdir') / 'default'" \ + "install_dir : '${placeholder "out"}/etc/default'" '' + lib.optionalString systemdSupport '' substituteInPlace meson.build \ --replace-fail \ - "systemdsystemunitdir = systemd.get_variable(pkgconfig : 'systemd' + unit + 'unitdir')" \ + "systemdsystemunitdir = systemd.get_variable(pkgconfig : 'systemd' + systemdunit + 'unitdir')" \ "systemdsystemunitdir = '${placeholder "out"}/lib/systemd/system'" + substituteInPlace meson.build \ + --replace-fail \ + "sysusersdir = systemd.get_variable(pkgconfig : 'sysusersdir')" \ + "sysusersdir = '${placeholder "out"}/lib/sysusers.d'" '' + lib.optionalString polkitSupport '' substituteInPlace meson.build \