Skip to content

Instantly share code, notes, and snippets.

@appleneko2001
Created October 20, 2023 15:51
Show Gist options
  • Select an option

  • Save appleneko2001/3819dfe8b3b39e1cb7d9e2a2cd54ed50 to your computer and use it in GitHub Desktop.

Select an option

Save appleneko2001/3819dfe8b3b39e1cb7d9e2a2cd54ed50 to your computer and use it in GitHub Desktop.
MODDED "my-udev-notify" script. Requires udev rules to make it works. Its a workaround to notify device connected / disconnected to users. Please do refer how to use the script first, then replace the script with this. https://github.com/dimonomid/my-udev-notify
#!/bin/bash
# thanks:
# - to guys from linux.org.ru;
# - to 'iptable' user from ##linux at irc.freenode.net.
# test command:
# sudo /bin/bash my-udev-notify -a add -p 'test_path' -b '555' -d '777'
# get path to this script
DIR="$(dirname $(readlink -f "$0"))"
# set default options {{{
# file for storing list of currently plugged devices
devlist_file="/var/tmp/udev-notify-devices"
show_notifications=true
notification_icons=true
play_sounds=true
use_espeak=false
plug_sound_path="$DIR/sounds/plug_sound.wav"
unplug_sound_path="$DIR/sounds/unplug_sound.wav"
# }}}
# read config file {{{
{
if [ -r /etc/my-udev-notify.conf ]; then
. /etc/my-udev-notify.conf
fi
#if [ -r ~/.my-udev-notify.conf ]; then
#. ~/.my-udev-notify.conf
#fi
}
# }}}
# retrieve options from command line {{{
# action: "add" or "remove"
action=
# dev_path: path like /devices/pci0000:00/0000:00:1d.0/usb5/5-1
dev_path=
# bus number and device number:
# they are needed since device is also stored at /dev/bus/usb/<bus_num>/<dev_num>
bus_num=
dev_num=
while getopts a:p:b:d: opt; do
case $opt in
a)
action=$OPTARG
;;
p)
dev_path=$OPTARG
;;
b)
bus_num=$OPTARG
;;
d)
dev_num=$OPTARG
;;
esac
done
shift $((OPTIND - 1))
# }}}
get_device_icon()
{
local dev_data=`echo "$1" | sed 's/###/\n/g' | grep -e 'bInterfaceClass' -A 2 -m 1 | cut -d ' ' -f 2,3,4,5,6,7,8,9 | tr -s ' ' '_'`
OLD_IFS=$IFS
IFS="
"
local dev_array=($dev_data)
IFS=$OLD_IFS
local class=${dev_array[0]}
local subclass=${dev_array[1]}
local protocol=${dev_array[2]}
case "$class:$subclass:$protocol" in
Audio:* )
dev_icon="audio-card"
;;
Communications:Abstract* )
dev_icon="modem"
;;
Communications:Ethernet* )
dev_icon="network-wired"
;;
Human_Interface_Device:*:Keyboard )
dev_icon="input-keyboard"
;;
Human_Interface_Device:*:Mouse )
dev_icon="input-mouse"
;;
Mass_Storage:RBC:* )
dev_icon="media-removable"
;;
Mass_Storage:Floppy:* )
dev_icon="media-floppy"
;;
Mass_Storage:SCSI:* )
dev_icon="media-removable"
;;
Printer:* )
dev_icon="printer"
;;
Hub:* )
dev_icon="emblem-shared"
;;
Video:* )
dev_icon="camera-web"
;;
Xbox:Controller:* )
dev_icon="input-gaming"
;;
Wireless:Radio_Frequency:Bluetooth )
dev_icon="bluetooth"
;;
*)
dev_icon="dialog-information"
;;
esac
}
show_visual_notification()
{
# TODO: wait for 'iptable' user from ##linux to say how to do it better
# or, at least it's better to use 'who' command instead of 'w',
# because 'who' echoes display number like (:0), and echoes nothing if no display,
# which is more convenient to parse.
local header=$1
local text=$2
if [[ $notification_icons == true ]]; then
get_device_icon "$text"
else
dev_icon=''
fi
text=`echo "$text" | sed 's/###/\n/g'`
declare -a logged_users=($(print_logged_users))
for (( i=0; i<${#logged_users[@]}; i=($i + 1) )); do
cur_user=${logged_users[$i]}
su $cur_user -c "notify-send -i '$dev_icon' '$header' '$text'"
done
}
show_visual_notification_simplified()
{
# TODO: wait for 'iptable' user from ##linux to say how to do it better
# or, at least it's better to use 'who' command instead of 'w',
# because 'who' echoes display number like (:0), and echoes nothing if no display,
# which is more convenient to parse.
local header=$1
local text=$2
local name=$3
if [[ $notification_icons == true ]]; then
get_device_icon "$text"
else
dev_icon=''
fi
declare -a logged_users=($(print_logged_users))
for (( i=0; i<${#logged_users[@]}; i=($i + 1) )); do
cur_user=${logged_users[$i]}
su $cur_user -c "notify-send -a '$header' -i '$dev_icon' '$header' '$name'"
done
}
print_logged_users()
{
who | grep "(.*)" | sed 's/^\s*\(\S\+\).*/\1/g' | uniq | sort
}
sound_or_speak()
{
local soundfile=$1
local speaktext=$2
declare -a logged_users=($(print_logged_users))
for (( i=0; i<${#logged_users[@]}; i=($i + 1) )); do
cur_user=${logged_users[$i]}
if [[ $use_espeak == true ]]; then
if [[ "$speaktext" != "" ]]; then
su $cur_user -c "/usr/bin/espeak '$speaktext'"
fi
else
if [[ -r "$soundfile" ]]; then
su $cur_user -c "/usr/bin/aplay -q '$soundfile'"
fi
fi
done
}
# notification for plugged device {{{
notify_plugged()
{
local dev_title=$1
local dev_name=$2
if [[ $show_notifications == true ]]; then
#notify-send "device plugged" "$dev_title" &
show_visual_notification_simplified "device plugged" "$dev_title" "$dev_name"
fi
if [[ $play_sounds == true ]]; then
sound_or_speak "$plug_sound_path" "Device plugged: $dev_name"
fi
}
# }}}
# notification for unplugged device {{{
notify_unplugged()
{
local dev_title=$1
local dev_name=$2
if [[ $show_notifications == true ]]; then
#notify-send "device unplugged" "$dev_title" &
show_visual_notification_simplified "device unplugged" "$dev_title" "$dev_name"
fi
if [[ $play_sounds == true ]]; then
sound_or_speak "$unplug_sound_path" "Device unplugged: $dev_name"
fi
}
# }}}
(
# we need for lock our $devlist_file
flock -w 10 200 || exit 1
case $action in
"reboot" )
rm $devlist_file
;;
"add" )
# ------------------- PLUG -------------------
if [[ "$bus_num" != "" && "$dev_num" != "" ]]; then
# make bus_num and dev_num have leading zeros
bus_num=`printf %03d $bus_num`
dev_num=`printf %03d $dev_num`
# Retrieve device title. Currently it's done just by lsusb and grep.
# Not so good: if one day lsusb change its output format, this script
# might stop working.
dev_title=`lsusb -D /dev/bus/usb/$bus_num/$dev_num | grep '^Device:\|bInterfaceClass\|bInterfaceSubClass\|bInterfaceProtocol'|sed 's/^\s*\([a-zA-Z]\+\):*\s*[0-9]*\s*/<b>\1:<\/b> /' | awk 1 ORS='###'`
# dev_name=`lsusb -D /dev/bus/usb/$bus_num/$dev_num | grep idProduct | tr -s ' ' | cut -s -d' ' -f4,5,6,7,8,9`
dev_text=`lsusb -D /dev/bus/usb/$bus_num/$dev_num | grep 'Device: ' | sed -r 's/^.*Device:.*(ID.\+?)/\1/'`
# Sometimes we might have the same device attached to different bus_num or dev_num:
# in this case, we just modify bus_num and dev_num to the current ones.
# At least, it often happens on reboot: during previous session, user plugged/unplugged
# devices, and dev_num is increased every time. But after reboot numbers are reset,
# so with this substitution we won't have duplicates in our devlist.
escaped_dev_path=`echo "$dev_path" | sed 's/[\/&*.^$]/\\\&/g'`
sed -i "s#^\([0-9]\{3\}:\)\{2\}\($escaped_dev_path\)#$bus_num:$dev_num:$dev_path#" $devlist_file
# udev often generates many events for the same device
# (I still don't know how to write udev rule to prevent it)
# so we need to check if this device is already stored in our devlist file
existing_dev_on_bus_cnt=`cat $devlist_file | grep "^$bus_num:$dev_num:" | awk 'END {print NR}'`
if [[ $existing_dev_on_bus_cnt == 0 ]]; then
# this device isn't stored yet in the devlist, so let's write it there.
echo "$bus_num:$dev_num:$dev_path title=\"$dev_title\"" >> $devlist_file
echo "$bus_num:$dev_num:$dev_path name=\"$dev_name\"" >> $devlist_file
# and, finally, notify the user. $dev_name
notify_plugged "$dev_title" "$dev_text"
fi
fi
;;
"remove" )
# ------------------- UNPLUG -------------------
# Unfortunately, udev doesn't emit bus_num and dev_num for "remove" events,
# and there's even no vendor_id and product_id.
# But it emits dev_path. So we have to maintain our own plugged devices list.
# Now we retrieve stored device title from our devlist by its dev_path.
dev_title=`cat $devlist_file | grep "$dev_path " | grep 'title="' | sed 's/.*title=\"\(.*\)\".*/\1/g'`
# sed somehow is sucks
dev_name=`echo $dev_title | sed -E 's/\#\#\#/\n/g' | sed -r 's/^.*Device:.*(ID.\+?)/\1/' | head -n 1`
# remove that device from list (since it was just unplugged)
cat $devlist_file | grep -v "$dev_path " > ${devlist_file}_tmp
mv ${devlist_file}_tmp $devlist_file
# if we have found title, then notify user, after all. $dev_name
if [[ "$dev_title" != "" ]]; then
notify_unplugged "$dev_title" "$dev_name"
fi
;;
esac
) 200>/var/lock/.udev-notify-devices.exclusivelock
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment