--- /dev/null
+#!/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 = <FIFO>;
+ 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(<SCHEDULE>) {
+ 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(<SCHEDULE>) {
+ 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=<CONF>) {
+ $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 &");
+}