]> sipb.mit.edu Git - snippets/.git/blobdiff - crondiff/crondiff.pl
Import crondiff
[snippets/.git] / crondiff / crondiff.pl
diff --git a/crondiff/crondiff.pl b/crondiff/crondiff.pl
new file mode 100644 (file)
index 0000000..b30cb46
--- /dev/null
@@ -0,0 +1,101 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Digest::MD5 qw(md5_hex);
+
+=head1 NAME
+
+crondiff.pl - Run command periodically and compare their output.
+
+=head1 DESCRIPTION
+
+crondiff.pl is designed to be run from cron periodically. It takes as
+input a list of commands to run. Each time it is invoked, it runs the
+commands listed, and sends mail to the user running the script if the
+output of that command has changed since the last run.
+
+=head1 USAGE
+
+=over 4
+
+=item
+
+Create a file C<~/.crondiff/cmdlist>, containing one line per file, of
+commands to be checked.
+
+=item
+
+Add C<crondiff.pl> to your crontab. The following example runs it
+directly out of the locker (not necessarily a good idea!) every hour:
+
+    # m h  dom mon dow   command
+      0 *  *   *   * PATH=/bin:/usr/bin: perl /mit/snippets/crondiff/crondiff.pl
+
+=back
+
+=head1 ASSUMPTIONS
+
+=over 4
+
+=item
+
+Mail goes to C<$USER@mit.edu>, for the user who runs this script.
+
+=item
+
+Mail will be sent using C<mutt -x>.
+
+=back
+
+If you don't like these assumptions, patches are welcome :)
+
+=cut
+
+my $confdir = $ENV{HOME} . "/.crondiff";
+unless($ENV{USER}) {
+    chomp(my $user = `id -un`);
+    $ENV{USER} = $user;
+};
+my $mailto = $ENV{USER} . '@mit.edu';
+my @mailer = qw(mutt -x);
+
+my $cachedir = "$confdir/cache";
+
+mkdir("$cachedir") unless -e "$cachedir";
+
+open(my $clist, "<", "$confdir/cmdlist")
+  or exit;
+
+while(my $cmd = <$clist>) {
+    chomp($cmd);
+    my $hash = md5_hex($cmd);
+    my $pid = fork;
+    if($pid == 0) {
+        # Child
+        close(STDOUT);
+        close(STDERR);
+        open(STDOUT, ">>", "$cachedir/$hash.new");
+        open(STDERR, ">>", "$cachedir/$hash.new");
+        exec($cmd);
+    }
+    waitpid($pid, 0);
+    if($? != 0) {
+        # Exited with error, should send mail, but punting for now.
+        unlink("$cachedir/$hash.new");
+        next;
+    } elsif(-f "$cachedir/$hash"
+       && system("diff -q $cachedir/$hash.new $cachedir/$hash > /dev/null 2>&1") != 0) {
+        # Output changed
+        if(($pid = fork) == 0) {
+            close(STDOUT);
+            open(STDOUT, "|-", @mailer, $mailto, '-s', "Output of [$cmd]");
+            system("date");
+            print "----\n";
+            system("diff", "-u", "-U", "0", "$cachedir/$hash", "$cachedir/$hash.new");
+            exit;
+        } else {
+            waitpid($pid, 0);
+        }
+    }
+    rename("$cachedir/$hash.new", "$cachedir/$hash");
+}