diff options
Diffstat (limited to 'netsnmp-sendmail-setup')
-rwxr-xr-x | netsnmp-sendmail-setup | 213 |
1 files changed, 153 insertions, 60 deletions
diff --git a/netsnmp-sendmail-setup b/netsnmp-sendmail-setup index 86e8002..5e49ad6 100755 --- a/netsnmp-sendmail-setup +++ b/netsnmp-sendmail-setup @@ -1,10 +1,10 @@ #!/bin/sh #! -*-perl-*- # This file is part of NetSNMP::Sendmail -# Copyright (C) 2019 Sergey Poznyakoff <gray@gnu.org> +# Copyright (C) 2019-2020 Sergey Poznyakoff <gray@gnu.org> # # 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 3, or (at your option) # any later version. # @@ -32,32 +32,38 @@ sub addts { my $fd = shift; print $fd "# Line added by $0 at " . strftime("%Y-%m-%dT%H:%M:%S", localtime) . "\n"; } +my $my_ts_rx = qr{^# Line added by $0 at }; + use constant { EX_OK => 0, # Success EX_UNCHANGED => 1, # Files not changed EX_FATAL => 2, # Fatal error EX_USAGE => 64 # Usage error }; +use constant { + CMD_SETUP => 0, + CMD_REMOVE => 1 +}; + my $suppress_level = 0; my $dry_run; -my $changed = 0; my @updated_services; use constant { L_INFO => 0, L_NOTICE => 1, L_WARN => 2, L_ERR => 3 }; -use constant MAX_SUPPRESS_LEVEL => L_NOTICE; +use constant MAX_SUPPRESS_LEVEL => L_WARN; sub printlog { my $level = shift; if ($suppress_level <= $level) { my $fh = (($level >= L_WARN) ? \*STDERR : \*STDOUT); print $fh "$0: ".join(' ',@_)."\n"; @@ -93,32 +99,77 @@ sub restart_service { return if ($? == 0); } } } } +sub file_replace { + my ($file, $newfile) = @_; + my $bk = "$file~"; + unlink $bk if -e $bk; + rename $file, $bk or die "can't rename $file to $bk: $!"; + unless (rename $newfile, $file) { + printlog(L_WARN, + "can't rename $newfile to $file: $!; restoring from backup"); + unless (rename $bk, $file) { + printlog(L_ERR, "failed to rename $bk to $file: $!"); + exit(EX_FATAL); + } + } +} + +sub file_remline { + my ($file, $fd, $line, $endline) = @_; + $endline //= $line; + printlog(L_NOTICE, + ($endline == $line) ? "editing $file: removing line $line" + : "editing $file: removing lines $line-$endline"); + return if $dry_run; + my $ofd = File::Temp->new(DIR => dirname($file), UNLINK => $dry_run); + seek($fd, 0, SEEK_SET) or die "seek $file: $!"; + my $ln = 0; + while (<$fd>) { + $ln++; + next if ($line <= $ln && $ln <= $endline); + print $ofd $_; + } + close $ofd; + close $fd; + file_replace($file, $ofd->filename); +} + sub scan_snmpd_conf { my ($file, $fd) = @_; my $line = 0; my $comline; my $insert_line; while (<$fd>) { ++$line; chomp; s/^\s+//; + + if (/$my_ts_rx/) { + $comline = $line; + next; + } + if (/^perl\s+use\s+NetSNMP::Sendmail/) { - printlog(L_INFO, "$file:$line: NetSNMP::Sendmail already enabled"); - return; + if ($comline && $comline + 1 == $line) { + return (1, $comline, $line); + } else { + return (1, $line); + } } + if (/^perl\s+use/) { $insert_line = $line; } } - return $insert_line || $line + 1; + return (0, $insert_line || $line + 1); } sub update_snmpd_conf { my ($ifile, $ifd, $insert_line, $stmt) = @_; seek($ifd, 0, SEEK_SET) or die "seek: $!"; @@ -137,36 +188,31 @@ sub update_snmpd_conf { } if ($insert_line) { printlog(L_NOTICE, "editing $ifile (append)"); addts $ofd; print $ofd "$stmt\n"; } - $changed++; - return $ofd; + close $ofd; + file_replace($ifile, $ofd->filename) unless ($dry_run); } sub edit_snmpd_conf { - my ($name, $stmt) = @_; + my ($command, $name, $stmt) = @_; my $u = umask(077); if (open(my $fd, '<', $name)) { - if (defined(my $insert_line = scan_snmpd_conf($name, $fd))) { - my $fh = update_snmpd_conf($name, $fd, $insert_line, $stmt); - unless ($dry_run) { - my $bk = "$name~"; - unlink $bk if -e $bk; - rename $name, $bk or die "can't rename $name to $bk: $!"; - unless (rename $fh->filename, $name) { - printlog(L_WARN, - "can't rename ".$fh->filename." to $name: $!; restoring from backup"); - unless (rename $bk, $name) { - printlog(L_ERR, "failed to rename $bk to $name: $!"); - exit(EX_FATAL); - } - } + my ($found, $line, $endline) = scan_snmpd_conf($name, $fd); + if ($command == CMD_SETUP) { + if ($found) { + printlog(L_INFO, "$name:".($endline ? $endline : $line).": NetSNMP::Sendmail already enabled"); + } else { + update_snmpd_conf($name, $fd, $line, $stmt); + push @updated_services, 'snmpd'; } - push @updated_services, 'snmpd'; + } elsif ($found) { + file_remline($name, $fd, $line, $endline); + push @updated_services, 'snmpd'; } close $fd; } else { printlog(L_ERR, "can't open $name: $!"); exit(EX_FATAL); } @@ -207,56 +253,79 @@ sub check_module { } sub scan_sendmail_mc { my $fd = shift; my $name; my $last_nl; + my $comline; + my $line = 0; while (<$fd>) { + $line++; $last_nl = chomp; s/^\s+//; + + if (/$my_ts_rx/) { + $comline = $line; + next; + } if (/^define\(\s*`?STATUS_FILE'?\s*,\s*`?(.+?)'?\s*\)/) { $name = $1; + last } } - return ($name, $last_nl); + + return ($name, $last_nl, + ($comline && $comline + 1 == $line) ? ($comline, $line) : ($line)); } sub edit_sendmail_mc { - my $file = shift; + my ($command, $file, $default_statfile) = @_; if (open(my $fd, '+<', $file)) { my $need_make; - my ($statfile, $last_nl) = scan_sendmail_mc($fd); - if ($statfile) { - printlog(L_INFO, "status file $statfile"); - } else { - $statfile = shift; - printlog(L_NOTICE, "editing $file"); - unless ($dry_run) { - seek($fd, 0, SEEK_END) or die "seek: $!"; - print $fd "\n" unless $last_nl; - addts $fd; - print $fd "define(`STATUS_FILE', `$statfile')\n"; + my ($statfile, $last_nl, $line, $endline) = scan_sendmail_mc($fd); + if ($command == CMD_SETUP) { + if ($statfile) { + printlog(L_INFO, + "$file:".($endline ? $endline : $line). + ": status file $statfile already enabled"); + } else { + $statfile = $default_statfile; + printlog(L_NOTICE, "editing $file"); + unless ($dry_run) { + seek($fd, 0, SEEK_END) or die "seek: $!"; + print $fd "\n" unless $last_nl; + addts $fd; + print $fd "define(`STATUS_FILE', `$statfile')\n"; + } + push @updated_services, 'sendmail'; + $need_make = !$dry_run; } - push @updated_services, 'sendmail'; - $need_make = 1; - $changed++; - } - close $fd; - if (-f $statfile) { - printlog(L_INFO, "$statfile exists"); - } else { - printlog(L_NOTICE, "creating $statfile"); - unless ($dry_run) { - if (open($fd, '>', $statfile)) { - close($fd); - } else { - warn "failed to create $statfile: $!"; + if (-f $statfile) { + printlog(L_INFO, "$statfile exists"); + } else { + printlog(L_NOTICE, "creating $statfile"); + unless ($dry_run) { + if (open($fd, '>', $statfile)) { + close($fd); + } else { + warn "failed to create $statfile: $!"; + } } } + } elsif ($statfile) { + if ($endline) { + file_remline($file, $fd, $line, $endline); + push @updated_services, 'sendmail'; + $need_make = !$dry_run; + } else { + printlog(L_INFO, + "$file:$line: retaining status file setup: not configured by $0"); + } } + close $fd; if ($need_make) { my $sendmail_dir = dirname($file); printlog(L_NOTICE, "running make in $sendmail_dir"); system("make " . ($dry_run ? '-n ' : '') @@ -271,20 +340,24 @@ sub edit_sendmail_mc { # Main my $snmpd_conf = '/etc/snmp/snmpd.conf'; my $sendmail_mc = '/etc/mail/sendmail.mc'; my $sendmail_statfile = '/etc/mail/sendmail.st'; my $sendmail_bindir; +my $command = CMD_SETUP; + GetOptions('quiet|q+' => \$suppress_level, 'dry-run|n' => \$dry_run, 'status-file=s' => \$sendmail_statfile, 'bindir=s' => \$sendmail_bindir, 'restart=s' => sub { - my ($name,$command) = split /=/, $_[1], 2; - $restart_override{$name} = $command; + my ($name,$cmd) = split /=/, $_[1], 2; + $restart_override{$name} = $cmd; }, + 'configure' => sub { $command = CMD_SETUP }, + 'deconfigure' => sub { $command = CMD_REMOVE }, 'help' => sub { pod2usage(-exitstatus => EX_OK, -verbose => 2); }, 'usage' => sub { pod2usage(-exitstatus => EX_OK, -verbose => 0); } @@ -295,15 +368,15 @@ pod2usage(-exitstatus => EX_USAGE, -verbose => 0, -output => \*STDERR) if ($suppress_level > MAX_SUPPRESS_LEVEL) { $suppress_level = MAX_SUPPRESS_LEVEL; } check_module; -check_file $snmpd_conf; -check_file $sendmail_mc; -check_file '/etc/mail/Makefile'; +check_file($snmpd_conf); +check_file($sendmail_mc); +check_file('/etc/mail/Makefile'); unless ($sendmail_bindir) { if (-d "/usr/lib/sm.bin") { $sendmail_bindir = "/usr/lib/sm.bin"; } } @@ -311,44 +384,55 @@ unless ($sendmail_bindir) { my $stmt = 'perl use NetSNMP::Sendmail'; if ($sendmail_bindir) { $stmt .= ' qw(:config bindir /usr/lib/sm.bin)'; } $stmt .= ';'; -edit_sendmail_mc($sendmail_mc, $sendmail_statfile); -edit_snmpd_conf($snmpd_conf, $stmt); +edit_sendmail_mc($command, $sendmail_mc, $sendmail_statfile); +edit_snmpd_conf($command, $snmpd_conf, $stmt); map { restart_service($_) } @updated_services; -exit($changed ? EX_OK : EX_UNCHANGED); +exit(@updated_services ? EX_OK : EX_UNCHANGED); __END__ =head1 NAME netsnmp-sendmail-setup - sets up Sendmail monitoring via SNMP =head1 SYNOPSIS B<netsnmp-sendmail-setup> [B<-nq>] [B<--bindir=I<DIR>>] +[B<--configure>] +[B<--deconfigure>] [B<--dry-run>] [B<--status-file=I<FILE>>] [B<--quiet>] [B<--restart=I<service>=I<command>>] - + B<netsnmp-sendmail-setup> B<--help> | B<--usage> =head1 DESCRIPTION Sets up B<sendmail> and B<snmpd> for obtaining Sendmail statistics via SNMP. First, it checks whether the Sendmail configuration source F</etc/mail/sendmail.mc> contains the B<STATUS_FILE> clause and adds it if not. Then, it creates the status file and runs B<make> in the F</etc/mail> directory. Finally, the file F</etc/snmp/snmpd.conf> is scanned for the B<perl use NetSNMP::Sendmail> statement. It is added if not already present. +Each added configuration line is preceded by a comment stating that it +was added by the script. + +When run with the B<--deconfigure> option, the reverse operation is +performed. The B<NetSNMP::Sendmail> configuration statement is removed +from the snmpd configuration unconditionally. The B<STATUS_FILE> clause +is removed from F</etc/mail/sendmail.mc> only if it is preceded by the +B<netsnmp-sendmail-setup> comment marker. The status file itself is +never removed. =head1 OPTIONS =over 4 =item B<--bindir=I<DIR>> @@ -357,12 +441,21 @@ Some installations place Sendmail binaries in a separate directory, which is not included in the B<$PATH> environment variable. Use this option to inform B<NetSNMP::Sendmail> about this. Notice for the users of Debian-based systems: the F</usr/lib/sm.bin> directory is picked up automatically. +=item B<--configure> + +A no-op option included for symmetry with B<--deconfigure>. + +=item B<--deconfigure> + +Remove the configuration statements previously added to the snmdp and +sendmail configuration files. + =item B<-n>, B<--dry-run> Dry run mode. Don't modify any files, just print what would have been done and exit with the appropriate error code (see B<EXIT STATUS> section). Use the B<--quiet> option to control the amount of data printed. |