File: generate-uaccess-udev-rules.py

package info (click to toggle)
liquidctl 1.16.0-1
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 3,452 kB
  • sloc: python: 15,304; sh: 712; xml: 84; makefile: 4
file content (139 lines) | stat: -rwxr-xr-x 5,565 bytes parent folder | download | duplicates (2)
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"'
        )