How to stop inactive user sessions triggering Suspend to RAM in a single-seat, multi-user installation of Lubuntu 18.04

In my previous post I mentioned a problem that I had still not been able to fix in a single-seat, multi-user installation of Lubuntu 18.04: Xfce Power Manager in each user’s account can cause the installation to suspend to RAM if a user has not logged out of his/her session and another user is using a different session. Each user account in Lubuntu 18.04 has its own XfcePower Manager settings, stored in the file ~/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml. If the property /xfce4-power-manager/inactivity-on-ac has a value of 15 (minutes) or higher, that session can cause the machine to suspend to RAM even if the session is not active while someone else’s session is active. The example below illustrates the effect.

Consider five users mick, christine, john, stevie and lindsey with the following settings for the number of minutes of inactivity that will trigger suspension to RAM:

mick@aspirexc600:~$ xfconf-query -c xfce4-power-manager -p /xfce4-power-manager/inactivity-on-ac
30
christine@aspirexc600:~$ xfconf-query -c xfce4-power-manager -p /xfce4-power-manager/inactivity-on-ac
25
john@aspirexc600:~$ xfconf-query -c xfce4-power-manager -p /xfce4-power-manager/inactivity-on-ac
45
stevie@aspirexc600:~$ xfconf-query -c xfce4-power-manager -p /xfce4-power-manager/inactivity-on-ac
15
lindsey@aspirexc600:~$ xfconf-query -c xfce4-power-manager -p /xfce4-power-manager/inactivity-on-ac
30

Now, suppose that john boots the machine, logs in to his account to check his e-mail, leaves the e-mail client open and goes off to grab lunch without logging out. Then stevie comes along and clicks on ‘Logout’ > ‘Switch User’ to display the LightDM greeter screen (or the greeter screen is already displayed because john‘s session has already been locked), logs in to her account and begins to use, say, LibreOffice Writer. Even though stevie is busy typing, the machine will suspend to RAM after 45 minutes of inactivity by john. This can be very annoying.

In addition to the individual users’ Xfce Power Manager configuration files in Lubuntu 18.04, I found the following Xfce Power Manager configuration files which appear to be system-wide:

/etc/xdg/xdg-Lubuntu/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml
/etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml

First attempt at fixing the problem

I asked all the users to configure their accounts to never cause the machine to suspend, by using the Xfce Power Manager settings GUI in their session and selecting ‘Never’. I noticed this caused each user’s /xfce4-power-manager/inactivity-on-ac property to become ‘14‘:

user $ xfconf-query -c xfce4-power-manager -p /xfce4-power-manager/inactivity-on-ac
14

Note that users must not edit their file ~/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml; if they do, the settings shown in the Xfce Power Manager settings GUI will not be updated. Users must either use the Xfce Power Manager settings GUI or xfconf-query commands as explained on the askubuntu Web page ‘Change xfce4-power-manager option from terminal‘.

Then I edited the file /etc/xdg/xdg-Lubuntu/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml to make its contents the same as the previous contents of the individual users’ settings when Suspend to RAM was enabled individually):

<?xml version="1.0" encoding="UTF-8"?>

<channel name="xfce4-power-manager" version="1.0">
  <property name="xfce4-power-manager" type="empty">
    <property name="power-button-action" type="uint" value="3"/>
    <property name="show-tray-icon" type="bool" value="true"/>
    <property name="brightness-switch-restore-on-exit" type="int" value="1"/>
    <property name="brightness-switch" type="int" value="0"/>
    <property name="presentation-mode" type="bool" value="false"/>
    <property name="inactivity-on-ac" type="uint" value="30"/>
    <property name="blank-on-ac" type="int" value="10"/>
    <property name="dpms-on-ac-sleep" type="uint" value="0"/>
    <property name="dpms-on-ac-off" type="uint" value="0"/>
    <property name="brightness-on-ac" type="uint" value="9"/>
    <property name="lock-screen-suspend-hibernate" type="bool" value="true"/>
    <property name="logind-handle-lid-switch" type="bool" value="false"/>
    <property name="dpms-enabled" type="bool" value="false"/>
    <property name="general-notification" type="bool" value="true"/>
    <property name="sleep-button-action" type="uint" value="0"/>
    <property name="hibernate-button-action" type="uint" value="0"/>
  </property>
</channel>

After rebooting, leaving one or more users logged in without any activity did not cause the installation to suspend to RAM after 30 minutes of no activity in any session.

