Committing all changes before migrating servers
[tpope-extra.git] / perl / away-tpope
index dd26efb682f04a53f4d01f43029d174ec93cd643..a82fb848cea1334e2cd173ece5bd96f90ac1365f 100755 (executable)
@@ -3,8 +3,8 @@
 # -*- perl -*- vim:set ft=perl sw=4 sts=4:
 
 use strict;
-use vars qw(%state @ssh $last $pipe $arg);
-@ssh=("ssh","-a","-x","-oBatchmode=yes","-oSetupTimeOut=20");
+use vars qw(%state @ssh $last $pipe $arg $slow);
+@ssh=("ssh","-a","-x","-oBatchmode=yes","-oSetupTimeOut=20","-qq");
 $pipe = "/tmp/.away-tpope";
 
 $arg=shift || "";
@@ -12,7 +12,7 @@ daemonize() if ($arg eq "-d");
 daemon() if ($arg eq "-D");
 
 
-if($arg eq "activity" || $arg eq "away") {
+if($arg eq "activity" || $arg eq "away" || $arg eq "out" || $arg eq "check") {
     if(exists($ARGV[0])) {
        die "Daemon not running\n" unless (-p $pipe);
        my $a = join(" ",@ARGV);
@@ -20,7 +20,6 @@ if($arg eq "activity" || $arg eq "away") {
        open FIFO, ">$pipe";
        print FIFO "$arg $a";
     } else {
-       $arg =~ s/^away$/customaway/;
        load();
        print $state{$arg}."\n" if($state{$arg});
     }
@@ -29,6 +28,7 @@ if($arg eq "activity" || $arg eq "away") {
 }
 
 sub daemon {
+    die "Daemon already running?\n" if (-e "/tmp/.away-tpope.pid");
     open TMP, ">/tmp/.away-tpope.pid";
     print TMP "$$\n";
     close TMP;
@@ -36,15 +36,17 @@ sub daemon {
     $SIG{'INT'} = \&quit_handler;
     $SIG{'TERM'} = \&quit_handler;
     $SIG{'QUIT'} = \&quit_handler;
-    $SIG{'HUP'} = \&cycle;
+    $SIG{'HUP'} = \&hup_handler;
     $SIG{'USR2'} = \&restart_handler;
     die "Daemon already running\n" if (-r "/tmp/.tpope-away.pid");
     unless (-p $pipe) {
        unlink $pipe;
        system("mkfifo", $pipe);
+       chmod 0600, $pipe;
     }
+    $last=0;
+    $slow=1;
     cycle();
-    $last=time;
     while(1) {
        main_loop();
     }
@@ -83,24 +85,38 @@ sub main_loop {
                custom_activity($read);
            } elsif($c eq "away") {
                custom_away($read);
+           } elsif($c eq "out") {
+               custom_out($read);
+           } elsif($c eq "check") {
+               host_check($read);
            } else {
                print "Unknown command: $c\n";
            }
     }
-    if(time-$last >=300) {
-       local $SIG{'HUP'} = "IGNORE";
+    if(time-$last >= 240) {
        cycle();
-       $last=time;
     }
 }
 
 sub cycle {
-    do_phone();
-    do_hosts();
+    local $SIG{'HUP'} = sub {$last = 1};
+    do_chat();
+    if((!$last) || $slow++ ) {
+       do_hosts();
+       do_power();
+       do_phone();
+       $slow=0;
+    }
+    eval_chat();
     do_schedule();
-    process_away();
+    do_decision();
     save();
     do_custom();
+    if($last==1) {
+       $last=0;
+    } else {
+       $last=int(time/60)*60;
+    }
 }
 
 sub ping {
@@ -122,13 +138,17 @@ sub do_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;
+       $ev =~ s/ *\[.*$//;
+       $begin = $hs*60+$ms-10;
+       $maxend = $he*60+$me+10;
+       $end = ($begin+10)*3/4+($maxend-10)/4;
        if($begin <= $now && $now < $end) {
            $state{'class'} = $ev;
+           internal_out("School");
            $familiar = 1;
            last;
+       } elsif($begin-35<=$now && $now<$end && $state{'phone'} eq "absent") {
+           internal_out("School");
        } elsif (($state{'class'}||'') eq $ev) {
            $familiar = 1;
            undef $state{'class'}
@@ -138,13 +158,13 @@ sub do_schedule {
     close(SCHEDULE);
     undef $state{'class'} unless ($familiar);
 
-
     open(SCHEDULE, "today --category='!school !private'|");
     $familiar = 0;
     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);
+       $ev =~ s/ *\[.*$//;
        $begin = $hs*60+$ms;
        $maxend = $he*60+$me;
        $end = ($begin)*3/4+($maxend)/4;
@@ -152,7 +172,7 @@ sub do_schedule {
            $state{'schedule'} = $ev;
            $familiar = 1;
            last;
-       } elsif ($state{'schedule'} eq $ev) {
+       } elsif (($state{'schedule'}||'') eq $ev) {
            $familiar = 1;
            undef $state{'schedule'}
                if ($now > $maxend || $state{'phone'} eq "present");
@@ -162,51 +182,144 @@ sub do_schedule {
     undef $state{'schedule'} unless ($familiar);
 }
 
+sub do_chat {
+    if(-r ($ENV{'HOME'} . "/.chat")) {
+       open TMP, $ENV{'HOME'} . "/.chat";
+       my $chat = join ("", <TMP>);
+       close TMP;
+       chomp $chat;
+       $state{'chat'} = $chat;
+    } else {
+       undef $state{'chat'};
+    }
+}
+
+sub eval_chat {
+    if(($state{'chat'}||'') =~ /^(tpope-\d+|jmwaller|arwen|george)$/) {
+       internal_out("Work",3*60*60);
+    } elsif(($state{'chat'}||'') eq "accd") {
+       #internal_out("School",30*60);
+    }
+}
+
 sub do_hosts {
-    my @livehosts;
-    foreach my $host ("sarah", "homer", "lisa", "mona") {
-       push @livehosts, $host if(ping($host));
+    my (@check) = ("tobias", "lucille", "lindsay", "buster");
+    my (@uphosts, @livehosts, $host, $hostlist);
+    if(($_[0] || 0) == 1) {
+       $hostlist=$state{'hosts'};
+       $hostlist=~s/0\S* ?//g;
+       @livehosts = split / /, $hostlist;
+    } else {
+       $hostlist="";
+       foreach $host (@check) {
+           push @uphosts, $host if(ping($host));
+       }
+       foreach $host (@uphosts) {
+           if(is_alive($host)) {
+               push @livehosts,$host;
+               $hostlist="$host $hostlist";
+           } else {
+               $hostlist="0$host $hostlist";
+           }
+       }
+       $hostlist=~s/ $//;
+       $state{'hosts'}=$hostlist;
     }
-    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);
+    if(scalar @livehosts == 0) {
+       $state{'alive'} = '';
+    } elsif(scalar @livehosts > 1 && $state{'chat'}) {
+       foreach $host (@livehosts) {
+           if ($host eq $state{'chat'}) {
+               $state{'alive'} = $host;
+               return;
+           }
+       }
+    }
+    $state{'alive'}=$livehosts[0];
+}
+
+sub is_alive {
+    my $ret;
+    eval {
+       local $SIG{ALRM} = sub { die "alarm\n" };
+       alarm(30);
+       $ret=!(system(@ssh,shift, '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 gob .*(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);
+       alarm(0);
+    };
+    if($@) {
+       undef $ret;
+       die unless $@ eq "alarm\n";
     }
-    $state{'alive'} = $alive;
+    return $ret;
 }
 
 sub do_phone {
     my $phone;
-    if(!ping('mona')) {
+    if(!ping('tobias')) {
        $phone="unknown";
     } else {
-       my $last_slh=`@ssh mona cat .blue/last_slh 2>/dev/null`;
+       my $last_slh=`@ssh tobias cat .blue/last_slh 2>/dev/null`;
        if(!$last_slh) {
            $phone="unknown";
-       } elsif (time-$last_slh < 600) {
+       } elsif (time-$last_slh < 540) {
            $phone="present";
        } else {
            $phone="absent";
        }
     }
-    if($state{'phone'} eq 'absent' && $phone eq 'present') {
-       custom_away("");
+    if(($state{'phone'}||"") ne 'present' && $phone eq 'present') {
+       custom_out("");
+       internal_out("");
     }
+    $state{'phone'} = $phone;
 }
 
-sub process_away {
-    if ($state{'customaway'}) {
+sub do_power {
+    open TMP, "upsc milhouse\@localhost 2>/dev/null|";
+    my $ups='';
+    while(<TMP>) {
+       chomp;
+       if(/status: (.*)/i) {
+           $ups=$1;
+       }
+    }
+    close TMP;
+    $state{'ups'}=$ups;
+}
+
+sub do_decision {
+    custom_away("")
+       if ($state{'customawayexp'} && $state{'customawayexp'}<time);
+    custom_out("")
+       if ($state{'customoutexp'} && $state{'customoutexp'}<time);
+    internal_out("")
+       if ($state{'internaloutexp'} && $state{'internaloutexp'}<time);
+    custom_activity("")
+       if ($state{'customactivityexp'} && $state{'customactivityexp'}<time);
+    if (exists($state{'customactivity'})) {
+       $state{'activity'}=$state{'customactivity'};
+    } elsif ($state{'ups'} eq 'OB' || $state{'ups'} eq 'LB') {
+       $state{'activity'}="Power outage";
+    } else {
+       undef $state{'activity'};
+    }
+    if ($state{'customout'} && $state{'phone'} eq 'absent') {
+       $state{'away'}=$state{'customout'};
+    } elsif ($state{'customaway'}) {
        $state{'away'}=$state{'customaway'};
     } elsif ($state{'schedule'}) {
        $state{'away'}=$state{'schedule'};
     } elsif ($state{'phone'} ne 'present' && $state{'class'}) {
        $state{'away'}="Class: ".$state{'class'};
+    } elsif ($state{'internalout'} && $state{'phone'} eq 'absent') {
+       $state{'away'}=$state{'internalout'};
     } 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) {
+       if (0 <= $now[2] && $now[2] < 7) {
            $state{'away'}="Sleeping";
        } else {
            $state{'away'}="Away from keyboard";
@@ -214,22 +327,78 @@ sub process_away {
     }
 }
 
+sub host_check {
+    return undef unless($_[0]);
+    my $host=$_[0];
+    if($host eq "phone") {
+       do_phone();
+       do_decision();
+       save();
+       do_custom();
+    } elsif($host eq "chat") {
+       do_chat();
+       do_hosts(1);
+       do_decision();
+       save();
+    } elsif($state{'hosts'} =~ /0$host/ && is_alive($_[0])) {
+       $state{'hosts'} =~ s/0$host/$host/;
+       do_hosts(1);
+       do_decision();
+       save();
+       do_custom();
+    } elsif($state{'hosts'} =~ /(?!0)$host/ && ! is_alive($_[0])) {
+       $state{'hosts'} =~ s/1?$host/0$host/;
+       do_hosts(1);
+       do_decision();
+       save();
+    }
+}
+
 sub custom_away {
     if($_[0]) {
        $state{'customaway'} = $_[0];
+       $state{'customawayexp'} = time+48*60*60;
     } else {
        undef $state{'customaway'};
+       undef $state{'customawayexp'};
     }
-    process_away();
+    do_decision();
+    save();
+}
+
+sub custom_out {
+    if($_[0]) {
+       $state{'customout'} = $_[0];
+       $state{'customoutexp'} = time+14*24*60*60;
+    } else {
+       undef $state{'customout'};
+       undef $state{'customoutexp'};
+    }
+    do_decision();
+    save();
+}
+
+sub internal_out {
+    if($_[0]) {
+       $state{'internalout'} = $_[0];
+       $state{'internaloutexp'} = time+($_[1]||150*60);
+    } else {
+       undef $state{'internalout'};
+       undef $state{'internaloutexp'};
+    }
+    do_decision();
     save();
 }
 
 sub custom_activity {
     if($_[0]) {
-       $state{'activity'} = $_[0];
+       $state{'customactivity'} = $_[0];
+       $state{'customactivityexp'} = time+12*60*60;
     } else {
-       undef $state{'activity'};
+       undef $state{'customactivity'};
+       undef $state{'customactivityexp'};
     }
+    do_decision();
     save();
 }
 
@@ -238,11 +407,11 @@ sub save {
        $state{'status'}=$state{'activity'};
     } elsif (defined($state{'away'})) {
        $state{'status'}=$state{'away'};
-    } elsif ($state{'alive'} eq "mona" || $state{'alive'} eq "homer") {
+    } elsif ($state{'alive'} eq "tobias" || $state{'alive'} eq "lucille") {
        $state{'status'}="On desktop";
-    } elsif ($state{'alive'} eq "lisa") {
+    } elsif ($state{'alive'} eq "lindsay") {
        $state{'status'}="On laptop";
-    } elsif ($state{'alive'} eq "sarah") {
+    } elsif ($state{'alive'} eq "buster") {
        $state{'status'}="In bed";
     }
     open CONF, '>' . $ENV{'HOME'} . "/.away-tpope" || die $!;
@@ -250,7 +419,8 @@ sub save {
        my $val=$state{$k};
        next unless($val);
        $val =~ s/\n/\\n/g;
-       print CONF "$k=$val\n";
+       $val =~ s/"/\\"/g;
+       print CONF "$k=\"$val\"\n";
     }
     close CONF;
     if(defined($state{'activity'})) {
@@ -274,15 +444,25 @@ sub save {
 
 sub load {
     open CONF, '<' . $ENV{'HOME'} . "/.away-tpope" or return;
+    undef $state{'customactivity'};
+    undef $state{'customaway'};
+    undef $state{'customout'};
+    undef $state{'internalout'};
     while(my $line=<CONF>) {
        $line =~ s/\\n/\n/g;
-       $line =~ s/^([^=]*)=//;
+       $line =~ s/\\"/"/g;
+       $line =~ s/"$//;
+       $line =~ s/^([^=]*)="?//;
        chomp $line;
        $state{$1}=$line;
     }
     close CONF;
 }
 
+sub hup_handler {
+    $last=1;
+}
+
 sub restart_handler {
     alarm 0;
     unlink $pipe;