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?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
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("%un", 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 [[ $idle = false && $idleTimeMillis -gt $idleAfter ]] ; then
echo "start idle" # or whatever command(s) you want to run...
idle=true
fi
if [[ $idle = true && $idleTimeMillis -lt $idleAfter ]] ; 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…
Method 2
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.
Method 3
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.
Method 4
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.
Method 5
This thread has a couple of solutions based on detecting keyboard and mouse activity. I run xprintidle from a cron job which starts every few minutes. But as they point out xprintidle is unmaintained, so you may want to install the Perl module and use that instead:
$ cpanm X11::IdleTime ... $ sleep 10; perl -MX11::IdleTime -le 'print GetIdleTime()' 9
I would use either solution by polling from cron, in a script which exits at the top if the UI hasn’t been sufficiently idle. Of course the script would need an export DISPLAY=:0.0 in order to talk to X11.
Remember that your keyboard and mouse may be inactive when watching a movie, or running a CPU-heavy task.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0