Skip to content

Instantly share code, notes, and snippets.

@altercation
Created September 19, 2014 23:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save altercation/bd9d4b4bb7149b4825b2 to your computer and use it in GitHub Desktop.
Save altercation/bd9d4b4bb7149b4825b2 to your computer and use it in GitHub Desktop.
script to conditionally sleep surface pro 3 on removal of all external usb devices, with installers to set correct udev wake rules
#!/usr/bin/env zsh
setopt EXTENDED_GLOB NO_UNSET ERR_EXIT
typeset -g SCRIPTUSERHOME=/${(j:/:)${(s:/:)0:A}[1,2]} # TODO: probably a better way than this
typeset -g SCRIPTUSER=${${(s:/:)0:A}[2]} # TODO: probably a better way than this
typeset -g SCRIPTNAME="${${SCRIPTPATH:=$0:A}:t}"
typeset -g SCRIPTROOT="${SCRIPTPATH:h}"
typeset -g FUNCPATH=$SCRIPTROOT/functions
fpath=($FUNCPATH $fpath)
autoload $FUNCPATH/*(#q.:t)
typeset -g SCRIPTCONF="$SCRIPTUSERHOME/${XDG_CONFIG_HOME:-.config}/$SCRIPTNAME"
typeset -g SCRIPTSTATE="$SCRIPTCONF/state"
typeset -g I3INDICATORS="$SCRIPTUSERHOME/${XDG_CONFIG_HOME:-.config}/i3status"
typeset -g I3INDICATOR="$I3INDICATORS/$SCRIPTNAME"
[[ -d $SCRIPTCONF ]] || { mkdir $SCRIPTCONF; chown $SCRIPTUSER:$SCRIPTUSER $SCRIPTCONF} &>/dev/null
touch $SCRIPTSTATE
[[ -d $I3INDICATORS ]] || { mkdir $I3INDICATORS; chown $SCRIPTUSER:$SCRIPTUSER $I3INDICATORS; } &>/dev/null
alias runonce='${(P)${(U)0}:-false} && return 0 || eval typeset -g ${(U)0}=true'
# following .X11-unix line is breaking when called by root
#[[ -n $DISPLAY ]] || typeset -gx DISPLAY="$(print /tmp/.X11-unix/*(:t:s/X/:))"
typeset -gx DISPLAY=:0
[[ $(loginctl list-sessions) =~ "seat0" ]] || {
logger -s "${SCRIPTNAME:-$0}${SCRIPTNAME:+>$0}: No X Session identified"
exit 1
}
if [[ $USERNAME == root ]]
then
xauth_xauthority=/home/${${(s:/:)SCRIPTPATH}[2]}/.Xauthority
xhost_xauthority=/var/run/gdm/auth-for-gdm*/database
[[ -f $xhost_xauthority ]] && typeset -gx XAUTHORITY=$xhost_xauthority || {
[[ -f $xauth_xauthority ]] && typeset -gx XAUTHORITY=$xhost_xauthority }
fi
# plug/unplug usb to internal usb hub will suspend/resume machine
# script development clues
# # udevadm info -a -p /sys/devices/pci0000:00/0000:00:14.0/usb1/1-1
# # udevadm test --action remove /sys/devices/pci0000:00/0000:00:14.0/usb1/1-1
# # udevadm monitor --environment --udev
# # cat /usr/lib/udev/rules.d/* | grep -i remove
# ---------------------------------------------------------------------
# script values
# ---------------------------------------------------------------------
typeset -g SCRIPTNAME="${${SCRIPTPATH:=$(readlink -f $0)}:t}"
typeset -g SCRIPTROOT="${SCRIPTPATH:h}"
typeset -g SCRIPTLOCK="/var/run/$SCRIPTNAME/lock"
[[ -d ${SCRIPTLOCK:h} ]] || mkdir ${SCRIPTLOCK:h} &>/dev/null
# ---------------------------------------------------------------------
# initializers
# ---------------------------------------------------------------------
install () {
[[ $USER == root ]] || { print "run with sudo or as root when calling $SCRIPTNAME $0"; exit 1 }
typeset -A rules
rules=(
99-$SCRIPTNAME.rules 'ACTION=="remove", ENV{ID_USB_INTERFACE_NUM}=="*", RUN+="'$SCRIPTPATH' conditional_suspend $parent"'
99-usb-hub-wakeup.rules "ACTION==\"add\", SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"1d6b\", RUN+=\"/bin/sh -c \'echo enabled > /sys\$env{DEVPATH}/power/wakeup\'\""
99-usb-hid-wakeup.rules "ACTION==\"add\", SUBSYSTEM==\"usb\", DRIVER==\"usbhid\", RUN+=\"/bin/sh -c \'echo enabled > /sys\$env{DEVPATH}/../../power/wakeup\'\", RUN+=\"/bin/sh -c \'echo enabled > /sys\$env{DEVPATH}/../power/wakeup\'\", RUN+=\"/bin/sh -c \'echo enabled > /sys\$env{DEVPATH}/power/wakeup\'\""
)
#99-usb-hub-wakeup.rules 'SUBSYSTEM=="usb", ATTRS{idVendor}=="1d6b", ATTRS{idProduct}=="*", RUN+="/usr/bin/echo enabled > /sys\$env{DEVPATH}/power/wakeup"'
for rulename in ${(k)rules}
do
rulepath=/etc/udev/rules.d/$rulename
rule=${rules[$rulename]}
[[ -f $rulepath ]] && rm -f $rulepath
print $rule > $rulepath \
&& { print "Successfully wrote udev rules file $rulepath" } \
|| { print "Failed to write udev rules file $rulepath"; exit 1 }
done
# service="
# [Unit]
# Description=Suspend system if all usb devices unplugged
#
# [Service]
# Type=simple
# ExecStart=$SCRIPTPATH %I"
#
# print -l -- "${(@)${(@f)service}##[[:space:]]#}" > /etc/systemd/system/$SCRIPTNAME\@.service \
# && { print "Successfully wrote systemd service unit" } \
# || { print "Failed to write systemd service unit"; exit 1 }
#
# systemctl daemon-reload
udevadm control --reload
}
lock () {
print $(date +%N) > $SCRIPTLOCK || {
notify-send "failed to make lock"
exit 1 }
}
unlock () {
rm $SCRIPTLOCK
}
conditional_suspend () {
(( $(cat /sys/class/power_supply/*/online) >0 )) && exit 0 # if on ac
# there are four builtin usb hub devices, so that's the count if we've unplugged everything
if [[ -z ${*:-} ]] && (( ${#${(f)"$(lsusb -s 001:)"}} == 4 ))
then
# make sure that usb hub is set to resume and then suspend
#if [[ "$(</sys/devices/pci0000:00/0000:00:14.0/usb1/power/wakeup)" != enabled ]]
#then
# envinit
# notify-send "Setting usb hub to wakeup mode (check udev rule!)"
# print "enabled" > /sys/devices/pci0000:00/0000:00:14.0/usb1/power/wakeup || {
# notify-send "Failed!"; exit 1 }
#fi
# disable all except hub, hub parents, hub descendants
for w in /sys/devices/**/wakeup; do print disabled > $w; done
device=/sys/devices/pci0000:00/0000:00:14.0/usb1
while [[ -n $device && $device != "/" ]]
do
[[ -f $device/power/wakeup ]] && print enabled > $device/power/wakeup
device=${device:h}
done
if (( ${#${(f)"$(lsusb -s 001:)"}} <= 4 )) \
&& (( $(cat /sys/class/power_supply/*/online) == 0 )) \
&& [[ ! -f $SCRIPTLOCK ]]
then
lock
systemctl suspend
unlock
elif [[ -f $SCRIPTLOCK ]]
then
# script lock present, skip suspend
:
elif (( ${#${(f)"$(lsusb -s 001:)"}} > 4 ))
then
# more than 4 devices attached, skip suspend
:
elif (( $(cat /sys/class/power_supply/*/online) == 1 ))
then
# still plugged in to power, skip suspend
:
else
notify-send --urgency=critical "$SCRIPTNAME: Unknown state encountered in $0"
fi
fi
}
${*:-}
@altercation
Copy link
Author

The above is an ugly hack and messy code. I look forward to throwing it away soon.

@paulhandy
Copy link

did you ever come up with a better solution than this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment