]> git.decadent.org.uk Git - dak.git/blobdiff - tools/debianqueued-0.9/dqueued-watcher
Added debianqueued in a tools subdir
[dak.git] / tools / debianqueued-0.9 / dqueued-watcher
diff --git a/tools/debianqueued-0.9/dqueued-watcher b/tools/debianqueued-0.9/dqueued-watcher
new file mode 100755 (executable)
index 0000000..b44470a
--- /dev/null
@@ -0,0 +1,504 @@
+#!/usr/bin/perl -w
+#
+# dqueued-watcher -- for regularily watching the queue daemon
+#
+# This script is intended to check periodically (e.g. started by cron) that
+# everything is ok with debianqueued. If the daemon isn't running, it notifies
+# the maintainer. It also checks if a new Debian keyring is available (in a
+# Debian mirror aera, f.i.) and then updates the keyring used by debianqueued.
+#
+# Copyright (C) 1997 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+#
+# This program is free software.  You can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation: either version 2 or
+# (at your option) any later version.
+# This program comes with ABSOLUTELY NO WARRANTY!
+#
+# $Id: dqueued-watcher,v 1.28 1999/07/08 09:43:22 ftplinux Exp $
+#
+# $Log: dqueued-watcher,v $
+# Revision 1.28  1999/07/08 09:43:22  ftplinux
+# Bumped release number to 0.9
+#
+# Revision 1.27  1999/07/07 11:58:22  ftplinux
+# Also update gpg keyring if $conf::gpg_keyring is set.
+#
+# Revision 1.26  1998/07/06 14:24:36  ftplinux
+# Some changes to handle debian-keyring.tar.gz files which expand to a
+# directory including a date.
+#
+# Revision 1.25  1998/05/14 14:21:45  ftplinux
+# Bumped release number to 0.8
+#
+# Revision 1.24  1998/03/30 12:31:05  ftplinux
+# Don't count "already reported" or "ignored for now" errors as .changes errors.
+# Also list files for several error types.
+# Also print out names of processed jobs.
+#
+# Revision 1.23  1998/03/30 11:27:37  ftplinux
+# If called with args, make summaries for the log files given.
+# make_summary: New arg $to_stdout, for printing report directly.
+#
+# Revision 1.22  1998/03/23 14:05:15  ftplinux
+# Bumped release number to 0.7
+#
+# Revision 1.21  1997/12/16 13:19:29  ftplinux
+# Bumped release number to 0.6
+#
+# Revision 1.20  1997/11/20 15:18:48  ftplinux
+# Bumped release number to 0.5
+#
+# Revision 1.19  1997/10/31 12:26:31  ftplinux
+# Again added new counters in make_summary: suspicious_files,
+# transient_changes_errs.
+# Extended tests for bad_changes.
+# Quotes in pattern seem not to work, replaced by '.'.
+#
+# Revision 1.18  1997/10/30 14:17:32  ftplinux
+# In make_summary, implemented some new counters for command files.
+#
+# Revision 1.17  1997/10/17 09:39:09  ftplinux
+# Fixed wrong args to plural_s
+#
+# Revision 1.16  1997/09/25 11:20:42  ftplinux
+# Bumped release number to 0.4
+#
+# Revision 1.15  1997/09/17 12:16:33  ftplinux
+# Added writing summaries to a file
+#
+# Revision 1.14  1997/09/16 11:39:29  ftplinux
+# In make_summary, initialize all counters to avoid warnings about uninited
+# values.
+#
+# Revision 1.13  1997/09/16 10:53:36  ftplinux
+# Made logging more verbose in queued and dqueued-watcher
+#
+# Revision 1.12  1997/08/18 13:07:15  ftplinux
+# Implemented summary mails
+#
+# Revision 1.11  1997/08/18 12:11:44  ftplinux
+# Replaced timegm by timelocal in parse_date; times in log file are
+# local times...
+#
+# Revision 1.10  1997/08/18 11:27:20  ftplinux
+# Revised age calculation of log file for rotating
+#
+# Revision 1.9  1997/08/12 09:54:40  ftplinux
+# Bumped release number
+#
+# Revision 1.8  1997/08/11 12:49:10  ftplinux
+# Implemented logfile rotating
+#
+# Revision 1.7  1997/07/28 13:20:38  ftplinux
+# Added release numner to startup message
+#
+# Revision 1.6  1997/07/25 10:23:04  ftplinux
+# Made SIGCHLD handling more portable between perl versions
+#
+# Revision 1.5  1997/07/09 10:13:55  ftplinux
+# Alternative implementation of status file as plain file (not FIFO), because
+# standard wu-ftpd doesn't allow retrieval of non-regular files. New config
+# option $statusdelay for this.
+#
+# Revision 1.4  1997/07/08 08:39:56  ftplinux
+# Need to remove -z from tar options if --use-compress-program
+#
+# Revision 1.3  1997/07/08 08:34:15  ftplinux
+# If dqueued-watcher runs as cron job, $PATH might not contain gzip. Use extra
+# --use-compress-program option to tar, and new config var $gzip.
+#
+# Revision 1.2  1997/07/03 13:05:57  ftplinux
+# Added some verbosity if stdin is a terminal
+#
+# Revision 1.1.1.1  1997/07/03 12:54:59  ftplinux
+# Import initial sources
+#
+#
+
+require 5.002;
+use strict;
+use POSIX;
+require "timelocal.pl";
+
+sub LINEWIDTH { 79 }
+my $batchmode = !(-t STDIN);
+$main::curr_year = (localtime)[5];
+
+do {
+       my $version;
+       ($version = 'Release: 0.9 $Revision: 1.28 $ $Date: 1999/07/08 09:43:22 $ $Author: ftplinux $') =~ s/\$ ?//g;
+       print "dqueued-watcher $version\n" if !$batchmode;
+};
+
+package conf;
+($conf::queued_dir = (($0 !~ m,^/,) ? POSIX::getcwd()."/" : "") . $0)
+       =~ s,/[^/]+$,,;
+require "$conf::queued_dir/config";
+my # avoid spurious warnings about unused vars
+$junk = $conf::gzip;
+$junk = $conf::maintainer_mail;
+$junk = $conf::log_age;
+package main;
+
+# prototypes
+sub check_daemon();
+sub daemon_running();
+sub rotate_log();
+sub logf($);
+sub parse_date($);
+sub make_summary($$$);
+sub stimes($);
+sub plural_s($);
+sub format_list($@);
+sub mail($@);
+sub logger(@);
+sub format_time();
+
+# the main program:
+if (@ARGV) {
+       # with arguments, make summaries (to stdout) for the logfiles given
+       foreach (@ARGV) {
+               make_summary( 1, undef, $_ );
+       }
+}
+else {
+       # without args, just do normal maintainance actions
+       check_daemon();
+       rotate_log();
+}
+exit 0;
+
+
+#
+# check if the daemon is running, notify maintainer if not
+#
+sub check_daemon() {
+       my $daemon_down_text = "Daemon is not running\n";
+       my( $line, $reported );
+
+       if (daemon_running()) {
+               print "Daemon is running\n" if !$batchmode;
+               return;
+       }
+       print "Daemon is NOT running!\n" if !$batchmode;
+
+       $reported = 0;
+       if ($conf::statusfile && -f $conf::statusfile && ! -p _ &&
+               open( STATUSFILE, "<$conf::statusfile" )) {
+               $line = <STATUSFILE>;
+               close( STATUSFILE );
+               $reported = $line eq $daemon_down_text;
+       }
+       if (!$reported) {
+               mail( "debianqueued down",
+                         "The Debian queue daemon isn't running!\n",
+                         "Please start it up again.\n" );
+               logger( "Found that daemon is not running\n" );
+       }
+
+       # remove unnecessary pid file
+       # also remove status FIFO, so opening it for reading won't block
+       # forever
+       unlink( $conf::pidfile, $conf::statusfile );
+
+       # replace status FIFO by a file that tells the user the daemon is down
+       if ($conf::statusfile) {
+               open( STATUSFILE, ">$conf::statusfile" )
+                       or die "Can't open $conf::statusfile: $!\n";
+               print STATUSFILE $daemon_down_text;
+               close( STATUSFILE );
+       }
+}
+
+#
+# check if daemon is running
+#
+sub daemon_running() {
+       my $pid;
+       local( *PIDFILE );
+       
+       if (open( PIDFILE, "<$conf::pidfile" )) {
+               chomp( $pid = <PIDFILE> );
+               close( PIDFILE );
+               $main::daemon_pid = $pid, return 1 if $pid && kill( 0, $pid );
+       }
+       return 0;
+}
+
+#
+# check if new keyring is available, if yes extract it
+#
+
+sub rotate_log() {
+       my( $first_date, $f1, $f2, $i );
+       local( *F );
+
+       return if !defined $main::daemon_pid || !-f $conf::logfile;
+
+       open( F, "<$conf::logfile" ) or die "Can't open $conf::logfile: $!\n";
+       while( <F> ) {
+               last if $first_date = parse_date( $_ );
+       }
+       close( F );
+       # Simply don't rotate if nothing couldn't be parsed as date -- probably
+       # the file is empty.
+       return if !$first_date;
+       # assume year-wrap if $first_date is in the future
+       $first_date -= 365*24*60*60 if $first_date > time;
+       # don't rotate if first date too young
+       return if time - $first_date < $conf::log_age*24*60*60;
+       logger( "Logfile older than $conf::log_age days, rotating\n" );
+       
+       # remove oldest log
+       $f1 = logf($conf::log_keep-1);
+       if (-f $f1) {
+               unlink( $f1 ) or warn "Can't remove $f1: $!\n";
+       }
+
+       # rename other logs
+       for( $i = $conf::log_keep-2; $i > 0; --$i ) {
+               $f1 = logf($i);
+               $f2 = logf($i+1);
+               if ($i == 0) {
+               }
+               if (-f $f1) {
+                       rename( $f1, $f2 ) or warn "Can't rename $f1 to $f2: $!\n";
+               }
+       }
+       
+       # compress newest log
+       $f1 = "$conf::logfile.0";
+       $f2 = "$conf::logfile.1.gz";
+       if (-f $f1) {
+               system $conf::gzip, "-9f", $f1
+                       and die "gzip failed on $f1 (status $?)\n";
+               rename( "$f1.gz", $f2 ) or warn "Can't rename $f1.gz to $f2: $!\n";
+       }
+
+       # rename current log and signal the daemon to open a new logfile
+       rename( $conf::logfile, $f1 );
+       kill( 1, $main::daemon_pid );
+
+       print "Rotated log files\n" if !$batchmode;
+       make_summary( 0, $first_date, $f1 )
+               if $conf::mail_summary || $conf::summary_file;
+}
+
+sub logf($) {
+       my $num = shift;
+       return sprintf( "$conf::logfile.%d.gz", $num );
+}
+
+sub parse_date($) {
+       my $date = shift;
+       my( $mon, $day, $hours, $mins, $month, $year, $secs );
+       my %month_num = ( "jan", 0, "feb", 1, "mar", 2, "apr", 3, "may", 4,
+                                         "jun", 5, "jul", 6, "aug", 7, "sep", 8, "oct", 9,
+                                         "nov", 10, "dec", 11 );
+
+       warn "Invalid date: $date\n", return 0
+               unless $date =~ /^(\w\w\w)\s+(\d+)\s+(\d+):(\d+):(\d+)\s/;
+       ($mon, $day, $hours, $mins, $secs) = ($1, $2, $3, $4, $5);
+       
+       $mon =~ tr/A-Z/a-z/;
+       return 0 if !exists $month_num{$mon};
+       $month = $month_num{$mon};
+       return timelocal( $secs, $mins, $hours, $day, $month, $main::curr_year );
+}
+
+sub make_summary($$$) {
+       my $to_stdout = shift;
+       my $startdate = shift;
+       my $file = shift;
+       my( $starts, $statusd_starts, $suspicious_files, $transient_errs,
+           $upl_failed, $success, $commands, $rm_cmds, $mv_cmds, $msg,
+           $uploader );
+       my( @pgp_fail, %transient_errs, @changes_errs, @removed_changes,
+           @already_present, @del_stray, %uploaders, %cmd_uploaders );
+       local( *F );
+       
+       if (!open( F, "<$file" )) {
+               mail( "debianqueued summary failed",
+                         "Couldn't open $file to make summary of events." );
+               return;
+       }
+
+       $starts = $statusd_starts = $suspicious_files = $transient_errs =
+               $upl_failed = $success = $commands = $rm_cmds = $mv_cmds = 0;
+       while( <F> ) {
+               $startdate = parse_date( $_ ) if !$startdate;
+               ++$starts if /daemon \(pid \d+\) started$/;
+               ++$statusd_starts if /forked status daemon/;
+               push( @pgp_fail, $1 )
+                       if /PGP signature check failed on (\S+)/;
+               ++$suspicious_files if /found suspicious filename/;
+               ++$transient_errs, ++$transient_errs{$1}
+                       if /(\S+) (doesn.t exist|is too small) \(ignored for now\)/;
+               push( @changes_errs, $1 )
+                       if (!/\((already reported|ignored for now)\)/ &&
+                               (/(\S+) doesn.t exist/ || /(\S+) has incorrect (size|md5)/)) ||
+                          /(\S+) doesn.t contain a Maintainer: field/ ||
+                          /(\S+) isn.t signed with PGP/ ||
+                          /(\S+) doesn.t mention any files/;
+               push( @removed_changes, $1 )
+                       if /(\S+) couldn.t be processed for \d+ hours and is now del/ ||
+                          /(\S+) couldn.t be uploaded for \d+ times/;
+               push( @already_present, $1 )
+                       if /(\S+) is already present on master/;
+               ++$upl_failed if /Upload to \S+ failed/;
+               ++$success, push( @{$uploaders{$2}}, $1 )
+                       if /(\S+) processed successfully \(uploader (\S*)\)$/;
+               push( @del_stray, $1 ) if /Deleted stray file (\S+)/;
+               ++$commands if /processing .*\.commands$/;
+               ++$rm_cmds if / > rm /;
+               ++$mv_cmds if / > mv /;
+               ++$cmd_uploaders{$1}
+                       if /\(command uploader (\S*)\)$/;
+       }
+       close( F );
+
+       $msg .= "Queue Daemon Summary from " . localtime($startdate) . " to " .
+                   localtime(time) . ":\n\n";
+       
+       $msg .= "Daemon started ".stimes($starts)."\n"
+               if $starts;
+       $msg .= "Status daemon restarted ".stimes($statusd_starts-$starts)."\n"
+               if $statusd_starts > $starts;
+       $msg .= @pgp_fail." job".plural_s(@pgp_fail)." failed PGP check:\n" .
+                   format_list(2,@pgp_fail)
+               if @pgp_fail; 
+       $msg .= "$suspicious_files file".plural_s($suspicious_files)." with ".
+                       "suspicious names found\n"
+               if $suspicious_files;
+       $msg .= "Detected ".$transient_errs." transient error".
+                       plural_s($transient_errs)." in .changes files:\n".
+                       format_list(2,keys %transient_errs)
+               if $transient_errs;
+       $msg .= "Detected ".@changes_errs." error".plural_s(@changes_errs).
+                   " in .changes files:\n".format_list(2,@changes_errs)
+               if @changes_errs;
+       $msg .= @removed_changes." job".plural_s(@removed_changes).
+                   " removed due to persistent errors:\n".
+                       format_list(2,@removed_changes)
+               if @removed_changes;
+       $msg .= @already_present." job".plural_s(@already_present).
+                       " were already present on master:\n".format_list(2,@already_present)
+               if @already_present;
+       $msg .= @del_stray." stray file".plural_s(@del_stray)." deleted:\n".
+                       format_list(2,@del_stray)
+               if @del_stray;
+       $msg .= "$commands command file".plural_s($commands)." processed\n"
+               if $commands;
+       $msg .= "  ($rm_cmds rm, $mv_cmds mv commands)\n"
+               if $rm_cmds || $mv_cmds;
+       $msg .= "$success job".plural_s($success)." processed successfully\n";
+
+       if ($success) {
+               $msg .= "\nPeople who used the queue:\n";
+               foreach $uploader ( keys %uploaders ) {
+                       $msg .= "  $uploader (".@{$uploaders{$uploader}}."):\n".
+                                       format_list(4,@{$uploaders{$uploader}});
+               }
+       }
+
+       if (%cmd_uploaders) {
+               $msg .= "\nPeople who used command files:\n";
+               foreach $uploader ( keys %cmd_uploaders ) {
+                       $msg .= "  $uploader ($cmd_uploaders{$uploader})\n";
+               }
+       }
+
+       if ($to_stdout) {
+               print $msg;
+       }
+       else {
+               if ($conf::mail_summary) {
+                       mail( "debianqueued summary", $msg );
+               }
+               
+               if ($conf::summary_file) {
+                       local( *F );
+                       open( F, ">>$conf::summary_file" ) or
+                               die "Cannot open $conf::summary_file for appending: $!\n";
+                       print F "\n", "-"x78, "\n", $msg;
+                       close( F );
+               }
+       }
+}
+
+sub stimes($) {
+       my $num = shift;
+       return $num == 1 ? "once" : "$num times";
+}
+
+sub plural_s($) {
+       my $num = shift;
+       return $num == 1 ? "" : "s";
+}
+
+sub format_list($@) {
+       my $indent = shift;
+       my( $i, $pos, $ret, $item, $len );
+
+       $ret = " " x $indent; $pos += $indent;
+       while( $item = shift ) {
+               $len = length($item);
+               $item .= ", ", $len += 2 if @_;
+               if ($pos+$len > LINEWIDTH) {
+                       $ret .= "\n" . " "x$indent;
+                       $pos = $indent;
+               }
+               $ret .= $item;
+               $pos += $len;
+       }
+       $ret .= "\n";
+       return $ret;
+}
+
+#
+# send mail to maintainer
+#
+sub mail($@) {
+       my $subject = shift;
+       local( *MAIL );
+       
+       open( MAIL, "|$conf::mail -s '$subject' '$conf::maintainer_mail'" )
+               or (warn( "Could not open pipe to $conf::mail: $!\n" ), return);
+       print MAIL @_;
+       print MAIL "\nGreetings,\n\n\tYour Debian queue daemon watcher\n";
+       close( MAIL )
+               or warn( "$conf::mail failed (exit status $?)\n" );
+}
+
+#
+# log something to logfile
+#
+sub logger(@) {
+       my $now = format_time();
+       local( *LOG );
+       
+       if (!open( LOG, ">>$conf::logfile" )) {
+               warn( "Can't open $conf::logfile\n" );
+               return;
+       }
+       print LOG "$now dqueued-watcher: ", @_;
+       close( LOG );
+}
+
+#
+# return current time as string
+#
+sub format_time() {
+       my $t;
+
+       # omit weekday and year for brevity
+       ($t = localtime) =~ /^\w+\s(.*)\s\d+$/;
+       return $1;
+}
+
+
+# Local Variables:
+#  tab-width: 4
+#  fill-column: 78
+# End: