#!/bin/bash
# Script for Raspberry that save image of the used flash to a USB disk, or restore the content of an image (saved in a USB disk) to a flash disk
# Can be used to backup the SD of a raspberry, or to clone an image in another SD.
# Author: Creasol https://creasol.it
# This software is open source, distributed with license GNU GPL3 https://www.gnu.org/licenses/gpl-3.0.en.html

#structure of SD partitions
sfdisk_8GB='
label: dos
label-id: 0x12345678
device: /dev/mmcblk0
unit: sectors

/dev/mmcblk0p1 : start=        2048, size=      819200, type=c
/dev/mmcblk0p2 : start=      841728, size=     6144000, type=83
/dev/mmcblk0p3 : start=     7190528, size=     8000000, type=83
'

sfdisk_16GB='
label: dos
label-id: 0x12345678
device: /dev/mmcblk0
unit: sectors

/dev/mmcblk0p1 : start=        2048, size=      819200, type=c
/dev/mmcblk0p2 : start=      841728, size=    10240000, type=83
/dev/mmcblk0p3 : start=    11286528, size=    16000000, type=83
'

sfdisk_32GB='
label: dos
label-id: 0x12345678
device: /dev/sda
unit: sectors

/dev/sda1 : start=        2048, size=      819200, type=c
/dev/sda2 : start=      841728, size=    10240000, type=83
/dev/sda3 : start=    11286528, size=    48000000, type=83
'

function getAnswer () {
	local x
		echo -e  "\n\n\a"
		_echo $1
		read x
		if [ "$x" == 'n' -o "$x" == 'N' ]; then
			echo "No"
			return 0
		else
			echo "Yes"
			return 1
		fi
}

#translations to other languages
declare -A text
text[en,0]="Raspberry/Domoticz image save and restore"
text[en,1]="Written by Creasol https://creasol.it"
text[en,2]="This script must be called from root user. Type \"sudo su -\" , or \"su -\" to become root, then call this script again"
text[en,3]="Would you like to create an image of the SD [SD => IMAGE] (Y/n/ctrl-c)"
text[en,4]="Write the name of device where image will be placed (for example sda1)"
text[en,5]="========== List of attached devices: =========="
text[en,6]="Write the name of FLASH device to backup (for example mmcblk0)"
text[en,7]="Invalid device: please choose the FLASH device, type disk"
text[en,8]="========= Terminated : press a key to see the image file =========="
text[en,9]="Would you like to write an image to new SD [IMAGE => SD] (Y/n/ctrl-c)"
text[en,10]="Write the name of device where image have been placed (for example sda1 or mmcblk0p2 or ...)"
text[en,11]="========== List of files in the selected device =========="
text[en,12]="Write the image filename"
text[en,13]="Image file exists, but was not generated by FsArchiver, so it cannot be used by this script"
text[en,14]="Do not rename the image filename, because it contains the right label-id for the disk"
text[en,15]="Write the name of FLASH device to be written (example sdb)"
text[en,16]="Too small SD device: must be at least 8GB"
text[en,17]="Device is not type disk: select the device associated to the SD disk (not a partition!), like sda, sdb, ..."
text[en,18]="Write the image configuration, like" 
text[en,19]="Original DiskID (label-id) not found in the image filename: image filename must not be renamed!"
text[en,20]="Most probably you have to modify files /boot/cmdline.txt and /etc/fstab to match the new DiskId that now is 12345678"
text[en,21]="Error unmount target disk, mounted on $d"
text[en,22]="Press a key to continue..."
text[en,23]="Cannot determine disk size: use at least 8GB disks"
text[en,24]="or press ENTER..."