So I then edited the file /etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml to make its contents the same as the above. After rebooting, leaving one or more users logged in without any activity did not cause the installation to suspend to RAM after 30 minutes of no activity in any session.

I therefore assume that the above two files are ignored by Xfce Power Manager during normal operation.

Second attempt at fixing the problem

I used the procedure given in the Xfce4-power-manager FAQs to check if Xfce Power Manager in Lubuntu 18.04 uses systemd-logind to suspend the installation, and indeed it does:

TRACE[xfpm-polkit.c:366] xfpm_polkit_check_auth_intern(): Action=org.freedesktop.login1.suspend is authorized=TRUE

Therefore I edited /etc/systemd/logind.conf to add IdleAction=suspend and IdleActionSec=30min, and rebooted. However, this had no discernable effect either. Leaving one or more users logged in without any activity did not cause the installation to suspend to RAM after 30 minutes of no activity in any session.

Third attempt (successful) at fixing the problem

So, what to do?! In my previous post I explained how I had fixed the problem of not being able to suspend to RAM automatically from the LightDM greeter screen. I decided to keep the scripts from that post and add a new script sessions_sleep.sh to the root crontab. The contents of all the files and the crontab are shown below.

user $ cd /etc/lightdm/lightdm.conf.d/
user $ cat 10_lubuntu.conf 
[Seat:*]
greeter-setup-script=/etc/lightdm/lightdm.conf.d/lightdm_sleep.sh
session-setup-script=/etc/lightdm/lightdm.conf.d/lightdm_kill_sleep.sh
session-cleanup-script=/etc/lightdm/lightdm.conf.d/unmount_FREECOM_HDD.sh
user $ ls
05_lubuntu.conf  lightdm_kill_sleep.sh       sessions_sleep.sh
10_lubuntu.conf  lightdm_sleep.sh            unmount_FREECOM_HDD.sh
user $ cat lightdm_sleep.sh 
#!/bin/bash
# This forms part of the scheme to provide automatic suspension while the greeter screen is displayed
file="/tmp/unique_identifier"
(while true; do sleep 30m; systemctl suspend; done) &
echo $! > $file
user $ cat lightdm_kill_sleep.sh
#!/bin/bash
# This forms part of the scheme to provide automatic suspension while the greeter screen is displayed
file="/tmp/unique_identifier"
if [ -f "$file" ]; then
    kill `cat $file`
    rm $file
fi
user $ tail -n 11 unmount_FREECOM_HDD.sh
#----------------------------------------------------------------------------------------------------
#
# This forms part of the scheme to provide automatic suspension while the greeter screen is displayed
file="/tmp/unique_identifier"
if [ -f "$file" ]; then
    kill `cat $file`
    rm $file
fi
#
#----------------------------------------------------------------------------------------------------
exit 0

If the machine did not already have a permanently-connected external USB HDD (LABEL=”FREECOM HDD”) then it would have sufficed to specify a script named, for example, lightdm_kill_sleep2.sh instead of unmount_FREECOM_HDD.sh:

user $ cat 10_lubuntu.conf 
[Seat:*]
greeter-setup-script=/etc/lightdm/lightdm.conf.d/lightdm_sleep.sh
session-setup-script=/etc/lightdm/lightdm.conf.d/lightdm_kill_sleep.sh
session-cleanup-script=/etc/lightdm/lightdm.conf.d/lightdm_kill_sleep2.sh
user $ cat lightdm_kill_sleep2.sh
#!/bin/bash
# This forms part of the scheme to provide automatic suspension while the greeter screen is displayed
file="/tmp/unique_identifier"
if [ -f "$file" ]; then
    kill `cat $file`
    rm $file
