4

At the risk of posting a duplicate question:

How can I run a local script (as the current user) when I log out?

I read (and tried) among others:

  1. This one (Marton's version), which did perfectly nothing, used on a simple script creating a file, absolute paths to the script (being executable) and the file.
  2. This one, which must be outdated; it made me remove the file from command line to be able to use my system again.
  3. I also looked into this one, without succes.

I believe I must be doing something wrong here, but cannot find out what is my blind spot.

Context

Looking for an elegant solution (workaround) to this question, presumably the result of a bug, I made a script that, depending on the argument, either takes a snapshot of the current desktop icon arrangement, or arranges the icons, according to the last snapshot. The script works fine, but is really no candidate to run as a background script.

The script therefore should run on logout to get the arrangement, obviously run by the current user.

2
  • What Ubuntu version are you targeting? From 15.04 I would expect you'd need to hook into systemd rather than upstart. Sorry that I can't be more specific but I haven't looked at the user facilities of systemd in detail.
    – zwets
    Jan 12 '16 at 22:26
  • 1
    @zwets Ah, I should have mentioned,: 14.04 (lightdm) Jan 12 '16 at 22:28
3
+50

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 use dbus-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

5
  • This looks very promising, will look later today! Mar 18 '16 at 17:13
  • main() … main O.o What have you been smoking recently?
    – muru
    Mar 18 '16 at 17:19
  • @muru mostly Pall Mall . . .What ? It's no different than having def main: in python and then calling if __ name __ == "__ main __": main . Besides , I kind of miss C style formatting in bash scripts. Doesnt' that look pretty ? Mar 18 '16 at 17:34
  • No, it looks ugly. In Python, it might have some purpose, I really don't see the sense in shell scripts. And I'm sure it's illegal to call main in C.
    – muru
    Mar 18 '16 at 17:36
  • You already had me with the monitoring script. This is the roadd I was looking for. Thanks for pointing me in the direction of dbus, which is mostly an unexplored area to me. Mar 18 '16 at 18:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.