#!/bin/sh

# sysbackup.sh version 0.2
#
# Copyright (C) Jeremy Laidman, March 2005
#
# SysBackup is released under the GNU General Public License.
# See http://www.gnu.org/licenses/gpl.txt for details.
#
# This script creates a tarball to be used to restore a system
# tarball includes a script "install.sh" to do the restore.
#
# To do:
# - fully test
# - have a plugin system for special RPMs to handle special cases,
#   such as a postfix plugin that uses the configuration files
#   to find extra files in /etc/postfix/ (eg canonical) and backs
#   those up too
# - allow options for various stuff, eg:
#   - clear user passwords
#   - set root password
#   - enable/disable yum
# - get tar to do relative backups

BACKUPDIR=/var/tmp/sysbackup

die() {
	echo "$1" >&2
	exit 1
}

[ `id -u` -eq 0 ] || die "Must run as root"

[ -d $BACKUPDIR ] &&
	die "Backup directory $BACKUPDIR already exists, please remove first."
mkdir $BACKUPDIR ||
	die "Unable to make backup directory $BACKUPDIR."

cd $BACKUPDIR

# get a list of installed RPMs
echo -n "Getting list of installed RPMs..."
rpm -qa > rpmlist.txt
echo "done."

# get changed files
echo -n "Getting list of altered files..."
rpm -Va > rpmverify.txt
echo "done."

# get changed config files
echo -n "Backing up changed configuration files..."
grep "^..5.....  c " rpmverify.txt | sed 's/^[^\/]*//' > changedconfs.txt
tar -cz -T changedconfs.txt -f changedconfs.tgz
echo "done."

echo -n "Backing up changed scripts..."
grep "^..5.....  [^c] " rpmverify.txt | sed 's/^[^\/]*//' | grep -v "^/var" > changedscripts.txt
tar -cz -T changedscripts.txt -f changedscripts.tgz
echo "done."

echo -n "Backing up changed permissions..."
grep "^.M.*" rpmverify.txt | sed 's/^[^\/]*//' | while read X; do ls -ld $X; done | awk '{printf "%s %s %s %s\n",$1,$3,$4,$9}' > changedmodes.txt
echo "done."

echo -n "Backing up all configuration files..."
# the plan is to check what they're replacing and if not
# the same, rename the old ones to have a .sysrestore extension
rpm -qa -c > configfiles.txt
tar -cz -T configfiles.txt -X changedconfs.txt -f configfiles.tgz
echo "done."

echo -n "Backing up user accounts (not home directories)..."
touch accounts.tgz
chmod 600 accounts.tgz
tar -czf accounts.tgz /etc/shadow
echo "done."

echo -n "Backing up state of init scripts..."
chkconfig --list > initstate.txt
echo "done."

OTHERFILES="/etc/hosts /etc/resolv.conf /etc/host.conf /etc/at.allow /etc/grub.conf /etc/sysconfig/iptables /etc/sysconfig/network /etc/sysconfig/network-scripts/ifcfg-*"
echo -n "Backing up host identity and resolver files..."
tar -czf hostid.tgz $OTHERFILES
echo "done."

DOYUM=yes
if [ "$DOYUM" ]; then
	# should be a yum-only section
	echo -n "Backing up yum repos..."
	REPOLIST=""
	[ -d /etc/yum.repos.d/ ] && REPOLIST="${REPOLIST} /etc/yum.repos.d/*"
	[ -f /etc/yum.conf ] && REPOLIST="${REPOLIST} /etc/yum.conf"
	tar -cz -f repos.tgz $REPOLIST
	echo "done."

	echo -n "Finding non-yum RPMs..."
	yum list extras > yum-extras.txt
	echo "done."
fi

echo -n "Backing up RPM GPG public keys..."
rpm -q gpg-pubkey-\*|while read F; do echo $F; rpm -qi $F > ${F}.pubkey; done
echo "done."

echo -n "Creating sysrestore script..."
# probably better doing this by copying a script from elsewhere
# but for now...
cat <<"EOF" >sysrestore.sh
#!/bin/sh

die() {
	echo "$1" >&2
	exit 1
}

show_rpm_basename() {
	PKG=$1
	echo $PKG | sed 's/-[0-9].*//'
}

[ `id -u` -eq 0 ] || die "Must run as root"

echo "System Configuration Restore"
echo ""
[ -f rpmlist.txt -a -f changedconfs.txt -a sysrestore.sh ] ||
	die "Can't find required files, please run from `basename $0`."
echo "This script will replace configuration files, and should not be run"
echo "on a production system.  It should only be run on a minimally installed"
echo "system."
echo ""
echo -n "Continue? (y/n) [n]:"
read YN
[ "$YN" = "y" -o "$YN" = "Y" ] || die "aborted"

