SUMMARY: wtmpx rotater

Richard Dows (marius@randomc.com)
Tue, 16 Sep 1997 17:33:53 -0400

This is a multipart MIME message.

--Boundary_(ID_y0JZY7Nj9jCTs8cs0ghFZQ)
Content-type: text/plain; CHARSET=US-ASCII

Original Question:
> From marius@tequila.randomc.com Tue Sep 9 01:15:33 1997
>
> Has anyone got a utility that rotates the utmp, utmpx, wtmp, wtmpx files
> correctly ( without messing up the 'w', 'finger', 'who' commands ) ? If you
> do, I'd appreciate a copy of it if at all possible. Thanks ;)
>
> R. Dows <marius@randomc.com>
> SysAdmin
>

Here are the replies I received:

>From Karl Vogel (vogelke@c17mis.region2.wpafb.af.mil)

#!/bin/sh
#
# $Id: rotate_log,v 1.3 1995/06/16 16:27:56 vogelke Exp $
# $Source: /source/usr/local/cron/log/RCS/rotate_log,v $
#
# Shell script to rotate a log. Usage:
#
# rotate_log log [ -L src_dir ] [ -n n ] [ -c ] [ -B backup_dir ]
# [ -b backup_name ] [ -m mode ] [ -o owner ] [ -g group ]
# [ -M mode ] [ -O owner ] [ -G group ] [ -e prolog_cmd ]
# [ -E postlog_cmd ] logfile
#
# where the switches mean:
#
# -L Name of the directory 'log' is found in.
# -n Number of backup copies to keep.
# -s Skip empty logs.
# -c Compress the backup copy with compress.
# -z Compress the backup copy with gzip.
# -B Dir to keep the backup copies in.
# -b Name of backup copy.
# -m Mode of new log.
# -o Owner of new log (only root can use this option).
# -g Group of new log (only root can use this option).
# -M Mode of backup copy.
# -O Owner of backup copy (only root can use this option).
# -G Group of backup copy (only root can use this option).
# -e Command to be executed immediately before copying the logfile.
# -E Command to be executed immediately after copying the logfile.
#
# Stylistic note -- any error that says 'Unexpected error' means I
# thought this particular error can't happen.
#
# This program is Copyright 1989, 1994, Steven C. Simmons. It may be
# freely redistributed provided this notice remains intact and any changes
# you make are clearly marked as such.
#
# ORIGINAL RCS INFORMATION:
# Source: /home/lokkur/scs/src/rotate_log/rotate_log/RCS/rotate_log,v
# Revision: 0.9
# Author: scs Date: 1994/11/14 23:28:31
# State: Exp Locker:
#
# Log: rotate_log,v
# Revision 0.9 1994/11/14 23:28:31 scs
# Added gzip compression.
#
# Revision 0.8 1991/08/02 19:01:20 scs
# Corrected error in paraterization of chmod/chgrp/chown. Didn't
# this get fixed once before?
#
# Revision 0.7 91/04/30 22:10:10 scs
# Added -s switch.
#
# Revision 0.6 91/04/27 16:16:59 scs
# Fixed bug with -b/-n combo losing arguements. Patch supplied by
# Michael Bredeweg (...clif!comnet!mike).
#
# Revision 0.5 91/04/03 19:49:00 scs
# Parameterized locations of chmod, chgrp, chown.
#

PATH=/bin:/usr/local/bin
export PATH

SCRIPT=`basename $0`

#
# Commonly used commands
#

MV=/bin/mv
CP=/bin/cp
CHMOD=/bin/chmod
CHOWN=/bin/chown
CHGRP=/bin/chgrp

#
# Assign default values as needed
#

SOURCE_DIR=""
SOURCE_NAME=""
BACKUP_NAME=""
BACKUP_DIR=""
BACKUP_COUNT="3"
COMPRESS=""
SOURCE_MODE=""
SOURCE_OWNER=""
SOURCE_GROUP=""
BACKUP_MODE=""
BACKUP_OWNER=""
BACKUP_GROUP=""
SKIP_FLAG="false"

#
# Basic switch parsing. Right now, this forces us to use whitespace
# between switch char and actual execution. Whenever we get data
# for a switch, errorcheck it immediately.
#