fi
user $ sudo nano sessions_sleep.sh
user $ sudo chmod +x sessions_sleep.sh
user $ cat sessions_sleep.sh 
#!/bin/bash
while true
do
    # Only monitor idle time and suspend after specified inactivity if lightdm_sleep.sh is not taking care of those
    if [[ `ps -ef | grep bash | grep lightdm_sleep.sh | wc -l` -eq 0 ]]; then
        #-------------------------------STAGE 1: FIND OUT WHO IS THE ACTIVE USER--------------------------------------
        #
        while IFS=: read -r f1 f2 f3 f4 f5 f6 f7
        # $f1 is username
        # $f2 is password ('x')
        # $f3 is UID
        # $f4 is GID
        # $f5 is UID info
        # $f6 is home directory
        # $f7 is command/shell
        do
            if [[ $f6 == *"/home/"* ]] && [[ $f7 == "/bin/bash" ]]; then
                if `loginctl list-users | grep -ve '^$\|USER\|listed' | awk -F' ' '{print $2}' | grep -q $f1`; then
                    state=`loginctl show-user $f3 | grep State | awk -F'=' '{print $2}'`
                    if [[ $state != "active" ]]; then
                        inactive_user=$f1
                    elif [[ $state == "active" ]]; then
                        active_user=$f1
                    fi
                fi
            fi
        done < /etc/passwd
        #
        #-------------------------------STAGE 2: ASCERTAIN USER SESSIONS---------------------------------------------
        #
        # Find idle time for each X Windows session and suspend to RAM if the active user has been idle for >=30min.
        #
        who -u | grep -v "\." > /tmp/logged-in_users
        #
        while read a b c d e f g
        # $a is username
        # $b is the tty (tty1 to tty12)
        # $c is the date (yyyy-mm-dd)
        # $d is the time (hh:mm)
        # $e is the idle time (hh:mm) which does not reflect reality in this installation, for some reason
        # $f is the PID
        # $g is the display e.g. "(:1)"
        # Example: "john     tty7         2019-08-31 17:08 00:01        1624 (:0)"
        do
            if [[ $(echo $b | sed 's/[^0-9]*//g') -gt 6 ]]; then
                display=$(echo $g | sed 's/[^0-9]*//g')
                idle_millisecs=$(env DISPLAY=:$display sudo -u $a xprintidle)
                let idle_minutes=$idle_millisecs/60000
                if [[ $idle_minutes -ge 30 ]] && [[ $a == "$active_user" ]]; then
                    systemctl suspend
                fi
            fi
        done < /tmp/logged-in_users
        rm /tmp/logged-in_users
        #
        #------------------------------------------------------------------------------------------------------------
        sleep 10 # Frequency to repeat check
   fi
done

I installed the utility xprintidle via the Linux distribution’s package manager. As the name of the utility suggests, it returns the time (in milliseconds) that an X Windows session has been idle. Nice utility, by the way.

user $ sudo crontab -e
user $ sudo crontab -l | grep -v ^#
@reboot sudo /etc/lightdm/lightdm.conf.d/sessions_sleep.sh

Note that, despite its name, ‘@reboot‘ in the cron job will run the script after a cold boot as well as after a warm boot (reboot). Also note that the use of ‘sudo‘ in the root cron job is not an error; it makes the root cron job use the root user’s environment variables.

Remember that the property /xfce4-power-manager/inactivity-on-ac has to be configured to have a value of 14 (which corresponds to ‘Never’ in the Xfce Power Manager settings GUI) for every user. This should be done by each user using the Xfce Power Manager settings GUI in their own session.

Basically, the scheme works as follows: At boot, Lubuntu 18.04 launches the looping Bash script sessions_sleep.sh, which remains running but does nothing because no X Windows users are logged in. When LightDM runs the greeter-setup-script (lightdm_sleep.sh) and displays the greeter screen, sessions_sleep.sh still does nothing while lightdm_sleep.sh is running and taking care of managing suspension. When an X Windows user logs in and LightDM’s session-setup-script (lightdm_kill_sleep.sh) kills the running script lightdm_sleep.sh, the script sessions_sleep.sh then takes over monitoring users’ activity in X Windows and triggers suspension if the active user has not used his/her session for 30 minutes. If an X Windows user logs out, LightDM’s session-cleanup-script (unmount_FREECOM_HDD.sh) also kills lightdm_sleep.sh if it is running. When LightDM again runs its greeter-setup-script (lightdm_sleep.sh) and displays the greeter screen, that again inhibits sessions_sleep.sh from taking any action if no X Windows user is logged in. This all sounds convoluted, but it seems to work fine so far.

Because Xfce Power Manager is no longer used to monitor idle time and trigger suspension, ‘Presentation mode’ in Xfce Power Manager can no longer prevent the system from suspending after 30 minutes of inactivity while someone is watching a long video or playing music, for example. However this is not a problem; to temporarily inhibit suspension the user can use the method given in my earlier post ‘How to move a mouse pointer automatically in Linux to simulate user activity‘.

The Lubuntu 18.04 architecture

