#!/usr/local/bin/perl
;#
;# Copyright (c) 1995-1999
;# Ikuo Nakagawa. All rights reserved.
;#
;# Redistribution and use in source and binary forms, with or without
;# modification, are permitted provided that the following conditions
;# are met:
;#
;# 1. Redistributions of source code must retain the above copyright
;# notice unmodified, this list of conditions, and the following
;# disclaimer.
;# 2. Redistributions in binary form must reproduce the above copyright
;# notice, this list of conditions and the following disclaimer in the
;# documentation and/or other materials provided with the distribution.
;#
;# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
;# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
;# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
;# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
;# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
;# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
;# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
;# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
;# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
;# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
;# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;#
;# $Id: rotate,v 1.6 1999/11/10 23:12:28 ikuo Exp $
;#
;# How to use `rotate' program:
;#
;# To rotate "/var/log/xxx.log" to "/var/log/xxx.log.old", and
;# create a new file "/var/log/xxx.log":
;# rotate /var/log/xxx.log
;#
;# If you want to rotate files with suffixes, try additional
;# argument for `rotate' command.
;# rotate /var/log/xxx.log 2 1 0
;#
;# You can specify the owner/group or file permission mode for
;# the new file (like `install' command):
;# rotate -o root -g wheel -m 644 /var/log/messages 2 1 0
;#
;# You can also compress rotated file with `gzip':
;# rotate -z /var/log/access.log 2 1 0
;#
;# or with `compress':
;# rotate -Z /var/log/access.log 2 1 0
;#
;# This is because we supports perl version 4.
require 'getopts.pl';
;# Get program name
($program) = ($0 =~ m%([^/]+)$%);
;# For zero based index.
$[ = 0;
;# Show debug log to STDOUT.
sub debug {
local($_); # used in grep.
grep((print "$_\n"), @_) if $opt_v;
}
;# Initialize options (for "perl -cw").
undef $opt_N;
undef $opt_T;
undef $opt_Z;
undef $opt_g;
undef $opt_m;
undef $opt_n;
undef $opt_o;
undef $opt_t;
undef $opt_v;
undef $opt_z;
;# Parsing options
unless (&Getopts("NTZg:m:no:tvz") && defined($target = shift)) {
die <<"END";
Usage: $program [options] path [suffix suffix ...]
Options:
-v verbose mode.
-n do not real work. only show processing.
-N do not create a new file.
-z compress with `gzip'.
-Z compress with `compress'.
-o specify owner.
-g specify group.
-m specify mode.
-T use `YYYY-MM-DD' (given by current time) as the default
suffix, instead of `old'.
-t use `YYYY-MM-DD' (from last modified time of the target)
as the default suffix, instead of `old'.
END
}
;# Test mode requires verbose option
$opt_v++ if $opt_n;
;# If no suffix was given, we generate default one.
unless (@ARGV) {
if ($opt_T || $opt_t) {
if ($opt_t && ! -e $target) {
die("$target must exist if -t flag is specified.\n");
}
$t = $opt_t ? (stat($target))[9] : time;
@t = reverse((localtime($t))[0..5]);
$t[0] += 1900;
$t[1]++;
@ARGV = (sprintf("%04d-%02d-%02d", @t));
} else {
@ARGV = ('old');
}
}
;# Rotate the target file.
&safe_rotate($target, @ARGV);
;# Touch the new one.
&safe_create($target) unless $opt_N;
;# Normal termination.
exit;
;# Touch a file. Create a new one if it does not exist.
sub touch {
local($a) = @_;
local(*FILE);
$a ne '' && open(FILE, '>>'.$a) && close(FILE) && -e $a;
}
;#
sub safe_unlink {
local($a) = @_;
if (-e $a) {
&debug("unlink \"$a\"");
$opt_n || unlink($a) || die("unlink($a): $!");
}
}
;#
sub safe_rename {
local($a, $b) = @_; # from, to
if (-e $a) {
&debug("rename \"$a\" to \"$b\"");
$opt_n || rename($a, $b) || die("rename($a, $b): $!");
}
}
;#
sub safe_compress {
local($a) = @_;
if (-z $a) { # compress will fail in this case
&debug("we won't compress zero-sized file: \"$a\"");
} else {
&debug("compress \"$a\"");
$opt_n || system('compress', $a) == 0
|| die("system(compress, $a): failure.\n");
}
}
;#
sub safe_gzip {
local($a) = @_;
&debug("gzip \"$a\"");
$opt_n || system('gzip', $a) == 0
|| die("system(gzip, $a): failure.\n");
}
;# Create a new one
sub safe_create {
local($a) = shift;
&debug("touch \"$a\"");
$opt_n || &touch($a) || die("touch($a): $!");
# set owner and group
if (defined($opt_o) || defined($opt_g)) {
local($uid, $gid) = (stat($a))[4, 5];
!defined($opt_o)
|| (($uid = $opt_o) =~ /^\d+$/)
|| defined($uid = getpwnam($opt_o))
|| die("getpwnam($opt_o): $!");
!defined($opt_g)
|| (($gid = $opt_g) =~ /^\d+$/)
|| defined($gid = getgrnam($opt_g))
|| die("getgrnam($opt_g): $!");
&debug("chown($uid, $gid, \"$a\")");
$opt_n || chown($uid, $gid, $a)
|| die("chown($a): $!");
}
# set file mode
if (defined($opt_m)) {
$opt_m =~ /^\d+$/ || die "illegal mode: $opt_m\n";
$opt_m = oct($opt_m);
&debug("chmod ".sprintf("%04o", $opt_m).", \"$a\"");
$opt_n || chmod($opt_m, $a) || die("chmod($a): $!");
}
# success.
1;
}
;# Rotate - do real work.
sub safe_rotate {
local($a) = shift;
# check existence, and suffixes
return 0 unless $a ne '' && -e $a && @_;
# log message
&debug("rotating \"$a\"");
# remove oldest one
local($b) = $a.'.'.shift;
&safe_unlink($b);
&safe_unlink($b.'.Z');
&safe_unlink($b.'.gz');
# loop to rotate files
while (@_) {
local($x) = $a.'.'.shift;
&safe_rename($x, $b);
&safe_rename($x.'.Z', $b.'.Z');
&safe_rename($x.'.gz', $b.'.gz');
$b = $x;
}
# rotate last one
&safe_rename($a, $b);
# shall we compress rotated one?
$opt_z ? &safe_gzip($b) : $opt_Z ? &safe_compress($b) : 1;
}
syntax highlighted by Code2HTML, v. 0.9.1