0

I want to execute a script on connecting a specific keyboard. Here's my attempt and the data I've gathered.

After cooking it down, this is the minimal problem I am still having:

/etc/udev/rules.d/99-kinesis-testing-high-number.rules:

ACTION=="add", RUN+="/bin/sh -c '/bin/echo UDEV > /tmp/layout_kinesis.log'"

Reload and trigger:

 sudo udevadm control --reload-rules && sudo udevadm trigger

Nothing happens in the file /tmp/layout_kinesis.log upon connecting a USB device.

What is wrong with this minimal rule?

Ubuntu 20.04.


This is from /var/log/syslog:

Mar 30 08:24:25 ionian mtp-probe: checking bus 2, device 96: "/sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.1/2-1.1.2"
Mar 30 08:24:25 ionian mtp-probe: bus: 2, device: 96 was not an MTP device
Mar 30 08:24:25 ionian systemd-udevd[59515]: 2-1.1.2: /etc/udev/rules.d/99-kinesis-testing-high-number.rules:1 Failed to write ATTR{/sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.1/2-1.1.2/idProduct}, ignoring: Permission denied
Mar 30 08:24:25 ionian systemd-udevd[59515]: 2-1.1.2: Process '/bin/sh -c '/bin/echo UDEV > /tmp/layout_kinesis.log'' failed with exit code 2.
Mar 30 08:24:25 ionian systemd-udevd[59515]: 2-1.1.2:1.0: /etc/udev/rules.d/99-kinesis-testing-high-number.rules:1 Failed to write ATTR{/sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.1/2-1.1.2/2-1.1.2:1.0/idProduct}, ignoring: No such file or directory
Mar 30 08:24:25 ionian systemd-udevd[59513]: 2-1.1.2:1.1: /etc/udev/rules.d/99-kinesis-testing-high-number.rules:1 Failed to write ATTR{/sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.1/2-1.1.2/2-1.1.2:1.1/idProduct}, ignoring: No such file or directory
Mar 30 08:24:25 ionian systemd-udevd[59515]: 2-1.1.2:1.0: Process '/bin/sh -c '/bin/echo UDEV > /tmp/layout_kinesis.log'' failed with exit code 2.
Mar 30 08:24:25 ionian systemd-udevd[59513]: 2-1.1.2:1.1: Process '/bin/sh -c '/bin/echo UDEV > /tmp/layout_kinesis.log'' failed with exit code 2.

The rule seems to trigger (although I'm not sure why it pops up three times). I've found a number of issues online about read-only file systems for udev.

All these links seem to have trouble with the file system mounted as read-only for udev. But I'm not sure it's the case for me too:

$ ps aux|grep udev
root       61004  0.0  0.0  23816  7416 ?        Ss   09:00   0:00 /lib/systemd/systemd-udevd
$ less /proc/61004/mounts
/dev/sda1 / ext4 rw,relatime,errors=remount-ro 0 0

It says rw, but it also says errors=remount-ro...


Below are details of how I got there:


lsusb gives me Bus and Device (002 and 048):

$ lsusb
Bus 002 Device 008: ID 046d:c52b Logitech, Inc. Unifying Receiver
Bus 002 Device 007: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode)
Bus 002 Device 010: ID 045e:0b12 Microsoft Corp. 
Bus 002 Device 048: ID 05f3:0007 PI Engineering, Inc. Kinesis Advantage PRO MPC/USB Keyboard
Bus 002 Device 047: ID 05f3:0081 PI Engineering, Inc. Kinesis Integrated Hub

From there, udevadm gives me attributes:

$ sudo udevadm info --attribute-walk /dev/bus/usb/002/048

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.1/2-1.1.2':
    KERNEL=="2-1.1.2"
    SUBSYSTEM=="usb"
    DRIVER=="usb"
    ATTR{devpath}=="1.1.2"
    ATTR{bcdDevice}=="0320"
    ATTR{devnum}=="48"
    ATTR{bMaxPower}=="64mA"
    ATTR{bMaxPacketSize0}=="8"
    ATTR{quirks}=="0x0"
    ATTR{tx_lanes}=="1"
    ATTR{bNumInterfaces}==" 2"
    ATTR{idProduct}=="0007"
    ATTR{speed}=="12"
    ATTR{bConfigurationValue}=="1"
    ATTR{configuration}==""
    ATTR{busnum}=="2"
    ATTR{bDeviceClass}=="00"
    ATTR{maxchild}=="0"
    ATTR{bNumConfigurations}=="1"
    ATTR{idVendor}=="05f3"
    ATTR{ltm_capable}=="no"
    ATTR{bDeviceSubClass}=="00"
    ATTR{avoid_reset_quirk}=="0"
    ATTR{bmAttributes}=="a0"
    ATTR{urbnum}=="427"
    ATTR{bDeviceProtocol}=="00"
    ATTR{removable}=="fixed"
    ATTR{version}==" 1.10"
    ATTR{rx_lanes}=="1"
    ATTR{authorized}=="1"

  looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.1':
    KERNELS=="2-1.1"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{authorized}=="1"
    ATTRS{devnum}=="77"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{bcdDevice}=="0320"
    ATTRS{idVendor}=="05f3"
    ATTRS{urbnum}=="31"
    ATTRS{manufacturer}=="PI Engineering"
    ATTRS{devpath}=="1.1"
    ATTRS{bmAttributes}=="a0"
    ATTRS{bMaxPower}=="50mA"
    ATTRS{idProduct}=="0081"
    ATTRS{ltm_capable}=="no"
    ATTRS{configuration}==""
    ATTRS{bConfigurationValue}=="1"
    ATTRS{maxchild}=="4"
    ATTRS{bNumInterfaces}==" 1"
    ATTRS{tx_lanes}=="1"
    ATTRS{version}==" 1.10"
    ATTRS{busnum}=="2"
    ATTRS{rx_lanes}=="1"
    ATTRS{speed}=="12"
    ATTRS{bMaxPacketSize0}=="8"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{bDeviceClass}=="09"
    ATTRS{bDeviceProtocol}=="00"
    ATTRS{removable}=="unknown"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{product}=="Kinesis Keyboard Hub"
    ATTRS{quirks}=="0x0"
