84

I'd like to run a script if the Gnome session is locked and unlocked.
Is there a way that I can intercept this and perform certain actions when the desktop is locked or unlocked?

intika
  • 14,406
Naftuli Kay
  • 39,676

12 Answers12

77

Gnome-screensaver emits some signals on dbus when something happens.

Here the documentation (with some examples).

You could write a scripts that runs:

dbus-monitor --session "type='signal',interface='org.gnome.ScreenSaver'"

and that does what you need anytime dbus-monitor prints a line about the screen being locked/unlocked.


Here a bash command to do what you need:

dbus-monitor --session "type='signal',interface='org.gnome.ScreenSaver'" |
  while read x; do
    case "$x" in 
      *"boolean true"*) echo SCREEN_LOCKED;;
      *"boolean false"*) echo SCREEN_UNLOCKED;;  
    esac
  done

Just replace echo SCREEN_LOCKED and echo SCREEN_UNLOCKED with what you need.

Riot
  • 231
peoro
  • 3,778
  • Hi @peoro,

    That makes me think you can unlock or lock gnome screen/session from a scripted program, worth doing some ssh tricks about it ;-)

    – Nikhil Mulley Jan 03 '12 at 05:13
  • 2
    @Nikhil: to do that you don't need to play around with dbus: gnome-screensaver-command is already there. Passing -a to gnome-screensaver-command you'll lock the screen, while you'll unlock it with -d. Anyway most gnome apps use dbus extensively, so you'll be able to do many amazing things with it. – peoro Jan 03 '12 at 05:18
  • 1
    @peoro Thanks great, very helpful! Can I also run this as some sort of daemon? When I enter this in the terminal now, it has to stay open to monitor the dbus for that case.

    I would like to execute this command at login and then it can be active during the entire session.

    – Sander Jan 04 '12 at 16:13
  • 1
    I think things may have changed now in 2014? as the output doesnt change if the screen was only locked, it only shows something when it gets blanked and is very different from here :(, I created this question http://askubuntu.com/questions/505681/unity-how-to-detect-if-the-screen-is-locked, do you believe there is still some way to do that? thx! – Aquarius Power Aug 02 '14 at 02:18
  • How to run that script that it catches the lock event? Kinda like a watcher. – Starx Nov 01 '16 at 13:03
  • 1
    Does anybody know how or where to hook this script to start listening on first login? – hanzo2001 Nov 29 '17 at 11:30
29

Nowadays I think it's better to listen to the LockedHint rather than screensaver messages. That way you're not tied to a screensaver implementation.

Here's a simple script to do that:

gdbus monitor -y -d org.freedesktop.login1 | grep LockedHint

Gives this:

/org/freedesktop/login1/session/_32: org.freedesktop.DBus.Properties.PropertiesChanged ('org.freedesktop.login1.Session', {'LockedHint': <true>}, @as [])
/org/freedesktop/login1/session/_32: org.freedesktop.DBus.Properties.PropertiesChanged ('org.freedesktop.login1.Session', {'LockedHint': <false>}, @as [])
Matthew
  • 401
  • 1
    This approach with gdbus monitor is the one that worked for me on Ubuntu 20.04 with Gnome 3. Packaged it as a script if you want to use it as a building block for automation: https://github.com/gogama/lockheed. – 0xbe5077ed Nov 04 '22 at 17:52
  • Nice. A little more information would be appreciated (without having to go through all the docs). – Luis A. Florit Sep 04 '23 at 20:37
23

In ubuntu 14.04 the DBus event for screen lock unlock has changed and the new script for binding to screen lock and unlock events looks like the following

dbus-monitor --session "type='signal',interface='com.ubuntu.Upstart0_6'" | \
(
  while true; do
    read X
    if echo $X | grep "desktop-lock" &> /dev/null; then
      SCREEN_LOCKED;
    elif echo $X | grep "desktop-unlock" &> /dev/null; then
      SCREEN_UNLOCKED;
    fi
  done
)
9

Expanding on already given answer.

If you try to run a script from inside a screen or tmux session, you'll need to find the correct $DBUS_SESSION_BUS_ADDRESS first and pass it as an argument for dbus-monitor instead of --session. Also if you're running it as a daemon you should make sure only one instance is running at a time (e.g. with a lock file) and that the script cleans up after itself with trap. The following example will work as a daemon in most current Gnome environments (tested on Ubuntu GNOME 16.04):

#!/bin/bash
set -o nounset                # good practice, exit if unset variable used

pidfile=/tmp/lastauth.pid     # lock file path
logfile=/tmp/lastauth.log     # log file path

cleanup() {                   # when cleaning up:
    rm -f $pidfile            # * remove the lock file
    trap - INT TERM EXIT      # * reset kernel signal catching
    exit                      # * stop the daemon
}

log() {                       # simple logging format example
    echo $(date +%Y-%m-%d\ %X) -- $USER -- "$@" >> $logfile
}

if [ -e "$pidfile" ]; then    # if lock file exists, exit
    log $0 already running...
    exit
fi

trap cleanup INT TERM EXIT    # call cleanup() if e.g. killed

log daemon started...

echo $$ > $pidfile            # create lock file with own PID inside

# usually `dbus-daemon` address can be guessed (`-s` returns 1st PID found)
export $(grep -z DBUS_SESSION_BUS_ADDRESS /proc/$(pidof -s dbus-daemon)/environ)

expr='type=signal,interface=org.gnome.ScreenSaver' # DBus watch expression here

dbus-monitor --address $DBUS_SESSION_BUS_ADDRESS "$expr" | \ 
    while read line; do
        case "$line" in
            *"boolean true"*) log session locked;;
            *"boolean false"*) log session unlocked;;
        esac
    done