while [ "$#" -gt "0" ] ; do
case $1 in
-L ) if [ "$2" = "" ] ; then
echo "${SCRIPT}: The $1 switch requires a directory name. Aborting."
exit 0
fi
if [ -w "${2}" -a -d "${2}" ] ; then
:
else
echo "${SCRIPT}: Sorry, you cannot modify the directory ${2}. Aborting."
exit 0
fi
SOURCE_DIR="${2}/"
shift
;;
-b ) if [ "$2" = "" ] ; then
echo "${SCRIPT}: The $1 switch requires a backup name. Aborting."
exit 0
fi
BACKUP_NAME="$2"
shift
;;
-B ) if [ "$2" = "" ] ; then
echo "${SCRIPT}: The $1 switch requires a directory name. Aborting."
exit 0
fi
if [ -w "${2}" -a -d "${2}" ] ; then
:
else
echo "${SCRIPT}: Sorry, you cannot create files in ${DIR}. Aborting."
exit 0
fi
BACKUP_DIR="${2}/"
shift
;;
-n ) if [ "$2" = "" ] ; then
echo "${SCRIPT}: The $1 switch requires a backup count. Aborting."
exit 0
fi
if echo $2 | grep '[^0-9]' > /dev/null 2>&1 ; then
echo "${SCRIPT}: The backup count ($2) must be a positive number.
Aborting."
exit 0
fi
if [ $2 -eq 0 ] ; then
echo "${SCRIPT}: The backup count ($2) must be greater than zero.
Aborting."
exit 0
fi
BACKUP_COUNT="$2"
shift
;;
-s ) SKIP_FLAG="true"
;;
-c ) COMPRESS="compress"
COMP_EXT=".Z"
;;
-z ) COMPRESS="gzip"
COMP_EXT=".gz"
;;
-m ) if [ "$2" = "" ] ; then
echo "${SCRIPT}: The $1 switch requires a permissions mode. Aborting."
exit 0
fi
SOURCE_MODE="$2"
shift
;;
-M ) if [ "$2" = "" ] ; then
echo "${SCRIPT}: The $1 switch requires a permissions mode. Aborting."
exit 0
fi
BACKUP_MODE="$2"
shift
;;
-o ) if [ "$2" = "" ] ; then
echo "${SCRIPT}: The $1 switch requires the name of the new owner.
Aborting."
exit 0
fi
if grep '^${2}:' < /etc/passwd > /dev/null 2>&1 ; then
echo "${SCRIPT}: No such login id as $2. Aborting."
exit 0
fi
SOURCE_OWNER="$2"
shift
;;
-O ) if [ "$2" = "" ] ; then
echo "${SCRIPT}: The $1 switch requires the name of the new owner.
Aborting."
exit 0
fi
if grep '^${2}:' < /etc/passwd > /dev/null 2>&1 ; then
echo "${SCRIPT}: No such login id as $2. Aborting."
exit 0
fi
BACKUP_OWNER="$2"
shift
;;
-g ) if [ "$2" = "" ] ; then
echo "${SCRIPT}: The $1 switch requires the name of the new group.
Aborting."
exit 0
fi
if grep '^${2}:' < /etc/group > /dev/null 2>&1 ; then
echo "${SCRIPT}: No such group as $2. Aborting."
exit 0
fi
SOURCE_GROUP="$2"
shift
;;
-G ) if [ "$2" = "" ] ; then
echo "${SCRIPT}: The $1 switch requires the name of the new group.
Aborting."
exit 0
fi
if grep '^${2}:' < /etc/group > /dev/null 2>&1 ; then
echo "${SCRIPT}: No such group as $2. Aborting."
exit 0
fi
BACKUP_GROUP="$2"
shift
;;
-e ) if [ "$2" = "" ] ; then
echo "${SCRIPT}: The $1 switch requires a command to be executed.
Aborting."
exit 0
fi
CMD_BEFORE="${2}"
shift
;;
-E ) if [ "$2" = "" ] ; then
echo "${SCRIPT}: The $1 switch requires a command to be executed.
Aborting."
exit 0
fi
CMD_AFTER="${2}"
shift
;;
-* ) echo "${SCRIPT}: No such switch as $1. Aborting."
exit 0
;;
* ) if [ "${SOURCE_NAME}" != "" ] ; then
echo "${SCRIPT}: You must specify only one log file. Aborting."
exit 0
else
SOURCE_NAME=$1
fi
;;
esac
shift
done

