From pa.dec.com!decwrl!olivea!uunet!sparky!kent Thu Apr 4 17:27:02 PST 1991 Article: 2133 of comp.sources.misc Path: pa.dec.com!decwrl!olivea!uunet!sparky!kent From: scs@lokkur.dexter.mi.us (Steve Simmons) Newsgroups: comp.sources.misc Subject: v17i077: rotate_log - logfile management utility, Part01/01 Message-ID: <1991Apr4.032422.3329@sparky.IMD.Sterling.COM> Date: 4 Apr 91 03:24:22 GMT Sender: kent@sparky.IMD.Sterling.COM (Kent Landfield) Organization: Sterling Software, IMD Lines: 680 Approved: kent@sparky.imd.sterling.com X-Checksum-Snefru: 546b5905 408eb05e 3b323a11 c9900530 Submitted-by: Steve Simmons Posting-number: Volume 17, Issue 77 Archive-name: rotate_log/part01 Tired of writing yet another log trimming sequence in yet another shell script? Then this little ditty is for you. It rotates logs, compresses them, cleans out the old logs, kills the oldest logs, lets you invoke custom commands along the way, complains politely if anything is wrong, and has a hot cup of coffee waiting for you in the morning. OK, it won't do the coffee. But it does everything else, and has managed to worm it's way into crontabs all over the joint. Today I was putting it in to clean Yet Another Log, and saw that our operators are using it, our other system programmer is using it, etc, etc. They're even reading the man page. Since it's such a success at work, it must be time to throw it to the wolves, er, world. Enjoy! Steve Simmons scs@lokkur.dexter.mi.us ---- #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'ReadMe' <<'END_OF_FILE' XTired of writing yet another log trimming sequence in yet another shell Xscript? Then this little ditty is for you. It rotates logs, compresses Xthem, cleans out the old logs, kills the oldest logs, lets you invoke Xcustom commands along the way, complains politely if anything is wrong, Xand has a hot cup of coffee waiting for you in the morning. X XOK, it won't do the coffee. But it does everything else, and has managed Xto worm it's way into crontabs all over the joint. Today I was putting Xit in to clean Yet Another Log, and saw that our operators are using it, Xour other system programmer is using it, etc, etc. They're even Xreading the man page. Since it's such a success at work, it must Xbe time to throw it to the wolves, er, world. Enjoy! X XSteve Simmons Xscs@lokkur.dexter.mi.us XApril 3, 1991 END_OF_FILE if test 805 -ne `wc -c <'ReadMe'`; then echo shar: \"'ReadMe'\" unpacked with wrong size! fi # end of 'ReadMe' fi if test -f 'rotate_log' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'rotate_log'\" else echo shar: Extracting \"'rotate_log'\" \(10991 characters\) sed "s/^X//" >'rotate_log' <<'END_OF_FILE' X: X# Shell script to rotate a log. Usage: X# X# rotate_log log [ -L src_dir ] [ -n n ] [ -c ] [ -B backup_dir ] X# [ -b backup_name ] [ -m mode ] [ -o owner ] [ -g group ] X# [ -M mode ] [ -O owner ] [ -G group ] [ -e prolog_cmd ] X# [ -E postlog_cmd ] logfile X# X# where the switches mean: X# X# -L Name of the directory 'log' is found in. X# -n Number of backup copies to keep. X# -c Compress the backup copy. X# -B Dir to keep the backup copies in. X# -b Name of backup copy. X# -m Mode of new log. X# -o Owner of new log (only root can use this option). X# -g Group of new log (only root can use this option). X# -M Mode of backup copy. X# -O Owner of backup copy (only root can use this option). X# -G Group of backup copy (only root can use this option). X# -e Command to be executed immediately before copying the logfile. X# -E Command to be executed immediately after copying the logfile. X# X# Stylistic note -- any error that says 'Unexpected error' means I X# thought this particular error can't happen. X# X# This program is Copyright 1989, Steven C. Simmons. It may be freely X# redistributed provided this notice remains intact and any changes you X# make are clearly marked as such. X# X# $Source: /home/lokkur/scs/src/rotate_log/RCS/rotate_log,v $ X# X# $Revision: 0.5 $ X# X# $Author: scs $ $Date: 91/04/03 19:49:00 $ X# X# $State: Exp $ $Locker: $ X# X# $Log: rotate_log,v $ X# Revision 0.5 91/04/03 19:49:00 scs X# Parameterized locations of chmod, chgrp, chown. X# X# Revision 0.4 89/12/12 09:35:59 scs X# Bug fixes to pick up more defaults. X# X# Revision 0.3 89/12/11 21:34:25 scs X# Broke out commands separately to avoid ksh wierdness. Ported X# to system V.2 sh, ksh. Removed some BSDisms that makes it more X# portable. X# X# Revision 0.2 89/12/11 20:53:51 scs X# Improved error checking on source directory. Corrected erroneous X# error message. X# X# Revision 0.1 89/12/11 19:39:31 scs X# Initial revision. X# XSCRIPT=`basename $0` X# X# Commonly used commands X# XMV=/bin/mv XCP=/bin/cp XCHMOD=/bin/chmod XCHOWN=/etc/chown XCHGRP=/bin/chgrp X# X# Assign default values as needed X# XSOURCE_DIR="" XSOURCE_NAME="" XBACKUP_NAME="" XBACKUP_DIR="" XBACKUP_COUNT="3" XCOMPRESS="" XSOURCE_MODE="" XSOURCE_OWNER="" XSOURCE_GROUP="" XBACKUP_MODE="" XBACKUP_OWNER="" XBACKUP_GROUP="" X# X# Basic switch parsing. Right now, this forces us to use whitespace X# between switch char and actual execution. Whenever we get data X# for a switch, errorcheck it immediately. X# Xwhile [ "$#" -gt "0" ] ; do X case $1 in X -L ) if [ "$2" = "" ] ; then X echo "${SCRIPT}: The $1 switch requires a directory name. Aborting." X exit 0 X fi X if [ -w "${2}" -a -d "${2}" ] ; then X : X else X echo "${SCRIPT}: Sorry, you cannot modify the directory ${2}. Aborting." X exit 0 X fi X SOURCE_DIR="${2}/" X shift X ;; X -b ) if [ "$2" = "" ] ; then X echo "${SCRIPT}: The $1 switch requires a backup name. Aborting." X exit 0 X fi X BACKUP_NAME="$2" X shift X ;; X -B ) if [ "$2" = "" ] ; then X echo "${SCRIPT}: The $1 switch requires a directory name. Aborting." X exit 0 X fi X if [ -w "${2}" -a -d "${2}" ] ; then X : X else X echo "${SCRIPT}: Sorry, you cannot create files in ${DIR}. Aborting." X exit 0 X fi X BACKUP_DIR="${2}/" X shift X ;; X -n ) if [ "$2" = "" ] ; then X echo "${SCRIPT}: The $1 switch requires a backup count. Aborting." X exit 0 X fi X if echo $2 | grep '[^0-9]' > /dev/null 2>&1 ; then X echo "${SCRIPT}: The backup count ($2) must be a positive number. Aborting." X exit 0 X fi X if [ $2 -eq 0 ] ; then X echo "${SCRIPT}: The backup count ($2) must be greater than zero. Aborting." X exit 0 X fi X BACKUP_COUNT="$2" X shift X ;; X -c ) COMPRESS="compress" X ;; X -m ) if [ "$2" = "" ] ; then X echo "${SCRIPT}: The $1 switch requires a permissions mode. Aborting." X exit 0 X fi X SOURCE_MODE="$CHMOD $2" X shift X ;; X -M ) if [ "$2" = "" ] ; then X echo "${SCRIPT}: The $1 switch requires a permissions mode. Aborting." X exit 0 X fi X BACKUP_MODE="$CHMOD $2" X shift X ;; X -o ) if [ "$2" = "" ] ; then X echo "${SCRIPT}: The $1 switch requires the name of the new owner. Aborting." X exit 0 X fi X if grep '^${2}:' < /etc/passwd > /dev/null 2>&1 ; then X echo "${SCRIPT}: No such login id as $2. Aborting." X exit 0 X fi X SOURCE_OWNER="$CHOWN $2" X shift X ;; X -O ) if [ "$2" = "" ] ; then X echo "${SCRIPT}: The $1 switch requires the name of the new owner. Aborting." X exit 0 X fi X if grep '^${2}:' < /etc/passwd > /dev/null 2>&1 ; then X echo "${SCRIPT}: No such login id as $2. Aborting." X exit 0 X fi X BACKUP_OWNER="$CHOWN $2" X shift X ;; X -g ) if [ "$2" = "" ] ; then X echo "${SCRIPT}: The $1 switch requires the name of the new group. Aborting." X exit 0 X fi X if grep '^${2}:' < /etc/group > /dev/null 2>&1 ; then X echo "${SCRIPT}: No such group as $2. Aborting." X exit 0 X fi X SOURCE_GROUP="$CHGRP $2" X shift X ;; X -G ) if [ "$2" = "" ] ; then X echo "${SCRIPT}: The $1 switch requires the name of the new group. Aborting." X exit 0 X fi X if grep '^${2}:' < /etc/group > /dev/null 2>&1 ; then X echo "${SCRIPT}: No such group as $2. Aborting." X exit 0 X fi X BACKUP_GROUP="$CHGRP $2" X shift X ;; X -e ) if [ "$2" = "" ] ; then X echo "${SCRIPT}: The $1 switch requires a command to be executed. Aborting." X exit 0 X fi X CMD_BEFORE="${2}" X shift X ;; X -E ) if [ "$2" = "" ] ; then X echo "${SCRIPT}: The $1 switch requires a command to be executed. Aborting." X exit 0 X fi X CMD_AFTER="${2}" X shift X ;; X -* ) echo "${SCRIPT}: No such switch as $1. Aborting." X exit 0 X ;; X * ) if [ "${SOURCE_NAME}" != "" ] ; then X echo "${SCRIPT}: You must specify only one log file. Aborting." X exit 0 X else X SOURCE_NAME=$1 X fi X ;; X esac X shift Xdone Xif [ "${SOURCE_NAME}" = "" ] ; then X echo "${SCRIPT}: You must specify a log file. Aborting." X exit 0 Xfi X# X# Do sanity checks X# X# If he specified a source directory or backup directory, the source X# and backup file names cannot contain a slash. X# Xif [ "${SOURCE_DIR}" != "" ] ; then X if echo $SOURCE_NAME | grep / > /dev/null ; then X cat << EOF X${SCRIPT}: If you specify a source directory (eg, -L ${SOURCE_DIR}), Xthen the log file name (${SOURCE_NAME}) must be a simple file name, Xie, not containing any slashes. Aborting. XEOF X exit 0 X fi X # X # If they don't explicitly select a backup directory but the X # do specify a log directory, use the same for both. X # X if [ "${BACKUP_DIR}" = "" ] ; then X BACKUP_DIR="${SOURCE_DIR}" X fi Xfi Xif [ "${BACKUP_DIR}" != "" ] ; then X if echo $BACKUP_NAME | grep / > /dev/null ; then X cat << EOF X${SCRIPT}: If you specify a backup directory (eg, -B ${BACKUP_DIR}), Xthen the backup log name (${BACKUP_NAME}) must be a simple file name, Xie, not containing any slashes. Aborting. XEOF X exit 0 X fi Xfi X# X# Make sure we can mod the directory where the log is. X# Xif [ -w `dirname "${SOURCE_NAME}"` ] ; then X : Xelse X cat << EOF X${SCRIPT}: Sorry, you do not have permission to move/rename the Xlog file (file '`basename ${SOURCE_NAME}`', directory '`dirname ${SOURCE_NAME}`'). Aborting. XEOF X exit 0 Xfi X# X# Make sure we can read/write the log itself. X# XSOURCE_PATH="${SOURCE_DIR}${SOURCE_NAME}" Xif [ -w "${SOURCE_PATH}" -a -r "${SOURCE_PATH}" ] ; then X : Xelse X echo "${SCRIPT}: Sorry, you do not have permission to read and modify" X echo "the file '${SOURCE_PATH}'. Aborting." X exit 0 Xfi X# X# Make sure root operations are only run by root X# Xif [ "${BACKUP_OWNER}" != "" -o "${SOURCE_OWNER}" != "" -o "${BACKUP_GROUP}" != "" -o "${SOURCE_GROUP}" != "" ] Xthen X set `who am i` X if [ "root" != "$1" ] ; then X echo "${SCRIPT}: Sorry, you can only change owner or group if you are root." X echo "Aborting." X exit 0 X fi Xfi X# X# Build the list of backup names, in reverse order. Make them X# full pathnames. X# Xif [ "${BACKUP_NAME}" = "" ] ; then X BACKUP_NAME="${SOURCE_NAME}" Xfi Xwhile expr $BACKUP_COUNT \> 0 > /dev/null ; do X BACKUP_COUNT=`expr $BACKUP_COUNT - 1` X BACKUP_FILES="${BACKUP_FILES} ${BACKUP_DIR}${BACKUP_NAME}.${BACKUP_COUNT}" Xdone X# X# Rotate the existing backups. Assume no change in permissions, etc. X# Xset ${BACKUP_FILES} Xif [ "${1}" = "" ] ; then X echo "${SCRIPT}: unexpected error in using backup list. Aborting." X exit 0 Xfi XOLDEST="${1}" Xshift Xwhile [ "$1" != "" ] ; do X NEXT="${1}" X rm -f "${OLDEST}" "${OLDEST}.Z" X if [ -f "${NEXT}" ] ; then X ${MV} "${NEXT}" "${OLDEST}" X elif [ -f "${NEXT}.Z" ] ; then X ${MV} "${NEXT}.Z" "${OLDEST}.Z" X else X echo "${SCRIPT}: Missing backup log ${NEXT} or ${NEXT}.Z. Continuing." X fi X OLDEST="${NEXT}" X shift Xdone X# X# Copy the current log to be the first backup. Use full pathnames X# as appropriate. X# Xif [ "${SOURCE_DIR}" != "" ] ; then X if cd "${SOURCE_DIR}" ; then X : X else X echo "${SCRIPT}: unexpected error in command 'cd ${SOURCE_DIR}'. Aborting." X exit 0 X fi Xfi Xrm -f "${NEXT}" "${NEXT}.Z" Xif [ "${CMD_BEFORE}" != "" ] ; then X ${CMD_BEFORE} Xfi Xif ${MV} "${SOURCE_NAME}" "${NEXT}" ; then X : Xelse X echo "${SCRIPT}: unexpected error making first backup. Aborting." X exit 0 Xfi Xif [ "${CMD_AFTER}" != "" ] ; then X ${CMD_AFTER} Xfi Xrm -f "${SOURCE_NAME}" "${SOURCE_NAME}".Z Xif cat > "${SOURCE_NAME}" < /dev/null ; then X : Xelse X echo "${SCRIPT}: unexpected error emptying log file. Continuing." Xfi X# X# Set ownership and permission on the log as requested. X# Xif [ "${SOURCE_MODE}" != "" ] ; then X if ${CHMOD} ${SOURCE_MODE} "${SOURCE_NAME}" ; then X : X else X echo "${SCRIPT}: unexpected error executing command '${CHMOD} ${SOURCE_MODE} ${SOURCE_NAME}'." X echo "Aborting." X exit 0 X fi Xfi Xif [ "${SOURCE_OWNER}" != "" ] ; then X if ${CHOWN} ${SOURCE_OWNER} "${SOURCE_NAME}" ; then X : X else X echo "${SCRIPT}: unexpected error executing command '${CHOWN} ${SOURCE_OWNER} ${SOURCE_NAME}'." X echo "Aborting." X exit 0 X fi Xfi Xif [ "${SOURCE_GROUP}" != "" ] ; then X if ${CHGRP} ${SOURCE_GROUP} "${SOURCE_NAME}" ; then X : X else X echo "${SCRIPT}: unexpected error executing command '${CHGRP} ${SOURCE_GROUP} ${SOURCE_NAME}'." X echo "Aborting." X exit 0 X fi Xfi X# X# Set ownerships and permissions as requested on the backed up log. X# Note that the compress must come last in this sequence. X# Xif [ "${BACKUP_MODE}" != "" ] ; then X if ${CHMOD} ${BACKUP_MODE} "${NEXT}" ; then X : X else X echo "${SCRIPT}: unexpected error executing command '${CHMOD} ${BACKUP_MODE} ${NEXT}'." X echo "Aborting." X exit 0 X fi Xfi Xif [ "${BACKUP_OWNER}" != "" ] ; then X if ${CHOWN} ${BACKUP_OWNER} "${NEXT}" ; then X : X else X echo "${SCRIPT}: unexpected error executing command '${CHOWN} ${BACKUP_OWNER} ${NEXT}'." X echo "Aborting." X exit 0 X fi Xfi Xif [ "${BACKUP_GROUP}" != "" ] ; then X if ${CHGRP} ${BACKUP_GROUP} "${NEXT}" ; then X : X else X echo "${SCRIPT}: unexpected error executing command '${CHGRP} ${BACKUP_GROUP} ${NEXT}'." X echo "Aborting." X exit 0 X fi Xfi Xif [ "${COMPRESS}" != "" ] ; then X if ${COMPRESS} "${NEXT}" ; then X : X else X echo "${SCRIPT}: unexpected error executing command '${COMPRESS} ${NEXT}'." X echo "Aborting." X exit 0 X fi Xfi END_OF_FILE if test 10991 -ne `wc -c <'rotate_log'`; then echo shar: \"'rotate_log'\" unpacked with wrong size! fi chmod +x 'rotate_log' # end of 'rotate_log' fi if test -f 'rotate_log.8' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'rotate_log.8'\" else echo shar: Extracting \"'rotate_log.8'\" \(3321 characters\) sed "s/^X//" >'rotate_log.8' <<'END_OF_FILE' X.\" Manual page for rotate_log X.\" X.\" $Source: /home/lokkur/scs/src/rotate_log/RCS/rotate_log.8,v $ X.\" X.\" $Revision: 1.3 $ X.\" X.\" $Author: scs $ $Date: 89/12/11 17:54:30 $ X.\" X.\" $State: Exp $ $Locker: scs $ X.\" X.\" $Log: rotate_log.8,v $ X."\ Revision 1.3 89/12/11 17:54:30 scs X."\ Added prolog and postlog commands. X."\ X."\ Revision 1.2 89/12/11 17:34:46 scs X."\ Corrected minor format errors. X."\ X."\ Revision 1.1 89/12/11 17:29:36 scs X."\ Initial revision X."\ X.TH ROTATE_LOG 8 89/12/11 X.SH NAME Xrotate_log \- rotate logfiles, removing the oldest X.SH SYNOPSIS X.B Xrotate_log log [ \-L log_dir ] [ \-n n ] [ \-c ] [ \-B backup_dir ] [ \-b backup_name ] [ \-m mode ] [ \-o owner ] [ \-g group ] [ \-M mode ] [ \-O owner ] [ \-G group ] [ -e "prolog_command" ] [ -E "postlog_command" ] logfile X.R X.SH DESCRIPTION X.B Rotate_log Xtakes a lot of the drudgery out of managing logfiles. XIt assume you have a log file X.I logfile Xand a series of backup copies X.I "logfile.0," X.I "logfile.1," Xetc, Xwhere X.I 0 Xis the newest backup and X.I N Xis the oldest. X.B Rotate_log Xwill remove the oldest, Xrename the rest, Xand empty out the original X.I logfile. XIt has lots of options to let you control things: X.TP X.B "\-L log_dir" XAssume the X.I logfile Xis in a directory named X.I log_dir. X.TP X.B "\-n n" XUse X.I n Xbackups. XIf you don't set this, it defaults to three. X.TP X.B "\-c" X.I Compress(1) Xthe backup copies. X.TP X.B "\-B backup_dir" XPut the backed up log copies in the directory X.I backup_dir. X.TP X.B "\-b backup_name" XName the backed up log copies X.I backup_name. XNote you can use both the X.B \-B Xand X.B \-b Xswitches at the same time. X.TP X.B "\-m mode" XSet the mode (permissions) on X.I logfile Xto X.I mode. X.TP X.B "\-o owner" XSet the ownership of X.I logfile Xto X.I owner X(this option is only available to X.I root ). X.TP X.B "\-g group" XSet the group membership of X.I logfile Xto X.I group X(this option is only available to X.I root ). X.TP X.B "\-M mode" XSet the mode (permissions) on X.I logfile.0 Xto X.I mode. X.TP X.B "\-O owner" XSet the ownership of X.I logfile.0 Xto X.I owner X(this option is only available to X.I root ). X.TP X.B "\-O group" XSet the group membership of X.I logfile.0 Xto X.I group X(this option is only available to X.I root ). X.TP X.B "\-e prolog_command" X.I prolog_command Xis a command to be executed just before moving X.I logfile Xto X.I logfile.0. XNote that if X.I prolog_command Xhas spaces, blanks, or metacharacters they must be quoted. X.TP X.B "\-E postlog_command" X.I prolog_command Xis a command to be executed immediately after emptying X.I logfile XNote it is only executed if X.I logfile Xis successfully emptied. XNote also that if X.I prolog_command Xhas spaces, blanks, or metacharacters they must be quoted. X.PP XNote that the mode and ownership settings apply only the the newly Xcreated backups. XIt is assumed that all older backups are already set as desired. X.SH "ERROR MESSAGES" XHopefully self-explaining. XThis command bends over backwards checking for permission before Xtrying things, Xand tries very hard to be informative when something isn't right. X.SH FILES XNone. X.SH AUTHOR XSteve Simmons X.SH "COPYRIGHT STATUS" XCopyright 1989 Steve Simmons X.I (scs@lokkur.dexter.mi.us). XFreely redistributable provided this copyright remains intact. X.SH BUGS XPlease report them to the author, scs@lokkur.dexter.mi.us. END_OF_FILE if test 3321 -ne `wc -c <'rotate_log.8'`; then echo shar: \"'rotate_log.8'\" unpacked with wrong size! fi # end of 'rotate_log.8' fi echo shar: End of shell archive. exit 0 exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.