zephyr-post-receive: Translate Git trailing space highlighting
[snippets/.git] / crondiff / crondiff.pl
1 #!/usr/bin/env perl
2 use strict;
3 use warnings;
4 use Digest::MD5 qw(md5_hex);
5
6 =head1 NAME
7
8 crondiff.pl - Run command periodically and compare their output.
9
10 =head1 DESCRIPTION
11
12 crondiff.pl is designed to be run from cron periodically. It takes as
13 input a list of commands to run. Each time it is invoked, it runs the
14 commands listed, and sends mail to the user running the script if the
15 output of that command has changed since the last run.
16
17 =head1 USAGE
18
19 =over 4
20
21 =item
22
23 Create a file C<~/.crondiff/cmdlist>, containing one line per file, of
24 commands to be checked.
25
26 =item
27
28 Add C<crondiff.pl> to your crontab. The following example runs it
29 directly out of the locker (not necessarily a good idea!) every hour:
30
31     # m h  dom mon dow   command
32       0 *  *   *   * PATH=/bin:/usr/bin: perl /mit/snippets/crondiff/crondiff.pl
33
34 =back
35
36 =head1 ASSUMPTIONS
37
38 =over 4
39
40 =item
41
42 Mail goes to C<$USER@mit.edu>, for the user who runs this script.
43
44 =item
45
46 Mail will be sent using C<mutt -x>.
47
48 =back
49
50 If you don't like these assumptions, patches are welcome :)
51
52 =cut
53
54 my $confdir = $ENV{HOME} . "/.crondiff";
55 unless($ENV{USER}) {
56     chomp(my $user = `id -un`);
57     $ENV{USER} = $user;
58 };
59 my $mailto = $ENV{USER} . '@mit.edu';
60 my @mailer = qw(mutt -x);
61
62 my $cachedir = "$confdir/cache";
63
64 mkdir("$cachedir") unless -e "$cachedir";
65
66 open(my $clist, "<", "$confdir/cmdlist")
67   or exit;
68
69 while(my $cmd = <$clist>) {
70     chomp($cmd);
71     my $hash = md5_hex($cmd);
72     my $pid = fork;
73     if($pid == 0) {
74         # Child
75         close(STDOUT);
76         close(STDERR);
77         open(STDOUT, ">>", "$cachedir/$hash.new");
78         open(STDERR, ">>", "$cachedir/$hash.new");
79         exec($cmd);
80     }
81     waitpid($pid, 0);
82     if($? != 0) {
83         # Exited with error, should send mail, but punting for now.
84         unlink("$cachedir/$hash.new");
85         next;
86     } elsif(-f "$cachedir/$hash"
87        && system("diff -q $cachedir/$hash.new $cachedir/$hash > /dev/null 2>&1") != 0) {
88         # Output changed
89         if(($pid = fork) == 0) {
90             close(STDOUT);
91             open(STDOUT, "|-", @mailer, $mailto, '-s', "Output of [$cmd]");
92             system("date");
93             print "----\n";
94             system("diff", "-u", "-U", "0", "$cachedir/$hash", "$cachedir/$hash.new");
95             exit;
96         } else {
97             waitpid($pid, 0);
98         }
99     }
100     rename("$cachedir/$hash.new", "$cachedir/$hash");
101 }