if [ "${SOURCE_NAME}" = "" ] ; then
echo "${SCRIPT}: You must specify a log file. Aborting."
exit 0
fi

#
# Do sanity checks
#
# If he specified a source directory or backup directory, the source
# and backup file names cannot contain a slash.
#

if [ "${SOURCE_DIR}" != "" ] ; then
if echo $SOURCE_NAME | grep / > /dev/null ; then
cat << EOF
${SCRIPT}: If you specify a source directory (eg, -L ${SOURCE_DIR}),
then the log file name (${SOURCE_NAME}) must be a simple file name,
ie, not containing any slashes. Aborting.
EOF
exit 0
fi

#
# If they don't explicitly select a backup directory but the
# do specify a log directory, use the same for both.
#

if [ "${BACKUP_DIR}" = "" ] ; then
BACKUP_DIR="${SOURCE_DIR}"
fi
fi

if [ "${BACKUP_DIR}" != "" ] ; then
if echo $BACKUP_NAME | grep / > /dev/null ; then
cat << EOF
${SCRIPT}: If you specify a backup directory (eg, -B ${BACKUP_DIR}),
then the backup log name (${BACKUP_NAME}) must be a simple file name,
ie, not containing any slashes. Aborting.
EOF
exit 0
fi
fi

#
# Make sure we can mod the directory where the log is.
#

