#!/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; }