====== [SCRIPT] RestoreMenu ======
====== Descripcion ======
Ver el post del blog: http://blog.ciberterminal.net/index.php/2012/01/20/script-de-copia-y-restauracion-de-un-sistema-windows
====== Acciones a realizar ======
Instalar un sistema linux base con las siguientes dependencias:
* bash >= 4.0
* sudo
* dd
* gzip/gunzip
* [[http://www.tuxera.com/community/ntfs-3g-download/|ntfsprogs]]
* acpi (comprobación de batería en portátil)
* dmidecode (revisamos el serial, el script hay que configurarlo cada vez)
* pigz (para proceso en paralelo)
===== Pasos generales =====
* Se particiona el disco.
* Instalamos windows
* Instalamos un linux "minimal", y dependencias necesarias para que el script funcione
* Añadimos nuestro script.
* Modificamos los parámetros de configuración necesarios en el mismo
* Creamos un usuario de sistema sin privilegios, modificamos //~/.bashrc// para lanzar automáticamente el script cuando se logue el usuario.
* Deshabilitamos la posibilidad de salir del script (//trap//) para evitar problemas.
* Nos guardamos el pass de root ;-)
* Modificamos grub para que el menú diga algo como Restore or similar y el usuario no se asuste viendo //GNU/Linux//
* Informar al usuario de como funciona el entorno, aunque es muy muy sencillo)
===== Script =====
==== Codigo ====
#!/bin/bash
# Coded by dodger
# More information on
# http://blog.ciberterminal.net/index.php/2012/01/20/script-de-copia-y-restauracion-de-un-sistema-windows
SNAPSHOTDIR=/home/snapshots
WINDOWSDEV=/dev/sda1
SERIAL="serialnumber"
HOST="hostname"
INITIAL_IMAGE="INITIAL-IMAGE_20120119-115923_mar-eee.img.gz"
COMPRESSOR=/usr/bin/pigz
COMPRESSOPTIONS="-9fc"
UNCOMPRESSOR=/bin/gunzip
UNCOMPRESSOPTIONS="-dc"
NTFSCLONE="/root/ntfs-3g_ntfsprogs-2011.4.12/ntfsprogs/ntfsclone"
NTFSRESIZE="/root/ntfs-3g_ntfsprogs-2011.4.12/ntfsprogs/ntfsresize"
# AUTOCONFIG
unset IMAGELIST
unset IMAGESIZE
[ ! -d ${SNAPSHOTDIR} ] && mkdir $SNAPSHOTDIR
trap_it()
{
[[ "$(kill -l | tail -1 | tr '+' '-')" =~ ([0-9]{1,})\)[[:space:]]{1,}[A-Z0-9\-]{3,15}(.|)$ ]]
local TRAPIT=$(eval echo {1..${BASH_REMATCH[1]}})
trap 'echo "NO PERMITIDO"' ${TRAPIT}
}
print_main_menu()
{
local let OPTION=0
clear
cat > /dev/stdout < "
read OPTION
OPTION=${OPTION:-98}
[[ ! ${OPTION} =~ ^[0-9]{1,}$ ]] && OPTION=0
done
return ${BASH_REMATCH[0]}
}
test()
{
local MENU=$1
local let OPTION=0
case $MENU in
"main" )
;;
"restore" )
;;
"clone" )
;;
* )
;;
esac
}
help()
{
clear
cat > /dev/stdout < "
read YN
[[ ! ${YN} =~ ^[yY]$ ]] && return 1
return 0
}
restore_clone_main()
{
local ACTION=$1
local IMAGEFILE=$2
local DDPID=0
check_cable
show_agree
[ ! $? -eq 0 ] && return 1
case "$ACTION" in
"clone")
${NTFSCLONE} --save-image --output - ${WINDOWSDEV} | $COMPRESSOR $COMPRESSOPTIONS > ${IMAGEFILE}
;;
"restore")
$UNCOMPRESSOR $UNCOMPRESSOPTIONS $IMAGEFILE | ${NTFSCLONE} --restore-image --overwrite ${WINDOWSDEV} -
;;
*)
echo "Error, REPORT"
return 1
;;
esac
echo "Completado, Pulse ENTER para continuar"
read
return 0
}
restore_menu()
{
local let OPTION=99
local YN=n
clear
echo "##################################################"
echo "Menu de restauracion"
echo "Paso 1: Elegir imagen:"
list_images
for ((x=0; x<${#IMAGELIST[*]} ; x++)) ; do
echo "${x} ) ${IMAGELIST[$x]}"
done
echo -e "\nIntroduce el numero de imagen a restaurar"
while [ ${OPTION} = 99 ]
do
printf "%s" "-> "
read OPTION
OPTION=${OPTION:-99}
[ ${OPTION} -lt 0 ] || [ ${OPTION} -ge ${#IMAGELIST[*]} ] || [[ ! ${OPTION} =~ ^[0-9]{1,}$ ]] && OPTION=99
done
echo "##################################################"
echo "Paso 2: Restaurar imagen: ${IMAGELIST[$OPTION]}"
echo "La descripcion de la imagen es:"
cat ${SNAPSHOTDIR}/$(basename ${IMAGELIST[$OPTION]} .img.gz).description
echo "Estas seguro?"
printf "%s" "(y/N)-> "
read YN
[[ ${YN} =~ ^[yY]$ ]] && restore_clone_main restore "${SNAPSHOTDIR}/${IMAGELIST[$OPTION]}"
if [ $? -eq 0 ] ; then
echo "Es necesario reiniciar el Ordenador..."
sleep 2
reboot_machine
fi
}
clone_menu()
{
MYDATE="$(date +%Y%m%d-%H%M%S)"
local let OPTION=99
local YN=n
local DESCRIPTION=""
local USEDSPACE="$(${NTFSRESIZE} -i --no-action ${WINDOWSDEV} 2>&1 | grep "freeing" | awk '{print $5}')"
USEDSPACE=$(($USEDSPACE/1024/2))
local FREESPACE="$(df -P ${SNAPSHOTDIR} | grep "%" | awk '{print $4}')"
clear
echo "##################################################"
echo "Menu de Clonado"
[ $USEDSPACE -gt $FREESPACE ] && echo "El espacio necesario es mayor que el libre, es necesario hacer espacio (borrar imagenes)" && return 1
while [[ ! $DESCRIPTION ]] ; do
echo "Introduce una descripcion para la imagen y pulsa ENTER"
printf "%s" "-> "
read DESCRIPTION
echo "${DESCRIPTION}" > "${SNAPSHOTDIR}/${MYDATE}_${HOST}.description"
done
restore_clone_main clone "${SNAPSHOTDIR}/${MYDATE}_${HOST}.img.gz"
return 0
}
delete_menu()
{
local let OPTION=98
local YN=n
clear
list_images
list_image_size
[ ${#IMAGELIST[@]} -eq 1 ] && echo -e "No hay imagenes que borrar,\nPresione ENTER" && read && return 0
echo "##################################################"
echo "Menu de Borrado"
echo "Paso 1: Elegir imagen para borrar:"
for ((x=0; x<${#IMAGELIST[@]} ; x++)) ; do
[[ ! "${IMAGELIST[$x]}" =~ ^${INITIAL_IMAGE}$ ]] && echo "$x ) \"${IMAGELIST[$x]}\" , Tamaño : ${IMAGESIZE[$x]} MB"
done
echo -e "\nIntroduce el numero de imagen a eliminar o 99 para salir"
while [ ${OPTION:-98} = 98 ]
do
printf "%s" "-> "
read OPTION
OPTION=${OPTION:-98}
[ ${OPTION} -eq 99 ] && return 1
[ ${OPTION} -lt 0 ] || [ ${OPTION} -gt ${#IMAGELIST[*]} ] || [[ ! ${OPTION} =~ ^[0-9]{1,}$ ]] && OPTION=98
done
echo "Estas seguro?"
printf "%s" "(y/N)-> "
read YN
if [[ ${YN} =~ ^[yY]$ ]]
then
echo "Borrando ${IMAGELIST[$OPTION]}"
if [[ ! "${IMAGELIST[$OPTION]}" =~ ^${INITIAL_IMAGE}$ ]]
then
rm -fv "${SNAPSHOTDIR}/${IMAGELIST[$OPTION]}"
rm -fv "${SNAPSHOTDIR}/$(basename ${IMAGELIST[$OPTION]} .img.gz).description"
fi
fi
}
list_menu()
{
local let OPTION=98
local YN=n
clear
list_images
list_image_size
echo "##################################################"
echo "Lista de imagenes"
for ((x=0; x<${#IMAGELIST[@]} ; x++)) ; do
echo "$x ) Nombre: \"${IMAGELIST[$x]}\" , Tamaño : ${IMAGESIZE[$x]} MB"
done
echo "##################################################"
echo "Presiona ENTER para continuar"
read
}
main()
{
trap_it
while true ; do
local let OPTION=0
print_main_menu
OPTION=$?
case $OPTION in
1)
restore_menu
;;
2)
clone_menu
;;
3)
delete_menu
;;
4)
list_menu
;;
5)
help
;;
99)
reboot_machine
;;
*)
;;
esac
done
}
main
==== Variables ====
^ Variable ^ Valor por defecto ^ Descripcion ^
| ''SNAPSHOTDIR'' | ''/home/snapshots'' | Directorio donde se almacenarán los snapshots y descripciones |
| ''WINDOWSDEV'' | ''/dev/sda1'' | Partición que contiene Windows |
| ''SERIAL'' | ''"8BOAAQ256174"'' | El nº de serie del equipo, se puede ver con la dependencia ''dmidecode'' |
| ''HOST'' | ''"my-hostname"'' | Nombre del host, únicamente se usa para generar un nombre de imagen |
| ''COMPRESSOR'' | ''/usr/bin/pigz'' | Comando usado para comprimir, en este caso [[http://blog.ciberterminal.net/index.php/2011/11/15/ciclo-herramientas-gnulinux-v-pbzip2/|pigz]] para aprovechar los procesadores multi-core. |
| ''COMPRESSOPTIONS'' | ''"-9fc"'' | Opciones para la compresión |
| ''UNCOMPRESSOR'' | ''/bin/gunzip'' | Ídem pero para descomprimir |
| ''UNCOMPRESSOPTIONS'' | ''"-dc"'' | Opciones para la descompresión |
| ''NTFSCLONE'' | ''"/root/ntfs-3g_ntfsprogs-2011.4.12/ntfsprogs/ntfsclone"'' | Donde está el binario de ntfsclone, a veces puede ser necesario compilar (ver [[restore_menu#Troubleshooting|Troubleshooting]]) |
| ''NTFSRESIZE'' | ''"/root/ntfs-3g_ntfsprogs-2011.4.12/ntfsprogs/ntfsresize"'' | Ídem que antes, si hemos compilado |
==== Ejemplo de Configuración restante ====
=== grub ===
# general configuration:
timeout 2
default 0
color light-blue/black light-cyan/blue
# (0) Windows
title Windows
rootnoverify (hd0,0)
makeactive
chainloader +1
# (1) Arch Linux
title Recovery Mode
root (hd0,1)
kernel /boot/vmlinuz-linux root=/dev/sda2 ro
initrd /boot/initramfs-linux.img
=== .bashrc ===
#
# ~/.bashrc
#
sudo /root/restore_menu.sh
=== sudoers ===
username ALL=(ALL) NOPASSWD: /root/restore_menu.sh
====== Troubleshooting ======
===== ntfsclone =====
ERROR: $Bitmap size is smaller than expected
Solución:
http://www.tuxera.com/forum/viewtopic.php?f=2&t=27527