echo ""
echo "Building package list..."
RPMLIST=`cat rpmlist.txt | while read RPM; do
	RPM=\`show_rpm_basename $RPM\`
	if ! rpm -q $RPM > /dev/null; then
		show_rpm_basename "$RPM"
	fi
done`
echo "Need to install: ${RPMLIST}"

echo ""

echo -n "Restoring user accounts..."
tar -C / -xzf accounts.tgz
echo "done."

echo -n "Restoring all configuration files..."
tar -C / -xzf configfiles.tgz
echo "done."

echo -n "Restoring changed configuration files..."
tar -C / -xzf changedconfs.tgz
echo "done."

echo -n "Restoring host identity and resolver files..."
tar -C / -xzf hostid.tgz
echo "done."

echo -n "Restoring yum repos..."
tar -C / -xzf repos.tgz
echo "done."

echo -n "Restoring RPM gpg pubkeys..."
for KEY in `ls gpg-pubkey-*`; do
	KEYNAME=`echo $PUBKEY | sed 's/\.pubkey$//'`
	rpm -q $KEYNAME >/dev/null ||
		rpm --import $KEY
done
echo "done."

echo -n "Installing missing RPMs using yum..."
yum install ${RPMLIST}
echo "done."

echo -n "Restoring changed scripts..."
tar -C / -xzf changedscripts.tgz
echo "done."

echo -n "Restoring changed permissions..."
cat changedmodes.txt | while read MODE USER GROUP FILE; do
	if [ -f $FILE -o -d $FILE ]; then
		chown $USER $FILE
		chgrp $GROUP $FILE
		MODEU=`echo $MODE|cut -c2-4`
		MODEG=`echo $MODE|cut -c5-7`
		MODEO=`echo $MODE|cut -c8-10`
		chmod u=$MODEU $FILE
		chmod g=$MODEG $FILE
		chmod o=$MODEO $FILE
	else
		echo "$FILE is neither file nor directory, ignored."
	fi
done
echo "done."

echo "Restoring state of init scripts..."
cat initstate.txt | while read LINE; do
	set - ""$LINE
	SERVICE=$1
	S0=$2
	S1=$3
	S2=$4
	S3=$5
	S4=$6
	S5=$7
	S6=$8

	if ! chkconfig --list $SERVICE >/dev/null 2>&1; then
		echo "Enabling $SERVICE"
		chkconfig --add $SERVICE
	fi
	
	if [ "$LINE" != "`chkconfig --list $SERVICE`" ]; then
		# need to update
		echo "Adjusting $SERVICE init states"
		LEVELS=`echo $LINE | sed 's/^[^ ]*//;s/  *[0-6]:off//g;s/  *\([0-6]\):on/\1/g'`
		chkconfig --level $LEVELS $SERVICE on ||
			echo "Error setting init levels for $SERVICE"
		LEVELS=`echo $LINE | sed 's/^[^ ]*//;s/  *[0-6]:on//g;s/  *\([0-6]\):off/\1/g'`
		chkconfig --level $LEVELS $SERVICE off ||
			echo "Error setting init levels for $SERVICE"
	fi
done

echo -n "Constructing list of RPMs to remove..."
rpm -qa --qf "%{NAME}\n" | while read RPM; do
	grep -q "^$RPM-[0-9]" rpmlist.txt || echo $RPM >> rpmremovelist.txt
done
echo "Suggest these RPMs are removed (rpmremovelist.txt):"
sed 's/^/  /' rpmremovelist.txt

EOF

chmod ugo+x sysrestore.sh
echo "done."

echo -n "Making tarball..."
DATE=`date "+%Y%m%d-%H%M%S"`
NODENAME=`uname -n|sed 's/\..*$//'`
TARBALLNAME="sysbackup-${NODENAME}-${DATE}.tgz"

# bash trick
cd -
cp $0 $BACKUPDIR
cd -
FILES="accounts.tgz changedconfs.tgz changedconfs.txt changedscripts.tgz changedscripts.txt configfiles.tgz configfiles.txt changedmodes.txt hostid.tgz repos.tgz rpmlist.txt rpmverify.txt initstate.txt sysrestore.sh `basename $0`"

cd ..
FILELIST=`for FILE in $FILES; do
	echo "sysbackup/${FILE}"
done`
# make sure nobody can read the tarball, otherwise they
# can get the shadow file out of it
touch sysbackup/$TARBALLNAME
chmod 600 sysbackup/$TARBALLNAME
tar -czf sysbackup/$TARBALLNAME $FILELIST

echo "done."
echo "Please make a backup copy of $TARBALLNAME and keep it in a safe place."
echo "To restore, extract into a temp directory and run sysrestore.sh as root."

