From fb0cd0f84d0ecfba0c5a5b0045eee0ede0477a0e Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Thu, 18 Jun 2020 12:41:32 +0300 Subject: Update netsnmp-sendmail-setup * netsnmp-sendmail-setup: Add --deconfigure option: remove previosly added configuration statements. --- netsnmp-sendmail-setup | 213 +++++++++++++++++++++++++++++++++++-------------- 1 file 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,7 +1,7 @@ #!/bin/sh #! -*-perl-*- # This file is part of NetSNMP::Sendmail -# Copyright (C) 2019 Sergey Poznyakoff +# Copyright (C) 2019-2020 Sergey Poznyakoff # # 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 @@ -35,6 +35,8 @@ sub addts { . "\n"; } +my $my_ts_rx = qr{^# Line added by $0 at }; + use constant { EX_OK => 0, # Success EX_UNCHANGED => 1, # Files not changed @@ -42,9 +44,13 @@ use constant { 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 { @@ -54,7 +60,7 @@ use constant { L_ERR => 3 }; -use constant MAX_SUPPRESS_LEVEL => L_NOTICE; +use constant MAX_SUPPRESS_LEVEL => L_WARN; sub printlog { my $level = shift; @@ -96,6 +102,41 @@ sub restart_service { } } +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) = @_; @@ -107,15 +148,25 @@ sub scan_snmpd_conf { ++$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 { @@ -140,30 +191,25 @@ sub update_snmpd_conf { 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 { @@ -210,50 +256,73 @@ 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); @@ -274,14 +343,18 @@ 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); }, @@ -298,9 +371,9 @@ if ($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") { @@ -314,12 +387,12 @@ if ($sendmail_bindir) { } $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 @@ -331,11 +404,13 @@ netsnmp-sendmail-setup - sets up Sendmail monitoring via SNMP B [B<-nq>] [B<--bindir=I>] +[B<--configure>] +[B<--deconfigure>] [B<--dry-run>] [B<--status-file=I>] [B<--quiet>] [B<--restart=I=I>] - + B B<--help> | B<--usage> =head1 DESCRIPTION @@ -346,6 +421,15 @@ F contains the B clause and adds it if not. Then, it creates the status file and runs B in the F directory. Finally, the file F is scanned for the B 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 configuration statement is removed +from the snmpd configuration unconditionally. The B clause +is removed from F only if it is preceded by the +B comment marker. The status file itself is +never removed. =head1 OPTIONS @@ -360,6 +444,15 @@ inform B about this. Notice for the users of Debian-based systems: the F 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 -- cgit v1.2.1