Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I want to run a command when the user becomes inactive (the system is idle). For example:

echo "You started to be inactive."

Also, when the user becomes active again (the system is not idle anymore):

echo "You started to be active, again."

I need a shell script that will do this. Is this possible without a timer/interval? Maybe some system events?

share|improve this question
2  
Can you go into a little bit deeper detail about what you actually want to achieve? –  Nils Mar 22 at 21:31
3  
How do you measure idleness? Is a user watching a movie idle (because he isn't interacting with the computer)? Is a user who is logged in remotely active? –  Gilles Mar 22 at 23:30
    
From what I know, the system has this built-in concept: idle, active etc. Is this right? –  Ionică Bizău Mar 23 at 5:33
    
@IonicăBizău: Not really. Idle in the context of the system would only mean that all processes are sleeping, that is 'waiting for something'. Although this may happen quite often on a modern system and may even be the case most of the time, it does not stay that way for long as there is always something to be done and if it is updating the clock. Idleness, especially in the context of your question, is more a function of the user than the system and even than it is usually linked to a specific session. –  Adaephon Mar 25 at 8:32
    
@Adaephon Ok. In my concept, a system is idle when the user doesn't do any action (mouse move, click, key press) at the computer for x minutes. The system becomes active when the user make the first action: clicks a mouse button, moves it, presses a key and so on. Is it possible to detect these idle*/*active states using a shell script? –  Ionică Bizău Mar 25 at 11:33

4 Answers 4

up vote 4 down vote accepted
+150

This thread on the ArchLinux forums contains a short C program that queries the xscreensaver for information how long the user has been idle, this seems to be quite close to your requirements:

#include <X11/extensions/scrnsaver.h>
#include <stdio.h>

int main(void) {
    Display *dpy = XOpenDisplay(NULL);

    if (!dpy) {
        return(1);
    }

    XScreenSaverInfo *info = XScreenSaverAllocInfo();
    XScreenSaverQueryInfo(dpy, DefaultRootWindow(dpy), info);
    printf("%u\n", info->idle);

      return(0);
}

Save this as getIdle.c and compile with

gcc -o getIdle getIdle.c -lXss -lX11

to get an executable file getIdle. This program prints the "idle time" (user does not move/click with mouse, does not use keyboard) in milliseconds, so a bash script that builds upon this could looke like this:

#!/bin/bash

idle=false
idleAfter=3000     # consider idle after 3000 ms

while true; do
  idleTimeMillis=$(./getIdle)
  echo $idleTimeMillis  # just for debug purposes.
  if [[ $idleTimeMillis -gt $idleAfter && $idle = false  ]] ; then
    echo "start idle"   # or whatever command(s) you want to run...
    idle=true
  fi

  if [[ $idleTimeMillis -lt $idleAfter && $idle = true ]] ; then
    echo "end idle"     # same here.
    idle=false
  fi
  sleep 1      # polling interval

done

This still needs regular polling, but it does everything you need...

share|improve this answer
    
Works as supposed! +1 –  Ionică Bizău Apr 3 at 12:28
    
You are worth of more points! –  Ionică Bizău Apr 3 at 12:35

TMOUT in bash will terminate an interactive session after the set number of seconds. You may use that mechanism.

You migth capture the logout by setting an according trap (I did not test that), or by using the bash-logout-scripts (~/.bash_logout).

Here is a good superuser-answer into that direction.

share|improve this answer
    
This does not address how the user would execute a command, rather than logging out, after $TMOUT seconds have passed. –  chepner Mar 23 at 3:46
    
Can you add more information to your answer? It is still unclear for me... –  Ionică Bizău Mar 23 at 6:39
    
@chepner I added some more detail to my answer. –  Nils Mar 23 at 21:53
    
Either way, this simply logs the user out after time is up. I don't think there is any way to abort the log out, either from an EXIT trap or from .bash_logout. –  chepner Mar 24 at 12:30

This is not quite what you asked for, but there is always the batch-command (usually a special invocation of the at-command and using the atd-daemon).

batch lets you cue-up a command to be run when the load-average drop below a certain limit (usually 1.5, but this can be set when starting atd). With at it's also possible to cue a job in such a way that rather than being run at a certain time; the job is just delivered to batchat that time, and first run when the load-average drops (eg. it's run as soon as the load-average drops under 1.5 sometime after 2:00am).

Unfortunately a batch-job will then run to it's end, it will not stop if the computer is no-longer idle.

+++

If you have to go the programming-route (which it looks like from the other answers), I think I'd try to make something similar to the atd or crond daemons, that monitored logged-in users and/or load-average. This daemon could then run scripts/programs from a certain directory, and start/continue or stop them (with signals) as needed be.

share|improve this answer
    
If you did this and used .lock files you could easily handle timing issues. –  mikeserv Apr 5 at 12:02

I don't know of a way to do this without polling some sort of system stats, like the other answers use a screensaver or bash idle timer, or running from .bash_logout, but here's an idea to check CPU usage.

This would still involve polling every n seconds, and if your CPU usage is under whatever amount you choose then you can script whatever you want to run. However, whatever you run could raise the CPU usage, but you could use nice on your "stuff" to not count it.

Here's a test script using top, but you could use mpstat instead, or check load averages instead?

while true
do
idle=$(top -bn2 | grep "Cpu(s)"|tail -n 1|sed "s/.*, *\([0-9.]*\)%* id.*/\1/")
echo "idle is $idle"
if [[ $idle > 90 ]]
then
    echo "idle above 90%"
    echo "Do stuff now"
else
    echo "idle below 90%"
    echo "Stop doing stuff now"
fi
sleep 1
done

That's just a script I threw together to test out reading the idle from top. You could parse /proc/stat but I think it only shows total times, and you'd need to compare results over an interval. Top has it's own problem for me (linux mint 16), on the first run it seems to never change cpustats, as if it has to wait to parse /proc/stat itself, hence the top -bn2 but in theory top -bn1 should work.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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