Current File : //usr/share/webmin/logrotate/logrotate-lib.pl |
# logrotate-lib.pl
# Common functions for parsing the logrotate configuration file.
BEGIN { push(@INC, ".."); };
use WebminCore;
&init_config();
if (open(VERSION, "<$module_config_directory/version")) {
chop($logrotate_version = <VERSION>);
close(VERSION);
}
# Use sample config if it exists but real config doesn't yet
if (!-r $config{'logrotate_conf'} && -r $config{'sample_conf'}) {
©_source_dest($config{'sample_conf'}, $config{'logrotate_conf'});
}
sub get_config_parent
{
if (!$get_config_parent_cache) {
local ($conf, $lines) = &get_config();
$get_config_parent_cache = { 'members' => $conf,
'file' => $config{'logrotate_conf'},
'line' => 0,
'eline' => $lines,
'global' => 1 };
}
return $get_config_parent_cache;
}
# get_config([file])
# Returns a list of logrotate config file entries
sub get_config
{
local $file = $_[0] || $config{'logrotate_conf'};
if (!$_[0] && $get_config_cache{$file}) {
return wantarray ? ( $get_config_cache{$file},
$get_config_lnum_cache{$file},
$get_config_files_cache{$file} )
: $get_config_cache{$file};
}
local @files = ( $file );
local @rv;
local $addto = \@rv;
local $section = undef;
local $lnum = 0;
local $fh = "FILE".$file_count++;
open($fh, "<".$file);
while(<$fh>) {
s/\r|\n//g;
s/#.*$//;
if (/^\s*(.*)\{\s*$/) {
# Start of a section
push(@name, &split_words($1));
$section = { 'name' => [ @name ],
'members' => [ ],
'index' => scalar(@$addto),
'line' => defined($namestart) ? $namestart : $lnum,
'eline' => $lnum,
'file' => $file };
push(@$addto, $section);
$addto = $section->{'members'};
@name = ( );
$namestart = undef;
}
elsif ((/^\s*\// || /^\s*"\//) && !$section) {
# A path before a section
$namestart = $lnum if (!@name);
push(@name, &split_words($_));
}
elsif (/^\s*}\s*$/) {
# End of a section
$addto = \@rv;
$section->{'eline'} = $lnum;
$section = undef;
}
elsif (/^\s*include\s+(.*)$/i) {
# Including other directives files
local $incfile = $1;
if (-d $incfile) {
# Multiple files!
local $f;
opendir(DIR, $incfile);
local @dirs = sort { $a cmp $b } readdir(DIR);
closedir(DIR);
foreach $f (@dirs) {
next if ($f =~ /^\./ ||
$f =~ /\.rpm(save|orig|new)$/ ||
$f =~ /\~$/ ||
$f =~ /,v$/ ||
$f =~ /\.swp$/ ||
$f =~ /\.lock$/);
local ($inc, $ilnum, $ifiles) =
&get_config("$incfile/$f");
push(@files, @$ifiles);
map { $_->{'index'} += @$addto } @$inc;
push(@$addto, @$inc);
}
}
else {
# A single file
local ($inc, $ilnum, $ifiles) = &get_config($incfile);
push(@files, @$ifiles);
map { $_->{'index'} += @$addto } @$inc;
push(@$addto, @$inc);
}
}
elsif (/^\s*(\S+)\s*(.*)$/) {
# Single directive
local $dir = { 'name' => $1,
'value' => $2,
'index' => scalar(@$addto),
'line' => $lnum,
'eline' => $lnum,
'file' => $file };
push(@$addto, $dir);
if ($1 eq 'postrotate' || $1 eq 'prerotate') {
# Followed by a multi-line script!
while(<$fh>) {
$lnum++;
s/\r|\n//g;
last if (/^\s*(endscript|endrotate)\s*$/);
s/^\s+//;
$dir->{'script'} .= $_."\n";
}
$dir->{'eline'} = $lnum;
}
}
$lnum++;
}
close($fh);
if (!$_[0]) {
$get_config_cache{$file} = \@rv;
$get_config_lnum_cache{$file} = $lnum;
$get_config_files_cache{$file} = \@files;
}
return wantarray ? (\@rv, $lnum, \@files) : \@rv;
}
sub split_words
{
local @rv;
local $str = $_[0];
while($str =~ /^\s*"(.*)"(.*)$/ || $str =~ /^\s*(\S+)(.*)$/) {
push(@rv, $1);
$str = $2;
}
return @rv;
}
sub join_words
{
return join(" ", map { /\s/ ? "\"$_\"" : $_ } @_);
}
# find(name, &config)
sub find
{
local @rv = grep { lc($_->{'name'}) eq lc($_[0]) } @{$_[1]};
return wantarray ? @rv : $rv[0];
}
# find_value(name, &config)
sub find_value
{
local @rv = map { defined($_->{'script'}) ? $_->{'script'} : $_->{'value'} }
grep { lc($_->{'name'}) eq lc($_[0]) } @{$_[1]};
return wantarray ? @rv : $rv[0];
}
# get_logrotate_version(&out)
sub get_logrotate_version
{
local $out = &backquote_command("$config{'logrotate'} -v 2>&1", 1);
${$_[0]} = $out if ($_[0]);
return $out =~ /logrotate\s+([0-9\.]+)\s/ ||
$out =~ /logrotate\-([0-9\.]+)\s/ ? $1 : undef;
}
# get_period(&conf)
sub get_period
{
foreach $p ("daily", "weekly", "monthly") {
local $ex = &find($p, $_[0]);
return $p if ($ex);
}
return undef;
}
# save_directive(&parent, &old|name, &new, [indent])
sub save_directive
{
local $conf = $_[0]->{'members'};
local $old = !defined($_[1]) ? undef : ref($_[1]) ? $_[1] : &find($_[1], $conf);
local $lref = &read_file_lines($old ? $old->{'file'} : $_[0]->{'file'});
local $new = !defined($_[2]) ? undef : ref($_[2]) ? $_[2] :
{ 'name' => $old ? $old->{'name'} : $_[1],
'value' => $_[2] };
local @lines = &directive_lines($new, $_[3]) if ($new);
local $gparent = &get_config_parent();
if ($old && $new) {
# Update
local $oldlines = $old->{'eline'} - $old->{'line'} + 1;
splice(@$lref, $old->{'line'}, $oldlines, @lines);
$new->{'line'} = $old->{'line'};
$new->{'index'} = $old->{'index'};
$new->{'file'} = $old->{'file'};
$new->{'eline'} = $new->{'line'} + scalar(@lines) - 1;
%$old = %{$new};
&renumber($gparent, $old->{'file'}, $old->{'eline'},
scalar(@lines) - $oldlines, $old);
}
elsif ($old && !$new) {
# Delete
local $oldlines = $old->{'eline'} - $old->{'line'} + 1;
splice(@$lref, $old->{'line'}, $old->{'eline'} - $old->{'line'} + 1);
splice(@$conf, $old->{'index'}, 1);
&renumber($gparent, $old->{'file'}, $old->{'line'}, -$oldlines);
}
elsif (!$old && $new && $_[0]->{'global'} && !$new->{'members'}) {
# Add at the start of the file
if (defined($_[0]->{'line'})) {
splice(@$lref, 0, 0, @lines);
$new->{'line'} = 0;
$new->{'eline'} = $new->{'line'} + scalar(@lines) - 1;
$new->{'file'} = $_[0]->{'file'};
&renumber($gparent, $new->{'file'}, $new->{'line'}-1, scalar(@lines));
}
$new->{'index'} = 0;
splice(@$conf, 0, 0, $new);
}
elsif (!$old && $new) {
# Add (to end of section)
if (defined($_[0]->{'line'})) {
if (!$new->{'file'} || $_[0]->{'file'} eq $new->{'file'}) {
# Adding to parent file
splice(@$lref, $_[0]->{'eline'}, 0, @lines);
$new->{'line'} = $_[0]->{'eline'};
$new->{'eline'} = $new->{'line'} + scalar(@lines) - 1;
$new->{'file'} = $_[0]->{'file'};
&renumber($gparent, $new->{'file'}, $new->{'line'}-1, scalar(@lines));
}
else {
# Adding to another file
local $lref2 = &read_file_lines($new->{'file'});
$new->{'line'} = scalar(@$lref2);
$new->{'eline'} = $new->{'line'} + scalar(@lines) - 1;
push(@$lref2, @lines);
}
}
$new->{'index'} = scalar(@$conf);
push(@$conf, $new);
}
}
# renumber(&object, file, startline, count, [&skip])
sub renumber
{
return if (!$_[3]);
if ($_[0]->{'file'} eq $_[1] && $_[0] ne $_[4]) {
$_[0]->{'line'} += $_[3] if ($_[0]->{'line'} > $_[2]);
$_[0]->{'eline'} += $_[3] if ($_[0]->{'eline'} > $_[2]);
}
if ($_[0]->{'members'}) {
local $c;
foreach $c (@{$_[0]->{'members'}}) {
&renumber($c, $_[1], $_[2], $_[3], $_[4]);
}
}
}
# directive_lines(&dir, indent)
sub directive_lines
{
local @rv;
if ($_[0]->{'members'}) {
push(@rv, $_[1].&join_words(@{$_[0]->{'name'}})." {");
foreach $m (@{$_[0]->{'members'}}) {
push(@rv, &directive_lines($m, $_[1]."\t"));
}
push(@rv, $_[1]."}");
}
elsif ($_[0]->{'script'}) {
push(@rv, $_[1].$_[0]->{'name'});
foreach $s (split(/\n/, $_[0]->{'script'})) {
push(@rv, $_[1].$s);
}
push(@rv, $_[1]."endscript");
}
else {
push(@rv, $_[1].$_[0]->{'name'}.
($_[0]->{'value'} eq "" ? "" : " ".$_[0]->{'value'}));
}
return @rv;
}
# delete_if_empty(file)
sub delete_if_empty
{
local $conf = &get_config();
local %files = map { $_, 1 } &unique(map { $_->{'file'} } @$conf);
&unlink_file($_[0]) if (!$files{$_[0]});
}
%global_default = ( "nocompress" => "",
"compress" => undef,
"nodelaycompress" => "",
"delaycompress" => undef,
"ifempty" => "",
"notifempty" => undef,
"nocopytruncate" => "",
"copytruncate" => undef,
"nomissingok" => "",
"missingok" => undef,
"rotate" => 0,
"create" => "",
"nocreate" => undef,
"noolddir" => "",
"olddir" => undef,
"ext" => undef,
"mail" => undef,
"nomail" => "",
"maillast" => "",
"mailfirst" => undef,
"errors" => undef,
"postrotate" => undef,
"prerotate" => undef,
"errors" => undef,
);
# rotate_log_now(&log)
# Call logrotate on a config fragment file to rotate just one set of logs
# immediately.
sub rotate_log_now
{
local $conf = &get_config();
local $temp = &transname();
open(TEMP, ">$temp");
local $c;
foreach $c (@$conf) {
if (!$c->{'members'}) {
print TEMP map { "$_\n" } &directive_lines($c);
}
}
print TEMP map { "$_\n" } &directive_lines($_[0]);
close(TEMP);
&set_ownership_permissions(undef, undef, 0700, $temp);
local $out = &backquote_logged("$config{'logrotate'} -f $temp 2>&1");
return ($?, $out);
}
# get_add_file([filename])
# Returns the file to which new logrotate sections should be added
sub get_add_file
{
local ($filename) = @_;
$filename =~ s/\*/ALL/g;
if ($config{'add_file'} && -d $config{'add_file'} && $filename) {
# Adding to a new file in a directory
return "$config{'add_file'}/$filename.conf";
}
elsif ($config{'add_file'} && !-d $config{'add_file'}) {
# Make sure file is valid
local ($conf, $lnum, $files) = &get_config();
if (&indexof($config{'add_file'}, @$files) >= 0) {
return $config{'add_file'};
}
}
return $config{'logrotate_conf'};
}
1;