Conversation
Extend chwd to detect USB devices via sysfs (/sys/bus/usb/devices/) alongside existing PCI detection. Add profile infrastructure for USB device types with separate config/database directories. Include an fprint profile that matches fingerprint sensor vendor IDs from all manufacturers supported by libfprint (Goodix, Synaptics, Elan, Validity, AuthenTec, FPC, etc.) and installs fprintd. Signed-off-by: Peter Jung <admin@ptr1337.dev>
Signed-off-by: Peter Jung <admin@ptr1337.dev>
Signed-off-by: Peter Jung <admin@ptr1337.dev>
This reverts commit 4bd43d3.
There was a problem hiding this comment.
Pull request overview
This PR extends chwd’s hardware detection and profile infrastructure to support USB devices (via sysfs) alongside existing PCI detection, and introduces an initial USB profile for fingerprint readers (fprintd).
Changes:
- Add USB device discovery from
/sys/bus/usb/devicesand include USB devices in profile availability/autoconfigure flows. - Split profile storage into PCI vs USB config/database directories and update listing/install logic accordingly.
- Add an initial USB
fprintprofile and update English i18n strings for new/renamed list headings/messages.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/profile.rs | Include both PCI and USB devices when collecting available profiles. |
| src/misc.rs | Document that USB directories are optional in environment checks. |
| src/main.rs | Use combined installed profiles/devices; choose DB dir based on profile location. |
| src/device_misc.rs | Parameterize detailed listing by bus type (PCI/USB). |
| src/data.rs | Add USB device discovery; split PCI/USB installed/all profiles; make profile-dir reading tolerant of missing dirs. |
| src/consts.rs | Add USB config/database directory constants. |
| src/console_writer.rs | List PCI+USB profiles/devices and adjust message IDs. |
| profiles/usb/fprint/profiles.toml | Add USB fingerprint reader profile that installs/enables fprintd and writes PAM sudo config. |
| i18n/en/chwd.ftl | Add/update English strings for USB and generalized “installed profiles” messaging. |
Comments suppressed due to low confidence (2)
src/data.rs:156
- In fill_profiles(), read_dir errors are now silently ignored (return), which can mask real problems (e.g., permission/IO errors) and lead to “no profiles found” without any diagnostic. Also, iterating
for entry in dir_entriesand then callingentry.as_ref().unwrap()can still panic on per-entry errors. Consider only treatingErrorKind::NotFoundas optional (especially for USB), logging/propagating other errors, and iterating withfilter_map(Result::ok)(or handling Err explicitly) to avoid unwrap panics.
let dir_entries = match fs::read_dir(conf_path) {
Ok(entries) => entries,
Err(_) => return,
};
for entry in dir_entries {
let config_file_path = format!(
"{}/{}",
entry.as_ref().unwrap().path().as_os_str().to_str().unwrap(),
crate::consts::CHWD_CONFIG_FILE
src/device_misc.rs:38
- The detailed listing logs
device.sysfs_id, butsysfs_idis never populated in the codebase (devices are constructed withsysfs_id: String::new()), so the identifier printed here will be empty for both PCI and USB. Usedevice.sysfs_busid(or populate sysfs_id during device discovery) so--detailoutput shows a meaningful device identifier.
log::info!(
"{} {}: {} ({}:{}:{})",
bus_type,
fl!("device"),
device.sysfs_id,
device.class_id,
device.vendor_id,
device.device_id
);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
profiles/usb/fprint/profiles.toml
Outdated
| vendor_ids = "045e 0483 04f3 05ba 06cb 08ff 0bda 10a5 138a 147e 1c7a 2541 27c6 2808 298d" | ||
| device_ids = "*" | ||
| class_ids = "*" | ||
| priority = 5 | ||
| packages = "fprintd" | ||
| post_install = """ | ||
| systemctl enable fprintd.service | ||
| mkdir -p /etc/pam.d/sudo.d | ||
| echo 'auth sufficient pam_fprintd.so' > /etc/pam.d/sudo.d/50-fprintd.conf |
There was a problem hiding this comment.
This profile matches only on vendor_ids with device_ids = "*"/class_ids = "*", so it will match any USB device from these vendors (e.g., many Realtek/Microsoft devices) even when no fingerprint reader is present. Because the profile installs fprintd and modifies PAM, false positives here have significant side effects. Please narrow the match criteria (e.g., vendor+product ID pairs from the supported-devices list, or a stricter device_name_pattern/class constraint) instead of wildcarding device_ids.
| fn db_dir_for_profile(profile: &Profile) -> &'static str { | ||
| if profile.prof_path.contains("/usb/") { | ||
| consts::CHWD_USB_DATABASE_DIR | ||
| } else { | ||
| consts::CHWD_PCI_DATABASE_DIR | ||
| } | ||
| } |
There was a problem hiding this comment.
db_dir_for_profile() infers whether a profile is USB by checking whether prof_path contains the substring "/usb/". This is brittle (depends on filesystem layout and path formatting) and can misroute installs if paths change. Prefer deriving the target DB dir via Path prefix checks against the known config roots, or storing an explicit bus/type on Profile (or alongside it) when profiles are loaded.
| invalid-profile = profile '{$invalid_profile}' is invalid! | ||
| all-pci-profiles = All PCI profiles: | ||
| installed-pci-profiles = Installed PCI profiles: | ||
| all-usb-profiles = All USB profiles: | ||
| installed-profiles = Installed profiles: | ||
| pci-profiles-not-found = No PCI profiles found! | ||
| no-installed-pci-profiles = No installed PCI profiles! | ||
| no-installed-profile-device = no installed profile for PCI devices found! | ||
| usb-profiles-not-found = No USB profiles found! | ||
| no-installed-profiles = No installed profiles! | ||
| no-installed-profile-device = no installed profile for devices found! |
There was a problem hiding this comment.
New/renamed Fluent message IDs were introduced here (e.g., installed-profiles, no-installed-profiles, all-usb-profiles, usb-profiles-not-found). Other locale files under i18n/* still only define the old PCI-specific keys, so non-English locales will fall back to English for these messages. Consider updating other translations (or adding backward-compatible aliases) to keep localization coverage complete.
Replace wildcard device_ids with explicit libfprint-supported product IDs (~190 entries in ids/fprint.ids) to prevent false matches on non-fingerprint USB hardware sharing a vendor ID. Add chwd ownership marker to the PAM drop-in so post_remove only removes files it created. Fix fill_profiles() to only ignore NotFound errors instead of silently swallowing all IO failures. Signed-off-by: Peter Jung <admin@ptr1337.dev>
Signed-off-by: Peter Jung <admin@ptr1337.dev>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 19 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (2)
src/data.rs:160
fill_profiles()iteratesfor entry in dir_entriesbut then callsentry.as_ref().unwrap(). A single unreadable directory entry will panic and abort profile loading. Prefer iterating withfor entry in dir_entries.filter_map(Result::ok)(optionally logging skipped entries) to make profile discovery robust.
for entry in dir_entries {
let config_file_path = format!(
"{}/{}",
entry.as_ref().unwrap().path().as_os_str().to_str().unwrap(),
crate::consts::CHWD_CONFIG_FILE
src/device_misc.rs:38
- The detailed device log prints
device.sysfs_id, butsysfs_idis never populated for either PCI or USB devices (it’s initialized as empty). This makes the output ambiguous—especially for USB wheresysfs_busid(e.g.1-2) is the meaningful identifier. Consider printingsysfs_busidhere (or populatingsysfs_id).
log::info!(
"{} {}: {} ({}:{}:{})",
bus_type,
fl!("device"),
device.sysfs_id,
device.class_id,
device.vendor_id,
device.device_id
);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| self.installed_pci_profiles | ||
| .iter() | ||
| .chain(self.installed_usb_profiles.iter()) | ||
| .cloned() | ||
| .collect() | ||
| } | ||
|
|
||
| /// Returns combined list of all available profiles (PCI + USB). | ||
| #[must_use] | ||
| pub fn all_profiles(&self) -> Vec<Profile> { | ||
| self.all_pci_profiles.iter().chain(self.all_usb_profiles.iter()).cloned().collect() |
There was a problem hiding this comment.
installed_profiles()/all_profiles() now concatenate the PCI and USB vectors without re-sorting, which can break the previous global priority ordering (previously a single list was sorted by priority). Consider sorting the merged Vec by descending priority (and possibly secondary key like name) before returning to keep behavior stable.
| self.installed_pci_profiles | |
| .iter() | |
| .chain(self.installed_usb_profiles.iter()) | |
| .cloned() | |
| .collect() | |
| } | |
| /// Returns combined list of all available profiles (PCI + USB). | |
| #[must_use] | |
| pub fn all_profiles(&self) -> Vec<Profile> { | |
| self.all_pci_profiles.iter().chain(self.all_usb_profiles.iter()).cloned().collect() | |
| let mut profiles: Vec<Profile> = self | |
| .installed_pci_profiles | |
| .iter() | |
| .chain(self.installed_usb_profiles.iter()) | |
| .cloned() | |
| .collect(); | |
| // Preserve global ordering by sorting by descending priority, | |
| // and then by name as a stable secondary key. | |
| profiles.sort_by(|a, b| { | |
| b.priority | |
| .cmp(&a.priority) | |
| .then_with(|| a.name.cmp(&b.name)) | |
| }); | |
| profiles | |
| } | |
| /// Returns combined list of all available profiles (PCI + USB). | |
| #[must_use] | |
| pub fn all_profiles(&self) -> Vec<Profile> { | |
| let mut profiles: Vec<Profile> = self | |
| .all_pci_profiles | |
| .iter() | |
| .chain(self.all_usb_profiles.iter()) | |
| .cloned() | |
| .collect(); | |
| // Preserve global ordering by sorting by descending priority, | |
| // and then by name as a stable secondary key. | |
| profiles.sort_by(|a, b| { | |
| b.priority | |
| .cmp(&a.priority) | |
| .then_with(|| a.name.cmp(&b.name)) | |
| }); | |
| profiles |
| if grep -q 'Managed by chwd' /etc/pam.d/sudo.d/50-fprintd.conf 2>/dev/null; then | ||
| rm -f /etc/pam.d/sudo.d/50-fprintd.conf | ||
| fi | ||
| if ! systemctl list-dependencies --reverse fprintd.service 2>/dev/null | grep -q .; then |
There was a problem hiding this comment.
The post_remove check for disabling fprintd.service is effectively always false: systemctl list-dependencies --reverse fprintd.service | grep -q . will typically match at least one line (often the unit itself), so the service won’t be disabled even when nothing depends on it. Consider a more precise check (e.g., filter out the unit itself / header lines, or use systemctl is-enabled and disable unconditionally if you enabled it).
| if ! systemctl list-dependencies --reverse fprintd.service 2>/dev/null | grep -q .; then | |
| if systemctl is-enabled --quiet fprintd.service 2>/dev/null; then |
There was a problem hiding this comment.
Guess we dont even need to enable service. It should do automatically
ventureoo
left a comment
There was a problem hiding this comment.
I think design of this code is not good. Instead of storing the profiles for PCI and USB separately, you should just add a type hint inside profile structure.
But let @vnepogodin sort it out.
| } | ||
|
|
||
| fn db_dir_for_profile(profile: &Profile) -> &'static str { | ||
| if profile.prof_path.contains("/usb/") { |
There was a problem hiding this comment.
It's hacky. Why not just add profile type as enum inside the structure?
can merge as-is, and later bc6655e |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Extend chwd to detect USB devices via sysfs (/sys/bus/usb/devices/)
alongside existing PCI detection. Add profile infrastructure for USB
device types with separate config/database directories.
Include an fprint profile that matches fingerprint sensor vendor IDs
from all manufacturers supported by libfprint (Goodix, Synaptics, Elan,
Validity, AuthenTec, FPC, etc.) and installs fprintd.
List is fetched from https://fprint.freedesktop.org/supported-devices.html
Tested on Framework Laptop with supported fingerprint. Works fine.
Polkit implementation I did not got to work, not sure why.
When this is installed/setup on a computer with a fingerprint sensor, but no fingerprint being not set yet it just directly jumps to use password.