#!/bin/ksh -u
# Mail the contents of the documentation directory - presumably to sysadmins
# Author: Alain D D Williams, addw@phcomp.co.uk, 2008
# SCCS: @(#)MailDocumentation	1.13 12/23/09 12:47:40
# The tar file will be gpg encrypted with the recipient's public key.
# The Subject: line will be something like:
#	Documentation from somehost.example.com
# which allows automatic processing of mail at the receiving end and automatic saving of the contents.
# A file .LastMailed is maintained; nothing is sent if no file is younger than this.

PROGNAME=${0##*/}

DocDir=/etc/Documentation

# Nothing should need changing below this line:

Verbose=0
now=$( date '+%Y''%m%d%H''%M.%S' )	# Time at script start -- defeat SCCS
TmpDir=${TMPDIR:-/usr/tmp}
SubjAppend=
Mktmp=n		# If 'y' make the temp directory
Rmtmp=
MachineName=$( uname -n )
NumTriggers=0
Tiggers=
tmsg=

GenInfo=n		# If 'y' generate system information iff we send something
SysInfoFile=SYSTEM.INFO	# The name of the file that we create with this system info

export PATH=$PATH:/sbin:/usr/sbin	# Sometimes what we inherit is deficient

# Print a usage message & exit the program.
# If an argument is given print that as an error and exit 1
function Usage {
	(( $# > 0 )) && echo "$PROGNAME: $*" >&2
	cat <<-!
		Mail everything in the the documentation directory if anything has changed since last run.
		Sent as a gpg encrypted tar file.
		Usage: $PROGNAME [-opts] [pgp_key_email_address:]email_address ...
		-d dir	Documentation Directory to send, default $DocDir
		-i	collect/update system Information if documentation is sent
		-n name	Name of the machine in the subject line, default here is $( uname -n )
		-s txt	Append to email Subject line, after this machine name, prepended with a colon
		-t file	Trigger file, resend documentation if this file has been updated
		-T dir	temporary directory to use, must not exist, otherwise use preexisting $TmpDir
		-v	Verbose (multiple use gives more verbosity)
		-x	eXplain
		--help	help message
		Version: 1.13 12/23/09, latest from: http://www.phcomp.co.uk/Packages/MailDocumentation.html
		The file .LastMailed records the time of last email sent.
		!
	exit $#
}

# Echo the arguments if in verbose mode
function Note {
	(( Verbose == 0 )) && return

	echo "$PROGNAME: $*"
}

# Print the message to stderr and exit:
function Die {
	echo "$PROGNAME: $*" >&2
	exit 2
}

# Complain:
function Winge {
	echo "$PROGNAME: $*" >&2
}

# Generate a snapshot of system information.
# This code is similar to what is found in TapeBackup and is Linuxy in nature
function GenerateSystemInfo {

    (	cat <<-!
	**** DO NOT HAND EDIT ****
	This file is automatically generated by $PROGNAME, it may be rewritten the
	next time that $PROGNAME runs.

	This file contains information about the system on which it exists that may be useful
	if you need to rebuild it.

	For information on $PROGNAME see: http://www.phcomp.co.uk/Packages/MailDocumentation.html

	!

	echo "System information:"
	uname -a
	echo

	echo "Memory & Swap:"
	free
	swapon -s

	echo
	echo "Hard disk parameters:"

	for hd in $(sed -n -e '/[hs]d.$/s/^.* //p' /proc/partitions )
	do	hdparm -i /dev/$hd
	done

	echo
	echo "Disk parititioning:"
	fdisk -l 2>/dev/null

	# If mdadm is installed:
	if type mdadm > /dev/null 2>&1
	then	echo
		echo "Possible mirror config:"
		mdadm --examine --verbose --scan --config=partitions

		# Try to give a bit more info:
		for md in $( mdadm --examine --verbose --scan --config=partitions | awk '/^ARRAY/ { print $2 } ' )
		do	echo
			echo "Mirror device: $md"
			mdadm --misc --detail $md
		done
	fi

	# If LVM is installed:
	if type pvdisplay > /dev/null 2>&1
	then	echo
		echo "LVM config:"
		pvdisplay --verbose 2> /dev/null
		lvdisplay 2> /dev/null
		vgdisplay --verbose 2> /dev/null
	fi

	echo
	echo "Mounted File Systems:"
	mount -v
	echo

	# Don't inspect over NFS - we could hang as a result
	echo "Disk Usage:"
	FSys="$( mount | awk '$1 ~ /:/ {next} {print $3}' )"
	df -Pk  $FSys
	echo
	df -Pki $FSys

	# How are they mounted ?
	echo
	echo "Contents of /etc/fstab:"
	cat /etc/fstab
	echo

	# Lilo information
	if type lilo > /dev/null 2>&1
	then	echo
		echo "Lilo information:"
		lilo -q -v -v -v
		echo
	fi

	echo "Boot info:"
	echo "ls -l /boot"
	ls -l /boot
	echo

	# This might get some duplicates:
	for file in grub.conf menu.lst
	do	if [[ -r /boot/grub/$file ]]
		then	echo
			echo "Contents of: /boot/grub/$file" 
			cat /boot/grub/$file
			echo
		fi
	done

	if [[ -r /etc/Hacks ]]
	then	echo
		echo "Contents of /etc/Hacks, last modified: $( find /etc/Hacks -printf '%Tc' )"
		echo "================================"
		cat /etc/Hacks
		echo "================================"
		echo
	fi

	echo "User information:"
	cat /etc/passwd
	echo ""

	if type ifconfig > /dev/null 2>&1
	then	echo
		echo "Network config: ifconfig"
		ifconfig
	fi

	if type netstat > /dev/null 2>&1
	then	echo
		echo "Network config: routing tables"
		netstat -rn
	fi

	if type rpm > /dev/null 2>&1
	then	echo
		echo "Installed packages (rpm):"
		rpm -qa
	fi

	if type dpkg > /dev/null 2>&1
	then	echo
		echo "Installed packages (dpkg):"
		dpkg --list
	fi

    ) > $SysInfoFile

	# Stop it triggering more mail:
	touch -t $now $SysInfoFile
}


# Parse options, recognise --help
while	[[ $# -ge $OPTIND ]] && eval A1=\$$OPTIND || A1=
	if [[ $A1 = --help ]]
	then	opt=x	# --help is -x
	else	getopts :d:in:s:t:T:vx opt
	fi
do	case "$opt" in
	d)	DocDir="$OPTARG" ;;
	i)	GenInfo=y ;;
	n)	MachineName="$OPTARG" ;;
	s)	SubjAppend=":$OPTARG" ;;
	t)	Tiggers[$NumTriggers]="$OPTARG"
		(( NumTriggers++ ))
		tmsg=', no trigger files touched' ;;
	T)	TmpDir="$OPTARG"
		Mktmp=y ;;
	v)	(( Verbose++ )) ;;
	x)	Usage ;;
	:)	Usage "Missing argument to option '$OPTARG'" ;;
	\?)	Usage "Unknown option '$OPTARG'" ;;
	*)	Usage "Internal program error, unrecognised argument '$opt'" ;;
	esac
