1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
# uses the psf/black style
import sys
from inspect import cleandoc
if __name__ == "__main__":
# This script is meant to be executed from this directory or the project root.
# We use that assumption to make Python pick the local liquidctl modules,
# instead other versions that may be installed on the environment/system.
sys.path = ["../..", ""] + sys.path
# The 71-liquidctl.rules file must always be written in UTF-8. Therefore,
# when we detect that stdout has been redirected to a file, make sure that
# it is set to UTF-8, regardless of the current locale.
if not sys.stdout.isatty() and sys.stdout.encoding != "utf-8":
print("liquidctl udev rules files must use UTF-8, reconfiguring stdout", file=sys.stderr)
sys.stdout.reconfigure(encoding="utf-8")
from liquidctl.driver.base import find_all_subclasses
from liquidctl.driver.nvidia import _NvidiaI2CDriver
from liquidctl.driver.usb import BaseUsbDriver
HEADER = """
# Rules that grant unprivileged access to devices supported by liquidctl.
#
# Users and distros are encouraged to use these if they want liquidctl to work
# without requiring root privileges (e.g. without the use of `sudo`).
#
# In the case of I²C/SMBus devices, these rules also cause the loading of the
# `i2c-dev` kernel module. This module is required for access to I²C/SMBus
# devices from userspace, and manually loading kernel modules is in itself a
# privileged operation.
#
# Distros will likely want to place this file in `/usr/lib/udev/rules.d/`, while
# users installing this manually SHOULD use `/etc/udev/rules.d/` instead.
#
# The suggested name for this file is `71-liquidctl.rules`. Whatever name is
# used, it MUST lexically appear before 73-seat-late.rules. The suggested name
# was chosen so that it is also lexically after systemd-provided 70-uaccess.rules.
#
# Once installed, reload and trigger the new rules with:
#
# # udevadm control --reload
# # udevadm trigger
#
# Note that this will not change the mode of `/dev/hidraw*` devices that have
# already been created. In practice, this means that HIDs may continue to require
# privileged access until, either, they are rebound to their kernel drivers, or
# the system is rebooted.
#
# These rules assume a system with modern versions of systemd/udev, that support
# the `uaccess` tag. On older systems the rules can be changed to instead set
# GROUP="plugdev" and MODE="0660"; other groups and modes may also be used.
#
# The use of the `uaccess` mechanism assumes that only physical sessions (or
# "seats") need unprivileged access to the devices.[^1][^2] In case headless
# sessions are also expected to interactively run liquidctl, GROUP and MODE should
# also be set, as a fallback.
#
# Finally, this file was automatically generated. To update it, from a Linux
# shell and the current directory, execute:
#
# $ python generate-uaccess-udev-rules.py > 71-liquidctl.rules
#
# [^1]: https://github.com/systemd/systemd/issues/4288
# [^2]: https://wiki.archlinux.org/title/Users_and_groups#Pre-systemd_groups
"""
MANUAL_RULES = r"""
# Section: special cases
# Host SMBus on Intel mainstream/HEDT platforms
KERNEL=="i2c-*", DRIVERS=="i801_smbus", TAG+="uaccess", \
RUN{builtin}="kmod load i2c-dev"
"""
print(cleandoc(HEADER))
print()
print()
print(cleandoc(MANUAL_RULES))
print()
print()
print(f"# Section: NVIDIA graphics cards")
nvidia_devs = {}
for driver in find_all_subclasses(_NvidiaI2CDriver):
for did, sdid, description in driver._MATCHES:
ids = (driver._VENDOR, did, sdid)
if ids in nvidia_devs:
nvidia_devs[ids].append(description)
nvidia_devs[ids].sort()
else:
nvidia_devs[ids] = [description]
nvidia_devs = [
(svid, did, sdid, description) for (svid, did, sdid), description in nvidia_devs.items()
]
nvidia_devs.sort(key=lambda x: x[3][0])
for svid, did, sdid, descriptions in nvidia_devs:
print()
for desc in descriptions:
print(f"# {desc}")
entry = f"""
KERNEL=="i2c-*", ATTR{{name}}=="NVIDIA i2c adapter 1 *", ATTRS{{vendor}}=="0x10de", \\
ATTRS{{device}}=="{did:#06x}", ATTRS{{subsystem_vendor}}=="{svid:#06x}", \\
ATTRS{{subsystem_device}}=="{sdid:#06x}", DRIVERS=="nvidia", TAG+="uaccess", \\
RUN{{builtin}}="kmod load i2c-dev"
"""
print(cleandoc(entry))
print()
print()
print(f"# Section: USB devices and USB HIDs")
usb_devs = {}
for driver in find_all_subclasses(BaseUsbDriver):
for vid, pid, description, _ in driver._MATCHES:
ids = (vid, pid)
if ids in usb_devs:
usb_devs[ids].append(description)
usb_devs[ids].sort()
else:
usb_devs[ids] = [description]
usb_devs = [(vid, pid, description) for (vid, pid), description in usb_devs.items()]
usb_devs.sort(key=lambda x: x[2][0])
for vid, pid, descriptions in usb_devs:
print()
for desc in descriptions:
print(f"# {desc}")
print(
f'SUBSYSTEMS=="usb", ATTRS{{idVendor}}=="{vid:04x}", ATTRS{{idProduct}}=="{pid:04x}", TAG+="uaccess"'
)
|