Help with udev rules

hi, im trying to create my first udev rules. i googled a lot but i cant get it to work :frowning:

my approach was:

i created:

testing the rule gave me the following result:

i suspect the last few lines to be relevant, however i dont know what to do with is. there is no “subsystem” folder in /sys/


another thing that i think could be related to my issue is, that ddcutil (even though working as intended) gives me the following error each time i use it. this was not the case with my previous distro, where i set up ddcutil the exact same way, following the ddcutil wiki.

For ddcutil try to load i2c-dev module first

sudo modprobe i2c-dev

For udev rule first try with ATTRS{uniq}=="98:7a:14:3d:18:b8" instead of ATTRS{PHYS}... and add KERNEL=="js0"

If that does not work, then try to get information from actual device not the subsytem (which should be hidraw, anyway, but input may work). I dont have the xbox controller but I guess it will create a device node /dev/input/js0 for the controller itself dont use /sys/subsystem/bluetooth.... as it might change dynamically.

So execute

udevadm info --attribute-walk --path=$(udevadm info --query=path --name=/dev/input/js0)

I suspect RUN+=“~/Scripts/xbox_controller.sh” part is problematic, too. What is the full path to your script? Is it in home folder? Udev is not running in interactive shell and the scripts will need to start with #!/bin/sh it will not run bash and will run as root.

Anyway I would do it like this:

  1. Create a file /etc/udev/rules.d/98-xboxcontroller.rules with the following contents
ACTION=="add", KERNEL=="js0", SUBSYSTEM=="input", ATTRS{uniq}=="98:7a:14:3d:18:b8", RUN+="/usr/local/bin/udev-test-add.sh"
ACTION=="remove", KERNEL=="js0", SUBSYSTEM=="input", ATTRS{uniq}=="98:7a:14:3d:18:b8", RUN+="/usr/local/bin/udev-test-del.sh"
  1. Create my run scripts in /usr/local/bin and chmod +x them with test contents like this for added script:
#!/bin/sh
touch /tmp/added-xbox-$(date +%s)

and removed script

#!/bin/sh
touch /tmp/removed-xbox-$(date +%s)
  1. At last I would test them with
sudo udevadm test $(udevadm info --query=path --name=/dev/input/js0) 2>&1

and with turning controller on and off.

thanks for your help jake!

loading i2c-dev doesnt get rid of the error messages from ddcutil. since its working i dont really care too much though


for my udev rule i tried what you said, however it didnt work. but i DID get it to work addressing the xbox controller battery like this:

the funny thing is ACTION==“remove” works, but ACTION==“add” doesnt. thats why right now it is *, which triggers both events. do you have any idea if maybe for the battery the action isnt add but something else like read, initiate, etc?


also i noticed creating a file on desktop works, but launching programs dont. it doesnt matter if i use bin/bash or bin/sh, “touching” a file works with both but launching (for bin/sh i used “exec command”)

Can you provide the full output of

udevadm info --attribute-walk --path=$(udevadm info --query=path --name=/dev/input/js0)

Thats strange, notice the uniq value in that hid string :smiley: I think there is a better way to specify the device, but whatever works :smiley:

Check the execution order of your rules file. Rename it to be something like 99-xbox.rules
Action could be anything, but you can easily find out what it is; udev will set ACTION env variable when it executes your rule run script. Change your scripts to use env instead of touch

#!/bin/sh
env > /tmp/added-xbox-$(date +%s)

and check the variables in that file.

Yeah, you can not launch GUI stuff like this from udev, even with exec ... & , also udev will kill any running script if it thinks it executes for too long. If you want to launch a GUI program it would be better to signal some other process or use systemd (maybe?)

well i got it working so far, connecting my bluetooth keyboard (actual usecase, xbox controller was just testing) triggers the script

however, the only command that seems to be working is creating a dummy file on my desktop :-/


i dont know much about systemd, i used MX with sysvinit before EOS. i looked into it and it seems my udev rule can start a systemd service that will launch my script without killing it right away? i mean that sounds kinda weird but if it works

