aboutsummaryrefslogtreecommitdiff
path: root/netsnmp-sendmail-setup
diff options
context:
space:
mode:
Diffstat (limited to 'netsnmp-sendmail-setup')
-rwxr-xr-xnetsnmp-sendmail-setup213
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.

Return to:

Send suggestions and report system problems to the System administrator.