12

I have searched around a bit for this and can't seem to find anything helpful.

I have my PC running Ubuntu 12.10 set up to suspend after 30 minutes of inactivity. I don't want to change that, it works great most of the time.

What I do want to do is disable the automatic suspend if a particular application is running. How can I do this?

The closest thing I've found so far is to add a shell script in /usr/lib/pm-utils/sleep.d which checks if the application is running and returns 1 to indicate that suspend should be prevented. But it looks like the system then gives up on suspending automatically, instead of trying again after another 30 minutes. (As far as I can tell, if I move the mouse, that restarts the timer again.) It's quite likely the application will finish after a couple of hours, and I'd rather my PC then suspended automatically if I'm not using it at that point. (So I don't want to add a call to pm-suspend when the application finishes.)

Is this possible?

EDIT: As I noted in one of the comments below, what I actually wanted was to inhibit suspend when my PC was serving files over NFS; I just wanted to focus on the "suspend" part of the question because I already had an idea how to solve the NFS part. Using the 'xdotool' idea given in one of the answers, I have come up with the following script which I run from cron every few minutes. It's not ideal because it stops the screensaver kicking in as well, but it does work. I need to have a look at why 'caffeine' doesn't correctly re-enable suspend later on, then I could probably do better. Anyway, this does seem to work, so I'm including it here in case anyone else is interested.

#!/bin/bash

# If the output of this function changes between two successive runs of this
# script, we inhibit auto-suspend.
function check_activity() {
  /usr/sbin/nfsstat --server --list
}

# Prevent the automatic suspend from kicking in. 
function inhibit_suspend() {
  # Slightly jiggle the mouse pointer about; we do a small step and
  # reverse step to try to stop this being annoying to anyone using the
  # PC. TODO: This isn't ideal, apart from being a bit hacky it stops
  # the screensaver kicking in as well, when all we want is to stop
  # the PC suspending. Can 'caffeine' help?
  export DISPLAY=:0.0
  xdotool mousemove_relative --sync --  1  1
  xdotool mousemove_relative --sync -- -1 -1
}

LOG="$HOME/log/nfs-suspend-blocker.log"
ACTIVITYFILE1="$HOME/tmp/nfs-suspend-blocker.current"
ACTIVITYFILE2="$HOME/tmp/nfs-suspend-blocker.previous"

echo "Started run at $(date)" >> "$LOG"
if [ ! -f "$ACTIVITYFILE1" ]; then
  check_activity > "$ACTIVITYFILE1"
  exit 0;
fi

/bin/mv "$ACTIVITYFILE1" "$ACTIVITYFILE2"
check_activity > "$ACTIVITYFILE1"

if cmp --quiet "$ACTIVITYFILE1" "$ACTIVITYFILE2"; then
  echo "No activity detected since last run" >> "$LOG"
else
  echo "Activity detected since last run; inhibiting suspend" >> "$LOG"
  inhibit_suspend
fi

EDIT 2: The script above works but thanks to another comment below, I am now using this pair of scripts, which have the advantage of allowing the screensaver to kick in while I am inhibiting suspend. The first is /usr/lib/pm-utils/sleep.d/000nfs-inhibit, which will prevent a suspend attempt if an inhibit file exists:

#!/bin/sh

LOG="/home/zorn/log/nfs-suspend-blocker.log"
INHIBITFILE="/home/zorn/tmp/nfs-suspend-blocker.inhibit"

echo "$0: Started run at $(date), arguments: $*" >> "$LOG"
if [ "$1" = "suspend" ] && [ -f "$INHIBITFILE" ]; then
  echo "$0: Inhibiting suspend" >> "$LOG"
  exit 1
fi
exit 0

The second is a modified version of the previous nfs-suspend-blocker script and should still be run from cron. It now follows the strategy outlined in the comment below:

```bash
#!/bin/bash

# This works in tandem with /usr/lib/pm-utils/sleep.d/000nfs-inhibit, which
# will prevent a suspend occurring if $INHIBITFILE is present. Once it prevents
# a suspend, it appears that it requires some "user activity" to restart the
# timer which will cause a subsequent suspend attempt, so in addition to
# creating or removing $INHIBITFILE this script also jiggles the mouse after
# removing the file to restart the timer.

# If the output of this function changes between two successive runs of this
# script, we inhibit auto-suspend.
function check_activity() {
    /usr/sbin/nfsstat --server --list
}

# Slightly jiggle the mouse pointer about; we do a small step and reverse step
# to try to stop this being annoying to anyone using the PC.
function jiggle_mouse() {
  export DISPLAY=:0.0
  xdotool mousemove_relative --sync --  1  1
  xdotool mousemove_relative --sync -- -1 -1
}

LOG="$HOME/log/nfs-suspend-blocker.log"
ACTIVITYFILE1="$HOME/tmp/nfs-suspend-blocker.current"
ACTIVITYFILE2="$HOME/tmp/nfs-suspend-blocker.previous"
INHIBITFILE="$HOME/tmp/nfs-suspend-blocker.inhibit"

echo "$0: Started run at $(date)" >> "$LOG"
if [ ! -f "$ACTIVITYFILE1" ]; then
  check_activity > "$ACTIVITYFILE1"
  exit 0;
fi

/bin/mv "$ACTIVITYFILE1" "$ACTIVITYFILE2"
check_activity > "$ACTIVITYFILE1"

if cmp --quiet "$ACTIVITYFILE1" "$ACTIVITYFILE2"; then
  echo "$0: No activity detected since last run" >> "$LOG"
  if [ -f "$INHIBITFILE" ]; then
    echo "$0: Removing suspend inhibit file and jiggling mouse" >> "$LOG"
    /bin/rm "$INHIBITFILE"
    jiggle_mouse
  fi
else
  echo "$0: Activity detected since last run; inhibiting suspend" >> "$LOG"
  touch "$INHIBITFILE"
fi
1
  • Rather than clutter the question you should put your two solutions to the problem as an answer below.
    – Cas
    Feb 8, 2013 at 22:24

5 Answers 5

8

A program to keep your computer awake is Caffeine. I would make a .bash_aliases file to also call caffeine when your original code is called.

alias newname="origcode && caffeine"

Depending on which code you are trying to keep your computer awake for, you'll have to make a custom script that includes killing caffeine when the other code is stopped. Some more details about the particular code would help.

Update: A simpler way would be to run xdotool, which can be installed with sudo apt-get install xdotool. You can write a script that is called when your target code is opened and then use the sleep command for 29 minutes and then run xdotool key a or something arbitrary to keep the computer awake.

3
  • 2
    For xdo, it's probably better to pick a key that doesn't inherently do something. Pressing shift, for example, will wake the screen without typing on the active window.
    – Oli
    Nov 26, 2012 at 22:59
  • 1
    Thank you. I was deliberately hiding a little bit of detail in the original question - what I explicitly want to do is stop my PC (which is serving media content via NFS) suspending while my media centre is accessing it. If you know a way to specifically address this that would be great, otherwise I can see a couple of avenues which will allow this to work via Caffeine (e.g. the media centre will ssh into my PC every 10 minutes and run a shell script which sleeps for 15 minutes).
    – Zorn
    Nov 26, 2012 at 23:23
  • 1
    I have tried Caffeine and it seems to work except that once the running program disappears, the Caffeine output says suspend is no longer inhibited but I left it for twice the inactivity period and although the screen suspended the PC never did. I will play around with this further, although the xdo route is probably going to be even more convenient and I will try that first. PS Is there any way to insert a newline into a comment?!
    – Zorn
    Nov 26, 2012 at 23:24
5

If

  1. A script in /usr/lib/pm-utils/sleep.d can check if the application is running and return 1 to indicate that suspend should be prevented.
  2. The problem of "the system then gives up on suspending automatically, instead of trying again after another 30 minutes" is solved by moving the mouse which restarts the timer again (I hope I've understood properly what you mean by this)

then why not just jiggle mouse pointer after the application terminates.

To summarise:

  1. Use sleep.d to keep system from suspending.
  2. Write a script that jiggles mouse once.
  3. Call 'Long-running-script && mousejiggle'

This will not hinder the screensaver.

The only problem is that it will be 30 minutes after the process terminates when the system suspends. This is the case with your 'EDIT' solution also.

PS: I was looking for a solution to a similar problem when I learnt of xdotool from this page. So, thanks. Hope this helps.

2
  • Ingenious, thank you! I will give that a go this weekend.
    – Zorn
    Nov 30, 2012 at 22:33
  • The hint concerning pm-utils helped. Note that the package pm-utils needs to be installed in order for this to work -- the directory itself may exist even if the package is not installed.
    – krlmlr
    Mar 8, 2015 at 16:43
2

Just extracting out the core trick here for my needs, I wind up with the following: On /usr/local/bin/nosleep I have this script:

#!/bin/bash
export DISPLAY=:0.0
while true; do xdotool mousemove_relative --sync --  1  1 ; xdotool mousemove_relative --sync -- -1 -1; sleep 60; done

This is very handy! I couldn't even see it working so I changed '1' to '100' and added a sleep 2; between the xdtool calls, just to verify to works. It sure does.

1

Although EDIT 2 lets the screensaver kick-in and resumes autosuspend service on removal of inhibit file, as noted above it will be 30 minutes after the file is removed when the system will suspend.

One possible solution is to disable the inbuilt auto-screensaver and auto-suspend functionality and implement them on our own, and choose the behaviour of the timer as required. The command xprintidle (you may have to install this) prints the number of milliseconds for which there has been no keyboard or mouse activity. This opens up several possibilities. I've implemented the following inactivity manager in python (not too much of a bash scripter). Features include setting the command, timeout and inhibit file(I've called it lock) for screensaver and/or autosuspend. In addition there is option to choose whether inactivity timer should restart when inhibit file is removed or not (behaviour can be different for suspend and screensaver). I've tried to make the usage clear in the notes but if something is unclear do ask.

#!/usr/bin/python

#Notes:##################

#   1. All TIMEOUTs are specified in seconds
#   2. 0 or negative TIMEOUT disables a particular action.
#   3. If an actionCOMMAND (like pm-suspend) requires 'sudo'ing, make them 'sudo'able without password. Alternatively, you may run this script in sudo mode, and make this script sudoable without password. https://askubuntu.com/questions/159007/specific-sudo-commands-without-password
#   4. 'action'_timer_starts_... option: True - if a lock file is created and then removed, inactivity timer (for that action) restarts at the time of deletion of lock. False - doesn't restart.
#   5. screensaverCOMMAND can be screen-lock (security) or screen-off (power saving) or both. To do both, but at different times (I can't see any reason to do so) extend this script from two actions (screensaver, autosuspend) to three (screen-lock, screen-off, autosuspend).

#########################

import os
import time
import threading
import subprocess

HOME = os.getenv('HOME') + '/'

#Configuration###########

screensaverCOMMAND = "gnome-screensaver-command --lock && xset -display :0.0 +dpms dpms force off"
autosuspendCOMMAND = "gnome-screensaver-command --lock && sudo pm-suspend"

screensaverTIMEOUT = 10*60
autosuspendTIMEOUT = 20*60

screensaverLOCK = HOME + ".inactivitymanager/screensaverLOCK"
autosuspendLOCK = HOME + ".inactivitymanager/autosuspendLOCK"

screensaver_timer_starts_only_after_lockfile_is_deleted = False
autosuspend_timer_starts_only_after_lockfile_is_deleted = False

#########################

def stayOn():
    print "inactivitymanager is running..."
    try:
        while True:
            time.sleep(10)
    except:
        print "Closed."

class inactivity_action(threading.Thread):
    def __init__(self, command, timeout, lock, timer_starts_blah):
        threading.Thread.__init__(self)
        self.daemon = True
        self.command = command
        self.timeout = timeout
        self.lock = lock
        self.timer_starts_blah = timer_starts_blah
    def run(self):
        if not(self.timer_starts_blah):
            while True:
                try:
                    while True:
                        time.sleep(1)
                        f = open(self.lock, 'r')
                        f.close()
                except IOError:
                    xidletime = int(subprocess.Popen('xprintidle', stdout = subprocess.PIPE).communicate()[0])/1000
                    if xidletime > self.timeout:
                        os.system(self.command)
                    else:
                        time.sleep(self.timeout - xidletime + 2)
        else:
            lockremovetime = 0
            while True:
                lockdetected = False
                try:
                    while True:
                        time.sleep(1)
                        f = open(self.lock, 'r')
                        f.close()
                        lockdetected = True
                except IOError: #Will enter this section if/when lockfile is/becomes absent
                    xidletime = int(subprocess.Popen('xprintidle', stdout = subprocess.PIPE).communicate()[0])/1000
                    if lockdetected:
                        lockremovetime = int(time.time())
                    timesincelockremove = int(time.time()) - lockremovetime
                    if min(xidletime, timesincelockremove) > self.timeout:
                        os.system(self.command)

if screensaverTIMEOUT > 0:
    inactivity_screensaver = inactivity_action(screensaverCOMMAND, screensaverTIMEOUT, screensaverLOCK, screensaver_timer_starts_only_after_lockfile_is_deleted)
    inactivity_screensaver.start()

if autosuspendTIMEOUT > 0:
    inactivity_autosuspend = inactivity_action(autosuspendCOMMAND, autosuspendTIMEOUT, autosuspendLOCK, autosuspend_timer_starts_only_after_lockfile_is_deleted)
    inactivity_autosuspend.start()

stayOn()

Usage:

  1. Just add inactivitymanager & to .profile or .xsessionrc in home directory (see which one works for you. Don't add in both, else two instances of this script will run simultaneously, something I've not handled. I guess it is in these details that the mainstream implementations trump custom ones).
  2. You may have to install xprintidle.

How the inhibit file gets there is left to the imagination of the user for now (if I bring myself to implement a daemon for this, I'll put that in an EDIT to this answer). You (OP) of course have solved it for your case. One pitfall to avoid when trying to inhibit suspend for more than one process is deleting the lock file when one process terminates while another is still running. Alternatively, the script can be edited slightly to inhibit suspend if some file exists in a particular directory (a lock directory). This way each process can have its own lock file.

Notes:

  1. This script is pretty light on the processor and memory. But removing the time.sleep(1) s in the code might cause trouble - haven't checked though.
  2. pm-suspend requires sudo permissions. To pm-suspend without giving password checkout How do I run specific sudo commands without a password?. Alternatively you may run this script in sudo mode, and make this script sudoable without password (Not a problem if you running script as root)
  3. The script might run into trouble if timeouts are set to be less than ~10 seconds (Haven't check where exactly the trouble starts must be less than 5, I guess). This can be handled by removing some time.sleep(1) s at the expense of system resources. Don't think anybody will need this though.
  4. Because we have a handle on the timers, no mousejiggles required!
1

Systemd supports this through the systemd-inhibit command. Example from this page:

# systemd-inhibit wodim foobar.iso

This burns the ISO image foobar.iso on a CD using wodim(1), and inhibits system sleeping, shutdown and idle while doing so.

1
  • Strangely, but on archlinux (and it seems that i'm not the only one affected) it doesn't save from suspend/sleep. To work around this issue, I've created a wrapper around the main sleep service. The solution is simple and just delays sleep until inhibit lock is released. For more details, please, check askubuntu.com/a/1467178/106514.
    – diabolusss
    May 10, 2023 at 4:51

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .