Difficulties with monitor hotplug UDEV

I have tried so many rules… the script works via command line and via i3 hotkey… but I cant get ANYTHING to work on the udev event that is being triggered. I have been trying to get this to work for so long… I have tried using other scripts such as ones that just try to echo to a file in /tmp to see if it was my script but those dont work either. I can even see the correct rule being printed to the 99-local.rules file as its supposed to be…

It just needs to run the script whenever the monitor is plugged in/unplugged. Any other method than udev of achieving this same effect without polling would be ok too.

link to my configuration with the rules ive tried and how I am creating it, and then the udev monitor event below

KERNEL[43765.651020] change   /devices/pci0000:00/0000:00:02.0/drm/card0 (drm)
ACTION=change
DEVPATH=/devices/pci0000:00/0000:00:02.0/drm/card0
SUBSYSTEM=drm
HOTPLUG=1
DEVNAME=/dev/dri/card0
DEVTYPE=drm_minor
SEQNUM=6945
MAJOR=226
MINOR=0

UDEV  [43765.656213] change   /devices/pci0000:00/0000:00:02.0/drm/card0 (drm)
ACTION=change
DEVPATH=/devices/pci0000:00/0000:00:02.0/drm/card0
SUBSYSTEM=drm
HOTPLUG=1
DEVNAME=/dev/dri/card0
DEVTYPE=drm_minor
SEQNUM=6945
USEC_INITIALIZED=5614610
PATH=/nix/store/a3d7il4hlfw3vij5nl3rplbmrf6l316r-udev-path/bin:/nix/store/a3d7il4hlfw3vij5nl3rplbmrf6l316r-udev-path/sbin
ID_PATH=pci-0000:00:02.0
ID_PATH_TAG=pci-0000_00_02_0
ID_FOR_SEAT=drm-pci-0000_00_02_0
DMI_VENDOR=ASUSTeK COMPUTER INC.
DMI_FAMILY=TUF GAMING
DISPLAY=:0
XAUTHORITY=/home/birdee/.Xauthority
MAJOR=226
MINOR=0
DEVLINKS=/dev/dri/by-path/pci-0000:00:02.0-card
TAGS=:master-of-seat:seat:uaccess:
CURRENT_TAGS=:master-of-seat:seat:uaccess:

This answer may help: linux - How can I check if a udev rule fired? - Super User. Try enabling debug logging as it suggests.

Look at the systemd journal carefully after reloading udev rules. There might be an error message which is relevant – e.g. something else in 99-local.rules which messes up your rule.

This looks more like a udev problem than a NixOS problem.

Try putting your rule in /run/udev/rules.d/99-birdee-testing.rules, then udevadm control --reload. Get something working in isolation before involving NixOS.

Searching these forums will help.

Jan 20 23:02:02 nestOS (udev-worker)[313975]: card0: Running command "/nix/store/yxbq8x7anwd2vxgy217f7r4b15smx8mh-randrMemory.sh"
Jan 20 23:02:02 nestOS (udev-worker)[313975]: card0: Starting '/nix/store/yxbq8x7anwd2vxgy217f7r4b15smx8mh-randrMemory.sh'
Jan 20 23:02:02 nestOS (udev-worker)[313975]: Successfully forked off '(spawn)' as PID 314013.
Jan 20 23:02:02 nestOS (udev-worker)[313975]: card0: '/nix/store/yxbq8x7anwd2vxgy217f7r4b15smx8mh-randrMemory.sh'(err) '/usr/bin/env: 'bash': No such file or directory'
Jan 20 23:02:02 nestOS (udev-worker)[313975]: card0: Process '/nix/store/yxbq8x7anwd2vxgy217f7r4b15smx8mh-randrMemory.sh' failed with exit code 127.
Jan 20 23:02:02 nestOS (udev-worker)[313975]: card0: Command "/nix/store/yxbq8x7anwd2vxgy217f7r4b15smx8mh-randrMemory.sh" returned 127 (error), ignoring.

Lmaoooo it might be my shebang XD

I am unsure as to why it couldnt find awk or i3-msg?

