#!/usr/bin/env bash
version="snapshot-0.2.9 (22.11.2014)"
ME=${0##*/}
# adapted for MX-14 by anticapitalista@operamail.com 
# from version="refractasnapshot-9.0.6-2 (20121026)" by fsmithred@gmail.com
# which was based primarily on refractasnapshot-8.0.4  by Dean Linkous with ideas
# borrowed from dzsnapshot-gui.sh by David Hare, which was based on an
# earlier version of this script.
# Copyright: fsmithred@gmail.com 2011, 2012
# Copyright: anticapitalista@operamail.com 2012, 2013, 2014
# License: GPL-3
# This is free software with NO WARRANTY. Use at your own risk!

# DESCRIPTION
# This script makes a copy of your system with rsync and then creates
# an iso file to be used as a live-cd. There are options in the config
# file to change the location of the copy and the location of the final
# iso file, in case there's not enough room on the system drive. Read
# the config file for more options. (snapshot-mx.conf)

# If you want to change any defaults, change them in the configfile.
# Default is /etc/snapshot-mx.conf
# If you want to use a different config file for testing, change this
# variable. Normally, users should not edit anything in this script.
configfile="/etc/snapshot-mx.conf"

TEXTDOMAINDIR=/usr/share/locale
TEXTDOMAIN=snapshot-mx

show_help () {
	printf "$help_text"
	exit 0
}

help_text="
	Usage:  $0  [option]
	
	Run with no options to create .iso file for a live, bootable CD/DVD
	copy of the running system.
	
	valid options:
		-h, --help		show this help text
		-v, --version	display the version information

	*** See $configfile for information about settings.

"

while [[ $1 == -* ]]; do
	case "$1" in
	
		-h|--help)
			show_help ;;
		
		-v|--version)
			printf "\n$version\n\n" 
			exit 0 ;;
		
		*) 
			printf "\t invalid option: $1 \n\n"
			printf "\t Try:  $0 -h for full help. \n\n"
			exit 1 ;;
    esac
done		

snapshot_configuration () {
if [[ -f $configfile ]]; then
    source $configfile
fi
# Check for values in $configfile and use them.
# If any are unset, these defaults will be used.
error_log=${error_log:="/var/log/snapshot_errors.log"}
work_dir=${work_dir:="/home/work"}
snapshot_dir=${snapshot_dir:="/home/snapshot"}
save_work=${save_work:="no"}
snapshot_excludes=${snapshot_excludes:="/usr/lib/snapshot-mx/snapshot_exclude.list"}
initrd_modules_file=${initrd_modules_file:="/usr/lib/snapshot-mx/initrd_modules.list"}
snapshot_persist=${snapshot_persist:="no"}
kernel_image=${kernel_image:="/vmlinuz"}
initrd_image=${initrd_image:="/initrd.gz"}
stamp=${stamp:=""}
snapshot_basename=${snapshot_basename:="snapshot"}
make_md5sum=${make_md5sum:="no"}
make_isohybrid=${make_isohybrid:="yes"}
edit_boot_menu=${edit_boot_menu:="no"}
iso_dir=${iso_dir:="/usr/lib/snapshot-mx/new-iso"}
lib_mod_dir=${lib_mod_dir:="/lib/modules/"}
ata_dir=${ata_dir:="kernel/drivers/ata"}
text_editor=${text_editor:="/usr/bin/nano"}
}

snapshot_configuration

# Record errors in a logfile.
exec 2>"$error_log"

# Check that user is root.
[[ $(id -u) -eq 0 ]] || { echo -e $"\t You need to be root!\n" ; exit 1 ; }