#italian translation by Paolo Subiaco
text[it,0]="Salva e ripristina le immagini di Raspberry/Domoticz"
text[it,1]="Sviluppato da Creasol https://creasol.it"
text[it,2]="Questo script va eseguito dall utente root. Digitare \"sudo su -\" oppure \"su -\" per diventare root, e poi richiamare nuovamente questo script"
text[it,3]="Vuoi creare un'immagine dall'SD [SD => IMMAGINE] (Y/n/ctrl-c)"
text[it,4]="Scrivi il nome del dispositivo dove salvare l'immagine (ad esempio sda1)"
text[it,5]="========== Lista dei dispositivi disponibili: =========="
text[it,6]="Scrivi il nome del dispositivo FLASH da backuppare (ad esempio mmcblk0)"
text[it,7]="Dispositivo invalido: scegliere un dispositibo FLASH di tipo \"disk\""
text[it,8]="========= Terminato : premere un tasto per vedere il file immagine =========="
text[it,9]="Vuoi scrivere un'immagine in un nuovo SD [IMMAGINE => SD] (Y/n/ctrl-c)"
text[it,10]="Scrivi il nome del dispositivo contenente l'immagine (ad esempio sda1 o mmcblk0p2 o ....)"
text[it,11]="========== Lista dei files nel dispositivo =========="
text[it,12]="Scrivi il nome del file associato all'immagine"
text[it,13]="Il file esiste, ma non e'stato generato da FsArchiver e non puo' essere gestito da questo script"
text[it,14]="Non rinominare il file contenente l'immagine, perche' contiene l'ID del disco"
text[it,15]="Scrivere il nome del dispositivo FLASH da scrivere (ad esempio sdb)"
text[it,16]="Dispostivo SD troppo piccolo: deve essere almeno da 8GB"
text[it,17]="Il dispositivo non e' del tipo \"disk\": selezionare il dispostivo associato all'intera memoria SD (e non una partizione!), come sda, sdb, ..."
text[it,18]="Scrivi la configurazione dell'immagine, ad esempio" 
text[it,19]="Impossibile conoscere il DiskID (label-id) originale, non presente nel nome del file immagine: mai rinominare le immagini!"
text[it,20]="Molto probabilmente dovrai montare le partizioni e modificare i files /boot/cmdline.txt e /etc/fstab scrivendo il nuovo DiskId che e' 12345678"
text[it,21]="Errore smontando il disco di destinazione, montato su $d"
text[it,22]="Premi un tasto per continuare..."
text[it,23]="Non riesco a determinare la dimensione del disco: utilizzare dischi da almeno 8GB"
text[it,24]="oppure premi INVIO..."

#Would you like to translate to another language? Copy the lines text[en,..], replace "en" with the new language (e.g. "fr" for French) and translate the texts
#Please send translation or any suggestion/patch to linux@creasol.it , so we can publish your improvements too. Thanks

function _echo () {
	# print text_${lang}[$1]
	txt=${text[${lang},$1]}
	if [ -n "${txt}" ]; then
		echo "$txt"
	else
		echo "${text[en,$1]}"
	fi
}

lang=${LANG:0:2}

_echo 0 #"Raspberry Domoticz save/restore image"
_echo 1 #"Written by Creasol https://creasol.it"

# Check that script is executed by root
if [ "`whoami`" != "root" ]; then
	_echo 2 #'This script must be called from root user. Type "sudo su -" , or "su -" to become root, then call this script again'
	exit
fi

# if fsarchiver does not exist, update repository and get fsarchiver among other software
if [ -z "`which fsarchiver`" ]; then
	apt update
	apt -y install fsarchiver wget curl 
fi

