Switch users on a GNOME desktop

Works on Debian Squeeze.

<!> Update 20/May/2013, works on Debian Wheezy too (gdm3).

A simple utility to pause background processes for non-active, but logged in, desktop users.

Problem statement

On a home machine we have multiple users. GNOME via GDM provides a wonderful facility to allow multiple users to be logged in, and a user can simply switch to their desktop envinronment within seconds without needing to logoff.

However, this can represent a problem as user's sessions who are not logged in can have processes running which draw too much CPU or RAM capacity. Additionally, certain utilities running within the GNOME desktop environment popup dialog boxes, for example when a new CD is loaded. Users not logged also get these popup dialogs, and need to clear them when they next log in.

Resolution

My idea was to pause any non-active user sessions, but there didn't seem to be any obvious way to do this, and certainly Google didn't turn up any solutions.

The first part of the solution is fairly straight forward, processes can be paused by sending them the SIGINT '-STOP' using the kill (or pkill) command.

However, in order to manage this, we'd need to execute a script at some stage during the switch user process. Fortunately GDM provides the /etc/gdm3/Init/Default script which executes when a user switch is attempted. This script will run when the GDM login screen appears. At this stage ALL users process should be running to ensure they can easily login and restart their session. So the first part of the script restarts all the non-privileged users process, my script just restarts process for users whose UID is 4 numbers long (most normal users in Debian).

The script continues to run as a background process until a user's session become active (this is determined by using the /usr/bin/ck-list-sessions utility). When it has determined an active user, it goes through all the non-active sessions (also extracted from the output of /usr/bin/ck-list-sessions) and sends a -STOP signal to them to pause them then exits.

So create the following script, I put local scripts such as this into /usr/local/sbin.

/usr/local/sbin/pause_non-active_user_processes.sh follows:-

#
# What: pause_non-active_user_processes.sh
# When: 24/Nov/2011 4:46pm
# Who:  Phil Jensen
# Why:  I wanted a way to pause the processes for users in non-active
#       X-sessions so the active user doesn't have any "slow down".
#

CK_OUTPUT=/tmp/$$.ck-list-sessions.dump
X_SESSIONS=/tmp/$$.x-sessions.list
LOG_FILE=/var/log/paused_users.log

echo "Program launched on `date`" > ${LOG_FILE}
sleep 1

get_session_list() {
        # Get a list of all the sessions
        /usr/bin/ck-list-sessions > ${CK_OUTPUT}

        cat ${CK_OUTPUT} | while read LINE
        do
                echo "$LINE" | grep "^Session" > /dev/null 2>&1
                if [ $? -eq 0 ]; then
                        echo "" >> ${X_SESSIONS}
                fi
                echo -n "$LINE|" >> ${X_SESSIONS}
        done 
}

restart_all_user_processes() {
        for INACTIVE_UID in `cat ${X_SESSIONS} \
                | grep "active = FALSE" \
                | grep "unix-user = '[0-9][0-9][0-9][0-9]'" \
                | cut -d'|' -f2 \
                | cut -f2 -d "'" `
        do
                echo "Restarting processes for UID = ${INACTIVE_UID}" >> ${LOG_F
ILE}
                pkill -CONT -U ${INACTIVE_UID}
        done
}

cleanup() {
        rm /tmp/$$.*
}

# Get a list of all the ConsoleKit sessions
get_session_list
# Restart all the user processes so they can log in to their session
restart_all_user_processes

echo -n "Waiting for an active user" >> ${LOG_FILE}
while true
do
        # Get a list of all the ConsoleKit sessions
        get_session_list
        #date >> ${LOG_FILE}
        echo -n "." >> ${LOG_FILE}

        # Check for any active sessions.
        # When we have an active session the others (inactive) can be paused.
        #echo "Active sessions" >> ${LOG_FILE}
        cat ${X_SESSIONS} \
                | grep "active = TRUE" \
                | grep "unix-user = '[0-9][0-9][0-9][0-9]'" \
                | cut -d'|' -f2 \
                | cut -f2 -d "'" >> ${LOG_FILE}

        NO_ACTIVE=`cat ${X_SESSIONS} | grep "active = TRUE" \
                | grep -c "unix-user = '[0-9][0-9][0-9][0-9]'"`
        if [ ${NO_ACTIVE} -eq 1 ]; then
                echo "Active user found" >> ${LOG_FILE}
                echo "Inactive sessions" >> ${LOG_FILE}
                # Get a UID list for all the inactive users with a 
                # UID of four numbers.
                for INACTIVE_UID in `cat ${X_SESSIONS} \
                        | grep "active = FALSE" \
                        | grep "unix-user = '[0-9][0-9][0-9][0-9]'" \
                        | cut -d'|' -f2 \
                        | cut -f2 -d "'" `
                do
                        echo "Pausing processes for UID = ${INACTIVE_UID}" >> ${
LOG_FILE}
                        pkill -STOP -U ${INACTIVE_UID}
                done
                echo "Exiting on `date`!" >> ${LOG_FILE}
                cleanup
                exit 0
        fi

        cleanup

        sleep 1
done

Of course you will need to make the script executable.

chmod +x /usr/local/sbin/pause_non-active_user_processes.sh

Then insert the following line near the top of /etc/gdm3/Init/Default. I put mine just below the comments.

nohup /usr/local/sbin/pause_non-active_user_processes.sh &

To verify if this is running correctly, a log file /var/log/paused_users.log is output.

Hope this is useful.


CategoryDesktop