123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- #!/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:
|