You are not logged in.
Goal: When the USB keyboard is being connected, a script should run to change several keyboard settings.
This udev rule successfully runs the script when the keyboard is being connected:
ACTION=="add", ATTRS{idVendor}=="XXXX", ATTRS{idProduct}=="YYYY", RUN+="/home/username/bin/keyboard-udev", OWNER="username"
The wrapper script (as suggested in this superuser thread):
/home/username/bin/keyboard-udev
--------------------------------
#!/bin/bash
# Background process to allow udev to continue
# Sleep to wait for keyboard being activated
(sleep 1 && /home/username/bin/keyboard) &
Sleeping longer has no effect.
The script to change the keyboard settings:
/home/username/bin/keyboard
---------------------------
#!/bin/bash
DISPLAY=":0"
HOME=/home/username
XAUTHORITY=$HOME/.Xauthority
export DISPLAY XAUTHORITY HOME
xset r rate 175 75
numlockx
setxkbmap -layout "de" -variant nodeadkeys
xmodmap $HOME/.Xmodmap
xbindkeys
notify-send "This notification is being shown!"
As you can see, I've checked with a notify-send command that the script is indeed being executed.
If I run this script manually after the keyboard has been connected, it successfully changes everything it is supposed to change.
The successful notify-send command should prove that DISPLAY, HOME and XAUTHORITY are correctly set.
Question: Why does the script work when run manually, but fail when run via udev?
Note that all of this did work some time ago and only broke, maybe, 1 to 6 months ago (not sure when exactly). I assume it broke after some update.
Last edited by Markus00000 (2014-05-16 10:18:18)
Offline
If I run this script manually after the keyboard has been connected, it successfully changes everything it is supposed to change
If I were you I would stick to this manual approach, rather than using udev to act in the upper graphical layer where it is not supposed to go or without warranty of success.
Offline
I connect and disconnect the USB keyboard often, therefore, the manual approach is quite cumbersome. Maybe there is an alternative to udev which is supposed to act in the upper graphical layer?
Offline
Hello,
I run into the same problem:
The background is that the script is run 4 times(!). Here is my
testscript:
#!/bin/bash
# No mixture or confusion due to locale output
export LANG=C
DEBUG=1
if [ $DEBUG -ne 0 ]; then
# redirect all script output to a temporary logfile
exec >/tmp/`basename $0`-$$.log 2>&1
echo "Command line of this script is |$@|"
set -x
echo =======================================
fi
usr=""
while [ -z "$usr" ]; do
usr=`who | awk '/\(:[0-9]\)/ { print $1 }' | sort -u`
sleep 1
done
for i in $usr; do
pid=`ps -u $i | grep startkde | awk '{ print $1 }'`
disp=`cat /proc/$pid/environ | tr '\000' '\n' | sed -ne 's/DISPLAY=//p'`
export DISPLAY=$disp
# set
# sleep 1
# test -f /home/$i/.xmodmap && su $i -c "DISPLAY=$disp xset -q"
date
test -f /home/$i/.xmodmap && su $i -c "DISPLAY=$disp xmodmap -pk | grep 91"
test -f /home/$i/.xmodmap && su $i -c "DISPLAY=$disp /usr/bin/xmodmap /home/$i/.xmodmap"
sleep 1
test -f /home/$i/.xmodmap && su $i -c "DISPLAY=$disp xmodmap -pk | grep 91"
echo Return code is $?
done
Maybe you find some hints. For me the solution will be to add a udev
remove rule removing a temporary file. The settings will only be done
if this file does not exist and is created once the settings are done.
Hth -- Peter
Offline
This no longer works as of 2014-07-28. Updated answer in the post below this one.
Thank you so much for registering and replying!
All I had to do was to modify my script as follows:
/home/username/bin/keyboard-udev
--------------------------------
#!/bin/bash
# Background process to allow udev to continue
# Path to lock file
lock="/tmp/keyboard.lock"
# Lock the file (other atomic alternatives would be "ln" or "mkdir")
exec 9>"$lock"
if ! flock -n 9; then
# Script is already running
exit 1
fi
/home/username/bin/keyboard &
# The lock file will be unlocked when the script ends
Additionally, I removed the one second sleep. The script failed once since but I don't know whether it was caused by the missing delay.
It did work at least 20 times, thank you!
Last edited by Markus00000 (2014-07-28 19:47:56)
Offline
Updated solution
Why? Because the above solution stopped working. It might be the case that Xorg resets some settings after udev ran. See https://bugzilla.redhat.com/show_bug.cgi?id=601853 for more details. As the bug has been reported in 2010, I'm not sure why the above solution worked at all. Anyways, in my case "xset r rate [number] [number]" was overridden among other settings.
Here's the udev rule I'm using which you'll have to adjust to match your keyboard:
/etc/udev/rules.d/99-keyboard.rules
-----------------------------------
# WARNING: Command might be executed multiple times
ACTION=="add", ATTRS{phys}=="usb-0000:00:14.0-?/input0", RUN+="/usr/local/bin/keyboard-udev"
I failed to build a set of udev rules that consistently ran the command only once. With this rule it will be executed 1-3 times. To list possible attributes run:
udevadm info -a -n /dev/input/by-id/<your_keyboard>
Now to the script that udev runs. Only one instance of this script should ever run at any time because of the locked file check. (Though with this new solution the script has never been invoked concurrently on my machine.) Previously, the script that set the keyboard settings could be run directly at this point. Now, the workaround is to write to a temporary file instead. In other words, the temporary file gets written whenever the keyboard is connected and the udev rule consequently applied:
/usr/local/bin/keyboard-udev
----------------------------
#!/bin/bash
# Background process to allow udev to continue
# Path to lock file
lock="/tmp/keyboard.lock"
# Lock the file (other atomic alternatives would be "ln" or "mkdir")
exec 9>"$lock"
if ! flock -n 9; then
notify-send -t 5000 "Keyboard script is already running."
exit 1
fi
#/usr/local/bin/keyboard & # If Xorg would not reset settings after udev finished
echo '' > /tmp/keyboard.lock &
# The lock file will be unlocked when the script ends
The script that applies the keyboard settings does no longer require any environment variables. If you're interested in how many times it's executed on your machine, uncomment the notify-send command. Obviously, you'll want to change these settings and commands to your liking:
/usr/local/bin/keyboard
-----------------------
#!/bin/bash
xset r rate 175 75
numlockx
setxkbmap -layout "de" -variant nodeadkeys
xmodmap ${HOME}/.Xmodmap
(killall xbindkeys || true) &> /dev/null # Continue even if exit status is 1
xbindkeys # Only one instance will run
#notify-send -t 5000 "Keyboard settings applied"
Finally, the keyboard settings are automatically applied when Xorg is started. I assume udev can run much earlier in the boot process and therefore might be executed long before Xorg is running. The settings are also applied whenever the keyboard is plugged in while Xorg is running. That's whenever the keyboard-udev script writes to the temporary file, thereby indicating that the keyboard was connected:
~/.xinitrc
----------
[...]
/usr/local/bin/keyboard &
file-inotify /tmp/keyboard.lock /usr/local/bin/keyboard & # Triggered by udev rule
[...]
As file-notify is reusable I've put it in an extra file:
~/bin/file-inotify
------------------
#!/bin/bash
# Usage: file-inotify <file> <command>
# Command is run when file is written.
path=$(realpath "$1")
job="$2"
#basename=$(basename "$1")
dirname=$(dirname "$1")
inotifywait -m -e close_write --format '%w%f' "$dirname" \
| while read file
do
if [[ $(realpath "$file") == "$path" ]]; then
sh -c "$job"
fi
done
That's all. Except for the cosmetic issue that the script runs multiple times, it's working great so far.
Last edited by Markus00000 (2014-07-29 05:28:18)
Offline