aboutsummaryrefslogtreecommitdiff
path: root/netsnmp-sendmail-setup
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2019-05-22 15:35:50 +0300
committerSergey Poznyakoff <gray@gnu.org>2019-05-22 18:05:17 +0300
commite30ddffa198dedeed50c1d9e2bbb98cabbec2eae (patch)
tree8325e367dd45569645aa5b2b9fef1619b49976e6 /netsnmp-sendmail-setup
parent6fbbfaa9ffd65b35c77cc54e4d81d94e0539da22 (diff)
downloadnetsnmp-sendmail-e30ddffa198dedeed50c1d9e2bbb98cabbec2eae.tar.gz
netsnmp-sendmail-e30ddffa198dedeed50c1d9e2bbb98cabbec2eae.tar.bz2
New utility: netsnmp-sendmail-setup
Diffstat (limited to 'netsnmp-sendmail-setup')
-rwxr-xr-xnetsnmp-sendmail-setup470
1 files changed, 470 insertions, 0 deletions
diff --git a/netsnmp-sendmail-setup b/netsnmp-sendmail-setup
new file mode 100755
index 0000000..86e8002
--- /dev/null
+++ b/netsnmp-sendmail-setup
@@ -0,0 +1,470 @@
+#!/bin/sh
+#! -*-perl-*-
+# This file is part of NetSNMP::Sendmail
+# Copyright (C) 2019 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.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+eval 'exec perl -x -wS $0 ${1+"$@"}'
+ if 0;
+
+use strict;
+use warnings;
+use File::Temp;
+use File::Basename;
+use IPC::Cmd qw(can_run);
+use POSIX qw(strftime);
+use Fcntl;
+use Getopt::Long qw(:config gnu_getopt no_ignore_case require_order);
+use Pod::Usage;
+
+sub addts {
+ my $fd = shift;
+ print $fd "# Line added by $0 at "
+ . strftime("%Y-%m-%dT%H:%M:%S", localtime)
+ . "\n";
+}
+
+use constant {
+ EX_OK => 0, # Success
+ EX_UNCHANGED => 1, # Files not changed
+ EX_FATAL => 2, # Fatal error
+ EX_USAGE => 64 # Usage error
+};
+
+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;
+
+sub printlog {
+ my $level = shift;
+ if ($suppress_level <= $level) {
+ my $fh = (($level >= L_WARN) ? \*STDERR : \*STDOUT);
+ print $fh "$0: ".join(' ',@_)."\n";
+ }
+}
+
+my @restart_commands = (
+ [qw(systemctl restart $service)],
+ [qw(service $service restart)],
+ [qw(/etc/init.d/$service restart)],
+ [qw(/etc/rc.d/$service restart)],
+);
+my %restart_override;
+
+sub restart_service {
+ my $service = shift;
+
+ my $cmd = $restart_override{$service};
+ return if $cmd && $cmd eq 'no';
+
+ printlog(L_NOTICE, "restarting $service");
+ return if $dry_run;
+
+ if ($cmd) {
+ printlog(L_NOTICE, "running $cmd");
+ system($cmd);
+ } else {
+ foreach $cmd (@restart_commands) {
+ my @c = map { (my $s = $_) =~ s/\$service/$service/g; $s } @$cmd;
+ if (can_run($c[0])) {
+ printlog(L_NOTICE, "running @c");
+ system(@c);
+ return if ($? == 0);
+ }
+ }
+ }
+}
+
+sub scan_snmpd_conf {
+ my ($file, $fd) = @_;
+
+ my $line = 0;
+ my $comline;
+ my $insert_line;
+
+ while (<$fd>) {
+ ++$line;
+ chomp;
+ s/^\s+//;
+ if (/^perl\s+use\s+NetSNMP::Sendmail/) {
+ printlog(L_INFO, "$file:$line: NetSNMP::Sendmail already enabled");
+ return;
+ }
+ if (/^perl\s+use/) {
+ $insert_line = $line;
+ }
+ }
+ return $insert_line || $line + 1;
+}
+
+sub update_snmpd_conf {
+ my ($ifile, $ifd, $insert_line, $stmt) = @_;
+
+ seek($ifd, 0, SEEK_SET) or die "seek: $!";
+ my $ofd = File::Temp->new(DIR => dirname($ifile), UNLINK => $dry_run);
+ my $line = 0;
+ while (<$ifd>) {
+ chomp;
+ ++$line;
+ if ($insert_line && $line == $insert_line) {
+ printlog(L_NOTICE, "editing $ifile (line $line)");
+ addts $ofd;
+ print $ofd "$stmt\n";
+ $insert_line = undef;
+ }
+ print $ofd "$_\n";
+ }
+ if ($insert_line) {
+ printlog(L_NOTICE, "editing $ifile (append)");
+ addts $ofd;
+ print $ofd "$stmt\n";
+ }
+ $changed++;
+ return $ofd;
+}
+
+sub edit_snmpd_conf {
+ my ($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);
+ }
+ }
+ }
+ push @updated_services, 'snmpd';
+ }
+ close $fd;
+ } else {
+ printlog(L_ERR, "can't open $name: $!");
+ exit(EX_FATAL);
+ }
+ umask($u);
+}
+
+sub check_file {
+ my $file = shift;
+ if (-f $file) {
+ if (! -r $file) {
+ printlog(L_ERR, "$file is not readable");
+ exit(EX_FATAL);
+ }
+ } else {
+ printlog(L_ERR, "$file does not exist");
+ exit(EX_FATAL);
+ }
+}
+
+# Check if NetSNMP::Sendmail is available.
+# To avoid namespace contamination, do it in a subprocess.
+# The module requires SmtpAgent.pm, whic spits out lots of messages
+# to STDERR when loaded outside of snmpd, so first redirect stderr
+# to /dev/null.
+sub check_module {
+ my $pid = fork();
+ die "fork failed" unless defined $pid;
+ if ($pid == 0) {
+ open(STDERR, '>', '/dev/null');
+ require NetSNMP::Sendmail;
+ exit 0;
+ }
+ wait;
+ if ($?) {
+ printlog(L_ERR, "NetSNMP::Sendmail doesn't seem to be installed");
+ exit(EX_FATAL);
+ }
+}
+
+sub scan_sendmail_mc {
+ my $fd = shift;
+ my $name;
+ my $last_nl;
+ while (<$fd>) {
+ $last_nl = chomp;
+ s/^\s+//;
+ if (/^define\(\s*`?STATUS_FILE'?\s*,\s*`?(.+?)'?\s*\)/) {
+ $name = $1;
+ }
+ }
+ return ($name, $last_nl);
+}
+
+sub edit_sendmail_mc {
+ my $file = shift;
+ 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";
+ }
+ 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 ($need_make) {
+ my $sendmail_dir = dirname($file);
+ printlog(L_NOTICE, "running make in $sendmail_dir");
+ system("make "
+ . ($dry_run ? '-n ' : '')
+ . "-C $sendmail_dir");
+ }
+ } else {
+ printlog(L_ERR, "can't open $file: $!");
+ exit(EX_FATAL);
+ }
+}
+
+# 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;
+
+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;
+ },
+ 'help' => sub {
+ pod2usage(-exitstatus => EX_OK, -verbose => 2);
+ },
+ 'usage' => sub {
+ pod2usage(-exitstatus => EX_OK, -verbose => 0);
+ }
+) or pod2usage(-exitstatus => EX_USAGE, -verbose => 0, -output => \*STDERR);
+
+pod2usage(-exitstatus => EX_USAGE, -verbose => 0, -output => \*STDERR)
+ if @ARGV;
+
+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';
+
+unless ($sendmail_bindir) {
+ if (-d "/usr/lib/sm.bin") {
+ $sendmail_bindir = "/usr/lib/sm.bin";
+ }
+}
+
+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);
+
+map { restart_service($_) } @updated_services;
+
+exit($changed ? 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<--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.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--bindir=I<DIR>>
+
+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<-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.
+
+=item B<-q>, B<--quiet>
+
+Quiet mode. When used once, suppresses informative output. When used twice,
+suppresses both informative output and notification messages about modified
+files.
+
+=item B<--restart=I<service>=I<command>>
+
+Use I<command> to restart system service I<service> (either B<snmpd> or
+B<sendmail>). Use B<--restart=I<service>=no> to skip restarting this
+particular I<service>.
+
+In the absence of this option, B<netsnmp-sendmail-setup> uses the first
+available command from the following list:
+
+ systemctl restart $service
+ service $service restart
+ /etc/init.d/$service restart
+ /etc/rc.d/$service restart
+
+=item B<--status-file=I<FILE>>
+
+Name of the Sendmail status file to use when generating the
+B<define(STATUS_FILE)> statement in the Sendmail configuration file.
+
+=item B<--help>
+
+Displays short help message.
+
+=item B<--usage>
+
+Displaye short usage message.
+
+=back
+
+=head1 FILES
+
+=over 4
+
+=item F</etc/mail/Makefile>
+
+This file is supposed to recreate the B<sendmail.cf> file from B<sendmail.mc>
+if no special goal is given.
+
+=item F</etc/snmp/snmpd.conf>
+
+Default B<snmpd> configuration file. This file must exist.
+
+=item F</etc/mail/sendmail.mc>
+
+Default source file for creating B<sendmail.cf>.
+
+=item F</etc/mail/sendmail.st>
+
+Default statistics file to use. Can be changed using the B<--status-file>
+option.
+
+=item F</usr/lib/sm.bin>
+
+Default directory for Sendmail binaries on Debian-based installations. If
+exists, it will be used in the B<NetSNMP::Sendmail> configuration.
+
+This can be changed using the B<--bindir> command line option.
+
+=back
+
+=head1 EXIT STATUS
+
+=over 4
+
+=item B<0>
+
+Success.
+
+=item B<1>
+
+Success, no modification was necessary.
+
+=item B<2>
+
+Fatal error occurred.
+
+=item B<64>
+
+Command line usage error.
+
+=back
+
+=head1 SEE ALSO
+
+B<NetSNMP::Sendmail>(3).
+
+=head1 BUGS
+
+The Sendmail configuration directory and the name of the B<snmpd>
+configuration file are hardcoded.
+
+The command relies on B<make -C /etc/mail> to create F<sendmail.cf> from
+F<sendmail.mc>.
+
+=cut

Return to:

Send suggestions and report system problems to the System administrator.