# while loop to do all 
while [ 1 ]; do
	echo "================================================"
	getAnswer 3 #"Would you like to create an image of the SD [SD => IMAGE]"
	if [ $? -ne 0 ]; then
		while [ 1 ]; do
			_echo 5 #"========== List of attached devices: =========="
			lsblk
			_echo 4 #"Write the name of USB device where image will be placed (for example sda1)"
			read imgdev
			if [ ! -d /mnt/img ]; then
				mkdir /mnt/img
			fi
			umount /mnt/img 2>/dev/null
			mount /dev/${imgdev} /mnt/img
			if [ $? -eq 0 ]; then
				# mounted successfully
				break;
			fi
		done
		while [ 1 ]; do
			_echo 5
			lsblk
			_echo 6 #"Write the name of FLASH device to backup (for example mmcblk0)"
			read sddev
			sddevs=`lsblk -i -p -o NAME |grep -- -/dev/${sddev}|cut -b 3-`
			if [ -n "${sddevs}" ]; then
				# devices exist
				break;
			else
				_echo 7 #"Invalid device: please choose the FLASH device, type disk"
			fi
		done
		apt clean
		labelid="`sfdisk -d /dev/${sddev}|grep label-id|cut -b 11-`"
		dstsfdisk="raspberry_domoticz_${labelid}_`date +%F`.sfdisk"
		echo "sfdisk -d /dev/${sddev} >/mnt/img/${dstsfdisk}"
		sfdisk -d /dev/${sddev} >/mnt/img/${dstsfdisk}
		dstfile="raspberry_domoticz_${labelid}_`date +%F`.fsa"
		fsarchiver -v -A savefs /mnt/img/${dstfile} ${sddevs}
		_echo 8 #"========= Terminated : press a key to see the image file =========="
		read x
		ls -l /mnt/img
	fi




	echo "================================================"
	getAnswer 9 #"Would you like to write an image to new SD [IMAGE => SD]"
	if [ $? -ne 0 ]; then
		while [ 1 ]; do
			_echo 5 #"========== List of attached devices: =========="
			lsblk
			_echo 10 #"Write the name of USB device where image have been placed (for example sda1)"
			read imgdev
			if [ ! -d /mnt/img ]; then
				mkdir /mnt/img
			fi
			umount /mnt/img 2>/dev/null
			mount /dev/${imgdev} /mnt/img
			if [ $? -eq 0 ]; then
				# mounted successfully
				break;
			fi
		done
		while [ 1 ]; do
			_echo 11 #"========== List of files in the selected device =========="
			ls -l /mnt/img
			_echo 12 #"Write the image filename"
			read imgfile
			if [ -r "/mnt/img/${imgfile}" ]; then
				# image file exist: check it was made by FsArchiver
				if [ "`head -c3 /mnt/img/${imgfile}`" == "FsA" ]; then
					break
				else
					_echo 13 #"Image file exists, but was not generated by FsArchiver, so it can't be used by this script"
				fi
			else
				echo "File ${imgfile} does not exist"
			fi
		done
		while [ 1 ]; do	
			_echo 5 # "========== List of attached devices: =========="
			lsblk
			_echo 15 #"Write the name of FLASH device to be written (example sdb)"
			read sddev
			lsblk -i -d /dev/${sddev}
			sddevsize=`lsblk -i -d -o SIZE /dev/${sddev}`
			sddevtype=`lsblk -i -d -o TYPE /dev/${sddev}`
			if [[ "${sddevtype}" =~ "disk" ]]; then
				# device is a disk
				sddevsize="`echo ${sddevsize}|cut -b 6-|tr -d G|sed 's/[\.,][0-9]*//'`"
				echo "SD size=${sddevsize}"
				if [ ${sddevsize} -lt 6 ]; then
					part=''
					_echo 16 #"Too small SD device: must be at least 8GB"
				elif [ ${sddevsize} -le 8 ]; then
					#8GB SD
					part="${sfdisk_8GB}"
				elif [ ${sddevsize} -le 16 ]; then
					#16GB SD
					part="${sfdisk_16GB}"
				else
					#32GB SD or more
					part="${sfdisk_32GB}"
				fi
				if [ -n "${part}" ]; then
					#unmount fs on sddev
					error=0
					for d in `mount |grep /dev/${sddev}|cut -d ' ' -f 3`; do 
						echo "Unmounting $d ..."
						umount $d
						if [ $? -ne 0 ]; then
							_echo 21 #Error umounting ....
							error=1									
						fi
					done
					if [ $error -eq 0 ]; then 
						break 	#No errors: continue
					else
						_echo 22 #Press a key to continue
						read x
					fi
				else
					_echo 23 #Cannot determine partition type
				fi
			else
				_echo 17 #"Device is not type disk: select the device associated to the SD disk (not a partition!), like sda, sdb, ..."
			fi
		done
		echo "Partitioning device ${sddev} ..."
		# partitioning SD using a different DiskID (label-id) to avoid automounting problem during image writing
		labelid="`echo ${imgfile}|sed 's/^.*_\(0x[0-9a-f]\{8\}\).*/\1/'`"
		if [[ ${labelid} =~ 0x[0-9a-f]{8} ]]; then
			echo "Restore original DiskID (label-id): ${labelid}"
			echo "${part}"|sed "s/0x12345678/${labelid}/" |sfdisk /dev/${sddev}
		else
			echo "${part}" |sfdisk /dev/${sddev}
			_echo 19 #"Original DiskID (label-id) not found in the image filename: image filename must not be renamed!"
			_echo 20 #"Most probably you have to modify files /boot/cmdline.txt and /etc/fstab to match the new DiskId (12345678)"
		fi
		sleep 10
		#Unmount partitions again
		while [ -n "`mount |grep /dev/${sddev}`" ]; do
			for d in `mount |grep /dev/${sddev}|cut -d ' ' -f 3`; do 
				echo "Unmounting $d ..."
				umount $d
			done
			sleep 10
		done
		while [ 1 ]; do
			echo -e "\a==================================================================="
			_echo 18 #"Write the image configuration, like" 
			echo "  id=0,dest=/dev/${sddev}1 id=1,dest=/dev/${sddev}2 id=2,dest=/dev/${sddev}3"
			_echo 24 #"or press ENTER..."
			read fsarest
			if [ -z "${fsarest}" ]; then
				fsarest="id=0,dest=/dev/${sddev}1 id=1,dest=/dev/${sddev}2 id=2,dest=/dev/${sddev}3"
			fi
			break
		done
		#Unmount partitions again
		while [ -n "`mount |grep /dev/${sddev}`" ]; do
			for d in `mount |grep /dev/${sddev}|cut -d ' ' -f 3`; do 
				echo "Unmounting $d ..."
				umount $d
			done
			sleep 10
		done
		fsarchiver -v restfs /mnt/img/${imgfile} ${fsarest}
		echo "Waiting for SD end writing..."
		sync; sleep 2 #wait few seconds to avoid sfdisk returning error
		echo "End writing SD!"
	fi
done
	
	
				
			