if [ -w `dirname "${SOURCE_NAME}"` ] ; then
:
else
cat << EOF
${SCRIPT}: Sorry, you do not have permission to move/rename the
log file (file '`basename ${SOURCE_NAME}`', directory '`dirname
${SOURCE_NAME}`'). Aborting.
EOF
exit 0
fi

#
# Make sure we can read/write the log itself.
#

SOURCE_PATH="${SOURCE_DIR}${SOURCE_NAME}"

if [ -w "${SOURCE_PATH}" -a -r "${SOURCE_PATH}" ] ; then
:
else
echo "${SCRIPT}: Sorry, you do not have permission to read and modify"
echo "the file '${SOURCE_PATH}'. Aborting."
exit 0
fi

#
# If the log is empty and the skip flag (-s) is set, do nothing.
#

if [ "${SKIP_FLAG}" = "true" ] ; then
set `wc -c ${SOURCE_PATH}`
if [ 0 = $1 ] ; then
exit 0
fi
fi

#
# Make sure root operations are only run by root
#

if [ "${BACKUP_OWNER}" != "" -o "${SOURCE_OWNER}" != "" -o "${BACKUP_GROUP}"
!= "" -o "${SOURCE_GROUP}" != "" ]
then
# If we can write the password file, we have root privs
if [ -w /etc/passwd ] ; then
:
else
echo "${SCRIPT}: Sorry, you can only change owner or group if you are root."
echo "Aborting."
exit 0
fi
fi

#
# Build the list of backup names, in reverse order. Make them
# full pathnames.
#

if [ "${BACKUP_NAME}" = "" ] ; then
BACKUP_NAME="${SOURCE_NAME}"
fi
while expr $BACKUP_COUNT \> 0 > /dev/null ; do
BACKUP_COUNT=`expr $BACKUP_COUNT - 1`
BACKUP_FILES="${BACKUP_FILES} ${BACKUP_DIR}${BACKUP_NAME}.${BACKUP_COUNT}"
done

#
# Rotate the existing backups. Assume no change in permissions, etc.
#

set ${BACKUP_FILES}
if [ "${1}" = "" ] ; then
echo "${SCRIPT}: unexpected error in using backup list. Aborting."
exit 0
fi

OLDEST="${1}"
NEXT="${1}"
shift

while [ "$1" != "" ] ; do
NEXT="${1}"
rm -f "${OLDEST}" "${OLDEST}${COMP_EXT}"
if [ -f "${NEXT}" ] ; then
${MV} "${NEXT}" "${OLDEST}"
elif [ -f "${NEXT}${COMP_EXT}" ] ; then
${MV} "${NEXT}${COMP_EXT}" "${OLDEST}${COMP_EXT}"
else
echo "${SCRIPT}: Missing backup log ${NEXT} or ${NEXT}${COMP_EXT}.
Continuing."
fi
OLDEST="${NEXT}"
shift
done

#
# Copy the current log to be the first backup. Use full pathnames
# as appropriate.
#

if [ "${SOURCE_DIR}" != "" ] ; then
if cd "${SOURCE_DIR}" ; then
:
else
echo "${SCRIPT}: unexpected error in command 'cd ${SOURCE_DIR}'. Aborting."
exit 0
fi
fi

rm -f "${NEXT}" "${NEXT}${COMP_EXT}"
if [ "${CMD_BEFORE}" != "" ] ; then
${CMD_BEFORE}
fi

if ${MV} "${SOURCE_NAME}" "${NEXT}" ; then
:
else
echo "${SCRIPT}: unexpected error making first backup. Aborting."
exit 0
fi

if [ "${CMD_AFTER}" != "" ] ; then
${CMD_AFTER}
fi

rm -f "${SOURCE_NAME}" "${SOURCE_NAME}${COMP_EXT}"
if cat > "${SOURCE_NAME}" < /dev/null ; then
:
else
echo "${SCRIPT}: unexpected error emptying log file. Continuing."
fi

#
# Set ownership and permission on the log as requested.
#

if [ "${SOURCE_MODE}" != "" ] ; then
if ${CHMOD} ${SOURCE_MODE} "${SOURCE_NAME}" ; then
:
else
echo "${SCRIPT}: unexpected error executing command '${CHMOD} ${SOURCE_MODE}
${SOURCE_NAME}'."
echo "Aborting."
exit 0
fi
fi

if [ "${SOURCE_OWNER}" != "" ] ; then
if ${CHOWN} ${SOURCE_OWNER} "${SOURCE_NAME}" ; then
:
else
echo "${SCRIPT}: unexpected error executing command '${CHOWN}
${SOURCE_OWNER} ${SOURCE_NAME}'."
echo "Aborting."
exit 0
fi
fi

if [ "${SOURCE_GROUP}" != "" ] ; then
if ${CHGRP} ${SOURCE_GROUP} "${SOURCE_NAME}" ; then
:
else
echo "${SCRIPT}: unexpected error executing command '${CHGRP}
${SOURCE_GROUP} ${SOURCE_NAME}'."
echo "Aborting."
exit 0
fi
fi

#
# Set ownerships and permissions as requested on the backed up log.
# Note that the compress must come last in this sequence.
#

if [ "${BACKUP_MODE}" != "" ] ; then
if ${CHMOD} ${BACKUP_MODE} "${NEXT}" ; then
:
else
echo "${SCRIPT}: unexpected error executing command '${CHMOD} ${BACKUP_MODE}
${NEXT}'."
echo "Aborting."
exit 0
fi
fi

if [ "${BACKUP_OWNER}" != "" ] ; then
if ${CHOWN} ${BACKUP_OWNER} "${NEXT}" ; then
:
else
echo "${SCRIPT}: unexpected error executing command '${CHOWN}
${BACKUP_OWNER} ${NEXT}'."
echo "Aborting."
exit 0
fi
fi

if [ "${BACKUP_GROUP}" != "" ] ; then
if ${CHGRP} ${BACKUP_GROUP} "${NEXT}" ; then
:
else
echo "${SCRIPT}: unexpected error executing command '${CHGRP}
${BACKUP_GROUP} ${NEXT}'."
echo "Aborting."
exit 0
fi
fi

if [ "${COMPRESS}" != "" ] ; then
if ${COMPRESS} "${NEXT}" ; then
:
else
echo "${SCRIPT}: unexpected error executing command '${COMPRESS} ${NEXT}'."
echo "Aborting."
exit 0
fi
fi

exit 0

>From Miquel Cabanas (miquel@proton.uab.es)
And Simon-Bernard Drolet (Simon-Bernard.Drolet@M3iSystems.com)

--Boundary_(ID_y0JZY7Nj9jCTs8cs0ghFZQ)
Content-type: application/octet-stream; name=wtmpxcut.c
Content-description: wtmpxcut.c
Content-disposition: attachment; filename=wtmpxcut.c

/* wtmpxcut */

/*----------------------------------------------------------------------*
* *
* Description: Source du fichier exec. /equipe/sysadmin/bin/wtmpxcut *
* qui permet de reduire le fichier /var/adm/wtmpx en enle-*
* vant toutes les entrees plus vieilles qu'une certaine *
* date specifiee en parametre. Si aucun parametre n'est *
* specifie, on utilise une valeur par defaut (DELAIS). *
* *
*----------------------------------------------------------------------*/

#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <utmpx.h>
#include <time.h>
#include <errno.h>

#define DELAIS (time_t) (30) /* en jour */

int main(argc, argv)
int argc;
char **argv;
{
struct utmpx wtmpx_rec;
int old_fd, new_fd;
int wtmpx_rec_sz = sizeof(wtmpx_rec);
time_t periode, temps_limite;

if (argc > 1)
periode = (time_t) atoi(argv[1]);
else
periode = DELAIS;

/*
* Copie de /var/adm/wtmpx dans /tmp/wtmpx
*/
putenv("IFS=' \t\n'"); /* Securite */
putenv("PATH=/usr/bin"); /* Securite */
if (system("cp /var/adm/wtmpx /tmp/wtmpx") < 0) {
fprintf(stderr,"%s: Erreur en copiant wtmpx dans /tmp.\nBye.\n",argv[0]);
exit(1);
} /* if */

/*
* Calcule du temps limite en time_t
*/
temps_limite = time(NULL) - (periode * 24 * 3600);
fprintf(stdout,
"\n%s: la date limite est %s\n", argv[0], asctime(localtime(&temps_limite)));

/*
* Ouvre le nouveau wtmp en ecriture
*/
if ((new_fd = open("/var/adm/wtmpx", O_WRONLY | O_TRUNC)) < 0) {
fprintf(stderr, "%s: Erreur %d d'ouverture de /var/adm/wtmpx.\nBye.\n",
argv[0], errno);
exit(1);
} /* if */

/*
* Ouvre la copie de wtmp en lecture
*/
if ((old_fd = open("/tmp/wtmpx", O_RDONLY)) < 0) {
fprintf(stderr, "%s: Erreur %d d'ouverture de /tmp/wtmpx.\nBye.\n",
argv[0], errno);
exit(1);
} /* if */

/*
* En partant du debut du fichier, on compare les dates
* et si elles sont trop vieilles, on les oublies.
* Lorsqu'elles sont dans la periode, on les garde dans
* le fichier /var/adm/wtmpx.
*/
while (read(old_fd, &wtmpx_rec, wtmpx_rec_sz) == wtmpx_rec_sz) {
if (wtmpx_rec.ut_xtime >= temps_limite) {
do {
if (write(new_fd, &wtmpx_rec, wtmpx_rec_sz) != wtmpx_rec_sz) {
fprintf(stderr, "%s: Erreur %d d'ecriture du record.\nBye.",
argv[0], errno);
exit(1);
} /* if */
} while (read(old_fd, &wtmpx_rec, wtmpx_rec_sz) == wtmpx_rec_sz );
} /* if */
} /* while */

exit(0); /* Sortie elegante */

} /* main() */

--Boundary_(ID_y0JZY7Nj9jCTs8cs0ghFZQ)
Content-type: text/plain; charset=us-ascii

>From Rachel Polanskis (grove@zeta.org.au)

http://tequila.randomc.com/~marius/code/trimwtmp.tar.gz

>From P. Alejandro (alejolo@ideam.gov.co)
#/sbin/sh

$DATE=3D`date +%m.%d.%y`
if [ -as /var/log/wtmp]
cp /var/log/wtmp /var/log/wtmp.$DATE ;
cp /dev/null /var/log/wtmp ;
exit 0;
else
/bin/mailx -s "We messed up" root;
exit 1;
fi

>From Randall S. Winchester (rsw@Glue.umd.edu)

He suggested using newsyslog from MIT, also keeps syslog in order.

Thanks to everyone involved, I am really glad I joined the list, and very
grateful to all those that answered my questions ;)

R Dows <marius@randomc.com>
SysAdmin, Random Access Inc.,
Atlanta, GA.,
30328
(770)804-1190

--Boundary_(ID_y0JZY7Nj9jCTs8cs0ghFZQ)--