I suspect most Lubuntu 18.04 installations are on laptops or desktop machines with a single user, i.e. single-seat, single-user installations. In such a case, unless the user has created multiple user accounts that he/she logs into concurrently (by using ‘Switch User’, for example), the machine will never suspend unexpectedly while the user is logged in and using the session. I think the way LightDM, light-locker, systemd-logind and Xfce Power Manager have been bundled in Lubuntu 18.04 to manage suspending to RAM is a dog’s breakfast. The design apparently does not take into consideration that different people could be logged in concurrently in a single-seat installation. Try forcing people to log off so that only one person is ever logged in — it won’t happen! To be interrupted by Suspend to RAM triggered by Xfce Power Manager due to inactivity in a different session is illogical; the system should not suspend when someone is actively using the system. Therefore, in my opinion, management of suspension (and hibernation) ought to be configured and managed system-wide, not on a per-user basis, and a design should not require users to hack the installation to the extent I have described above. I was ‘scratching an itch’, but users should not have to jump through hoops to get an installation to function in a sensible manner. For all I know there may be a simpler way of achieving the functionality in Lubuntu 18.04 that I have described in this post and my previous post, but, if there is, it is not obvious. LightDM, light-locker, systemd and Xfce Power Manager are developed by different people, and functionality such as suspension and hibernation does not seem to have been considered using ‘helicopter vision’. Designing disparate applications developed separately to work together holistically is not a trivial task.

Anyway, hopefully I have fixed the problem and also ‘scratched my itch’. No more unexpected suspensions while I am using the family desktop machine!

Advertisements

About Fitzcarraldo
A Linux user with an interest in all things technical.

