#!/usr/bin/perl -w # $Id$ # -*- perl -*- vim:set ft=perl sw=4 sts=4: use strict; use vars qw(%state @ssh $last $pipe $arg $slow); @ssh=("ssh","-a","-x","-oBatchmode=yes","-oSetupTimeOut=20","-qq"); $pipe = "/tmp/.away-tpope"; $arg=shift || ""; daemonize() if ($arg eq "-d"); daemon() if ($arg eq "-D"); 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); $a =~ s/^-c$//; open FIFO, ">$pipe"; print FIFO "$arg $a"; } else { load(); print $state{$arg}."\n" if($state{$arg}); } } else { exit 1; } sub daemon { die "Daemon already running?\n" if (-e "/tmp/.away-tpope.pid"); 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'} = \&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(); 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); } elsif($c eq "out") { custom_out($read); } elsif($c eq "check") { host_check($read); } else { print "Unknown command: $c\n"; } } if(time-$last >= 240) { cycle(); } } sub cycle { local $SIG{'HUP'} = sub {$last = 1}; do_chat(); if((!$last) || $slow++ ) { do_hosts(); do_power(); do_phone(); $slow=0; } eval_chat(); do_schedule(); do_decision(); save(); do_custom(); if($last==1) { $last=0; } else { $last=int(time/60)*60; } } 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; my $now=$now[2]*60+$now[1]; my $familiar = 0; 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); $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'} if ($now > $maxend || $state{'phone'} eq "present"); } } close(SCHEDULE); undef $state{'class'} unless ($familiar); open(SCHEDULE, "today --category='!school !private'|"); $familiar = 0; 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); $ev =~ s/ *\[.*$//; $begin = $hs*60+$ms; $maxend = $he*60+$me; $end = ($begin)*3/4+($maxend)/4; if($begin <= $now && $now < $end) { $state{'schedule'} = $ev; $familiar = 1; last; } elsif (($state{'schedule'}||'') eq $ev) { $familiar = 1; undef $state{'schedule'} if ($now > $maxend || $state{'phone'} eq "present"); } } close(SCHEDULE); undef $state{'schedule'} unless ($familiar); } sub do_chat { if(-r ($ENV{'HOME'} . "/.chat")) { open TMP, $ENV{'HOME'} . "/.chat"; my $chat = join ("", ); 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 (@check) = ("tobias", "lindsay"); 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; } 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 michael .*(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"; } return $ret; } sub do_phone { my $phone; if(!ping('tobias')) { $phone="unknown"; } else { my $last_slh=`@ssh tobias cat .blue/last_slh .blue/last_gm 2>/dev/null`; if(!$last_slh) { $phone="unknown"; } elsif (time-$last_slh < 1140) { $phone="present"; } else { $phone="absent"; } } if(($state{'phone'}||"") ne 'present' && $phone eq 'present') { custom_out(""); internal_out(""); } $state{'phone'} = $phone; } sub do_power { open TMP, "upsc sally\@localhost 2>/dev/null|"; my $ups=''; while() { chomp; if(/status: (.*)/i) { $ups=$1; } } close TMP; $state{'ups'}=$ups; } sub do_decision { custom_away("") if ($state{'customawayexp'} && $state{'customawayexp'}' . $ENV{'HOME'} . "/.away-tpope" || die $!; foreach my $k (keys %state) { my $val=$state{$k}; next unless($val); $val =~ s/\n/\\n/g; $val =~ s/"/\\"/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; undef $state{'customactivity'}; undef $state{'customaway'}; undef $state{'customout'}; undef $state{'internalout'}; while(my $line=) { $line =~ s/\\n/\n/g; $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; unlink "/tmp/.away-tpope.pid"; save(); exec($0,$arg) || die $!; print "Restarting $0 $arg...\n"; } sub quit_handler { unlink $pipe; unlink "/tmp/.away-tpope.pid"; save(); exit 0; } sub do_custom { system("away-actions &"); }