not sure how to accomplish yet, if you have any advice i will gladly take it :slight_smile:


i tried running systemctl --user import-environment DISPLAY XAUTHORITY to get rid of the “cant connect to x server” and “cannot open display” errors. to be honest i dont really know what i did there, is this safe?

Can you describe your usecase? What are you trying to do exactly when your bluetooth keyboard is connected? Configure it like you posted in your previous script and show the notification? There are better ways than udev for UI handling. You can monitor Xs’ input events to configure the keyboard and run your scripts.

Inputplug is one popular solution https://github.com/andrewshadura/inputplug

Systemd is the “cleanest” solution, but it can be complicated to setup. Basically udev should signal systemd socket, that will then activate systemd service that will run your script in user context. All other udev solutions are “hacky” and are not secure especially because udev is running as root and in non-interactive shell.

All that said, I will show you a way how to do this from a quick and dirty script with kdialog, even if I don’t recommend doing this. Remember, you are running GUI app as root in user context, it can mess a lot of things up.

Besides DISPLAY and XAUTHORITY env vars, you will need XDG_RUNTIME_DIR and QT_QPA_PLATFORM set if you need any KDE app to run from this context, so your script may look like this:

#!/bin/sh

# use `ps` to find running Xwayland process, it will return something like this
# /usr/bin/Xwayland :1 -auth /run/user/1000/xauth_ifAwgP -listen 57 -listen 58 -displayfd 49 -rootless -wm 52
# on my system, adapt to different process if using X11
#
# there we have DISPLAY, XAUTHORITY, XDG_RUNTIME_DIR in the returned command line    
MY_RUNNING_X_PS="$(ps -A -o cmd= | grep Xwayland | grep -v grep)" 

export DISPLAY="$(echo ${MY_RUNNING_X_PS} | cut -d ' ' -f 2)" # :1 
export XAUTHORITY="$(echo ${MY_RUNNING_X_PS} | cut -d ' ' -f 4)" # /run/user/1000/xauth....
export XDG_RUNTIME_DIR="/run/user/1000" # this can be extracted too, but left as exercise      
export QT_QPA_PLATFORM="wayland" # put xcb here if X11
export WAYLAND_DISPLAY="wayland-0" # don't have/need this env on X11
                                                                                               
env > /tmp/myevent-$(date +%s) && kdialog --passivepopup "custom config loaded" 10 --title "bluetooth keyboard ${ACTION}"

my usecase is the script i posted. the notification is just there to indicate the script has worked, i just need the 2 xinput + setxkbmap commands to configure my bluetooth keyboard. the keyboard goes to sleep after 5 minutes, after waking up all custom settings are forgotten and i need to reset them somehow.

however, i would really like to be able to use udev rules for other usecases as well. for instance start my backup script, whenever i plug in my externel backup drive or start steam, whenever i connect my xbox controller. but the important thing is the bluetooth keyboard.

i dont wanna use a “hacky” solution, especially if it brings security risks! so i got that right, udev runs as root, even if it triggers a systemd service that starts my script? … and thats why its so hard to use udev to start commands that rely on X server?

im down to use any solution thats safe. i will take a look at inputplug, thanks for the suggestion. also thank you for writing the script for me, but as i already said: i care about security and if you advise not to do it that way i wont.

im really willing to learn, thats the fun part about linux.

i would appreciate if you could answer my question at the end of my last post regarding the imported environment variables. is this a permanent change i did to my system and how can i revert it? as much as i want my problem to be solved i want to have a clean and safe system. i did this out of desperation to be honest :confounded:

---- edit ----

how i understand it udev and the program you suggested (inputplug) both are daemons (?), that continiously run in the background and watch for changes. they probably update quite often, to get the information quickly. my existing udev rule can create a dummy file, so why not write my own daemon? something like

Not a big issue, disable/delete the service you created, and thats it, no permanent damage to the system

Yes, more or less. The big difference is that udev is a linux subsystem and operates on lower level than inputplug daemon which “listens” for Xorg input events.

Sure, you can do that, just make sure the “daemon” is running in user context.

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.