How can I run a local command (to run a script) on (just before) log out of a Unity session?
Solution 1:
Basic theory
As you may or may not know , a lot of Unity actions are implemented using dbus . The big convenience about dbus is that you can perform lots of root-required actions by calling appropriate methods in command-line without need of having admin account, for instance shutting down your system.
Specifically in our case here we are interested in com.canonical.Unity
service, /com/canonical/Unity/Session
path. You can list information about it with qdbus
command. Particularly we're interested in signals that have Requested
in them.
$ qdbus com.canonical.Unity /com/canonical/Unity/Session | grep 'signal.*Requested.*'
signal void com.canonical.Unity.Session.LockRequested()
signal void com.canonical.Unity.Session.LogoutRequested(bool have_inhibitors)
signal void com.canonical.Unity.Session.RebootRequested(bool have_inhibitors)
signal void com.canonical.Unity.Session.ShutdownRequested(bool have_inhibitors)
signal void com.canonical.Unity.Session.UnlockRequested()
These signals appear on the bus when user clicks any of the shutdown, lock, logout, or reboot items in the drop-down session menu on the right side of the Unity's top bar. So what we need to do is this:
- monitor the bus for those signals to appear.
- once they appear - perform a certain action.
Of course , the big disclamer is that if a user uses sudo shutdown -P now
or gnome-session-quit
, that won't be told to the dbus
, so whatever action is wanted won't be done.
Monitoring the dbus
Conveniently there is a command for that, dbus-monitor
and we can filter its output using a set of options, and perform an action on it using while read do … done
structure
Bellow is an example of a simple monitoring script.
dbus-monitor --profile "interface='com.canonical.Unity.Session',type=signal" | \
while read -r line;
do
echo "$line"
sleep 0.25
done
What it does is merely monitor the bus for signals , and echo them back to stdout if they appear; of course echo could be any command. Run it , and try clicking on each item in the session menu that should generate signals, and you should see something like this:
$ ./logout_monitor.sh
sig 1458319246 172587 2 /org/freedesktop/DBus org.freedesktop.DBus NameAcquired
sig 1458319251 213766 5496 /com/canonical/Unity/Session com.canonical.Unity.Session RebootRequested
sig 1458319265 62555 5525 /com/canonical/Unity/Session com.canonical.Unity.Session LogoutRequested
sig 1458319271 856770 5555 /com/canonical/Unity/Session com.canonical.Unity.Session LockRequested
sig 1458319273 223940 5564 /com/canonical/Unity/Session com.canonical.Unity.Session Locked
sig 1458319276 991413 5604 /com/canonical/Unity/Session com.canonical.Unity.Session UnlockRequested
sig 1458319278 3443 5606 /com/canonical/Unity/Session com.canonical.Unity.Session Unlocked
So now we have a monitoring function that can perform an action once an interrupt occurs. Now , to address the specific question that you were trying to solve , your script is taking snapshots of the icons on the desktop. There's no harm done if we take snapshot each time a signal occurs - it doesn't have to be reboot or shutdown specifically. So if we monitor that specific Unity interface for signals and take snapshots upon each signal's arrival, we can simplify the scripting.
Interrupt-driven script
Bellow you can see an interrupt-driven script which will monitor the bus for signals (ignoring the first one ) and if we have a signal coming in , then call an interrupt function (which in your case will be the icon snapshot script that you've written).
#!/bin/bash
main()
{
ARG="cow"
dbus-monitor --profile "interface='com.canonical.Unity.Session',type=signal" |
while read -r line;
do
grep -q '.*NameAcquired.*' <<< "$line" && continue # Ignore that first line
if [ -n "$line" ];then
interrupt $ARG # call your python snapshot script here
fi
done
}
interrupt()
{
echo 'Old McDonald had a ' $ARG ' e-i-e-i-o '
}
main
NOTE: Dbus relies on having GUI session , since (as far as I know) it starts with Unity/Gnome login. Older environments such as blackbox and openbox, as well as TTY environments do not start dbus session, hence that script will puke if you try to launch it in ~/.profile
(I had one user who tried to do something like that and he got into trouble). Start such script using Unity's or Gnome's Startup Applications program.
Going beyond this script
- Dbus has a lot of API's including python
- I prefer using
qdbus
because it's simple, but one could always usedbus-send
. Examples - The interrupt function can be anything, even other
dbus
methods. There's a lot to look into.
Trivia: I've used this same method for preventing shutdown if apt is running