One Response to How to stop inactive user sessions triggering Suspend to RAM in a single-seat, multi-user installation of Lubuntu 18.04

  1. Fitzcarraldo says:

    Here is a modified version of the sessions_sleep.sh script that you can run in a terminal to see what it does:

    john@aspirexc600:~$ sudo nano sessions_sleep_testing.sh
    john@aspirexc600:~$ sudo chmod +x sessions_sleep_testing.sh
    john@aspirexc600:~$ cat sessions_sleep_testing.sh
    #!/bin/bash
    echo "****************************************"
    echo "Make sure you run this script using sudo"
    echo "****************************************"
    while true
    do
        #-------------------------------STAGE 1: FIND OUT WHO IS THE ACTIVE USER--------------------------------------
        #
        while IFS=: read -r f1 f2 f3 f4 f5 f6 f7
        # $f1 is username
        # $f2 is password ('x')
        # $f3 is UID
        # $f4 is GID
        # $f5 is UID info
        # $f6 is home directory
        # $f7 is command/shell
        do
            if [[ $f6 == *"/home/"* ]] && [[ $f7 == "/bin/bash" ]]; then
                if `loginctl list-users | grep -ve '^$\|USER\|listed' | awk -F' ' '{print $2}' | grep -q $f1`; then
                    state=`loginctl show-user $f3 | grep State | awk -F'=' '{print $2}'`
                    if [[ $state != "active" ]]; then
                        inactive_user=$f1
                        echo "STAGE 1: inactive user is $f1"
                    elif [[ $state == "active" ]]; then
                        active_user=$f1
                        echo "STAGE 1: active user is $f1"
                    fi
                fi
            fi
        done < /etc/passwd
        #
        #-------------------------------STAGE 2: ASCERTAIN USER SESSIONS---------------------------------------------
        #
        # Find idle time for each X Windows session and suspend to RAM if the active user has been idle for >=30min.
        #
        who -u | grep -v "\." > /tmp/logged-in_users
        #
        while read a b c d e f g
        #
        # $a is username
        # $b is the tty (tty1 to tty12)
        # $c is the date (yyyy-mm-dd)
        # $d is the time (hh:mm)
        # $e is the idle time (hh:mm) which does not reflect reality in this installation, for some reason
        # $f is the PID
        # $g is the display e.g. "(:1)"
        # Example: "john     tty7         2019-08-31 17:08 00:01        1624 (:0)"
        #
        do
            if [[ $(echo $b | sed 's/[^0-9]*//g') -gt 6 ]]; then
                display=$(echo $g | sed 's/[^0-9]*//g') # Use this if you launch this script from a terminal
                idle_millisecs=$(env DISPLAY=:$display sudo -u $a xprintidle)
                let idle_minutes=$idle_millisecs/60000
                if [[ $idle_minutes -ge 2 ]] && [[ $a == "$active_user" ]]; then
                    echo -n "STAGE 2: User $a session on $b triggered Suspend to RAM at "
                    date
                fi
            fi
            echo "STAGE 2: $a on $b with display :$display has been idle for $idle_minutes minutes"
        done < /tmp/logged-in_users
        rm /tmp/logged-in_users
        #
        #------------------------------------------------------------------------------------------------------------
        sleep 10 # Frequency to repeat check
    done
    

    Here is a sample of its output:

    john@aspirexc600:~$ sudo ./sessions_sleep_testing.sh 
    ****************************************
    Make sure you run this script using sudo
    ****************************************
    STAGE 1: active user is john
    STAGE 1: inactive user is stevie
    STAGE 2: john on tty7 with display :0 has been idle for 0 minutes
    STAGE 2: stevie on tty8 with display :1 has been idle for 1 minutes
    STAGE 1: active user is john
    STAGE 1: inactive user is stevie
    STAGE 2: john on tty7 with display :0 has been idle for 0 minutes
    STAGE 2: stevie on tty8 with display :1 has been idle for 1 minutes
    STAGE 1: active user is john
    STAGE 1: inactive user is stevie
    STAGE 2: john on tty7 with display :0 has been idle for 0 minutes
    STAGE 2: stevie on tty8 with display :1 has been idle for 1 minutes
    STAGE 1: active user is john
    STAGE 1: inactive user is stevie
    STAGE 2: john on tty7 with display :0 has been idle for 0 minutes
    STAGE 2: stevie on tty8 with display :1 has been idle for 1 minutes
    STAGE 1: active user is john
    STAGE 1: inactive user is stevie
    STAGE 2: john on tty7 with display :0 has been idle for 0 minutes
    STAGE 2: stevie on tty8 with display :1 has been idle for 2 minutes
    STAGE 1: active user is john
    STAGE 1: inactive user is stevie
    STAGE 2: john on tty7 with display :0 has been idle for 0 minutes
    STAGE 2: stevie on tty8 with display :1 has been idle for 2 minutes
    STAGE 1: active user is john
    STAGE 1: inactive user is stevie
    STAGE 2: john on tty7 with display :0 has been idle for 1 minutes
    STAGE 2: stevie on tty8 with display :1 has been idle for 2 minutes
    STAGE 1: active user is john
    STAGE 1: inactive user is stevie
    STAGE 2: john on tty7 with display :0 has been idle for 1 minutes
    STAGE 2: stevie on tty8 with display :1 has been idle for 2 minutes
    STAGE 1: active user is john
    STAGE 1: inactive user is stevie
    STAGE 2: john on tty7 with display :0 has been idle for 1 minutes
    STAGE 2: stevie on tty8 with display :1 has been idle for 2 minutes
    STAGE 1: active user is john
    STAGE 1: inactive user is stevie
    STAGE 2: john on tty7 with display :0 has been idle for 1 minutes
    STAGE 2: stevie on tty8 with display :1 has been idle for 2 minutes
    STAGE 1: active user is john
    STAGE 1: inactive user is stevie
    STAGE 2: john on tty7 with display :0 has been idle for 1 minutes
    STAGE 2: stevie on tty8 with display :1 has been idle for 3 minutes
    STAGE 1: active user is john
    STAGE 1: inactive user is stevie
    STAGE 2: john on tty7 with display :0 has been idle for 1 minutes
    STAGE 2: stevie on tty8 with display :1 has been idle for 3 minutes
    STAGE 1: active user is john
    STAGE 1: inactive user is stevie
    STAGE 2: User john session on tty7 triggered Suspend to RAM at Mon  2 Sep 23:43:55 BST 2019
    STAGE 2: john on tty7 with display :0 has been idle for 2 minutes
    STAGE 2: stevie on tty8 with display :1 has been idle for 3 minutes
    STAGE 1: active user is john
    STAGE 1: inactive user is stevie
    STAGE 2: User john session on tty7 triggered Suspend to RAM at Mon  2 Sep 23:44:05 BST 2019
    STAGE 2: john on tty7 with display :0 has been idle for 2 minutes
    STAGE 2: stevie on tty8 with display :1 has been idle for 3 minutes
    STAGE 1: active user is john
    STAGE 1: inactive user is stevie
    STAGE 2: john on tty7 with display :0 has been idle for 0 minutes
    STAGE 2: stevie on tty8 with display :1 has been idle for 3 minutes
    STAGE 1: active user is john
    STAGE 1: inactive user is stevie
    STAGE 2: john on tty7 with display :0 has been idle for 0 minutes
    STAGE 2: stevie on tty8 with display :1 has been idle for 3 minutes
    ^C
    

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.