# Default text editor is nano. Make sure it exists if user intends to
# edit files before squashing the filesystem.
if [[ $edit_boot_menu = "yes" ]] ; then
	[[ -e $text_editor ]] || { echo -e $"\n Error! The text editor is set to ${text_editor},
 but it is not installed. Edit $configfile
 and set the text_editor variable to the editor of your choice.
 (examples: /usr/bin/vim, /usr/bin/joe)\n" ; exit 1 ; }
fi

# Check to see if user wants to use live persistence and install live-init-mx.
# Don't forget to remove afterwards!
if [[ $snapshot_persist = "yes" ]] ; then
echo -e $"Live init scripts will be installed"
apt-get update && apt-get install live-init-mx
fi

# Function to check for old snapshots and filesystem copy
check_copies () {
# Check how many snapshots already exist and their total size
if [[ -d $snapshot_dir ]]; then
	snapshot_count=$(ls "$snapshot_dir"/*.iso | wc -l)
	snapshot_size=$(du -sh "$snapshot_dir" | awk '{print $1}')
	if [[ -z $snapshot_size ]]; then
		snapshot_size="0 bytes"
	fi
else
	snapshot_count="0"
	snapshot_size="0 bytes"
fi

# Check for saved copy of the system
if [[ -d "$work_dir"/new-squashfs ]]; then
    saved_size=$(du -sh "$work_dir"/new-squashfs | awk '{ print $1 }')
    saved_copy=$(echo $"* You have a saved copy of the system using $saved_size of space
   located at $work_dir/new-squashfs.")
fi

# Create a message to say whether the filesystem copy will be saved or not.
if [[ $save_work = "yes" ]]; then
	save_message=$(echo $"* The temporary copy of the filesystem will be saved 
   at $work_dir/new-squashfs.")
else
	save_message=$(echo $"* The temporary copy of the filesystem will be created 
   at $work_dir/new-squashfs and removed when this program finishes.")
fi
}

# Create snapshot_dir and work_dir if necessary.
check_directories () {

# Check that snapshot_dir exists
if ! [[ -d $snapshot_dir ]]; then
	mkdir -p "$snapshot_dir"
	chmod 777 "$snapshot_dir"
fi

# Check that work directories exist or create them.
if [[ $save_work = "no" ]]; then
    if [[ -d $work_dir ]]; then
        rm -rf "$work_dir"
    fi
    mkdir -p "$work_dir"/new-iso
    mkdir -p "$work_dir"/new-squashfs
elif [[ $save_work = "yes" ]]; then
	if ! [[ -d $work_dir ]]; then
	    mkdir -p "$work_dir"/new-iso
        mkdir -p "$work_dir"/new-squashfs
    fi
fi
}

# Check disk space on mounted /, /home, /media, /mnt, /tmp
check_space () {

disk_space=$(df -h | awk '/Filesystem/ { print $1 "\t " $2 "\t" $3 "\t" $4 "\t" $5 "  " $6 }' ;
df -h | awk '$6=="/" { print $1 "\t " $2 "\t" $3 "\t" $4 "\t" $5 "  " $6 }' ;
df -h | awk '$6=="/home" { print $1 "\t " $2 "\t" $3 "\t" $4 "\t" $5 "  " $6 }' ;
df -h | awk '$6~"/mnt" { print $1 "\t " $2 "\t" $3 "\t" $4 "\t" $5 "  " $6 }' ;
df -h | awk '$6~"/media" { print $1 "\t " $2 "\t" $3 "\t" $4 "\t" $5 "  " $6 }' ;
# Next line is useful only if you have an encrypted volume mounted at non-standard location.
#df -h | awk '$1~"/dev/mapper" { print $1 "\t" $2 "\t" $3 "\t" $4 "\t" $5 "  " $6 }' ;
df -h | awk '$6=="/tmp" { print $1 "\t " $2 "\t" $3 "\t" $4 "\t" $5 "  " $6 }' ;)

}

# Detect kernel in use and others installed.
detect_kernels () {
kernel_used=$(uname -r)
kernels_avail=$(ls /boot/vmlinuz-* | sed 's|/boot/vmlinuz-||g'| grep -v $kernel_used)

}

# Show current settings and disk space 
report_space () {
echo $"
 You will need plenty of free space. It is recommended that free space 
 (Avail) in the partition that holds the work directory (probably /)
 should be two times the total installed system size (Used). You can 
 deduct the space taken up by previous snapshots and any saved copies of
 the system from the Used amount.
 
 * You are presently running kernel version $kernel_used
 * $kernels_avail is also available.
 * snapshot will use kernel version $kernel_used

 * You have $snapshot_count snapshots taking up $snapshot_size of disk space.
 $saved_copy
 $save_message
 * The snapshot directory is currently set to $snapshot_dir
 $tmp_warning

 You can change these and other settings by editing 
 $configfile.


 Current disk usage:
 (For complete listing, exit and run 'df -h')

 $disk_space
 
    Press ENTER to proceed or hit ctrl-c to exit.  "
    read -p " "
}

check_copies
check_directories
check_space
detect_kernels
report_space

copy_modules() {
    local to=$1  from=$2 ; shift 2

    local list
    if [ -f $1 ]; then
        list=$(cat $1)
    else
        list="$*"
    fi
   
    local name mod expr
    for name in $list; do
        mod=$(echo $name | sed -e 's/[_-]/[_-]/g' -e 's/$/.ko/')
        expr="$expr${expr:+ -o} -name $mod"
    done

    mkdir -p $to

    local mod_list=$(find $from $expr)
    local count=$(echo "$mod_list" | grep -c .)
    printf $"Copying %s modules into the initrd\n" $count

    local file sub_dir
    for file in $mod_list; do
        #[ "$verbose" ] && echo "Add initrd module $(basename $file .ko)"
        sub_dir="$to/$(basename $(dirname $file))"
        mkdir -p $sub_dir
        cp $file $sub_dir/
    done
}

open_initrd() {
    local file=$1  dir=$2
    mkdir -p $dir
    chmod a+rx $dir
    (cd $dir && gunzip -c $file | cpio -idum)
}

close_initrd() {
    local dir=$1  file=$2
    (cd $dir && find . | cpio -o -H newc --owner root:root | gzip -9) > $file
    #rm -rf $dir
}
 
if [ ! -e "$initrd_modules_file" ]; then
    echo "Could not find list of modules to put into the initrd"
    echo "Missing file: $initrd_modules_file"
    exit 2
fi


# The real work starts here
cd $work_dir

#copy correct vmlinuz to "$work_dir"/new-iso/
echo "Copying the new-iso filesystem..."
cp /boot/vmlinuz-$kernel_used "$iso_dir"/antiX/vmlinuz
rsync -a "$iso_dir"/ "$work_dir"/new-iso/ 
rm "$iso_dir"/antiX/vmlinuz

initrd_dir=$(mktemp -d /tmp/$ME-XXXXX)
open_initrd $iso_dir/antiX/initrd.gz $initrd_dir

mod_dir=$initrd_dir/lib/modules
rm -rf $mod_dir/*
copy_modules $mod_dir/$kernel_used /lib/modules/$kernel_used $initrd_modules_file

close_initrd $initrd_dir $work_dir/new-iso/antiX/initrd.gz

   
# Copy the filesystem
rsync -a / new-squashfs/ --delete --exclude="$work_dir" --exclude="$snapshot_dir" --exclude-from="$snapshot_excludes"

# /etc/fstab should exist, even if it's empty,
# to prevent error messages at boot
touch "$work_dir"/new-squashfs/etc/fstab

# Need to define $filename here (moved up from genisoimage)
# and use it as directory name to identify the build on the cdrom.
# and put package list inside that directory
if [[ $stamp = "datetime" ]]; then
    # use this variable so iso and md5 have same time stamp
	filename="$snapshot_basename"-$(date +%Y%m%d_%H%M).iso
else
    n=1
    while [[ -f "$snapshot_dir"/snapshot$n.iso ]]; do
        ((n++))
    done
    filename="$snapshot_basename"$n.iso
fi

# Remove any old package-list directories (only works for same basename)
for dir in "$work_dir"/new-iso/"${snapshot_basename}"* ; do
	rm -r "$dir"
echo $"Removing old package-list directory:  $dir"
done

mkdir -p "$work_dir"/new-iso/"${filename%.iso}"
dpkg -l | grep "ii" | awk '{ print $2 }' > "$work_dir"/new-iso/"${filename%.iso}"/package_list


# Pause to edit the boot menu or anything else in $work_dir
if [[ $edit_boot_menu = "yes" ]]; then
	echo $"
 You may now go to another virtual console to edit any files in the work
 directory, or hit ENTER and edit the boot menu.
 "
 read -p " "
 "$text_editor" "$work_dir"/new-iso/
fi

# Squash the filesystem copy
#echo $"Squashing the filesystem..."
mksquashfs new-squashfs new-iso/antiX/linuxfs ${mksq_opt} 

# create the iso file, make it isohybrid
# create md5sum file for the iso
echo $"Creating CD/DVD image file..."
cd new-iso

genisoimage -l -V MX-14-live -R -J -pad -no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/isolinux.cat -o "$snapshot_dir"/"$filename" .
	
if [[ $make_isohybrid = "yes" ]]; then
        echo $"Making hybrid iso"
	isohybrid "$snapshot_dir"/"$filename"
fi

if [[ $make_md5sum = "yes" ]]; then
		md5sum "$snapshot_dir"/$filename > "$snapshot_dir"/"$filename".md5
fi

# Cleanup
if [[ $save_work = "no" ]]; then
    echo $"Cleaning..."
    cd /
    rm -rf "$work_dir"
else
    rm "$work_dir"/new-iso/antiX/linuxfs
fi


if [[ $snapshot_persist = "yes" ]] ; then
echo -e $"Remove live-init-mx"
apt-get purge live-init-mx
fi

echo $"All finished!"

exit 0
