#!/usr/bin/perl -w # $Id$ # -*- perl -*- vim:set ft=perl sw=4 sts=4: use strict; use vars qw(%state @ssh $last $pipe); @ssh=("ssh","-a","-x","-oBatchmode=yes","-oSetupTimeOut=20"); $pipe = "/tmp/.away-tpope"; my $arg=shift || ""; daemonize() if ($arg eq "-d"); daemon() if ($arg eq "-D"); if($arg eq "activity" || $arg eq "away") { if(exists($ARGV[0])) { die "Daemon not running\n" unless (-p $pipe); my $a = join(" ",@ARGV); $a =~ s/^-c$//; open FIFO, ">$pipe"; print FIFO "$arg $a"; } else { $arg =~ s/^away$/customaway/; load(); print $state{$arg}."\n" if($state{$arg}); } } else { exit 1; } sub daemon { open TMP, ">/tmp/.away-tpope.pid"; print TMP "$$\n"; close TMP; load(); $SIG{'INT'} = \&quit_handler; $SIG{'TERM'} = \&quit_handler; $SIG{'QUIT'} = \&quit_handler; $SIG{'HUP'} = \&cycle; die "Daemon already running\n" if (-r "/tmp/.tpope-away.pid"); unless (-p $pipe) { unlink $pipe; system("mkfifo", $pipe); } cycle(); $last=time; while(1) { main_loop(); } } sub daemonize { chdir "/"; my $pid=fork(); if($pid) { exit(0) } elsif(defined($pid)) { daemon(); } else { exit(1); } } sub main_loop { my $read=""; eval { local $SIG{ALRM} = sub { die "alarm\n" }; alarm 1; open FIFO, "<$pipe" and $read = ; alarm 0; close FIFO; }; if($@) { die unless $@ eq "alarm\n"; close FIFO; } if($read) { chomp $read; $read =~ s/^([a-z]*)( |$)//; my $c = $1 || ""; if($c eq "activity") { custom_activity($read); } elsif($c eq "away") { custom_away($read); } else { print "Unknown command: $c\n"; } } if(time-$last >=300) { local $SIG{'HUP'} = "IGNORE"; cycle(); $last=time; } } sub cycle { do_schedule(); do_hosts(); do_phone(); process_away(); save(); do_custom(); } sub ping { open(SOUT, ">&STDOUT"); close(STDOUT); my $ret=!(system('ping','-q','-c','1',shift)>>8); open(STDOUT, ">&SOUT"); close(SOUT); return $ret; } sub do_schedule { my @now=localtime(time); my $now=$now[3]*60+$now[2]; open(SCHEDULE, "today --category=school|"); while() { my ($begin, $end, $maxend); next unless /(\d\d):(\d\d)-(\d\d):(\d\d) (.*)/; my ($hs,$ms,$he,$me,$ev) = ($1, $2, $3, $4, $5); $begin = $hs*60+$ms-25; $maxend = $he*60+$me+5; $end = ($begin+25)*3/4+($maxend-5)/4; if($begin <= $now && $now < $end) { $state{'class'} = $ev; last; } elsif ($state{'class'} eq $ev && $now > $maxend) { $state{'class'} = ''; } } close(SCHEDULE); open(SCHEDULE, "today --category='!school !private'|"); while() { my ($begin, $end, $maxend); next unless /(\d\d):(\d\d)-(\d\d):(\d\d) (.*)/; my ($hs,$ms,$he,$me,$ev) = ($1, $2, $3, $4, $5); $begin = $hs*60+$ms; $maxend = $he*60+$me; $end = ($begin)*3/4+($maxend)/4; if($begin <= $now && $now < $end) { $state{'schedule'} = $ev; last; } elsif ($state{'schedule'} eq $ev && $now > $maxend) { $state{'schedule'} = ''; } } close(SCHEDULE); } sub do_hosts { my @livehosts; foreach my $host ("sarah", "homer", "lisa", "mona") { push @livehosts, $host if(ping($host)); } my $alive = ""; foreach my $host (@livehosts) { $alive=$host unless(system(@ssh,$host, 'if pidof xscreensaver >/dev/null && DISPLAY=:0.0 xscreensaver-command -version >/dev/null 2>&1; then if DISPLAY=:0.0 xscreensaver-command -time 2>&1 |egrep "non-blanked|no saver status" >/dev/null; then true; else pid=`ps ax|egrep "[0-9]:[0-9][0-9] ssh marge .*(screen.*RR irc|Chat)"|sed -e "s/^ *//"|cut -d" " -f 1`; [ -f "$HOME/.irc.lock" -o -z "$pid" ] || kill $pid; false; fi; else false; fi') >> 8); } $state{'alive'} = $alive; } sub do_phone { my $phone; if(!ping('mona')) { $phone="unknown"; } else { my $last_slh=`@ssh mona cat .blue/last_slh 2>/dev/null`; if(!$last_slh) { $phone="unknown"; } elsif (time-$last_slh < 600) { $phone="present"; } else { $phone="absent"; } } if($state{'phone'} eq 'absent' && $phone eq 'present') { custom_away(""); } } sub process_away { if ($state{'customaway'}) { $state{'away'}=$state{'customaway'}; } elsif ($state{'school'}) { $state{'away'}="Class: ".$state{'school'}; } elsif ($state{'schedule'}) { $state{'away'}=$state{'schedule'}; } elsif ($state{'alive'}) { undef($state{'away'}); } elsif ($state{'phone'} eq 'absent') { $state{'away'}="Out"; } elsif ($state{'phone'} ne 'absent') { my @now=localtime; if ($now[2] < 10) { $state{'away'}="Sleeping"; } else { $state{'away'}="Away from keyboard"; } } } sub custom_away { if($_[0]) { $state{'customaway'} = $_[0]; } else { undef $state{'customaway'}; } process_away(); save(); } sub custom_activity { if($_[0]) { $state{'activity'} = $_[0]; } else { undef $state{'activity'}; } save(); } sub save { if (defined($state{'activity'})) { $state{'status'}=$state{'activity'}; } elsif (defined($state{'away'})) { $state{'status'}=$state{'away'}; } elsif ($state{'alive'} eq "mona" || $state{'alive'} eq "homer") { $state{'status'}="On desktop"; } elsif ($state{'alive'} eq "lisa") { $state{'status'}="On laptop"; } elsif ($state{'alive'} eq "sarah") { $state{'status'}="In bed"; } open CONF, '>' . $ENV{'HOME'} . "/.away-tpope" || die $!; foreach my $k (keys %state) { my $val=$state{$k}; next unless($val); $val =~ s/\n/\\n/g; print CONF "$k=$val\n"; } close CONF; if(defined($state{'activity'})) { open TMP, '>' . $ENV{'HOME'} . "/.activity"; print TMP $state{'activity'}."\n"; close TMP; } else { unlink($ENV{'HOME'} . "/.activity"); } if(defined($state{'away'})) { open TMP, '>' . $ENV{'HOME'} . "/.away"; print TMP $state{'away'}."\n"; close TMP; } else { unlink($ENV{'HOME'} . "/.away"); } open TMP, '>' . $ENV{'HOME'} . "/.status"; print TMP $state{'status'}."\n"; close TMP; } sub load { open CONF, '<' . $ENV{'HOME'} . "/.away-tpope" or return; while(my $line=) { $line =~ s/\\n/\n/g; $line =~ s/^([^=]*)=//; chomp $line; $state{$1}=$line; } close CONF; } sub quit_handler { unlink $pipe; unlink "/tmp/.away-tpope.pid"; save(); exit 0; } sub do_custom { system("away-actions &"); }