1#!/usr/local/bin/perl
2;#
3;# Copyright (c) 1995-1999
4;#	Ikuo Nakagawa. All rights reserved.
5;#
6;# Redistribution and use in source and binary forms, with or without
7;# modification, are permitted provided that the following conditions
8;# are met:
9;#
10;# 1. Redistributions of source code must retain the above copyright
11;#    notice unmodified, this list of conditions, and the following
12;#    disclaimer.
13;# 2. Redistributions in binary form must reproduce the above copyright
14;#    notice, this list of conditions and the following disclaimer in the
15;#    documentation and/or other materials provided with the distribution.
16;#
17;# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18;# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19;# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20;# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
21;# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22;# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
23;# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24;# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25;# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26;# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27;# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28;#
29;# $Id: rotate,v 1.6 1999/11/10 23:12:28 ikuo Exp $
30;#
31;# How to use `rotate' program:
32;#
33;#   To rotate "/var/log/xxx.log" to "/var/log/xxx.log.old", and
34;#   create a new file "/var/log/xxx.log":
35;#	rotate /var/log/xxx.log
36;#
37;#   If you want to rotate files with suffixes, try additional
38;#   argument for `rotate' command.
39;#	rotate /var/log/xxx.log 2 1 0
40;#
41;#   You can specify the owner/group or file permission mode for
42;#   the new file (like `install' command):
43;#	rotate -o root -g wheel -m 644 /var/log/messages 2 1 0
44;#
45;#   You can also compress rotated file with `gzip':
46;#	rotate -z /var/log/access.log 2 1 0
47;#
48;#   or with `compress':
49;#	rotate -Z /var/log/access.log 2 1 0
50;#
51;# This is because we supports perl version 4.
52require 'getopts.pl';
53
54;# Get program name
55($program) = ($0 =~ m%([^/]+)$%);
56
57;# For zero based index.
58$[ = 0;
59
60;# Show debug log to STDOUT.
61sub debug {
62	local($_); # used in grep.
63	grep((print "$_\n"), @_) if $opt_v;
64}
65
66;# Initialize options (for "perl -cw").
67undef $opt_N;
68undef $opt_T;
69undef $opt_Z;
70undef $opt_g;
71undef $opt_m;
72undef $opt_n;
73undef $opt_o;
74undef $opt_t;
75undef $opt_v;
76undef $opt_z;
77
78;# Parsing options
79unless (&Getopts("NTZg:m:no:tvz") && defined($target = shift)) {
80	die <<"END";
81Usage: $program [options] path [suffix suffix ...]
82Options:
83	-v	verbose mode.
84	-n	do not real work. only show processing.
85	-N	do not create a new file.
86	-z	compress with `gzip'.
87	-Z	compress with `compress'.
88	-o	specify owner.
89	-g	specify group.
90	-m	specify mode.
91	-T	use `YYYY-MM-DD' (given by current time) as the default
92		suffix, instead of `old'.
93	-t	use `YYYY-MM-DD' (from last modified time of the target)
94		as the default suffix, instead of `old'.
95END
96}
97
98;# Test mode requires verbose option
99$opt_v++ if $opt_n;
100
101;# If no suffix was given, we generate default one.
102unless (@ARGV) {
103	if ($opt_T || $opt_t) {
104		if ($opt_t && ! -e $target) {
105			die("$target must exist if -t flag is specified.\n");
106		}
107		$t = $opt_t ? (stat($target))[9] : time;
108		@t = reverse((localtime($t))[0..5]);
109		$t[0] += 1900;
110		$t[1]++;
111		@ARGV = (sprintf("%04d-%02d-%02d", @t));
112	} else {
113		@ARGV = ('old');
114	}
115}
116
117;# Rotate the target file.
118&safe_rotate($target, @ARGV);
119
120;# Touch the new one.
121&safe_create($target) unless $opt_N;
122
123;# Normal termination.
124exit;
125
126;# Touch a file. Create a new one if it does not exist.
127sub touch {
128	local($a) = @_;
129	local(*FILE);
130
131	$a ne '' && open(FILE, '>>'.$a) && close(FILE) && -e $a;
132}
133
134;#
135sub safe_unlink {
136	local($a) = @_;
137
138	if (-e $a) {
139		&debug("unlink \"$a\"");
140		$opt_n || unlink($a) || die("unlink($a): $!");
141	}
142}
143
144;#
145sub safe_rename {
146	local($a, $b) = @_; # from, to
147
148	if (-e $a) {
149		&debug("rename \"$a\" to \"$b\"");
150		$opt_n || rename($a, $b) || die("rename($a, $b): $!");
151	}
152}
153
154;#
155sub safe_compress {
156	local($a) = @_;
157
158	if (-z $a) { # compress will fail in this case
159		&debug("we won't compress zero-sized file: \"$a\"");
160	} else {
161		&debug("compress \"$a\"");
162		$opt_n	|| system('compress', $a) == 0
163			|| die("system(compress, $a): failure.\n");
164	}
165}
166
167;#
168sub safe_gzip {
169	local($a) = @_;
170
171	&debug("gzip \"$a\"");
172	$opt_n	|| system('gzip', $a) == 0
173		|| die("system(gzip, $a): failure.\n");
174}
175
176;# Create a new one
177sub safe_create {
178	local($a) = shift;
179
180	&debug("touch \"$a\"");
181	$opt_n || &touch($a) || die("touch($a): $!");
182
183	# set owner and group
184	if (defined($opt_o) || defined($opt_g)) {
185		local($uid, $gid) = (stat($a))[4, 5];
186		!defined($opt_o)
187			|| (($uid = $opt_o) =~ /^\d+$/)
188			|| defined($uid = getpwnam($opt_o))
189			|| die("getpwnam($opt_o): $!");
190		!defined($opt_g)
191			|| (($gid = $opt_g) =~ /^\d+$/)
192			|| defined($gid = getgrnam($opt_g))
193			|| die("getgrnam($opt_g): $!");
194		&debug("chown($uid, $gid, \"$a\")");
195		$opt_n	|| chown($uid, $gid, $a)
196			|| die("chown($a): $!");
197	}
198
199	# set file mode
200	if (defined($opt_m)) {
201		$opt_m =~ /^\d+$/ || die "illegal mode: $opt_m\n";
202		$opt_m = oct($opt_m);
203		&debug("chmod ".sprintf("%04o", $opt_m).", \"$a\"");
204		$opt_n || chmod($opt_m, $a) || die("chmod($a): $!");
205	}
206
207	# success.
208	1;
209}
210
211;# Rotate - do real work.
212sub safe_rotate {
213	local($a) = shift;
214
215	# check existence, and suffixes
216	return 0 unless $a ne '' && -e $a && @_;
217
218	# log message
219	&debug("rotating \"$a\"");
220
221	# remove oldest one
222	local($b) = $a.'.'.shift;
223	&safe_unlink($b);
224	&safe_unlink($b.'.Z');
225	&safe_unlink($b.'.gz');
226
227	# loop to rotate files
228	while (@_) {
229		local($x) = $a.'.'.shift;
230		&safe_rename($x, $b);
231		&safe_rename($x.'.Z', $b.'.Z');
232		&safe_rename($x.'.gz', $b.'.gz');
233		$b = $x;
234	}
235
236	# rotate last one
237	&safe_rename($a, $b);
238
239	# shall we compress rotated one?
240	$opt_z ? &safe_gzip($b) : $opt_Z ? &safe_compress($b) : 1;
241}
242