cleanup # let's not leave orphaned lock file when the loop ends (e.g. dbus dies)

If this doesn't work for you, it's probably because:

  1. you don't use Gnome - check other answers for better DBus watch expression.
  2. you run multiple DBus lines - check details on how to make PID finding deterministic.
GAD3R
  • 66,769
cprn
  • 1,025
  • 1
    This actually answers a different question I had for dynamically discovering the DBus session information given a user account, which I have solved here. Thanks for your contribution here! – Naftuli Kay Jun 02 '17 at 05:10
  • Thanks. I'll link your solution in the answer for further reading. – cprn Jun 02 '17 at 09:53
  • 1
    Nice solution with great techniques within. To suppress bash: warning: command substitution: ignored null byte in input, I suggest: export $(grep -z DBUS_SESSION_BUS_ADDRESS /proc/$(pidof -s dbus-daemon)/environ|xargs -0). – fbicknel Jan 31 '22 at 21:45
8

If you're on Kubuntu or using KDE / Plasma as your Desktop Environment, you have to listen for the interface org.freedesktop.ScreenSaver, so the script for listening to that event would look like this:

dbus-monitor --session "type='signal',interface='org.freedesktop.ScreenSaver'" |
  while read x; do
    case "$x" in 
      *"boolean true"*) echo SCREEN_LOCKED;;
      *"boolean false"*) echo SCREEN_UNLOCKED;;  
    esac
  done
Bob
  • 350
  • 5
    If you use KDE/Plasma you can use the Plasma Desktop settings to execute scripts on notifications... Personalization>Notifications>Notifications>Screensaver. – xenoid Apr 03 '19 at 12:40
  • 1
    thanks @xenoid ! KDE is such a personalizable de that you never have to fiddle with these things. its as if they anticipate what I need – Ankur S Sep 19 '19 at 13:32
5

Ubuntu 16.04: ozma’s solution did not work for me, however this one did:

dbus-monitor --session "type=signal,interface=com.canonical.Unity.Session,member=Unlocked" | 
  while read MSG; do
    LOCK_STAT=`echo $MSG | awk '{print $NF}'`
    if [[ "$LOCK_STAT" == "member=Unlocked" ]]; then
        echo "was unlocked"
    fi
  done
Tom Adler
  • 181
2

this may not be the cleanest solution but it works for me when dbus-monitor ... does not:

gdbus monitor -y -d org.freedesktop.login1 |\
 grep --line-buffered -i "LockedHint" |\
 sed -uE 's/.*LockedHint.*<(.*)>.*/\1/g'

Should return stream of lines with strings 'true' or 'false'

Using: Fedora release 30 (Thirty)

1

Thanks for HaterTot comment another version. Tested on ubuntu 18.04

