Runcheckdir now works as a well-behaved system daemon
[chksht.git] / runcheckdir
index 7dbfa2f..15d7762 100755 (executable)
@@ -1,94 +1,85 @@
 #!/usr/bin/perl
-use File::Basename;
+use Getopt::Long;
+use Cwd qw/abs_path/;
+use POSIX qw/setsid setuid setgid/;
 use strict;
 
+my ($nodaemon, $verbose, $pidfile, $logfile, $user, $group, $uid, $gid);
+$pidfile = '/var/run/runcheckdir.pid';
+$logfile = '/var/log/runcheckdir.log';
 
-my $dir = shift;
-unless (-d $dir) {
-       print "Arg is not a directory";
+GetOptions(
+       "nodaemon" => \$nodaemon,
+       "verbose"  => \$verbose,
+       "pidfile=s"  => \$pidfile,
+       "logfile=s"  => \$logfile,
+       "user=s"  => \$user,
+       "group=s"  => \$group,
+);
+
+my $dir = abs_path(shift);
+unless ($dir) {
+       print "No directory specified\n";
        exit 1;
 }
-my $me = basename $dir;
-my ($status, $details);
-
-sub do_command {
-       my $command = shift;
-       $command =~ /^(\w+)(\s+(.*))?$/;
-       my ($do, $args) = ($1, $3);
-       if ($do eq 'mail') {
-               open MAIL, '|-', qq!mail -s "$me $status" $args! or die "Could not mail";
-               print MAIL $details,"\n";
-               close MAIL;
-       } elsif ($do eq 'exec') {
-               system($args);
-       } elsif ($do eq 'pipe') {
-               open PIPE, '|-', $args;
-               print PIPE "$status\n";
-               print PIPE $details;
-               close PIPE;
-       }
+unless (-d $dir) {
+       print "$dir is not a directory\n";
+       exit 1;
 }
 
+$uid = (getpwnam($user))[2] if $user;
+$gid = (getpwnam($group))[3] if $group;
 
-my ($checkcommand, %on);
-$on{success} = [];
-$on{failure} = [];
-$on{change} = [];
+sub daemonize {
+       if (fork()) {
+               exit 0;
+       }
+       setsid();
 
-open CONFIG, "$dir/check";
-while (<CONFIG>) {
-       my @words = split(/\s+/);
-       my $command = shift @words;
-       if ($command eq 'check') {
-               $checkcommand = join(' ', @words);
-       } elsif ($command eq 'on') {
-               my $when = shift @words;
-               if ($when eq 'failure') {
-                       push @{$on{failure}}, join(' ', @words);
-               } elsif ($when eq 'success') {
-                       push @{$on{success}}, join(' ', @words);
-               } elsif ($when eq 'change') {
-                       push @{$on{change}}, join(' ', @words);
-               } else {
-                       print "Unknown event in 'on', $dir/check line $.\n";
-               }
+       umask 027;
+       if (open STDOUT, ">>$logfile") {
+               open STDERR, ">>$logfile";
+               chown $uid, $gid, $logfile;
        } else {
-               print "Unknown command '$command', $dir/check line $.\n";
+               print "WARNING: Could not open logfile. You will get NO error output.\n";
+               open STDOUT, ">/dev/null";
+               open STDERR, ">/dev/null";
        }
-}
-close CONFIG;
+       open STDIN, "/dev/null";
 
-unless ($checkcommand) {
-       print "Check command not specified in $dir/check\n";
-       exit 1;
-}
-unless (@{$on{failure}} || @{$on{success}} || @{$on{change}}) {
-       print "No actions specified in $dir/check. This is probably a mistake.\n";
+       open PID, ">$pidfile" or warn "Could not write PID file\n";
+       print PID "$$\n";
+       close PID;
+       chown $uid, $gid, $pidfile;
+
+       chdir '/';
+       print "Daemon started at ",scalar gmtime(),"\n";
 }
 
-open STATUS, "$dir/checkstatus";
-chomp(my $oldstatus = <STATUS>);
-close STATUS;
+daemonize unless $nodaemon;
 
-open CHECK, '-|', $checkcommand;
-$status = <CHECK>;
-$details = join('', <CHECK>);
-close CHECK;
-my $exitstatus = $? >> 8;
+setuid($uid) if $uid;
+setgid($uid) if $gid;
 
-my $command;
-if ($exitstatus == 0 && $oldstatus != 0) {
-       do_command($command) foreach $command @{$on{success}};
-}
-if ($exitstatus != 0 && $oldstatus == 0) {
-       do_command($command) foreach $command @{$on{failure}};
-}
-if ($exitstatus != $oldstatus) {
-       do_command($command) foreach $command @{$on{change}};
-}
+$|++;
 
-open STATUS, ">$dir/checkstatus";
-print STATUS "$exitstatus\n";
-print STATUS "$status\n";
-print STATUS "$details\n";
-close STATUS;
+while (1) {
+       opendir(CHECKS, $dir);
+       while (my $d = readdir(CHECKS)) {
+               next if (! -d "$dir/$d" || $d eq '.' || $d eq '..');
+               unless (-f "$dir/$d/check") {
+                       print "No check script in $dir/$d\n";
+                       next;
+               }
+               print "Running check $dir/$d: ";
+               system("runcheck $dir/$d");
+               my $exitstatus = $? >> 8;
+               if ($exitstatus) {
+                       print "FAILURE\n";
+               } else {
+                       print "OK\n";
+               }
+               sleep 10;
+       }
+       closedir(CHECKS);
+}