#!/bin/bash

# remove_changelog: emergency remove changelog files from server.
#
# This is emergency tool to cleanup changelogs in server if changelog
# records cannot be removed by regular means, e.g. due to llog corruptions
#
# Tool goes through changelog_catalog and removes all plain llogs listed
# then removes changelog_catalog itself and changelog_users
# Script accept single parameter which is mount point of server FS mounted
# locally, script accepts also --dry-run option to emulate files removal
#
# Steps to cleanup problematic llogs:
#
# 1. mount MDT filesystem locally on server as ldiskfs mount
# 2. run script first in dry-run mode to make sure it parses llogs as needed:
#    # bash remove_changelog -n <ldiskfs_mount>
# 3. save all llogs for analysis:
#    # bash remove_changelog -n -z /tmp/llogs_saved <ldiskfs_mount>
# 4. check that /tmp/llogs_saved.tar.gz exists and has all llogs inside:
#    # ls -ali /tmp/llogs_saved.tar.gz
#    # tar -tf /tmp/llog_saved.tar.gz
# 5. finally run script to delete all llogs:
#    # bash remove_changelog <ldiskfs_mount>
#
# For better llogs compression xz can be used as well, pass it to the script
# via GZIP env variable:
#    # GZIP=xz bash remove_changelog -n -z /tmp/llogs_saved <ldiskfs_mount>
# Archive name will ends with .xz in that case instead of .gz




ECHO=echo
PROG=$(basename $0)
LLOG_READER=${LLOG_READER:-llog_reader}
GZIP=${GZIP:-gzip}

usage() {
    cat -- <<USAGE 1>&2
usage: remove_changelog [--dry-run|-n] [--help|-h] [--quiet|-q]
			[--zip}-z] <archive> <localmount>
	--help|-h	show this usage message
	--dry-run|-n	only print the names of files to be removed
	--quiet|-q	run quietly (don't print filenames or status)
	--zip|-z <name_prefix>
			save all llogs into compressed tar archive with given
			name prefix using gzip by default. Other compression
			tools can be used via GZIP env variable.

The 'localmount' argument should be an ldiskfs mounted MDT device mountpoint.

Examples:
      remove_changelog /mnt/mdt0
      remove_changelog --dry-run /mnt/mdt0
      remove_changelog -z /tmp/llogs /mnt/mdt0
USAGE
    exit 1
}

OPT_DRYRUN=false
OPT_ARCH=""
OPT_MOUNT=""

# Examine any long options and arguments
while [ -n "$*" ]; do
	arg="$1"
	case "$arg" in
	-h|--help) usage;;
	-n|--dry-run) OPT_DRYRUN=true;;
	-q|--quiet) ECHO=:;;
	-z|--zip) OPT_ARCH="$2.tar"; shift;;
	*)
	   [ -d "$arg" ] && OPT_MOUNT="$arg";;
	esac
	shift
done

remove_changelog() {
	local mntpoint=$OPT_MOUNT
	local catalog=${mntpoint}/changelog_catalog
	local users=${mntpoint}/changelog_users
	local arch=$OPT_ARCH

	if [[ -z $(df -t ldiskfs $mntpoint 2>/dev/null) ]] ; then
		echo "$PROG: '$mntpoint' is not ldiskfs mount."
		exit 1
	fi

	if $OPT_DRYRUN; then
		$ECHO "Dry run was requested, no changes will be applied"
	fi

	$ECHO "Scan changelog_catalog at '$mntpoint':"
	if [[ ! -f $catalog ]] ; then
		echo "$PROG: $catalog doesn't exist already."
	else
		if [[ ! $(which $LLOG_READER 2>/dev/null) ]] ; then
			echo "$PROG: $LLOG_READER is missing."
			exit 1
		fi
		[[ -z $arch ]] || tar -cf $arch $catalog 2>/dev/null
		if (( $(stat -c %s $catalog) >= 8192 )) ; then
			while read -r path ; do
				[[ -z $arch ]] ||
					tar -rf $arch ${mntpoint}/$path 2>/dev/null
				$ECHO "rm ${mntpoint}/$path"
				$OPT_DRYRUN || rm -f ${mntpoint}/$path
			done < <($LLOG_READER $catalog |
				 awk -F "path=" '/path=/ { print $2 }')
		else
			echo "$PROG: $catalog is too small."
		fi
		$ECHO "> $catalog"
		$OPT_DRYRUN || > $catalog
	fi

	if [[ -f $users ]] ; then
		[[ -z $arch ]] || tar -rf $arch $users 2>/dev/null
		$ECHO "> $users"
		$OPT_DRYRUN || > $users
	else
		echo "$PROG: $user doesn't exist."
	fi
	if [[ "$arch" ]] ; then
		$GZIP -3 $arch
		$ECHO "llog archive was created by $GZIP"
	fi
}

if [ -z $OPT_MOUNT ] ; then
	echo "Mount is not specified, exiting"
	exit 1
fi
remove_changelog