...

And so, my resulting udev rule in /etc/udev/rules.c/90-kinesis-advantage.rules:

SUBSYSTEM=="usb", ACTION=="add", ATTR{idVendor}=="05f3", ATTR{idProduct}=="0007", RUN+="/bin/echo aaa >> /tmp/layout_kinesis.log"

Then reload the rules:

$ sudo udevadm control --reload-rules && sudo udevadm trigger

And monitor the output file:

$ tail -f /tmp/layout_kinesis.log

But disconnecting and reconnecting the keyboard gives no reaction.


I've tried matching the parent as well, but got no luck:

SUBSYSTEMS=="usb", ACTION=="add", ATTRS{idVendor}=="05f3", RUN+="/bin/sh -c 'echo aaa >> /tmp/layout_kinesis.log'"

Also tried to remove sh out and tried this, but nope: RUN+="/bin/echo aaa >> /tmp/layout_kinesis.log"


The last add entry in udevadm monitor -e is:

UDEV  [51347.305368] add      /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.1/2-1.1.2/2-1.1.2:1.1/0003:05F3:0007.003C/input/input112/event6 (input)
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.1/2-1.1.2/2-1.1.2:1.1/0003:05F3:0007.003C/input/input112/event6
SUBSYSTEM=input
DEVNAME=/dev/input/event6
SEQNUM=26402
USEC_INITIALIZED=51347305171
ID_INPUT=1
ID_INPUT_KEY=1
ID_VENDOR=05f3
ID_VENDOR_ENC=05f3
ID_VENDOR_ID=05f3
ID_MODEL=0007
ID_MODEL_ENC=0007
ID_MODEL_ID=0007
ID_REVISION=0320
ID_SERIAL=05f3_0007
ID_TYPE=hid
ID_BUS=usb
ID_USB_INTERFACES=:030101:030000:
ID_USB_INTERFACE_NUM=01
ID_USB_DRIVER=usbhid
ID_PATH=pci-0000:00:1d.0-usb-0:1.1.2:1.1
ID_PATH_TAG=pci-0000_00_1d_0-usb-0_1_1_2_1_1
XKBMODEL=pc105
XKBLAYOUT=us
XKBVARIANT=
XKBOPTIONS=
BACKSPACE=guess
LIBINPUT_DEVICE_GROUP=3/5f3/7:usb-0000:00:1d.0-1.1
MAJOR=13
MINOR=70
DEVLINKS=/dev/input/by-path/pci-0000:00:1d.0-usb-0:1.1.2:1.1-event /dev/input/by-id/usb-05f3_0007-event-if01
TAGS=:power-switch:

So I tried this, because of some recollection I had:

ACTION=="add", ENV{ID_SERIAL}=="05f3_0007", RUN+="/bin/echo aaa >> /tmp/layout_kinesis.log"

Still nothing.


udevadm test shows that run is populated, at least (this output with the ENV{ID_SERIAL} version just above):

