ossp-pkg/shtool/sh.rotate
1.3
##
## rotate -- Logfile rotation
## Copyright (c) 2001-2002 Ralf S. Engelschall <rse@engelschall.com>
## Originally written for use in OpenPKG
##
## This file is part of shtool and free software; you can redistribute
## it and/or modify it under the terms of the GNU General Public
## License as published by the Free Software Foundation; either version
## 2 of the License, or (at your option) any later version.
##
## This file is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
## USA, or contact Ralf S. Engelschall <rse@engelschall.com>.
##
str_tool="rotate"
str_usage="[-v|--verbose] [-t|--trace] [-f|--force] [-n|--num-files <count>] [-s|--size <size>] [-c|--copy] [-r|--remove] [-a|--archive-dir <dir>] [-z|--compress [<tool>:]<level>] [-b|--background] [-d|--delay] [-p|--pad <len>] [-o|--owner <owner>] [-g|--group <group>] [-m|--mode <mode>] [-M|--migrate <cmd>] [-P|--prolog <cmd>] [-E|--epilog <cmd>] <file> [...]"
arg_spec="1+"
opt_spec="v.t.f.n:s:c.r.a:z:b.d.p:o:g:m:M:P:E:"
opt_alias="v:verbose,t:trace,f:force,n:num-files,s:size,c:copy,r:remove,a:archive-dir,z:compress,b:background,d:delay,p:pad,o:owner,g:group,m:mode,M:migrate,P:prolog,E:epilog"
opt_v=no
opt_t=no
opt_f=no
opt_n=10
opt_s=""
opt_c=no
opt_r=no
opt_a=""
opt_z=""
opt_b=no
opt_d=no
opt_p=1
opt_o=""
opt_g=""
opt_m=""
opt_M=""
opt_P=""
opt_E=""
. ./sh.common
# make sure we have at least one file to rotate
if [ ".$opt_n" = .0 ]; then
echo "$msgprefix:Error: invalid argument \`$opt_n' to option -n." 1>&2
exit 1
fi
# canonicalize -s option argument
if [ ".$opt_s" != . ]; then
if [ ".`expr $opt_s : '[0-9]*$'`" != .0 ]; then
:
elif [ ".`expr $opt_s : '[0-9]*[Kk]$'`" != .0 ]; then
opt_s=`expr $opt_s : '\([0-9]*\)[Kk]$'`
opt_s=`expr $opt_s \* 1024`
elif [ ".`expr $opt_s : '[0-9]*[Mm]$'`" != .0 ]; then
opt_s=`expr $opt_s : '\([0-9]*\)[Mm]$'`
opt_s=`expr $opt_s \* 1048576` # 1024*1024
elif [ ".`expr $opt_s : '[0-9]*[Gg]$'`" != .0 ]; then
opt_s=`expr $opt_s : '\([0-9]*\)[Gg]$'`
opt_s=`expr $opt_s \* 1073741824` # 1024*1024*1024
else
echo "$msgprefix:Error: invalid argument \`$opt_s' to option -s." 1>&2
exit 1
fi
fi
# option -d/-z consistency
if [ ".$opt_d" = .yes -a ".$opt_z" = . ]; then
echo "$msgprefix:Error: option -d requires option -z." 1>&2
exit 1
fi
# make sure target directory exists
if [ ".$opt_a" != . ]; then
if [ ! -d $opt_a ]; then
if [ ".$opt_f" = .no ]; then
echo "$msgprefix:Error: archive directory \`$opt_a' does not exist." 1>&2
exit 1
fi
mkdir $opt_a || exit $?
chmod 755 $opt_a
fi
if [ ! -w $opt_a ]; then
echo "$msgprefix:Error: archive directory \`$opt_a' not writable." 1>&2
exit 1
fi
fi
# determine compression approach
if [ ".$opt_z" != . ]; then
comp_lvl="$opt_z"
comp_prg=""
case $comp_lvl in
*:* ) eval `echo $comp_lvl |\
sed -e 's%^\(.*\):\(.*\)$%comp_prg="\1"; comp_lvl="\2"%'` ;;
esac
# compression level consistency
case $comp_lvl in
[0-9] )
;;
* ) echo "$msgprefix:Error: invalid compression level \`$comp_lvl'" 1>&2
exit 1
;;
esac
# determine a suitable compression tool
if [ ".$comp_prg" = . ]; then
# check whether the test command supports the -x option
if [ -x /bin/sh ] 2>/dev/null; then
minusx="-x"
else
minusx="-r"
fi
# search for tools in $PATH
paths="`echo $PATH |\
sed -e 's%/*:%:%g' -e 's%/*$%%' \
-e 's/^:/.:/' -e 's/::/:.:/g' -e 's/:$/:./' \
-e 's/:/ /g'`"
for prg in bzip2 gzip compress; do
for path in $paths; do
if [ $minusx "$path/$prg" -a ! -d "$path/$prg" ]; then
comp_prg="$prg"
break
fi
done
if [ ".$comp_prg" != . ]; then
break
fi
done
if [ ".$comp_prg" = . ]; then
echo "$msgprefix:Error: no suitable compression tool found in \$PATH" 1>&2
exit 1
fi
fi
# determine standard compression extension
# and make sure it is a known tool
case $comp_prg in
*/bzip2 | bzip2 ) comp_ext="bz2" comp_lvl="-$comp_lvl" ;;
*/gzip | gzip ) comp_ext="gz" comp_lvl="-$comp_lvl" ;;
*/compress | compress ) comp_ext="Z"; comp_lvl="" ;;
* ) echo "$msgprefix:Error: tool \`$comp_prg' is not a known compression tool" 1>&2
exit 1
;;
esac
comp_suf=".$comp_ext"
fi
# iterate over all given logfile arguments
for file in $*; do
# make sure the logfile exists
if [ ! -f $file ]; then
if [ ".$opt_f" = .yes ]; then
continue
fi
echo "$msgprefix:Error: logfile \`$file' not found" 1>&2
exit 1
fi
# determine log directory (where original logfile is placed)
ldir="."
case $file in
*/* ) eval `echo $file | sed -e 's%^\(.*\)/\([^/]*\)$%ldir="\1"; file="\2";%'` ;;
esac
# determine archive directory (where rotated logfiles are placed)
adir="$ldir"
if [ ".$opt_a" != . ]; then
case "$opt_a" in
/* | ./* ) adir="$opt_a" ;;
* ) adir="$ldir/$opt_a" ;;
esac
fi
# optionally take logfile size into account
if [ ".$opt_s" != . ]; then
# determine size of logfile
set -- `ls -l $ldir/$file | sed -e 's; -> .*$;;' -e 's;[ ][ ]*; ;g'`
n=`expr $# - 4`
eval "size=\`echo \${$n}\`"
# skip logfile if size is still too small
if [ $size -lt $opt_s ]; then
if [ ".$opt_v" = .yes ]; then
echo "$ldir/$file: still too small in size -- skipping"
fi
continue
fi
fi
# verbosity
if [ ".$opt_v" = .yes ]; then
echo "rotating $ldir/$file"
fi
# execute prolog
if [ ".$opt_P" != . ]; then
if [ ".$opt_t" = .yes ]; then
echo "$opt_P"
fi
eval $opt_P
[ $? -ne 0 ] && exit $?
fi
# kick away out-rotated logfile
n=`expr $opt_n - 1`
n=`echo dummy | awk "{ printf(\"%0${opt_p}d\", n); }" n=$n`
if [ -f "${adir}/${file}.${n}${comp_suf}" ]; then
# optionally migrate away the out-rotated logfile
if [ ".$opt_M" != . ]; then
if [ ".$opt_t" = .yes ]; then
echo "$opt_M ${adir}/${file}.${n}${comp_suf}"
fi
eval "$opt_M ${adir}/${file}.${n}${comp_suf}"
[ $? -ne 0 ] && exit $?
fi
# finally get rid of the out-rotated logfile
if [ ".$opt_t" = .yes ]; then
echo "rm -f ${adir}/${file}.${n}${comp_suf}"
fi
rm -f ${adir}/${file}.${n}${comp_suf} || exit $?
fi
# rotate already archived logfiles
while [ $n -gt 0 ]; do
m=$n
n=`expr $n - 1`
n=`echo dummy | awk "{ printf(\"%0${opt_p}d\", n); }" n=$n`
if [ $n -eq 0 -a ".$opt_d" = .yes ]; then
# special case: first rotation file under delayed compression situation
if [ ! -f "${adir}/${file}.${n}" ]; then
continue
fi
# compress file (delayed)
if [ ".$opt_b" = .yes ]; then
if [ ".$opt_t" = .yes ]; then
echo "mv ${adir}/${file}.${n} ${adir}/${file}.${m}"
fi
mv ${adir}/${file}.${n} ${adir}/${file}.${m} || exit $?
if [ ".$opt_t" = .yes ]; then
echo "(${comp_prg} ${comp_lvl} <${adir}/${file}.${m} >${adir}/${file}.${m}${comp_suf}; rm -f ${adir}/${file}.${m}) &"
fi
( ${comp_prg} ${comp_lvl} \
<${adir}/${file}.${m} \
>${adir}/${file}.${m}${comp_suf} || exit $?
rm -f ${adir}/${file}.${m} || exit $?
) </dev/null >/dev/null 2>&1 &
else
if [ ".$opt_t" = .yes ]; then
echo "${comp_prg} ${comp_lvl} <${adir}/${file}.${n} >${adir}/${file}.${m}${comp_suf}"
fi
${comp_prg} ${comp_lvl} \
<${adir}/${file}.${n} \
>${adir}/${file}.${m}${comp_suf} || exit $?
if [ ".$opt_t" = .yes ]; then
echo "rm -f ${adir}/${file}.${n}"
fi
rm -f ${adir}/${file}.${n} || exit $?
fi
# fix file attributes
if [ ".$opt_o" != . ]; then
if [ ".$opt_t" = .yes ]; then
echo "chown $opt_o ${adir}/${file}.${m}${comp_suf}"
fi
chown $opt_o ${adir}/${file}.${m}${comp_suf} || exit $?
fi
if [ ".$opt_g" != . ]; then
if [ ".$opt_t" = .yes ]; then
echo "chgrp $opt_g ${adir}/${file}.${m}${comp_suf}"
fi
chgrp $opt_g ${adir}/${file}.${m}${comp_suf} || exit $?
fi
if [ ".$opt_m" != . ]; then
if [ ".$opt_t" = .yes ]; then
echo "chmod $opt_m ${adir}/${file}.${m}${comp_suf}"
fi
chmod $opt_m ${adir}/${file}.${m}${comp_suf} || exit $?
fi
else
# standard case: second and following rotation file
if [ ! -f "${adir}/${file}.${n}${comp_suf}" ]; then
continue
fi
if [ ".$opt_t" = .yes ]; then
echo "mv ${adir}/${file}.${n}${comp_suf} ${adir}/${file}.${m}${comp_suf}"
fi
mv ${adir}/${file}.${n}${comp_suf} ${adir}/${file}.${m}${comp_suf} || exit $?
fi
done
# move away current logfile
if [ ".$opt_c" = .yes ]; then
# approach: copy[+truncate]
if [ ".$opt_t" = .yes ]; then
echo "cp -p ${ldir}/${file} ${adir}/${file}.${n}"
fi
cp -p ${ldir}/${file} ${adir}/${file}.${n} || exit $?
if [ ".$opt_r" = .no ]; then
if [ ".$opt_t" = .yes ]; then
echo "cp /dev/null ${ldir}/${file}"
fi
cp /dev/null ${ldir}/${file} || exit $?
fi
else
# approach: move[+touch]
if [ ".$opt_t" = .yes ]; then
echo "mv ${ldir}/${file} ${adir}/${file}.${n}"
fi
mv ${ldir}/${file} ${adir}/${file}.${n} || exit $?
if [ ".$opt_r" = .no ]; then
if [ ".$opt_t" = .yes ]; then
echo "touch ${ldir}/${file}"
fi
touch ${ldir}/${file} || exit $?
# fix file attributes
if [ ".$opt_o" != . ]; then
if [ ".$opt_t" = .yes ]; then
echo "chown $opt_o ${ldir}/${file}"
fi
chown $opt_o ${ldir}/${file} || exit $?
fi
if [ ".$opt_g" != . ]; then
if [ ".$opt_t" = .yes ]; then
echo "chgrp $opt_g ${ldir}/${file}"
fi
chgrp $opt_g ${ldir}/${file} || exit $?
fi
if [ ".$opt_m" != . ]; then
if [ ".$opt_t" = .yes ]; then
echo "chmod $opt_m ${ldir}/${file}"
fi
chmod $opt_m ${ldir}/${file} || exit $?
fi
fi
fi
# regular compression step
if [ ".$opt_z" != . -a ".$opt_d" = .no ]; then
# compress file
if [ ".$opt_b" = .yes ]; then
if [ ".$opt_t" = .yes ]; then
echo "(${comp_prg} ${comp_lvl} <${adir}/${file}.${n} >${adir}/${file}.${n}${comp_suf}; rm -f ${adir}/${file}.${n}) &"
fi
( ${comp_prg} ${comp_lvl} \
<${adir}/${file}.${n} \
>${adir}/${file}.${n}${comp_suf} || exit $?
rm -f ${adir}/${file}.${n} || exit $?
) </dev/null >/dev/null 2>&1 &
else
if [ ".$opt_t" = .yes ]; then
echo "${comp_prg} ${comp_lvl} <${adir}/${file}.${n} >${adir}/${file}.${n}${comp_suf}"
fi
${comp_prg} ${comp_lvl} \
<${adir}/${file}.${n} \
>${adir}/${file}.${n}${comp_suf} || exit $?
if [ ".$opt_t" = .yes ]; then
echo "rm -f ${opt_a}${file}.${n}"
fi
rm -f ${adir}/${file}.${n} || exit $?
fi
# fix file attributes
if [ ".$opt_o" != . ]; then
if [ ".$opt_t" = .yes ]; then
echo "chown $opt_o ${adir}/${file}.${n}${comp_suf}"
fi
chown $opt_o ${adir}/${file}.${n}${comp_suf} || exit $?
fi
if [ ".$opt_g" != . ]; then
if [ ".$opt_t" = .yes ]; then
echo "chgrp $opt_g ${adir}/${file}.${n}${comp_suf}"
fi
chgrp $opt_g ${adir}/${file}.${n}${comp_suf} || exit $?
fi
if [ ".$opt_m" != . ]; then
if [ ".$opt_t" = .yes ]; then
echo "chmod $opt_m ${adir}/${file}.${n}${comp_suf}"
fi
chmod $opt_m ${adir}/${file}.${n}${comp_suf} || exit $?
fi
fi
# execute epilog
if [ ".$opt_E" != . ]; then
if [ ".$opt_t" = .yes ]; then
echo "$opt_E"
fi
eval $opt_E
[ $? -ne 0 ] && exit $?
fi
done