done
shift $((OPTIND - 1))

(( $# == 0 )) && Die "No addresses to email to"

TarFile=$TmpDir/Documentation.$$.tar

# Create a temp directory to work in - the directory will be mode 700 for security
if [[ $Mktmp = y ]]
then	[[ -e $TmpDir ]] && Die "$TmpDir must not exist, I want to use it as a temporary directory"
	mkdir --mode=700 $TmpDir || Die "Creation of temporary directory $TmpDir failed"
	Rmtmp="rm -rf $TmpDir"
fi

trap "rm -f $TarFile $TarFile.gpg ; $Rmtmp" 0	# Be tidy

cd "$DocDir" || exit

# Check if there are any trigger files.
# If we have no last time, act as if we have inspected all triggers
[[ -f .LastMailed ]] && tn=0 || tn=$NumTriggers
trig=no

while (( tn < NumTriggers ))
do
	(( Verbose > 1 )) && echo "Check trigger file ${Tiggers[$tn]}"

	# Check that the trigger files exists & it is newer than .LastMailed:
	if [[ -f ${Tiggers[$tn]} &&
	      $( find ${Tiggers[$tn]} -newer .LastMailed | wc -l ) -ne 0 ]]
	then	(( Verbose > 1 )) && echo "Trigger file ${Tiggers[$tn]} has been touched"
		trig=yes
	fi

	((tn++))
done

# Anything happened since last time ?
[[ $trig = no && ( -f .LastMailed && $( find -type f -newer .LastMailed | wc -l ) -eq 0 ) ]] &&
	Note "Nothing to do: no new files$tmsg" &&
	exit

umask 077	# Be safe

# Something has changed

# Generate a snapshot of system information ?
[[ $GenInfo = y ]] &&
	GenerateSystemInfo

# Generate the file that we send
tar cf $TarFile .

[[ ! -s $TarFile ]] && Die "Tar file not created properly"

# Must send to each recipient separately because it needs to be encrypted with
# each recipient's public key separately:
for email
do	# Possibly different GPG and sending email addresses, split out:
	gpg_addr=${email%:*}
	send_addr=${email#*:}
	Note "Sending to: $email, gpg=$gpg_addr send_addr=$send_addr"

	# If we have enrypted & sent it once we need to zap the encrypted file so that we can
	# encrypt for the next recipient:
	rm -f $TarFile.gpg

	# Encrypt using the recipient's key:
	if gpg --encrypt --recipient $gpg_addr $TarFile
	then	mutt -a $TarFile.gpg -s "Documentation from $MachineName$SubjAppend" $send_addr <<-!
			Documentation update attached for $MachineName$SubjAppend.
			Uname says: $( uname -n )
			The attachment is encrypted with gpg for $gpg_addr.
		!
	else	Winge "Can't encrypt file for $gpg_addr"
		# If we can't encrypt for one destination, try for the others
	fi
done

# Use time at start of script - so that we don't loose something that is updated while the script runs.
# We might send something twice - that is not harmful.
touch -t $now .LastMailed

# end