echo "start at `date`" >> ~/Documents/time-trecking.log
dbus-monitor --session "type=signal,interface=org.gnome.ScreenSaver" | 
  while read MSG; do
    LOCK_STAT=`echo $MSG | grep boolean | awk '{print $2}'`
    if [[ "$LOCK_STAT" == "true" ]]; then
        echo "locked `date +%s` `date`" >> ~/Documents/time-trecking.log
    elif [[ "$LOCK_STAT" == "false" ]]; then
        echo "unlock `date +%s` `date`" >> ~/Documents/time-trecking.log
    fi
  done

[Before Edit] this is what worked for me in ubuntu 16.04

dbus-monitor --session "type=signal,interface=org.gnome.ScreenSaver" | 
  while read MSG; do
    LOCK_STAT=`echo $MSG | grep boolean | awk '{print $2}'`
    if [[ "$LOCK_STAT" == "true" ]]; then
        echo "was locked"
    else
        echo "was un-locked"
    fi
  done
ozma
  • 119
  • This didn't work for me. As it finished executing and does not listen to the state changed. – Starx Nov 01 '16 at 13:12
  • which screen saver are you using? gnome or xscreensaver? which flavor ubuntu, xubuntu, kubuntu etc. which version (it was tested on 16.04) – ozma Nov 01 '16 at 14:06
  • ubuntu gnome 16.04 – Starx Nov 02 '16 at 09:25
  • 1
    this worked for me on Ubuntu 19.10, but for me the "else" condition kept triggering when other messages unrelated messages popped up on invocation. So I just changed the logic to explicitly check for "false" – HaterTot Mar 10 '20 at 17:02
  • 1
    Might be easier to use a case rather than (non-portable) [[. Instead of grep|awk, consider using a single awk invocation (awk '$1 == "boolean" {print $2}'). – Toby Speight Oct 13 '22 at 15:55
1

Version for Cinnamon Desktop (Ubuntu 18.04):

dbus-monitor --session "type='signal',interface='org.cinnamon.ScreenSaver'" | \
(
  while true; do
    read X
    if echo $X | grep "boolean true" &> /dev/null; then
      echo `date` - SCREEN_LOCKED;
    elif echo $X | grep "boolean false" &> /dev/null; then
      echo `date` - SCREEN_UNLOCKED;
    fi
  done
)
0

upstart session jobs support desktop-lock and desktop-unlock events in start on stanza. Just create a job .conf for your user with relevant triggers and commands to call under $XDG_CONFIG_HOME/upstart/ or $HOME/.config/upstart like example below:

description "some job description"
start on desktop-lock
script
        /path/to/your/executable
end script
0

For KDE Plasma, we can listen to org.freedesktop.ScreenSaver with:

dbus-monitor --session "type='signal',interface='org.freedesktop.ScreenSaver'"

But note that lock/unlock action are reported twice, the following script limit the action within 5 seconds (to avoid executing required command twice)

#!/bin/bash

screen_locked () {
    if [[ $firstrun == '' ]] || [[ $(expr $(date +%s) - $firstrun) -ge 5 ]]
    then
        screen_locked_core;
        firstrun=$(date +%s);
    fi
}

screen_locked_core () {
    echo locked
}

dbus-monitor --session "type='signal',interface='org.freedesktop.ScreenSaver'" |
while read x; do
    case "$x" in 
        *"boolean true"*) screen_locked;;
#       *"boolean false"*) screen_unlocked;;  
    esac
done
intika
  • 14,406
0

I used the same answer as peoro, but first I run only dbus-monitor --session "type='signal'" in a terminal, and locked an unlocked my screen. Then, I could figure out the correct name for my operating system:

signal time=1680812432.954794 sender=:1.27 -> destination=(null destination) serial=3080 path=/org/xfce/ScreenSaver; interface=org.xfce.ScreenSaver; member=ActiveChanged
   boolean true
signal time=1680812435.757417 sender=:1.27 -> destination=(null destination) serial=3082 path=/org/xfce/ScreenSaver; interface=org.xfce.ScreenSaver; member=ActiveChanged
   boolean false

So, my command starts with dbus-monitor --session "type='signal',interface='org.xfce.ScreenSaver'"' instead of dbus-monitor --session "type='signal',interface='org.gnome.ScreenSaver'".

user
  • 781