What a load of BS
[tpope-extra.git] / perl / away-tpope
1 #!/usr/bin/perl -w
2 # $Id$
3 # -*- perl -*- vim:set ft=perl sw=4 sts=4:
4
5 use strict;
6 use vars qw(%state @ssh $last $pipe $arg);
7 @ssh=("ssh","-a","-x","-oBatchmode=yes","-oSetupTimeOut=20");
8 $pipe = "/tmp/.away-tpope";
9
10 $arg=shift || "";
11 daemonize() if ($arg eq "-d");
12 daemon() if ($arg eq "-D");
13
14
15 if($arg eq "activity" || $arg eq "away") {
16     if(exists($ARGV[0])) {
17         die "Daemon not running\n" unless (-p $pipe);
18         my $a = join(" ",@ARGV);
19         $a =~ s/^-c$//;
20         open FIFO, ">$pipe";
21         print FIFO "$arg $a";
22     } else {
23         $arg =~ s/^away$/customaway/;
24         load();
25         print $state{$arg}."\n" if($state{$arg});
26     }
27 } else {
28     exit 1;
29 }
30
31 sub daemon {
32     open TMP, ">/tmp/.away-tpope.pid";
33     print TMP "$$\n";
34     close TMP;
35     load();
36     $SIG{'INT'} = \&quit_handler;
37     $SIG{'TERM'} = \&quit_handler;
38     $SIG{'QUIT'} = \&quit_handler;
39     $SIG{'HUP'} = \&cycle;
40     $SIG{'USR2'} = \&restart_handler;
41     die "Daemon already running\n" if (-r "/tmp/.tpope-away.pid");
42     unless (-p $pipe) {
43         unlink $pipe;
44         system("mkfifo", $pipe);
45     }
46     cycle();
47     $last=time;
48     while(1) {
49         main_loop();
50     }
51 }
52
53 sub daemonize {
54     chdir "/";
55     my $pid=fork();
56     if($pid) {
57         exit(0)
58     }
59     elsif(defined($pid)) {
60         daemon();
61     } else { exit(1); }
62 }
63
64 sub main_loop {
65     my $read="";
66     eval {
67         local $SIG{ALRM} = sub { die "alarm\n" };
68         alarm 1;
69         open FIFO, "<$pipe" and
70         $read = <FIFO>;
71         alarm 0;
72         close FIFO;
73     };
74     if($@) {
75         die unless $@ eq "alarm\n";
76         close FIFO;
77     }
78     if($read) {
79         chomp $read;
80         $read =~ s/^([a-z]*)( |$)//;
81         my $c = $1 || "";
82             if($c eq "activity") {
83                 custom_activity($read);
84             } elsif($c eq "away") {
85                 custom_away($read);
86             } else {
87                 print "Unknown command: $c\n";
88             }
89     }
90     if(time-$last >=300) {
91         local $SIG{'HUP'} = "IGNORE";
92         cycle();
93         $last=time;
94     }
95 }
96
97 sub cycle {
98     do_phone();
99     do_hosts();
100     do_schedule();
101     process_away();
102     save();
103     do_custom();
104 }
105
106 sub ping {
107     open(SOUT, ">&STDOUT");
108     close(STDOUT);
109     my $ret=!(system('ping','-q','-c','1',shift)>>8);
110     open(STDOUT, ">&SOUT");
111     close(SOUT);
112     return $ret;
113 }
114
115 sub do_schedule {
116     my @now=localtime;
117     my $now=$now[2]*60+$now[1];
118     my $familiar = 0;
119
120     open(SCHEDULE, "today --category=school|");
121     while(<SCHEDULE>) {
122         my ($begin, $end, $maxend);
123         next unless /(\d\d):(\d\d)-(\d\d):(\d\d) (.*)/;
124         my ($hs,$ms,$he,$me,$ev) = ($1, $2, $3, $4, $5);
125         $begin = $hs*60+$ms-25;
126         $maxend = $he*60+$me+5;
127         $end = ($begin+25)*3/4+($maxend-5)/4;
128         if($begin <= $now && $now < $end) {
129             $state{'class'} = $ev;
130             $familiar = 1;
131             last;
132         } elsif (($state{'class'}||'') eq $ev) {
133             $familiar = 1;
134             undef $state{'class'}
135                 if ($now > $maxend || $state{'phone'} eq "present");
136         }
137     }
138     close(SCHEDULE);
139     undef $state{'class'} unless ($familiar);
140
141
142     open(SCHEDULE, "today --category='!school !private'|");
143     $familiar = 0;
144     while(<SCHEDULE>) {
145         my ($begin, $end, $maxend);
146         next unless /(\d\d):(\d\d)-(\d\d):(\d\d) (.*)/;
147         my ($hs,$ms,$he,$me,$ev) = ($1, $2, $3, $4, $5);
148         $begin = $hs*60+$ms;
149         $maxend = $he*60+$me;
150         $end = ($begin)*3/4+($maxend)/4;
151         if($begin <= $now && $now < $end) {
152             $state{'schedule'} = $ev;
153             $familiar = 1;
154             last;
155         } elsif ($state{'schedule'} eq $ev) {
156             $familiar = 1;
157             undef $state{'schedule'}
158                 if ($now > $maxend || $state{'phone'} eq "present");
159         }
160     }
161     close(SCHEDULE);
162     undef $state{'schedule'} unless ($familiar);
163 }
164
165 sub do_hosts {
166     my @livehosts;
167     foreach my $host ("sarah", "homer", "lisa", "mona") {
168         push @livehosts, $host if(ping($host));
169     }
170     my $alive = "";
171     foreach my $host (@livehosts) {
172         $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);
173     }
174     $state{'alive'} = $alive;
175 }
176
177 sub do_phone {
178     my $phone;
179     if(!ping('mona')) {
180         $phone="unknown";
181     } else {
182         my $last_slh=`@ssh mona cat .blue/last_slh 2>/dev/null`;
183         if(!$last_slh) {
184             $phone="unknown";
185         } elsif (time-$last_slh < 600) {
186             $phone="present";
187         } else {
188             $phone="absent";
189         }
190     }
191     if($state{'phone'} eq 'absent' && $phone eq 'present') {
192         custom_away("");
193     }
194 }
195
196 sub process_away {
197     if ($state{'customaway'}) {
198         $state{'away'}=$state{'customaway'};
199     } elsif ($state{'schedule'}) {
200         $state{'away'}=$state{'schedule'};
201     } elsif ($state{'phone'} ne 'present' && $state{'class'}) {
202         $state{'away'}="Class: ".$state{'class'};
203     } elsif ($state{'alive'}) {
204         undef($state{'away'});
205     } elsif ($state{'phone'} eq 'absent') {
206         $state{'away'}="Out";
207     } elsif ($state{'phone'} ne 'absent') {
208         my @now=localtime;
209         if ($now[2] < 10) {
210             $state{'away'}="Sleeping";
211         } else {
212             $state{'away'}="Away from keyboard";
213         }
214     }
215 }
216
217 sub custom_away {
218     if($_[0]) {
219         $state{'customaway'} = $_[0];
220     } else {
221         undef $state{'customaway'};
222     }
223     process_away();
224     save();
225 }
226
227 sub custom_activity {
228     if($_[0]) {
229         $state{'activity'} = $_[0];
230     } else {
231         undef $state{'activity'};
232     }
233     save();
234 }
235
236 sub save {
237     if (defined($state{'activity'})) {
238         $state{'status'}=$state{'activity'};
239     } elsif (defined($state{'away'})) {
240         $state{'status'}=$state{'away'};
241     } elsif ($state{'alive'} eq "mona" || $state{'alive'} eq "homer") {
242         $state{'status'}="On desktop";
243     } elsif ($state{'alive'} eq "lisa") {
244         $state{'status'}="On laptop";
245     } elsif ($state{'alive'} eq "sarah") {
246         $state{'status'}="In bed";
247     }
248     open CONF, '>' . $ENV{'HOME'} . "/.away-tpope" || die $!;
249     foreach my $k (keys %state) {
250         my $val=$state{$k};
251         next unless($val);
252         $val =~ s/\n/\\n/g;
253         print CONF "$k=$val\n";
254     }
255     close CONF;
256     if(defined($state{'activity'})) {
257         open TMP, '>' . $ENV{'HOME'} . "/.activity";
258         print TMP $state{'activity'}."\n";
259         close TMP;
260     } else {
261         unlink($ENV{'HOME'} . "/.activity");
262     }
263     if(defined($state{'away'})) {
264         open TMP, '>' . $ENV{'HOME'} . "/.away";
265         print TMP $state{'away'}."\n";
266         close TMP;
267     } else {
268         unlink($ENV{'HOME'} . "/.away");
269     }
270     open TMP, '>' . $ENV{'HOME'} . "/.status";
271     print TMP $state{'status'}."\n";
272     close TMP;
273 }
274
275 sub load {
276     open CONF, '<' . $ENV{'HOME'} . "/.away-tpope" or return;
277     while(my $line=<CONF>) {
278         $line =~ s/\\n/\n/g;
279         $line =~ s/^([^=]*)=//;
280         chomp $line;
281         $state{$1}=$line;
282     }
283     close CONF;
284 }
285
286 sub restart_handler {
287     alarm 0;
288     unlink $pipe;
289     unlink "/tmp/.away-tpope.pid";
290     save();
291     exec($0,$arg)  || die $!;
292     print "Restarting $0 $arg...\n";
293 }
294
295 sub quit_handler {
296     unlink $pipe;
297     unlink "/tmp/.away-tpope.pid";
298     save();
299     exit 0;
300 }
301
302 sub do_custom {
303     system("away-actions &");
304 }