#!/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 (August 2011)
# 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-gui-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 xserver is running and user is root.
[[ $DISPLAY ]] || { echo $"There is no xserver running. Exiting..." ; exit 1 ; }
[[ $(id -u) -eq 0 ]] || { yad --image "error" --text $"You need to be root\! \n\nCannot continue." ; exit 1 ; }

TEXT1=$"Are you ready to create a live installable snapshot of your system? 
This utility will create a bootable image that you can burn to CD/DVD. 
The image will be stored in $snapshot_dir.

This is free software that comes with no warranty or guarantee of any type, 
including but not limited to express, implied, merchantability or fitness 
of purpose.

Copyright:
2012, 2013 anticapitalista@antiX@operamail.com 
2011, 2012 fsmithred@gmail.com 
(portions may be copyright Dean Linkous)"

# First window shows general information
yad --title "$version" --width 580 --center --text "$TEXT1"
if [ $? -ne 0 ]; then
    exit 0
fi

# Default gui editor is geany. Make sure it exists if user intends to
# edit files before squashing the filesystem.
if [[ $edit_boot_menu = "yes" ]] ; then
	[[ -e $gui_editor ]] || { yad --error --title="Warning!" --text=$"The graphical text editor is set to
${gui_editor}, but it is not installed. Edit $configfile
and set the gui_editor variable to the editor of your choice.
(examples: /usr/bin/gedit, /usr/bin/leafpad)" ; 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
apt-get update | tee >(yad --width 300 --title "Installing live-init-mx ..." --progress --pulsate)
apt-get install live-init-mx 
fi
kill $(pgrep yad)

# 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)

}
# These functions create the second window

check_copies
check_directories
check_space
detect_kernels

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

# Put information in a yad window to show current settings and disk space 

TEXT2=$"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.

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

$disk_space"

yad --width=580 --center --title "Disk Space"  --text "$TEXT2"


if [ $? -ne 0 ]; then
    exit 0
fi

TEXT3=$"
* 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.
"

yad --width=580 --center --title "Settings Report"  --text "$TEXT3"

if [ $? -ne 0 ]; then
    exit 0
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 -av / new-squashfs/ --delete --exclude="$work_dir" \
 --exclude="$snapshot_dir" --exclude-from="$snapshot_excludes" \
 | tee >(yad --width 300 --title $"Copying filesystem..." --progress --pulsate)
kill $(pgrep yad)

# /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
    yad --title=Edit Boot Menu --ok-label=Yes --cancel-label=No \
        --text=$"The program will now pause to allow you to edit any files in the work directory. Select Yes to edit the boot menu or select No to bypass this step and continue creating the snapshot."
    if [[ $? = 0 ]] ; then
         "$gui_editor" "$work_dir"/new-iso/
    fi
fi

# Squash the filesystem copy
mksquashfs new-squashfs new-iso/antiX/linuxfs ${mksq_opt}  | tee >(yad --width 300 --title "Squashing filesystem..." --progress --pulsate)
kill $(pgrep yad)
# 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-14live -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" . | tee >(yad --title "Creating CD/DVD image file..." --progress --pulsate)

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
kill $(pgrep yad)
# Cleanup
if [[ $save_work = "no" ]]; then
    cd /
    rm -rf "$work_dir" | tee >(yad --title "Cleaning..." --progress --pulsate)
else
    rm "$work_dir"/new-iso/antiX/linuxfs | tee >(yad --width 300 --title "Cleaning..." --progress --pulsate)
fi
kill $(pgrep yad)

if [[ $snapshot_persist = "yes" ]] ; then
apt-get purge live-init-mx | tee >(yad --width 300 --title "Removing live-init-mx" --progress --pulsate)
fi
kill $(pgrep yad)

yad --width 300 --text $"All finished!"

exit 0
