scripts/screenspy

168 строки
4.2 KiB
Perl
Executable File

#!/usr/bin/perl -w
# screenspy - Linux desktop monitoring daemon. Must be run as root.
# This script monitors one or more hardware devices (usually under /dev) for
# Linux systems running an X window manager.
#
# This script must be run as root because usually only root can read from
# hardware devices directly. You can set up a panel launcher to run this
# script with `sudo` if you previously set up the sudoers file to let your
# user run this script with no password.
#
# --Kirsle
# http://sh.kirsle.net/
use strict;
use warnings;
use threads;
use threads::shared;
#################
# Configuration #
#################
# X display to grab screenshots from.
$ENV{DISPLAY} ||= ":0.0";
# Devices to monitor.
our @devices = (
"/dev/console", # keyboard input
"/dev/input/mice", # mouse input
);
# Screenshot command.
our $screenshot = 'scrot -q 85 "%s"';
# Output directory for screenshots.
our $outdir = '/home/kirsle/Pictures/screenspy';
# Calibration: number of seconds (after no activity) for it to take a screenshot.
our $threshold = 2; # I found that 2 seconds is the best for my system, 1 second and it takes screenshots too often.
# Calibration: if there's too much activity after the threshold, take screenshots every N seconds.
our $prolonged = 5;
# If you want some indication that the app is running, put the command to run in here.
# This command will be repeatedly run. Recommended is to use zenity and put an icon in
# your system tray. Leave blank for no command.
our $notify = "zenity --notification --window-icon=/usr/share/pixmaps/gnome-spider.png --text 'Running...'";
#####################
# End Configuration #
#####################
# Only let this script run once.
&checkdupes();
# Each thread will report the time when the last event happened.
my %lastEvent : shared;
my %changed : shared;
# Spawn a thread for each device.
my @threads = ();
foreach my $dev (@devices) {
# Make sure we can read the device.
if (!-r $dev) {
system("zenity --error --text \"Don't have permission to read from device $dev\"");
}
push (@threads, threads->create (\&monitor, $dev));
}
# If they want a command run, spawn a thread for it too.
if (length $notify) {
push (@threads, threads->create (\&notify));
}
# Loop forever and let the threads do their thing.
while (1) {
# See if any events have stopped for longer than the threshold.
foreach my $dev (keys %lastEvent) {
if ($lastEvent{$dev} > 0 && time() - $lastEvent{$dev} >= $threshold) {
# take screenshot
print "$dev: idle, taking screenshot (" . (time() - $lastEvent{$dev}) . "; $threshold)\n";
&screenshot();
$lastEvent{$dev} = 0;
$changed{$dev} = 0;
}
}
select(undef,undef,undef,0.01);
}
sub monitor {
my $device = shift;
print "Monitoring device: $device\n";
# Store the time when the last event was seen.
$lastEvent{$device} = 0;
# When the lastEvent is first set (away from 0), record the time it was changed.
# This way for prolonged activity we can still take screenshots.
$changed{$device} = 0;
open (READ, $device);
my $buffer;
while (read(READ, $buffer, 1)) {
# Store the last event time
if ($lastEvent{$device} == 0) {
$changed{$device} = time();
}
$lastEvent{$device} = time();
# Too much activity?
if ($changed{$device} > 0 && time() - $changed{$device} > $prolonged) {
# Take screenshot.
print "$device: prolonged activity (> $prolonged seconds)\n";
&screenshot();
$changed{$device} = time();
}
}
close (READ);
}
sub notify {
while (1) {
system($notify);
}
}
sub screenshot {
print "Taking screenshot!\n";
my @time = localtime(time());
my $date = join(" ",
join("-",
sprintf("%04d", $time[5] + 1900),
sprintf("%02d", $time[4] + 1),
sprintf("%02d", $time[3]),
),
join(":",
sprintf("%02d", $time[2]),
sprintf("%02d", $time[1]),
sprintf("%02d", $time[0]),
),
);
my $fname = $date;
my $i = 1;
while (-f "$outdir/$fname.png") {
$fname = $date . " [$i]";
$i++;
}
my $cmd = $screenshot;
$cmd =~ s/%s/$outdir\/$fname\.png/ig;
system($cmd);
}
sub checkdupes {
my $ps = `ps aux | grep screenspy`;
foreach my $line (split(/\n/,$ps)) {
chomp $line;
next if $line =~ /grep/i;
next if $line =~ /$$/i;
if ($line) {
die "Script is already running!\n";
}
}
}