#!/bin/bash # logrotate - rotate log files # Copyright 2001, Chris F.A. Johnson # This program is 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, or (at your option) # any later version. # This program 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: # http://www.gnu.org/copyleft/gpl.html usage() { if [ -n "$1" ] then echo " $progname: $*" else echo " $progname - rotate log files" fi if [ $verbose -eq 1 ] then echo " LogRotate is a log rotation script. Run this from cron on a periodic basis (daily, weekly, monthly) etc. " fi echo " USAGE: $progname [OPTIONS] [log-control-file] OPTIONS: -c ZIP - use ZIP (gzip or bzip2) to compress rotated files -d DIR - place rotated files in directory DIR -f FILE - rotate FILE; do not use a control file -k N - keep last N lines of the logfile -n N - keep N log files (default 5) The above options do not override options in a control file unless: -o - override control file options" if [ $verbose -eq 0 ] then echo " -h - help: print this message -H - help: print more detailed message" else echo " -h - help: print shorter message -H - help: print this message" fi echo " -v - verbose: -V - print version information " if [ $verbose -eq 1 ] then echo " If no control file is given, $progname looks for ./.logrotate.conf then \$HOME/.logrotate.conf then /etc/logrotate.conf, and uses the first one it finds. The log-control-file is a text file containing a list of log files to rotate, wherein each entry is on a separate line and each entry contains the name (absolute or relative path: see NOTE) of the log file that needs to be rotated. Each line may also contain options, separated by tabs: dir=D - put rotated files in directory D. The name of the logfile is expanded to the full path and the slashes are changed to dashes. files=N - keep N log files keep=N - keep the last N lines in the file (This leaves them in the rotated file as well; I may change the behaviour to remove them from the rotated file, or add an option to do that.) gzip bzip2 - compress the log files with gzip or bzip2 size=N - do not rotate if the file is less than N bytes or lines N is an integer optionally ending with a letter: B - bytes (default) K - append 000 to the number M - append 000000 to the number G - append 000000000 to the number L - refer to number of lines rather than bytes # --- sample .logrotate.conf --- # Filename = /var/log/foo # Compress with gzip # Maximum number of rotated files = 5 # Keep the last 25 lines of the log file # Don't rotate if the log file contains fewer than 15,000 lines /var/log/foo gzip files=5 keep=25 size=15000L # Filename = ./mylog # All the defaults are used mylog # ---- end .logrotate.conf ---- NOTE: Files in \$HOME/.logrotate.conf and /etc/logrotate.conf should be absolute paths, since the working directory could be anywhere (unless a file of a certain name should be rotated wherever it is found)." fi echo " Copyright 2001, Chris F.A. Johnson Please send corrections, improvements, bugs reports, suggestions, etc., to cfaj@freeshell.org Logrotate is derived from a script by javadesigner@yahoo.com with modifications by Heiner Steven, but there is very little of the original code left. This script may be copied under the terms of the GNU General Public License" } fullpath() { filename=${1##*/} dirname=${1%/*} ( cd "$dirname" && echo "`pwd`/$filename" ) } die() { echo "$progname: $*" >&2 exit 1 } rotateOneFile() { logfile=$1 if [ -n "$savedir" ] then savelog="$savedir/`fullpath $logfile | tr '/' '-'`" else savelog=$logfile fi i=${maxno:-$default_maxno} while [ $i -ge 1 ] do newname="$savelog.$i$suffix" i=$(( $i - 1 )) oldlogfile=$savelog.$i$suffix if [ -r "$oldlogfile" ] then mv -f "$oldlogfile" "$newname" || die "Could not move \"$oldlogfile\" to \"$newname\"" if [ $verbose -ge 1 ] then echo "\"$oldlogfile\" --> \"$newname\"" fi fi done newlogfile="$savelog.0" mv "$logfile" "$newlogfile" || die "Could not move \"$logfile\" to \"$newlogfile\"" if [ $verbose -ge 1 ] then echo "\"$logfile\" --> \"$newlogfile\"" fi if [ $keep -gt 0 ] then tail -$keep "$newlogfile" > "$logfile" else > $logfile fi if [ "$compress" ] then rm -f "$newlogfile$suffix" $compress $OPT "$newlogfile" fi return 0 } version() { echo $version } verbose=0 longusage=0 version="0.5" copyright=2001 author="Chris F.A. Johnson" progname=${0##*/} TAB=`printf "\t"` default_maxno=5 override=0 keep=0 suffix= compress= filename= savedir= while getopts :vVhHf:n:c:k:od: var do case "$var" in c) compress=$OPTARG ;; d) savedir=$OPTARG ;; f) logfile=$OPTARG ;; k) keep=$OPTARG ;; n) maxno=$OPTARG ;; o) override=1 ;; h) usage; exit ;; H) verbose=$(( verbose + 1 )); usage; exit ;; v) verbose=$(( verbose + 1 )) ## allow levels of verbosity OPT="-v" ;; V) version; exit ;; *) usage "Illegal option -- $OPTARG" >&2 exit 1 ;; esac done shift $(( $OPTIND - 1 )) if [ $verbose -eq 3 ] then set -x fi RETURN=0 if [ -n "$filename" ] then if [ -r "$filename" ] then rotateOneFile "$filename" elif [ -f "$filename" ] then echo "$progname: \"$filename\" is not readable" >&2 RETURN=1 else echo "$progname: \"$filename\" not found" >&2 RETURN=1 fi exit $RETURN fi if [ $# -eq 0 ] then if [ -r ./.logrotate.conf ] then controlfile=./.logrotate.conf elif [ -r $HOME/.logrotate.conf ] then controlfile=HOME/.logrotate.conf elif [ -r /etc/logrotate.conf ] then controlfile=/etc/logrotate.conf else echo "$progname: could not find a control file" >&2 exit 1 fi fi if [ $verbose -ge 1 ] then echo "Using ${controlfile}" fi IFS=$TAB while read filename options do case "$filename" in [^/a-zA-Z0-9]\#*|\#*|"") continue ;; ## skip comments & empty lines esac if [ $verbose -ge 1 ] then echo "FILE: $filename" if [ $verbose -ge 2 ] then echo "OPTIONS: $options" fi fi [ -f "$filename" ] || { # ignore non-existent files if [ $verbose -ge 1 ] then echo "$filename not found" >&2 fi continue } if [ $override -eq 0 ] then suffix= compress= units= savedir= size=0 filesize=0 keep=0 maxno=$default_maxno for opt in $options do if [ $verbose -ge 1 ] then echo $opt fi case $opt in files=*) maxno=${opt#*=} ;; keep=*) keep=${opt#*=} ;; gzip) compress=gzip suffix=".gz" ;; bzip2) compress=bzip2 suffix=".bz2" ;; dir=*) savedir=${opt#*=} ;; size=*) size="${opt#*=}" units=bytes if [ "`uname`" = "SunOS" ] then SUN="-g" else SUN="" fi filesize=`ls -l $SUN $filename | { IFS=" " read a b c d size f echo $size }` case $size in [0-9]*B) size="${size%B}" ;; [0-9]*K) size="${size%K}000" ;; [0-9]*M) size="${size%M}000000" ;; [0-9]*G) size="${size%G}000000000" ;; [0-9]*L) size="${size%L}" filesize=$(( `wc -l < $filename` + 0 )) units=lines ;; esac if [ $filesize -lt $size ] then if [ $verbose -ge 1 ] then echo "Skipping $filename ($filesize < ${size%[A-Za-z]})" fi continue 2 else if [ $verbose -ge 1 ] then echo "$filename ( ${filesize#[ ]} >= $size $units )" fi fi ;; esac done fi if [ $verbose -ge 1 ] then if [ -n "$compress" ] then echo "Compression enabled, using $compress" fi fi rotateOneFile "$filename" done < "$controlfile"