$ udevadm test $(udevadm info -q path -n /dev/bus/usb/002/062)
# ... the end here:
Reading rules file: /etc/udev/rules.d/90-kinesis-advantage.rules
Reading rules file: /lib/udev/rules.d/90-libgpod.rules
Reading rules file: /lib/udev/rules.d/90-libinput-fuzz-override.rules
Reading rules file: /lib/udev/rules.d/90-nm-thunderbolt.rules
Reading rules file: /lib/udev/rules.d/90-pulseaudio.rules
Reading rules file: /lib/udev/rules.d/95-cd-devices.rules
Reading rules file: /lib/udev/rules.d/95-dm-notify.rules
Reading rules file: /lib/udev/rules.d/95-upower-csr.rules
Reading rules file: /lib/udev/rules.d/95-upower-hid.rules
Reading rules file: /lib/udev/rules.d/95-upower-hidpp.rules
Reading rules file: /lib/udev/rules.d/95-upower-wup.rules
Reading rules file: /lib/udev/rules.d/96-e2scrub.rules
Reading rules file: /lib/udev/rules.d/97-hid2hci.rules
Reading rules file: /lib/udev/rules.d/99-systemd.rules
Invalid inotify descriptor.
DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.1/2-1.1.2
DEVNAME=/dev/bus/usb/002/062
DEVTYPE=usb_device
DRIVER=usb
PRODUCT=5f3/7/320
TYPE=0/0/0
BUSNUM=002
DEVNUM=062
MAJOR=189
MINOR=189
ACTION=add
SUBSYSTEM=usb
ID_VENDOR=05f3
ID_VENDOR_ENC=05f3
ID_VENDOR_ID=05f3
ID_MODEL=0007
ID_MODEL_ENC=0007
ID_MODEL_ID=0007
ID_REVISION=0320
ID_SERIAL=05f3_0007
ID_BUS=usb
ID_USB_INTERFACES=:030101:030000:
ID_VENDOR_FROM_DATABASE=PI Engineering, Inc.
ID_MODEL_FROM_DATABASE=Kinesis Advantage PRO MPC/USB Keyboard
ID_PATH=pci-0000:00:1d.0-usb-0:1.1.2
ID_PATH_TAG=pci-0000_00_1d_0-usb-0_1_1_2
USEC_INITIALIZED=51523126321
run: '/bin/echo aaa >> /tmp/layout_kinesis.log'
Unload module index
Unloaded link configuration context.

I've also tried to combine attributes of the device and attributes of one parent:

SUBSYSTEMS=="usb", ATTRS{idVendor}=="05f3", ATTR{idProduct}="0007", ACTION=="add", RUN+="/bin/sh -c '/bin/echo UDEV > tmp/layout_kinesis.log'"

This is still not working.


About sandboxing:

$ systemctl cat systemd-udevd.service
# /lib/systemd/system/systemd-udevd.service
#  SPDX-License-Identifier: LGPL-2.1+
#
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=udev Kernel Device Manager
Documentation=man:systemd-udevd.service(8) man:udev(7)
DefaultDependencies=no
After=systemd-sysusers.service systemd-hwdb-update.service
Before=sysinit.target
ConditionPathIsReadWrite=/sys

[Service]
Type=notify
# Note that udev also adjusts the OOM score internally and will reset the value internally for its workers
OOMScoreAdjust=-1000
Sockets=systemd-udevd-control.socket systemd-udevd-kernel.socket
Restart=always
RestartSec=0
ExecStart=/lib/systemd/systemd-udevd
ExecReload=udevadm control --reload --timeout 0
KillMode=mixed
TasksMax=infinity
PrivateMounts=yes
ProtectHostname=yes
MemoryDenyWriteExecute=yes
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
RestrictRealtime=yes
RestrictSUIDSGID=yes
LockPersonality=yes
IPAddressDeny=any
WatchdogSec=3min
0

On modern systems, the udev RUN key has a few caveats associated with it.

The first is that you should not use it to start any long-running processes, because it may block the handling of other udev events for the same device or any device that depends on it.

The second caveat is that the systemd-udevd.service is sandboxed by default on modern distributions: see systemctl cat systemd-udevd.service and the SANDBOXING chapter on man 5 systemd.exec to see the exact sandboxing options applied in your distribution. But the end result is that udev's temporary files may be hidden away in its own namespace, which might explain why you won't see them.

2
  • Interesting. Right now I'm just doing a short echo to a file in /tmp/, so not long-running. Sandboxing is an idea, there's a lot to read! I've added my systemd-udevd.service to the end of my question. But /tmp/ would typically not be restricted, right? I've found quite the number of issues related to udev not able to write to the file system, see added links in my question. The hypothesis is that systemd starts udev before remounting the file system as writable. If that's the problem, it's been an issue for several years??
    – Gauthier
    Mar 30 at 6:42
  • Although their solution of restarting udev with sudo /etc/init.d/udev restart doesn't seem to work for me.
    – Gauthier
    Mar 30 at 6:44
0

I still can't get the echo to write to /tmp/ in the rule itself, but starting a oneshot systemd service works.

/etc/udev/rules.d/99-kinesis.rules:

SUBSYSTEM=="usb", ATTRS{idVendor}=="05f3", ATTR{idProduct}=="0007", ACTION=="add", TAG+="systemd", ENV{SYSTEMD_WANTS}="kinesis_custom.service"

/etc/sytemd/system/kinesis_custom.service:

[Unit]
Description=Autorun actions for Kinesis keyboard

[Service]
Type=oneshot
ExecStart=sh -c "/bin/echo UDEVSYSTEMD >> /tmp/layout_kinesis.log"
RemainAfterExit=true
StandardOutput=journal

I'm not sure if this is by design, according to the sandboxing that @telcoM mentioned, but I suppose I can work with that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.