Jan 20 23:32:13 nestOS (udev-worker)[607839]: card0: '/nix/store/w5m2bhyr9s4x3aw4jg7r8q544jcjsg27-randrMemory.sh'(err) '/nix/store/w5m2bhyr9s4x3aw4jg7r8q544jcjsg27-randrMemory.sh: line 67: i3-msg: command not found'
Jan 20 23:32:13 nestOS (udev-worker)[607839]: card0: '/nix/store/w5m2bhyr9s4x3aw4jg7r8q544jcjsg27-randrMemory.sh'(err) '/nix/store/w5m2bhyr9s4x3aw4jg7r8q544jcjsg27-randrMemory.sh: line 68: awk: command not found'
Jan 20 23:32:13 nestOS (udev-worker)[607839]: card0: '/nix/store/w5m2bhyr9s4x3aw4jg7r8q544jcjsg27-randrMemory.sh'(err) '/usr/bin/env: 'bash': No such file or directory'
Jan 20 23:32:13 nestOS (udev-worker)[607839]: card0: '/nix/store/w5m2bhyr9s4x3aw4jg7r8q544jcjsg27-randrMemory.sh'(err) '/usr/bin/env: 'bash': No such file or directory'
Jan 20 23:32:13 nestOS (udev-worker)[607839]: card0: '/nix/store/w5m2bhyr9s4x3aw4jg7r8q544jcjsg27-randrMemory.sh'(err) '/nix/store/w5m2bhyr9s4x3aw4jg7r8q544jcjsg27-randrMemory.sh: line 70: awk: command not found'
Jan 20 23:32:13 nestOS (udev-worker)[607839]: card0: '/nix/store/w5m2bhyr9s4x3aw4jg7r8q544jcjsg27-randrMemory.sh'(err) '/usr/bin/env: 'bash': No such file or directory'
Jan 20 23:32:13 nestOS (udev-worker)[607839]: card0: '/nix/store/w5m2bhyr9s4x3aw4jg7r8q544jcjsg27-randrMemory.sh'(err) '/usr/bin/env: 'bash': No such file or directory'
Jan 20 23:32:13 nestOS (udev-worker)[607839]: card0: '/nix/store/w5m2bhyr9s4x3aw4jg7r8q544jcjsg27-randrMemory.sh'(err) '/usr/bin/env: 'bash': No such file or directory'
Jan 20 23:32:13 nestOS (udev-worker)[607839]: card0: '/nix/store/w5m2bhyr9s4x3aw4jg7r8q544jcjsg27-randrMemory.sh'(err) '/usr/bin/env: 'bash': No such file or directory'
Jan 20 23:32:13 nestOS (udev-worker)[607839]: card0: '/nix/store/w5m2bhyr9s4x3aw4jg7r8q544jcjsg27-randrMemory.sh'(err) '/nix/store/w5m2bhyr9s4x3aw4jg7r8q544jcjsg27-randrMemory.sh: line 142: /tmp/monwkspc.json: Permission denied'
Jan 20 23:32:13 nestOS (udev-worker)[607839]: card0: '/nix/store/w5m2bhyr9s4x3aw4jg7r8q544jcjsg27-randrMemory.sh'(err) '/nix/store/w5m2bhyr9s4x3aw4jg7r8q544jcjsg27-randrMemory.sh: line 146: i3-msg: command not found'
Jan 20 23:32:13 nestOS (udev-worker)[607839]: card0: '/nix/store/w5m2bhyr9s4x3aw4jg7r8q544jcjsg27-randrMemory.sh'(err) '/usr/bin/env: 'bash': No such file or directory'
Jan 20 23:32:13 nestOS (udev-worker)[607839]: card0: '/nix/store/w5m2bhyr9s4x3aw4jg7r8q544jcjsg27-randrMemory.sh'(err) '/usr/bin/env: 'bash': No such file or directory'

udev doesn’t give rules access to the same PATH as a normal user. You can use something like pkgs.writeShellScript to make an executable shell script with the right shebang in your nixos config.

HOORAY!! the link gave me enough info to figure it out sorta?

I kinda thought writeShellScript just added executable = “true” permission to pkgs.writeTextFile? I was unaware that it also specified your shebang but it appears that it does.

What I did was this. I specified the shebangs as

#!/usr/bin/env ${pkgs.bash}/bin/bash

Then I made a function that calls all the stuff that journalctl -f with log level at debug told me about.

So for i3-msg for example it became

i3-msg() {
    ${pkgs.i3}/bin/i3-msg "$@"
}

That became the start of my script that I used readfile on, and that way it could find all the commands, as they were now local functions within the script.


This works fine sorta.

However, I do have 1 big issue.

It needs me to put an ENV{XAUTHORITY} = “/home/birdee/.Xauthority” like in the original exerpt and i think theres no way to do it other than in the udev rule itself

It needs that so that it runs as the right user. But I want this to be more dynamic. So I made it map over my set of users and make one for each. I also made it so the caches for the script dont collide.

However it will still run the script for all users, every time it happens, and glitch out and possibly not run the others correctly. I know, for example, if i map over config.users.users instead of my own users set, it runs ALL the rules, glitches out and doesnt run again.

In fact, it seems that if it fails for ANY user, it doesnt run at all, so having multiple users that can trigger this rule does not seem possible…

Can you think of a better way of doing this? Is there some way to check in the script if the current user is the correct one to run it for currently or something? Or at least make it so that errors dont stop it from working for other users?

https://github.com/BirdeeHub/birdeeSystems/blob/main/common/i3/xrandrMemoryi3/default.nix

Currently it looks like this

Its a nice little i3 utility module now. it has a system module that uses a udev rule to echo $RANDOM to a temp file

The rest happens in userland in home manager. Systemd service starts an inotify script that gets called when the file is written to. No polling. Provide 3 simple scripts containing your desired xrandr commands It handles running them and moving your i3 workspaces back from whence they came when you plug the monitor back in.